#include <linux/kernel.h>
#include <linux/smp_lock.h>
#include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/buffer_head.h>
+#include <linux/major.h>
+#include <linux/blkdev.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))) &&
+ !capable(CAP_SYS_ADMIN) && !vx_ccaps(VXC_QUOTA_CTL))
+ return -EPERM;
+ }
+ else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO)
+ if (!capable(CAP_SYS_ADMIN) && !vx_ccaps(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:
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))) &&
+ !capable(CAP_SYS_ADMIN) && !vx_ccaps(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) {
+ if (!capable(CAP_SYS_ADMIN) && !vx_ccaps(VXC_QUOTA_CTL))
return -EPERM;
+ }
+
+ return 0;
+}
- return security_quotactl (cmd, type, id, sb);
+static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+ int error;
+
+ 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)
return NULL;
}
+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_sem and so we could
+ * not do it inside dqonoff_sem. Moreover we need to be carefull
+ * about races with quotaoff() (that is the reason why we have own
+ * reference to inode). */
+ down(&sb_dqopt(sb)->dqonoff_sem);
+ 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]);
+ }
+ up(&sb_dqopt(sb)->dqonoff_sem);
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (discard[cnt]) {
+ down(&discard[cnt]->i_sem);
+ truncate_inode_pages(&discard[cnt]->i_data, 0);
+ up(&discard[cnt]->i_sem);
+ iput(discard[cnt]);
+ }
+ }
+}
+
void sync_dquots(struct super_block *sb, int type)
{
if (sb) {
if (sb->s_qcop->quota_sync)
- sb->s_qcop->quota_sync(sb, type);
+ quota_sync_sb(sb, type);
}
else {
- while ((sb = get_super_to_sync(type))) {
+ while ((sb = get_super_to_sync(type)) != NULL) {
if (sb->s_qcop->quota_sync)
- sb->s_qcop->quota_sync(sb, type);
+ quota_sync_sb(sb, type);
drop_super(sb);
}
}
}
/* Copy parameters and call proper function */
-static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
+static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void __user *addr)
{
int ret;
return 0;
}
+#ifdef CONFIG_BLK_DEV_VROOT
+extern struct block_device *vroot_get_real_bdev(struct block_device *);
+#endif
+
/*
* 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)
+asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t id, void __user *addr)
{
uint cmds, type;
struct super_block *sb = NULL;
putname(tmp);
if (IS_ERR(bdev))
return PTR_ERR(bdev);
+#ifdef CONFIG_BLK_DEV_VROOT
+ if (bdev && bdev->bd_inode &&
+ imajor(bdev->bd_inode) == VROOT_MAJOR) {
+ struct block_device *bdnew =
+ vroot_get_real_bdev(bdev);
+
+ bdput(bdev);
+ if (IS_ERR(bdnew))
+ return PTR_ERR(bdnew);
+ bdev = bdnew;
+ }
+#endif
sb = get_super(bdev);
bdput(bdev);
if (!sb)