aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLi Xi <pkuelelixi@gmail.com>2016-01-08 16:01:22 -0500
committerTheodore Ts'o <tytso@mit.edu>2016-01-08 16:01:22 -0500
commit9b7365fc1c82038faa52d56173b20221cf422cbe (patch)
tree3539021199fa3ec68cbca55e2689e7d741b66959 /fs
parent689c958cbe6be4f211b40747951a3ba2c73b6715 (diff)
ext4: add FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface support
This patch adds FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR ioctl interface support for ext4. The interface is kept consistent with XFS_IOC_FSGETXATTR/XFS_IOC_FSGETXATTR. Signed-off-by: Li Xi <lixi@ddn.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Reviewed-by: Andreas Dilger <adilger@dilger.ca> Reviewed-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/ext4.h47
-rw-r--r--fs/ext4/ioctl.c376
2 files changed, 336 insertions, 87 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 96cc151d5931..1c127213363a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -381,6 +381,13 @@ struct flex_groups {
381#define EXT4_FL_USER_VISIBLE 0x304BDFFF /* User visible flags */ 381#define EXT4_FL_USER_VISIBLE 0x304BDFFF /* User visible flags */
382#define EXT4_FL_USER_MODIFIABLE 0x204380FF /* User modifiable flags */ 382#define EXT4_FL_USER_MODIFIABLE 0x204380FF /* User modifiable flags */
383 383
384#define EXT4_FL_XFLAG_VISIBLE (EXT4_SYNC_FL | \
385 EXT4_IMMUTABLE_FL | \
386 EXT4_APPEND_FL | \
387 EXT4_NODUMP_FL | \
388 EXT4_NOATIME_FL | \
389 EXT4_PROJINHERIT_FL)
390
384/* Flags that should be inherited by new inodes from their parent. */ 391/* Flags that should be inherited by new inodes from their parent. */
385#define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\ 392#define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\
386 EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\ 393 EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\
@@ -619,6 +626,46 @@ enum {
619#define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) 626#define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
620#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy) 627#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
621 628
629#ifndef FS_IOC_FSGETXATTR
630/* Until the uapi changes get merged for project quota... */
631
632#define FS_IOC_FSGETXATTR _IOR('X', 31, struct fsxattr)
633#define FS_IOC_FSSETXATTR _IOW('X', 32, struct fsxattr)
634
635/*
636 * Structure for FS_IOC_FSGETXATTR and FS_IOC_FSSETXATTR.
637 */
638struct fsxattr {
639 __u32 fsx_xflags; /* xflags field value (get/set) */
640 __u32 fsx_extsize; /* extsize field value (get/set)*/
641 __u32 fsx_nextents; /* nextents field value (get) */
642 __u32 fsx_projid; /* project identifier (get/set) */
643 unsigned char fsx_pad[12];
644};
645
646/*
647 * Flags for the fsx_xflags field
648 */
649#define FS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */
650#define FS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */
651#define FS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */
652#define FS_XFLAG_APPEND 0x00000010 /* all writes append */
653#define FS_XFLAG_SYNC 0x00000020 /* all writes synchronous */
654#define FS_XFLAG_NOATIME 0x00000040 /* do not update access time */
655#define FS_XFLAG_NODUMP 0x00000080 /* do not include in backups */
656#define FS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */
657#define FS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */
658#define FS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */
659#define FS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */
660#define FS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */
661#define FS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */
662#define FS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */
663#define FS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
664#endif /* !defined(FS_IOC_FSGETXATTR) */
665
666#define EXT4_IOC_FSGETXATTR FS_IOC_FSGETXATTR
667#define EXT4_IOC_FSSETXATTR FS_IOC_FSSETXATTR
668
622#if defined(__KERNEL__) && defined(CONFIG_COMPAT) 669#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
623/* 670/*
624 * ioctl commands in 32 bit emulation 671 * ioctl commands in 32 bit emulation
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 5e872fd40e5e..2b0cb84255eb 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -14,6 +14,7 @@
14#include <linux/mount.h> 14#include <linux/mount.h>
15#include <linux/file.h> 15#include <linux/file.h>
16#include <linux/random.h> 16#include <linux/random.h>
17#include <linux/quotaops.h>
17#include <asm/uaccess.h> 18#include <asm/uaccess.h>
18#include "ext4_jbd2.h" 19#include "ext4_jbd2.h"
19#include "ext4.h" 20#include "ext4.h"
@@ -202,6 +203,238 @@ static int uuid_is_zero(__u8 u[16])
202 return 1; 203 return 1;
203} 204}
204 205
206static int ext4_ioctl_setflags(struct inode *inode,
207 unsigned int flags)
208{
209 struct ext4_inode_info *ei = EXT4_I(inode);
210 handle_t *handle = NULL;
211 int err = EPERM, migrate = 0;
212 struct ext4_iloc iloc;
213 unsigned int oldflags, mask, i;
214 unsigned int jflag;
215
216 /* Is it quota file? Do not allow user to mess with it */
217 if (IS_NOQUOTA(inode))
218 goto flags_out;
219
220 oldflags = ei->i_flags;
221
222 /* The JOURNAL_DATA flag is modifiable only by root */
223 jflag = flags & EXT4_JOURNAL_DATA_FL;
224
225 /*
226 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
227 * the relevant capability.
228 *
229 * This test looks nicer. Thanks to Pauline Middelink
230 */
231 if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
232 if (!capable(CAP_LINUX_IMMUTABLE))
233 goto flags_out;
234 }
235
236 /*
237 * The JOURNAL_DATA flag can only be changed by
238 * the relevant capability.
239 */
240 if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
241 if (!capable(CAP_SYS_RESOURCE))
242 goto flags_out;
243 }
244 if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
245 migrate = 1;
246
247 if (flags & EXT4_EOFBLOCKS_FL) {
248 /* we don't support adding EOFBLOCKS flag */
249 if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
250 err = -EOPNOTSUPP;
251 goto flags_out;
252 }
253 } else if (oldflags & EXT4_EOFBLOCKS_FL)
254 ext4_truncate(inode);
255
256 handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
257 if (IS_ERR(handle)) {
258 err = PTR_ERR(handle);
259 goto flags_out;
260 }
261 if (IS_SYNC(inode))
262 ext4_handle_sync(handle);
263 err = ext4_reserve_inode_write(handle, inode, &iloc);
264 if (err)
265 goto flags_err;
266
267 for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
268 if (!(mask & EXT4_FL_USER_MODIFIABLE))
269 continue;
270 if (mask & flags)
271 ext4_set_inode_flag(inode, i);
272 else
273 ext4_clear_inode_flag(inode, i);
274 }
275
276 ext4_set_inode_flags(inode);
277 inode->i_ctime = ext4_current_time(inode);
278
279 err = ext4_mark_iloc_dirty(handle, inode, &iloc);
280flags_err:
281 ext4_journal_stop(handle);
282 if (err)
283 goto flags_out;
284
285 if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
286 err = ext4_change_inode_journal_flag(inode, jflag);
287 if (err)
288 goto flags_out;
289 if (migrate) {
290 if (flags & EXT4_EXTENTS_FL)
291 err = ext4_ext_migrate(inode);
292 else
293 err = ext4_ind_migrate(inode);
294 }
295
296flags_out:
297 return err;
298}
299
300#ifdef CONFIG_QUOTA
301static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
302{
303 struct inode *inode = file_inode(filp);
304 struct super_block *sb = inode->i_sb;
305 struct ext4_inode_info *ei = EXT4_I(inode);
306 int err, rc;
307 handle_t *handle;
308 kprojid_t kprojid;
309 struct ext4_iloc iloc;
310 struct ext4_inode *raw_inode;
311
312 if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
313 EXT4_FEATURE_RO_COMPAT_PROJECT)) {
314 if (projid != EXT4_DEF_PROJID)
315 return -EOPNOTSUPP;
316 else
317 return 0;
318 }
319
320 if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE)
321 return -EOPNOTSUPP;
322
323 kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
324
325 if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
326 return 0;
327
328 err = mnt_want_write_file(filp);
329 if (err)
330 return err;
331
332 err = -EPERM;
333 mutex_lock(&inode->i_mutex);
334 /* Is it quota file? Do not allow user to mess with it */
335 if (IS_NOQUOTA(inode))
336 goto out_unlock;
337
338 err = ext4_get_inode_loc(inode, &iloc);
339 if (err)
340 goto out_unlock;
341
342 raw_inode = ext4_raw_inode(&iloc);
343 if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) {
344 err = -EOVERFLOW;
345 brelse(iloc.bh);
346 goto out_unlock;
347 }
348 brelse(iloc.bh);
349
350 dquot_initialize(inode);
351
352 handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
353 EXT4_QUOTA_INIT_BLOCKS(sb) +
354 EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
355 if (IS_ERR(handle)) {
356 err = PTR_ERR(handle);
357 goto out_unlock;
358 }
359
360 err = ext4_reserve_inode_write(handle, inode, &iloc);
361 if (err)
362 goto out_stop;
363
364 if (sb_has_quota_limits_enabled(sb, PRJQUOTA)) {
365 struct dquot *transfer_to[MAXQUOTAS] = { };
366
367 transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
368 if (transfer_to[PRJQUOTA]) {
369 err = __dquot_transfer(inode, transfer_to);
370 dqput(transfer_to[PRJQUOTA]);
371 if (err)
372 goto out_dirty;
373 }
374 }
375 EXT4_I(inode)->i_projid = kprojid;
376 inode->i_ctime = ext4_current_time(inode);
377out_dirty:
378 rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
379 if (!err)
380 err = rc;
381out_stop:
382 ext4_journal_stop(handle);
383out_unlock:
384 mutex_unlock(&inode->i_mutex);
385 mnt_drop_write_file(filp);
386 return err;
387}
388#else
389static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
390{
391 if (projid != EXT4_DEF_PROJID)
392 return -EOPNOTSUPP;
393 return 0;
394}
395#endif
396
397/* Transfer internal flags to xflags */
398static inline __u32 ext4_iflags_to_xflags(unsigned long iflags)
399{
400 __u32 xflags = 0;
401
402 if (iflags & EXT4_SYNC_FL)
403 xflags |= FS_XFLAG_SYNC;
404 if (iflags & EXT4_IMMUTABLE_FL)
405 xflags |= FS_XFLAG_IMMUTABLE;
406 if (iflags & EXT4_APPEND_FL)
407 xflags |= FS_XFLAG_APPEND;
408 if (iflags & EXT4_NODUMP_FL)
409 xflags |= FS_XFLAG_NODUMP;
410 if (iflags & EXT4_NOATIME_FL)
411 xflags |= FS_XFLAG_NOATIME;
412 if (iflags & EXT4_PROJINHERIT_FL)
413 xflags |= FS_XFLAG_PROJINHERIT;
414 return xflags;
415}
416
417/* Transfer xflags flags to internal */
418static inline unsigned long ext4_xflags_to_iflags(__u32 xflags)
419{
420 unsigned long iflags = 0;
421
422 if (xflags & FS_XFLAG_SYNC)
423 iflags |= EXT4_SYNC_FL;
424 if (xflags & FS_XFLAG_IMMUTABLE)
425 iflags |= EXT4_IMMUTABLE_FL;
426 if (xflags & FS_XFLAG_APPEND)
427 iflags |= EXT4_APPEND_FL;
428 if (xflags & FS_XFLAG_NODUMP)
429 iflags |= EXT4_NODUMP_FL;
430 if (xflags & FS_XFLAG_NOATIME)
431 iflags |= EXT4_NOATIME_FL;
432 if (xflags & FS_XFLAG_PROJINHERIT)
433 iflags |= EXT4_PROJINHERIT_FL;
434
435 return iflags;
436}
437
205long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 438long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
206{ 439{
207 struct inode *inode = file_inode(filp); 440 struct inode *inode = file_inode(filp);
@@ -217,11 +450,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
217 flags = ei->i_flags & EXT4_FL_USER_VISIBLE; 450 flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
218 return put_user(flags, (int __user *) arg); 451 return put_user(flags, (int __user *) arg);
219 case EXT4_IOC_SETFLAGS: { 452 case EXT4_IOC_SETFLAGS: {
220 handle_t *handle = NULL; 453 int err;
221 int err, migrate = 0;
222 struct ext4_iloc iloc;
223 unsigned int oldflags, mask, i;
224 unsigned int jflag;
225 454
226 if (!inode_owner_or_capable(inode)) 455 if (!inode_owner_or_capable(inode))
227 return -EACCES; 456 return -EACCES;
@@ -235,89 +464,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
235 464
236 flags = ext4_mask_flags(inode->i_mode, flags); 465 flags = ext4_mask_flags(inode->i_mode, flags);
237 466
238 err = -EPERM;
239 mutex_lock(&inode->i_mutex); 467 mutex_lock(&inode->i_mutex);
240 /* Is it quota file? Do not allow user to mess with it */ 468 err = ext4_ioctl_setflags(inode, flags);
241 if (IS_NOQUOTA(inode))
242 goto flags_out;
243
244 oldflags = ei->i_flags;
245
246 /* The JOURNAL_DATA flag is modifiable only by root */
247 jflag = flags & EXT4_JOURNAL_DATA_FL;
248
249 /*
250 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
251 * the relevant capability.
252 *
253 * This test looks nicer. Thanks to Pauline Middelink
254 */
255 if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
256 if (!capable(CAP_LINUX_IMMUTABLE))
257 goto flags_out;
258 }
259
260 /*
261 * The JOURNAL_DATA flag can only be changed by
262 * the relevant capability.
263 */
264 if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
265 if (!capable(CAP_SYS_RESOURCE))
266 goto flags_out;
267 }
268 if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
269 migrate = 1;
270
271 if (flags & EXT4_EOFBLOCKS_FL) {
272 /* we don't support adding EOFBLOCKS flag */
273 if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
274 err = -EOPNOTSUPP;
275 goto flags_out;
276 }
277 } else if (oldflags & EXT4_EOFBLOCKS_FL)
278 ext4_truncate(inode);
279
280 handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
281 if (IS_ERR(handle)) {
282 err = PTR_ERR(handle);
283 goto flags_out;
284 }
285 if (IS_SYNC(inode))
286 ext4_handle_sync(handle);
287 err = ext4_reserve_inode_write(handle, inode, &iloc);
288 if (err)
289 goto flags_err;
290
291 for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
292 if (!(mask & EXT4_FL_USER_MODIFIABLE))
293 continue;
294 if (mask & flags)
295 ext4_set_inode_flag(inode, i);
296 else
297 ext4_clear_inode_flag(inode, i);
298 }
299
300 ext4_set_inode_flags(inode);
301 inode->i_ctime = ext4_current_time(inode);
302
303 err = ext4_mark_iloc_dirty(handle, inode, &iloc);
304flags_err:
305 ext4_journal_stop(handle);
306 if (err)
307 goto flags_out;
308
309 if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
310 err = ext4_change_inode_journal_flag(inode, jflag);
311 if (err)
312 goto flags_out;
313 if (migrate) {
314 if (flags & EXT4_EXTENTS_FL)
315 err = ext4_ext_migrate(inode);
316 else
317 err = ext4_ind_migrate(inode);
318 }
319
320flags_out:
321 mutex_unlock(&inode->i_mutex); 469 mutex_unlock(&inode->i_mutex);
322 mnt_drop_write_file(filp); 470 mnt_drop_write_file(filp);
323 return err; 471 return err;
@@ -689,6 +837,60 @@ encryption_policy_out:
689 return -EOPNOTSUPP; 837 return -EOPNOTSUPP;
690#endif 838#endif
691 } 839 }
840 case EXT4_IOC_FSGETXATTR:
841 {
842 struct fsxattr fa;
843
844 memset(&fa, 0, sizeof(struct fsxattr));
845 ext4_get_inode_flags(ei);
846 fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE);
847
848 if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
849 EXT4_FEATURE_RO_COMPAT_PROJECT)) {
850 fa.fsx_projid = (__u32)from_kprojid(&init_user_ns,
851 EXT4_I(inode)->i_projid);
852 }
853
854 if (copy_to_user((struct fsxattr __user *)arg,
855 &fa, sizeof(fa)))
856 return -EFAULT;
857 return 0;
858 }
859 case EXT4_IOC_FSSETXATTR:
860 {
861 struct fsxattr fa;
862 int err;
863
864 if (copy_from_user(&fa, (struct fsxattr __user *)arg,
865 sizeof(fa)))
866 return -EFAULT;
867
868 /* Make sure caller has proper permission */
869 if (!inode_owner_or_capable(inode))
870 return -EACCES;
871
872 err = mnt_want_write_file(filp);
873 if (err)
874 return err;
875
876 flags = ext4_xflags_to_iflags(fa.fsx_xflags);
877 flags = ext4_mask_flags(inode->i_mode, flags);
878
879 mutex_lock(&inode->i_mutex);
880 flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) |
881 (flags & EXT4_FL_XFLAG_VISIBLE);
882 err = ext4_ioctl_setflags(inode, flags);
883 mutex_unlock(&inode->i_mutex);
884 mnt_drop_write_file(filp);
885 if (err)
886 return err;
887
888 err = ext4_ioctl_setproject(filp, fa.fsx_projid);
889 if (err)
890 return err;
891
892 return 0;
893 }
692 default: 894 default:
693 return -ENOTTY; 895 return -ENOTTY;
694 } 896 }