quota: special case Q_SYNC without device name

The Q_SYNC command can be called without the path to a device, in which case
it iterates over all superblocks.  Special case this variant directly in
sys_quotactl so that the other code always gets a superblock and doesn't
need to deal with this case.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jan Kara <jack@suse.cz>
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index dcf7db9..9fde5cd 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -124,10 +124,17 @@
 }
 #endif
 
-static void sync_dquots(int type)
+static int quota_sync_all(int type)
 {
 	struct super_block *sb;
 	int cnt;
+	int ret;
+
+	if (type >= MAXQUOTAS)
+		return -EINVAL;
+	ret = security_quotactl(Q_SYNC, type, 0, NULL);
+	if (ret)
+		return ret;
 
 	spin_lock(&sb_lock);
 restart:
@@ -157,6 +164,8 @@
 			goto restart;
 	}
 	spin_unlock(&sb_lock);
+
+	return 0;
 }
 
 static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id,
@@ -322,12 +331,9 @@
 	case Q_SETQUOTA:
 		return quota_setquota(sb, type, id, addr);
 	case Q_SYNC:
-		if (sb) {
-			if (!sb->s_qcop->quota_sync)
-				return -ENOSYS;
-			sync_quota_sb(sb, type);
-		} else
-			sync_dquots(type);
+		if (!sb->s_qcop->quota_sync)
+			return -ENOSYS;
+		sync_quota_sb(sb, type);
 		return 0;
 	case Q_XQUOTAON:
 	case Q_XQUOTAOFF:
@@ -392,18 +398,26 @@
 	cmds = cmd >> SUBCMDSHIFT;
 	type = cmd & SUBCMDMASK;
 
-	if (cmds != Q_SYNC || special) {
-		sb = quotactl_block(special);
-		if (IS_ERR(sb))
-			return PTR_ERR(sb);
+	/*
+	 * As a special case Q_SYNC can be called without a specific device.
+	 * It will iterate all superblocks that have quota enabled and call
+	 * the sync action on each of them.
+	 */
+	if (!special) {
+		if (cmds == Q_SYNC)
+			return quota_sync_all(type);
+		return -ENODEV;
 	}
 
+	sb = quotactl_block(special);
+	if (IS_ERR(sb))
+		return PTR_ERR(sb);
+
 	ret = check_quotactl_valid(sb, type, cmds, id);
 	if (ret >= 0)
 		ret = do_quotactl(sb, type, cmds, id, addr);
-	if (sb)
-		drop_super(sb);
 
+	drop_super(sb);
 	return ret;
 }