diff mbox series

[1/8] quota: Allow to pass mount path to quotactl

Message ID 20210122151536.7982-2-s.hauer@pengutronix.de
State Superseded
Headers show
Series Add quota support to UBIFS | expand

Commit Message

Sascha Hauer Jan. 22, 2021, 3:15 p.m. UTC
This patch introduces the Q_PATH flag to the quotactl cmd argument.
When given, the path given in the special argument to quotactl will
be the mount path where the filesystem is mounted, instead of a path
to the block device.
This is necessary for filesystems which do not have a block device as
backing store. Particularly this is done for upcoming UBIFS support.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/quota/quota.c           | 66 ++++++++++++++++++++++++++++----------
 include/uapi/linux/quota.h |  1 +
 2 files changed, 50 insertions(+), 17 deletions(-)

Comments

Christoph Hellwig Jan. 22, 2021, 5:16 p.m. UTC | #1
On Fri, Jan 22, 2021 at 04:15:29PM +0100, Sascha Hauer wrote:
> This patch introduces the Q_PATH flag to the quotactl cmd argument.
> When given, the path given in the special argument to quotactl will
> be the mount path where the filesystem is mounted, instead of a path
> to the block device.
> This is necessary for filesystems which do not have a block device as
> backing store. Particularly this is done for upcoming UBIFS support.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>

I hate overloading quotactl even more.  Why not add a new quotactl_path
syscall instead?

