From c7c8c61370be22f6308ac7c8707ce53c91ebeb10 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Wed, 29 Apr 2020 08:00:18 -0700 Subject: [PATCH] ANDROID: Incremental fs: Add setattr call As was, chmod would change the cached inode's mode, which would persist until the inode was uncached. Fix to change mode of backing file, but make sure mount files are read only, backing files are always writeable. Test: App no longer fails with incfs errors Bug: 154972299 Signed-off-by: Paul Lawrence Change-Id: I40517331f24329484387c6b880f1517f887b29f6 (cherry picked from commit fe4fae35fe307a15cacc5e6693a98bf5140e643b) --- fs/incfs/vfs.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index be66759fc405..1535c66ed860 100644 --- a/fs/incfs/vfs.c +++ b/fs/incfs/vfs.c @@ -68,6 +68,7 @@ static struct inode *alloc_inode(struct super_block *sb); static void free_inode(struct inode *inode); static void evict_inode(struct inode *inode); +static int incfs_setattr(struct dentry *dentry, struct iattr *ia); static ssize_t incfs_getxattr(struct dentry *d, const char *name, void *value, size_t size); static ssize_t incfs_setxattr(struct dentry *d, const char *name, @@ -98,7 +99,8 @@ static const struct inode_operations incfs_dir_inode_ops = { .rename = dir_rename_wrap, .unlink = dir_unlink, .link = dir_link, - .rmdir = dir_rmdir + .rmdir = dir_rmdir, + .setattr = incfs_setattr, }; static const struct file_operations incfs_dir_fops = { @@ -158,7 +160,7 @@ static const struct file_operations incfs_log_file_ops = { }; static const struct inode_operations incfs_file_inode_ops = { - .setattr = simple_setattr, + .setattr = incfs_setattr, .getattr = simple_getattr, .listxattr = incfs_listxattr }; @@ -369,6 +371,7 @@ static int inode_set(struct inode *inode, void *opaque) inode->i_mapping->a_ops = &incfs_address_space_ops; inode->i_op = &incfs_file_inode_ops; inode->i_fop = &incfs_file_ops; + inode->i_mode &= ~0222; } else if (S_ISDIR(inode->i_mode)) { inode->i_size = 0; inode->i_blocks = 1; @@ -2029,6 +2032,45 @@ static void evict_inode(struct inode *inode) clear_inode(inode); } +static int incfs_setattr(struct dentry *dentry, struct iattr *ia) +{ + struct dentry_info *di = get_incfs_dentry(dentry); + struct dentry *backing_dentry; + struct inode *backing_inode; + int error; + + if (ia->ia_valid & ATTR_SIZE) + return -EINVAL; + + if (!di) + return -EINVAL; + backing_dentry = di->backing_path.dentry; + if (!backing_dentry) + return -EINVAL; + + backing_inode = d_inode(backing_dentry); + + /* incfs files are readonly, but the backing files must be writeable */ + if (S_ISREG(backing_inode->i_mode)) { + if ((ia->ia_valid & ATTR_MODE) && (ia->ia_mode & 0222)) + return -EINVAL; + + ia->ia_mode |= 0222; + } + + inode_lock(d_inode(backing_dentry)); + error = notify_change(backing_dentry, ia, NULL); + inode_unlock(d_inode(backing_dentry)); + + if (error) + return error; + + if (S_ISREG(backing_inode->i_mode)) + ia->ia_mode &= ~0222; + + return simple_setattr(dentry, ia); +} + static ssize_t incfs_getxattr(struct dentry *d, const char *name, void *value, size_t size) {