mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-05 00:41:03 +01:00
394 lines
12 KiB
Diff
394 lines
12 KiB
Diff
Subject: VFS: new fsetattr() file operation
|
|
|
|
From: Miklos Szeredi <mszeredi@suse.cz>
|
|
|
|
Add a new file operation: f_op->fsetattr(), that is invoked by
|
|
ftruncate, fchmod, fchown and utimensat. Fall back to i_op->setattr()
|
|
if it is not defined.
|
|
|
|
For the reasons why we need this, see patch adding fgetattr().
|
|
|
|
ftruncate() already passed the open file to the filesystem via the
|
|
ia_file member of struct iattr. However it is cleaner to have a
|
|
separate file operation for this, so remove ia_file, ATTR_FILE and
|
|
convert existing users: fuse and AFS.
|
|
|
|
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> ---
|
|
Signed-off-by: John Johansen <jjohansen@suse.de> ---
|
|
|
|
---
|
|
fs/afs/dir.c | 1 +
|
|
fs/afs/file.c | 1 +
|
|
fs/afs/inode.c | 19 +++++++++++++++----
|
|
fs/afs/internal.h | 1 +
|
|
fs/attr.c | 19 +++++++++++++++----
|
|
fs/fuse/dir.c | 20 +++++++++-----------
|
|
fs/fuse/file.c | 7 +++++++
|
|
fs/fuse/fuse_i.h | 4 ++++
|
|
fs/open.c | 20 ++++++++------------
|
|
fs/utimes.c | 2 +-
|
|
include/linux/fs.h | 10 ++--------
|
|
11 files changed, 64 insertions(+), 40 deletions(-)
|
|
|
|
--- a/fs/afs/dir.c
|
|
+++ b/fs/afs/dir.c
|
|
@@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil
|
|
.release = afs_release,
|
|
.readdir = afs_readdir,
|
|
.lock = afs_lock,
|
|
+ .fsetattr = afs_fsetattr,
|
|
};
|
|
|
|
const struct inode_operations afs_dir_inode_operations = {
|
|
--- a/fs/afs/file.c
|
|
+++ b/fs/afs/file.c
|
|
@@ -36,6 +36,7 @@ const struct file_operations afs_file_op
|
|
.fsync = afs_fsync,
|
|
.lock = afs_lock,
|
|
.flock = afs_flock,
|
|
+ .fsetattr = afs_fsetattr,
|
|
};
|
|
|
|
const struct inode_operations afs_file_inode_operations = {
|
|
--- a/fs/afs/inode.c
|
|
+++ b/fs/afs/inode.c
|
|
@@ -358,7 +358,8 @@ void afs_clear_inode(struct inode *inode
|
|
/*
|
|
* set the attributes of an inode
|
|
*/
|
|
-int afs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
+static int afs_do_setattr(struct dentry *dentry, struct iattr *attr,
|
|
+ struct file *file)
|
|
{
|
|
struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);
|
|
struct key *key;
|
|
@@ -380,8 +381,8 @@ int afs_setattr(struct dentry *dentry, s
|
|
afs_writeback_all(vnode);
|
|
}
|
|
|
|
- if (attr->ia_valid & ATTR_FILE) {
|
|
- key = attr->ia_file->private_data;
|
|
+ if (file) {
|
|
+ key = file->private_data;
|
|
} else {
|
|
key = afs_request_key(vnode->volume->cell);
|
|
if (IS_ERR(key)) {
|
|
@@ -391,10 +392,20 @@ int afs_setattr(struct dentry *dentry, s
|
|
}
|
|
|
|
ret = afs_vnode_setattr(vnode, key, attr);
|
|
- if (!(attr->ia_valid & ATTR_FILE))
|
|
+ if (!file)
|
|
key_put(key);
|
|
|
|
error:
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
}
|
|
+
|
|
+int afs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
+{
|
|
+ return afs_do_setattr(dentry, attr, NULL);
|
|
+}
|
|
+
|
|
+int afs_fsetattr(struct file *file, struct iattr *attr)
|
|
+{
|
|
+ return afs_do_setattr(file->f_path.dentry, attr, file);
|
|
+}
|
|
--- a/fs/afs/internal.h
|
|
+++ b/fs/afs/internal.h
|
|
@@ -550,6 +550,7 @@ extern void afs_zap_data(struct afs_vnod
|
|
extern int afs_validate(struct afs_vnode *, struct key *);
|
|
extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
|
|
extern int afs_setattr(struct dentry *, struct iattr *);
|
|
+extern int afs_fsetattr(struct file *, struct iattr *);
|
|
extern void afs_clear_inode(struct inode *);
|
|
|
|
/*
|
|
--- a/fs/attr.c
|
|
+++ b/fs/attr.c
|
|
@@ -100,8 +100,8 @@ int inode_setattr(struct inode * inode,
|
|
}
|
|
EXPORT_SYMBOL(inode_setattr);
|
|
|
|
-int notify_change(struct dentry *dentry, struct vfsmount *mnt,
|
|
- struct iattr *attr)
|
|
+int fnotify_change(struct dentry *dentry, struct vfsmount *mnt,
|
|
+ struct iattr *attr, struct file *file)
|
|
{
|
|
struct inode *inode = dentry->d_inode;
|
|
mode_t mode = inode->i_mode;
|
|
@@ -160,8 +160,12 @@ int notify_change(struct dentry *dentry,
|
|
|
|
if (inode->i_op && inode->i_op->setattr) {
|
|
error = security_inode_setattr(dentry, mnt, attr);
|
|
- if (!error)
|
|
- error = inode->i_op->setattr(dentry, attr);
|
|
+ if (!error) {
|
|
+ if (file && file->f_op && file->f_op->fsetattr)
|
|
+ error = file->f_op->fsetattr(file, attr);
|
|
+ else
|
|
+ error = inode->i_op->setattr(dentry, attr);
|
|
+ }
|
|
} else {
|
|
error = inode_change_ok(inode, attr);
|
|
if (!error)
|
|
@@ -183,5 +187,12 @@ int notify_change(struct dentry *dentry,
|
|
|
|
return error;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(fnotify_change);
|
|
+
|
|
+int notify_change(struct dentry *dentry, struct vfsmount *mnt,
|
|
+ struct iattr *attr)
|
|
+{
|
|
+ return fnotify_change(dentry, mnt, attr, NULL);
|
|
+}
|
|
|
|
EXPORT_SYMBOL(notify_change);
|
|
--- a/fs/fuse/dir.c
|
|
+++ b/fs/fuse/dir.c
|
|
@@ -1064,21 +1064,22 @@ static int fuse_dir_fsync(struct file *f
|
|
return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
|
|
}
|
|
|
|
-static bool update_mtime(unsigned ivalid)
|
|
+static bool update_mtime(unsigned ivalid, bool have_file)
|
|
{
|
|
/* Always update if mtime is explicitly set */
|
|
if (ivalid & ATTR_MTIME_SET)
|
|
return true;
|
|
|
|
/* If it's an open(O_TRUNC) or an ftruncate(), don't update */
|
|
- if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE)))
|
|
+ if ((ivalid & ATTR_SIZE) && ((ivalid & ATTR_OPEN) || have_file))
|
|
return false;
|
|
|
|
/* In all other cases update */
|
|
return true;
|
|
}
|
|
|
|
-static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
|
|
+static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
|
|
+ bool have_file)
|
|
{
|
|
unsigned ivalid = iattr->ia_valid;
|
|
|
|
@@ -1097,7 +1098,7 @@ static void iattr_to_fattr(struct iattr
|
|
if (!(ivalid & ATTR_ATIME_SET))
|
|
arg->valid |= FATTR_ATIME_NOW;
|
|
}
|
|
- if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
|
|
+ if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, have_file)) {
|
|
arg->valid |= FATTR_MTIME;
|
|
arg->mtime = iattr->ia_mtime.tv_sec;
|
|
arg->mtimensec = iattr->ia_mtime.tv_nsec;
|
|
@@ -1158,8 +1159,8 @@ void fuse_release_nowrite(struct inode *
|
|
* vmtruncate() doesn't allow for this case, so do the rlimit checking
|
|
* and the actual truncation by hand.
|
|
*/
|
|
-static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
|
|
- struct file *file)
|
|
+int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
|
|
+ struct file *file)
|
|
{
|
|
struct inode *inode = entry->d_inode;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
@@ -1203,7 +1204,7 @@ static int fuse_do_setattr(struct dentry
|
|
|
|
memset(&inarg, 0, sizeof(inarg));
|
|
memset(&outarg, 0, sizeof(outarg));
|
|
- iattr_to_fattr(attr, &inarg);
|
|
+ iattr_to_fattr(attr, &inarg, file != NULL);
|
|
if (file) {
|
|
struct fuse_file *ff = file->private_data;
|
|
inarg.valid |= FATTR_FH;
|
|
@@ -1273,10 +1274,7 @@ error:
|
|
|
|
static int fuse_setattr(struct dentry *entry, struct iattr *attr)
|
|
{
|
|
- if (attr->ia_valid & ATTR_FILE)
|
|
- return fuse_do_setattr(entry, attr, attr->ia_file);
|
|
- else
|
|
- return fuse_do_setattr(entry, attr, NULL);
|
|
+ return fuse_do_setattr(entry, attr, NULL);
|
|
}
|
|
|
|
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
|
|
--- a/fs/fuse/file.c
|
|
+++ b/fs/fuse/file.c
|
|
@@ -1458,6 +1458,11 @@ static loff_t fuse_file_llseek(struct fi
|
|
return retval;
|
|
}
|
|
|
|
+static int fuse_fsetattr(struct file *file, struct iattr *attr)
|
|
+{
|
|
+ return fuse_do_setattr(file->f_path.dentry, attr, file);
|
|
+}
|
|
+
|
|
static const struct file_operations fuse_file_operations = {
|
|
.llseek = fuse_file_llseek,
|
|
.read = do_sync_read,
|
|
@@ -1471,6 +1476,7 @@ static const struct file_operations fuse
|
|
.fsync = fuse_fsync,
|
|
.lock = fuse_file_lock,
|
|
.flock = fuse_file_flock,
|
|
+ .fsetattr = fuse_fsetattr,
|
|
.splice_read = generic_file_splice_read,
|
|
};
|
|
|
|
@@ -1484,6 +1490,7 @@ static const struct file_operations fuse
|
|
.fsync = fuse_fsync,
|
|
.lock = fuse_file_lock,
|
|
.flock = fuse_file_flock,
|
|
+ .fsetattr = fuse_fsetattr,
|
|
/* no mmap and splice_read */
|
|
};
|
|
|
|
--- a/fs/fuse/fuse_i.h
|
|
+++ b/fs/fuse/fuse_i.h
|
|
@@ -543,6 +543,10 @@ void fuse_truncate(struct address_space
|
|
*/
|
|
int fuse_dev_init(void);
|
|
|
|
+
|
|
+int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
|
|
+ struct file *file);
|
|
+
|
|
/**
|
|
* Cleanup the client device
|
|
*/
|
|
--- a/fs/open.c
|
|
+++ b/fs/open.c
|
|
@@ -207,16 +207,12 @@ int do_truncate(struct dentry *dentry, s
|
|
|
|
newattrs.ia_size = length;
|
|
newattrs.ia_valid = ATTR_SIZE | time_attrs;
|
|
- if (filp) {
|
|
- newattrs.ia_file = filp;
|
|
- newattrs.ia_valid |= ATTR_FILE;
|
|
- }
|
|
|
|
/* Remove suid/sgid on truncate too */
|
|
newattrs.ia_valid |= should_remove_suid(dentry);
|
|
|
|
mutex_lock(&dentry->d_inode->i_mutex);
|
|
- err = notify_change(dentry, mnt, &newattrs);
|
|
+ err = fnotify_change(dentry, mnt, &newattrs, filp);
|
|
mutex_unlock(&dentry->d_inode->i_mutex);
|
|
return err;
|
|
}
|
|
@@ -591,7 +587,7 @@ asmlinkage long sys_fchmod(unsigned int
|
|
mode = inode->i_mode;
|
|
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
|
|
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
|
- err = notify_change(dentry, file->f_path.mnt, &newattrs);
|
|
+ err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file);
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
out_drop_write:
|
|
@@ -645,7 +641,7 @@ asmlinkage long sys_chmod(const char __u
|
|
}
|
|
|
|
static int chown_common(struct dentry * dentry, struct vfsmount *mnt,
|
|
- uid_t user, gid_t group)
|
|
+ uid_t user, gid_t group, struct file *file)
|
|
{
|
|
struct inode * inode;
|
|
int error;
|
|
@@ -672,7 +668,7 @@ static int chown_common(struct dentry *
|
|
newattrs.ia_valid |=
|
|
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
|
|
mutex_lock(&inode->i_mutex);
|
|
- error = notify_change(dentry, mnt, &newattrs);
|
|
+ error = fnotify_change(dentry, mnt, &newattrs, file);
|
|
mutex_unlock(&inode->i_mutex);
|
|
out:
|
|
return error;
|
|
@@ -689,7 +685,7 @@ asmlinkage long sys_chown(const char __u
|
|
error = mnt_want_write(nd.path.mnt);
|
|
if (error)
|
|
goto out_release;
|
|
- error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
|
|
+ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL);
|
|
mnt_drop_write(nd.path.mnt);
|
|
out_release:
|
|
path_put(&nd.path);
|
|
@@ -714,7 +710,7 @@ asmlinkage long sys_fchownat(int dfd, co
|
|
error = mnt_want_write(nd.path.mnt);
|
|
if (error)
|
|
goto out_release;
|
|
- error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
|
|
+ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL);
|
|
mnt_drop_write(nd.path.mnt);
|
|
out_release:
|
|
path_put(&nd.path);
|
|
@@ -733,7 +729,7 @@ asmlinkage long sys_lchown(const char __
|
|
error = mnt_want_write(nd.path.mnt);
|
|
if (error)
|
|
goto out_release;
|
|
- error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
|
|
+ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL);
|
|
mnt_drop_write(nd.path.mnt);
|
|
out_release:
|
|
path_put(&nd.path);
|
|
@@ -757,7 +753,7 @@ asmlinkage long sys_fchown(unsigned int
|
|
goto out_fput;
|
|
dentry = file->f_path.dentry;
|
|
audit_inode(NULL, dentry);
|
|
- error = chown_common(dentry, file->f_path.mnt, user, group);
|
|
+ error = chown_common(dentry, file->f_path.mnt, user, group, file);
|
|
mnt_drop_write(file->f_path.mnt);
|
|
out_fput:
|
|
fput(file);
|
|
--- a/fs/utimes.c
|
|
+++ b/fs/utimes.c
|
|
@@ -151,7 +151,7 @@ long do_utimes(int dfd, char __user *fil
|
|
}
|
|
}
|
|
mutex_lock(&inode->i_mutex);
|
|
- error = notify_change(path.dentry, path.mnt, &newattrs);
|
|
+ error = fnotify_change(path.dentry, path.mnt, &newattrs, f);
|
|
mutex_unlock(&inode->i_mutex);
|
|
mnt_drop_write_and_out:
|
|
mnt_drop_write(path.mnt);
|
|
--- a/include/linux/fs.h
|
|
+++ b/include/linux/fs.h
|
|
@@ -330,7 +330,6 @@ typedef void (dio_iodone_t)(struct kiocb
|
|
#define ATTR_ATTR_FLAG 1024
|
|
#define ATTR_KILL_SUID 2048
|
|
#define ATTR_KILL_SGID 4096
|
|
-#define ATTR_FILE 8192
|
|
#define ATTR_KILL_PRIV 16384
|
|
#define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */
|
|
|
|
@@ -352,13 +351,6 @@ struct iattr {
|
|
struct timespec ia_atime;
|
|
struct timespec ia_mtime;
|
|
struct timespec ia_ctime;
|
|
-
|
|
- /*
|
|
- * Not an attribute, but an auxilary info for filesystems wanting to
|
|
- * implement an ftruncate() like method. NOTE: filesystem should
|
|
- * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
|
|
- */
|
|
- struct file *ia_file;
|
|
};
|
|
|
|
/*
|
|
@@ -1244,6 +1236,7 @@ struct file_operations {
|
|
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
|
|
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
|
|
int (*setlease)(struct file *, long, struct file_lock **);
|
|
+ int (*fsetattr)(struct file *, struct iattr *);
|
|
};
|
|
|
|
struct inode_operations {
|
|
@@ -1752,6 +1745,7 @@ extern int do_remount_sb(struct super_bl
|
|
extern sector_t bmap(struct inode *, sector_t);
|
|
#endif
|
|
extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *);
|
|
+extern int fnotify_change(struct dentry *, struct vfsmount *, struct iattr *, struct file *);
|
|
extern int permission(struct inode *, int, struct nameidata *);
|
|
extern int generic_permission(struct inode *, int,
|
|
int (*check_acl)(struct inode *, int));
|