> +static struct super_block *quotactl_sb(dev_t dev, int cmd)
>  {
>  	struct super_block *sb;
>  	bool excl = false, thawed = false;
>  
>  	if (quotactl_cmd_onoff(cmd)) {
>  		excl = true;
> @@ -901,12 +887,50 @@ static struct super_block *quotactl_block(const char __user *special, int cmd)
>  		goto retry;
>  	}
>  	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)
> +{
> +#ifdef CONFIG_BLOCK
> +	struct filename *tmp = getname(special);
> +	int error;
> +	dev_t dev;
>  
> +	if (IS_ERR(tmp))
> +		return ERR_CAST(tmp);
> +	error = lookup_bdev(tmp->name, &dev);
> +	putname(tmp);
> +	if (error)
> +		return ERR_PTR(error);
> +
> +	return quotactl_sb(dev, cmd);
>  #else
>  	return ERR_PTR(-ENODEV);
>  #endif

Normal kernel style would be to keep the ifdef entirely outside the
function.

> +static struct super_block *quotactl_path(const char __user *special, int cmd)
> +{
> +	struct super_block *sb;
> +	struct path path;
> +	int error;
> +
> +	error = user_path_at(AT_FDCWD, special, LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT,
> +			   &path);

This adds an overly long line.

> +	if (error)
> +		return ERR_PTR(error);
> +
> +	sb = quotactl_sb(path.mnt->mnt_sb->s_dev, cmd);

I think quotactl_sb should take the superblock directly.  This will
need a little refactoring of user_get_super, but will lead to much
better logic.
Sascha Hauer Jan. 25, 2021, 8:38 a.m. UTC | #2
On Fri, Jan 22, 2021 at 05:16:58PM +0000, Christoph Hellwig wrote:
> On Fri, Jan 22, 2021 at 04:15:29PM +0100, Sascha Hauer wrote:
> > This patch introduces the Q_PATH flag to the quotactl cmd argument.
> > When given, the path given in the special argument to quotactl will
> > be the mount path where the filesystem is mounted, instead of a path
> > to the block device.
> > This is necessary for filesystems which do not have a block device as
> > backing store. Particularly this is done for upcoming UBIFS support.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> 
> I hate overloading quotactl even more.  Why not add a new quotactl_path
> syscall instead?

We can probably do that. Honza, what do you think?

> 
> > +static struct super_block *quotactl_sb(dev_t dev, int cmd)
> >  {
> >  	struct super_block *sb;
> >  	bool excl = false, thawed = false;
> >  
> >  	if (quotactl_cmd_onoff(cmd)) {
> >  		excl = true;
> > @@ -901,12 +887,50 @@ static struct super_block *quotactl_block(const char __user *special, int cmd)
> >  		goto retry;
> >  	}
> >  	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)
> > +{
> > +#ifdef CONFIG_BLOCK
> > +	struct filename *tmp = getname(special);
> > +	int error;
> > +	dev_t dev;
> >  
> > +	if (IS_ERR(tmp))
> > +		return ERR_CAST(tmp);
> > +	error = lookup_bdev(tmp->name, &dev);
> > +	putname(tmp);
> > +	if (error)
> > +		return ERR_PTR(error);
> > +
> > +	return quotactl_sb(dev, cmd);
> >  #else
> >  	return ERR_PTR(-ENODEV);
> >  #endif
> 
> Normal kernel style would be to keep the ifdef entirely outside the
> function.

It has been like this before, so changing this should be done in an
extra patch. Not sure if it's worth it though.

> 
> > +static struct super_block *quotactl_path(const char __user *special, int cmd)
> > +{
> > +	struct super_block *sb;
> > +	struct path path;
> > +	int error;
> > +
> > +	error = user_path_at(AT_FDCWD, special, LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT,
> > +			   &path);
> 
> This adds an overly long line.

ok, will change.

> 
> > +	if (error)
> > +		return ERR_PTR(error);
> > +
> > +	sb = quotactl_sb(path.mnt->mnt_sb->s_dev, cmd);
> 
> I think quotactl_sb should take the superblock directly.  This will
> need a little refactoring of user_get_super, but will lead to much
> better logic.

What do you mean by "take"? Take the superblock as an argument to
quotactl_sb() or take a reference to the superblock?
Sorry, I don't really get where you aiming at.

Sascha
Jan Kara Jan. 25, 2021, 3:45 p.m. UTC | #3
On Mon 25-01-21 09:38:54, Sascha Hauer wrote:
> On Fri, Jan 22, 2021 at 05:16:58PM +0000, Christoph Hellwig wrote:
> > On Fri, Jan 22, 2021 at 04:15:29PM +0100, Sascha Hauer wrote:
> > > This patch introduces the Q_PATH flag to the quotactl cmd argument.
> > > When given, the path given in the special argument to quotactl will
> > > be the mount path where the filesystem is mounted, instead of a path
> > > to the block device.
> > > This is necessary for filesystems which do not have a block device as
> > > backing store. Particularly this is done for upcoming UBIFS support.
> > > 
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > 
> > I hate overloading quotactl even more.  Why not add a new quotactl_path
> > syscall instead?
> 
> We can probably do that. Honza, what do you think?

Hum, yes, probably it would be cleaner to add a new syscall for this so
that we don't overload quotactl(2). I just didn't think of this.

> > > +static struct super_block *quotactl_path(const char __user *special, int cmd)
> > > +{
> > > +	struct super_block *sb;
> > > +	struct path path;
> > > +	int error;
> > > +
> > > +	error = user_path_at(AT_FDCWD, special, LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT,
> > > +			   &path);
> > 
> > This adds an overly long line.
> 
> ok, will change.
> 
> > 
> > > +	if (error)
> > > +		return ERR_PTR(error);
> > > +
> > > +	sb = quotactl_sb(path.mnt->mnt_sb->s_dev, cmd);
> > 
> > I think quotactl_sb should take the superblock directly.  This will
> > need a little refactoring of user_get_super, but will lead to much
> > better logic.
> 
> What do you mean by "take"? Take the superblock as an argument to
> quotactl_sb() or take a reference to the superblock?
> Sorry, I don't really get where you aiming at.

I think Christoph was pointing at the fact it is suboptimal to search for
superblock by device number when you already have a pointer to it.  And I
guess he was suggesting we could pass 'sb' pointer to quotactl_sb() when we
already have it. Although to be honest, I'm not sure how Christoph imagines
the refactoring of user_get_super() he mentions - when we have a path
looked up through user_path(), that pins the superblock the path is on so
it cannot be unmounted. So perhaps quotactl_sb() can done like:

	...
retry:
	if (passed_sb) {
		sb = passed_sb;
		sb->s_count++;
		if (excl)
			down_write(&sb->s_umount);
		else
			down_read(&sb->s_umount);
	} else {
		sb = user_get_super(dev, excl);
		if (!sb)
			return ERR_PTR(-ENODEV);
	}
	...


								Honza
Christoph Hellwig Jan. 25, 2021, 8:42 p.m. UTC | #4
On Mon, Jan 25, 2021 at 04:45:07PM +0100, Jan Kara wrote:
> > What do you mean by "take"? Take the superblock as an argument to
> > quotactl_sb() or take a reference to the superblock?
> > Sorry, I don't really get where you aiming at.
> 
> I think Christoph was pointing at the fact it is suboptimal to search for
> superblock by device number when you already have a pointer to it.  And I
> guess he was suggesting we could pass 'sb' pointer to quotactl_sb() when we
> already have it. Although to be honest, I'm not sure how Christoph imagines
> the refactoring of user_get_super() he mentions - when we have a path
> looked up through user_path(), that pins the superblock the path is on so
> it cannot be unmounted. So perhaps quotactl_sb() can done like:

I don't think we need a quotactl_sb at all, do_quotactl is in fact a
pretty good abstraction as-is.

For the path based one we just need to factor out a little helper
to set excl and thaw and then call it like:

	sb = path.dentry->d_inode->i_sb;

	if (excl)
		down_write(&sb->s_umount);
	else
		down_read(&sb->s_umount);
	if (thawed && sb->s_writers.frozen != SB_UNFROZEN)
		ret = -EBUSY;
	else
		ret = do_quotactl(sb, type, cmds, id, addr, &path);
	if (excl)
		up_write(&sb->s_umount);
	else
		up_read(&sb->s_umount);

as there is no good reason to bring over the somewhat strange wait until
unfrozen semantics to a new syscall.
Sascha Hauer Jan. 26, 2021, 10:45 a.m. UTC | #5
On Mon, Jan 25, 2021 at 04:45:07PM +0100, Jan Kara wrote:
> On Mon 25-01-21 09:38:54, Sascha Hauer wrote:
> > On Fri, Jan 22, 2021 at 05:16:58PM +0000, Christoph Hellwig wrote:
> > > On Fri, Jan 22, 2021 at 04:15:29PM +0100, Sascha Hauer wrote:
> > > > This patch introduces the Q_PATH flag to the quotactl cmd argument.
> > > > When given, the path given in the special argument to quotactl will
> > > > be the mount path where the filesystem is mounted, instead of a path
> > > > to the block device.
> > > > This is necessary for filesystems which do not have a block device as
> > > > backing store. Particularly this is done for upcoming UBIFS support.
> > > > 
> > > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > 
> > > I hate overloading quotactl even more.  Why not add a new quotactl_path
> > > syscall instead?
> > 
> > We can probably do that. Honza, what do you think?
> 
> Hum, yes, probably it would be cleaner to add a new syscall for this so
> that we don't overload quotactl(2). I just didn't think of this.

How should the semantics of that new syscall look like?

The easiest and most obvious way would be to do it like the quotactl(2)
and just replace the special argument with a path:

int quotactl_path(int cmd, const char *path, int id, caddr_t addr);

If we try adding a new syscall then we could completely redefine the API
and avoid the shortcomings of the original quotactl(2) if there are any.
Can you foresee the discussions we end up in? I am afraid I am opening a
can of worms here.
OTOH there might be value in keeping the new syscall compatible to the
existing one, but I don't know how much this argument counts.

Sascha
Jan Kara Jan. 26, 2021, 1:17 p.m. UTC | #6
On Mon 25-01-21 20:42:49, Christoph Hellwig wrote:
> On Mon, Jan 25, 2021 at 04:45:07PM +0100, Jan Kara wrote:
> > > What do you mean by "take"? Take the superblock as an argument to
> > > quotactl_sb() or take a reference to the superblock?
> > > Sorry, I don't really get where you aiming at.
> > 
> > I think Christoph was pointing at the fact it is suboptimal to search for
> > superblock by device number when you already have a pointer to it.  And I
> > guess he was suggesting we could pass 'sb' pointer to quotactl_sb() when we
> > already have it. Although to be honest, I'm not sure how Christoph imagines
> > the refactoring of user_get_super() he mentions - when we have a path
> > looked up through user_path(), that pins the superblock the path is on so
> > it cannot be unmounted. So perhaps quotactl_sb() can done like:
> 
> I don't think we need a quotactl_sb at all, do_quotactl is in fact a
> pretty good abstraction as-is.
> 
> For the path based one we just need to factor out a little helper
> to set excl and thaw and then call it like:
> 
> 	sb = path.dentry->d_inode->i_sb;
> 
> 	if (excl)
> 		down_write(&sb->s_umount);
> 	else
> 		down_read(&sb->s_umount);
> 	if (thawed && sb->s_writers.frozen != SB_UNFROZEN)
> 		ret = -EBUSY;
> 	else
> 		ret = do_quotactl(sb, type, cmds, id, addr, &path);
> 	if (excl)
> 		up_write(&sb->s_umount);
> 	else
> 		up_read(&sb->s_umount);
> 
> as there is no good reason to bring over the somewhat strange wait until
> unfrozen semantics to a new syscall.

Well, I don't think that "wait until unfrozen" is that strange e.g. for
Q_SETQUOTA - it behaves like setxattr() or any other filesystem
modification operation. And IMO it is desirable that filesystem freezing is
transparent for operations like these. For stuff like Q_QUOTAON, I agree
that returning EBUSY makes sense but then I'm not convinced it's really
simpler or more useful behavior...

								Honza
Christoph Hellwig Jan. 26, 2021, 1:34 p.m. UTC | #7
On Tue, Jan 26, 2021 at 02:17:52PM +0100, Jan Kara wrote:
> Well, I don't think that "wait until unfrozen" is that strange e.g. for
> Q_SETQUOTA - it behaves like setxattr() or any other filesystem
> modification operation. And IMO it is desirable that filesystem freezing is
> transparent for operations like these. For stuff like Q_QUOTAON, I agree
> that returning EBUSY makes sense but then I'm not convinced it's really
> simpler or more useful behavior...

If we want it to behave like other syscalls we'll just need to throw in
a mnt_want_write/mnt_drop_write pair.  Than it behaves exactly like other
syscalls.
Jan Kara Jan. 26, 2021, 4:18 p.m. UTC | #8
On Tue 26-01-21 13:34:06, Christoph Hellwig wrote:
> On Tue, Jan 26, 2021 at 02:17:52PM +0100, Jan Kara wrote:
> > Well, I don't think that "wait until unfrozen" is that strange e.g. for
> > Q_SETQUOTA - it behaves like setxattr() or any other filesystem
> > modification operation. And IMO it is desirable that filesystem freezing is
> > transparent for operations like these. For stuff like Q_QUOTAON, I agree
> > that returning EBUSY makes sense but then I'm not convinced it's really
> > simpler or more useful behavior...
> 
> If we want it to behave like other syscalls we'll just need to throw in
> a mnt_want_write/mnt_drop_write pair.  Than it behaves exactly like other
> syscalls.

Right, we could do that. I'd just note that the "wait until unfrozen" and
holding of sb->s_umount semaphore is equivalent to
mnt_want_write/mnt_drop_write pair. But I agree
mnt_want_write/mnt_drop_write is easier to understand and there's no reason
not to use it. So I'm for that simplification in the new syscall.

								Honza
Sascha Hauer Jan. 27, 2021, 2:05 p.m. UTC | #9
On Tue, Jan 26, 2021 at 05:18:29PM +0100, Jan Kara wrote:
> On Tue 26-01-21 13:34:06, Christoph Hellwig wrote:
> > On Tue, Jan 26, 2021 at 02:17:52PM +0100, Jan Kara wrote:
> > > Well, I don't think that "wait until unfrozen" is that strange e.g. for
> > > Q_SETQUOTA - it behaves like setxattr() or any other filesystem
> > > modification operation. And IMO it is desirable that filesystem freezing is
> > > transparent for operations like these. For stuff like Q_QUOTAON, I agree
> > > that returning EBUSY makes sense but then I'm not convinced it's really
> > > simpler or more useful behavior...
> > 
> > If we want it to behave like other syscalls we'll just need to throw in
> > a mnt_want_write/mnt_drop_write pair.  Than it behaves exactly like other
> > syscalls.
> 
> Right, we could do that. I'd just note that the "wait until unfrozen" and
> holding of sb->s_umount semaphore is equivalent to
> mnt_want_write/mnt_drop_write pair. But I agree
> mnt_want_write/mnt_drop_write is easier to understand and there's no reason
> not to use it. So I'm for that simplification in the new syscall.

Due to the user_path_at() to the mountpoint the fs won't go away, so I
guess for non-exclusive, non-write quota command I don't need any
additional locking. For non-exclusive, write commands I'll need a
mnt_want_write/mnt_drop_write pair. What about the exclusive, write
commands (Q_QUOTAON/Q_QUOTAOFF)?

Sascha
Jan Kara Jan. 27, 2021, 2:19 p.m. UTC | #10
On Wed 27-01-21 15:05:10, Sascha Hauer wrote:
> On Tue, Jan 26, 2021 at 05:18:29PM +0100, Jan Kara wrote:
> > On Tue 26-01-21 13:34:06, Christoph Hellwig wrote:
> > > On Tue, Jan 26, 2021 at 02:17:52PM +0100, Jan Kara wrote:
> > > > Well, I don't think that "wait until unfrozen" is that strange e.g. for
> > > > Q_SETQUOTA - it behaves like setxattr() or any other filesystem
> > > > modification operation. And IMO it is desirable that filesystem freezing is
> > > > transparent for operations like these. For stuff like Q_QUOTAON, I agree
> > > > that returning EBUSY makes sense but then I'm not convinced it's really
> > > > simpler or more useful behavior...
> > > 
> > > If we want it to behave like other syscalls we'll just need to throw in
> > > a mnt_want_write/mnt_drop_write pair.  Than it behaves exactly like other
> > > syscalls.
> > 
> > Right, we could do that. I'd just note that the "wait until unfrozen" and
> > holding of sb->s_umount semaphore is equivalent to
> > mnt_want_write/mnt_drop_write pair. But I agree
> > mnt_want_write/mnt_drop_write is easier to understand and there's no reason
> > not to use it. So I'm for that simplification in the new syscall.
> 
> Due to the user_path_at() to the mountpoint the fs won't go away, so I
> guess for non-exclusive, non-write quota command I don't need any
> additional locking.

They still need s_umount in read mode - to serialize against quotaon /
quotaoff (happening either through quotactl or filesystem remount).

> For non-exclusive, write commands I'll need a
> mnt_want_write/mnt_drop_write pair. What about the exclusive, write
> commands (Q_QUOTAON/Q_QUOTAOFF)?

And these still need s_umount in write mode for similar reasons. So the
only thing you really save is the "wait-for-unfreeze" code.

								Honza
Jan Kara Jan. 27, 2021, 2:46 p.m. UTC | #11
On Tue 26-01-21 11:45:57, Sascha Hauer wrote:
> On Mon, Jan 25, 2021 at 04:45:07PM +0100, Jan Kara wrote:
> > On Mon 25-01-21 09:38:54, Sascha Hauer wrote:
> > > On Fri, Jan 22, 2021 at 05:16:58PM +0000, Christoph Hellwig wrote:
> > > > On Fri, Jan 22, 2021 at 04:15:29PM +0100, Sascha Hauer wrote:
> > > > > This patch introduces the Q_PATH flag to the quotactl cmd argument.
> > > > > When given, the path given in the special argument to quotactl will
> > > > > be the mount path where the filesystem is mounted, instead of a path
> > > > > to the block device.
> > > > > This is necessary for filesystems which do not have a block device as
> > > > > backing store. Particularly this is done for upcoming UBIFS support.
> > > > > 
> > > > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > > 
> > > > I hate overloading quotactl even more.  Why not add a new quotactl_path
> > > > syscall instead?
> > > 
> > > We can probably do that. Honza, what do you think?
> > 
> > Hum, yes, probably it would be cleaner to add a new syscall for this so
> > that we don't overload quotactl(2). I just didn't think of this.
> 
> How should the semantics of that new syscall look like?
> 
> The easiest and most obvious way would be to do it like the quotactl(2)
> and just replace the special argument with a path:
> 
> int quotactl_path(int cmd, const char *path, int id, caddr_t addr);

Yes, that's what I meant.

> If we try adding a new syscall then we could completely redefine the API
> and avoid the shortcomings of the original quotactl(2) if there are any.
> Can you foresee the discussions we end up in? I am afraid I am opening a
> can of worms here.
> OTOH there might be value in keeping the new syscall compatible to the
> existing one, but I don't know how much this argument counts.

That's a good question but also a can of worms as you write :). One obvious
problem with quotactl() is that's it's ioctl-like interface. So we have
several different operations mixed into a single syscall. Currently there
are these operations:

#define Q_SYNC     0x800001     /* sync disk copy of a filesystems quotas */
#define Q_QUOTAON  0x800002     /* turn quotas on */
#define Q_QUOTAOFF 0x800003     /* turn quotas off */
#define Q_GETFMT   0x800004     /* get quota format used on given filesystem */
#define Q_GETINFO  0x800005     /* get information about quota files */
#define Q_SETINFO  0x800006     /* set information about quota files */
#define Q_GETQUOTA 0x800007     /* get user quota structure */
#define Q_SETQUOTA 0x800008     /* set user quota structure */
#define Q_GETNEXTQUOTA 0x800009 /* get disk limits and usage >= ID */
<plus their XFS variants>

In a puristic world they'd be 9 different syscalls ... or somewhat less
because Q_GETNEXTQUOTA is a superset of Q_GETQUOTA, we could drop Q_SYNC
and Q_GETFMT because they have dubious value these days so we'd be left
with 6. I don't have a strong opinion whether 6 syscalls are worth the
cleanliness or whether we should go with just one new quotactl_path()
syscall. I've CCed linux-api in case other people have opinion.

Anyway, even if we go with single quotactl_path() syscall we should remove
the duplication between VFS and XFS quotactls when we are creating a new
syscall. Thoughts?

								Honza
Christoph Hellwig Jan. 27, 2021, 4:25 p.m. UTC | #12
On Wed, Jan 27, 2021 at 03:46:46PM +0100, Jan Kara wrote:
> In a puristic world they'd be 9 different syscalls ... or somewhat less
> because Q_GETNEXTQUOTA is a superset of Q_GETQUOTA, we could drop Q_SYNC
> and Q_GETFMT because they have dubious value these days so we'd be left
> with 6. I don't have a strong opinion whether 6 syscalls are worth the
> cleanliness or whether we should go with just one new quotactl_path()
> syscall. I've CCed linux-api in case other people have opinion.
> 
> Anyway, even if we go with single quotactl_path() syscall we should remove
> the duplication between VFS and XFS quotactls when we are creating a new
> syscall. Thoughts?

I thunk the multiplexer is just fine.  We don't really need Q_SYNC
for a new syscall.  For XFS vs classic VFS quota we probably don't
need to duplicate the two data structure, but we need to make sure we
catch the superset of the information if we want to disable the old
ones.

So I suspect just supporting evrything excpt for the global Q_SYNC
and reusing do_quotactl as-is is the most maintainable and easiest
to understand way forward.
diff mbox series

Patch

diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 6d16b2be5ac4..f653b27a9a4e 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -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"
@@ -859,25 +860,10 @@  static bool quotactl_cmd_onoff(int cmd)
 		 (cmd == Q_XQUOTAON) || (cmd == Q_XQUOTAOFF);
 }
 
-/*
- * 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_sb(dev_t dev, int cmd)
 {
-#ifdef CONFIG_BLOCK
 	struct super_block *sb;
-	struct filename *tmp = getname(special);
 	bool excl = false, thawed = false;
-	int error;
-	dev_t dev;
-
-	if (IS_ERR(tmp))
-		return ERR_CAST(tmp);
-	error = lookup_bdev(tmp->name, &dev);
-	putname(tmp);
-	if (error)
-		return ERR_PTR(error);
 
 	if (quotactl_cmd_onoff(cmd)) {
 		excl = true;
@@ -901,12 +887,50 @@  static struct super_block *quotactl_block(const char __user *special, int cmd)
 		goto retry;
 	}
 	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)
+{
+#ifdef CONFIG_BLOCK
+	struct filename *tmp = getname(special);
+	int error;
+	dev_t dev;
 
+	if (IS_ERR(tmp))
+		return ERR_CAST(tmp);
+	error = lookup_bdev(tmp->name, &dev);
+	putname(tmp);
+	if (error)
+		return ERR_PTR(error);
+
+	return quotactl_sb(dev, cmd);
 #else
 	return ERR_PTR(-ENODEV);
 #endif
 }
 
+static struct super_block *quotactl_path(const char __user *special, int cmd)
+{
+	struct super_block *sb;
+	struct path path;
+	int error;
+
+	error = user_path_at(AT_FDCWD, special, LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT,
+			   &path);
+	if (error)
+		return ERR_PTR(error);
+
+	sb = quotactl_sb(path.mnt->mnt_sb->s_dev, cmd);
+
+	path_put(&path);
+
+	return sb;
+}
+
 /*
  * This is the system call interface. This communicates with
  * the user-level programs. Currently this only supports diskquota
@@ -920,6 +944,7 @@  SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
 	struct super_block *sb = NULL;
 	struct path path, *pathp = NULL;
 	int ret;
+	bool q_path;
 
 	cmds = cmd >> SUBCMDSHIFT;
 	type = cmd & SUBCMDMASK;
@@ -927,6 +952,9 @@  SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
 	if (type >= MAXQUOTAS)
 		return -EINVAL;
 
+	q_path = cmds & Q_PATH;
+	cmds &= ~Q_PATH;
+
 	/*
 	 * As a special case Q_SYNC can be called without a specific device.
 	 * It will iterate all superblocks that have quota enabled and call
@@ -951,7 +979,11 @@  SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
 			pathp = &path;
 	}
 
-	sb = quotactl_block(special, cmds);
+	if (q_path)
+		sb = quotactl_path(special, cmds);
+	else
+		sb = quotactl_block(special, cmds);
+
 	if (IS_ERR(sb)) {
 		ret = PTR_ERR(sb);
 		goto out;
diff --git a/include/uapi/linux/quota.h b/include/uapi/linux/quota.h
index f17c9636a859..e1787c0df601 100644
--- a/include/uapi/linux/quota.h
+++ b/include/uapi/linux/quota.h
@@ -71,6 +71,7 @@ 
 #define Q_GETQUOTA 0x800007	/* get user quota structure */
 #define Q_SETQUOTA 0x800008	/* set user quota structure */
 #define Q_GETNEXTQUOTA 0x800009	/* get disk limits and usage >= ID */
+#define Q_PATH     0x400000	/* quotactl special arg contains mount path */
 
 /* Quota format type IDs */
 #define	QFMT_VFS_OLD 1