This series adds quota support to UBIFS. This series follows a very simple approach to quota: Neither the quota limits nor the quota usage are ever written to the medium. The quota usage is reconstructed from the filesystem during mount time. The quota limits must be set by the user each time after mount. This is probably not very convenient for systems that are used interactively, but UBIFS is targetted to embedded systems and here running a script after mount shouldn't be a problem. This of course isn't the way quota was thought to be, but I believe this is a good compromise for a feature that I predict is only rarely used on UBIFS. The big upside of this approach is that no on-disk format changes are required and thus we can't get any broken/corrupt filesystems because of quota support. Reconstructing the quota data each time during mount has an noticable but I think for many cases acceptable time overhead. I mounted a ~56MiB rootfs with 1920 files which takes around 0.7s longer when quota is enabled. As UBIFS works on mtd there is no block_device involved. The quotactl system call requires a path to a block device as argument. To overcome this we add support for passing the mount point instead. quota uses get_super_exclusive_thawed(), get_super_thawed() and get_super() to get hold of a super_block. All these functions require a block_device which we do not have for UBIFS, so this code has to be refactored a bit. I'm a bit outside of my comfort zone here, so please review carefully ;) The UBIFS quota support itself is based on a series by Dongsheng Yang posted here: http://lists.infradead.org/pipermail/linux-mtd/2015-September/061812.html This part hasn't changed much, except that the code for reading and writing quota files has been dropped. Sascha Dongsheng Yang (1): ubifs: Add quota support Sascha Hauer (10): quota: Make inode optional quota: Only module_put the format when existing fs: move __get_super() out of loop fs, quota: introduce wait_super_thawed() to wait until a superblock is thawed quota: Allow to pass quotactl a mountpoint ubifs: move checks and preparation into setflags() ubifs: Add support for FS_IOC_FS[SG]ETXATTR ioctls ubifs: do not ubifs_inode() on potentially NULL pointer ubifs: Add support for project id ubifs: export get_znode fs/quota/dquot.c | 9 +- fs/quota/quota.c | 79 ++++-- fs/super.c | 74 +++--- fs/ubifs/Makefile | 1 + fs/ubifs/dir.c | 31 ++- fs/ubifs/file.c | 43 +++ fs/ubifs/ioctl.c | 222 ++++++++++++++-- fs/ubifs/journal.c | 4 +- fs/ubifs/quota.c | 590 +++++++++++++++++++++++++++++++++++++++++ fs/ubifs/super.c | 46 +++- fs/ubifs/tnc.c | 34 +-- fs/ubifs/ubifs-media.h | 6 +- fs/ubifs/ubifs.h | 33 +++ include/linux/fs.h | 5 +- 14 files changed, 1076 insertions(+), 101 deletions(-) create mode 100644 fs/ubifs/quota.c -- 2.20.1
To add support for filesystems which do not store quota informations in an inode make the inode optional. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/quota/dquot.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index be9c471cdbc8..3cb836351c22 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2800,8 +2800,10 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state) tstate->flags |= QCI_LIMITS_ENFORCED; tstate->spc_timelimit = mi->dqi_bgrace; tstate->ino_timelimit = mi->dqi_igrace; - tstate->ino = dqopt->files[type]->i_ino; - tstate->blocks = dqopt->files[type]->i_blocks; + if (dqopt->files[type]) { + tstate->ino = dqopt->files[type]->i_ino; + tstate->blocks = dqopt->files[type]->i_blocks; + } tstate->nextents = 1; /* We don't know... */ spin_unlock(&dq_data_lock); } -- 2.20.1
For filesystems which do not have a quota_format_type such as upcoming UBIFS quota fmt may be NULL. Only put the format when it's non NULL. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/quota/dquot.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 3cb836351c22..b043468e53f2 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -218,6 +218,9 @@ static struct quota_format_type *find_quota_format(int id) static void put_quota_format(struct quota_format_type *fmt) { + if (!fmt) + return; + module_put(fmt->qf_owner); } -- 2.20.1
__get_super_thawed() calls __get_super() multiple times. I can't see a case where __get_super() would return another valid superblock when called again, so move the call to __get_super() out of the loop. This is done in preparation for the next patch. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/super.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/super.c b/fs/super.c index 5960578a4076..f85d1ea194ae 100644 --- a/fs/super.c +++ b/fs/super.c @@ -786,17 +786,26 @@ EXPORT_SYMBOL(get_super); static struct super_block *__get_super_thawed(struct block_device *bdev, bool excl) { + struct super_block *s = __get_super(bdev, excl); + if (!s) + return NULL; + while (1) { - struct super_block *s = __get_super(bdev, excl); - if (!s || s->s_writers.frozen == SB_UNFROZEN) + if (s->s_writers.frozen == SB_UNFROZEN) return s; + if (!excl) up_read(&s->s_umount); else up_write(&s->s_umount); + wait_event(s->s_writers.wait_unfrozen, s->s_writers.frozen == SB_UNFROZEN); - put_super(s); + + if (!excl) + down_read(&sb->s_umount); + else + down_write(&sb->s_umount); } } -- 2.20.1
quota uses three different functions to get a superblock from a block_device. Instead, retrieve the superblock always with get_super() and introduce wait_super_thawed() which is then used to wait until the superblock is thawed. This way the code can better be shared with the code introduced in the next step: We want to add quota support for filesystems without a block_device, so here functions around a block_device can't be used. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/quota/quota.c | 30 +++++++++++++++---- fs/super.c | 72 +++++++++++++++++----------------------------- include/linux/fs.h | 4 +-- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/fs/quota/quota.c b/fs/quota/quota.c index cb13fb76dbee..64a212576a72 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -791,6 +791,16 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) struct block_device *bdev; struct super_block *sb; struct filename *tmp = getname(special); + bool thawed = false, exclusive; + int ret; + + if (quotactl_cmd_onoff(cmd)) { + thawed = true; + exclusive = true; + } else if (quotactl_cmd_write(cmd)) { + thawed = true; + exclusive = false; + } if (IS_ERR(tmp)) return ERR_CAST(tmp); @@ -798,16 +808,24 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) putname(tmp); if (IS_ERR(bdev)) return ERR_CAST(bdev); - if (quotactl_cmd_onoff(cmd)) - sb = get_super_exclusive_thawed(bdev); - else if (quotactl_cmd_write(cmd)) - sb = get_super_thawed(bdev); - else - sb = get_super(bdev); + bdput(bdev); + + sb = get_super(bdev); if (!sb) return ERR_PTR(-ENODEV); + if (thawed) { + ret = wait_super_thawed(sb, exclusive); + if (ret) { + if (exclusive) + drop_super_exclusive(sb); + else + drop_super(sb); + return ERR_PTR(ret); + } + } + return sb; #else return ERR_PTR(-ENODEV); diff --git a/fs/super.c b/fs/super.c index f85d1ea194ae..13380ffcbd8d 100644 --- a/fs/super.c +++ b/fs/super.c @@ -783,61 +783,41 @@ struct super_block *get_super(struct block_device *bdev) } EXPORT_SYMBOL(get_super); -static struct super_block *__get_super_thawed(struct block_device *bdev, - bool excl) +/** + * wait_super_thawed - wait for a superblock being thawed + * @sb: superblock to wait for + * @excl: if true, get s_umount semaphore exclusively + * + * Wait until the superblock is thawed. s_umount semaphore must be released on + * entry and will be held when returning. + */ +int wait_super_thawed(struct super_block *sb, bool excl) { - struct super_block *s = __get_super(bdev, excl); - if (!s) - return NULL; + up_read(&sb->s_umount); while (1) { - if (s->s_writers.frozen == SB_UNFROZEN) - return s; - - if (!excl) - up_read(&s->s_umount); + if (excl) + down_write(&sb->s_umount); else - up_write(&s->s_umount); + down_read(&sb->s_umount); - wait_event(s->s_writers.wait_unfrozen, - s->s_writers.frozen == SB_UNFROZEN); + /* still alive? */ + if (!sb->s_root || !(sb->s_flags & SB_BORN)) + return -ENODEV; - if (!excl) - down_read(&sb->s_umount); - else - down_write(&sb->s_umount); - } -} + if (sb->s_writers.frozen == SB_UNFROZEN) + return 0; -/** - * get_super_thawed - get thawed superblock of a device - * @bdev: device to get the superblock for - * - * Scans the superblock list and finds the superblock of the file system - * mounted on the device. The superblock is returned once it is thawed - * (or immediately if it was not frozen). %NULL is returned if no match - * is found. - */ -struct super_block *get_super_thawed(struct block_device *bdev) -{ - return __get_super_thawed(bdev, false); -} -EXPORT_SYMBOL(get_super_thawed); + if (excl) + up_write(&sb->s_umount); + else + up_read(&sb->s_umount); -/** - * get_super_exclusive_thawed - get thawed superblock of a device - * @bdev: device to get the superblock for - * - * Scans the superblock list and finds the superblock of the file system - * mounted on the device. The superblock is returned once it is thawed - * (or immediately if it was not frozen) and s_umount semaphore is held - * in exclusive mode. %NULL is returned if no match is found. - */ -struct super_block *get_super_exclusive_thawed(struct block_device *bdev) -{ - return __get_super_thawed(bdev, true); + wait_event(sb->s_writers.wait_unfrozen, + sb->s_writers.frozen == SB_UNFROZEN); + } } -EXPORT_SYMBOL(get_super_exclusive_thawed); +EXPORT_SYMBOL(wait_super_thawed); /** * get_active_super - get an active reference to the superblock of a device diff --git a/include/linux/fs.h b/include/linux/fs.h index 997a530ff4e9..59d9ea62942a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3220,9 +3220,9 @@ extern struct file_system_type *get_filesystem(struct file_system_type *fs); extern void put_filesystem(struct file_system_type *fs); extern struct file_system_type *get_fs_type(const char *name); extern struct super_block *get_super(struct block_device *); -extern struct super_block *get_super_thawed(struct block_device *); -extern struct super_block *get_super_exclusive_thawed(struct block_device *bdev); +extern int wait_super_thawed(struct super_block *sb, bool excl); extern struct super_block *get_active_super(struct block_device *bdev); +extern int super_wait_thawed(struct super_block *sb, bool excl); extern void drop_super(struct super_block *sb); extern void drop_super_exclusive(struct super_block *sb); extern void iterate_supers(void (*)(struct super_block *, void *), void *); -- 2.20.1
So far quotactl only allows a path to a block device to specify a filesystem to work on. For filesystems that don't work on block devices such as UBIFS there is no block device, so let quotactl take the mountpoint as an alternative way. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/quota/quota.c | 69 ++++++++++++++++++++++++++++++++-------------- fs/super.c | 17 ++++++++++++ include/linux/fs.h | 1 + 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 64a212576a72..6d9807092ae1 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -19,6 +19,7 @@ #include <linux/types.h> #include <linux/writeback.h> #include <linux/nospec.h> +#include <linux/mount.h> static int check_quotactl_permission(struct super_block *sb, int type, int cmd, qid_t id) @@ -781,16 +782,53 @@ static bool quotactl_cmd_onoff(int cmd) (cmd == Q_XQUOTAON) || (cmd == Q_XQUOTAOFF); } +static struct super_block *quotactl_block(const char __user *special) +{ +#ifdef CONFIG_BLOCK + struct block_device *bdev; + struct filename *tmp = getname(special); + + if (IS_ERR(tmp)) + return ERR_CAST(tmp); + bdev = lookup_bdev(tmp->name); + putname(tmp); + if (IS_ERR(bdev)) + return ERR_CAST(bdev); + + bdput(bdev); + + return get_super(bdev); +#else + return ERR_PTR(-ENODEV); +#endif +} + +static struct super_block *quotactl_path(const char __user *special) +{ + struct super_block *sb; + struct path path; + int ret; + + ret = user_path_at(AT_FDCWD, special, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path); + if (ret) + return ERR_PTR(ret); + + sb = path.mnt->mnt_sb; + + reference_super(sb); + + path_put(&path); + + return sb; +} + /* * look up a superblock on which quota ops will be performed * - use the name of a block device to find the superblock thereon */ -static struct super_block *quotactl_block(const char __user *special, int cmd) +static struct super_block *quotactl_get_super(const char __user *special, int cmd) { -#ifdef CONFIG_BLOCK - struct block_device *bdev; struct super_block *sb; - struct filename *tmp = getname(special); bool thawed = false, exclusive; int ret; @@ -802,18 +840,12 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) exclusive = false; } - if (IS_ERR(tmp)) - return ERR_CAST(tmp); - bdev = lookup_bdev(tmp->name); - putname(tmp); - if (IS_ERR(bdev)) - return ERR_CAST(bdev); - - bdput(bdev); - - sb = get_super(bdev); - if (!sb) - return ERR_PTR(-ENODEV); + sb = quotactl_block(special); + if (IS_ERR(sb)) { + sb = quotactl_path(special); + if (IS_ERR(sb)) + return ERR_CAST(sb); + } if (thawed) { ret = wait_super_thawed(sb, exclusive); @@ -827,9 +859,6 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) } return sb; -#else - return ERR_PTR(-ENODEV); -#endif } /* @@ -873,7 +902,7 @@ int kernel_quotactl(unsigned int cmd, const char __user *special, pathp = &path; } - sb = quotactl_block(special, cmds); + sb = quotactl_get_super(special, cmds); if (IS_ERR(sb)) { ret = PTR_ERR(sb); goto out; diff --git a/fs/super.c b/fs/super.c index 13380ffcbd8d..b23641f94557 100644 --- a/fs/super.c +++ b/fs/super.c @@ -819,6 +819,23 @@ int wait_super_thawed(struct super_block *sb, bool excl) } EXPORT_SYMBOL(wait_super_thawed); +/** + * reference_super - get a reference to a given superblock + * @sb: superblock to get the reference from + * + * Takes a reference to a superblock. Can be used as when the superblock + * is known and leaves it in a state as if get_super had been called. + */ +void reference_super(struct super_block *sb) +{ + spin_lock(&sb_lock); + sb->s_count++; + spin_unlock(&sb_lock); + + down_read(&sb->s_umount); +} +EXPORT_SYMBOL_GPL(reference_super); + /** * get_active_super - get an active reference to the superblock of a device * @bdev: device to get the superblock for diff --git a/include/linux/fs.h b/include/linux/fs.h index 59d9ea62942a..66aa9e5490d0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3223,6 +3223,7 @@ extern struct super_block *get_super(struct block_device *); extern int wait_super_thawed(struct super_block *sb, bool excl); extern struct super_block *get_active_super(struct block_device *bdev); extern int super_wait_thawed(struct super_block *sb, bool excl); +extern void reference_super(struct super_block *sb); extern void drop_super(struct super_block *sb); extern void drop_super_exclusive(struct super_block *sb); extern void iterate_supers(void (*)(struct super_block *, void *), void *); -- 2.20.1
setflags() can be reused for upcoming FS_IOC_FS[SG]ETXATTR ioctl support. In preparation for that move the checks and preparation into that function so we can reuse them aswell. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/ubifs/ioctl.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index 034ad14710d1..b9c4a51bceea 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -95,18 +95,35 @@ static int ubifs2ioctl(int ubifs_flags) return ioctl_flags; } -static int setflags(struct inode *inode, int flags) +static int setflags(struct file *file, int flags) { int oldflags, err, release; + struct inode *inode = file_inode(file); struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_info *c = inode->i_sb->s_fs_info; struct ubifs_budget_req req = { .dirtied_ino = 1, .dirtied_ino_d = ui->data_len }; - err = ubifs_budget_space(c, &req); + if (IS_RDONLY(inode)) + return -EROFS; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + /* + * Make sure the file-system is read-write and make sure it + * will not become read-only while we are changing the flags. + */ + err = mnt_want_write_file(file); if (err) return err; + dbg_gen("set flags: %#x, i_flags %#x", flags, inode->i_flags); + + err = ubifs_budget_space(c, &req); + if (err) + goto out_drop; + mutex_lock(&ui->ui_mutex); oldflags = ubifs2ioctl(ui->flags); err = vfs_ioc_setflags_prepare(inode, oldflags, flags); @@ -124,12 +141,18 @@ static int setflags(struct inode *inode, int flags) ubifs_release_budget(c, &req); if (IS_SYNC(inode)) err = write_inode_now(inode, 1); + + mnt_drop_write_file(file); + return err; out_unlock: ubifs_err(c, "can't modify inode %lu attributes", inode->i_ino); mutex_unlock(&ui->ui_mutex); ubifs_release_budget(c, &req); +out_drop: + mnt_drop_write_file(file); + return err; } @@ -146,12 +169,6 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return put_user(flags, (int __user *) arg); case FS_IOC_SETFLAGS: { - if (IS_RDONLY(inode)) - return -EROFS; - - if (!inode_owner_or_capable(inode)) - return -EACCES; - if (get_user(flags, (int __user *) arg)) return -EFAULT; @@ -161,17 +178,8 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (!S_ISDIR(inode->i_mode)) flags &= ~FS_DIRSYNC_FL; - /* - * Make sure the file-system is read-write and make sure it - * will not become read-only while we are changing the flags. - */ - err = mnt_want_write_file(file); - if (err) - return err; - dbg_gen("set flags: %#x, i_flags %#x", flags, inode->i_flags); - err = setflags(inode, flags); - mnt_drop_write_file(file); - return err; + + return setflags(file, flags); } case FS_IOC_SET_ENCRYPTION_POLICY: { struct ubifs_info *c = inode->i_sb->s_fs_info; -- 2.20.1
The FS_IOC_FS[SG]ETXATTR ioctls are an alternative to FS_IOC_[GS]ETFLAGS with additional features. This patch adds support for these ioctls. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/ubifs/ioctl.c | 89 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index b9c4a51bceea..121aa1003e24 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -95,9 +95,39 @@ static int ubifs2ioctl(int ubifs_flags) return ioctl_flags; } -static int setflags(struct file *file, int flags) +/* Transfer xflags flags to internal */ +static inline unsigned long ubifs_xflags_to_iflags(__u32 xflags) { - int oldflags, err, release; + unsigned long iflags = 0; + + if (xflags & FS_XFLAG_SYNC) + iflags |= UBIFS_APPEND_FL; + if (xflags & FS_XFLAG_IMMUTABLE) + iflags |= UBIFS_IMMUTABLE_FL; + if (xflags & FS_XFLAG_APPEND) + iflags |= UBIFS_APPEND_FL; + + return iflags; +} + +/* Transfer internal flags to xflags */ +static inline __u32 ubifs_iflags_to_xflags(unsigned long flags) +{ + __u32 xflags = 0; + + if (flags & UBIFS_APPEND_FL) + xflags |= FS_XFLAG_SYNC; + if (flags & UBIFS_IMMUTABLE_FL) + xflags |= FS_XFLAG_IMMUTABLE; + if (flags & UBIFS_APPEND_FL) + xflags |= FS_XFLAG_APPEND; + + return xflags; +} + +static int setflags(struct file *file, int flags, struct fsxattr *fa) +{ + int ubi_flags, oldflags, err, release; struct inode *inode = file_inode(file); struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_info *c = inode->i_sb->s_fs_info; @@ -110,6 +140,11 @@ static int setflags(struct file *file, int flags) if (!inode_owner_or_capable(inode)) return -EACCES; + if (fa) + ubi_flags = ubifs_xflags_to_iflags(fa->fsx_xflags); + else + ubi_flags = ioctl2ubifs(flags); + /* * Make sure the file-system is read-write and make sure it * will not become read-only while we are changing the flags. @@ -126,11 +161,11 @@ static int setflags(struct file *file, int flags) mutex_lock(&ui->ui_mutex); oldflags = ubifs2ioctl(ui->flags); - err = vfs_ioc_setflags_prepare(inode, oldflags, flags); + err = vfs_ioc_setflags_prepare(inode, oldflags, ubifs2ioctl(ubi_flags)); if (err) goto out_unlock; - ui->flags = ioctl2ubifs(flags); + ui->flags = ubi_flags; ubifs_set_inode_flags(inode); inode->i_ctime = current_time(inode); release = ui->dirty; @@ -156,6 +191,44 @@ static int setflags(struct file *file, int flags) return err; } +static int ubifs_ioc_fsgetxattr(struct file *file, void __user *arg) +{ + struct inode *inode = file_inode(file); + struct ubifs_inode *ui = ubifs_inode(inode); + struct fsxattr fa; + + memset(&fa, 0, sizeof(fa)); + + fa.fsx_xflags = ubifs_iflags_to_xflags(ui->flags); + + if (copy_to_user(arg, &fa, sizeof(fa))) + return -EFAULT; + + return 0; +} + +static int check_xflags(unsigned int flags) +{ + if (flags & ~(FS_XFLAG_SYNC | FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND)) + return -EOPNOTSUPP; + return 0; +} + +static int ubifs_ioc_fssetxattr(struct file *file, const void __user *arg) +{ + struct fsxattr fa = {}; + int err; + + if (copy_from_user(&fa, (struct fsxattr __user *)arg, sizeof(fa))) + return -EFAULT; + + err = check_xflags(fa.fsx_xflags); + if (err) + return err; + + return setflags(file, 0, &fa); +} + long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int flags, err; @@ -179,7 +252,7 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) flags &= ~FS_DIRSYNC_FL; - return setflags(file, flags); + return setflags(file, flags, NULL); } case FS_IOC_SET_ENCRYPTION_POLICY: { struct ubifs_info *c = inode->i_sb->s_fs_info; @@ -193,6 +266,12 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case FS_IOC_GET_ENCRYPTION_POLICY: return fscrypt_ioctl_get_policy(file, (void __user *)arg); + case FS_IOC_FSGETXATTR: + return ubifs_ioc_fsgetxattr(file, (void __user *)arg); + + case FS_IOC_FSSETXATTR: + return ubifs_ioc_fssetxattr(file, (const void __user *)arg); + default: return -ENOTTY; } -- 2.20.1
new_inode() may return NULL, so only derefence the return inode when non NULL. This is merely a cleanup as calling ubifs_inode() on a NULL pointer doesn't do any harm, only using the return value would. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/ubifs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 0b98e3c8b461..cfce5fee9262 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -95,10 +95,10 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir, } inode = new_inode(c->vfs_sb); - ui = ubifs_inode(inode); if (!inode) return ERR_PTR(-ENOMEM); + ui = ubifs_inode(inode); /* * Set 'S_NOCMTIME' to prevent VFS form updating [mc]time of inodes and * marking them dirty in file write path (see 'file_update_time()'). -- 2.20.1
The project id is necessary for quota project id support. This adds support for the project id to UBIFS aswell as support for the FS_PROJINHERIT_FL flag. This includes a change for the UBIFS on-disk format. struct ubifs_ino_node gains a project id number and a UBIFS_PROJINHERIT_FL flag. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/ubifs/dir.c | 11 +++++++- fs/ubifs/ioctl.c | 61 +++++++++++++++++++++++++++++++++++++++++- fs/ubifs/journal.c | 2 +- fs/ubifs/super.c | 1 + fs/ubifs/ubifs-media.h | 6 +++-- fs/ubifs/ubifs.h | 4 +++ 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index cfce5fee9262..e2d3dfb67ae3 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -56,7 +56,8 @@ static int inherit_flags(const struct inode *dir, umode_t mode) */ return 0; - flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL); + flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL | + UBIFS_PROJINHERIT_FL); if (!S_ISDIR(mode)) /* The "DIRSYNC" flag only applies to directories */ flags &= ~UBIFS_DIRSYNC_FL; @@ -112,6 +113,11 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir, current_time(inode); inode->i_mapping->nrpages = 0; + if (ubifs_inode(dir)->flags & UBIFS_PROJINHERIT_FL) + ui->projid = ubifs_inode(dir)->projid; + else + ui->projid = make_kprojid(&init_user_ns, UBIFS_DEF_PROJID); + switch (mode & S_IFMT) { case S_IFREG: inode->i_mapping->a_ops = &ubifs_file_address_operations; @@ -705,6 +711,9 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir, ubifs_assert(c, inode_is_locked(dir)); ubifs_assert(c, inode_is_locked(inode)); + if (!projid_eq(dir_ui->projid, ui->projid)) + return -EXDEV; + err = fscrypt_prepare_link(old_dentry, dir, dentry); if (err) return err; diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index 121aa1003e24..824def711381 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -66,6 +66,8 @@ static int ioctl2ubifs(int ioctl_flags) ubifs_flags |= UBIFS_IMMUTABLE_FL; if (ioctl_flags & FS_DIRSYNC_FL) ubifs_flags |= UBIFS_DIRSYNC_FL; + if (ioctl_flags & FS_PROJINHERIT_FL) + ubifs_flags |= UBIFS_PROJINHERIT_FL; return ubifs_flags; } @@ -91,6 +93,8 @@ static int ubifs2ioctl(int ubifs_flags) ioctl_flags |= FS_IMMUTABLE_FL; if (ubifs_flags & UBIFS_DIRSYNC_FL) ioctl_flags |= FS_DIRSYNC_FL; + if (ubifs_flags & UBIFS_PROJINHERIT_FL) + ioctl_flags |= FS_PROJINHERIT_FL; return ioctl_flags; } @@ -106,6 +110,8 @@ static inline unsigned long ubifs_xflags_to_iflags(__u32 xflags) iflags |= UBIFS_IMMUTABLE_FL; if (xflags & FS_XFLAG_APPEND) iflags |= UBIFS_APPEND_FL; + if (xflags & FS_XFLAG_PROJINHERIT) + iflags |= UBIFS_PROJINHERIT_FL; return iflags; } @@ -121,10 +127,51 @@ static inline __u32 ubifs_iflags_to_xflags(unsigned long flags) xflags |= FS_XFLAG_IMMUTABLE; if (flags & UBIFS_APPEND_FL) xflags |= FS_XFLAG_APPEND; + if (flags & UBIFS_PROJINHERIT_FL) + xflags |= FS_XFLAG_PROJINHERIT; return xflags; } +static int ubifs_ioctl_check_project(struct ubifs_inode *ui, struct fsxattr *fa) +{ + /* + * Project Quota ID state is only allowed to change from within the init + * namespace. Enforce that restriction only if we are trying to change + * the quota ID state. Everything else is allowed in user namespaces. + */ + if (current_user_ns() == &init_user_ns) + return 0; + + if (__kprojid_val(ui->projid) != fa->fsx_projid) + return -EINVAL; + + if (ui->flags & UBIFS_PROJINHERIT_FL) { + if (!(fa->fsx_xflags & FS_XFLAG_PROJINHERIT)) + return -EINVAL; + } else { + if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT) + return -EINVAL; + } + + return 0; +} + +static int ubifs_ioc_setproject(struct file *file, __u32 projid) +{ + struct inode *inode = file_inode(file); + struct ubifs_inode *ui = ubifs_inode(inode); + kprojid_t kprojid; + + kprojid = make_kprojid(&init_user_ns, (projid_t)projid); + if (projid_eq(kprojid, ui->projid)) + return 0; + + ui->projid = kprojid; + + return 0; +} + static int setflags(struct file *file, int flags, struct fsxattr *fa) { int ubi_flags, oldflags, err, release; @@ -165,6 +212,16 @@ static int setflags(struct file *file, int flags, struct fsxattr *fa) if (err) goto out_unlock; + if (fa) { + err = ubifs_ioctl_check_project(ui, fa); + if (err) + goto out_unlock; + + err = ubifs_ioc_setproject(file, fa->fsx_projid); + if (err) + goto out_unlock; + } + ui->flags = ubi_flags; ubifs_set_inode_flags(inode); inode->i_ctime = current_time(inode); @@ -200,6 +257,7 @@ static int ubifs_ioc_fsgetxattr(struct file *file, void __user *arg) memset(&fa, 0, sizeof(fa)); fa.fsx_xflags = ubifs_iflags_to_xflags(ui->flags); + fa.fsx_projid = (__u32)from_kprojid(&init_user_ns, ui->projid); if (copy_to_user(arg, &fa, sizeof(fa))) return -EFAULT; @@ -209,7 +267,8 @@ static int ubifs_ioc_fsgetxattr(struct file *file, void __user *arg) static int check_xflags(unsigned int flags) { - if (flags & ~(FS_XFLAG_SYNC | FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND)) + if (flags & ~(FS_XFLAG_SYNC | FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND | + FS_XFLAG_PROJINHERIT)) return -EOPNOTSUPP; return 0; } diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index 4fd9683b8245..d8961993f9dd 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -54,7 +54,6 @@ */ static inline void zero_ino_node_unused(struct ubifs_ino_node *ino) { - memset(ino->padding1, 0, 4); memset(ino->padding2, 0, 26); } @@ -469,6 +468,7 @@ static void pack_inode(struct ubifs_info *c, struct ubifs_ino_node *ino, ino->xattr_cnt = cpu_to_le32(ui->xattr_cnt); ino->xattr_size = cpu_to_le32(ui->xattr_size); ino->xattr_names = cpu_to_le32(ui->xattr_names); + ino->projid = cpu_to_le32(from_kprojid(&init_user_ns, ui->projid)); zero_ino_node_unused(ino); /* diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 2c0803b0ac3a..dc7e235b1a4a 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -141,6 +141,7 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) ui->xattr_size = le32_to_cpu(ino->xattr_size); ui->xattr_names = le32_to_cpu(ino->xattr_names); ui->synced_i_size = ui->ui_size = inode->i_size; + ui->projid = make_kprojid(&init_user_ns, le32_to_cpu(ino->projid)); ui->xattr = (ui->flags & UBIFS_XATTR_FL) ? 1 : 0; diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h index 3c9792cbb6ff..418f4eba1624 100644 --- a/fs/ubifs/ubifs-media.h +++ b/fs/ubifs/ubifs-media.h @@ -316,6 +316,7 @@ enum { * UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous * UBIFS_XATTR_FL: this inode is the inode for an extended attribute value * UBIFS_CRYPT_FL: use encryption for this inode + * UBIFS_PROJINHERIT_FL: Create with parents projid * * Note, these are on-flash flags which correspond to ioctl flags * (@FS_COMPR_FL, etc). They have the same values now, but generally, do not @@ -329,6 +330,7 @@ enum { UBIFS_DIRSYNC_FL = 0x10, UBIFS_XATTR_FL = 0x20, UBIFS_CRYPT_FL = 0x40, + UBIFS_PROJINHERIT_FL = 0x80, }; /* Inode flag bits used by UBIFS */ @@ -497,7 +499,7 @@ union ubifs_dev_desc { * @data_len: inode data length * @xattr_cnt: count of extended attributes this inode has * @xattr_size: summarized size of all extended attributes in bytes - * @padding1: reserved for future, zeroes + * @projid: Quota project id * @xattr_names: sum of lengths of all extended attribute names belonging to * this inode * @compr_type: compression type used for this inode @@ -531,7 +533,7 @@ struct ubifs_ino_node { __le32 data_len; __le32 xattr_cnt; __le32 xattr_size; - __u8 padding1[4]; /* Watch 'zero_ino_node_unused()' if changing! */ + __le32 projid; __le32 xattr_names; __le16 compr_type; __u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */ diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index c55f212dcb75..3f8a33fca67b 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -155,6 +155,8 @@ #define UBIFS_HMAC_ARR_SZ 0 #endif +#define UBIFS_DEF_PROJID 0 + /* * Lockdep classes for UBIFS inode @ui_mutex. */ @@ -362,6 +364,7 @@ struct ubifs_gced_idx_leb { * inodes * @ui_size: inode size used by UBIFS when writing to flash * @flags: inode flags (@UBIFS_COMPR_FL, etc) + * @projid: The project id of this inode * @compr_type: default compression type used for this inode * @last_page_read: page number of last page read (for bulk read) * @read_in_a_row: number of consecutive pages read in a row (for bulk read) @@ -413,6 +416,7 @@ struct ubifs_inode { loff_t synced_i_size; loff_t ui_size; int flags; + kprojid_t projid; pgoff_t last_page_read; pgoff_t read_in_a_row; int data_len; -- 2.20.1
get_znode will be needed by upcoming UBIFS quota support. Rename it to ubifs_get_znode and export it. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/ubifs/tnc.c | 32 ++++++++++++++++---------------- fs/ubifs/ubifs.h | 2 ++ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index e8e7b0e9532e..188fa036e655 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -571,15 +571,15 @@ static int matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr, } /** - * get_znode - get a TNC znode that may not be loaded yet. + * ubifs_get_znode - get a TNC znode that may not be loaded yet. * @c: UBIFS file-system description object * @znode: parent znode * @n: znode branch slot number * * This function returns the znode or a negative error code. */ -static struct ubifs_znode *get_znode(struct ubifs_info *c, - struct ubifs_znode *znode, int n) +struct ubifs_znode *ubifs_get_znode(struct ubifs_info *c, + struct ubifs_znode *znode, int n) { struct ubifs_zbranch *zbr; @@ -619,11 +619,11 @@ static int tnc_next(struct ubifs_info *c, struct ubifs_znode **zn, int *n) nn = znode->iip + 1; znode = zp; if (nn < znode->child_cnt) { - znode = get_znode(c, znode, nn); + znode = ubifs_get_znode(c, znode, nn); if (IS_ERR(znode)) return PTR_ERR(znode); while (znode->level != 0) { - znode = get_znode(c, znode, 0); + znode = ubifs_get_znode(c, znode, 0); if (IS_ERR(znode)) return PTR_ERR(znode); } @@ -663,12 +663,12 @@ static int tnc_prev(struct ubifs_info *c, struct ubifs_znode **zn, int *n) nn = znode->iip - 1; znode = zp; if (nn >= 0) { - znode = get_znode(c, znode, nn); + znode = ubifs_get_znode(c, znode, nn); if (IS_ERR(znode)) return PTR_ERR(znode); while (znode->level != 0) { nn = znode->child_cnt - 1; - znode = get_znode(c, znode, nn); + znode = ubifs_get_znode(c, znode, nn); if (IS_ERR(znode)) return PTR_ERR(znode); } @@ -2572,7 +2572,7 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) while (znode->child_cnt == 1 && znode->level != 0) { zp = znode; zbr = &znode->zbranch[0]; - znode = get_znode(c, znode, 0); + znode = ubifs_get_znode(c, znode, 0); if (IS_ERR(znode)) return PTR_ERR(znode); znode = dirty_cow_znode(c, zbr); @@ -3096,12 +3096,12 @@ static struct ubifs_znode *left_znode(struct ubifs_info *c, return NULL; if (n >= 0) { /* Now go down the rightmost branch to 'level' */ - znode = get_znode(c, znode, n); + znode = ubifs_get_znode(c, znode, n); if (IS_ERR(znode)) return znode; while (znode->level != level) { n = znode->child_cnt - 1; - znode = get_znode(c, znode, n); + znode = ubifs_get_znode(c, znode, n); if (IS_ERR(znode)) return znode; } @@ -3133,11 +3133,11 @@ static struct ubifs_znode *right_znode(struct ubifs_info *c, return NULL; if (n < znode->child_cnt) { /* Now go down the leftmost branch to 'level' */ - znode = get_znode(c, znode, n); + znode = ubifs_get_znode(c, znode, n); if (IS_ERR(znode)) return znode; while (znode->level != level) { - znode = get_znode(c, znode, 0); + znode = ubifs_get_znode(c, znode, 0); if (IS_ERR(znode)) return znode; } @@ -3222,13 +3222,13 @@ static struct ubifs_znode *lookup_znode(struct ubifs_info *c, } if (znode->level == level + 1) break; - znode = get_znode(c, znode, n); + znode = ubifs_get_znode(c, znode, n); if (IS_ERR(znode)) return znode; } /* Check if the child is the one we are looking for */ if (znode->zbranch[n].lnum == lnum && znode->zbranch[n].offs == offs) - return get_znode(c, znode, n); + return ubifs_get_znode(c, znode, n); /* If the key is unique, there is nowhere else to look */ if (!is_hash_key(c, key)) return NULL; @@ -3254,7 +3254,7 @@ static struct ubifs_znode *lookup_znode(struct ubifs_info *c, /* Check it */ if (znode->zbranch[n].lnum == lnum && znode->zbranch[n].offs == offs) - return get_znode(c, znode, n); + return ubifs_get_znode(c, znode, n); /* Stop if the key is less than the one we are looking for */ if (keys_cmp(c, &znode->zbranch[n].key, key) < 0) break; @@ -3276,7 +3276,7 @@ static struct ubifs_znode *lookup_znode(struct ubifs_info *c, /* Check it */ if (znode->zbranch[n].lnum == lnum && znode->zbranch[n].offs == offs) - return get_znode(c, znode, n); + return ubifs_get_znode(c, znode, n); /* Stop if the key is greater than the one we are looking for */ if (keys_cmp(c, &znode->zbranch[n].key, key) > 0) break; diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 3f8a33fca67b..fd9e52749ef7 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1819,6 +1819,8 @@ int ubifs_find_dirty_idx_leb(struct ubifs_info *c); int ubifs_save_dirty_idx_lnums(struct ubifs_info *c); /* tnc.c */ +struct ubifs_znode *ubifs_get_znode(struct ubifs_info *c, + struct ubifs_znode *znode, int n); int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode **zn, int *n); int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, -- 2.20.1
From: Dongsheng Yang <yangds.fnst@cn.fujitsu.com> This introduces poor man's quota support for UBIFS. Unlike other implementations we never store anything on the flash. This has two big advantages: - No possible regressions with a changed on-disk format - no quota files can get out of sync. There are downsides aswell: - During mount the whole index must be scanned which takes some time - The quota limits must be set manually each time a filesystem is mounted. UBIFS is targetted for embedded systems and quota limits are likely not changed interactively, so having to restore the quota limits with a script shouldn't be a big deal. The mount time penalty is a price we must pay, but for that we get a simple and straight forward implementation for this rather rarely used feature. The quota data itself is stored in a red-black tree in memory. It is implemented as a quota format. When enabled with the "quota" mount option all three quota types (user, group, project) are enabled. The quota integration into UBIFS is taken from a series posted earlier by Dongsheng Yang. Like the earlier series we only account regular files for quota. All others are counted in the number of files, but do not require any quota space. iSigned-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- fs/ubifs/Makefile | 1 + fs/ubifs/dir.c | 18 +- fs/ubifs/file.c | 43 ++++ fs/ubifs/ioctl.c | 32 +++ fs/ubifs/journal.c | 2 + fs/ubifs/quota.c | 590 +++++++++++++++++++++++++++++++++++++++++++++ fs/ubifs/super.c | 45 +++- fs/ubifs/tnc.c | 2 +- fs/ubifs/ubifs.h | 27 +++ 9 files changed, 756 insertions(+), 4 deletions(-) create mode 100644 fs/ubifs/quota.c diff --git a/fs/ubifs/Makefile b/fs/ubifs/Makefile index 5c4b845754a7..07f657f5ea8f 100644 --- a/fs/ubifs/Makefile +++ b/fs/ubifs/Makefile @@ -9,3 +9,4 @@ ubifs-y += misc.o ubifs-$(CONFIG_FS_ENCRYPTION) += crypto.o ubifs-$(CONFIG_UBIFS_FS_XATTR) += xattr.o ubifs-$(CONFIG_UBIFS_FS_AUTHENTICATION) += auth.o +ubifs-$(CONFIG_QUOTA) += quota.o diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index e2d3dfb67ae3..b7a2c255ffcd 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -113,6 +113,11 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir, current_time(inode); inode->i_mapping->nrpages = 0; + dquot_initialize(inode); + err = dquot_alloc_inode(inode); + if (err) + goto fail_drop; + if (ubifs_inode(dir)->flags & UBIFS_PROJINHERIT_FL) ui->projid = ubifs_inode(dir)->projid; else @@ -158,8 +163,8 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir, spin_unlock(&c->cnt_lock); ubifs_err(c, "out of inode numbers"); make_bad_inode(inode); - iput(inode); - return ERR_PTR(-EINVAL); + err = -EINVAL; + goto fail_free; } ubifs_warn(c, "running out of inode numbers (current %lu, max %u)", (unsigned long)c->highest_inum, INUM_WATERMARK); @@ -187,6 +192,13 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir, } return inode; + +fail_free: + dquot_free_inode(inode); +fail_drop: + dquot_drop(inode); + iput(inode); + return ERR_PTR(err); } static int dbg_check_name(const struct ubifs_info *c, @@ -776,6 +788,8 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry) unsigned int saved_nlink = inode->i_nlink; struct fscrypt_name nm; + dquot_initialize(inode); + /* * Budget request settings: deletion direntry, deletion inode (+1 for * @dirtied_ino), changing the parent directory inode. If budgeting diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 400970d740bb..a2800d56217f 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -427,8 +427,10 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping, struct ubifs_inode *ui = ubifs_inode(inode); pgoff_t index = pos >> PAGE_SHIFT; int uninitialized_var(err), appending = !!(pos + len > inode->i_size); + int quota_size = 0; int skipped_read = 0; struct page *page; + int ret = 0; ubifs_assert(c, ubifs_inode(inode)->ui_size == inode->i_size); ubifs_assert(c, !c->ro_media && !c->ro_mount); @@ -436,6 +438,16 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping, if (unlikely(c->ro_error)) return -EROFS; + quota_size = ((pos + len) - inode->i_size); + if (quota_size < 0) + quota_size = 0; + if (S_ISREG(inode->i_mode)) { + dquot_initialize(inode); + ret = dquot_alloc_space_nodirty(inode, quota_size); + if (unlikely(ret)) + return ret; + } + /* Try out the fast-path part first */ page = grab_cache_page_write_begin(mapping, index, flags); if (unlikely(!page)) @@ -541,6 +553,7 @@ static int ubifs_write_end(struct file *file, struct address_space *mapping, struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_info *c = inode->i_sb->s_fs_info; loff_t end_pos = pos + len; + int quota_size = 0; int appending = !!(end_pos > inode->i_size); dbg_gen("ino %lu, pos %llu, pg %lu, len %u, copied %d, i_size %lld", @@ -559,6 +572,11 @@ static int ubifs_write_end(struct file *file, struct address_space *mapping, dbg_gen("copied %d instead of %d, read page and repeat", copied, len); cancel_budget(c, page, ui, appending); + quota_size = ((pos + len) - inode->i_size); + if (quota_size < 0) + quota_size = 0; + if (S_ISREG(inode->i_mode)) + dquot_free_space_nodirty(inode, quota_size); ClearPageChecked(page); /* @@ -1112,6 +1130,7 @@ static int do_truncation(struct ubifs_info *c, struct inode *inode, int err; struct ubifs_budget_req req; loff_t old_size = inode->i_size, new_size = attr->ia_size; + loff_t quota_size = (old_size - new_size); int offset = new_size & (UBIFS_BLOCK_SIZE - 1), budgeted = 1; struct ubifs_inode *ui = ubifs_inode(inode); @@ -1191,6 +1210,10 @@ static int do_truncation(struct ubifs_info *c, struct inode *inode, do_attr_changes(inode, attr); err = ubifs_jnl_truncate(c, inode, old_size, new_size); mutex_unlock(&ui->ui_mutex); + if (quota_size < 0) + quota_size = 0; + if (S_ISREG(inode->i_mode)) + dquot_free_space_nodirty(inode, quota_size); out_budg: if (budgeted) @@ -1227,6 +1250,17 @@ static int do_setattr(struct ubifs_info *c, struct inode *inode, if (attr->ia_valid & ATTR_SIZE) { dbg_gen("size %lld -> %lld", inode->i_size, new_size); + if (S_ISREG(inode->i_mode)) { + if (new_size > inode->i_size) { + err = dquot_alloc_space_nodirty(inode, new_size - inode->i_size); + if (err) { + ubifs_release_budget(c, &req); + return err; + } + } else { + dquot_free_space_nodirty(inode, inode->i_size - new_size); + } + } truncate_setsize(inode, new_size); } @@ -1278,6 +1312,15 @@ int ubifs_setattr(struct dentry *dentry, struct iattr *attr) if (err) return err; + if (is_quota_modification(inode, attr)) + dquot_initialize(inode); + if ((attr->ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) || + (attr->ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) { + err = dquot_transfer(inode, attr); + if (err) + return err; + } + if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size < inode->i_size) /* Truncation to a smaller size */ err = do_truncation(c, inode, attr); diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index 824def711381..0805665ce0b4 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -157,16 +157,48 @@ static int ubifs_ioctl_check_project(struct ubifs_inode *ui, struct fsxattr *fa) return 0; } +#ifdef CONFIG_QUOTA +static int ubifs_transfer_project_quota(struct inode *inode, kprojid_t kprojid) +{ + struct dquot *transfer_to[MAXQUOTAS] = {}; + struct ubifs_info *c = inode->i_sb->s_fs_info; + struct super_block *sb = c->vfs_sb; + int err = 0; + + err = dquot_initialize(inode); + if (err) + return err; + + transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid)); + if (!IS_ERR(transfer_to[PRJQUOTA])) { + err = __dquot_transfer(inode, transfer_to); + dqput(transfer_to[PRJQUOTA]); + } + + return err; +} +#else +static int ubifs_transfer_project_quota(struct inode *inode, kprojid_t kprojid) +{ + return 0; +} +#endif + static int ubifs_ioc_setproject(struct file *file, __u32 projid) { struct inode *inode = file_inode(file); struct ubifs_inode *ui = ubifs_inode(inode); kprojid_t kprojid; + int err; kprojid = make_kprojid(&init_user_ns, (projid_t)projid); if (projid_eq(kprojid, ui->projid)) return 0; + err = ubifs_transfer_project_quota(inode, kprojid); + if (err) + return err; + ui->projid = kprojid; return 0; diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index d8961993f9dd..cba061c74c41 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -630,6 +630,8 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, goto out_finish; } ui->del_cmtno = c->cmt_no; + if (S_ISREG(inode->i_mode)) + dquot_free_space_nodirty((struct inode *)inode, inode->i_size); } err = write_head(c, BASEHD, dent, len, &lnum, &dent_offs, sync); diff --git a/fs/ubifs/quota.c b/fs/ubifs/quota.c new file mode 100644 index 000000000000..88351c227975 --- /dev/null +++ b/fs/ubifs/quota.c @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of UBIFS. + * + * Copyright (C) 2019 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + */ +#include "ubifs.h" + +struct ubifs_dqblk { + struct mem_dqblk dqblk; + struct rb_node rb; + struct kqid kqid; +}; + +/** + * ubifs_dqblk_insert - find qid in tree + * @c: UBIFS file-system description object + * @new: The new entry to insert + * + * This inserts a new entry to the dqblk tree which is sorted by qids. Caller + * must make sure this entry doesn't exist already. + */ +static void ubifs_dqblk_insert(struct ubifs_info *c, struct ubifs_dqblk *new) +{ + struct rb_root *root = &c->dqblk_tree[new->kqid.type]; + struct rb_node **link = &root->rb_node, *parent = NULL; + struct ubifs_dqblk *ud; + + /* Go to the bottom of the tree */ + while (*link) { + parent = *link; + + ud = rb_entry(parent, struct ubifs_dqblk, rb); + + if (qid_lt(new->kqid, ud->kqid)) + link = &(*link)->rb_left; + else + link = &(*link)->rb_right; + } + + /* Put the new node there */ + rb_link_node(&new->rb, parent, link); + rb_insert_color(&new->rb, root); +} + +/** + * ubifs_dqblk_find - find qid in tree + * @c: UBIFS file-system description object + * @qid: The qid to look for + * + * This walks the dqblk tree and searches a given qid. Returns the dqblk entry + * when found or NULL otherwise. + */ +static struct ubifs_dqblk *ubifs_dqblk_find(struct ubifs_info *c, + struct kqid qid) +{ + struct rb_node *node = c->dqblk_tree[qid.type].rb_node; + + while (node) { + struct ubifs_dqblk *ud = rb_entry(node, struct ubifs_dqblk, rb); + + if (qid_eq(qid, ud->kqid)) + return ud; + + if (qid_lt(qid, ud->kqid)) + node = node->rb_left; + else + node = node->rb_right; + } + + return NULL; +} + +/** + * ubifs_dqblk_find_next - find the next qid + * @c: UBIFS file-system description object + * @qid: The qid to look for + * + * Find the next dqblk entry with a qid that is bigger or equally big than the + * given qid. Returns the next dqblk entry if found or NULL if no dqblk exists + * with a qid that is at least equally big. + */ +static struct ubifs_dqblk *ubifs_dqblk_find_next(struct ubifs_info *c, + struct kqid qid) +{ + struct rb_node *node = c->dqblk_tree[qid.type].rb_node; + struct ubifs_dqblk *next = NULL; + + while (node) { + struct ubifs_dqblk *ud = rb_entry(node, struct ubifs_dqblk, rb); + + if (qid_eq(qid, ud->kqid)) + return ud; + + if (qid_lt(qid, ud->kqid)) { + if (!next || qid_lt(ud->kqid, next->kqid)) + next = ud; + + node = node->rb_left; + } else { + node = node->rb_right; + } + } + + return next; +} + +/** + * ubifs_dqblk_get - get dqblk entry for given qid + * @c: UBIFS file-system description object + * @qid: The qid to look for + * + * This searches the given qid in the dqblk tree and returns it if found. If not, + * a new dqblk entry is created, inserted into the dqblk tree and returned. + */ +static struct ubifs_dqblk *ubifs_dqblk_get(struct ubifs_info *c, + struct kqid qid) +{ + struct ubifs_dqblk *ud; + + ud = ubifs_dqblk_find(c, qid); + if (ud) + return ud; + + ud = kzalloc(sizeof(*ud), GFP_KERNEL); + if (!ud) + return NULL; + + ud->kqid = qid; + + ubifs_dqblk_insert(c, ud); + + return ud; +} + +/** + * ubifs_check_quota_file - check if quota file is valid for this format + * @sb: The superblock + * @type: Quota type + * + * We currently do not store any quota file on flash. It's completely in RAM and + * thus always valid. + */ +static int ubifs_check_quota_file(struct super_block *sb, int type) +{ + return 1; +} + +/** + * ubifs_read_file_info - read quota file info + * @sb: The superblock + * @type: Quota type + * + * We currently do not store any quota info on flash. Just fill in default + * values. + */ +static int ubifs_read_file_info(struct super_block *sb, int type) +{ + struct quota_info *dqopt = sb_dqopt(sb); + struct mem_dqinfo *info = &dqopt->info[type]; + + down_read(&dqopt->dqio_sem); + + /* + * Used space is stored as unsigned 64-bit value in bytes but + * quota core supports only signed 64-bit values so use that + * as a limit + */ + info->dqi_max_spc_limit = 0x7fffffffffffffffLL; /* 2^63-1 */ + info->dqi_max_ino_limit = 0x7fffffffffffffffLL; + info->dqi_bgrace = 0; + info->dqi_igrace = 0; + info->dqi_flags = 0; + + up_read(&dqopt->dqio_sem); + + return 0; +} + +/** + * ubifs_write_file_info - write quota file to device + * @sb: The superblock + * @type: Quota type + * + * We currently do not store any quota file on flash. Nothing to do here. + */ +static int ubifs_write_file_info(struct super_block *sb, int type) +{ + return 0; +} + +/** + * ubifs_read_dquot - read dquot from device + * @dquot: The dquot entry to read + * + * For us this means finding the entry in the dquot tree. + */ +static int ubifs_read_dquot(struct dquot *dquot) +{ + struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); + struct ubifs_info *c = dquot->dq_sb->s_fs_info; + struct ubifs_dqblk *ud; + int ret = 0; + + down_read(&dqopt->dqio_sem); + + ud = ubifs_dqblk_find(c, dquot->dq_id); + if (ud) + dquot->dq_dqb = ud->dqblk; + else + ret = -ENOENT; + + up_read(&dqopt->dqio_sem); + + return 0; +} + +/** + * ubifs_write_dquot - write dquot to device + * @dquot: The dquot entry to read + * + * For us this means finding or creating the entry in the dquot tree and setting + * it to the dquots contents. + */ +static int ubifs_write_dquot(struct dquot *dquot) +{ + struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); + struct ubifs_info *c = dquot->dq_sb->s_fs_info; + int ret = 0; + struct ubifs_dqblk *ud; + + down_write(&dqopt->dqio_sem); + + ud = ubifs_dqblk_get(c, dquot->dq_id); + if (!ud) { + ret = -ENOMEM; + goto out; + } + + ud->dqblk = dquot->dq_dqb; + +out: + up_write(&dqopt->dqio_sem); + + return ret; +} + +/** + * ubifs_release_dquot - release memory associated to a dquot entry + * @dquot: The dquot entry + * + * Nothing to do here, we didn't allocate anything. Our data is freed at unmount + * time. + */ +static int ubifs_release_dquot(struct dquot *dquot) +{ + return 0; +} + +/** + * ubifs_free_file_info - free memory allocated during reading the file info + * @sb: The superblock + * @type: Quota type + * + * Nothing to do here. + */ +static int ubifs_free_file_info(struct super_block *sb, int type) +{ + return 0; +} + +static int ubifs_get_next_id(struct super_block *sb, struct kqid *qid) +{ + struct quota_info *dqopt = sb_dqopt(sb); + struct ubifs_info *c = sb->s_fs_info; + struct ubifs_dqblk *ud; + int ret = 0; + + down_read(&dqopt->dqio_sem); + + ud = ubifs_dqblk_find_next(c, *qid); + if (!ud) { + ret = -ENOENT; + goto out; + } + + *qid = ud->kqid; +out: + up_read(&dqopt->dqio_sem); + + return ret; +} + +static const struct quota_format_ops ubifs_format_ops = { + .check_quota_file = ubifs_check_quota_file, + .read_file_info = ubifs_read_file_info, + .write_file_info = ubifs_write_file_info, + .free_file_info = ubifs_free_file_info, + .read_dqblk = ubifs_read_dquot, + .commit_dqblk = ubifs_write_dquot, + .release_dqblk = ubifs_release_dquot, + .get_next_id = ubifs_get_next_id, +}; + +/** + * add_inode_quota - read quota informations for an inode + * @c: UBIFS file-system description object + * @zbr: The zbranch to read the inode from + * + * This reads the inode as referred to by @zbr from the medium and + * extracts the quota relevant informations. Returns 0 for success + * or a negative error code otherwise. + */ +static int add_inode_quota(struct ubifs_info *c, struct ubifs_zbranch *zbr) +{ + struct ubifs_ino_node *ino; + int err, type = key_type(c, &zbr->key); + struct kqid qid[MAXQUOTAS]; + kprojid_t kprojid; + kgid_t kgid; + kuid_t kuid; + int i; + + if (type != UBIFS_INO_KEY) { + ubifs_err(c, "%s called on non INO node %d", __func__, type); + return 0; + } + + if (zbr->len < UBIFS_CH_SZ) { + ubifs_err(c, "bad leaf length %d (LEB %d:%d)", + zbr->len, zbr->lnum, zbr->offs); + return -EINVAL; + } + + ino = kmalloc(zbr->len, GFP_NOFS); + if (!ino) + return -ENOMEM; + + err = ubifs_tnc_read_node(c, zbr, ino); + if (err) { + ubifs_err(c, "cannot read leaf node at LEB %d:%d, error %d", + zbr->lnum, zbr->offs, err); + goto out_free; + } + + kuid = make_kuid(&init_user_ns, le32_to_cpu(ino->uid)); + qid[USRQUOTA] = make_kqid_uid(kuid); + + kgid = make_kgid(&init_user_ns, le32_to_cpu(ino->gid)); + qid[GRPQUOTA] = make_kqid_gid(kgid); + + kprojid = make_kprojid(&init_user_ns, le32_to_cpu(ino->projid)); + qid[PRJQUOTA] = make_kqid_projid(kprojid); + + for (i = 0; i < UBIFS_MAXQUOTAS; i++) { + struct ubifs_dqblk *ud; + + ud = ubifs_dqblk_get(c, qid[i]); + if (!ud) { + err = -ENOMEM; + goto out_free; + } + + if (S_ISREG(le32_to_cpu(ino->mode))) + ud->dqblk.dqb_curspace += le64_to_cpu(ino->size); + ud->dqblk.dqb_curinodes++; + } + + err = 0; + +out_free: + kfree(ino); + + return err; +} + +/** + * get_next_parent_inum - get the next inum + * @c: UBIFS file-system description object + * @znode: The znode to start searching from + * @inum: The next inum returned here + * + * Helper function for ubifs_quota_walk_index(). If we know the next inum we + * find up the tree is still the same as we just handled we do not have to walk + * down the tree but can skip these branches. Returns 0 for success or -ENOENT + * when we can't go further to the upper-right but hit the root node instead. + */ +static int get_next_parent_inum(struct ubifs_info *c, struct ubifs_znode *znode, + ino_t *inum) +{ + struct ubifs_znode *zp; + + while (1) { + int nn; + + zp = znode->parent; + if (!zp) + return -ENOENT; + + nn = znode->iip + 1; + znode = zp; + + if (nn < znode->child_cnt) { + *inum = key_inum(c, &znode->zbranch[nn].key); + return 0; + } + } +} + +/** + * ubifs_quota_walk_index - Collect quota info + * @c: UBIFS file-system description object + * + * This function walks the whole index and collects the quota usage information + * for all inodes. Returns 0 for success or a negative error code otherwise. + */ +static int ubifs_quota_walk_index(struct ubifs_info *c) +{ + int err; + struct ubifs_znode *znode; + int i, nn = 0; + ino_t inum = 0, pinum; + + if (!c->zroot.znode) { + c->zroot.znode = ubifs_load_znode(c, &c->zroot, NULL, 0); + if (IS_ERR(c->zroot.znode)) { + err = PTR_ERR(c->zroot.znode); + c->zroot.znode = NULL; + return err; + } + } + + znode = c->zroot.znode; + + while (1) { + while (znode->level != 0) { + znode = ubifs_get_znode(c, znode, nn); + if (IS_ERR(znode)) + return PTR_ERR(znode); + nn = 0; + } + + for (i = 0; i < znode->child_cnt; i++) { + if (inum == key_inum(c, &znode->zbranch[i].key)) + continue; + + add_inode_quota(c, &znode->zbranch[i]); + inum = key_inum(c, &znode->zbranch[i].key); + } + + while (1) { + struct ubifs_znode *zp; + + zp = znode->parent; + if (!zp) + return 0; + + nn = znode->iip + 1; + znode = zp; + + err = get_next_parent_inum(c, znode, &pinum); + + /* + * Optimization: When the next parent node is still for + * the inode we just handled we can skip parsing our + * children. + */ + if (!err && pinum == inum) + continue; + + /* + * Optimization: We can skip scanning all child nodes + * which are for the same inode as we already looked at. + */ + while (nn < znode->child_cnt - 1 && + inum == key_inum(c, &znode->zbranch[nn + 1].key)) + nn++; + + if (nn < znode->child_cnt) + break; + } + } +} + +/** + * ubifs_enable_quotas - enable quota + * @c: UBIFS file-system description object + * + * Enable usage tracking for all quota types. + */ +int ubifs_enable_quotas(struct ubifs_info *c) +{ + struct super_block *sb = c->vfs_sb; + struct quota_info *dqopt = sb_dqopt(sb); + int type; + + if (!c->quota_enable) + return 0; + + dqopt->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY; + + for (type = 0; type < UBIFS_MAXQUOTAS; type++) { + struct mem_dqinfo *info = sb_dqinfo(sb, type); + unsigned int flags = DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED; + + dqopt->flags |= dquot_state_flag(flags, type); + dqopt->info[type].dqi_flags |= DQF_SYS_FILE; + dqopt->ops[type] = &ubifs_format_ops; + + info->dqi_max_spc_limit = 0x7fffffffffffffffLL; + info->dqi_max_ino_limit = 0x7fffffffffffffffLL; + + c->dqblk_tree[type] = RB_ROOT; + } + + return ubifs_quota_walk_index(c); +} + +/** + * ubifs_disable_quotas - disable quota + * @c: UBIFS file-system description object + * + * Disable quota for UBFIS. + */ +void ubifs_disable_quotas(struct ubifs_info *c) +{ + struct rb_node *n; + struct ubifs_dqblk *ud; + int type; + + if (!c->quota_enable) + return; + + dquot_disable(c->vfs_sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED); + + for (type = 0; type < UBIFS_MAXQUOTAS; type++) { + while ((n = rb_first(&c->dqblk_tree[type]))) { + ud = rb_entry(n, struct ubifs_dqblk, rb); + rb_erase(n, &c->dqblk_tree[type]); + kfree(ud); + } + } +} + +static int ubifs_get_projid(struct inode *inode, kprojid_t *projid) +{ + struct ubifs_inode *ui = ubifs_inode(inode); + + *projid = ui->projid; + + return 0; +} + +static const struct dquot_operations ubifs_dquot_operations = { + .write_dquot = dquot_commit, + .acquire_dquot = dquot_acquire, + .release_dquot = dquot_release, + .mark_dirty = dquot_mark_dquot_dirty, + .write_info = dquot_commit_info, + .alloc_dquot = dquot_alloc, + .destroy_dquot = dquot_destroy, + .get_next_id = dquot_get_next_id, + .get_projid = ubifs_get_projid, +}; + +static const struct quotactl_ops ubifs_quotactl_ops = { + .get_state = dquot_get_state, + .set_info = dquot_set_dqinfo, + .get_dqblk = dquot_get_dqblk, + .set_dqblk = dquot_set_dqblk, + .get_nextdqblk = dquot_get_next_dqblk, +}; + +/** + * ubifs_init_quota - init quota ops for UBIFS + * @c: UBIFS file-system description object + * + * This inits the quota operations for UBIFS + */ +void ubifs_init_quota(struct ubifs_info *c) +{ + struct super_block *sb = c->vfs_sb; + + if (!c->quota_enable) + return; + + sb->dq_op = &ubifs_dquot_operations; + sb->s_qcop = &ubifs_quotactl_ops; + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; +} diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index dc7e235b1a4a..3f20866b73db 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -151,6 +151,7 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) switch (inode->i_mode & S_IFMT) { case S_IFREG: + inode_set_bytes(inode, ino->size); inode->i_mapping->a_ops = &ubifs_file_address_operations; inode->i_op = &ubifs_file_inode_operations; inode->i_fop = &ubifs_file_operations; @@ -344,6 +345,7 @@ static void ubifs_evict_inode(struct inode *inode) if (is_bad_inode(inode)) goto out; + dquot_initialize(inode); ui->ui_size = inode->i_size = 0; err = ubifs_jnl_delete_inode(c, inode); if (err) @@ -353,7 +355,7 @@ static void ubifs_evict_inode(struct inode *inode) */ ubifs_err(c, "can't delete inode %lu, error %d", inode->i_ino, err); - + dquot_free_inode(inode); out: if (ui->dirty) ubifs_release_dirty_inode_budget(c, ui); @@ -363,6 +365,7 @@ static void ubifs_evict_inode(struct inode *inode) smp_wmb(); } done: + dquot_drop(inode); clear_inode(inode); fscrypt_put_encryption_info(inode); } @@ -425,6 +428,9 @@ static int ubifs_show_options(struct seq_file *s, struct dentry *root) else if (c->mount_opts.chk_data_crc == 1) seq_puts(s, ",no_chk_data_crc"); + if (c->quota_enable) + seq_puts(s, ",quota"); + if (c->mount_opts.override_compr) { seq_printf(s, ",compr=%s", ubifs_compr_name(c, c->mount_opts.compr_type)); @@ -913,6 +919,13 @@ static int check_volume_empty(struct ubifs_info *c) return 0; } +#ifdef CONFIG_QUOTA +static struct dquot **ubifs_get_dquots(struct inode *inode) +{ + return ubifs_inode(inode)->i_dquot; +} +#endif + /* * UBIFS mount options. * @@ -940,6 +953,7 @@ enum { Opt_auth_key, Opt_auth_hash_name, Opt_ignore, + Opt_quota, Opt_err, }; @@ -956,6 +970,7 @@ static const match_table_t tokens = { {Opt_ignore, "ubi=%s"}, {Opt_ignore, "vol=%s"}, {Opt_assert, "assert=%s"}, + {Opt_quota, "quota"}, {Opt_err, NULL}, }; @@ -1090,6 +1105,15 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options, break; case Opt_ignore: break; +#ifdef CONFIG_QUOTA + case Opt_quota: + c->quota_enable = true; + break; +#else + case Opt_quota: + ubifs_err(c, "quota operations not supported"); + break; +#endif default: { unsigned long flag; @@ -1786,6 +1810,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) */ err = dbg_check_space_info(c); } + c->vfs_sb->s_flags &= ~SB_RDONLY; mutex_unlock(&c->umount_mutex); return err; @@ -1855,6 +1880,7 @@ static void ubifs_remount_ro(struct ubifs_info *c) err = dbg_check_space_info(c); if (err) ubifs_ro_mode(c, err); + c->vfs_sb->s_flags |= SB_RDONLY; mutex_unlock(&c->umount_mutex); } @@ -1865,6 +1891,8 @@ static void ubifs_put_super(struct super_block *sb) ubifs_msg(c, "un-mount UBI device %d", c->vi.ubi_num); + ubifs_disable_quotas(c); + /* * The following asserts are only valid if there has not been a failure * of the media. For example, there will be dirty inodes if we failed @@ -1962,11 +1990,17 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) err = ubifs_remount_rw(c); if (err) return err; + err = dquot_resume(sb, -1); + if (err) + return err; } else if (!c->ro_mount && (*flags & SB_RDONLY)) { if (c->ro_error) { ubifs_msg(c, "cannot re-mount R/O due to prior errors"); return -EROFS; } + err = dquot_suspend(sb, -1); + if (err) + return err; ubifs_remount_ro(c); } @@ -1997,6 +2031,9 @@ const struct super_operations ubifs_super_operations = { .remount_fs = ubifs_remount_fs, .show_options = ubifs_show_options, .sync_fs = ubifs_sync_fs, +#ifdef CONFIG_QUOTA + .get_dquots = ubifs_get_dquots, +#endif }; /** @@ -2157,6 +2194,8 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) #endif fscrypt_set_ops(sb, &ubifs_crypt_operations); + ubifs_init_quota(c); + mutex_lock(&c->umount_mutex); err = mount_ubifs(c); if (err) { @@ -2171,6 +2210,10 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) goto out_umount; } + err = ubifs_enable_quotas(c); + if (err) + goto out_umount; + sb->s_root = d_make_root(root); if (!sb->s_root) { err = -ENOMEM; diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index 188fa036e655..624568365ad3 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -600,7 +600,7 @@ struct ubifs_znode *ubifs_get_znode(struct ubifs_info *c, * This function returns %0 if the next TNC entry is found, %-ENOENT if there is * no next entry, or a negative error code otherwise. */ -static int tnc_next(struct ubifs_info *c, struct ubifs_znode **zn, int *n) +int tnc_next(struct ubifs_info *c, struct ubifs_znode **zn, int *n) { struct ubifs_znode *znode = *zn; int nn = *n; diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index fd9e52749ef7..884fce6c133e 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -27,6 +27,7 @@ #include <linux/security.h> #include <linux/xattr.h> #include <linux/random.h> +#include <linux/quotaops.h> #include <crypto/hash_info.h> #include <crypto/hash.h> #include <crypto/algapi.h> @@ -157,6 +158,8 @@ #define UBIFS_DEF_PROJID 0 +#define UBIFS_MAXQUOTAS 3 + /* * Lockdep classes for UBIFS inode @ui_mutex. */ @@ -416,6 +419,9 @@ struct ubifs_inode { loff_t synced_i_size; loff_t ui_size; int flags; +#ifdef CONFIG_QUOTA + struct dquot *i_dquot[UBIFS_MAXQUOTAS]; +#endif kprojid_t projid; pgoff_t last_page_read; pgoff_t read_in_a_row; @@ -1045,6 +1051,7 @@ struct ubifs_debug_info; * @rw_incompat: the media is not R/W compatible * @assert_action: action to take when a ubifs_assert() fails * @authenticated: flag indigating the FS is mounted in authenticated mode + * @quota_enable: If true, quota is enabled on this filesystem * * @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and * @calc_idx_sz @@ -1386,6 +1393,9 @@ struct ubifs_info { struct ubi_device_info di; struct ubi_volume_info vi; + bool quota_enable; + struct rb_root dqblk_tree[UBIFS_MAXQUOTAS]; + struct rb_root orph_tree; struct list_head orph_list; struct list_head orph_new; @@ -2108,6 +2118,23 @@ static inline bool ubifs_crypt_is_encrypted(const struct inode *inode) return ui->flags & UBIFS_CRYPT_FL; } +/* quota.c */ +#ifdef CONFIG_QUOTA +void ubifs_init_quota(struct ubifs_info *c); +int ubifs_enable_quotas(struct ubifs_info *c); +void ubifs_disable_quotas(struct ubifs_info *c); +#else +static inline void ubifs_init_quota(struct ubifs_info *c) +{ +} +static inline int ubifs_enable_quotas(struct ubifs_info *c) +{ + return 0; +} +static inline void ubifs_disable_quotas(struct ubifs_info *c) +{ +} +#endif /* Normal UBIFS messages */ __printf(2, 3) void ubifs_msg(const struct ubifs_info *c, const char *fmt, ...); -- 2.20.1
> -----Original Message-----
> From: linux-mtd [mailto:linux-mtd-bounces@lists.infradead.org] On Behalf Of
> Sascha Hauer
> Sent: Wednesday, August 14, 2019 2:19 PM
> To: linux-fsdevel@vger.kernel.org
> Cc: Richard Weinberger <richard@nod.at>; Sascha Hauer
> <s.hauer@pengutronix.de>; linux-mtd@lists.infradead.org;
> kernel@pengutronix.de; Jan Kara <jack@suse.com>
> Subject: [PATCH 07/11] ubifs: Add support for FS_IOC_FS[SG]ETXATTR ioctls
>
> The FS_IOC_FS[SG]ETXATTR ioctls are an alternative to FS_IOC_[GS]ETFLAGS
> with additional features. This patch adds support for these ioctls.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> fs/ubifs/ioctl.c | 89
> +++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 84 insertions(+), 5 deletions(-)
>
> diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index
> b9c4a51bceea..121aa1003e24 100644
> --- a/fs/ubifs/ioctl.c
> +++ b/fs/ubifs/ioctl.c
> @@ -95,9 +95,39 @@ static int ubifs2ioctl(int ubifs_flags)
> return ioctl_flags;
> }
>
> -static int setflags(struct file *file, int flags)
> +/* Transfer xflags flags to internal */ static inline unsigned long
> +ubifs_xflags_to_iflags(__u32 xflags)
> {
> - int oldflags, err, release;
> + unsigned long iflags = 0;
> +
> + if (xflags & FS_XFLAG_SYNC)
> + iflags |= UBIFS_APPEND_FL;
Erm... what does |FS_XFLAG_SYNC| have to do with |*APPEND| ? Is this a typo ?
----
Bye,
Roland
--
Roland Mainz, MAA/CAS
Eckelmann AG, Berliner Str. 161, 65205 Wiesbaden
Telefon +49/611/7103-661, Fax +49/611/7103-133
r.mainz@eckelmann.de
Eckelmann Group - Source of inspiration
[-- Attachment #1: Type: text/plain, Size: 5320 bytes --] Hi Sascha, I love your patch! Perhaps something to improve: [auto build test WARNING on linus/master] [cannot apply to v5.3-rc4 next-20190814] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Sascha-Hauer/Add-quota-support-to-UBIFS/20190815-010732 config: i386-randconfig-a002-201932 (attached as .config) compiler: gcc-7 (Debian 7.4.0-10) 7.4.0 reproduce: # save the attached .config to linux build tree make ARCH=i386 If you fix the issue, kindly add following tag Reported-by: kbuild test robot <lkp@intel.com> Note: it may well be a FALSE warning. FWIW you are at least aware of it now. http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings All warnings (new ones prefixed by >>): fs/quota/quota.c: In function 'quotactl_get_super': fs/quota/quota.c:838:13: error: implicit declaration of function 'quotactl_cmd_write'; did you mean 'quotactl_cmd_onoff'? [-Werror=implicit-function-declaration] } else if (quotactl_cmd_write(cmd)) { ^~~~~~~~~~~~~~~~~~ quotactl_cmd_onoff fs/quota/quota.c: In function 'kernel_quotactl': >> fs/quota/quota.c:853:7: warning: 'exclusive' may be used uninitialized in this function [-Wmaybe-uninitialized] if (exclusive) ^ fs/quota/quota.c:832:23: note: 'exclusive' was declared here bool thawed = false, exclusive; ^~~~~~~~~ cc1: some warnings being treated as errors vim +/exclusive +853 fs/quota/quota.c ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 824 ^1da177e4c3f41 fs/quota.c Linus Torvalds 2005-04-16 825 /* 9361401eb7619c fs/quota.c David Howells 2006-09-30 826 * look up a superblock on which quota ops will be performed 9361401eb7619c fs/quota.c David Howells 2006-09-30 827 * - use the name of a block device to find the superblock thereon 9361401eb7619c fs/quota.c David Howells 2006-09-30 828 */ ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 829 static struct super_block *quotactl_get_super(const char __user *special, int cmd) 9361401eb7619c fs/quota.c David Howells 2006-09-30 830 { 9361401eb7619c fs/quota.c David Howells 2006-09-30 831 struct super_block *sb; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 832 bool thawed = false, exclusive; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 833 int ret; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 834 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 835 if (quotactl_cmd_onoff(cmd)) { 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 836 thawed = true; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 837 exclusive = true; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 @838 } else if (quotactl_cmd_write(cmd)) { 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 839 thawed = true; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 840 exclusive = false; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 841 } 9361401eb7619c fs/quota.c David Howells 2006-09-30 842 ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 843 sb = quotactl_block(special); ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 844 if (IS_ERR(sb)) { ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 845 sb = quotactl_path(special); ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 846 if (IS_ERR(sb)) ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 847 return ERR_CAST(sb); ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 848 } 9361401eb7619c fs/quota.c David Howells 2006-09-30 849 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 850 if (thawed) { 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 851 ret = wait_super_thawed(sb, exclusive); 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 852 if (ret) { 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 @853 if (exclusive) 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 854 drop_super_exclusive(sb); 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 855 else 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 856 drop_super(sb); 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 857 return ERR_PTR(ret); 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 858 } 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 859 } 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 860 9361401eb7619c fs/quota.c David Howells 2006-09-30 861 return sb; 9361401eb7619c fs/quota.c David Howells 2006-09-30 862 } 9361401eb7619c fs/quota.c David Howells 2006-09-30 863 :::::: The code at line 853 was first introduced by commit :::::: 335508f54c9cd0c8589271420bee8a38cff13ed5 fs, quota: introduce wait_super_thawed() to wait until a superblock is thawed :::::: TO: Sascha Hauer <s.hauer@pengutronix.de> :::::: CC: 0day robot <lkp@intel.com> --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 34608 bytes --]
On Wed, Aug 14, 2019 at 02:18:26PM +0200, Sascha Hauer wrote:
> __get_super_thawed() calls __get_super() multiple times. I can't see a case
> where __get_super() would return another valid superblock when called
> again, so move the call to __get_super() out of the loop. This is done in
> preparation for the next patch.
How about "someone has come and unmounted the sucker under us"?
[-- Attachment #1: Type: text/plain, Size: 9108 bytes --] Hi Sascha, I love your patch! Yet something to improve: [auto build test ERROR on linus/master] [cannot apply to v5.3-rc4 next-20190814] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Sascha-Hauer/Add-quota-support-to-UBIFS/20190815-010732 config: i386-randconfig-c001-201932 (attached as .config) compiler: gcc-7 (Debian 7.4.0-10) 7.4.0 reproduce: # save the attached .config to linux build tree make ARCH=i386 If you fix the issue, kindly add following tag Reported-by: kbuild test robot <lkp@intel.com> All errors (new ones prefixed by >>): fs/quota/quota.c: In function 'quotactl_get_super': >> fs/quota/quota.c:838:13: error: implicit declaration of function 'quotactl_cmd_write'; did you mean 'quotactl_cmd_onoff'? [-Werror=implicit-function-declaration] } else if (quotactl_cmd_write(cmd)) { ^~~~~~~~~~~~~~~~~~ quotactl_cmd_onoff Cyclomatic Complexity 1 arch/x86/include/asm/barrier.h:array_index_mask_nospec Cyclomatic Complexity 1 arch/x86/include/asm/current.h:get_current Cyclomatic Complexity 3 include/linux/string.h:memset Cyclomatic Complexity 1 include/linux/err.h:ERR_PTR Cyclomatic Complexity 1 include/linux/err.h:PTR_ERR Cyclomatic Complexity 1 include/linux/err.h:IS_ERR Cyclomatic Complexity 1 include/linux/err.h:ERR_CAST Cyclomatic Complexity 1 include/linux/thread_info.h:check_object_size Cyclomatic Complexity 2 include/linux/thread_info.h:copy_overflow Cyclomatic Complexity 4 include/linux/thread_info.h:check_copy_size Cyclomatic Complexity 1 include/linux/uidgid.h:__kuid_val Cyclomatic Complexity 1 include/linux/uidgid.h:uid_eq Cyclomatic Complexity 1 include/linux/uidgid.h:make_kuid Cyclomatic Complexity 1 include/linux/uidgid.h:make_kgid Cyclomatic Complexity 1 include/linux/projid.h:make_kprojid Cyclomatic Complexity 3 include/linux/quota.h:make_kqid Cyclomatic Complexity 1 include/linux/quota.h:qid_has_mapping Cyclomatic Complexity 1 include/linux/quota.h:dquot_state_flag Cyclomatic Complexity 1 include/linux/fs.h:sb_rdonly Cyclomatic Complexity 1 include/linux/namei.h:user_path_at Cyclomatic Complexity 2 include/linux/uaccess.h:copy_from_user Cyclomatic Complexity 2 include/linux/uaccess.h:copy_to_user Cyclomatic Complexity 1 include/linux/security.h:security_quotactl Cyclomatic Complexity 1 include/linux/cred.h:current_user_ns Cyclomatic Complexity 1 include/linux/quotaops.h:sb_dqopt Cyclomatic Complexity 1 include/linux/quotaops.h:sb_has_quota_usage_enabled Cyclomatic Complexity 1 include/linux/quotaops.h:sb_has_quota_suspended Cyclomatic Complexity 1 include/linux/quotaops.h:sb_has_quota_loaded Cyclomatic Complexity 3 include/linux/quotaops.h:sb_has_quota_active Cyclomatic Complexity 4 fs/quota/quota.c:quota_sync_one Cyclomatic Complexity 3 fs/quota/quota.c:quota_getfmt Cyclomatic Complexity 7 fs/quota/quota.c:quota_getinfo Cyclomatic Complexity 9 fs/quota/quota.c:quota_setinfo Cyclomatic Complexity 1 fs/quota/quota.c:qbtos Cyclomatic Complexity 1 fs/quota/quota.c:stoqb Cyclomatic Complexity 1 fs/quota/quota.c:copy_to_if_dqblk Cyclomatic Complexity 5 fs/quota/quota.c:quota_getquota Cyclomatic Complexity 7 fs/quota/quota.c:copy_from_if_dqblk Cyclomatic Complexity 4 fs/quota/quota.c:quota_setquota Cyclomatic Complexity 3 fs/quota/quota.c:quota_enable Cyclomatic Complexity 3 fs/quota/quota.c:quota_disable Cyclomatic Complexity 7 fs/quota/quota.c:quota_state_to_flags Cyclomatic Complexity 7 fs/quota/quota.c:quota_getstate Cyclomatic Complexity 4 fs/quota/quota.c:quota_getxstate Cyclomatic Complexity 6 fs/quota/quota.c:quota_getstatev Cyclomatic Complexity 6 fs/quota/quota.c:quota_getxstatev Cyclomatic Complexity 1 fs/quota/quota.c:quota_bbtob Cyclomatic Complexity 1 fs/quota/quota.c:quota_btobb Cyclomatic Complexity 16 fs/quota/quota.c:copy_from_xfs_dqblk Cyclomatic Complexity 7 fs/quota/quota.c:copy_qcinfo_from_xfs_dqblk Cyclomatic Complexity 3 fs/quota/quota.c:copy_to_xfs_dqblk Cyclomatic Complexity 5 fs/quota/quota.c:quota_getxquota Cyclomatic Complexity 3 fs/quota/quota.c:quota_rmxquota Cyclomatic Complexity 3 fs/quota/quota.c:quotactl_cmd_onoff Cyclomatic Complexity 1 fs/quota/quota.c:quotactl_block Cyclomatic Complexity 1 fs/quota/quota.c:__do_sys_quotactl Cyclomatic Complexity 3 fs/quota/quota.c:quota_sync_all Cyclomatic Complexity 2 fs/quota/quota.c:quotactl_path Cyclomatic Complexity 8 fs/quota/quota.c:quotactl_get_super Cyclomatic Complexity 10 fs/quota/quota.c:check_quotactl_permission Cyclomatic Complexity 5 fs/quota/quota.c:quota_getnextquota Cyclomatic Complexity 8 fs/quota/quota.c:quota_setxquota Cyclomatic Complexity 5 fs/quota/quota.c:quota_getnextxquota Cyclomatic Complexity 4 fs/quota/quota.c:qtype_enforce_flag Cyclomatic Complexity 5 fs/quota/quota.c:quota_quotaon Cyclomatic Complexity 4 fs/quota/quota.c:quota_quotaoff Cyclomatic Complexity 25 fs/quota/quota.c:do_quotactl Cyclomatic Complexity 9 fs/quota/quota.c:kernel_quotactl Cyclomatic Complexity 1 fs/quota/quota.c:__se_sys_quotactl cc1: some warnings being treated as errors vim +838 fs/quota/quota.c ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 824 ^1da177e4c3f41 fs/quota.c Linus Torvalds 2005-04-16 825 /* 9361401eb7619c fs/quota.c David Howells 2006-09-30 826 * look up a superblock on which quota ops will be performed 9361401eb7619c fs/quota.c David Howells 2006-09-30 827 * - use the name of a block device to find the superblock thereon 9361401eb7619c fs/quota.c David Howells 2006-09-30 828 */ ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 829 static struct super_block *quotactl_get_super(const char __user *special, int cmd) 9361401eb7619c fs/quota.c David Howells 2006-09-30 830 { 9361401eb7619c fs/quota.c David Howells 2006-09-30 831 struct super_block *sb; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 832 bool thawed = false, exclusive; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 833 int ret; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 834 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 835 if (quotactl_cmd_onoff(cmd)) { 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 836 thawed = true; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 837 exclusive = true; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 @838 } else if (quotactl_cmd_write(cmd)) { 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 839 thawed = true; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 840 exclusive = false; 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 841 } 9361401eb7619c fs/quota.c David Howells 2006-09-30 842 ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 843 sb = quotactl_block(special); ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 844 if (IS_ERR(sb)) { ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 845 sb = quotactl_path(special); ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 846 if (IS_ERR(sb)) ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 847 return ERR_CAST(sb); ab9c4e200cc992 fs/quota/quota.c Sascha Hauer 2019-08-14 848 } 9361401eb7619c fs/quota.c David Howells 2006-09-30 849 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 850 if (thawed) { 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 851 ret = wait_super_thawed(sb, exclusive); 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 852 if (ret) { 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 853 if (exclusive) 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 854 drop_super_exclusive(sb); 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 855 else 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 856 drop_super(sb); 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 857 return ERR_PTR(ret); 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 858 } 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 859 } 335508f54c9cd0 fs/quota/quota.c Sascha Hauer 2019-08-14 860 9361401eb7619c fs/quota.c David Howells 2006-09-30 861 return sb; 9361401eb7619c fs/quota.c David Howells 2006-09-30 862 } 9361401eb7619c fs/quota.c David Howells 2006-09-30 863 :::::: The code at line 838 was first introduced by commit :::::: 335508f54c9cd0c8589271420bee8a38cff13ed5 fs, quota: introduce wait_super_thawed() to wait until a superblock is thawed :::::: TO: Sascha Hauer <s.hauer@pengutronix.de> :::::: CC: 0day robot <lkp@intel.com> --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 34932 bytes --]
On Wed, Aug 14, 2019 at 02:18:27PM +0200, Sascha Hauer wrote: > quota uses three different functions to get a superblock from a > block_device. Instead, retrieve the superblock always with get_super() > and introduce wait_super_thawed() which is then used to wait until the > superblock is thawed. This way the code can better be shared with the > code introduced in the next step: We want to add quota support for > filesystems without a block_device, so here functions around a > block_device can't be used. > + * wait_super_thawed - wait for a superblock being thawed > + * @sb: superblock to wait for > + * @excl: if true, get s_umount semaphore exclusively > + * > + * Wait until the superblock is thawed. s_umount semaphore must be released on > + * entry and will be held when returning. > + */ Never expose anything like that - if locking rules depend upon the flags, it MUST NOT be anything beyond a static helper. I'm serious - that kind of stuff ends up with trouble again and again. Just don't.
On Wed, Aug 14, 2019 at 02:18:28PM +0200, Sascha Hauer wrote:
> +/**
> + * reference_super - get a reference to a given superblock
> + * @sb: superblock to get the reference from
> + *
> + * Takes a reference to a superblock. Can be used as when the superblock
> + * is known and leaves it in a state as if get_super had been called.
> + */
> +void reference_super(struct super_block *sb)
> +{
> + spin_lock(&sb_lock);
> + sb->s_count++;
> + spin_unlock(&sb_lock);
> +
> + down_read(&sb->s_umount);
> +}
> +EXPORT_SYMBOL_GPL(reference_super);
NAK, for a plenty of reasons
1) introduction of EXPORT_SYMBOL_GPL garbage
2) aforementioned garbage on something that doesn't need to be exported
3) *way* too easily abused - get_super() is, at least, not tempting to
play with in random code. This one is, and it's too low-level to
allow.
On Thu, Aug 15, 2019 at 12:36:32AM +0100, Al Viro wrote:
> On Wed, Aug 14, 2019 at 02:18:28PM +0200, Sascha Hauer wrote:
> > +/**
> > + * reference_super - get a reference to a given superblock
> > + * @sb: superblock to get the reference from
> > + *
> > + * Takes a reference to a superblock. Can be used as when the superblock
> > + * is known and leaves it in a state as if get_super had been called.
> > + */
> > +void reference_super(struct super_block *sb)
> > +{
> > + spin_lock(&sb_lock);
> > + sb->s_count++;
> > + spin_unlock(&sb_lock);
> > +
> > + down_read(&sb->s_umount);
> > +}
> > +EXPORT_SYMBOL_GPL(reference_super);
>
> NAK, for a plenty of reasons
>
> 1) introduction of EXPORT_SYMBOL_GPL garbage
> 2) aforementioned garbage on something that doesn't need to be exported
> 3) *way* too easily abused - get_super() is, at least, not tempting to
> play with in random code. This one is, and it's too low-level to
> allow.
... and this is a crap userland API.
*IF* you want mountpoint-based variants of quotactl() commands, by all means,
add those. Do not overload the old ones. And for path-based you don't
need to mess with superblock references - just keep the struct path until
the end. That will keep the superblock alive and active just fine.
On Thu, Aug 15, 2019 at 12:39:46AM +0100, Al Viro wrote:
> > 1) introduction of EXPORT_SYMBOL_GPL garbage
> > 2) aforementioned garbage on something that doesn't need to be exported
> > 3) *way* too easily abused - get_super() is, at least, not tempting to
> > play with in random code. This one is, and it's too low-level to
> > allow.
>
> ... and this is a crap userland API.
>
> *IF* you want mountpoint-based variants of quotactl() commands, by all means,
> add those. Do not overload the old ones. And for path-based you don't
> need to mess with superblock references - just keep the struct path until
> the end. That will keep the superblock alive and active just fine.
To clarify: I suggest something like #define Q_PATH 0x400000
with users passing something like QCMD(Q_QUOTAON | Q_PATH, ...) instead
of QCMD(Q_QUOTAON, ...) to get a path-based behaviour.
On Wed, Aug 14, 2019 at 02:11:08PM +0000, Mainz, Roland wrote: > > > > > -----Original Message----- > > From: linux-mtd [mailto:linux-mtd-bounces@lists.infradead.org] On Behalf Of > > Sascha Hauer > > Sent: Wednesday, August 14, 2019 2:19 PM > > To: linux-fsdevel@vger.kernel.org > > Cc: Richard Weinberger <richard@nod.at>; Sascha Hauer > > <s.hauer@pengutronix.de>; linux-mtd@lists.infradead.org; > > kernel@pengutronix.de; Jan Kara <jack@suse.com> > > Subject: [PATCH 07/11] ubifs: Add support for FS_IOC_FS[SG]ETXATTR ioctls > > > > The FS_IOC_FS[SG]ETXATTR ioctls are an alternative to FS_IOC_[GS]ETFLAGS > > with additional features. This patch adds support for these ioctls. > > > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > > --- > > fs/ubifs/ioctl.c | 89 > > +++++++++++++++++++++++++++++++++++++++++++++--- > > 1 file changed, 84 insertions(+), 5 deletions(-) > > > > diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index > > b9c4a51bceea..121aa1003e24 100644 > > --- a/fs/ubifs/ioctl.c > > +++ b/fs/ubifs/ioctl.c > > @@ -95,9 +95,39 @@ static int ubifs2ioctl(int ubifs_flags) > > return ioctl_flags; > > } > > > > -static int setflags(struct file *file, int flags) > > +/* Transfer xflags flags to internal */ static inline unsigned long > > +ubifs_xflags_to_iflags(__u32 xflags) > > { > > - int oldflags, err, release; > > + unsigned long iflags = 0; > > + > > + if (xflags & FS_XFLAG_SYNC) > > + iflags |= UBIFS_APPEND_FL; > > Erm... what does |FS_XFLAG_SYNC| have to do with |*APPEND| ? Is this a typo ? Hm, some copy-paste accident probably. That's rubbish of course. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
On Thu, Aug 15, 2019 at 12:39:46AM +0100, Al Viro wrote: > On Thu, Aug 15, 2019 at 12:36:32AM +0100, Al Viro wrote: > > On Wed, Aug 14, 2019 at 02:18:28PM +0200, Sascha Hauer wrote: > > > +/** > > > + * reference_super - get a reference to a given superblock > > > + * @sb: superblock to get the reference from > > > + * > > > + * Takes a reference to a superblock. Can be used as when the superblock > > > + * is known and leaves it in a state as if get_super had been called. > > > + */ > > > +void reference_super(struct super_block *sb) > > > +{ > > > + spin_lock(&sb_lock); > > > + sb->s_count++; > > > + spin_unlock(&sb_lock); > > > + > > > + down_read(&sb->s_umount); > > > +} > > > +EXPORT_SYMBOL_GPL(reference_super); > > > > NAK, for a plenty of reasons > > > > 1) introduction of EXPORT_SYMBOL_GPL garbage > > 2) aforementioned garbage on something that doesn't need to be exported > > 3) *way* too easily abused - get_super() is, at least, not tempting to > > play with in random code. This one is, and it's too low-level to > > allow. > > ... and this is a crap userland API. > > *IF* you want mountpoint-based variants of quotactl() commands, by all means, > add those. Do not overload the old ones. And for path-based you don't > need to mess with superblock references - just keep the struct path until > the end. That will keep the superblock alive and active just fine. I'll happily drop these changes. To clarify, quota currently does: if (quotactl_cmd_onoff(cmd)) sb = get_super_exclusive_thawed(bdev); else if (quotactl_cmd_write(cmd)) sb = get_super_thawed(bdev); else sb = get_super(bdev); You are saying that the struct super_block I get from a struct path pointer is in a suitable state for all the cases above, right? Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
On Thu 15-08-19 00:51:24, Al Viro wrote:
> On Thu, Aug 15, 2019 at 12:39:46AM +0100, Al Viro wrote:
> > > 1) introduction of EXPORT_SYMBOL_GPL garbage
> > > 2) aforementioned garbage on something that doesn't need to be exported
> > > 3) *way* too easily abused - get_super() is, at least, not tempting to
> > > play with in random code. This one is, and it's too low-level to
> > > allow.
> >
> > ... and this is a crap userland API.
> >
> > *IF* you want mountpoint-based variants of quotactl() commands, by all means,
> > add those. Do not overload the old ones. And for path-based you don't
> > need to mess with superblock references - just keep the struct path until
> > the end. That will keep the superblock alive and active just fine.
>
> To clarify: I suggest something like #define Q_PATH 0x400000
> with users passing something like QCMD(Q_QUOTAON | Q_PATH, ...) instead
> of QCMD(Q_QUOTAON, ...) to get a path-based behaviour.
Yeah, this sounds like a good plan to me. If Sasha plans on using userspace
quota-tools for handling ubifs, some work will be needed there as well but
it's doable.
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
Hello, On Wed 14-08-19 14:18:34, Sascha Hauer wrote: > From: Dongsheng Yang <yangds.fnst@cn.fujitsu.com> > > This introduces poor man's quota support for UBIFS. Unlike other > implementations we never store anything on the flash. This has two > big advantages: > > - No possible regressions with a changed on-disk format > - no quota files can get out of sync. > > There are downsides aswell: > > - During mount the whole index must be scanned which takes some time > - The quota limits must be set manually each time a filesystem is mounted. > > UBIFS is targetted for embedded systems and quota limits are likely not > changed interactively, so having to restore the quota limits with a > script shouldn't be a big deal. The mount time penalty is a price we > must pay, but for that we get a simple and straight forward > implementation for this rather rarely used feature. > > The quota data itself is stored in a red-black tree in memory. It is > implemented as a quota format. When enabled with the "quota" mount > option all three quota types (user, group, project) are enabled. > > The quota integration into UBIFS is taken from a series posted earlier > by Dongsheng Yang. Like the earlier series we only account regular files > for quota. All others are counted in the number of files, but do not > require any quota space. > > iSigned-off-by: Sascha Hauer <s.hauer@pengutronix.de> Missing Signed-off-by from Dongsheng? Also yours has 'i' there. > +/** > + * ubifs_enable_quotas - enable quota > + * @c: UBIFS file-system description object > + * > + * Enable usage tracking for all quota types. > + */ > +int ubifs_enable_quotas(struct ubifs_info *c) > +{ > + struct super_block *sb = c->vfs_sb; > + struct quota_info *dqopt = sb_dqopt(sb); > + int type; > + > + if (!c->quota_enable) > + return 0; > + > + dqopt->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY; > + > + for (type = 0; type < UBIFS_MAXQUOTAS; type++) { > + struct mem_dqinfo *info = sb_dqinfo(sb, type); > + unsigned int flags = DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED; > + > + dqopt->flags |= dquot_state_flag(flags, type); > + dqopt->info[type].dqi_flags |= DQF_SYS_FILE; > + dqopt->ops[type] = &ubifs_format_ops; > + > + info->dqi_max_spc_limit = 0x7fffffffffffffffLL; > + info->dqi_max_ino_limit = 0x7fffffffffffffffLL; This is wrong. You shouldn't mess with quota internals like that. You should use dquot_enable() (and you even implemented ->read_file_info() format operation to properly fill in info structure). So you just need to change dquot_enable() to cope with situation when inode is NULL. Probably create a variant dquot_enable_sb() that gets superblock pointer instead of inode and then factor out bits from vfs_load_quota_inode() that are needed also for the case without quota inode. > @@ -956,6 +970,7 @@ static const match_table_t tokens = { > {Opt_ignore, "ubi=%s"}, > {Opt_ignore, "vol=%s"}, > {Opt_assert, "assert=%s"}, > + {Opt_quota, "quota"}, > {Opt_err, NULL}, > }; Usually, we have usrquota, grpquota, prjquota mount options to enable individual quota types. It would seem better not to differ from these unless you have a good reason. Honza -- Jan Kara <jack@suse.com> SUSE Labs, CR
On Wed 14-08-19 14:18:25, Sascha Hauer wrote: > For filesystems which do not have a quota_format_type such as upcoming > UBIFS quota fmt may be NULL. Only put the format when it's non NULL. > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> But you do have quota format in the end. So is this patch needed? Honza > --- > fs/quota/dquot.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c > index 3cb836351c22..b043468e53f2 100644 > --- a/fs/quota/dquot.c > +++ b/fs/quota/dquot.c > @@ -218,6 +218,9 @@ static struct quota_format_type *find_quota_format(int id) > > static void put_quota_format(struct quota_format_type *fmt) > { > + if (!fmt) > + return; > + > module_put(fmt->qf_owner); > } > > -- > 2.20.1 > > -- Jan Kara <jack@suse.com> SUSE Labs, CR
On Thu, Aug 15, 2019 at 01:18:00PM +0200, Jan Kara wrote: > On Wed 14-08-19 14:18:25, Sascha Hauer wrote: > > For filesystems which do not have a quota_format_type such as upcoming > > UBIFS quota fmt may be NULL. Only put the format when it's non NULL. > > > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > > But you do have quota format in the end. So is this patch needed? I have quota_format_ops, but as I do not store any quota data I do not have a quota_format_type. Yes, this patch is needed. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |