Quotactl change

Jan Kara (jack@ucw.cz)
Sat, 6 Oct 2001 15:07:32 +0200


Hello,

I'm sending you a change for quotactl interface which Nathan Scott proposed
for XFS. Actually it's his patch with just a few changes from me.
It allows quotactl() to be overidden by a filesystem and so XFS can do it's
tricks with quota without patching dquot.c. Sideeffect of this change is a
cleanup in quotactl() interface :).
Also statistics about quotas are now exported to /proc which is IMO more
flexible and nicer than GETSTATS call.
I also commented out MAXDQUOTS sysctl because it has no sense anymore.
BTW: IMHO the same can be done with MAXINODES.

Honza

------------------------------------<cut>--------------------------------
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/fs/Makefile linux-2.4.10-ac3-fix+quotactl/fs/Makefile
--- linux-2.4.10-ac3-fix/fs/Makefile Wed Oct 3 23:38:37 2001
+++ linux-2.4.10-ac3-fix+quotactl/fs/Makefile Sat Oct 6 10:46:35 2001
@@ -14,12 +14,10 @@
super.o block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
- filesystems.o jbd-kernel.o namespace.o
+ filesystems.o jbd-kernel.o namespace.o quota.o

ifeq ($(CONFIG_QUOTA),y)
obj-y += dquot.o
-else
-obj-y += noquot.o
endif

subdir-$(CONFIG_PROC_FS) += proc
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/fs/dquot.c linux-2.4.10-ac3-fix+quotactl/fs/dquot.c
--- linux-2.4.10-ac3-fix/fs/dquot.c Thu Oct 4 12:25:37 2001
+++ linux-2.4.10-ac3-fix+quotactl/fs/dquot.c Sat Oct 6 11:57:06 2001
@@ -65,6 +65,7 @@
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
+#include <linux/proc_fs.h>

#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -73,8 +74,6 @@
#define __DQUOT_NUM_VERSION__ (6*10000+5*100+0)
#define __DQUOT_PARANOIA

-int nr_dquots, nr_free_dquots;
-
static const char *quotatypes[] = INITQFNAMES;
static const uint quota_magics[] = INITQMAGICS;
static const uint quota_versions[] = INITQVERSIONS;
@@ -1480,11 +1479,13 @@
static int set_dqblk(struct super_block *sb, qid_t id, short type, int flags, struct mem_dqblk *dqblk)
{
struct dquot *dquot;
- int error = -EFAULT;
struct mem_dqblk dq_dqblk;

+ if (!sb || !sb_has_quota_enabled(sb, type))
+ return -ESRCH;
+
if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct mem_dqblk)))
- return error;
+ return -EFAULT;

if (sb && (dquot = dqget(sb, id, type)) != NODQUOT) {
/* We can't block while changing quota structure... */
@@ -1550,20 +1551,38 @@
return error;
}

-static int get_stats(caddr_t addr)
+#ifdef CONFIG_PROC_FS
+static int read_stats(char *buffer, char **start, off_t offset, int count, int *eof, void *data)
{
- int error = -EFAULT;
- struct dqstats stats;
+ int len;

dqstats.allocated_dquots = nr_dquots;
dqstats.free_dquots = nr_free_dquots;

- /* make a copy, in case we page-fault in user space */
- memcpy(&stats, &dqstats, sizeof(struct dqstats));
- if (!copy_to_user(addr, &stats, sizeof(struct dqstats)))
- error = 0;
- return error;
-}
+ len = sprintf(buffer, "Version %u\n", dqstats.version);
+ len += sprintf(buffer + len, "%u %u %u %u %u %u %u %u\n",
+ dqstats.lookups, dqstats.drops,
+ dqstats.reads, dqstats.writes,
+ dqstats.cache_hits, dqstats.allocated_dquots,
+ dqstats.free_dquots, dqstats.syncs);
+
+ if (offset >= len) {
+ *start = buffer;
+ *eof = 1;
+ return 0;
+ }
+ *start = buffer + offset;
+ if ((len -= offset) > count)
+ return count;
+ *eof = 1;
+
+ return len;
+}
+#define quota_proc_init() \
+ create_proc_read_entry("fs/quota", 0, 0, read_stats, NULL);
+#else
+#define quota_proc_init() do { } while (0)
+#endif

static int get_info(struct super_block *sb, short type, struct mem_dqinfo *pinfo)
{
@@ -1886,6 +1905,7 @@
INIT_LIST_HEAD(dquot_hash + i);
dqstats.version = __DQUOT_NUM_VERSION__;
printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__);
+ quota_proc_init();
return 0;
}
__initcall(dquot_init);
@@ -2043,115 +2063,59 @@
return error;
}

