diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 271c4c4cb760..1461254f301d 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -118,11 +118,7 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, goto out; /* save current_cred and override it */ - saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data); - if (!saved_cred) { - err = -ENOMEM; - goto out; - } + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); @@ -131,7 +127,7 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (!err) sdcardfs_copy_and_fix_attrs(file_inode(file), file_inode(lower_file)); - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out: return err; } @@ -153,16 +149,12 @@ static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd, goto out; /* save current_cred and override it */ - saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data); - if (!saved_cred) { - err = -ENOMEM; - goto out; - } + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); if (lower_file->f_op->compat_ioctl) err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out: return err; } @@ -249,11 +241,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } /* save current_cred and override it */ - saved_cred = override_fsids(sbi, SDCARDFS_I(inode)->data); - if (!saved_cred) { - err = -ENOMEM; - goto out_err; - } + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); file->private_data = kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); @@ -283,7 +271,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); out_revert_cred: - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out_err: dput(parent); return err; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index b935568b20f8..57bdc686094f 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -22,6 +22,7 @@ #include #include +/* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_data *data) { @@ -49,6 +50,7 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, return old_cred; } +/* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred *old_cred) { const struct cred *cur_cred; @@ -76,10 +78,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), - SDCARDFS_I(dir)->data); - if (!saved_cred) - return -ENOMEM; + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -120,11 +119,53 @@ out: out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out_eacces: return err; } +#if 0 +static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_dir_dentry; + u64 file_size_save; + int err; + struct path lower_old_path, lower_new_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + file_size_save = i_size_read(d_inode(old_dentry)); + sdcardfs_get_lower_path(old_dentry, &lower_old_path); + sdcardfs_get_lower_path(new_dentry, &lower_new_path); + lower_old_dentry = lower_old_path.dentry; + lower_new_dentry = lower_new_path.dentry; + lower_dir_dentry = lock_parent(lower_new_dentry); + + err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), + lower_new_dentry, NULL); + if (err || !d_inode(lower_new_dentry)) + goto out; + + err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, d_inode(lower_new_dentry)); + fsstack_copy_inode_size(dir, d_inode(lower_new_dentry)); + set_nlink(d_inode(old_dentry), + sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink); + i_size_write(d_inode(new_dentry), file_size_save); +out: + unlock_dir(lower_dir_dentry); + sdcardfs_put_lower_path(old_dentry, &lower_old_path); + sdcardfs_put_lower_path(new_dentry, &lower_new_path); + REVERT_CRED(); + return err; +} +#endif + static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) { int err; @@ -141,10 +182,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), - SDCARDFS_I(dir)->data); - if (!saved_cred) - return -ENOMEM; + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -175,11 +213,43 @@ out: unlock_dir(lower_dir_dentry); dput(lower_dentry); sdcardfs_put_lower_path(dentry, &lower_path); - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out_eacces: return err; } +#if 0 +static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname); + if (err) + goto out; + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); + +out: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(); + return err; +} +#endif + static int touch(char *abs_path, mode_t mode) { struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); @@ -221,10 +291,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* save current_cred and override it */ - saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), - SDCARDFS_I(dir)->data); - if (!saved_cred) - return -ENOMEM; + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* check disk space */ parent_dentry = dget_parent(dentry); @@ -305,21 +372,13 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode if (make_nomedia_in_obb || ((pd->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { - revert_fsids(saved_cred); - saved_cred = override_fsids(sbi, - SDCARDFS_I(d_inode(dentry))->data); - if (!saved_cred) { - pr_err("sdcardfs: failed to set up .nomedia in %s: %d\n", - lower_path.dentry->d_name.name, - -ENOMEM); - goto out; - } + REVERT_CRED(saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", - lower_path.dentry->d_name.name, - touch_err); + lower_path.dentry->d_name.name, touch_err); goto out; } } @@ -332,7 +391,7 @@ out: out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); out_revert: - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out_eacces: return err; } @@ -352,10 +411,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), - SDCARDFS_I(dir)->data); - if (!saved_cred) - return -ENOMEM; + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry * the dentry on the original path should be deleted. @@ -380,11 +436,44 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) out: unlock_dir(lower_dir_dentry); sdcardfs_put_real_lower(dentry, &lower_path); - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out_eacces: return err; } +#if 0 +static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, + dev_t dev) +{ + int err; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev); + if (err) + goto out; + + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); + +out: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(); + return err; +} +#endif + /* * The locking rules in sdcardfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. @@ -413,10 +502,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* save current_cred and override it */ - saved_cred = override_fsids(SDCARDFS_SB(old_dir->i_sb), - SDCARDFS_I(new_dir)->data); - if (!saved_cred) - return -ENOMEM; + OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir)); sdcardfs_get_real_lower(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); @@ -463,7 +549,7 @@ out: dput(lower_new_dir_dentry); sdcardfs_put_real_lower(old_dentry, &lower_old_path); sdcardfs_put_lower_path(new_dentry, &lower_new_path); - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out_eacces: return err; } @@ -582,7 +668,33 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma if (IS_POSIXACL(inode)) pr_warn("%s: This may be undefined behavior...\n", __func__); err = generic_permission(&tmp, mask); + /* XXX + * Original sdcardfs code calls inode_permission(lower_inode,.. ) + * for checking inode permission. But doing such things here seems + * duplicated work, because the functions called after this func, + * such as vfs_create, vfs_unlink, vfs_rename, and etc, + * does exactly same thing, i.e., they calls inode_permission(). + * So we just let they do the things. + * If there are any security hole, just uncomment following if block. + */ +#if 0 + if (!err) { + /* + * Permission check on lower_inode(=EXT4). + * we check it with AID_MEDIA_RW permission + */ + struct inode *lower_inode; + + OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); + + lower_inode = sdcardfs_lower_inode(inode); + err = inode_permission(lower_inode, mask); + + REVERT_CRED(); + } +#endif return err; + } static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) @@ -673,10 +785,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, goto out_err; /* save current_cred and override it */ - saved_cred = override_fsids(SDCARDFS_SB(dentry->d_sb), - SDCARDFS_I(inode)->data); - if (!saved_cred) - return -ENOMEM; + OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -869,7 +978,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct out: sdcardfs_put_lower_path(dentry, &lower_path); - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out_err: return err; } @@ -953,6 +1062,13 @@ const struct inode_operations sdcardfs_dir_iops = { .setattr = sdcardfs_setattr_wrn, .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, + /* XXX Following operations are implemented, + * but FUSE(sdcard) or FAT does not support them + * These methods are *NOT* perfectly tested. + .symlink = sdcardfs_symlink, + .link = sdcardfs_link, + .mknod = sdcardfs_mknod, + */ }; const struct inode_operations sdcardfs_main_iops = { diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index beec63b107f4..98051996ad2c 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -426,12 +426,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), - SDCARDFS_I(dir)->data); - if (!saved_cred) { - ret = ERR_PTR(-ENOMEM); - goto out_err; - } + OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(parent, &lower_parent_path); @@ -462,7 +457,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, out: sdcardfs_put_lower_path(parent, &lower_parent_path); - revert_fsids(saved_cred); + REVERT_CRED(saved_cred); out_err: dput(parent); return ret; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index ec2290a16d3c..826afb5c7e88 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -88,6 +88,31 @@ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) +/* OVERRIDE_CRED() and REVERT_CRED() + * OVERRIDE_CRED() + * backup original task->cred + * and modifies task->cred->fsuid/fsgid to specified value. + * REVERT_CRED() + * restore original task->cred->fsuid/fsgid. + * These two macro should be used in pair, and OVERRIDE_CRED() should be + * placed at the beginning of a function, right after variable declaration. + */ +#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ + do { \ + saved_cred = override_fsids(sdcardfs_sbi, info->data); \ + if (!saved_cred) \ + return -ENOMEM; \ + } while (0) + +#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ + do { \ + saved_cred = override_fsids(sdcardfs_sbi, info->data); \ + if (!saved_cred) \ + return ERR_PTR(-ENOMEM); \ + } while (0) + +#define REVERT_CRED(saved_cred) revert_fsids(saved_cred) + /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions