SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) { struct file * file; int error = -EBADF; struct dentry * dentry; file = fget(fd); if (!file) goto out; error = mnt_want_write_file(file); if (error) goto out_fput; dentry = file->f_path.dentry; audit_inode(NULL, dentry); error = chown_common(&file->f_path, user, group); mnt_drop_write_file(file); out_fput: fput(file); out: return error; }
static int f2fs_ioc_commit_atomic_write(struct file *filp) { struct inode *inode = file_inode(filp); int ret; if (!inode_owner_or_capable(inode)) return -EACCES; if (f2fs_is_volatile_file(inode)) return 0; ret = mnt_want_write_file(filp); if (ret) return ret; if (f2fs_is_atomic_file(inode)) commit_inmem_pages(inode, false); ret = f2fs_sync_file(filp, 0, LONG_MAX, 0); mnt_drop_write_file(filp); clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); return ret; }
static long ext4_ioctl_group_add(struct file *file, struct ext4_new_group_data *input) { struct super_block *sb = file_inode(file)->i_sb; int err, err2=0; err = ext4_resize_begin(sb); if (err) return err; if (ext4_has_feature_bigalloc(sb)) { ext4_msg(sb, KERN_ERR, "Online resizing not supported with bigalloc"); err = -EOPNOTSUPP; goto group_add_out; } err = mnt_want_write_file(file); 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(file); if (!err && ext4_has_group_desc_csum(sb) && test_opt(sb, INIT_INODE_TABLE)) err = ext4_register_li_request(sb, input->group); group_add_out: ext4_resize_end(sb); return err; }
SYSCALL_DEFINE2(fchmod, unsigned int, fd, mode_t, mode) { struct inode * inode; struct dentry * dentry; struct file * file; int err = -EBADF; struct iattr newattrs; file = fget(fd); if (!file) goto out; dentry = file->f_path.dentry; inode = dentry->d_inode; audit_inode(NULL, dentry); err = mnt_want_write_file(file); if (err) goto out_putf; mutex_lock(&inode->i_mutex); err = security_path_chmod(dentry, file->f_vfsmnt, mode); if (err) goto out_unlock; if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; err = notify_change(dentry, &newattrs); out_unlock: mutex_unlock(&inode->i_mutex); mnt_drop_write(file->f_path.mnt); out_putf: fput(file); out: return err; }
static int efivarfs_ioc_setxflags(struct file *file, void __user *arg) { struct inode *inode = file->f_mapping->host; unsigned int flags; unsigned int i_flags = 0; int error; if (!inode_owner_or_capable(inode)) return -EACCES; if (copy_from_user(&flags, arg, sizeof(flags))) return -EFAULT; if (flags & ~FS_IMMUTABLE_FL) return -EOPNOTSUPP; if (!capable(CAP_LINUX_IMMUTABLE)) return -EPERM; if (flags & FS_IMMUTABLE_FL) i_flags |= S_IMMUTABLE; error = mnt_want_write_file(file); if (error) return error; mutex_lock(&inode->i_mutex); inode->i_flags &= ~S_IMMUTABLE; inode->i_flags |= i_flags; mutex_unlock(&inode->i_mutex); mnt_drop_write_file(file); return 0; }
static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; struct inode *inode = file_inode(file); switch (cmd) { case FS_IOC_GETFLAGS: ret = ovl_real_ioctl(file, cmd, arg); break; case FS_IOC_SETFLAGS: if (!inode_owner_or_capable(inode)) return -EACCES; ret = mnt_want_write_file(file); if (ret) return ret; ret = ovl_copy_up_with_data(file_dentry(file)); if (!ret) { ret = ovl_real_ioctl(file, cmd, arg); inode_lock(inode); ovl_copyflags(ovl_inode_real(inode), inode); inode_unlock(inode); } mnt_drop_write_file(file); break; default: ret = -ENOTTY; } return ret; }
STATIC int xfs_ioc_fssetxattr( xfs_inode_t *ip, struct file *filp, void __user *arg) { struct fsxattr fa; unsigned int mask; int error; if (copy_from_user(&fa, arg, sizeof(fa))) return -EFAULT; mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID; if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) mask |= FSX_NONBLOCK; error = mnt_want_write_file(filp); if (error) return error; error = xfs_ioctl_setattr(ip, &fa, mask); mnt_drop_write_file(filp); return -error; }
long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); unsigned int flags; int new_clusters; int status; struct ocfs2_space_resv sr; struct ocfs2_new_group_input input; struct reflink_arguments args; const char __user *old_path; const char __user *new_path; bool preserve; struct ocfs2_info info; void __user *argp = (void __user *)arg; switch (cmd) { case OCFS2_IOC_GETFLAGS: status = ocfs2_get_inode_attr(inode, &flags); if (status < 0) return status; flags &= OCFS2_FL_VISIBLE; return put_user(flags, (int __user *) arg); case OCFS2_IOC_SETFLAGS: if (get_user(flags, (int __user *) arg)) return -EFAULT; status = mnt_want_write_file(filp); if (status) return status; status = ocfs2_set_inode_attr(inode, flags, OCFS2_FL_MODIFIABLE); mnt_drop_write_file(filp); return status; case OCFS2_IOC_RESVSP: case OCFS2_IOC_RESVSP64: case OCFS2_IOC_UNRESVSP: case OCFS2_IOC_UNRESVSP64: if (copy_from_user(&sr, (int __user *) arg, sizeof(sr))) return -EFAULT; return ocfs2_change_file_space(filp, cmd, &sr); case OCFS2_IOC_GROUP_EXTEND: if (!capable(CAP_SYS_RESOURCE)) return -EPERM; if (get_user(new_clusters, (int __user *)arg)) return -EFAULT; status = mnt_want_write_file(filp); if (status) return status; status = ocfs2_group_extend(inode, new_clusters); mnt_drop_write_file(filp); return status; case OCFS2_IOC_GROUP_ADD: case OCFS2_IOC_GROUP_ADD64: if (!capable(CAP_SYS_RESOURCE)) return -EPERM; if (copy_from_user(&input, (int __user *) arg, sizeof(input))) return -EFAULT; status = mnt_want_write_file(filp); if (status) return status; status = ocfs2_group_add(inode, &input); mnt_drop_write_file(filp); return status; case OCFS2_IOC_REFLINK: if (copy_from_user(&args, argp, sizeof(args))) return -EFAULT; old_path = (const char __user *)(unsigned long)args.old_path; new_path = (const char __user *)(unsigned long)args.new_path; preserve = (args.preserve != 0); return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); case OCFS2_IOC_INFO: if (copy_from_user(&info, argp, sizeof(struct ocfs2_info))) return -EFAULT; return ocfs2_info_handle(inode, &info, 0); case FITRIM: { struct super_block *sb = inode->i_sb; 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 (copy_from_user(&range, argp, sizeof(range))) return -EFAULT; range.minlen = max_t(u64, q->limits.discard_granularity, range.minlen); ret = ocfs2_trim_fs(sb, &range); if (ret < 0) return ret; if (copy_to_user(argp, &range, sizeof(range))) return -EFAULT; return 0; } case OCFS2_IOC_MOVE_EXT: return ocfs2_ioctl_move_extents(filp, argp); default: return -ENOTTY; } }
/** * 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_file(filp); 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_file(filp); return error; }
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(file); 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(file); out: return err; }
long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); 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_file(filp); 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); inode_lock(inode); /* Is it quota file? Do not allow user to mess with it */ if (IS_NOQUOTA(inode)) { inode_unlock(inode); 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)) { inode_unlock(inode); ret = -EPERM; goto setflags_out; } } flags = flags & EXT2_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; ei->i_flags = flags; ext2_set_inode_flags(inode); inode->i_ctime = current_time(inode); inode_unlock(inode); mark_inode_dirty(inode); setflags_out: mnt_drop_write_file(filp); return ret; } case EXT2_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *) arg); case EXT2_IOC_SETVERSION: { __u32 generation; if (!inode_owner_or_capable(inode)) return -EPERM; ret = mnt_want_write_file(filp); if (ret) return ret; if (get_user(generation, (int __user *) arg)) { ret = -EFAULT; goto setversion_out; } inode_lock(inode); inode->i_ctime = current_time(inode); inode->i_generation = generation; inode_unlock(inode); mark_inode_dirty(inode); setversion_out: mnt_drop_write_file(filp); 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_file(filp); 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_file(filp); return 0; } default: return -ENOTTY; } }
int xfs_ioc_space( struct xfs_inode *ip, struct inode *inode, struct file *filp, int ioflags, unsigned int cmd, xfs_flock64_t *bf) { struct iattr iattr; enum xfs_prealloc_flags flags = 0; uint iolock = XFS_IOLOCK_EXCL; int error; /* * Only allow the sys admin to reserve space unless * unwritten extents are enabled. */ if (!xfs_sb_version_hasextflgbit(&ip->i_mount->m_sb) && !capable(CAP_SYS_ADMIN)) return -EPERM; if (inode->i_flags & (S_IMMUTABLE|S_APPEND)) return -EPERM; if (!(filp->f_mode & FMODE_WRITE)) return -EBADF; if (!S_ISREG(inode->i_mode)) return -EINVAL; if (filp->f_flags & O_DSYNC) flags |= XFS_PREALLOC_SYNC; if (ioflags & XFS_IO_INVIS) flags |= XFS_PREALLOC_INVISIBLE; error = mnt_want_write_file(filp); if (error) return error; xfs_ilock(ip, iolock); error = xfs_break_layouts(inode, &iolock, false); if (error) goto out_unlock; xfs_ilock(ip, XFS_MMAPLOCK_EXCL); iolock |= XFS_MMAPLOCK_EXCL; switch (bf->l_whence) { case 0: /*SEEK_SET*/ break; case 1: /*SEEK_CUR*/ bf->l_start += filp->f_pos; break; case 2: /*SEEK_END*/ bf->l_start += XFS_ISIZE(ip); break; default: error = -EINVAL; goto out_unlock; } /* * length of <= 0 for resv/unresv/zero is invalid. length for * alloc/free is ignored completely and we have no idea what userspace * might have set it to, so set it to zero to allow range * checks to pass. */ switch (cmd) { case XFS_IOC_ZERO_RANGE: case XFS_IOC_RESVSP: case XFS_IOC_RESVSP64: case XFS_IOC_UNRESVSP: case XFS_IOC_UNRESVSP64: if (bf->l_len <= 0) { error = -EINVAL; goto out_unlock; } break; default: bf->l_len = 0; break; } if (bf->l_start < 0 || bf->l_start > inode->i_sb->s_maxbytes || bf->l_start + bf->l_len < 0 || bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) { error = -EINVAL; goto out_unlock; } switch (cmd) { case XFS_IOC_ZERO_RANGE: flags |= XFS_PREALLOC_SET; error = xfs_zero_file_space(ip, bf->l_start, bf->l_len); break; case XFS_IOC_RESVSP: case XFS_IOC_RESVSP64: flags |= XFS_PREALLOC_SET; error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len, XFS_BMAPI_PREALLOC); break; case XFS_IOC_UNRESVSP: case XFS_IOC_UNRESVSP64: error = xfs_free_file_space(ip, bf->l_start, bf->l_len); break; case XFS_IOC_ALLOCSP: case XFS_IOC_ALLOCSP64: case XFS_IOC_FREESP: case XFS_IOC_FREESP64: flags |= XFS_PREALLOC_CLEAR; if (bf->l_start > XFS_ISIZE(ip)) { error = xfs_alloc_file_space(ip, XFS_ISIZE(ip), bf->l_start - XFS_ISIZE(ip), 0); if (error) goto out_unlock; } iattr.ia_valid = ATTR_SIZE; iattr.ia_size = bf->l_start; error = xfs_setattr_size(ip, &iattr); break; default: ASSERT(0); error = -EINVAL; } if (error) goto out_unlock; error = xfs_update_prealloc_flags(ip, flags); out_unlock: xfs_iunlock(ip, iolock); mnt_drop_write_file(filp); return error; }
static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) { struct inode *inode = file_inode(file); struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); int is_dir = S_ISDIR(inode->i_mode); u32 attr, oldattr; struct iattr ia; int err; err = get_user(attr, user_attr); if (err) goto out; err = mnt_want_write_file(file); if (err) goto out; inode_lock(inode); /* * ATTR_VOLUME and ATTR_DIR cannot be changed; this also * prevents the user from turning us into a VFAT * longname entry. Also, we obviously can't set * any of the NTFS attributes in the high 24 bits. */ attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR); /* Merge in ATTR_VOLUME and ATTR_DIR */ attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) | (is_dir ? ATTR_DIR : 0); oldattr = fat_make_attrs(inode); /* Equivalent to a chmod() */ ia.ia_valid = ATTR_MODE | ATTR_CTIME; ia.ia_ctime = current_time(inode); if (is_dir) ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO); else { ia.ia_mode = fat_make_mode(sbi, attr, S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO)); } /* The root directory has no attributes */ if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) { err = -EINVAL; goto out_unlock_inode; } if (sbi->options.sys_immutable && ((attr | oldattr) & ATTR_SYS) && !capable(CAP_LINUX_IMMUTABLE)) { err = -EPERM; goto out_unlock_inode; } /* * The security check is questionable... We single * out the RO attribute for checking by the security * module, just because it maps to a file mode. */ err = security_inode_setattr(file->f_path.dentry, &ia); if (err) goto out_unlock_inode; /* This MUST be done before doing anything irreversible... */ err = fat_setattr(file->f_path.dentry, &ia); if (err) goto out_unlock_inode; fsnotify_change(file->f_path.dentry, ia.ia_valid); if (sbi->options.sys_immutable) { if (attr & ATTR_SYS) inode->i_flags |= S_IMMUTABLE; else inode->i_flags &= ~S_IMMUTABLE; } fat_save_attrs(inode, attr); mark_inode_dirty(inode); out_unlock_inode: inode_unlock(inode); mnt_drop_write_file(file); out: 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 ... */ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); unsigned int flags; int err = 0; reiserfs_write_lock(inode->i_sb); switch (cmd) { case REISERFS_IOC_UNPACK: if (S_ISREG(inode->i_mode)) { if (arg) err = reiserfs_unpack(inode, filp); } else err = -ENOTTY; break; /* * 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)) { err = -ENOTTY; break; } flags = REISERFS_I(inode)->i_attrs; i_attrs_to_sd_attrs(inode, (__u16 *) & flags); err = put_user(flags, (int __user *)arg); break; case REISERFS_IOC_SETFLAGS:{ if (!reiserfs_attrs(inode->i_sb)) { err = -ENOTTY; break; } err = mnt_want_write_file(filp); if (err) break; if (!inode_owner_or_capable(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_file(filp); break; } case REISERFS_IOC_GETVERSION: err = put_user(inode->i_generation, (int __user *)arg); break; case REISERFS_IOC_SETVERSION: if (!inode_owner_or_capable(inode)) { err = -EPERM; break; } err = mnt_want_write_file(filp); if (err) break; 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_file(filp); break; default: err = -ENOTTY; } reiserfs_write_unlock(inode->i_sb); return err; }
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; 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); /* */ if (IS_NOQUOTA(inode)) goto flags_out; oldflags = ei->i_flags; /* */ jflag = flags & EXT4_JOURNAL_DATA_FL; /* */ if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) goto flags_out; } /* */ if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { if (!capable(CAP_SYS_RESOURCE)) goto flags_out; } if (oldflags & EXT4_EXTENTS_FL) { /* */ if (!(flags & EXT4_EXTENTS_FL)) { err = -EOPNOTSUPP; goto flags_out; } } else if (flags & EXT4_EXTENTS_FL) { /* */ migrate = 1; flags &= ~EXT4_EXTENTS_FL; } if (flags & EXT4_EOFBLOCKS_FL) { /* */ 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; 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_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; 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; /* */ 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; } }
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); 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 FS_IOC_GETFSMAP: return ext4_ioc_getfsmap(sb, (void __user *)arg); case EXT4_IOC_GETFLAGS: flags = ei->i_flags & EXT4_FL_USER_VISIBLE; return put_user(flags, (int __user *) arg); case EXT4_IOC_SETFLAGS: { int err; if (!inode_owner_or_capable(inode)) return -EACCES; if (get_user(flags, (int __user *) arg)) return -EFAULT; if (flags & ~EXT4_FL_USER_VISIBLE) return -EOPNOTSUPP; /* * chattr(1) grabs flags via GETFLAGS, modifies the result and * passes that to SETFLAGS. So we cannot easily make SETFLAGS * more restrictive than just silently masking off visible but * not settable flags as we always did. */ flags &= EXT4_FL_USER_MODIFIABLE; if (ext4_mask_flags(inode->i_mode, flags) != flags) return -EOPNOTSUPP; err = mnt_want_write_file(filp); if (err) return err; inode_lock(inode); err = ext4_ioctl_setflags(inode, flags); inode_unlock(inode); 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_metadata_csum(inode->i_sb)) { 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; } inode_lock(inode); handle = ext4_journal_start(inode, EXT4_HT_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 = current_time(inode); inode->i_generation = generation; err = ext4_mark_iloc_dirty(handle, inode, &iloc); } ext4_journal_stop(handle); unlock_out: inode_unlock(inode); 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_feature_bigalloc(sb)) { 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 fd donor; 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 = fdget(me.donor_fd); if (!donor.file) return -EBADF; if (!(donor.file->f_mode & FMODE_WRITE)) { err = -EBADF; goto mext_out; } if (ext4_has_feature_bigalloc(sb)) { ext4_msg(sb, KERN_ERR, "Online defrag not supported with bigalloc"); err = -EOPNOTSUPP; goto mext_out; } else if (IS_DAX(inode)) { ext4_msg(sb, KERN_ERR, "Online defrag not supported with DAX"); err = -EOPNOTSUPP; goto mext_out; } err = mnt_want_write_file(filp); if (err) goto mext_out; err = ext4_move_extents(filp, donor.file, me.orig_start, me.donor_start, me.len, &me.moved_len); mnt_drop_write_file(filp); if (copy_to_user((struct move_extent __user *)arg, &me, sizeof(me))) err = -EFAULT; mext_out: fdput(donor); return err; } case EXT4_IOC_GROUP_ADD: { struct ext4_new_group_data input; if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg, sizeof(input))) return -EFAULT; return ext4_ioctl_group_add(filp, &input); } 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. */ inode_lock((inode)); err = ext4_ext_migrate(inode); inode_unlock((inode)); 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_SWAP_BOOT: { int err; if (!(filp->f_mode & FMODE_WRITE)) return -EBADF; err = mnt_want_write_file(filp); if (err) return err; err = swap_inode_boot_loader(sb, inode); mnt_drop_write_file(filp); return err; } case EXT4_IOC_RESIZE_FS: { ext4_fsblk_t n_blocks_count; int err = 0, err2 = 0; ext4_group_t o_group = EXT4_SB(sb)->s_groups_count; if (copy_from_user(&n_blocks_count, (__u64 __user *)arg, sizeof(__u64))) { return -EFAULT; } err = ext4_resize_begin(sb); if (err) return err; err = mnt_want_write_file(filp); 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_file(filp); if (!err && (o_group > EXT4_SB(sb)->s_groups_count) && ext4_has_group_desc_csum(sb) && test_opt(sb, INIT_INODE_TABLE)) err = ext4_register_li_request(sb, o_group); 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 (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; } case EXT4_IOC_PRECACHE_EXTENTS: return ext4_ext_precache(inode); case EXT4_IOC_SET_ENCRYPTION_POLICY: if (!ext4_has_feature_encrypt(sb)) return -EOPNOTSUPP; return fscrypt_ioctl_set_policy(filp, (const void __user *)arg); case EXT4_IOC_GET_ENCRYPTION_PWSALT: { #ifdef CONFIG_EXT4_FS_ENCRYPTION int err, err2; struct ext4_sb_info *sbi = EXT4_SB(sb); handle_t *handle; if (!ext4_has_feature_encrypt(sb)) return -EOPNOTSUPP; if (uuid_is_zero(sbi->s_es->s_encrypt_pw_salt)) { err = mnt_want_write_file(filp); if (err) return err; handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto pwsalt_err_exit; } err = ext4_journal_get_write_access(handle, sbi->s_sbh); if (err) goto pwsalt_err_journal; generate_random_uuid(sbi->s_es->s_encrypt_pw_salt); err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh); pwsalt_err_journal: err2 = ext4_journal_stop(handle); if (err2 && !err) err = err2; pwsalt_err_exit: mnt_drop_write_file(filp); if (err) return err; } if (copy_to_user((void __user *) arg, sbi->s_es->s_encrypt_pw_salt, 16)) return -EFAULT; return 0; #else return -EOPNOTSUPP; #endif } case EXT4_IOC_GET_ENCRYPTION_POLICY: return fscrypt_ioctl_get_policy(filp, (void __user *)arg); case EXT4_IOC_FSGETXATTR: { struct fsxattr fa; memset(&fa, 0, sizeof(struct fsxattr)); fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE); if (ext4_has_feature_project(inode->i_sb)) { fa.fsx_projid = (__u32)from_kprojid(&init_user_ns, EXT4_I(inode)->i_projid); } if (copy_to_user((struct fsxattr __user *)arg, &fa, sizeof(fa))) return -EFAULT; return 0; } case EXT4_IOC_FSSETXATTR: { struct fsxattr fa; int err; if (copy_from_user(&fa, (struct fsxattr __user *)arg, sizeof(fa))) return -EFAULT; /* Make sure caller has proper permission */ if (!inode_owner_or_capable(inode)) return -EACCES; if (fa.fsx_xflags & ~EXT4_SUPPORTED_FS_XFLAGS) return -EOPNOTSUPP; flags = ext4_xflags_to_iflags(fa.fsx_xflags); if (ext4_mask_flags(inode->i_mode, flags) != flags) return -EOPNOTSUPP; err = mnt_want_write_file(filp); if (err) return err; inode_lock(inode); flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) | (flags & EXT4_FL_XFLAG_VISIBLE); err = ext4_ioctl_setflags(inode, flags); inode_unlock(inode); mnt_drop_write_file(filp); if (err) return err; err = ext4_ioctl_setproject(filp, fa.fsx_projid); if (err) return err; return 0; } case EXT4_IOC_SHUTDOWN: return ext4_shutdown(sb, arg); default: return -ENOTTY; } }
long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_path.dentry->d_inode; unsigned int flags; int new_clusters; int status; struct ocfs2_space_resv sr; struct ocfs2_new_group_input input; struct reflink_arguments args; const char *old_path, *new_path; bool preserve; struct ocfs2_info info; switch (cmd) { case OCFS2_IOC_GETFLAGS: status = ocfs2_get_inode_attr(inode, &flags); if (status < 0) return status; flags &= OCFS2_FL_VISIBLE; return put_user(flags, (int __user *) arg); case OCFS2_IOC_SETFLAGS: if (get_user(flags, (int __user *) arg)) return -EFAULT; status = mnt_want_write_file(filp); if (status) return status; status = ocfs2_set_inode_attr(inode, flags, OCFS2_FL_MODIFIABLE); mnt_drop_write_file(filp); return status; case OCFS2_IOC_RESVSP: case OCFS2_IOC_RESVSP64: case OCFS2_IOC_UNRESVSP: case OCFS2_IOC_UNRESVSP64: if (copy_from_user(&sr, (int __user *) arg, sizeof(sr))) return -EFAULT; return ocfs2_change_file_space(filp, cmd, &sr); case OCFS2_IOC_GROUP_EXTEND: if (!capable(CAP_SYS_RESOURCE)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif if (get_user(new_clusters, (int __user *)arg)) return -EFAULT; return ocfs2_group_extend(inode, new_clusters); case OCFS2_IOC_GROUP_ADD: case OCFS2_IOC_GROUP_ADD64: if (!capable(CAP_SYS_RESOURCE)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif if (copy_from_user(&input, (int __user *) arg, sizeof(input))) return -EFAULT; return ocfs2_group_add(inode, &input); case OCFS2_IOC_REFLINK: if (copy_from_user(&args, (struct reflink_arguments *)arg, sizeof(args))) return -EFAULT; old_path = (const char *)(unsigned long)args.old_path; new_path = (const char *)(unsigned long)args.new_path; preserve = (args.preserve != 0); return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); case OCFS2_IOC_INFO: if (copy_from_user(&info, (struct ocfs2_info __user *)arg, sizeof(struct ocfs2_info))) return -EFAULT; return ocfs2_info_handle(inode, &info, 0); case FITRIM: { struct super_block *sb = inode->i_sb; struct fstrim_range range; int ret = 0; if (!capable(CAP_SYS_ADMIN)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif if (copy_from_user(&range, (struct fstrim_range *)arg, sizeof(range))) return -EFAULT; ret = ocfs2_trim_fs(sb, &range); if (ret < 0) return ret; if (copy_to_user((struct fstrim_range *)arg, &range, sizeof(range))) return -EFAULT; return 0; } case OCFS2_IOC_MOVE_EXT: return ocfs2_ioctl_move_extents(filp, (void __user *)arg); default: return -ENOTTY; } }
long pmfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct pmfs_inode *pi; struct super_block *sb = inode->i_sb; unsigned int flags; int ret; pmfs_transaction_t *trans; pi = pmfs_get_inode(sb, inode->i_ino); if (!pi) return -EACCES; switch (cmd) { case FS_IOC_GETFLAGS: flags = le32_to_cpu(pi->i_flags) & PMFS_FL_USER_VISIBLE; return put_user(flags, (int __user *)arg); case FS_IOC_SETFLAGS: { unsigned int oldflags; ret = mnt_want_write_file(filp); if (ret) return ret; if (!inode_owner_or_capable(inode)) { ret = -EPERM; goto flags_out; } if (get_user(flags, (int __user *)arg)) { ret = -EFAULT; goto flags_out; } mutex_lock(&inode->i_mutex); oldflags = le32_to_cpu(pi->i_flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto flags_out; } } if (!S_ISDIR(inode->i_mode)) flags &= ~FS_DIRSYNC_FL; flags = flags & FS_FL_USER_MODIFIABLE; flags |= oldflags & ~FS_FL_USER_MODIFIABLE; inode->i_ctime = CURRENT_TIME_SEC; /*TODO: This transaction can be avoided if we had RTM */ trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, MAX_DATA_PER_LENTRY, LE_DATA); pmfs_memunlock_inode(sb, pi); pi->i_flags = cpu_to_le32(flags); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pmfs_set_inode_flags(inode, pi); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); out: mutex_unlock(&inode->i_mutex); flags_out: mnt_drop_write_file(filp); return ret; } case FS_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *)arg); case FS_IOC_SETVERSION: { __u32 generation; if (!inode_owner_or_capable(inode)) return -EPERM; ret = mnt_want_write_file(filp); if (ret) return ret; if (get_user(generation, (int __user *)arg)) { ret = -EFAULT; goto setversion_out; } mutex_lock(&inode->i_mutex); trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, sizeof(*pi), LE_DATA); inode->i_ctime = CURRENT_TIME_SEC; inode->i_generation = generation; pmfs_memunlock_inode(sb, pi); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pi->i_generation = cpu_to_le32(inode->i_generation); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); mutex_unlock(&inode->i_mutex); setversion_out: mnt_drop_write_file(filp); return ret; } default: return -ENOTTY; } }
static int ext4_ioctl_setproject(struct file *filp, __u32 projid) { struct inode *inode = file_inode(filp); struct super_block *sb = inode->i_sb; struct ext4_inode_info *ei = EXT4_I(inode); int err, rc; handle_t *handle; kprojid_t kprojid; struct ext4_iloc iloc; struct ext4_inode *raw_inode; struct dquot *transfer_to[MAXQUOTAS] = { }; if (!ext4_has_feature_project(sb)) { if (projid != EXT4_DEF_PROJID) return -EOPNOTSUPP; else return 0; } if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE) return -EOPNOTSUPP; kprojid = make_kprojid(&init_user_ns, (projid_t)projid); if (projid_eq(kprojid, EXT4_I(inode)->i_projid)) return 0; err = mnt_want_write_file(filp); if (err) return err; err = -EPERM; inode_lock(inode); /* Is it quota file? Do not allow user to mess with it */ if (ext4_is_quota_file(inode)) goto out_unlock; err = ext4_get_inode_loc(inode, &iloc); if (err) goto out_unlock; raw_inode = ext4_raw_inode(&iloc); if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) { err = ext4_expand_extra_isize(inode, EXT4_SB(sb)->s_want_extra_isize, &iloc); if (err) goto out_unlock; } else { brelse(iloc.bh); } dquot_initialize(inode); handle = ext4_journal_start(inode, EXT4_HT_QUOTA, EXT4_QUOTA_INIT_BLOCKS(sb) + EXT4_QUOTA_DEL_BLOCKS(sb) + 3); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto out_unlock; } err = ext4_reserve_inode_write(handle, inode, &iloc); if (err) goto out_stop; transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid)); if (!IS_ERR(transfer_to[PRJQUOTA])) { /* __dquot_transfer() calls back ext4_get_inode_usage() which * counts xattr inode references. */ down_read(&EXT4_I(inode)->xattr_sem); err = __dquot_transfer(inode, transfer_to); up_read(&EXT4_I(inode)->xattr_sem); dqput(transfer_to[PRJQUOTA]); if (err) goto out_dirty; } EXT4_I(inode)->i_projid = kprojid; inode->i_ctime = current_time(inode); out_dirty: rc = ext4_mark_iloc_dirty(handle, inode, &iloc); if (!err) err = rc; out_stop: ext4_journal_stop(handle); out_unlock: inode_unlock(inode); mnt_drop_write_file(filp); return err; }
int xfs_ioc_space( struct xfs_inode *ip, struct inode *inode, struct file *filp, int ioflags, unsigned int cmd, xfs_flock64_t *bf) { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; struct iattr iattr; bool setprealloc = false; bool clrprealloc = false; int error; /* * Only allow the sys admin to reserve space unless * unwritten extents are enabled. */ if (!xfs_sb_version_hasextflgbit(&ip->i_mount->m_sb) && !capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (inode->i_flags & (S_IMMUTABLE|S_APPEND)) return -XFS_ERROR(EPERM); if (!(filp->f_mode & FMODE_WRITE)) return -XFS_ERROR(EBADF); if (!S_ISREG(inode->i_mode)) return -XFS_ERROR(EINVAL); error = mnt_want_write_file(filp); if (error) return error; xfs_ilock(ip, XFS_IOLOCK_EXCL); switch (bf->l_whence) { case 0: /*SEEK_SET*/ break; case 1: /*SEEK_CUR*/ bf->l_start += filp->f_pos; break; case 2: /*SEEK_END*/ bf->l_start += XFS_ISIZE(ip); break; default: error = XFS_ERROR(EINVAL); goto out_unlock; } /* * length of <= 0 for resv/unresv/zero is invalid. length for * alloc/free is ignored completely and we have no idea what userspace * might have set it to, so set it to zero to allow range * checks to pass. */ switch (cmd) { case XFS_IOC_ZERO_RANGE: case XFS_IOC_RESVSP: case XFS_IOC_RESVSP64: case XFS_IOC_UNRESVSP: case XFS_IOC_UNRESVSP64: if (bf->l_len <= 0) { error = XFS_ERROR(EINVAL); goto out_unlock; } break; default: bf->l_len = 0; break; } if (bf->l_start < 0 || bf->l_start > mp->m_super->s_maxbytes || bf->l_start + bf->l_len < 0 || bf->l_start + bf->l_len >= mp->m_super->s_maxbytes) { error = XFS_ERROR(EINVAL); goto out_unlock; } switch (cmd) { case XFS_IOC_ZERO_RANGE: error = xfs_zero_file_space(ip, bf->l_start, bf->l_len); if (!error) setprealloc = true; break; case XFS_IOC_RESVSP: case XFS_IOC_RESVSP64: error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len, XFS_BMAPI_PREALLOC); if (!error) setprealloc = true; break; case XFS_IOC_UNRESVSP: case XFS_IOC_UNRESVSP64: error = xfs_free_file_space(ip, bf->l_start, bf->l_len); break; case XFS_IOC_ALLOCSP: case XFS_IOC_ALLOCSP64: case XFS_IOC_FREESP: case XFS_IOC_FREESP64: if (bf->l_start > XFS_ISIZE(ip)) { error = xfs_alloc_file_space(ip, XFS_ISIZE(ip), bf->l_start - XFS_ISIZE(ip), 0); if (error) goto out_unlock; } iattr.ia_valid = ATTR_SIZE; iattr.ia_size = bf->l_start; error = xfs_setattr_size(ip, &iattr); if (!error) clrprealloc = true; break; default: ASSERT(0); error = XFS_ERROR(EINVAL); } if (error) goto out_unlock; tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_writeid, 0, 0); if (error) { xfs_trans_cancel(tp, 0); goto out_unlock; } xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); if (!(ioflags & IO_INVIS)) { ip->i_d.di_mode &= ~S_ISUID; if (ip->i_d.di_mode & S_IXGRP) ip->i_d.di_mode &= ~S_ISGID; xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); } if (setprealloc) ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC; else if (clrprealloc) ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); if (filp->f_flags & O_DSYNC) xfs_trans_set_sync(tp); error = xfs_trans_commit(tp, 0); out_unlock: xfs_iunlock(ip, XFS_IOLOCK_EXCL); mnt_drop_write_file(filp); return -error; }
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_file(filp); 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_file(filp); 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_file(filp); if (err) return err; if (get_user(generation, (int __user *) arg)) { err = -EFAULT; goto setversion_out; } handle = ext3_journal_start(inode, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto setversion_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); setversion_out: mnt_drop_write_file(filp); return err; } 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_file(filp); 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_file(filp); 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_file(filp); 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_file(filp); 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_file(filp); 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_file(filp); 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 __user *)arg, sizeof(range))) return -EFAULT; ret = ext3_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; } }
long nova_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct address_space *mapping = filp->f_mapping; struct inode *inode = mapping->host; struct nova_inode *pi; struct super_block *sb = inode->i_sb; unsigned int flags; u64 new_tail = 0; int ret; pi = nova_get_inode(sb, inode); if (!pi) return -EACCES; switch (cmd) { case FS_IOC_GETFLAGS: flags = le32_to_cpu(pi->i_flags) & NOVA_FL_USER_VISIBLE; return put_user(flags, (int __user *)arg); case FS_IOC_SETFLAGS: { unsigned int oldflags; ret = mnt_want_write_file(filp); if (ret) return ret; if (!inode_owner_or_capable(inode)) { ret = -EPERM; goto flags_out; } if (get_user(flags, (int __user *)arg)) { ret = -EFAULT; goto flags_out; } mutex_lock(&inode->i_mutex); oldflags = le32_to_cpu(pi->i_flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto flags_out; } } if (!S_ISDIR(inode->i_mode)) flags &= ~FS_DIRSYNC_FL; flags = flags & FS_FL_USER_MODIFIABLE; flags |= oldflags & ~FS_FL_USER_MODIFIABLE; inode->i_ctime = CURRENT_TIME_SEC; nova_set_inode_flags(inode, pi, flags); nova_memunlock_inode(sb, pi); ret = nova_append_link_change_entry(sb, pi, inode, 0, &new_tail); if (!ret) nova_update_tail(pi, new_tail); nova_memlock_inode(sb, pi); mutex_unlock(&inode->i_mutex); flags_out: mnt_drop_write_file(filp); return ret; } case FS_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *)arg); case FS_IOC_SETVERSION: { __u32 generation; if (!inode_owner_or_capable(inode)) return -EPERM; ret = mnt_want_write_file(filp); if (ret) return ret; if (get_user(generation, (int __user *)arg)) { ret = -EFAULT; goto setversion_out; } mutex_lock(&inode->i_mutex); inode->i_ctime = CURRENT_TIME_SEC; inode->i_generation = generation; nova_memunlock_inode(sb, pi); ret = nova_append_link_change_entry(sb, pi, inode, 0, &new_tail); if (!ret) nova_update_tail(pi, new_tail); nova_memlock_inode(sb, pi); mutex_unlock(&inode->i_mutex); setversion_out: mnt_drop_write_file(filp); return ret; } case NOVA_PRINT_TIMING: { nova_print_timing_stats(sb); return 0; } case NOVA_CLEAR_STATS: { nova_clear_stats(); return 0; } case NOVA_PRINT_LOG: { nova_print_inode_log(sb, inode); return 0; } case NOVA_PRINT_LOG_PAGES: { nova_print_inode_log_pages(sb, inode); return 0; } case NOVA_PRINT_FREE_LISTS: { nova_print_free_lists(sb); return 0; } default: return -ENOTTY; } }
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_file(filp); if (err) return err; if (!inode_owner_or_capable(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; if (IS_NOQUOTA(inode)) { err = -EPERM; goto setflags_out; } mutex_lock(&inode->i_mutex); jfs_get_inode_flags(jfs_inode); oldflags = jfs_inode->mode2; 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_file(filp); return err; } default: return -ENOTTY; } }
long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); struct jfs_inode_info *jfs_inode = JFS_IP(inode); unsigned int flags; switch (cmd) { case JFS_IOC_GETFLAGS: 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_file(filp); if (err) return err; if (!inode_owner_or_capable(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 */ inode_lock(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)) { inode_unlock(inode); 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); inode_unlock(inode); inode->i_ctime = current_time(inode); mark_inode_dirty(inode); setflags_out: mnt_drop_write_file(filp); return err; } case FITRIM: { struct super_block *sb = inode->i_sb; struct request_queue *q = bdev_get_queue(sb->s_bdev); struct fstrim_range range; s64 ret = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!blk_queue_discard(q)) { jfs_warn("FITRIM not supported on device"); return -EOPNOTSUPP; } if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range))) return -EFAULT; range.minlen = max_t(unsigned int, range.minlen, q->limits.discard_granularity); ret = jfs_ioc_trim(inode, &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; } }
/* * Note: some of the ioctl's return positive numbers as a * byte count indicating success, such as readlink_by_handle. * So we don't "sign flip" like most other routines. This means * true errors need to be returned as a negative value. */ long xfs_file_ioctl( struct file *filp, unsigned int cmd, unsigned long p) { struct inode *inode = file_inode(filp); struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; void __user *arg = (void __user *)p; int ioflags = 0; int error; if (filp->f_mode & FMODE_NOCMTIME) ioflags |= IO_INVIS; trace_xfs_file_ioctl(ip); switch (cmd) { case FITRIM: return xfs_ioc_trim(mp, arg); case XFS_IOC_ALLOCSP: case XFS_IOC_FREESP: case XFS_IOC_RESVSP: case XFS_IOC_UNRESVSP: case XFS_IOC_ALLOCSP64: case XFS_IOC_FREESP64: case XFS_IOC_RESVSP64: case XFS_IOC_UNRESVSP64: case XFS_IOC_ZERO_RANGE: { xfs_flock64_t bf; if (copy_from_user(&bf, arg, sizeof(bf))) return -XFS_ERROR(EFAULT); return xfs_ioc_space(ip, inode, filp, ioflags, cmd, &bf); } case XFS_IOC_DIOINFO: { struct dioattr da; xfs_buftarg_t *target = XFS_IS_REALTIME_INODE(ip) ? mp->m_rtdev_targp : mp->m_ddev_targp; da.d_mem = da.d_miniosz = 1 << target->bt_sshift; da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1); if (copy_to_user(arg, &da, sizeof(da))) return -XFS_ERROR(EFAULT); return 0; } case XFS_IOC_FSBULKSTAT_SINGLE: case XFS_IOC_FSBULKSTAT: case XFS_IOC_FSINUMBERS: return xfs_ioc_bulkstat(mp, cmd, arg); case XFS_IOC_FSGEOMETRY_V1: return xfs_ioc_fsgeometry_v1(mp, arg); case XFS_IOC_FSGEOMETRY: return xfs_ioc_fsgeometry(mp, arg); case XFS_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *)arg); case XFS_IOC_FSGETXATTR: return xfs_ioc_fsgetxattr(ip, 0, arg); case XFS_IOC_FSGETXATTRA: return xfs_ioc_fsgetxattr(ip, 1, arg); case XFS_IOC_FSSETXATTR: return xfs_ioc_fssetxattr(ip, filp, arg); case XFS_IOC_GETXFLAGS: return xfs_ioc_getxflags(ip, arg); case XFS_IOC_SETXFLAGS: return xfs_ioc_setxflags(ip, filp, arg); case XFS_IOC_FSSETDM: { struct fsdmidata dmi; if (copy_from_user(&dmi, arg, sizeof(dmi))) return -XFS_ERROR(EFAULT); error = mnt_want_write_file(filp); if (error) return error; error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask, dmi.fsd_dmstate); mnt_drop_write_file(filp); return -error; } case XFS_IOC_GETBMAP: case XFS_IOC_GETBMAPA: return xfs_ioc_getbmap(ip, ioflags, cmd, arg); case XFS_IOC_GETBMAPX: return xfs_ioc_getbmapx(ip, arg); case XFS_IOC_FD_TO_HANDLE: case XFS_IOC_PATH_TO_HANDLE: case XFS_IOC_PATH_TO_FSHANDLE: { xfs_fsop_handlereq_t hreq; if (copy_from_user(&hreq, arg, sizeof(hreq))) return -XFS_ERROR(EFAULT); return xfs_find_handle(cmd, &hreq); } case XFS_IOC_OPEN_BY_HANDLE: { xfs_fsop_handlereq_t hreq; if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t))) return -XFS_ERROR(EFAULT); return xfs_open_by_handle(filp, &hreq); } case XFS_IOC_FSSETDM_BY_HANDLE: return xfs_fssetdm_by_handle(filp, arg); case XFS_IOC_READLINK_BY_HANDLE: { xfs_fsop_handlereq_t hreq; if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t))) return -XFS_ERROR(EFAULT); return xfs_readlink_by_handle(filp, &hreq); } case XFS_IOC_ATTRLIST_BY_HANDLE: return xfs_attrlist_by_handle(filp, arg); case XFS_IOC_ATTRMULTI_BY_HANDLE: return xfs_attrmulti_by_handle(filp, arg); case XFS_IOC_SWAPEXT: { struct xfs_swapext sxp; if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t))) return -XFS_ERROR(EFAULT); error = mnt_want_write_file(filp); if (error) return error; error = xfs_swapext(&sxp); mnt_drop_write_file(filp); return -error; } case XFS_IOC_FSCOUNTS: { xfs_fsop_counts_t out; error = xfs_fs_counts(mp, &out); if (error) return -error; if (copy_to_user(arg, &out, sizeof(out))) return -XFS_ERROR(EFAULT); return 0; } case XFS_IOC_SET_RESBLKS: { xfs_fsop_resblks_t inout; __uint64_t in; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (mp->m_flags & XFS_MOUNT_RDONLY) return -XFS_ERROR(EROFS); if (copy_from_user(&inout, arg, sizeof(inout))) return -XFS_ERROR(EFAULT); error = mnt_want_write_file(filp); if (error) return error; /* input parameter is passed in resblks field of structure */ in = inout.resblks; error = xfs_reserve_blocks(mp, &in, &inout); mnt_drop_write_file(filp); if (error) return -error; if (copy_to_user(arg, &inout, sizeof(inout))) return -XFS_ERROR(EFAULT); return 0; } case XFS_IOC_GET_RESBLKS: { xfs_fsop_resblks_t out; if (!capable(CAP_SYS_ADMIN)) return -EPERM; error = xfs_reserve_blocks(mp, NULL, &out); if (error) return -error; if (copy_to_user(arg, &out, sizeof(out))) return -XFS_ERROR(EFAULT); return 0; } case XFS_IOC_FSGROWFSDATA: { xfs_growfs_data_t in; if (copy_from_user(&in, arg, sizeof(in))) return -XFS_ERROR(EFAULT); error = mnt_want_write_file(filp); if (error) return error; error = xfs_growfs_data(mp, &in); mnt_drop_write_file(filp); return -error; } case XFS_IOC_FSGROWFSLOG: { xfs_growfs_log_t in; if (copy_from_user(&in, arg, sizeof(in))) return -XFS_ERROR(EFAULT); error = mnt_want_write_file(filp); if (error) return error; error = xfs_growfs_log(mp, &in); mnt_drop_write_file(filp); return -error; } case XFS_IOC_FSGROWFSRT: { xfs_growfs_rt_t in; if (copy_from_user(&in, arg, sizeof(in))) return -XFS_ERROR(EFAULT); error = mnt_want_write_file(filp); if (error) return error; error = xfs_growfs_rt(mp, &in); mnt_drop_write_file(filp); return -error; } case XFS_IOC_GOINGDOWN: { __uint32_t in; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (get_user(in, (__uint32_t __user *)arg)) return -XFS_ERROR(EFAULT); error = xfs_fs_goingdown(mp, in); return -error; } case XFS_IOC_ERROR_INJECTION: { xfs_error_injection_t in; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (copy_from_user(&in, arg, sizeof(in))) return -XFS_ERROR(EFAULT); error = xfs_errortag_add(in.errtag, mp); return -error; } case XFS_IOC_ERROR_CLEARALL: if (!capable(CAP_SYS_ADMIN)) return -EPERM; error = xfs_errortag_clearall(mp, 1); return -error; case XFS_IOC_FREE_EOFBLOCKS: { struct xfs_eofblocks eofb; if (copy_from_user(&eofb, arg, sizeof(eofb))) return -XFS_ERROR(EFAULT); if (eofb.eof_version != XFS_EOFBLOCKS_VERSION) return -XFS_ERROR(EINVAL); if (eofb.eof_flags & ~XFS_EOF_FLAGS_VALID) return -XFS_ERROR(EINVAL); if (memchr_inv(&eofb.pad32, 0, sizeof(eofb.pad32)) || memchr_inv(eofb.pad64, 0, sizeof(eofb.pad64))) return -XFS_ERROR(EINVAL); error = xfs_icache_free_eofblocks(mp, &eofb); return -error; } default: return -ENOTTY; } }
int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same) { struct file_dedupe_range_info *info; struct inode *src = file_inode(file); u64 off; u64 len; int i; int ret; bool is_admin = capable(CAP_SYS_ADMIN); u16 count = same->dest_count; struct file *dst_file; loff_t dst_off; ssize_t deduped; if (!(file->f_mode & FMODE_READ)) return -EINVAL; if (same->reserved1 || same->reserved2) return -EINVAL; off = same->src_offset; len = same->src_length; ret = -EISDIR; if (S_ISDIR(src->i_mode)) goto out; ret = -EINVAL; if (!S_ISREG(src->i_mode)) goto out; ret = clone_verify_area(file, off, len, false); if (ret < 0) goto out; ret = 0; /* pre-format output fields to sane values */ for (i = 0; i < count; i++) { same->info[i].bytes_deduped = 0ULL; same->info[i].status = FILE_DEDUPE_RANGE_SAME; } for (i = 0, info = same->info; i < count; i++, info++) { struct inode *dst; struct fd dst_fd = fdget(info->dest_fd); dst_file = dst_fd.file; if (!dst_file) { info->status = -EBADF; goto next_loop; } dst = file_inode(dst_file); ret = mnt_want_write_file(dst_file); if (ret) { info->status = ret; goto next_loop; } dst_off = info->dest_offset; ret = clone_verify_area(dst_file, dst_off, len, true); if (ret < 0) { info->status = ret; goto next_file; } ret = 0; if (info->reserved) { info->status = -EINVAL; } else if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) { info->status = -EINVAL; } else if (file->f_path.mnt != dst_file->f_path.mnt) { info->status = -EXDEV; } else if (S_ISDIR(dst->i_mode)) { info->status = -EISDIR; } else if (dst_file->f_op->dedupe_file_range == NULL) { info->status = -EINVAL; } else { deduped = dst_file->f_op->dedupe_file_range(file, off, len, dst_file, info->dest_offset); if (deduped == -EBADE) info->status = FILE_DEDUPE_RANGE_DIFFERS; else if (deduped < 0) info->status = deduped; else info->bytes_deduped += deduped; } next_file: mnt_drop_write_file(dst_file); next_loop: fdput(dst_fd); } out: return ret; }
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; unsigned char *attr_name; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t))) return -EFAULT; /* overflow check */ if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t)) return -E2BIG; 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; } error = -ENOMEM; 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((char *)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( d_inode(dentry), 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_file(parfilp); if (ops[i].am_error) break; ops[i].am_error = xfs_attrmulti_attr_set( d_inode(dentry), attr_name, ops[i].am_attrvalue, ops[i].am_length, ops[i].am_flags); mnt_drop_write_file(parfilp); break; case ATTR_OP_REMOVE: ops[i].am_error = mnt_want_write_file(parfilp); if (ops[i].am_error) break; ops[i].am_error = xfs_attrmulti_attr_remove( d_inode(dentry), attr_name, ops[i].am_flags); mnt_drop_write_file(parfilp); break; default: ops[i].am_error = -EINVAL; } } if (copy_to_user(am_hreq.ops, ops, size)) error = -EFAULT; kfree(attr_name); out_kfree_ops: kfree(ops); out_dput: dput(dentry); return error; }
static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, unsigned long srcfd, u64 off, u64 len, u64 destoff) { int rc; struct cifsFileInfo *smb_file_target = dst_file->private_data; struct inode *target_inode = file_inode(dst_file); struct cifs_tcon *target_tcon; struct fd src_file; struct cifsFileInfo *smb_file_src; struct inode *src_inode; struct cifs_tcon *src_tcon; cifs_dbg(FYI, "ioctl clone range\n"); /* the destination must be opened for writing */ if (!(dst_file->f_mode & FMODE_WRITE)) { cifs_dbg(FYI, "file target not open for write\n"); return -EINVAL; } /* check if target volume is readonly and take reference */ rc = mnt_want_write_file(dst_file); if (rc) { cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); return rc; } src_file = fdget(srcfd); if (!src_file.file) { rc = -EBADF; goto out_drop_write; } if ((!src_file.file->private_data) || (!dst_file->private_data)) { rc = -EBADF; cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); goto out_fput; } rc = -EXDEV; smb_file_target = dst_file->private_data; smb_file_src = src_file.file->private_data; src_tcon = tlink_tcon(smb_file_src->tlink); target_tcon = tlink_tcon(smb_file_target->tlink); /* check if source and target are on same tree connection */ if (src_tcon != target_tcon) { cifs_dbg(VFS, "file copy src and target on different volume\n"); goto out_fput; } src_inode = src_file.file->f_dentry->d_inode; /* * Note: cifs case is easier than btrfs since server responsible for * checks for proper open modes and file type and if it wants * server could even support copy of range where source = target */ /* so we do not deadlock racing two ioctls on same files */ if (target_inode < src_inode) { mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD); } else { mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_CHILD); } /* determine range to clone */ rc = -EINVAL; if (off + len > src_inode->i_size || off + len < off) goto out_unlock; if (len == 0) len = src_inode->i_size - off; cifs_dbg(FYI, "about to flush pages\n"); /* should we flush first and last page first */ truncate_inode_pages_range(&target_inode->i_data, destoff, PAGE_CACHE_ALIGN(destoff + len)-1); if (target_tcon->ses->server->ops->clone_range) rc = target_tcon->ses->server->ops->clone_range(xid, smb_file_src, smb_file_target, off, len, destoff); /* force revalidate of size and timestamps of target file now that target is updated on the server */ CIFS_I(target_inode)->time = 0; out_unlock: /* although unlocking in the reverse order from locking is not strictly necessary here it is a little cleaner to be consistent */ if (target_inode < src_inode) { mutex_unlock(&src_inode->i_mutex); mutex_unlock(&target_inode->i_mutex); } else { mutex_unlock(&target_inode->i_mutex); mutex_unlock(&src_inode->i_mutex); } out_fput: fdput(src_file); out_drop_write: mnt_drop_write_file(dst_file); return rc; }
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); 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 ((flags ^ oldflags) & EXT4_EXTENTS_FL) migrate = 1; 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, EXT4_HT_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) { if (flags & EXT4_EXTENTS_FL) err = ext4_ext_migrate(inode); else err = ext4_ind_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_metadata_csum(inode->i_sb)) { 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, EXT4_HT_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 fd donor; 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 = fdget(me.donor_fd); if (!donor.file) return -EBADF; if (!(donor.file->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"); err = -EOPNOTSUPP; goto mext_out; } err = mnt_want_write_file(filp); if (err) goto mext_out; err = ext4_move_extents(filp, donor.file, me.orig_start, me.donor_start, me.len, &me.moved_len); mnt_drop_write_file(filp); if (copy_to_user((struct move_extent __user *)arg, &me, sizeof(me))) err = -EFAULT; mext_out: fdput(donor); 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); if (!err && ext4_has_group_desc_csum(sb) && test_opt(sb, INIT_INODE_TABLE)) err = ext4_register_li_request(sb, input.group); 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_SWAP_BOOT: { int err; if (!(filp->f_mode & FMODE_WRITE)) return -EBADF; err = mnt_want_write_file(filp); if (err) return err; err = swap_inode_boot_loader(sb, inode); mnt_drop_write_file(filp); return err; } case EXT4_IOC_RESIZE_FS: { ext4_fsblk_t n_blocks_count; int err = 0, err2 = 0; ext4_group_t o_group = EXT4_SB(sb)->s_groups_count; 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 (copy_from_user(&n_blocks_count, (__u64 __user *)arg, sizeof(__u64))) { return -EFAULT; } err = ext4_resize_begin(sb); if (err) return err; err = mnt_want_write_file(filp); 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_file(filp); if (!err && (o_group > EXT4_SB(sb)->s_groups_count) && ext4_has_group_desc_csum(sb) && test_opt(sb, INIT_INODE_TABLE)) err = ext4_register_li_request(sb, o_group); resizefs_out: ext4_resize_end(sb); return err; } case FIDTRIM: case FITRIM: { struct request_queue *q = bdev_get_queue(sb->s_bdev); struct fstrim_range range; int ret = 0; int flags = cmd == FIDTRIM ? BLKDEV_DISCARD_SECURE : 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!blk_queue_discard(q)) return -EOPNOTSUPP; if ((flags & BLKDEV_DISCARD_SECURE) && !blk_queue_secdiscard(q)) 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, flags); if (ret < 0) return ret; if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range))) return -EFAULT; return 0; } case EXT4_IOC_PRECACHE_EXTENTS: return ext4_ext_precache(inode); default: return -ENOTTY; } }
long pmfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct address_space *mapping = filp->f_mapping; struct inode *inode = mapping->host; struct pmfs_inode *pi; struct super_block *sb = inode->i_sb; unsigned int flags; int ret; pmfs_transaction_t *trans; pi = pmfs_get_inode(sb, inode->i_ino); if (!pi) return -EACCES; switch (cmd) { case FS_IOC_GETFLAGS: flags = le32_to_cpu(pi->i_flags) & PMFS_FL_USER_VISIBLE; return put_user(flags, (int __user *)arg); case FS_IOC_SETFLAGS: { unsigned int oldflags; ret = mnt_want_write_file(filp); if (ret) return ret; if (!inode_owner_or_capable(inode)) { ret = -EPERM; goto flags_out; } if (get_user(flags, (int __user *)arg)) { ret = -EFAULT; goto flags_out; } mutex_lock(&inode->i_mutex); oldflags = le32_to_cpu(pi->i_flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto flags_out; } } if (!S_ISDIR(inode->i_mode)) flags &= ~FS_DIRSYNC_FL; flags = flags & FS_FL_USER_MODIFIABLE; flags |= oldflags & ~FS_FL_USER_MODIFIABLE; inode->i_ctime = CURRENT_TIME_SEC; trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, MAX_DATA_PER_LENTRY, LE_DATA); pmfs_memunlock_inode(sb, pi); pi->i_flags = cpu_to_le32(flags); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pmfs_set_inode_flags(inode, pi); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); out: mutex_unlock(&inode->i_mutex); flags_out: mnt_drop_write_file(filp); return ret; } case FS_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *)arg); case FS_IOC_SETVERSION: { __u32 generation; if (!inode_owner_or_capable(inode)) return -EPERM; ret = mnt_want_write_file(filp); if (ret) return ret; if (get_user(generation, (int __user *)arg)) { ret = -EFAULT; goto setversion_out; } mutex_lock(&inode->i_mutex); trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, sizeof(*pi), LE_DATA); inode->i_ctime = CURRENT_TIME_SEC; inode->i_generation = generation; pmfs_memunlock_inode(sb, pi); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pi->i_generation = cpu_to_le32(inode->i_generation); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); mutex_unlock(&inode->i_mutex); setversion_out: mnt_drop_write_file(filp); return ret; } case FS_PMFS_FSYNC: { struct sync_range packet; copy_from_user(&packet, (void *)arg, sizeof(struct sync_range)); pmfs_fsync(filp, packet.offset, packet.offset + packet.length, 1); return 0; } case PMFS_PRINT_TIMING: { pmfs_print_timing_stats(); return 0; } case PMFS_CLEAR_STATS: { pmfs_clear_stats(); return 0; } default: return -ENOTTY; } }