patch-2.4.22 linux-2.4.22/fs/quota.c

Next file: linux-2.4.22/fs/quota_v1.c
Previous file: linux-2.4.22/fs/proc/proc_misc.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.21/fs/quota.c linux-2.4.22/fs/quota.c
@@ -0,0 +1,489 @@
+/*
+ * Quota code necessary even when VFS quota support is not compiled
+ * into the kernel.  The interesting stuff is over in dquot.c, here
+ * we have symbols for initial quotactl(2) handling, the sysctl(2)
+ * variables, etc - things needed even when quota support disabled.
+ */
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <asm/current.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/quotaops.h>
+#include <linux/quotacompat.h>
+
+struct dqstats dqstats;
+
+/* Check validity of quotactl */
+static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+	if (type >= MAXQUOTAS)
+		return -EINVAL;
+	if (!sb && cmd != Q_SYNC)
+		return -ENODEV;
+	/* Is operation supported? */
+	if (sb && !sb->s_qcop)
+		return -ENOSYS;
+
+	switch (cmd) {
+		case Q_GETFMT:
+			break;
+		case Q_QUOTAON:
+			if (!sb->s_qcop->quota_on)
+				return -ENOSYS;
+			break;
+		case Q_QUOTAOFF:
+			if (!sb->s_qcop->quota_off)
+				return -ENOSYS;
+			break;
+		case Q_SETINFO:
+			if (!sb->s_qcop->set_info)
+				return -ENOSYS;
+			break;
+		case Q_GETINFO:
+			if (!sb->s_qcop->get_info)
+				return -ENOSYS;
+			break;
+		case Q_SETQUOTA:
+			if (!sb->s_qcop->set_dqblk)
+				return -ENOSYS;
+			break;
+		case Q_GETQUOTA:
+			if (!sb->s_qcop->get_dqblk)
+				return -ENOSYS;
+			break;
+		case Q_SYNC:
+			if (sb && !sb->s_qcop->quota_sync)
+				return -ENOSYS;
+			break;
+		case Q_XQUOTAON:
+		case Q_XQUOTAOFF:
+		case Q_XQUOTARM:
+			if (!sb->s_qcop->set_xstate)
+				return -ENOSYS;
+			break;
+		case Q_XGETQSTAT:
+			if (!sb->s_qcop->get_xstate)
+				return -ENOSYS;
+			break;
+		case Q_XSETQLIM:
+			if (!sb->s_qcop->set_xquota)
+				return -ENOSYS;
+			break;
+		case Q_XGETQUOTA:
+			if (!sb->s_qcop->get_xquota)
+				return -ENOSYS;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	/* Is quota turned on for commands which need it? */
+	switch (cmd) {
+		case Q_GETFMT:
+		case Q_GETINFO:
+		case Q_QUOTAOFF:
+		case Q_SETINFO:
+		case Q_SETQUOTA:
+		case Q_GETQUOTA:
+			if (!sb_has_quota_enabled(sb, type))
+				return -ESRCH;
+	}
+	/* Check privileges */
+	if (cmd == Q_GETQUOTA || cmd == Q_XGETQUOTA) {
+		if (((type == USRQUOTA && current->euid != id) ||
+		     (type == GRPQUOTA && !in_egroup_p(id))) &&
+		    !capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	}
+	else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO && cmd != Q_XGETQSTAT)
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	return 0;
+}
+
+/* Resolve device pathname to superblock */
+static struct super_block *resolve_dev(const char *path)
+{
+	int ret;
+	mode_t mode;
+	struct nameidata nd;
+	kdev_t dev;
+	struct super_block *sb;
+
+	ret = user_path_walk(path, &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 sb;
+out:
+	return ERR_PTR(ret);
+}
+
+/* Copy parameters and call proper function */
+static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
+{
+	int ret;
+
+	switch (cmd) {
+		case Q_QUOTAON: {
+			char *pathname;
+
+			if (IS_ERR(pathname = getname(addr)))
+				return PTR_ERR(pathname);
+			ret = sb->s_qcop->quota_on(sb, type, id, pathname);
+			putname(pathname);
+			return ret;
+		}
+		case Q_QUOTAOFF:
+			return sb->s_qcop->quota_off(sb, type);
+
+		case Q_GETFMT: {
+			__u32 fmt;
+
+			fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
+			if (copy_to_user(addr, &fmt, sizeof(fmt)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_GETINFO: {
+			struct if_dqinfo info;
+
+			if ((ret = sb->s_qcop->get_info(sb, type, &info)))
+				return ret;
+			if (copy_to_user(addr, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_SETINFO: {
+			struct if_dqinfo info;
+
+			if (copy_from_user(&info, addr, sizeof(info)))
+				return -EFAULT;
+			return sb->s_qcop->set_info(sb, type, &info);
+		}
+		case Q_GETQUOTA: {
+			struct if_dqblk idq;
+
+			if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)))
+				return ret;
+			if (copy_to_user(addr, &idq, sizeof(idq)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_SETQUOTA: {
+			struct if_dqblk idq;
+
+			if (copy_from_user(&idq, addr, sizeof(idq)))
+				return -EFAULT;
+			return sb->s_qcop->set_dqblk(sb, type, id, &idq);
+		}
+		case Q_SYNC:
+			if (sb)
+				return sb->s_qcop->quota_sync(sb, type);
+			sync_dquots_dev(NODEV, type);
+			return 0;
+		case Q_XQUOTAON:
+		case Q_XQUOTAOFF:
+		case Q_XQUOTARM: {
+			__u32 flags;
+
+			if (copy_from_user(&flags, addr, sizeof(flags)))
+				return -EFAULT;
+			return sb->s_qcop->set_xstate(sb, flags, cmd);
+		}
+		case Q_XGETQSTAT: {
+			struct fs_quota_stat fqs;
+		
+			if ((ret = sb->s_qcop->get_xstate(sb, &fqs)))
+				return ret;
+			if (copy_to_user(addr, &fqs, sizeof(fqs)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_XSETQLIM: {
+			struct fs_disk_quota fdq;
+
+			if (copy_from_user(&fdq, addr, sizeof(fdq)))
+				return -EFAULT;
+		       return sb->s_qcop->set_xquota(sb, type, id, &fdq);
+		}
+		case Q_XGETQUOTA: {
+			struct fs_disk_quota fdq;
+
+			if ((ret = sb->s_qcop->get_xquota(sb, type, id, &fdq)))
+				return ret;
+			if (copy_to_user(addr, &fdq, sizeof(fdq)))
+				return -EFAULT;
+			return 0;
+		}
+		/* We never reach here unless validity check is broken */
+		default:
+			BUG();
+	}
+	return 0;
+}
+
+static int check_compat_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+	if (type >= MAXQUOTAS)
+		return -EINVAL;
+	/* Is operation supported? */
+	/* sb==NULL for GETSTATS calls */
+	if (sb && !sb->s_qcop)
+		return -ENOSYS;
+
+	switch (cmd) {
+		case Q_COMP_QUOTAON:
+			if (!sb->s_qcop->quota_on)
+				return -ENOSYS;
+			break;
+		case Q_COMP_QUOTAOFF:
+			if (!sb->s_qcop->quota_off)
+				return -ENOSYS;
+			break;
+		case Q_COMP_SYNC:
+			if (sb && !sb->s_qcop->quota_sync)
+				return -ENOSYS;
+			break;
+		case Q_V1_SETQLIM:
+		case Q_V1_SETUSE:
+		case Q_V1_SETQUOTA:
+			if (!sb->s_qcop->set_dqblk)
+				return -ENOSYS;
+			break;
+		case Q_V1_GETQUOTA:
+			if (!sb->s_qcop->get_dqblk)
+				return -ENOSYS;
+			break;
+		case Q_V1_RSQUASH:
+			if (!sb->s_qcop->set_info)
+				return -ENOSYS;
+			break;
+		case Q_V1_GETSTATS:
+			return 0;	/* GETSTATS need no other checks */
+		default:
+			return -EINVAL;
+	}
+
+	/* Is quota turned on for commands which need it? */
+	switch (cmd) {
+		case Q_V2_SETFLAGS:
+		case Q_V2_SETGRACE:
+		case Q_V2_SETINFO:
+		case Q_V2_GETINFO:
+		case Q_COMP_QUOTAOFF:
+		case Q_V1_RSQUASH:
+		case Q_V1_SETQUOTA:
+		case Q_V1_SETQLIM:
+		case Q_V1_SETUSE:
+		case Q_V2_SETQUOTA:
+		/* Q_V2_SETQLIM: collision with Q_V1_SETQLIM */
+		case Q_V2_SETUSE:
+		case Q_V1_GETQUOTA:
+		case Q_V2_GETQUOTA:
+			if (!sb_has_quota_enabled(sb, type))
+				return -ESRCH;
+	}
+	if (cmd != Q_COMP_QUOTAON &&
+	    cmd != Q_COMP_QUOTAOFF &&
+	    cmd != Q_COMP_SYNC &&
+	    sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_OLD)
+		return -ESRCH;
+
+	/* Check privileges */
+	if (cmd == Q_V1_GETQUOTA || cmd == Q_V2_GETQUOTA) {
+		if (((type == USRQUOTA && current->euid != id) ||
+		     (type == GRPQUOTA && !in_egroup_p(id))) &&
+		    !capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	}
+	else if (cmd != Q_V1_GETSTATS && cmd != Q_V2_GETSTATS && cmd != Q_V2_GETINFO && cmd != Q_COMP_SYNC)
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	return 0;
+}
+
+static int v1_set_rsquash(struct super_block *sb, int type, int flag)
+{
+	struct if_dqinfo info;
+
+	info.dqi_valid = IIF_FLAGS;
+	info.dqi_flags = flag ? V1_DQF_RSQUASH : 0;
+	return sb->s_qcop->set_info(sb, type, &info);
+}
+
+static int v1_get_dqblk(struct super_block *sb, int type, qid_t id, struct v1c_mem_dqblk *mdq)
+{
+	struct if_dqblk idq;
+	int ret;
+
+	if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)) < 0)
+		return ret;
+	mdq->dqb_ihardlimit = idq.dqb_ihardlimit;
+	mdq->dqb_isoftlimit = idq.dqb_isoftlimit;
+	mdq->dqb_curinodes = idq.dqb_curinodes;
+	mdq->dqb_bhardlimit = idq.dqb_bhardlimit;
+	mdq->dqb_bsoftlimit = idq.dqb_bsoftlimit;
+	mdq->dqb_curblocks = toqb(idq.dqb_curspace);
+	mdq->dqb_itime = idq.dqb_itime;
+	mdq->dqb_btime = idq.dqb_btime;
+	if (id == 0) {	/* Times for id 0 are in fact grace times */
+		struct if_dqinfo info;
+
+		if ((ret = sb->s_qcop->get_info(sb, type, &info)) < 0)
+			return ret;
+		mdq->dqb_btime = info.dqi_bgrace;
+		mdq->dqb_itime = info.dqi_igrace;
+	}
+	return 0;
+}
+
+static int v1_set_dqblk(struct super_block *sb, int type, int cmd, qid_t id, struct v1c_mem_dqblk *mdq)
+{
+	struct if_dqblk idq;
+	int ret;
+
+	idq.dqb_valid = 0;
+	if (cmd == Q_V1_SETQUOTA || cmd == Q_V1_SETQLIM) {
+		idq.dqb_ihardlimit = mdq->dqb_ihardlimit;
+		idq.dqb_isoftlimit = mdq->dqb_isoftlimit;
+		idq.dqb_bhardlimit = mdq->dqb_bhardlimit;
+		idq.dqb_bsoftlimit = mdq->dqb_bsoftlimit;
+		idq.dqb_valid |= QIF_LIMITS;
+	}
+	if (cmd == Q_V1_SETQUOTA || cmd == Q_V1_SETUSE) {
+		idq.dqb_curinodes = mdq->dqb_curinodes;
+		idq.dqb_curspace = ((qsize_t)mdq->dqb_curblocks) << QUOTABLOCK_BITS;
+		idq.dqb_valid |= QIF_USAGE;
+	}
+	ret = sb->s_qcop->set_dqblk(sb, type, id, &idq);
+	if (!ret && id == 0 && cmd == Q_V1_SETQUOTA) {	/* Times for id 0 are in fact grace times */
+		struct if_dqinfo info;
+
+		info.dqi_bgrace = mdq->dqb_btime;
+		info.dqi_igrace = mdq->dqb_itime;
+		info.dqi_valid = IIF_BGRACE | IIF_IGRACE;
+		ret = sb->s_qcop->set_info(sb, type, &info);
+	}
+	return ret;
+}
+
+static void v1_get_stats(struct v1c_dqstats *dst)
+{
+	memcpy(dst, &dqstats, sizeof(dqstats));
+}
+
+/* Handle requests to old interface */
+static int do_compat_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
+{
+	int ret;
+
+	switch (cmd) {
+		case Q_COMP_QUOTAON: {
+			char *pathname;
+
+			if (IS_ERR(pathname = getname(addr)))
+				return PTR_ERR(pathname);
+			ret = sb->s_qcop->quota_on(sb, type, QFMT_VFS_OLD, pathname);
+			putname(pathname);
+			return ret;
+		}
+		case Q_COMP_QUOTAOFF:
+			return sb->s_qcop->quota_off(sb, type);
+		case Q_COMP_SYNC:
+			if (sb)
+				return sb->s_qcop->quota_sync(sb, type);
+			sync_dquots_dev(NODEV, type);
+			return 0;
+		case Q_V1_RSQUASH: {
+			int flag;
+
+			if (copy_from_user(&flag, addr, sizeof(flag)))
+				return -EFAULT;
+			return v1_set_rsquash(sb, type, flag);
+		}
+		case Q_V1_GETQUOTA: {
+			struct v1c_mem_dqblk mdq;
+
+			if ((ret = v1_get_dqblk(sb, type, id, &mdq)))
+				return ret;
+			if (copy_to_user(addr, &mdq, sizeof(mdq)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_V1_SETQLIM:
+		case Q_V1_SETUSE:
+		case Q_V1_SETQUOTA: {
+			struct v1c_mem_dqblk mdq;
+
+			if (copy_from_user(&mdq, addr, sizeof(mdq)))
+				return -EFAULT;
+			return v1_set_dqblk(sb, type, cmd, id, &mdq);
+		}
+		case Q_V1_GETSTATS: {
+			struct v1c_dqstats dst;
+
+			v1_get_stats(&dst);
+			if (copy_to_user(addr, &dst, sizeof(dst)))
+				return -EFAULT;
+			return 0;
+		}
+	}
+	BUG();
+	return 0;
+}
+
+/* Macros for short-circuiting the compatibility tests */
+#define NEW_COMMAND(c) ((c) & (0x80 << 16))
+#define XQM_COMMAND(c) (((c) & ('X' << 8)) == ('X' << 8))
+
+/*
+ * 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(unsigned int cmd, const char *special, qid_t id, caddr_t addr)
+{
+	uint cmds, type;
+	struct super_block *sb = NULL;
+	int ret = -EINVAL;
+
+	lock_kernel();
+	cmds = cmd >> SUBCMDSHIFT;
+	type = cmd & SUBCMDMASK;
+
+	if (cmds != Q_V1_GETSTATS && cmds != Q_V2_GETSTATS && IS_ERR(sb = resolve_dev(special))) {
+		ret = PTR_ERR(sb);
+		sb = NULL;
+		goto out;
+	}
+	if (!NEW_COMMAND(cmds) && !XQM_COMMAND(cmds)) {
+		if ((ret = check_compat_quotactl_valid(sb, type, cmds, id)) < 0)
+			goto out;
+		ret = do_compat_quotactl(sb, type, cmds, id, addr);
+		goto out;
+	}
+	if ((ret = check_quotactl_valid(sb, type, cmds, id)) < 0)
+		goto out;
+	ret = do_quotactl(sb, type, cmds, id, addr);
+out:
+	if (sb)
+		drop_super(sb);
+	unlock_kernel();
+	return ret;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)