#include <linux/kernel.h>
#include <linux/smp_lock.h>
#include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/buffer_head.h>
+#include <linux/capability.h>
+#include <linux/quotaops.h>
+#include <linux/vs_context.h>
-/* Check validity of quotactl */
-static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+/* Check validity of generic quotactl commands */
+static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
{
if (type >= MAXQUOTAS)
return -EINVAL;
if (sb && !sb->s_qcop->quota_sync)
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:
+ /* This is just informative test so we are satisfied without a lock */
+ if (!sb_has_quota_enabled(sb, type))
+ return -ESRCH;
+ }
+
+ /* Check privileges */
+ if (cmd == Q_GETQUOTA) {
+ if (((type == USRQUOTA && current->euid != id) ||
+ (type == GRPQUOTA && !in_egroup_p(id))) &&
+ !vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL))
+ return -EPERM;
+ }
+ else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO)
+ if (!vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL))
+ return -EPERM;
+
+ return 0;
+}
+
+/* Check validity of XFS Quota Manager commands */
+static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+ if (type >= XQM_MAXQUOTAS)
+ return -EINVAL;
+ if (!sb)
+ return -ENODEV;
+ if (!sb->s_qcop)
+ return -ENOSYS;
+
+ switch (cmd) {
case Q_XQUOTAON:
case Q_XQUOTAOFF:
case Q_XQUOTARM:
if (!sb->s_qcop->get_xquota)
return -ENOSYS;
break;
+ case Q_XQUOTASYNC:
+ if (!sb->s_qcop->quota_sync)
+ 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:
- /* This is just informative test so we are satisfied without a lock */
- 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))
+ if (cmd == Q_XGETQUOTA) {
+ if (((type == XQM_USRQUOTA && current->euid != id) ||
+ (type == XQM_GRPQUOTA && !in_egroup_p(id))) &&
+ !vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL))
return -EPERM;
- }
- else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO && cmd != Q_XGETQSTAT)
- if (!capable(CAP_SYS_ADMIN))
+ } else if (cmd != Q_XGETQSTAT && cmd != Q_XQUOTASYNC) {
+ if (!vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL))
return -EPERM;
+ }
+
+ return 0;
+}
+
+static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+ int error;
- return security_quotactl (cmd, type, id, sb);
+ if (XQM_COMMAND(cmd))
+ error = xqm_quotactl_valid(sb, type, cmd, id);
+ else
+ error = generic_quotactl_valid(sb, type, cmd, id);
+ if (!error)
+ error = security_quotactl(cmd, type, id, sb);
+ return error;
}
-static struct super_block *get_super_to_sync(int type)
+static void quota_sync_sb(struct super_block *sb, int type)
+{
+ int cnt;
+ struct inode *discard[MAXQUOTAS];
+
+ sb->s_qcop->quota_sync(sb, type);
+ /* This is not very clever (and fast) but currently I don't know about
+ * any other simple way of getting quota data to disk and we must get
+ * them there for userspace to be visible... */
+ if (sb->s_op->sync_fs)
+ sb->s_op->sync_fs(sb, 1);
+ sync_blockdev(sb->s_bdev);
+
+ /* Now when everything is written we can discard the pagecache so
+ * that userspace sees the changes. We need i_mutex and so we could
+ * not do it inside dqonoff_mutex. Moreover we need to be carefull
+ * about races with quotaoff() (that is the reason why we have own
+ * reference to inode). */
+ mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ discard[cnt] = NULL;
+ if (type != -1 && cnt != type)
+ continue;
+ if (!sb_has_quota_enabled(sb, cnt))
+ continue;
+ discard[cnt] = igrab(sb_dqopt(sb)->files[cnt]);
+ }
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (discard[cnt]) {
+ mutex_lock(&discard[cnt]->i_mutex);
+ truncate_inode_pages(&discard[cnt]->i_data, 0);
+ mutex_unlock(&discard[cnt]->i_mutex);
+ iput(discard[cnt]);
+ }
+ }
+}
+
+void sync_dquots(struct super_block *sb, int type)
{
- struct list_head *head;
int cnt, dirty;
-restart:
- spin_lock(&sb_lock);
- list_for_each(head, &super_blocks) {
- struct super_block *sb = list_entry(head, struct super_block, s_list);
+ if (sb) {
+ if (sb->s_qcop->quota_sync)
+ quota_sync_sb(sb, type);
+ return;
+ }
+ spin_lock(&sb_lock);
+restart:
+ list_for_each_entry(sb, &super_blocks, s_list) {
/* This test just improves performance so it needn't be reliable... */
for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++)
if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt)
sb->s_count++;
spin_unlock(&sb_lock);
down_read(&sb->s_umount);
- if (!sb->s_root) {
- drop_super(sb);
+ if (sb->s_root && sb->s_qcop->quota_sync)
+ quota_sync_sb(sb, type);
+ up_read(&sb->s_umount);
+ spin_lock(&sb_lock);
+ if (__put_super_and_need_restart(sb))
goto restart;
- }
- return sb;
}
spin_unlock(&sb_lock);
- return NULL;
-}
-
-void sync_dquots(struct super_block *sb, int type)
-{
- if (sb) {
- if (sb->s_qcop->quota_sync)
- sb->s_qcop->quota_sync(sb, type);
- }
- else {
- while ((sb = get_super_to_sync(type)) != 0) {
- if (sb->s_qcop->quota_sync)
- sb->s_qcop->quota_sync(sb, type);
- drop_super(sb);
- }
- }
}
/* Copy parameters and call proper function */
return -EFAULT;
return 0;
}
+ case Q_XQUOTASYNC:
+ return sb->s_qcop->quota_sync(sb, type);
/* We never reach here unless validity check is broken */
default:
BUG();
return 0;
}
+#if defined(CONFIG_BLK_DEV_VROOT) || defined(CONFIG_BLK_DEV_VROOT_MODULE)
+
+#include <linux/vroot.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/vserver/debug.h>
+
+static vroot_grb_func *vroot_get_real_bdev = NULL;
+
+static spinlock_t vroot_grb_lock = SPIN_LOCK_UNLOCKED;
+
+int register_vroot_grb(vroot_grb_func *func) {
+ int ret = -EBUSY;
+
+ spin_lock(&vroot_grb_lock);
+ if (!vroot_get_real_bdev) {
+ vroot_get_real_bdev = func;
+ ret = 0;
+ }
+ spin_unlock(&vroot_grb_lock);
+ return ret;
+}
+EXPORT_SYMBOL(register_vroot_grb);
+
+int unregister_vroot_grb(vroot_grb_func *func) {
+ int ret = -EINVAL;
+
+ spin_lock(&vroot_grb_lock);
+ if (vroot_get_real_bdev) {
+ vroot_get_real_bdev = NULL;
+ ret = 0;
+ }
+ spin_unlock(&vroot_grb_lock);
+ return ret;
+}
+EXPORT_SYMBOL(unregister_vroot_grb);
+
+#endif
+
+/*
+ * look up a superblock on which quota ops will be performed
+ * - use the name of a block device to find the superblock thereon
+ */
+static inline struct super_block *quotactl_block(const char __user *special)
+{
+#ifdef CONFIG_BLOCK
+ struct block_device *bdev;
+ struct super_block *sb;
+ char *tmp = getname(special);
+
+ if (IS_ERR(tmp))
+ return ERR_PTR(PTR_ERR(tmp));
+ bdev = lookup_bdev(tmp);
+ putname(tmp);
+ if (IS_ERR(bdev))
+ return ERR_PTR(PTR_ERR(bdev));
+#if defined(CONFIG_BLK_DEV_VROOT) || defined(CONFIG_BLK_DEV_VROOT_MODULE)
+ if (bdev && bdev->bd_inode &&
+ imajor(bdev->bd_inode) == VROOT_MAJOR) {
+ struct block_device *bdnew = (void *)-EINVAL;
+
+ if (vroot_get_real_bdev)
+ bdnew = vroot_get_real_bdev(bdev);
+ else
+ vxdprintk(VXD_CBIT(misc, 0),
+ "vroot_get_real_bdev not set");
+ bdput(bdev);
+ if (IS_ERR(bdnew))
+ return ERR_PTR(PTR_ERR(bdnew));
+ bdev = bdnew;
+ }
+#endif
+ sb = get_super(bdev);
+ bdput(bdev);
+ if (!sb)
+ return ERR_PTR(-ENODEV);
+
+ return sb;
+#else
+ return ERR_PTR(-ENODEV);
+#endif
+}
+
/*
* This is the system call interface. This communicates with
* the user-level programs. Currently this only supports diskquota
{
uint cmds, type;
struct super_block *sb = NULL;
- struct block_device *bdev;
- char *tmp;
int ret;
cmds = cmd >> SUBCMDSHIFT;
type = cmd & SUBCMDMASK;
if (cmds != Q_SYNC || special) {
- tmp = getname(special);
- if (IS_ERR(tmp))
- return PTR_ERR(tmp);
- bdev = lookup_bdev(tmp);
- putname(tmp);
- if (IS_ERR(bdev))
- return PTR_ERR(bdev);
- sb = get_super(bdev);
- bdput(bdev);
- if (!sb)
- return -ENODEV;
+ sb = quotactl_block(special);
+ if (IS_ERR(sb))
+ return PTR_ERR(sb);
}
ret = check_quotactl_valid(sb, type, cmds, id);