-/*
- * This is the system call interface. This communicates with
- * the user-level programs. Currently this only supports diskquota
- * calls. Maybe we need to add the process quotas etc. in the future,
- * but we probably should use rlimits for that.
- */
-asmlinkage long sys_quotactl(int cmd, const char *special, qid_t id, __kernel_caddr_t addr)
+static int generic_quotactl(struct super_block *sb, int cmd, int type, qid_t id, void *addr)
{
- int cmds = 0, type = 0, flags = 0;
- kdev_t dev;
- struct super_block *sb = NULL;
int ret = -EINVAL;

- lock_kernel();
- cmds = cmd >> SUBCMDSHIFT;
- type = cmd & SUBCMDMASK;
-
-
- if ((uint) type >= MAXQUOTAS || cmds > 0x1100 || cmds < 0x100 || cmds == 0x0300 ||
- cmds == 0x0400 || cmds == 0x0500 || cmds == 0x1000)
- goto out;
+ if ((uint) type >= MAXQUOTAS || cmd > 0x1100 || cmd < 0x100 ||
+ cmd == 0x0300 || cmd == 0x0400 || cmd == 0x0500 || cmd == 0x1000)
+ return ret;

ret = -EPERM;
- switch (cmds) {
+ switch (cmd) {
case Q_SYNC:
- case Q_GETSTATS:
case Q_GETINFO:
break;
case Q_GETQUOTA:
if (((type == USRQUOTA && current->euid != id) ||
(type == GRPQUOTA && !in_egroup_p(id))) &&
!capable(CAP_SYS_ADMIN))
- goto out;
+ return ret;
break;
default:
if (!capable(CAP_SYS_ADMIN))
- goto out;
- }
-
- dev = NODEV;
- if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) {
- mode_t mode;
- struct nameidata nd;
-
- ret = user_path_walk(special, &nd);
- if (ret)
- goto out;
-
- dev = nd.dentry->d_inode->i_rdev;
- mode = nd.dentry->d_inode->i_mode;
- path_release(&nd);
-
- ret = -ENOTBLK;
- if (!S_ISBLK(mode))
- goto out;
- ret = -ENODEV;
- sb = get_super(dev);
- if (!sb)
- goto out;
+ return ret;
}

ret = -EINVAL;
- switch (cmds) {
+ switch (cmd) {
case Q_QUOTAON:
- ret = quota_on(sb, type, (char *) addr);
- goto out;
+ return quota_on(sb, type, (char *) addr);
case Q_QUOTAOFF:
- ret = quota_off(sb, type);
- goto out;
+ return quota_off(sb, type);
case Q_GETQUOTA:
- ret = get_quota(sb, id, type, (struct mem_dqblk *) addr);
- goto out;
+ return get_quota(sb, id, type, (struct mem_dqblk *) addr);
case Q_SETQUOTA:
- flags |= SET_QUOTA;
- break;
+ return set_dqblk(sb, id, type, SET_QUOTA, (struct mem_dqblk *) addr);
case Q_SETUSE:
- flags |= SET_USE;
- break;
+ return set_dqblk(sb, id, type, SET_USE, (struct mem_dqblk *) addr);
case Q_SETQLIM:
- flags |= SET_QLIMIT;
- break;
+ return set_dqblk(sb, id, type, SET_QLIMIT, (struct mem_dqblk *) addr);
case Q_SYNC:
- ret = sync_dquots(dev, type);
- goto out;
- case Q_GETSTATS:
- ret = get_stats(addr);
- goto out;
+ return sync_dquots(sb->s_dev, type);
case Q_GETINFO:
- ret = get_info(sb, type, (struct mem_dqinfo *) addr);
- goto out;
+ return get_info(sb, type, (struct mem_dqinfo *) addr);
case Q_SETFLAGS:
- ret = set_info(ISET_FLAGS, sb, type, (struct mem_dqinfo *)addr);
- goto out;
+ return set_info(ISET_FLAGS, sb, type, (struct mem_dqinfo *)addr);
case Q_SETGRACE:
- ret = set_info(ISET_GRACE, sb, type, (struct mem_dqinfo *)addr);
- goto out;
+ return set_info(ISET_GRACE, sb, type, (struct mem_dqinfo *)addr);
case Q_SETINFO:
- ret = set_info(ISET_ALL, sb, type, (struct mem_dqinfo *)addr);
- goto out;
+ return set_info(ISET_ALL, sb, type, (struct mem_dqinfo *)addr);
default:
- goto out;
+ return ret;
}
-
- ret = -NODEV;
- if (sb && sb_has_quota_enabled(sb, type))
- ret = set_dqblk(sb, id, type, flags, (struct mem_dqblk *) addr);
-out:
- if (sb)
- drop_super(sb);
- unlock_kernel();
- return ret;
}
+
+struct quota_operations generic_quota_ops = {
+ quotactl: generic_quotactl
+};
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/fs/noquot.c linux-2.4.10-ac3-fix+quotactl/fs/noquot.c
--- linux-2.4.10-ac3-fix/fs/noquot.c Wed Oct 3 23:38:40 2001
+++ linux-2.4.10-ac3-fix+quotactl/fs/noquot.c Thu Jan 1 01:00:00 1970
@@ -1,20 +0,0 @@
-/* noquot.c: Quota stubs necessary for when quotas are not
- * compiled into the kernel.
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-
-int nr_dquots, nr_free_dquots;
-int max_dquots;
-
-asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
-{
- return(-ENOSYS);
-}
-
-void shrink_dqcache_memory(int priority, unsigned int gfp_mask)
-{
- ;
-}
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/fs/quota.c linux-2.4.10-ac3-fix+quotactl/fs/quota.c
--- linux-2.4.10-ac3-fix/fs/quota.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.10-ac3-fix+quotactl/fs/quota.c Sat Oct 6 11:57:27 2001
@@ -0,0 +1,68 @@
+/*
+ * Quota code necessary even when VFS quota support is not compiled
+ * into the kernel. Allows filesystems to implement their own form
+ * of quota if they wish. The interesting stuff is over in dquot.c,
+ * living here are symbols for initial quotactl(2) handling, sysctl
+ * variables, etc - things needed even when quota support disabled.
+ */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+
+int nr_dquots, nr_free_dquots;
+
+#ifndef CONFIG_QUOTA
+void shrink_dqcache_memory(int priority, unsigned int mask)
+{
+ return;
+}
+#endif
+
+asmlinkage long sys_quotactl(int cmd, const char *special, qid_t id, __kernel_caddr_t addr)
+{
+ int ret, cmds, type;
+ kdev_t dev;
+ mode_t mode;
+ struct nameidata nd;
+ struct super_block *sb = NULL;
+
+ lock_kernel();
+ cmds = cmd >> SUBCMDSHIFT;
+ type = cmd & SUBCMDMASK;
+
+ ret = -ENOSYS;
+ if (cmds == 0x0800 || cmds == 0x1100) /* both were Q_GETSTATS */
+ goto out;
+
+ ret = -ENODEV;
+ if (!special)
+ goto out;
+ ret = user_path_walk(special, &nd);
+ if (ret)
+ goto out;
+
+ dev = nd.dentry->d_inode->i_rdev;
+ mode = nd.dentry->d_inode->i_mode;
+ path_release(&nd);
+
+ ret = -ENOTBLK;
+ if (!S_ISBLK(mode))
+ goto out;
+ ret = -ENODEV;
+ sb = get_super(dev);
+ if (!sb)
+ goto out;
+
+ ret = -ENOSYS;
+ if (!sb->s_qop || !sb->s_qop->quotactl)
+ goto out;
+
+ ret = sb->s_qop->quotactl(sb, cmds, type, id, addr);
+
+out:
+ if (sb)
+ drop_super(sb);
+ unlock_kernel();
+ return ret;
+}
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/fs/super.c linux-2.4.10-ac3-fix+quotactl/fs/super.c
--- linux-2.4.10-ac3-fix/fs/super.c Wed Oct 3 23:38:42 2001
+++ linux-2.4.10-ac3-fix+quotactl/fs/super.c Sat Oct 6 10:46:35 2001
@@ -466,6 +466,7 @@
s->s_bdev = bdev;
s->s_flags = flags;
s->s_type = type;
+ s->s_qop = sb_generic_quota_ops;
spin_lock(&sb_lock);
list_add (&s->s_list, super_blocks.prev);
list_add (&s->s_instances, &type->fs_supers);
@@ -486,6 +487,7 @@
s->s_dev = 0;
s->s_bdev = 0;
s->s_type = NULL;
+ s->s_qop = NULL;
unlock_super(s);
spin_lock(&sb_lock);
list_del(&s->s_list);
@@ -617,6 +619,7 @@
s->s_bdev = bdev;
s->s_flags = flags;
s->s_type = fs_type;
+ s->s_qop = sb_generic_quota_ops;
list_add (&s->s_list, super_blocks.prev);
list_add (&s->s_instances, &fs_type->fs_supers);
s->s_count += S_BIAS;
@@ -636,6 +639,7 @@
s->s_dev = 0;
s->s_bdev = 0;
s->s_type = NULL;
+ s->s_qop = NULL;
unlock_super(s);
spin_lock(&sb_lock);
list_del(&s->s_list);
@@ -700,6 +704,7 @@
s->s_dev = dev;
s->s_flags = flags;
s->s_type = fs_type;
+ s->s_qop = sb_generic_quota_ops;
list_add (&s->s_list, super_blocks.prev);
list_add (&s->s_instances, &fs_type->fs_supers);
s->s_count += S_BIAS;
@@ -715,6 +720,7 @@
s->s_dev = 0;
s->s_bdev = 0;
s->s_type = NULL;
+ s->s_qop = NULL;
unlock_super(s);
spin_lock(&sb_lock);
list_del(&s->s_list);
@@ -770,6 +776,7 @@
sb->s_bdev = NULL;
put_filesystem(fs);
sb->s_type = NULL;
+ sb->s_qop = NULL;
unlock_super(sb);
unlock_kernel();
if (bdev)
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/include/linux/fs.h linux-2.4.10-ac3-fix+quotactl/include/linux/fs.h
--- linux-2.4.10-ac3-fix/include/linux/fs.h Sat Oct 6 11:03:30 2001
+++ linux-2.4.10-ac3-fix+quotactl/include/linux/fs.h Sat Oct 6 12:03:54 2001
@@ -656,6 +656,10 @@
struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */
};

