static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) { struct inode *inode = file->f_path.dentry->d_inode; struct hfsplus_inode_info *hip = HFSPLUS_I(inode); unsigned int flags; int err = 0; err = mnt_want_write(file->f_path.mnt); if (err) goto out; if (!inode_owner_or_capable(inode)) { err = -EACCES; goto out_drop_write; } if (get_user(flags, user_flags)) { err = -EFAULT; goto out_drop_write; } mutex_lock(&inode->i_mutex); if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) || inode->i_flags & (S_IMMUTABLE|S_APPEND)) { if (!capable(CAP_LINUX_IMMUTABLE)) { err = -EPERM; goto out_unlock_inode; } } /* don't silently ignore unsupported ext2 flags */ if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { err = -EOPNOTSUPP; goto out_unlock_inode; } if (flags & FS_IMMUTABLE_FL) inode->i_flags |= S_IMMUTABLE; else inode->i_flags &= ~S_IMMUTABLE; if (flags & FS_APPEND_FL) inode->i_flags |= S_APPEND; else inode->i_flags &= ~S_APPEND; if (flags & FS_NODUMP_FL) hip->userflags |= HFSPLUS_FLG_NODUMP; else hip->userflags &= ~HFSPLUS_FLG_NODUMP; inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); out_unlock_inode: mutex_unlock(&inode->i_mutex); out_drop_write: mnt_drop_write(file->f_path.mnt); out: return err; }
static long do_sys_truncate(const char __user *pathname, loff_t length) { struct path path; struct inode *inode; int error; error = -EINVAL; if (length < 0) /* sorry, but loff_t says... */ goto out; error = user_path(pathname, &path); if (error) goto out; inode = path.dentry->d_inode; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ error = -EISDIR; if (S_ISDIR(inode->i_mode)) goto dput_and_out; error = -EINVAL; if (!S_ISREG(inode->i_mode)) goto dput_and_out; error = mnt_want_write(path.mnt); if (error) goto dput_and_out; error = inode_permission(inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; error = -EPERM; if (IS_APPEND(inode)) goto mnt_drop_write_and_out; error = get_write_access(inode); if (error) goto mnt_drop_write_and_out; /* * Make sure that there are no leases. get_write_access() protects * against the truncate racing with a lease-granting setlease(). */ error = break_lease(inode, O_WRONLY); if (error) goto put_write_and_out; error = locks_verify_truncate(inode, NULL, length); if (!error) error = security_path_truncate(&path); if (!error) error = do_truncate(path.dentry, length, 0, NULL); put_write_and_out: put_write_access(inode); mnt_drop_write_and_out: mnt_drop_write(path.mnt); dput_and_out: path_put(&path); out: return error; }
STATIC int xfs_attrmulti_by_handle( struct file *parfilp, void __user *arg) { int error; xfs_attr_multiop_t *ops; xfs_fsop_attrmulti_handlereq_t am_hreq; struct dentry *dentry; unsigned int i, size; char *attr_name; if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t))) return -XFS_ERROR(EFAULT); dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq); if (IS_ERR(dentry)) return PTR_ERR(dentry); error = E2BIG; size = am_hreq.opcount * sizeof(xfs_attr_multiop_t); if (!size || size > 16 * PAGE_SIZE) goto out_dput; ops = memdup_user(am_hreq.ops, size); if (IS_ERR(ops)) { error = PTR_ERR(ops); goto out_dput; } attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL); if (!attr_name) goto out_kfree_ops; error = 0; for (i = 0; i < am_hreq.opcount; i++) { ops[i].am_error = strncpy_from_user(attr_name, ops[i].am_attrname, MAXNAMELEN); if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN) error = -ERANGE; if (ops[i].am_error < 0) break; switch (ops[i].am_opcode) { case ATTR_OP_GET: ops[i].am_error = xfs_attrmulti_attr_get( dentry->d_inode, attr_name, ops[i].am_attrvalue, &ops[i].am_length, ops[i].am_flags); break; case ATTR_OP_SET: ops[i].am_error = mnt_want_write(parfilp->f_path.mnt); if (ops[i].am_error) break; ops[i].am_error = xfs_attrmulti_attr_set( dentry->d_inode, attr_name, ops[i].am_attrvalue, ops[i].am_length, ops[i].am_flags); mnt_drop_write(parfilp->f_path.mnt); break; case ATTR_OP_REMOVE: ops[i].am_error = mnt_want_write(parfilp->f_path.mnt); if (ops[i].am_error) break; ops[i].am_error = xfs_attrmulti_attr_remove( dentry->d_inode, attr_name, ops[i].am_flags); mnt_drop_write(parfilp->f_path.mnt); break; default: ops[i].am_error = EINVAL; } } if (copy_to_user(am_hreq.ops, ops, size)) error = XFS_ERROR(EFAULT); kfree(attr_name); out_kfree_ops: kfree(ops); out_dput: dput(dentry); return -error; }
long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct jfs_inode_info *jfs_inode = JFS_IP(inode); unsigned int flags; switch (cmd) { case JFS_IOC_GETFLAGS: jfs_get_inode_flags(jfs_inode); flags = jfs_inode->mode2 & JFS_FL_USER_VISIBLE; flags = jfs_map_ext2(flags, 0); return put_user(flags, (int __user *) arg); case JFS_IOC_SETFLAGS: { unsigned int oldflags; int err; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (!is_owner_or_cap(inode)) { err = -EACCES; goto setflags_out; } if (get_user(flags, (int __user *) arg)) { err = -EFAULT; goto setflags_out; } flags = jfs_map_ext2(flags, 1); if (!S_ISDIR(inode->i_mode)) flags &= ~JFS_DIRSYNC_FL; /* Is it quota file? Do not allow user to mess with it */ if (IS_NOQUOTA(inode)) { err = -EPERM; goto setflags_out; } /* Lock against other parallel changes of flags */ mutex_lock(&inode->i_mutex); jfs_get_inode_flags(jfs_inode); oldflags = jfs_inode->mode2; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. */ if ((oldflags & JFS_IMMUTABLE_FL) || ((flags ^ oldflags) & (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) { if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); err = -EPERM; goto setflags_out; } } flags = flags & JFS_FL_USER_MODIFIABLE; flags |= oldflags & ~JFS_FL_USER_MODIFIABLE; jfs_inode->mode2 = flags; jfs_set_inode_flags(inode); mutex_unlock(&inode->i_mutex); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); setflags_out: mnt_drop_write(filp->f_path.mnt); return err; } default: return -ENOTTY; } }
/* * The locking rules in wrapfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. */ static int wrapfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *lower_old_dentry = NULL; struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; struct dentry *trap = NULL; struct path lower_old_path, lower_new_path; if(wrapfs_get_debug(new_dir->i_sb) & DEBUG_INODE) DEBUG_MESG("Enter"); wrapfs_get_lower_path(old_dentry, &lower_old_path); wrapfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; lower_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); /* source should not be ancestor of target */ if (trap == lower_old_dentry) { err = -EINVAL; goto out; } /* target should not be ancestor of source */ if (trap == lower_new_dentry) { err = -ENOTEMPTY; goto out; } err = mnt_want_write(lower_old_path.mnt); if (err) goto out; err = mnt_want_write(lower_new_path.mnt); if (err) goto out_drop_old_write; err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, lower_new_dir_dentry->d_inode, lower_new_dentry); if (err) goto out_err; fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); if (new_dir != old_dir) { fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); } out_err: mnt_drop_write(lower_new_path.mnt); out_drop_old_write: mnt_drop_write(lower_old_path.mnt); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); dput(lower_new_dir_dentry); wrapfs_put_lower_path(old_dentry, &lower_old_path); wrapfs_put_lower_path(new_dentry, &lower_new_path); if(wrapfs_get_debug(new_dir->i_sb) & DEBUG_INODE) DEBUG_RETURN("Exit", err); return err; }
/* * The locking rules in sdcardfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. */ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *lower_old_dentry = NULL; struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; struct dentry *trap = NULL; struct path lower_old_path, lower_new_path; OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb)); 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_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); /* source should not be ancestor of target */ if (trap == lower_old_dentry) { err = -EINVAL; goto out; } /* target should not be ancestor of source */ if (trap == lower_new_dentry) { err = -ENOTEMPTY; goto out; } err = mnt_want_write(lower_old_path.mnt); if (err) goto out; err = mnt_want_write(lower_new_path.mnt); if (err) goto out_drop_old_write; err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, lower_new_dir_dentry->d_inode, lower_new_dentry); if (err) goto out_err; /* Copy attrs from lower dir, but i_uid/i_gid */ fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); fix_fat_permission(new_dir); if (new_dir != old_dir) { fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); fix_fat_permission(old_dir); } out_err: mnt_drop_write(lower_new_path.mnt); out_drop_old_write: mnt_drop_write(lower_old_path.mnt); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); dput(lower_new_dir_dentry); sdcardfs_put_lower_path(old_dentry, &lower_old_path); sdcardfs_put_lower_path(new_dentry, &lower_new_path); REVERT_CRED(); return err; }
static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path lowerpath; struct path upperpath; struct inode *root_inode; struct dentry *root_dentry; struct ovl_entry *oe; struct ovl_fs *ufs; int err; err = -ENOMEM; ufs = kmalloc(sizeof(struct ovl_fs), GFP_KERNEL); if (!ufs) goto out; err = ovl_parse_opt((char *) data, &ufs->config); if (err) goto out_free_ufs; err = -EINVAL; if (!ufs->config.upperdir || !ufs->config.lowerdir) { printk(KERN_ERR "overlayfs: missing upperdir or lowerdir\n"); goto out_free_config; } oe = ovl_alloc_entry(); if (oe == NULL) goto out_free_config; root_inode = ovl_new_inode(sb, S_IFDIR, oe); if (!root_inode) goto out_free_oe; err = kern_path(ufs->config.upperdir, LOOKUP_FOLLOW, &upperpath); if (err) goto out_put_root; err = kern_path(ufs->config.lowerdir, LOOKUP_FOLLOW, &lowerpath); if (err) goto out_put_upperpath; err = -ENOTDIR; if (!S_ISDIR(upperpath.dentry->d_inode->i_mode) || !S_ISDIR(lowerpath.dentry->d_inode->i_mode)) goto out_put_lowerpath; sb->s_stack_depth = max(upperpath.mnt->mnt_sb->s_stack_depth, lowerpath.mnt->mnt_sb->s_stack_depth) + 1; err = -EINVAL; if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { printk(KERN_ERR "overlayfs: maximum fs stacking depth exceeded\n"); goto out_put_lowerpath; } ufs->upper_mnt = clone_private_mount(&upperpath); err = PTR_ERR(ufs->upper_mnt); if (IS_ERR(ufs->upper_mnt)) { printk(KERN_ERR "overlayfs: failed to clone upperpath\n"); goto out_put_lowerpath; } ufs->lower_mnt = clone_private_mount(&lowerpath); err = PTR_ERR(ufs->lower_mnt); if (IS_ERR(ufs->lower_mnt)) { printk(KERN_ERR "overlayfs: failed to clone lowerpath\n"); goto out_put_upper_mnt; } /* * Make lower_mnt R/O. That way fchmod/fchown on lower file * will fail instead of modifying lower fs. */ ufs->lower_mnt->mnt_flags |= MNT_READONLY; /* If the upper fs is r/o, we mark overlayfs r/o too */ if (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY) sb->s_flags |= MS_RDONLY; if (!(sb->s_flags & MS_RDONLY)) { err = mnt_want_write(ufs->upper_mnt); if (err) goto out_put_lower_mnt; } err = -ENOMEM; root_dentry = d_alloc_root(root_inode); if (!root_dentry) goto out_drop_write; mntput(upperpath.mnt); mntput(lowerpath.mnt); oe->__upperdentry = dget(upperpath.dentry); oe->lowerdentry = lowerpath.dentry; root_dentry->d_fsdata = oe; root_dentry->d_op = &ovl_dentry_operations; sb->s_op = &ovl_super_operations; sb->s_root = root_dentry; sb->s_fs_info = ufs; return 0; out_drop_write: if (!(sb->s_flags & MS_RDONLY)) mnt_drop_write(ufs->upper_mnt); out_put_lower_mnt: mntput(ufs->lower_mnt); out_put_upper_mnt: mntput(ufs->upper_mnt); out_put_lowerpath: path_put(&lowerpath); out_put_upperpath: path_put(&upperpath); out_put_root: iput(root_inode); out_free_oe: kfree(oe); out_free_config: kfree(ufs->config.lowerdir); kfree(ufs->config.upperdir); out_free_ufs: kfree(ufs); out: return err; }
long hdd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct hdd_inode_info *hi = HDD_I(inode); unsigned int flags; int ret; switch (cmd) { case HDD_IOC_GETFLAGS: hdd_get_inode_flags(hi); flags = hi->i_flags & HDD_FL_USER_VISIBLE; return put_user(flags, (int __user *) arg); case HDD_IOC_SETFLAGS: { unsigned int oldflags; ret = mnt_want_write(filp->f_path.mnt); if (ret) return ret; if (!is_owner_or_cap(inode)) { ret = -EACCES; goto setflags_out; } if (get_user(flags, (int __user *) arg)) { ret = -EFAULT; goto setflags_out; } flags = hdd_mask_flags(inode->i_mode, flags); mutex_lock(&inode->i_mutex); if (IS_NOQUOTA(inode)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto setflags_out; } oldflags = hi->i_flags; if ((flags ^ oldflags) & (HDD_APPEND_FL | HDD_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto setflags_out; } } flags = flags & HDD_FL_USER_MODIFIABLE; flags |= oldflags & ~HDD_FL_USER_MODIFIABLE; hi->i_flags = flags; mutex_unlock(&inode->i_mutex); hdd_set_inode_flags(inode); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); setflags_out: mnt_drop_write(filp->f_path.mnt); return ret; } default: return -ENOTTY; } }
static int utimes_common(struct path *path, struct timespec *times) { int error; struct iattr newattrs; struct inode *inode = path->dentry->d_inode; error = mnt_want_write(path->mnt); if (error) goto out; if (times && times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) times = NULL; newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { if (times[0].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_ATIME; else if (times[0].tv_nsec != UTIME_NOW) { newattrs.ia_atime.tv_sec = times[0].tv_sec; newattrs.ia_atime.tv_nsec = times[0].tv_nsec; newattrs.ia_valid |= ATTR_ATIME_SET; } if (times[1].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_MTIME; else if (times[1].tv_nsec != UTIME_NOW) { newattrs.ia_mtime.tv_sec = times[1].tv_sec; newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } /* * Tell inode_change_ok(), that this is an explicit time * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET * were used. */ newattrs.ia_valid |= ATTR_TIMES_SET; } else { /* * If times is NULL (or both times are UTIME_NOW), * then we need to check permissions, because * inode_change_ok() won't do it. */ error = -EACCES; if (IS_IMMUTABLE(inode)) goto mnt_drop_write_and_out; if (!is_owner_or_cap(inode)) { error = inode_permission(inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; } } mutex_lock(&inode->i_mutex); error = notify_change(path->dentry, &newattrs); mutex_unlock(&inode->i_mutex); mnt_drop_write_and_out: mnt_drop_write(path->mnt); out: return error; }
/* * The locking rules in esdfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. */ static int esdfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *lower_old_dentry = NULL; struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; struct dentry *trap = NULL; struct path lower_old_path, lower_new_path; int mask; const struct cred *creds = esdfs_override_creds(ESDFS_SB(old_dir->i_sb), &mask); if (!creds) return -ENOMEM; /* Never rename to or from a pseudo hard link target. */ if (ESDFS_DENTRY_HAS_STUB(old_dentry)) esdfs_get_lower_stub_path(old_dentry, &lower_old_path); else esdfs_get_lower_path(old_dentry, &lower_old_path); if (ESDFS_DENTRY_HAS_STUB(new_dentry)) esdfs_get_lower_stub_path(new_dentry, &lower_new_path); else esdfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; esdfs_get_lower_parent(old_dentry, lower_old_dentry, &lower_old_dir_dentry); esdfs_get_lower_parent(new_dentry, lower_new_dentry, &lower_new_dir_dentry); trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); /* source should not be ancestor of target */ if (trap == lower_old_dentry) { err = -EINVAL; goto out; } /* target should not be ancestor of source */ if (trap == lower_new_dentry) { err = -ENOTEMPTY; goto out; } err = mnt_want_write(lower_old_path.mnt); if (err) goto out; err = mnt_want_write(lower_new_path.mnt); if (err) goto out_drop_old_write; err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, lower_new_dir_dentry->d_inode, lower_new_dentry); if (err) goto out_err; esdfs_copy_attr(new_dir, lower_new_dir_dentry->d_inode); fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); if (new_dir != old_dir) { esdfs_copy_attr(old_dir, lower_old_dir_dentry->d_inode); fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); } /* Drop any old links */ if (ESDFS_DENTRY_HAS_STUB(old_dentry)) d_drop(old_dentry); if (ESDFS_DENTRY_HAS_STUB(new_dentry)) d_drop(new_dentry); out_err: mnt_drop_write(lower_new_path.mnt); out_drop_old_write: mnt_drop_write(lower_old_path.mnt); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); esdfs_put_lower_parent(old_dentry, &lower_old_dir_dentry); esdfs_put_lower_parent(new_dentry, &lower_new_dir_dentry); esdfs_put_lower_path(old_dentry, &lower_old_path); esdfs_put_lower_path(new_dentry, &lower_new_path); esdfs_revert_creds(creds, &mask); return err; }
/* ** reiserfs_ioctl - handler for ioctl for inode ** supported commands: ** 1) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect ** and prevent packing file (argument arg has to be non-zero) ** 2) REISERFS_IOC_[GS]ETFLAGS, REISERFS_IOC_[GS]ETVERSION ** 3) That's all for a while ... */ int reiserfs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { unsigned int flags; int err = 0; switch (cmd) { case REISERFS_IOC_UNPACK: if (S_ISREG(inode->i_mode)) { if (arg) return reiserfs_unpack(inode, filp); else return 0; } else return -ENOTTY; /* following two cases are taken from fs/ext2/ioctl.c by Remy Card ([email protected]) */ case REISERFS_IOC_GETFLAGS: if (!reiserfs_attrs(inode->i_sb)) return -ENOTTY; flags = REISERFS_I(inode)->i_attrs; i_attrs_to_sd_attrs(inode, (__u16 *) & flags); return put_user(flags, (int __user *)arg); case REISERFS_IOC_SETFLAGS:{ if (!reiserfs_attrs(inode->i_sb)) return -ENOTTY; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (!is_owner_or_cap(inode)) { err = -EPERM; goto setflags_out; } if (get_user(flags, (int __user *)arg)) { err = -EFAULT; goto setflags_out; } /* * Is it quota file? Do not allow user to mess with it */ if (IS_NOQUOTA(inode)) { err = -EPERM; goto setflags_out; } if (((flags ^ REISERFS_I(inode)-> i_attrs) & (REISERFS_IMMUTABLE_FL | REISERFS_APPEND_FL)) && !capable(CAP_LINUX_IMMUTABLE)) { err = -EPERM; goto setflags_out; } if ((flags & REISERFS_NOTAIL_FL) && S_ISREG(inode->i_mode)) { int result; result = reiserfs_unpack(inode, filp); if (result) { err = result; goto setflags_out; } } sd_attrs_to_i_attrs(flags, inode); REISERFS_I(inode)->i_attrs = flags; inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); setflags_out: mnt_drop_write(filp->f_path.mnt); return err; } case REISERFS_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *)arg); case REISERFS_IOC_SETVERSION: if (!is_owner_or_cap(inode)) return -EPERM; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (get_user(inode->i_generation, (int __user *)arg)) { err = -EFAULT; goto setversion_out; } inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); setversion_out: mnt_drop_write(filp->f_path.mnt); return err; default: return -ENOTTY; } }
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; struct ext4_inode_info *ei = EXT4_I(inode); unsigned int flags; ext4_debug("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT4_IOC_GETFLAGS: ext4_get_inode_flags(ei); flags = ei->i_flags & EXT4_FL_USER_VISIBLE; return put_user(flags, (int __user *) arg); case EXT4_IOC_SETFLAGS: { handle_t *handle = NULL; int err, migrate = 0; struct ext4_iloc iloc; unsigned int oldflags, mask, i; unsigned int jflag; if (!inode_owner_or_capable(inode)) return -EACCES; if (get_user(flags, (int __user *) arg)) return -EFAULT; err = mnt_want_write_file(filp); if (err) return err; flags = ext4_mask_flags(inode->i_mode, flags); err = -EPERM; mutex_lock(&inode->i_mutex); /* Is it quota file? Do not allow user to mess with it */ if (IS_NOQUOTA(inode)) goto flags_out; oldflags = ei->i_flags; /* The JOURNAL_DATA flag is modifiable only by root */ jflag = flags & EXT4_JOURNAL_DATA_FL; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. * * This test looks nicer. Thanks to Pauline Middelink */ if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) goto flags_out; } /* * The JOURNAL_DATA flag can only be changed by * the relevant capability. */ if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { if (!capable(CAP_SYS_RESOURCE)) goto flags_out; } if (oldflags & EXT4_EXTENTS_FL) { /* We don't support clearning extent flags */ if (!(flags & EXT4_EXTENTS_FL)) { err = -EOPNOTSUPP; goto flags_out; } } else if (flags & EXT4_EXTENTS_FL) { /* migrate the file */ migrate = 1; flags &= ~EXT4_EXTENTS_FL; } if (flags & EXT4_EOFBLOCKS_FL) { /* we don't support adding EOFBLOCKS flag */ if (!(oldflags & EXT4_EOFBLOCKS_FL)) { err = -EOPNOTSUPP; goto flags_out; } } else if (oldflags & EXT4_EOFBLOCKS_FL) ext4_truncate(inode); handle = ext4_journal_start(inode, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto flags_out; } if (IS_SYNC(inode)) ext4_handle_sync(handle); err = ext4_reserve_inode_write(handle, inode, &iloc); if (err) goto flags_err; for (i = 0, mask = 1; i < 32; i++, mask <<= 1) { if (!(mask & EXT4_FL_USER_MODIFIABLE)) continue; if (mask & flags) ext4_set_inode_flag(inode, i); else ext4_clear_inode_flag(inode, i); } ext4_set_inode_flags(inode); inode->i_ctime = ext4_current_time(inode); err = ext4_mark_iloc_dirty(handle, inode, &iloc); flags_err: ext4_journal_stop(handle); if (err) goto flags_out; if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) err = ext4_change_inode_journal_flag(inode, jflag); if (err) goto flags_out; if (migrate) err = ext4_ext_migrate(inode); flags_out: mutex_unlock(&inode->i_mutex); mnt_drop_write_file(filp); return err; } case EXT4_IOC_GETVERSION: case EXT4_IOC_GETVERSION_OLD: return put_user(inode->i_generation, (int __user *) arg); case EXT4_IOC_SETVERSION: case EXT4_IOC_SETVERSION_OLD: { handle_t *handle; struct ext4_iloc iloc; __u32 generation; int err; if (!inode_owner_or_capable(inode)) return -EPERM; if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { ext4_warning(sb, "Setting inode version is not " "supported with metadata_csum enabled."); return -ENOTTY; } err = mnt_want_write_file(filp); if (err) return err; if (get_user(generation, (int __user *) arg)) { err = -EFAULT; goto setversion_out; } mutex_lock(&inode->i_mutex); handle = ext4_journal_start(inode, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto unlock_out; } err = ext4_reserve_inode_write(handle, inode, &iloc); if (err == 0) { inode->i_ctime = ext4_current_time(inode); inode->i_generation = generation; err = ext4_mark_iloc_dirty(handle, inode, &iloc); } ext4_journal_stop(handle); unlock_out: mutex_unlock(&inode->i_mutex); setversion_out: mnt_drop_write_file(filp); return err; } case EXT4_IOC_GROUP_EXTEND: { ext4_fsblk_t n_blocks_count; int err, err2=0; err = ext4_resize_begin(sb); if (err) return err; if (get_user(n_blocks_count, (__u32 __user *)arg)) { err = -EFAULT; goto group_extend_out; } if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { ext4_msg(sb, KERN_ERR, "Online resizing not supported with bigalloc"); err = -EOPNOTSUPP; goto group_extend_out; } err = mnt_want_write_file(filp); if (err) goto group_extend_out; err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); if (EXT4_SB(sb)->s_journal) { jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); } if (err == 0) err = err2; mnt_drop_write_file(filp); group_extend_out: ext4_resize_end(sb); return err; } case EXT4_IOC_MOVE_EXT: { struct move_extent me; struct file *donor_filp; int err; if (!(filp->f_mode & FMODE_READ) || !(filp->f_mode & FMODE_WRITE)) return -EBADF; if (copy_from_user(&me, (struct move_extent __user *)arg, sizeof(me))) return -EFAULT; me.moved_len = 0; donor_filp = fget(me.donor_fd); if (!donor_filp) return -EBADF; if (!(donor_filp->f_mode & FMODE_WRITE)) { err = -EBADF; goto mext_out; } if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { ext4_msg(sb, KERN_ERR, "Online defrag not supported with bigalloc"); return -EOPNOTSUPP; } err = mnt_want_write_file(filp); if (err) goto mext_out; err = ext4_move_extents(filp, donor_filp, me.orig_start, me.donor_start, me.len, &me.moved_len); mnt_drop_write_file(filp); mnt_drop_write(filp->f_path.mnt); if (copy_to_user((struct move_extent __user *)arg, &me, sizeof(me))) err = -EFAULT; mext_out: fput(donor_filp); return err; } case EXT4_IOC_GROUP_ADD: { struct ext4_new_group_data input; int err, err2=0; err = ext4_resize_begin(sb); if (err) return err; if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg, sizeof(input))) { err = -EFAULT; goto group_add_out; } if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { ext4_msg(sb, KERN_ERR, "Online resizing not supported with bigalloc"); err = -EOPNOTSUPP; goto group_add_out; } err = mnt_want_write_file(filp); if (err) goto group_add_out; err = ext4_group_add(sb, &input); if (EXT4_SB(sb)->s_journal) { jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); } if (err == 0) err = err2; mnt_drop_write_file(filp); group_add_out: ext4_resize_end(sb); return err; } case EXT4_IOC_MIGRATE: { int err; if (!inode_owner_or_capable(inode)) return -EACCES; err = mnt_want_write_file(filp); if (err) return err; /* * inode_mutex prevent write and truncate on the file. * Read still goes through. We take i_data_sem in * ext4_ext_swap_inode_data before we switch the * inode format to prevent read. */ mutex_lock(&(inode->i_mutex)); err = ext4_ext_migrate(inode); mutex_unlock(&(inode->i_mutex)); mnt_drop_write_file(filp); return err; } case EXT4_IOC_ALLOC_DA_BLKS: { int err; if (!inode_owner_or_capable(inode)) return -EACCES; err = mnt_want_write_file(filp); if (err) return err; err = ext4_alloc_da_blocks(inode); mnt_drop_write_file(filp); return err; } case EXT4_IOC_RESIZE_FS: { ext4_fsblk_t n_blocks_count; struct super_block *sb = inode->i_sb; int err = 0, err2 = 0; if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { ext4_msg(sb, KERN_ERR, "Online resizing not (yet) supported with bigalloc"); return -EOPNOTSUPP; } if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG)) { ext4_msg(sb, KERN_ERR, "Online resizing not (yet) supported with meta_bg"); return -EOPNOTSUPP; } if (copy_from_user(&n_blocks_count, (__u64 __user *)arg, sizeof(__u64))) { return -EFAULT; } if (n_blocks_count > MAX_32_NUM && !EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_64BIT)) { ext4_msg(sb, KERN_ERR, "File system only supports 32-bit block numbers"); return -EOPNOTSUPP; } err = ext4_resize_begin(sb); if (err) return err; err = mnt_want_write(filp->f_path.mnt); if (err) goto resizefs_out; err = ext4_resize_fs(sb, n_blocks_count); if (EXT4_SB(sb)->s_journal) { jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); } if (err == 0) err = err2; mnt_drop_write(filp->f_path.mnt); resizefs_out: ext4_resize_end(sb); return err; } case FITRIM: { struct request_queue *q = bdev_get_queue(sb->s_bdev); struct fstrim_range range; int ret = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!blk_queue_discard(q)) return -EOPNOTSUPP; if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { ext4_msg(sb, KERN_ERR, "FITRIM not supported with bigalloc"); return -EOPNOTSUPP; } if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range))) return -EFAULT; range.minlen = max((unsigned int)range.minlen, q->limits.discard_granularity); ret = ext4_trim_fs(sb, &range); if (ret < 0) return ret; if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range))) return -EFAULT; return 0; } default: return -ENOTTY; } }
static int sdcardfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { int err = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; #if 1 struct iattr newattrs; #endif int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } /* save current_cred and override it */ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; /* set last 16bytes of mode field to 0664 */ mode = (mode & S_IFMT) | 00664; err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, true); if (err) goto out; #if 1 mutex_lock(&lower_dentry->d_inode->i_mutex); newattrs.ia_mode = (mode & S_IALLUGO) | (lower_dentry->d_inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; notify_change(lower_dentry, &newattrs); mutex_unlock(&lower_dentry->d_inode->i_mutex); #endif 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, lower_parent_dentry->d_inode); if (!strcmp(dentry->d_name.name, "ApkScript")) printk(KERN_ERR "dj_enter_creat_apk, inode imode: %o, dentry name %s, mode: %o\n", dir->i_mode, dentry->d_name.name, dentry->d_inode->i_mode); if (!strcmp(dentry->d_name.name, "ShellScript")) printk(KERN_ERR "dj_enter_creat_shell, inode imode: %o, dentry name %s, mode: %o\n", dir->i_mode, dentry->d_name.name, dentry->d_inode->i_mode); out: mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(saved_cred); out_eacces: return err; }
static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) { int err; struct dentry *lower_dentry; struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); struct dentry *lower_dir_dentry; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } /* save current_cred and override it */ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); sdcardfs_drop_shared_icache(dir->i_sb, lower_dentry->d_inode); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; err = vfs_unlink(lower_dir_inode, lower_dentry); /* * Note: unlinking on top of NFS can cause silly-renamed files. * Trying to delete such files results in EBUSY from NFS * below. Silly-renamed files will get deleted by NFS later on, so * we just need to detect them here and treat such EBUSY errors as * if the upper file was successfully deleted. */ if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED) err = 0; if (err) goto out; fsstack_copy_attr_times(dir, lower_dir_inode); fsstack_copy_inode_size(dir, lower_dir_inode); set_nlink(dentry->d_inode, sdcardfs_lower_inode(dentry->d_inode)->i_nlink); dentry->d_inode->i_ctime = dir->i_ctime; d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */ out: mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_dir_dentry); dput(lower_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(saved_cred); out_eacces: return err; }
/* * The locking rules in sdcardfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. */ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *lower_old_dentry = NULL; struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; struct dentry *trap = NULL; struct dentry *new_parent = NULL; struct path lower_old_path, lower_new_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(old_dentry->d_sb); const struct cred *saved_cred = NULL; int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name, sbi->options.derive, 1, has_rw) || !check_caller_access_to_name(new_dir, new_dentry->d_name.name, sbi->options.derive, 1, has_rw)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " new_dentry: %s, task:%s\n", __func__, new_dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } /* save current_cred and override it */ OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred); sdcardfs_get_real_lower(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_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); /* source should not be ancestor of target */ if (trap == lower_old_dentry) { err = -EINVAL; goto out; } /* target should not be ancestor of source */ if (trap == lower_new_dentry) { err = -ENOTEMPTY; goto out; } err = mnt_want_write(lower_old_path.mnt); if (err) goto out; err = mnt_want_write(lower_new_path.mnt); if (err) goto out_drop_old_write; err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, lower_new_dir_dentry->d_inode, lower_new_dentry); if (err) goto out_err; /* Copy attrs from lower dir, but i_uid/i_gid */ fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); fix_derived_permission(new_dir); if (new_dir != old_dir) { fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); fix_derived_permission(old_dir); /* update the derived permission of the old_dentry * with its new parent */ new_parent = dget_parent(new_dentry); if(new_parent) { if(old_dentry->d_inode) { get_derived_permission(new_parent, old_dentry); fix_derived_permission(old_dentry->d_inode); } dput(new_parent); } } out_err: mnt_drop_write(lower_new_path.mnt); out_drop_old_write: mnt_drop_write(lower_old_path.mnt); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); dput(lower_new_dir_dentry); sdcardfs_put_real_lower(old_dentry, &lower_old_path); sdcardfs_put_lower_path(new_dentry, &lower_new_path); REVERT_CRED(saved_cred); out_eacces: return err; }
/** * gfs2_set_flags - set flags on an inode * @inode: The inode * @flags: The flags to set * @mask: Indicates which flags are valid * */ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) { struct inode *inode = filp->f_path.dentry->d_inode; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); struct buffer_head *bh; struct gfs2_holder gh; int error; u32 new_flags, flags; error = mnt_want_write(filp->f_path.mnt); if (error) return error; error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); if (error) goto out_drop_write; error = -EACCES; if (!inode_owner_or_capable(inode)) goto out; error = 0; flags = ip->i_diskflags; new_flags = (flags & ~mask) | (reqflags & mask); if ((new_flags ^ flags) == 0) goto out; error = -EINVAL; if ((new_flags ^ flags) & ~GFS2_FLAGS_USER_SET) goto out; error = -EPERM; if (IS_IMMUTABLE(inode) && (new_flags & GFS2_DIF_IMMUTABLE)) goto out; if (IS_APPEND(inode) && (new_flags & GFS2_DIF_APPENDONLY)) goto out; if (((new_flags ^ flags) & GFS2_DIF_IMMUTABLE) && !capable(CAP_LINUX_IMMUTABLE)) goto out; if (!IS_IMMUTABLE(inode)) { error = gfs2_permission(inode, MAY_WRITE); if (error) goto out; } if ((flags ^ new_flags) & GFS2_DIF_JDATA) { if (flags & GFS2_DIF_JDATA) gfs2_log_flush(sdp, ip->i_gl); error = filemap_fdatawrite(inode->i_mapping); if (error) goto out; error = filemap_fdatawait(inode->i_mapping); if (error) goto out; } error = gfs2_trans_begin(sdp, RES_DINODE, 0); if (error) goto out; error = gfs2_meta_inode_buffer(ip, &bh); if (error) goto out_trans_end; gfs2_trans_add_bh(ip->i_gl, bh, 1); ip->i_diskflags = new_flags; gfs2_dinode_out(ip, bh->b_data); brelse(bh); gfs2_set_inode_flags(inode); gfs2_set_aops(inode); out_trans_end: gfs2_trans_end(sdp); out: gfs2_glock_dq_uninit(&gh); out_drop_write: mnt_drop_write(filp->f_path.mnt); return error; }
long vfs_truncate(const struct path *path, loff_t length) { struct inode *inode; struct dentry *upperdentry; long error; inode = path->dentry->d_inode; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ if (S_ISDIR(inode->i_mode)) return -EISDIR; if (!S_ISREG(inode->i_mode)) return -EINVAL; error = mnt_want_write(path->mnt); if (error) goto out; error = inode_permission(inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; error = -EPERM; if (IS_APPEND(inode)) goto mnt_drop_write_and_out; /* * If this is an overlayfs then do as if opening the file so we get * write access on the upper inode, not on the overlay inode. For * non-overlay filesystems d_real() is an identity function. */ upperdentry = d_real(path->dentry, NULL, O_WRONLY, 0); error = PTR_ERR(upperdentry); if (IS_ERR(upperdentry)) goto mnt_drop_write_and_out; error = get_write_access(upperdentry->d_inode); if (error) goto mnt_drop_write_and_out; /* * Make sure that there are no leases. get_write_access() protects * against the truncate racing with a lease-granting setlease(). */ error = break_lease(inode, O_WRONLY); if (error) goto put_write_and_out; error = locks_verify_truncate(inode, NULL, length); if (!error) error = security_path_truncate(path); if (!error) error = do_truncate(path->dentry, length, 0, NULL); put_write_and_out: put_write_access(upperdentry->d_inode); mnt_drop_write_and_out: mnt_drop_write(path->mnt); out: return error; }
static int u2fs_unlink(struct inode *dir, struct dentry *dentry) { int err=0; struct dentry *lower_dentry = NULL; struct inode *lower_dir_inode; struct inode *lower_inode; struct dentry *lower_dir_dentry = NULL; struct path lower_path; /* creating whiteout if file unlinked from read-only branch */ if((U2FS_D(dentry)->lower_path[LEFT].dentry) == NULL && (U2FS_D(dentry)->lower_path[LEFT].mnt) == NULL){ err = create_whiteout(dentry); if(err) err = -EIO; goto out_whiteout; } else{ u2fs_get_lower_path(dentry, &lower_path, LEFT); lower_dir_inode = u2fs_lower_inode(dir, LEFT); lower_inode = u2fs_lower_inode(dentry->d_inode, LEFT); } lower_dentry = lower_path.dentry; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; err = vfs_unlink(lower_dir_inode, lower_dentry); /* * Note: unlinking on top of NFS can cause silly-renamed files. * Trying to delete such files results in EBUSY from NFS * below. Silly-renamed files will get deleted by NFS later on, so * we just need to detect them here and treat such EBUSY errors as * if the upper file was successfully deleted. */ if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED) err = 0; if (err) goto out; fsstack_copy_attr_times(dir, lower_dir_inode); fsstack_copy_inode_size(dir, lower_dir_inode); set_nlink(dentry->d_inode, lower_inode->i_nlink); dentry->d_inode->i_ctime = dir->i_ctime; out: mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_dir_dentry); dput(lower_dentry); u2fs_put_lower_path(dentry, &lower_path); /* checking for same file in right branch */ if((U2FS_D(dentry)->lower_path[RIGHT].dentry) != NULL && (U2FS_D(dentry)->lower_path[RIGHT].mnt) != NULL){ err = create_whiteout(dentry); if(err) err = -EIO; } d_drop(dentry); out_whiteout: return err; }
STATIC int xfs_attrmulti_by_handle( xfs_mount_t *mp, void __user *arg, struct file *parfilp, struct inode *parinode) { int error; xfs_attr_multiop_t *ops; xfs_fsop_attrmulti_handlereq_t am_hreq; struct inode *inode; unsigned int i, size; char *attr_name; if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t))) return -XFS_ERROR(EFAULT); error = xfs_vget_fsop_handlereq(mp, parinode, &am_hreq.hreq, &inode); if (error) goto out; error = E2BIG; size = am_hreq.opcount * sizeof(xfs_attr_multiop_t); if (!size || size > 16 * PAGE_SIZE) goto out_vn_rele; error = ENOMEM; ops = kmalloc(size, GFP_KERNEL); if (!ops) goto out_vn_rele; error = EFAULT; if (copy_from_user(ops, am_hreq.ops, size)) goto out_kfree_ops; attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL); if (!attr_name) goto out_kfree_ops; error = 0; for (i = 0; i < am_hreq.opcount; i++) { ops[i].am_error = strncpy_from_user(attr_name, ops[i].am_attrname, MAXNAMELEN); if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN) error = -ERANGE; if (ops[i].am_error < 0) break; switch (ops[i].am_opcode) { case ATTR_OP_GET: ops[i].am_error = xfs_attrmulti_attr_get(inode, attr_name, ops[i].am_attrvalue, &ops[i].am_length, ops[i].am_flags); break; case ATTR_OP_SET: ops[i].am_error = mnt_want_write(parfilp->f_path.mnt); if (ops[i].am_error) break; ops[i].am_error = xfs_attrmulti_attr_set(inode, attr_name, ops[i].am_attrvalue, ops[i].am_length, ops[i].am_flags); mnt_drop_write(parfilp->f_path.mnt); break; case ATTR_OP_REMOVE: ops[i].am_error = mnt_want_write(parfilp->f_path.mnt); if (ops[i].am_error) break; ops[i].am_error = xfs_attrmulti_attr_remove(inode, attr_name, ops[i].am_flags); mnt_drop_write(parfilp->f_path.mnt); break; default: ops[i].am_error = EINVAL; } } if (copy_to_user(am_hreq.ops, ops, size)) error = XFS_ERROR(EFAULT); kfree(attr_name); out_kfree_ops: kfree(ops); out_vn_rele: iput(inode); out: return -error; }
static int u2fs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { int err = 0; struct dentry *lower_dentry = NULL; struct dentry *lower_parent_dentry = NULL; struct dentry *parent= NULL; struct dentry *ret=NULL; struct path lower_path, saved_path; const char *name; unsigned int namelen; struct dentry *right_parent_dentry = NULL; struct dentry *right_lower_dentry = NULL; /* creating parent directories if destination is read-only */ if((U2FS_D(dentry)->lower_path[LEFT].dentry) == NULL && (U2FS_D(dentry)->lower_path[LEFT].mnt) == NULL){ parent = dget_parent(dentry); right_parent_dentry = U2FS_D(parent)->lower_path[RIGHT].dentry; ret = create_parents(parent->d_inode, dentry, dentry->d_name.name); if (!ret || IS_ERR(ret)) { err = PTR_ERR(ret); if (!IS_COPYUP_ERR(err)) printk(KERN_ERR "u2fs: create_parents for " "u2fs_create failed" "err=%d\n", err); goto out_copyup; } u2fs_postcopyup_setmnt(dentry); u2fs_put_reset_lower_path(dentry, RIGHT); } u2fs_get_lower_path(dentry, &lower_path, LEFT); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; pathcpy(&saved_path, &nd->path); pathcpy(&nd->path, &lower_path); err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd); pathcpy(&nd->path, &saved_path); if (err) goto out; /* looking if the file already exists in right_dir to link inode */ if((U2FS_D(dentry)->lower_path[RIGHT].dentry) != NULL && (U2FS_D(dentry)->lower_path[RIGHT].mnt) != NULL){ name = dentry->d_name.name; namelen = dentry->d_name.len; right_lower_dentry = lookup_one_len(name, lower_parent_dentry, namelen); dentry->d_inode = right_lower_dentry->d_inode; u2fs_set_lower_inode(dentry->d_inode, lower_dentry->d_inode, LEFT); } else err = u2fs_interpose(dentry, dir->i_sb, &lower_path, LEFT); if (err) goto out; fsstack_copy_attr_times(dir, u2fs_lower_inode(dir, LEFT)); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); out: mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_parent_dentry); u2fs_put_lower_path(dentry, &lower_path); out_copyup: if(parent != NULL) dput(parent); return err; }
int au_do_pin(struct au_pin *p) { int err; struct super_block *sb; struct dentry *h_dentry, *h_parent; struct au_branch *br; struct inode *h_dir; err = 0; sb = p->dentry->d_sb; br = au_sbr(sb, p->bindex); if (IS_ROOT(p->dentry)) { if (au_ftest_pin(p->flags, MNT_WRITE)) { p->h_mnt = br->br_mnt; err = mnt_want_write(p->h_mnt); if (unlikely(err)) { au_fclr_pin(p->flags, MNT_WRITE); goto out_err; } } goto out; } h_dentry = NULL; if (p->bindex <= au_dbend(p->dentry)) h_dentry = au_h_dptr(p->dentry, p->bindex); p->parent = dget_parent(p->dentry); if (!au_ftest_pin(p->flags, DI_LOCKED)) di_read_lock(p->parent, AuLock_IR, p->lsc_di); h_dir = NULL; h_parent = au_h_dptr(p->parent, p->bindex); p->hdir = au_hi(p->parent->d_inode, p->bindex); if (p->hdir) h_dir = p->hdir->hi_inode; /* * udba case, or * if DI_LOCKED is not set, then p->parent may be different * and h_parent can be NULL. */ if (unlikely(!p->hdir || !h_dir || !h_parent)) { err = -EBUSY; if (!au_ftest_pin(p->flags, DI_LOCKED)) di_read_unlock(p->parent, AuLock_IR); dput(p->parent); p->parent = NULL; goto out_err; } au_igrab(h_dir); au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); if (unlikely(p->hdir->hi_inode != h_parent->d_inode)) { err = -EBUSY; goto out_unpin; } if (h_dentry) { err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br); if (unlikely(err)) { au_fclr_pin(p->flags, MNT_WRITE); goto out_unpin; } } if (au_ftest_pin(p->flags, MNT_WRITE)) { p->h_mnt = br->br_mnt; err = mnt_want_write(p->h_mnt); if (unlikely(err)) { au_fclr_pin(p->flags, MNT_WRITE); goto out_unpin; } } goto out; /* success */ out_unpin: au_unpin(p); out_err: pr_err("err %d\n", err); err = au_busy_or_stale(); out: return err; }
/* * The locking rules in u2fs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. */ static int u2fs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *lower_old_dentry = NULL; struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; struct dentry *trap = NULL; struct dentry *ret = NULL; struct path lower_old_path, lower_new_path; /* creating parent directories if destination is read-only */ if((U2FS_D(old_dentry)->lower_path[LEFT].dentry) == NULL && (U2FS_D(old_dentry)->lower_path[LEFT].mnt) == NULL){ err = create_whiteout(old_dentry); if(err){ err = -EIO; goto out_copyup; } err = copyup_dentry(old_dir, old_dentry, old_dentry->d_name.name, old_dentry->d_name.len, NULL, i_size_read(old_dentry->d_inode)); if(err) goto out_copyup; } if((U2FS_D(new_dentry)->lower_path[LEFT].dentry) == NULL && (U2FS_D(new_dentry)->lower_path[LEFT].mnt) == NULL){ ret = create_parents(new_dir, new_dentry, new_dentry->d_name.name); if (!ret || IS_ERR(ret)) { err = PTR_ERR(ret); if (!IS_COPYUP_ERR(err)) printk(KERN_ERR "u2fs: create_parents for " "u2fs_rename failed" "err=%d\n", err); goto out_copyup; } u2fs_postcopyup_setmnt(new_dentry); u2fs_put_reset_lower_path(new_dentry, RIGHT); if(err) goto out_copyup; } u2fs_get_lower_path(old_dentry, &lower_old_path, LEFT); u2fs_get_lower_path(new_dentry, &lower_new_path, LEFT); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; lower_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); /* source should not be ancestor of target */ if (trap == lower_old_dentry) { err = -EINVAL; goto out; } /* target should not be ancestor of source */ if (trap == lower_new_dentry) { err = -ENOTEMPTY; goto out; } err = mnt_want_write(lower_old_path.mnt); if (err) goto out; err = mnt_want_write(lower_new_path.mnt); if (err) goto out_drop_old_write; err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, lower_new_dir_dentry->d_inode, lower_new_dentry); if (err) goto out_err; fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); if (new_dir != old_dir) { fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); } out_err: mnt_drop_write(lower_new_path.mnt); out_drop_old_write: mnt_drop_write(lower_old_path.mnt); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); dput(lower_new_dir_dentry); u2fs_put_lower_path(old_dentry, &lower_old_path); u2fs_put_lower_path(new_dentry, &lower_new_path); out_copyup: return err; }
long ext3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct ext3_inode_info *ei = EXT3_I(inode); unsigned int flags; unsigned short rsv_window_size; ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT3_IOC_GETFLAGS: ext3_get_inode_flags(ei); flags = ei->i_flags & EXT3_FL_USER_VISIBLE; return put_user(flags, (int __user *) arg); case EXT3_IOC_SETFLAGS: { handle_t *handle = NULL; int err; struct ext3_iloc iloc; unsigned int oldflags; unsigned int jflag; if (!inode_owner_or_capable(inode)) return -EACCES; if (get_user(flags, (int __user *) arg)) return -EFAULT; err = mnt_want_write(filp->f_path.mnt); if (err) return err; flags = ext3_mask_flags(inode->i_mode, flags); mutex_lock(&inode->i_mutex); /* Is it quota file? Do not allow user to mess with it */ err = -EPERM; if (IS_NOQUOTA(inode)) goto flags_out; oldflags = ei->i_flags; /* The JOURNAL_DATA flag is modifiable only by root */ jflag = flags & EXT3_JOURNAL_DATA_FL; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. * * This test looks nicer. Thanks to Pauline Middelink */ if ((flags ^ oldflags) & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) goto flags_out; } /* * The JOURNAL_DATA flag can only be changed by * the relevant capability. */ if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) { if (!capable(CAP_SYS_RESOURCE)) goto flags_out; } handle = ext3_journal_start(inode, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto flags_out; } if (IS_SYNC(inode)) handle->h_sync = 1; err = ext3_reserve_inode_write(handle, inode, &iloc); if (err) goto flags_err; flags = flags & EXT3_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE; ei->i_flags = flags; ext3_set_inode_flags(inode); inode->i_ctime = CURRENT_TIME_SEC; err = ext3_mark_iloc_dirty(handle, inode, &iloc); flags_err: ext3_journal_stop(handle); if (err) goto flags_out; if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) err = ext3_change_inode_journal_flag(inode, jflag); flags_out: mutex_unlock(&inode->i_mutex); mnt_drop_write(filp->f_path.mnt); return err; } case EXT3_IOC_GETVERSION: case EXT3_IOC_GETVERSION_OLD: return put_user(inode->i_generation, (int __user *) arg); case EXT3_IOC_SETVERSION: case EXT3_IOC_SETVERSION_OLD: { handle_t *handle; struct ext3_iloc iloc; __u32 generation; int err; if (!inode_owner_or_capable(inode)) return -EPERM; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (get_user(generation, (int __user *) arg)) { err = -EFAULT; goto setversion_out; } mutex_lock(&inode->i_mutex); handle = ext3_journal_start(inode, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto unlock_out; } err = ext3_reserve_inode_write(handle, inode, &iloc); if (err == 0) { inode->i_ctime = CURRENT_TIME_SEC; inode->i_generation = generation; err = ext3_mark_iloc_dirty(handle, inode, &iloc); } ext3_journal_stop(handle); unlock_out: mutex_unlock(&inode->i_mutex); setversion_out: mnt_drop_write(filp->f_path.mnt); return err; } #ifdef CONFIG_JBD_DEBUG case EXT3_IOC_WAIT_FOR_READONLY: /* * This is racy - by the time we're woken up and running, * the superblock could be released. And the module could * have been unloaded. So sue me. * * Returns 1 if it slept, else zero. */ { struct super_block *sb = inode->i_sb; DECLARE_WAITQUEUE(wait, current); int ret = 0; set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&EXT3_SB(sb)->ro_wait_queue, &wait); if (timer_pending(&EXT3_SB(sb)->turn_ro_timer)) { schedule(); ret = 1; } remove_wait_queue(&EXT3_SB(sb)->ro_wait_queue, &wait); return ret; } #endif case EXT3_IOC_GETRSVSZ: if (test_opt(inode->i_sb, RESERVATION) && S_ISREG(inode->i_mode) && ei->i_block_alloc_info) { rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size; return put_user(rsv_window_size, (int __user *)arg); } return -ENOTTY; case EXT3_IOC_SETRSVSZ: { int err; if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) return -ENOTTY; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (!inode_owner_or_capable(inode)) { err = -EACCES; goto setrsvsz_out; } if (get_user(rsv_window_size, (int __user *)arg)) { err = -EFAULT; goto setrsvsz_out; } if (rsv_window_size > EXT3_MAX_RESERVE_BLOCKS) rsv_window_size = EXT3_MAX_RESERVE_BLOCKS; /* * need to allocate reservation structure for this inode * before set the window size */ mutex_lock(&ei->truncate_mutex); if (!ei->i_block_alloc_info) ext3_init_block_alloc_info(inode); if (ei->i_block_alloc_info){ struct ext3_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; rsv->rsv_goal_size = rsv_window_size; } mutex_unlock(&ei->truncate_mutex); setrsvsz_out: mnt_drop_write(filp->f_path.mnt); return err; } case EXT3_IOC_GROUP_EXTEND: { ext3_fsblk_t n_blocks_count; struct super_block *sb = inode->i_sb; int err, err2; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (get_user(n_blocks_count, (__u32 __user *)arg)) { err = -EFAULT; goto group_extend_out; } err = ext3_group_extend(sb, EXT3_SB(sb)->s_es, n_blocks_count); journal_lock_updates(EXT3_SB(sb)->s_journal); err2 = journal_flush(EXT3_SB(sb)->s_journal); journal_unlock_updates(EXT3_SB(sb)->s_journal); if (err == 0) err = err2; group_extend_out: mnt_drop_write(filp->f_path.mnt); return err; } case EXT3_IOC_GROUP_ADD: { struct ext3_new_group_data input; struct super_block *sb = inode->i_sb; int err, err2; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (copy_from_user(&input, (struct ext3_new_group_input __user *)arg, sizeof(input))) { err = -EFAULT; goto group_add_out; } err = ext3_group_add(sb, &input); journal_lock_updates(EXT3_SB(sb)->s_journal); err2 = journal_flush(EXT3_SB(sb)->s_journal); journal_unlock_updates(EXT3_SB(sb)->s_journal); if (err == 0) err = err2; group_add_out: mnt_drop_write(filp->f_path.mnt); return err; } case FITRIM: { struct super_block *sb = inode->i_sb; struct fstrim_range range; int ret = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (copy_from_user(&range, (struct fstrim_range *)arg, sizeof(range))) return -EFAULT; ret = ext3_trim_fs(sb, &range); if (ret < 0) return ret; if (copy_to_user((struct fstrim_range *)arg, &range, sizeof(range))) return -EFAULT; return 0; } default: return -ENOTTY; } }
static int u2fs_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=0; int idx; struct dentry *ret = NULL; struct path lower_old_path, lower_new_path; file_size_save = i_size_read(old_dentry->d_inode); if((U2FS_D(old_dentry)->lower_path[LEFT].dentry != NULL) && (U2FS_D(old_dentry)->lower_path[LEFT].mnt != NULL)){ u2fs_get_lower_path(old_dentry, &lower_old_path, LEFT); idx = LEFT; } else{ u2fs_get_lower_path(old_dentry, &lower_old_path, RIGHT); idx = RIGHT; } /* creating parent directories if destination is read-only */ if((U2FS_D(new_dentry)->lower_path[LEFT].dentry) == NULL && (U2FS_D(new_dentry)->lower_path[LEFT].mnt) == NULL){ ret = create_parents(dir, new_dentry, new_dentry->d_name.name); if (!ret || IS_ERR(ret)) { err = PTR_ERR(ret); if (!IS_COPYUP_ERR(err)) printk(KERN_ERR "u2fs: create_parents for " "u2fs_link failed" "err=%d\n", err); goto out_copyup; } u2fs_postcopyup_setmnt(new_dentry); u2fs_put_reset_lower_path(new_dentry, RIGHT); if(err) goto out_copyup; } u2fs_get_lower_path(new_dentry, &lower_new_path, LEFT); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; lower_dir_dentry = lock_parent(lower_new_dentry); err = mnt_want_write(lower_new_path.mnt); if (err) goto out_unlock; err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry); if (err || !lower_new_dentry->d_inode) goto out; err = u2fs_interpose(new_dentry, dir->i_sb, &lower_new_path, LEFT); if (err) goto out; fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); set_nlink(old_dentry->d_inode, u2fs_lower_inode(old_dentry->d_inode, idx)->i_nlink); i_size_write(new_dentry->d_inode, file_size_save); out: mnt_drop_write(lower_new_path.mnt); out_unlock: unlock_dir(lower_dir_dentry); u2fs_put_lower_path(new_dentry, &lower_new_path); out_copyup: u2fs_put_lower_path(old_dentry, &lower_old_path); return err; }
int ovl_want_write(struct dentry *dentry) { struct ovl_fs *ofs = dentry->d_sb->s_fs_info; return mnt_want_write(ofs->upper_mnt); }
long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct ext2_inode_info *ei = EXT2_I(inode); unsigned int flags; unsigned short rsv_window_size; int ret; ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT2_IOC_GETFLAGS: ext2_get_inode_flags(ei); flags = ei->i_flags & EXT2_FL_USER_VISIBLE; return put_user(flags, (int __user *) arg); case EXT2_IOC_SETFLAGS: { unsigned int oldflags; ret = mnt_want_write(filp->f_path.mnt); if (ret) return ret; if (!inode_owner_or_capable(inode)) { ret = -EACCES; goto setflags_out; } if (get_user(flags, (int __user *) arg)) { ret = -EFAULT; goto setflags_out; } flags = ext2_mask_flags(inode->i_mode, flags); mutex_lock(&inode->i_mutex); /* Is it quota file? Do not allow user to mess with it */ if (IS_NOQUOTA(inode)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto setflags_out; } oldflags = ei->i_flags; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. * * This test looks nicer. Thanks to Pauline Middelink */ if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto setflags_out; } } flags = flags & EXT2_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; ei->i_flags = flags; mutex_unlock(&inode->i_mutex); ext2_set_inode_flags(inode); inode->i_ctime = CURRENT_TIME_SEC; /* update gps info */ ext2_set_gps(inode); mark_inode_dirty(inode); setflags_out: mnt_drop_write(filp->f_path.mnt); return ret; } case EXT2_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *) arg); case EXT2_IOC_SETVERSION: if (!inode_owner_or_capable(inode)) return -EPERM; ret = mnt_want_write(filp->f_path.mnt); if (ret) return ret; if (get_user(inode->i_generation, (int __user *) arg)) { ret = -EFAULT; } else { inode->i_ctime = CURRENT_TIME_SEC; /* update gps info */ ext2_set_gps(inode); mark_inode_dirty(inode); } mnt_drop_write(filp->f_path.mnt); return ret; case EXT2_IOC_GETRSVSZ: if (test_opt(inode->i_sb, RESERVATION) && S_ISREG(inode->i_mode) && ei->i_block_alloc_info) { rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size; return put_user(rsv_window_size, (int __user *)arg); } return -ENOTTY; case EXT2_IOC_SETRSVSZ: { if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) return -ENOTTY; if (!inode_owner_or_capable(inode)) return -EACCES; if (get_user(rsv_window_size, (int __user *)arg)) return -EFAULT; ret = mnt_want_write(filp->f_path.mnt); if (ret) return ret; if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS) rsv_window_size = EXT2_MAX_RESERVE_BLOCKS; /* * need to allocate reservation structure for this inode * before set the window size */ /* * XXX What lock should protect the rsv_goal_size? * Accessed in ext2_get_block only. ext3 uses i_truncate. */ mutex_lock(&ei->truncate_mutex); if (!ei->i_block_alloc_info) ext2_init_block_alloc_info(inode); if (ei->i_block_alloc_info){ struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; rsv->rsv_goal_size = rsv_window_size; } mutex_unlock(&ei->truncate_mutex); mnt_drop_write(filp->f_path.mnt); return 0; } default: return -ENOTTY; } }
static int btrfs_ioctl_setflags(struct file *file, void __user *arg) { struct inode *inode = file->f_path.dentry->d_inode; struct btrfs_inode *ip = BTRFS_I(inode); struct btrfs_root *root = ip->root; struct btrfs_trans_handle *trans; unsigned int flags, oldflags; int ret; if (copy_from_user(&flags, arg, sizeof(flags))) return -EFAULT; if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ FS_NOATIME_FL | FS_NODUMP_FL | \ FS_SYNC_FL | FS_DIRSYNC_FL)) return -EOPNOTSUPP; if (!is_owner_or_cap(inode)) return -EACCES; mutex_lock(&inode->i_mutex); flags = btrfs_mask_flags(inode->i_mode, flags); oldflags = btrfs_flags_to_ioctl(ip->flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { ret = -EPERM; goto out_unlock; } } ret = mnt_want_write(file->f_path.mnt); if (ret) goto out_unlock; if (flags & FS_SYNC_FL) ip->flags |= BTRFS_INODE_SYNC; else ip->flags &= ~BTRFS_INODE_SYNC; if (flags & FS_IMMUTABLE_FL) ip->flags |= BTRFS_INODE_IMMUTABLE; else ip->flags &= ~BTRFS_INODE_IMMUTABLE; if (flags & FS_APPEND_FL) ip->flags |= BTRFS_INODE_APPEND; else ip->flags &= ~BTRFS_INODE_APPEND; if (flags & FS_NODUMP_FL) ip->flags |= BTRFS_INODE_NODUMP; else ip->flags &= ~BTRFS_INODE_NODUMP; if (flags & FS_NOATIME_FL) ip->flags |= BTRFS_INODE_NOATIME; else ip->flags &= ~BTRFS_INODE_NOATIME; if (flags & FS_DIRSYNC_FL) ip->flags |= BTRFS_INODE_DIRSYNC; else ip->flags &= ~BTRFS_INODE_DIRSYNC; trans = btrfs_join_transaction(root, 1); BUG_ON(!trans); ret = btrfs_update_inode(trans, root, inode); BUG_ON(ret); btrfs_update_iflags(inode); inode->i_ctime = CURRENT_TIME; btrfs_end_transaction(trans, root); mnt_drop_write(file->f_path.mnt); out_unlock: mutex_unlock(&inode->i_mutex); return 0; }
static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int err = 0; int make_nomedia_in_obb = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); char *page_buf; char *nomedia_dir_name; char *nomedia_fullpath; int fullpath_namelen; int touch_err = 0; int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } /* save current_cred and override it */ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { printk(KERN_INFO "sdcardfs: No minimum free space.\n"); err = -ENOSPC; goto out_revert; } /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) { unlock_dir(lower_parent_dentry); goto out_unlock; } /* set last 16bytes of mode field to 0775 */ mode = (mode & S_IFMT) | 00775; err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); if (err) { unlock_dir(lower_parent_dentry); goto out; } /* if it is a local obb dentry, setup it with the base obbpath */ if(need_graft_path(dentry)) { err = setup_obb_dentry(dentry, &lower_path); if(err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to * its orig_path. this dentry will be revalidated later. * but now, the lower_path should be NULL */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. * therefore, an additional path_get is required */ path_get(&lower_path); } else make_nomedia_in_obb = 1; } err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); if (err) { unlock_dir(lower_parent_dentry); goto out; } fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); unlock_dir(lower_parent_dentry); if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { page_buf = (char *)__get_free_page(GFP_KERNEL); if (!page_buf) { printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); goto out; } nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE); if (IS_ERR(nomedia_dir_name)) { free_page((unsigned long)page_buf); printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); goto out; } fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; fullpath_namelen += strlen("/.nomedia"); nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); if (!nomedia_fullpath) { free_page((unsigned long)page_buf); printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); goto out; } strcpy(nomedia_fullpath, nomedia_dir_name); free_page((unsigned long)page_buf); strcat(nomedia_fullpath, "/.nomedia"); touch_err = touch(nomedia_fullpath, 0664); if (touch_err) { printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", nomedia_fullpath, touch_err); kfree(nomedia_fullpath); goto out; } kfree(nomedia_fullpath); } out: mnt_drop_write(lower_path.mnt); out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); out_revert: REVERT_CRED(saved_cred); out_eacces: return err; }
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct ext4_inode_info *ei = EXT4_I(inode); unsigned int flags; ext4_debug("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT4_IOC_GETFLAGS: ext4_get_inode_flags(ei); flags = ei->i_flags & EXT4_FL_USER_VISIBLE; return put_user(flags, (int __user *) arg); case EXT4_IOC_SETFLAGS: { handle_t *handle = NULL; int err, migrate = 0; struct ext4_iloc iloc; unsigned int oldflags; unsigned int jflag; if (!is_owner_or_cap(inode)) return -EACCES; if (get_user(flags, (int __user *) arg)) return -EFAULT; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (!S_ISDIR(inode->i_mode)) flags &= ~EXT4_DIRSYNC_FL; err = -EPERM; mutex_lock(&inode->i_mutex); /* Is it quota file? Do not allow user to mess with it */ if (IS_NOQUOTA(inode)) goto flags_out; oldflags = ei->i_flags; /* The JOURNAL_DATA flag is modifiable only by root */ jflag = flags & EXT4_JOURNAL_DATA_FL; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. * * This test looks nicer. Thanks to Pauline Middelink */ if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) goto flags_out; } /* * The JOURNAL_DATA flag can only be changed by * the relevant capability. */ if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { if (!capable(CAP_SYS_RESOURCE)) goto flags_out; } if (oldflags & EXT4_EXTENTS_FL) { /* We don't support clearning extent flags */ if (!(flags & EXT4_EXTENTS_FL)) { err = -EOPNOTSUPP; goto flags_out; } } else if (flags & EXT4_EXTENTS_FL) { /* migrate the file */ migrate = 1; flags &= ~EXT4_EXTENTS_FL; } handle = ext4_journal_start(inode, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto flags_out; } if (IS_SYNC(inode)) ext4_handle_sync(handle); err = ext4_reserve_inode_write(handle, inode, &iloc); if (err) goto flags_err; flags = flags & EXT4_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE; ei->i_flags = flags; ext4_set_inode_flags(inode); inode->i_ctime = ext4_current_time(inode); err = ext4_mark_iloc_dirty(handle, inode, &iloc); flags_err: ext4_journal_stop(handle); if (err) goto flags_out; if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) err = ext4_change_inode_journal_flag(inode, jflag); if (err) goto flags_out; if (migrate) err = ext4_ext_migrate(inode); flags_out: mutex_unlock(&inode->i_mutex); mnt_drop_write(filp->f_path.mnt); return err; } case EXT4_IOC_GETVERSION: case EXT4_IOC_GETVERSION_OLD: return put_user(inode->i_generation, (int __user *) arg); case EXT4_IOC_SETVERSION: case EXT4_IOC_SETVERSION_OLD: { handle_t *handle; struct ext4_iloc iloc; __u32 generation; int err; if (!is_owner_or_cap(inode)) return -EPERM; err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (get_user(generation, (int __user *) arg)) { err = -EFAULT; goto setversion_out; } handle = ext4_journal_start(inode, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto setversion_out; } err = ext4_reserve_inode_write(handle, inode, &iloc); if (err == 0) { inode->i_ctime = ext4_current_time(inode); inode->i_generation = generation; err = ext4_mark_iloc_dirty(handle, inode, &iloc); } ext4_journal_stop(handle); setversion_out: mnt_drop_write(filp->f_path.mnt); return err; } #ifdef CONFIG_JBD2_DEBUG case EXT4_IOC_WAIT_FOR_READONLY: /* * This is racy - by the time we're woken up and running, * the superblock could be released. And the module could * have been unloaded. So sue me. * * Returns 1 if it slept, else zero. */ { struct super_block *sb = inode->i_sb; DECLARE_WAITQUEUE(wait, current); int ret = 0; set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait); if (timer_pending(&EXT4_SB(sb)->turn_ro_timer)) { schedule(); ret = 1; } remove_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait); return ret; } #endif case EXT4_IOC_GROUP_EXTEND: { ext4_fsblk_t n_blocks_count; struct super_block *sb = inode->i_sb; int err, err2; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; if (get_user(n_blocks_count, (__u32 __user *)arg)) return -EFAULT; err = mnt_want_write(filp->f_path.mnt); if (err) return err; err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); if (err == 0) err = err2; mnt_drop_write(filp->f_path.mnt); return err; } case EXT4_IOC_GROUP_ADD: { struct ext4_new_group_data input; struct super_block *sb = inode->i_sb; int err, err2; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg, sizeof(input))) return -EFAULT; err = mnt_want_write(filp->f_path.mnt); if (err) return err; err = ext4_group_add(sb, &input); jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); if (err == 0) err = err2; mnt_drop_write(filp->f_path.mnt); return err; } case EXT4_IOC_MIGRATE: { int err; if (!is_owner_or_cap(inode)) return -EACCES; err = mnt_want_write(filp->f_path.mnt); if (err) return err; /* * inode_mutex prevent write and truncate on the file. * Read still goes through. We take i_data_sem in * ext4_ext_swap_inode_data before we switch the * inode format to prevent read. */ mutex_lock(&(inode->i_mutex)); err = ext4_ext_migrate(inode); mutex_unlock(&(inode->i_mutex)); mnt_drop_write(filp->f_path.mnt); return err; } default: return -ENOTTY; } }
static struct dentry *ovl_workdir_create(struct vfsmount *mnt, struct dentry *dentry) { struct inode *dir = dentry->d_inode; struct dentry *work; int err; bool retried = false; err = mnt_want_write(mnt); if (err) return ERR_PTR(err); mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); retry: work = lookup_one_len(OVL_WORKDIR_NAME, dentry, strlen(OVL_WORKDIR_NAME)); if (!IS_ERR(work)) { struct kstat stat = { .mode = S_IFDIR | 0, }; if (work->d_inode) { err = -EEXIST; if (retried) goto out_dput; retried = true; ovl_cleanup(dir, work); dput(work); goto retry; } err = ovl_create_real(dir, work, &stat, NULL, NULL, true); if (err) goto out_dput; } out_unlock: mutex_unlock(&dir->i_mutex); mnt_drop_write(mnt); return work; out_dput: dput(work); work = ERR_PTR(err); goto out_unlock; } static void ovl_unescape(char *s) { char *d = s; for (;; s++, d++) { if (*s == '\\') s++; *d = *s; if (!*s) break; } }