quota: Add mountpath based quota support

Add syscall quotactl_path, a variant of quotactl which allows to specify
the mountpath instead of a path of to a block device.

The quotactl syscall expects a path to the mounted block device to
specify the filesystem to work on. This limits usage to filesystems
which actually have a block device. quotactl_path replaces the path
to the block device with a path where the filesystem is mounted at.

The global Q_SYNC command to sync all filesystems is not supported for
this new syscall, otherwise quotactl_path behaves like quotactl.

Link: https://lore.kernel.org/r/20210304123541.30749-2-s.hauer@pengutronix.de
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
Sascha Hauer 2021-03-04 13:35:39 +01:00 committed by Jan Kara
parent 1e28eed176
commit 9dfa23c8de
1 changed files with 46 additions and 3 deletions

View File

@ -17,6 +17,7 @@
#include <linux/capability.h>
#include <linux/quotaops.h>
#include <linux/types.h>
#include <linux/mount.h>
#include <linux/writeback.h>
#include <linux/nospec.h>
#include "compat.h"
@ -827,8 +828,6 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
}
}
#ifdef CONFIG_BLOCK
/* Return 1 if 'cmd' will block on frozen filesystem */
static int quotactl_cmd_write(int cmd)
{
@ -850,7 +849,6 @@ static int quotactl_cmd_write(int cmd)
}
return 1;
}
#endif /* CONFIG_BLOCK */
/* Return true if quotactl command is manipulating quota on/off state */
static bool quotactl_cmd_onoff(int cmd)
@ -968,3 +966,48 @@ out:
path_put(pathp);
return ret;
}
SYSCALL_DEFINE4(quotactl_path, unsigned int, cmd, const char __user *,
mountpoint, qid_t, id, void __user *, addr)
{
struct super_block *sb;
struct path mountpath;
unsigned int cmds = cmd >> SUBCMDSHIFT;
unsigned int type = cmd & SUBCMDMASK;
int ret;
if (type >= MAXQUOTAS)
return -EINVAL;
ret = user_path_at(AT_FDCWD, mountpoint,
LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT, &mountpath);
if (ret)
return ret;
sb = mountpath.mnt->mnt_sb;
if (quotactl_cmd_write(cmds)) {
ret = mnt_want_write(mountpath.mnt);
if (ret)
goto out;
}
if (quotactl_cmd_onoff(cmds))
down_write(&sb->s_umount);
else
down_read(&sb->s_umount);
ret = do_quotactl(sb, type, cmds, id, addr, ERR_PTR(-EINVAL));
if (quotactl_cmd_onoff(cmds))
up_write(&sb->s_umount);
else
up_read(&sb->s_umount);
if (quotactl_cmd_write(cmds))
mnt_drop_write(mountpath.mnt);
out:
path_put(&mountpath);
return ret;
}