+struct quota_operations {
+ int (*quotactl)(struct super_block *, int, int, qid_t, void *);
+};
+
/*
* Umount options
*/
@@ -717,6 +721,7 @@
struct block_device *s_bdev;
struct list_head s_instances;
struct quota_info s_dquot; /* Diskquota specific options */
+ struct quota_operations *s_qop;

union {
struct minix_sb_info minix_sb;
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/include/linux/quota.h linux-2.4.10-ac3-fix+quotactl/include/linux/quota.h
--- linux-2.4.10-ac3-fix/include/linux/quota.h Wed Oct 3 23:38:47 2001
+++ linux-2.4.10-ac3-fix+quotactl/include/linux/quota.h Sat Oct 6 11:56:35 2001
@@ -107,7 +107,7 @@
#define Q_SETQUOTA 0x0E00 /* set limits and usage */
#define Q_SETUSE 0x0F00 /* set usage */
/* 0x1000 used by old RSQUASH */
-#define Q_GETSTATS 0x1100 /* get collected stats */
+/* 0x1100 used by GETSTATS v2, since replaced by procfs entry */

/*
* The following structure defines the format of the disk quota file
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/include/linux/quotaops.h linux-2.4.10-ac3-fix+quotactl/include/linux/quotaops.h
--- linux-2.4.10-ac3-fix/include/linux/quotaops.h Sat Oct 6 11:09:14 2001
+++ linux-2.4.10-ac3-fix+quotactl/include/linux/quotaops.h Sat Oct 6 12:17:15 2001
@@ -17,6 +17,9 @@

#include <linux/fs.h>

+extern struct quota_operations generic_quota_ops;
+#define sb_generic_quota_ops (&generic_quota_ops)
+
/*
* declaration of quota_function calls in kernel.
*/
@@ -166,6 +169,7 @@
/*
* NO-OP when quota not configured.
*/
+#define sb_generic_quota_ops (NULL)
#define DQUOT_INIT(inode) do { } while(0)
#define DQUOT_DROP(inode) do { } while(0)
#define DQUOT_ALLOC_INODE(inode) (0)
diff -ruN -X /home/jack/.kerndiffexclude linux-2.4.10-ac3-fix/include/linux/sysctl.h linux-2.4.10-ac3-fix+quotactl/include/linux/sysctl.h
--- linux-2.4.10-ac3-fix/include/linux/sysctl.h Sat Oct 6 11:04:28 2001
+++ linux-2.4.10-ac3-fix+quotactl/include/linux/sysctl.h Sat Oct 6 12:04:13 2001
@@ -535,7 +535,7 @@
FS_STATINODE=2,
FS_MAXINODE=3, /* int:maximum number of inodes that can be allocated */
FS_NRDQUOT=4, /* int:current number of allocated dquots */
- FS_MAXDQUOT=5, /* int:maximum number of dquots that can be allocated */
+ /* was FS_MAXDQUOT */
FS_NRFILE=6, /* int:current number of allocated filedescriptors */
FS_MAXFILE=7, /* int:maximum number of filedescriptors that can be allocated */
FS_DENTRY=8,
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/