Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518/* SPDX-License-Identifier: GPL-2.0 */#ifndef _LINUX_FS_NOTIFY_H#define _LINUX_FS_NOTIFY_H
/* * include/linux/fsnotify.h - generic hooks for filesystem notification, to * reduce in-source duplication from both dnotify and inotify. * * We don't compile any of this away in some complicated menagerie of ifdefs. * Instead, we rely on the code inside to optimize away as needed. * * (C) Copyright 2005 Robert Love */
#include <linux/fsnotify_backend.h>#include <linux/audit.h>#include <linux/slab.h>#include <linux/bug.h>
/* Are there any inode/mount/sb objects watched with priority prio or above? */static inline bool fsnotify_sb_has_priority_watchers(struct super_block *sb, int prio){ struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
/* Were any marks ever added to any object on this sb? */ if (!sbinfo) return false;
return atomic_long_read(&sbinfo->watched_objects[prio]);}
/* Are there any inode/mount/sb objects that are being watched at all? */static inline bool fsnotify_sb_has_watchers(struct super_block *sb){ return fsnotify_sb_has_priority_watchers(sb, 0);}
/* * Notify this @dir inode about a change in a child directory entry. * The directory entry may have turned positive or negative or its inode may * have changed (i.e. renamed over). * * Unlike fsnotify_parent(), the event will be reported regardless of the * FS_EVENT_ON_CHILD mask on the parent inode and will not be reported if only * the child is interested and not the parent. */static inline int fsnotify_name(__u32 mask, const void *data, int data_type, struct inode *dir, const struct qstr *name, u32 cookie){ if (!fsnotify_sb_has_watchers(dir->i_sb)) return 0;
return fsnotify(mask, data, data_type, dir, name, NULL, cookie);}
static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry, __u32 mask){ fsnotify_name(mask, dentry, FSNOTIFY_EVENT_DENTRY, dir, &dentry->d_name, 0);}
static inline void fsnotify_inode(struct inode *inode, __u32 mask){ if (!fsnotify_sb_has_watchers(inode->i_sb)) return;
if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR;
fsnotify(mask, inode, FSNOTIFY_EVENT_INODE, NULL, NULL, inode, 0);}
/* Notify this dentry's parent about a child's events. */static inline int fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, int data_type){ struct inode *inode = d_inode(dentry);
if (!fsnotify_sb_has_watchers(inode->i_sb)) return 0;
if (S_ISDIR(inode->i_mode)) { mask |= FS_ISDIR;
/* sb/mount marks are not interested in name of directory */ if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) goto notify_child; }
/* disconnected dentry cannot notify parent */ if (IS_ROOT(dentry)) goto notify_child;
return __fsnotify_parent(dentry, mask, data, data_type);
notify_child: return fsnotify(mask, data, data_type, NULL, NULL, inode, 0);}
/* * Simple wrappers to consolidate calls to fsnotify_parent() when an event * is on a file/dentry. */static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask){ fsnotify_parent(dentry, mask, dentry, FSNOTIFY_EVENT_DENTRY);}
static inline int fsnotify_path(const struct path *path, __u32 mask){ return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH);}
static inline int fsnotify_file(struct file *file, __u32 mask){ /* * FMODE_NONOTIFY are fds generated by fanotify itself which should not * generate new events. We also don't want to generate events for * FMODE_PATH fds (involves open & close events) as they are just * handle creation / destruction events and not "real" file events. */ if (FMODE_FSNOTIFY_NONE(file->f_mode)) return 0;
return fsnotify_path(&file->f_path, mask);}
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
int fsnotify_open_perm_and_set_mode(struct file *file);
/* * fsnotify_file_area_perm - permission hook before access to file range */static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, const loff_t *ppos, size_t count){ /* * filesystem may be modified in the context of permission events * (e.g. by HSM filling a file on access), so sb freeze protection * must not be held. */ lockdep_assert_once(file_write_not_started(file));
if (!(perm_mask & (MAY_READ | MAY_WRITE | MAY_ACCESS))) return 0;
/* * read()/write() and other types of access generate pre-content events. */ if (unlikely(FMODE_FSNOTIFY_HSM(file->f_mode))) { int ret = fsnotify_pre_content(&file->f_path, ppos, count);
if (ret) return ret; }
if (!(perm_mask & MAY_READ) || likely(!FMODE_FSNOTIFY_ACCESS_PERM(file->f_mode))) return 0;
/* * read() also generates the legacy FS_ACCESS_PERM event, so content * scanners can inspect the content filled by pre-content event. */ return fsnotify_path(&file->f_path, FS_ACCESS_PERM);}
/* * fsnotify_mmap_perm - permission hook before mmap of file range */static inline int fsnotify_mmap_perm(struct file *file, int prot, const loff_t off, size_t len){ /* * mmap() generates only pre-content events. */ if (!file || likely(!FMODE_FSNOTIFY_HSM(file->f_mode))) return 0;
return fsnotify_pre_content(&file->f_path, &off, len);}
/* * fsnotify_truncate_perm - permission hook before file truncate */static inline int fsnotify_truncate_perm(const struct path *path, loff_t length){ struct inode *inode = d_inode(path->dentry);
if (!(inode->i_sb->s_iflags & SB_I_ALLOW_HSM) || !fsnotify_sb_has_priority_watchers(inode->i_sb, FSNOTIFY_PRIO_PRE_CONTENT)) return 0;
return fsnotify_pre_content(path, &length, 0);}
/* * fsnotify_file_perm - permission hook before file access (unknown range) */static inline int fsnotify_file_perm(struct file *file, int perm_mask){ return fsnotify_file_area_perm(file, perm_mask, NULL, 0);}
#elsestatic inline int fsnotify_open_perm_and_set_mode(struct file *file){ return 0;}
static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, const loff_t *ppos, size_t count){ return 0;}
static inline int fsnotify_mmap_perm(struct file *file, int prot, const loff_t off, size_t len){ return 0;}
static inline int fsnotify_truncate_perm(const struct path *path, loff_t length){ return 0;}
static inline int fsnotify_file_perm(struct file *file, int perm_mask){ return 0;}#endif
/* * fsnotify_link_count - inode's link count changed */static inline void fsnotify_link_count(struct inode *inode){ fsnotify_inode(inode, FS_ATTRIB);}
/* * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir */static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, const struct qstr *old_name, int isdir, struct inode *target, struct dentry *moved){ struct inode *source = moved->d_inode; u32 fs_cookie = fsnotify_get_cookie(); __u32 old_dir_mask = FS_MOVED_FROM; __u32 new_dir_mask = FS_MOVED_TO; __u32 rename_mask = FS_RENAME; const struct qstr *new_name = &moved->d_name; struct fsnotify_rename_data rd = { .moved = moved, .target = target, };
if (isdir) { old_dir_mask |= FS_ISDIR; new_dir_mask |= FS_ISDIR; rename_mask |= FS_ISDIR; }
/* Event with information about both old and new parent+name */ fsnotify_name(rename_mask, &rd, FSNOTIFY_EVENT_RENAME, old_dir, old_name, 0);
fsnotify_name(old_dir_mask, source, FSNOTIFY_EVENT_INODE, old_dir, old_name, fs_cookie); fsnotify_name(new_dir_mask, &rd, FSNOTIFY_EVENT_RENAME, new_dir, new_name, fs_cookie);
if (target) fsnotify_link_count(target); fsnotify_inode(source, FS_MOVE_SELF); audit_inode_child(new_dir, moved, AUDIT_TYPE_CHILD_CREATE);}
/* * fsnotify_inode_delete - and inode is being evicted from cache, clean up is needed */static inline void fsnotify_inode_delete(struct inode *inode){ __fsnotify_inode_delete(inode);}
/* * fsnotify_vfsmount_delete - a vfsmount is being destroyed, clean up is needed */static inline void fsnotify_vfsmount_delete(struct vfsmount *mnt){ __fsnotify_vfsmount_delete(mnt);}
static inline void fsnotify_mntns_delete(struct mnt_namespace *mntns){ __fsnotify_mntns_delete(mntns);}
/* * fsnotify_inoderemove - an inode is going away */static inline void fsnotify_inoderemove(struct inode *inode){ fsnotify_inode(inode, FS_DELETE_SELF); __fsnotify_inode_delete(inode);}
/* * fsnotify_create - 'name' was linked in * * Caller must make sure that dentry->d_name is stable. * Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate * ->d_inode later */static inline void fsnotify_create(struct inode *dir, struct dentry *dentry){ audit_inode_child(dir, dentry, AUDIT_TYPE_CHILD_CREATE);
fsnotify_dirent(dir, dentry, FS_CREATE);}
/* * fsnotify_link - new hardlink in 'inode' directory * * Caller must make sure that new_dentry->d_name is stable. * Note: We have to pass also the linked inode ptr as some filesystems leave * new_dentry->d_inode NULL and instantiate inode pointer later */static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *new_dentry){ fsnotify_link_count(inode); audit_inode_child(dir, new_dentry, AUDIT_TYPE_CHILD_CREATE);
fsnotify_name(FS_CREATE, inode, FSNOTIFY_EVENT_INODE, dir, &new_dentry->d_name, 0);}
/* * fsnotify_delete - @dentry was unlinked and unhashed * * Caller must make sure that dentry->d_name is stable. * * Note: unlike fsnotify_unlink(), we have to pass also the unlinked inode * as this may be called after d_delete() and old_dentry may be negative. */static inline void fsnotify_delete(struct inode *dir, struct inode *inode, struct dentry *dentry){ __u32 mask = FS_DELETE;
if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR;
fsnotify_name(mask, inode, FSNOTIFY_EVENT_INODE, dir, &dentry->d_name, 0);}
/** * d_delete_notify - delete a dentry and call fsnotify_delete() * @dentry: The dentry to delete * * This helper is used to guaranty that the unlinked inode cannot be found * by lookup of this name after fsnotify_delete() event has been delivered. */static inline void d_delete_notify(struct inode *dir, struct dentry *dentry){ struct inode *inode = d_inode(dentry);
ihold(inode); d_delete(dentry); fsnotify_delete(dir, inode, dentry); iput(inode);}
/* * fsnotify_unlink - 'name' was unlinked * * Caller must make sure that dentry->d_name is stable. */static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry){ if (WARN_ON_ONCE(d_is_negative(dentry))) return;
fsnotify_delete(dir, d_inode(dentry), dentry);}
/* * fsnotify_mkdir - directory 'name' was created * * Caller must make sure that dentry->d_name is stable. * Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate * ->d_inode later */static inline void fsnotify_mkdir(struct inode *dir, struct dentry *dentry){ audit_inode_child(dir, dentry, AUDIT_TYPE_CHILD_CREATE);
fsnotify_dirent(dir, dentry, FS_CREATE | FS_ISDIR);}
/* * fsnotify_rmdir - directory 'name' was removed * * Caller must make sure that dentry->d_name is stable. */static inline void fsnotify_rmdir(struct inode *dir, struct dentry *dentry){ if (WARN_ON_ONCE(d_is_negative(dentry))) return;
fsnotify_delete(dir, d_inode(dentry), dentry);}
/* * fsnotify_access - file was read */static inline void fsnotify_access(struct file *file){ fsnotify_file(file, FS_ACCESS);}
/* * fsnotify_modify - file was modified */static inline void fsnotify_modify(struct file *file){ fsnotify_file(file, FS_MODIFY);}
/* * fsnotify_open - file was opened */static inline void fsnotify_open(struct file *file){ __u32 mask = FS_OPEN;
if (file->f_flags & __FMODE_EXEC) mask |= FS_OPEN_EXEC;
fsnotify_file(file, mask);}
/* * fsnotify_close - file was closed */static inline void fsnotify_close(struct file *file){ __u32 mask = (file->f_mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE;
fsnotify_file(file, mask);}
/* * fsnotify_xattr - extended attributes were changed */static inline void fsnotify_xattr(struct dentry *dentry){ fsnotify_dentry(dentry, FS_ATTRIB);}
/* * fsnotify_change - notify_change event. file was modified and/or metadata * was changed. */static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid){ __u32 mask = 0;
if (ia_valid & ATTR_UID) mask |= FS_ATTRIB; if (ia_valid & ATTR_GID) mask |= FS_ATTRIB; if (ia_valid & ATTR_SIZE) mask |= FS_MODIFY;
/* both times implies a utime(s) call */ if ((ia_valid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) mask |= FS_ATTRIB; else if (ia_valid & ATTR_ATIME) mask |= FS_ACCESS; else if (ia_valid & ATTR_MTIME) mask |= FS_MODIFY;
if (ia_valid & ATTR_MODE) mask |= FS_ATTRIB;
if (mask) fsnotify_dentry(dentry, mask);}
static inline void fsnotify_mnt_attach(struct mnt_namespace *ns, struct vfsmount *mnt){ fsnotify_mnt(FS_MNT_ATTACH, ns, mnt);}
static inline void fsnotify_mnt_detach(struct mnt_namespace *ns, struct vfsmount *mnt){ fsnotify_mnt(FS_MNT_DETACH, ns, mnt);}
static inline void fsnotify_mnt_move(struct mnt_namespace *ns, struct vfsmount *mnt){ fsnotify_mnt(FS_MNT_MOVE, ns, mnt);}
#endif /* _LINUX_FS_NOTIFY_H */