Signed-off-by: Heinrich Schuchardt <xypron.glpk@xxxxxx>
---
fs/attr.c | 35 ++++++++++++++++++++++++++++++++---
fs/fat/file.c | 2 +-
fs/namei.c | 2 +-
fs/open.c | 10 ++++++----
include/linux/fs.h | 3 ++-
include/linux/fsnotify.h | 12 +++++++++---
6 files changed, 51 insertions(+), 13 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c
index 6530ced..894967e 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -168,13 +168,17 @@ void setattr_copy(struct inode *inode, const struct iattr *attr)
EXPORT_SYMBOL(setattr_copy);
/**
- * notify_change - modify attributes of a filesytem object
+ * do_notify_change - modify attributes of a filesytem object
+ * @path: path of object affected
* @dentry: object affected
* @iattr: new attributes
* @delegated_inode: returns inode, if the inode is delegated
*
* The caller must hold the i_mutex on the affected object.
*
+ * If NULL is passed in attribute path, fanotify events cannot be
+ * created.
+ *
* If notify_change discovers a delegation in need of breaking,
* it will return -EWOULDBLOCK and return a reference to the inode in
* delegated_inode. The caller should then break the delegation and
@@ -187,7 +191,8 @@ EXPORT_SYMBOL(setattr_copy);
* the file open for write, as there can be no conflicting delegation in
* that case.
*/
-int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
+int do_notify_change(struct path *path, struct dentry *dentry,
+ struct iattr *attr, struct inode **delegated_inode)
{
struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode;
@@ -268,11 +273,35 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
error = simple_setattr(dentry, attr);
if (!error) {
- fsnotify_change(dentry, ia_valid);
+ fsnotify_change(path, dentry, ia_valid);
ima_inode_post_setattr(dentry);
evm_inode_post_setattr(dentry, ia_valid);
}
return error;
}
+
+/*
+ * notify_change - modify attributes of a filesytem object
+ *
+ * This call will not create fanotify events. It should be replaced by
+ * notify_change_path where possible.
+ */
+int notify_change(struct dentry *dentry, struct iattr *attr,
+ struct inode **delegated_inode)
+{
+ return do_notify_change(NULL, dentry, attr, delegated_inode);
+}
EXPORT_SYMBOL(notify_change);
+
+/*
+ * notify_change_path - modify attributes of a filesytem object
+ *
+ * This call is a replacement for notify_change. It creates fanotify events.
+ */
+int notify_change_path(struct path *path, struct iattr *attr,
+ struct inode **delegated_inode)
+{
+ return do_notify_change(path, path->dentry, attr, delegated_inode);
+}
+EXPORT_SYMBOL(notify_change_path);
diff --git a/fs/fat/file.c b/fs/fat/file.c
index f2c73ae..d293a0b 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -101,7 +101,7 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
if (err)
goto out_unlock_inode;
- fsnotify_change(file->f_path.dentry, ia.ia_valid);
+ fsnotify_change(&file->f_path, file->f_path.dentry, ia.ia_valid);
if (sbi->options.sys_immutable) {
if (attr & ATTR_SYS)
inode->i_flags |= S_IMMUTABLE;
diff --git a/fs/namei.c b/fs/namei.c
index 43927d1..0a1f3b7 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2603,7 +2603,7 @@ static int handle_truncate(struct file *filp)
if (!error)
error = security_path_truncate(path);
if (!error) {
- error = do_truncate(path->dentry, 0,
+ error = do_truncate(path, 0,
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
filp);
}
diff --git a/fs/open.c b/fs/open.c
index d6fd3ac..390e322 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -34,11 +34,12 @@
#include "internal.h"
-int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
+int do_truncate(struct path *path, loff_t length, unsigned int time_attrs,
struct file *filp)
{
int ret;
struct iattr newattrs;
+ struct dentry *dentry = path->dentry;
/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
if (length < 0)
@@ -58,7 +59,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
mutex_lock(&dentry->d_inode->i_mutex);
/* Note any delegations or leases have already been broken: */
- ret = notify_change(dentry, &newattrs, NULL);
+ ret = notify_change_path(path, &newattrs, NULL);
mutex_unlock(&dentry->d_inode->i_mutex);
return ret;
}
@@ -104,7 +105,7 @@ long vfs_truncate(struct path *path, loff_t length)
if (!error)
error = security_path_truncate(path);
if (!error)
- error = do_truncate(path->dentry, length, 0, NULL);
+ error = do_truncate(path, length, 0, NULL);
put_write_and_out:
put_write_access(inode);
@@ -188,7 +189,8 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
if (!error)
error = security_path_truncate(&f.file->f_path);
if (!error)
- error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
+ error = do_truncate(&f.file->f_path, length,
+ ATTR_MTIME|ATTR_CTIME, f.file);
sb_end_write(inode->i_sb);
out_putf:
fdput(f);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 4111ee0..e5b0b05 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2031,7 +2031,7 @@ struct filename {
};
extern long vfs_truncate(struct path *, loff_t);
-extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
+extern int do_truncate(struct path *, loff_t start, unsigned int time_attrs,
struct file *filp);
extern int do_fallocate(struct file *file, int mode, loff_t offset,
loff_t len);
@@ -2253,6 +2253,7 @@ extern void emergency_remount(void);
extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
+extern int notify_change_path(struct path *, struct iattr *, struct inode **);
extern int inode_permission(struct inode *, int);
extern int generic_permission(struct inode *, int);
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index 1c804b0..0fd594b 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -276,7 +276,8 @@ static inline void fsnotify_xattr(struct dentry *dentry)
* 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)
+static inline void fsnotify_change(struct path *path, struct dentry *dentry,
+ unsigned int ia_valid)
{
struct inode *inode = dentry->d_inode;
__u32 mask = 0;
@@ -303,8 +304,13 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
if (S_ISDIR(inode->i_mode))
mask |= FS_ISDIR;
- fsnotify_parent(NULL, dentry, mask);
- fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
+ fsnotify_parent(path, dentry, mask);
+ if (path == NULL)
+ fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE,
+ NULL, 0);
+ else
+ fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH,
+ NULL, 0);
}
}
--
2.1.1