X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fquota.c;h=879e255e72f91a900cca4eff44b1c5f67669696d;hb=refs%2Fheads%2Fvserver;hp=ffb35ef75d79d4127b5e2937918f34830312f6ea;hpb=a6d8dea2993ef90fb69b81372daa0b63f8aa940e;p=linux-2.6.git diff --git a/fs/quota.c b/fs/quota.c index ffb35ef75..879e255e7 100644 --- a/fs/quota.c +++ b/fs/quota.c @@ -15,11 +15,12 @@ #include #include #include -#include -#include +#include +#include +#include -/* 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; @@ -60,24 +61,6 @@ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t 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; } @@ -94,48 +77,83 @@ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t if (!sb_has_quota_enabled(sb, type)) return -ESRCH; } + /* Check privileges */ - if (cmd == Q_GETQUOTA || cmd == Q_XGETQUOTA) { + 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)) + !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) && !vx_ccaps(VXC_QUOTA_CTL)) + else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO) + if (!vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL)) return -EPERM; - return security_quotactl (cmd, type, id, sb); + return 0; } -static struct super_block *get_super_to_sync(int type) +/* Check validity of XFS Quota Manager commands */ +static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) { - struct list_head *head; - int cnt, dirty; + if (type >= XQM_MAXQUOTAS) + return -EINVAL; + if (!sb) + return -ENODEV; + if (!sb->s_qcop) + return -ENOSYS; -restart: - spin_lock(&sb_lock); - list_for_each(head, &super_blocks) { - struct super_block *sb = list_entry(head, struct super_block, s_list); + switch (cmd) { + 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; + case Q_XQUOTASYNC: + if (!sb->s_qcop->quota_sync) + return -ENOSYS; + break; + default: + return -EINVAL; + } - /* 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) - && info_any_dirty(&sb_dqopt(sb)->info[cnt])) - dirty = 1; - if (!dirty) - continue; - sb->s_count++; - spin_unlock(&sb_lock); - down_read(&sb->s_umount); - if (!sb->s_root) { - drop_super(sb); - goto restart; - } - return sb; + /* Check privileges */ + 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_XGETQSTAT && cmd != Q_XQUOTASYNC) { + if (!vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL)) + return -EPERM; } - spin_unlock(&sb_lock); - return NULL; + + return 0; +} + +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 void quota_sync_sb(struct super_block *sb, int type) @@ -152,11 +170,11 @@ static void quota_sync_sb(struct super_block *sb, int type) 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 + * 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). */ - down(&sb_dqopt(sb)->dqonoff_sem); + mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { discard[cnt] = NULL; if (type != -1 && cnt != type) @@ -165,12 +183,12 @@ static void quota_sync_sb(struct super_block *sb, int type) continue; discard[cnt] = igrab(sb_dqopt(sb)->files[cnt]); } - up(&sb_dqopt(sb)->dqonoff_sem); + mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (discard[cnt]) { - down(&discard[cnt]->i_sem); + mutex_lock(&discard[cnt]->i_mutex); truncate_inode_pages(&discard[cnt]->i_data, 0); - up(&discard[cnt]->i_sem); + mutex_unlock(&discard[cnt]->i_mutex); iput(discard[cnt]); } } @@ -178,17 +196,35 @@ static void quota_sync_sb(struct super_block *sb, int type) void sync_dquots(struct super_block *sb, int type) { + int cnt, dirty; + if (sb) { if (sb->s_qcop->quota_sync) quota_sync_sb(sb, type); + return; } - else { - while ((sb = get_super_to_sync(type)) != NULL) { - if (sb->s_qcop->quota_sync) - quota_sync_sb(sb, type); - drop_super(sb); - } + + 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) + && info_any_dirty(&sb_dqopt(sb)->info[cnt])) + dirty = 1; + if (!dirty) + continue; + sb->s_count++; + spin_unlock(&sb_lock); + down_read(&sb->s_umount); + 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; } + spin_unlock(&sb_lock); } /* Copy parameters and call proper function */ @@ -293,6 +329,8 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void 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(); @@ -300,10 +338,90 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void return 0; } -#ifdef CONFIG_BLK_DEV_VROOT -extern struct block_device *vroot_get_real_bdev(struct block_device *); +#if defined(CONFIG_BLK_DEV_VROOT) || defined(CONFIG_BLK_DEV_VROOT_MODULE) + +#include +#include +#include +#include +#include + +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 @@ -314,37 +432,15 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t { 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); -#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) - return -ENODEV; + sb = quotactl_block(special); + if (IS_ERR(sb)) + return PTR_ERR(sb); } ret = check_quotactl_valid(sb, type, cmds, id);