diff options
Diffstat (limited to 'fs/quota/quota.c')
| -rw-r--r-- | fs/quota/quota.c | 735 |
1 files changed, 228 insertions, 507 deletions
diff --git a/fs/quota/quota.c b/fs/quota/quota.c index ee91e2756950..95388f9b7356 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
| 11 | #include <asm/current.h> | 11 | #include <asm/current.h> |
| 12 | #include <asm/uaccess.h> | 12 | #include <asm/uaccess.h> |
| 13 | #include <linux/compat.h> | ||
| 14 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
| 15 | #include <linux/security.h> | 14 | #include <linux/security.h> |
| 16 | #include <linux/syscalls.h> | 15 | #include <linux/syscalls.h> |
| @@ -18,220 +17,205 @@ | |||
| 18 | #include <linux/capability.h> | 17 | #include <linux/capability.h> |
| 19 | #include <linux/quotaops.h> | 18 | #include <linux/quotaops.h> |
| 20 | #include <linux/types.h> | 19 | #include <linux/types.h> |
| 21 | #include <net/netlink.h> | 20 | #include <linux/writeback.h> |
| 22 | #include <net/genetlink.h> | ||
| 23 | 21 | ||
| 24 | /* Check validity of generic quotactl commands */ | 22 | static int check_quotactl_permission(struct super_block *sb, int type, int cmd, |
| 25 | static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, | 23 | qid_t id) |
| 26 | qid_t id) | ||
| 27 | { | 24 | { |
| 28 | if (type >= MAXQUOTAS) | ||
| 29 | return -EINVAL; | ||
| 30 | if (!sb && cmd != Q_SYNC) | ||
| 31 | return -ENODEV; | ||
| 32 | /* Is operation supported? */ | ||
| 33 | if (sb && !sb->s_qcop) | ||
| 34 | return -ENOSYS; | ||
| 35 | |||
| 36 | switch (cmd) { | 25 | switch (cmd) { |
| 37 | case Q_GETFMT: | 26 | /* these commands do not require any special privilegues */ |
| 38 | break; | 27 | case Q_GETFMT: |
| 39 | case Q_QUOTAON: | 28 | case Q_SYNC: |
| 40 | if (!sb->s_qcop->quota_on) | 29 | case Q_GETINFO: |
| 41 | return -ENOSYS; | 30 | case Q_XGETQSTAT: |
| 42 | break; | 31 | case Q_XQUOTASYNC: |
| 43 | case Q_QUOTAOFF: | 32 | break; |
| 44 | if (!sb->s_qcop->quota_off) | 33 | /* allow to query information for dquots we "own" */ |
| 45 | return -ENOSYS; | 34 | case Q_GETQUOTA: |
| 46 | break; | 35 | case Q_XGETQUOTA: |
| 47 | case Q_SETINFO: | 36 | if ((type == USRQUOTA && current_euid() == id) || |
| 48 | if (!sb->s_qcop->set_info) | 37 | (type == GRPQUOTA && in_egroup_p(id))) |
| 49 | return -ENOSYS; | ||
| 50 | break; | ||
| 51 | case Q_GETINFO: | ||
| 52 | if (!sb->s_qcop->get_info) | ||
| 53 | return -ENOSYS; | ||
| 54 | break; | ||
| 55 | case Q_SETQUOTA: | ||
| 56 | if (!sb->s_qcop->set_dqblk) | ||
| 57 | return -ENOSYS; | ||
| 58 | break; | ||
| 59 | case Q_GETQUOTA: | ||
| 60 | if (!sb->s_qcop->get_dqblk) | ||
| 61 | return -ENOSYS; | ||
| 62 | break; | ||
| 63 | case Q_SYNC: | ||
| 64 | if (sb && !sb->s_qcop->quota_sync) | ||
| 65 | return -ENOSYS; | ||
| 66 | break; | 38 | break; |
| 67 | default: | 39 | /*FALLTHROUGH*/ |
| 68 | return -EINVAL; | 40 | default: |
| 41 | if (!capable(CAP_SYS_ADMIN)) | ||
| 42 | return -EPERM; | ||
| 69 | } | 43 | } |
| 70 | 44 | ||
| 71 | /* Is quota turned on for commands which need it? */ | 45 | return security_quotactl(cmd, type, id, sb); |
| 72 | switch (cmd) { | 46 | } |
| 73 | case Q_GETFMT: | ||
| 74 | case Q_GETINFO: | ||
| 75 | case Q_SETINFO: | ||
| 76 | case Q_SETQUOTA: | ||
| 77 | case Q_GETQUOTA: | ||
| 78 | /* This is just an informative test so we are satisfied | ||
| 79 | * without the lock */ | ||
| 80 | if (!sb_has_quota_active(sb, type)) | ||
| 81 | return -ESRCH; | ||
| 82 | } | ||
| 83 | 47 | ||
| 84 | /* Check privileges */ | 48 | static int quota_sync_all(int type) |
| 85 | if (cmd == Q_GETQUOTA) { | 49 | { |
| 86 | if (((type == USRQUOTA && current_euid() != id) || | 50 | struct super_block *sb; |
| 87 | (type == GRPQUOTA && !in_egroup_p(id))) && | 51 | int ret; |
| 88 | !capable(CAP_SYS_ADMIN)) | 52 | |
| 89 | return -EPERM; | 53 | if (type >= MAXQUOTAS) |
| 54 | return -EINVAL; | ||
| 55 | ret = security_quotactl(Q_SYNC, type, 0, NULL); | ||
| 56 | if (ret) | ||
| 57 | return ret; | ||
| 58 | |||
| 59 | spin_lock(&sb_lock); | ||
| 60 | restart: | ||
| 61 | list_for_each_entry(sb, &super_blocks, s_list) { | ||
| 62 | if (!sb->s_qcop || !sb->s_qcop->quota_sync) | ||
| 63 | continue; | ||
| 64 | |||
| 65 | sb->s_count++; | ||
| 66 | spin_unlock(&sb_lock); | ||
| 67 | down_read(&sb->s_umount); | ||
| 68 | if (sb->s_root) | ||
| 69 | sb->s_qcop->quota_sync(sb, type, 1); | ||
| 70 | up_read(&sb->s_umount); | ||
| 71 | spin_lock(&sb_lock); | ||
| 72 | if (__put_super_and_need_restart(sb)) | ||
| 73 | goto restart; | ||
| 90 | } | 74 | } |
| 91 | else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO) | 75 | spin_unlock(&sb_lock); |
| 92 | if (!capable(CAP_SYS_ADMIN)) | ||
| 93 | return -EPERM; | ||
| 94 | 76 | ||
| 95 | return 0; | 77 | return 0; |
| 96 | } | 78 | } |
| 97 | 79 | ||
| 98 | /* Check validity of XFS Quota Manager commands */ | 80 | static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id, |
| 99 | static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, | 81 | void __user *addr) |
| 100 | qid_t id) | ||
| 101 | { | 82 | { |
| 102 | if (type >= XQM_MAXQUOTAS) | 83 | char *pathname; |
| 103 | return -EINVAL; | 84 | int ret = -ENOSYS; |
| 104 | if (!sb) | 85 | |
| 105 | return -ENODEV; | 86 | pathname = getname(addr); |
| 106 | if (!sb->s_qcop) | 87 | if (IS_ERR(pathname)) |
| 107 | return -ENOSYS; | 88 | return PTR_ERR(pathname); |
| 89 | if (sb->s_qcop->quota_on) | ||
| 90 | ret = sb->s_qcop->quota_on(sb, type, id, pathname, 0); | ||
| 91 | putname(pathname); | ||
| 92 | return ret; | ||
| 93 | } | ||
| 108 | 94 | ||
| 109 | switch (cmd) { | 95 | static int quota_getfmt(struct super_block *sb, int type, void __user *addr) |
| 110 | case Q_XQUOTAON: | 96 | { |
| 111 | case Q_XQUOTAOFF: | 97 | __u32 fmt; |
| 112 | case Q_XQUOTARM: | ||
| 113 | if (!sb->s_qcop->set_xstate) | ||
| 114 | return -ENOSYS; | ||
| 115 | break; | ||
| 116 | case Q_XGETQSTAT: | ||
| 117 | if (!sb->s_qcop->get_xstate) | ||
| 118 | return -ENOSYS; | ||
| 119 | break; | ||
| 120 | case Q_XSETQLIM: | ||
| 121 | if (!sb->s_qcop->set_xquota) | ||
| 122 | return -ENOSYS; | ||
| 123 | break; | ||
| 124 | case Q_XGETQUOTA: | ||
| 125 | if (!sb->s_qcop->get_xquota) | ||
| 126 | return -ENOSYS; | ||
| 127 | break; | ||
| 128 | case Q_XQUOTASYNC: | ||
| 129 | if (!sb->s_qcop->quota_sync) | ||
| 130 | return -ENOSYS; | ||
| 131 | break; | ||
| 132 | default: | ||
| 133 | return -EINVAL; | ||
| 134 | } | ||
| 135 | 98 | ||
| 136 | /* Check privileges */ | 99 | down_read(&sb_dqopt(sb)->dqptr_sem); |
| 137 | if (cmd == Q_XGETQUOTA) { | 100 | if (!sb_has_quota_active(sb, type)) { |
| 138 | if (((type == XQM_USRQUOTA && current_euid() != id) || | 101 | up_read(&sb_dqopt(sb)->dqptr_sem); |
| 139 | (type == XQM_GRPQUOTA && !in_egroup_p(id))) && | 102 | return -ESRCH; |
| 140 | !capable(CAP_SYS_ADMIN)) | ||
| 141 | return -EPERM; | ||
| 142 | } else if (cmd != Q_XGETQSTAT && cmd != Q_XQUOTASYNC) { | ||
| 143 | if (!capable(CAP_SYS_ADMIN)) | ||
| 144 | return -EPERM; | ||
| 145 | } | 103 | } |
| 104 | fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; | ||
| 105 | up_read(&sb_dqopt(sb)->dqptr_sem); | ||
| 106 | if (copy_to_user(addr, &fmt, sizeof(fmt))) | ||
| 107 | return -EFAULT; | ||
| 108 | return 0; | ||
| 109 | } | ||
| 146 | 110 | ||
| 111 | static int quota_getinfo(struct super_block *sb, int type, void __user *addr) | ||
| 112 | { | ||
| 113 | struct if_dqinfo info; | ||
| 114 | int ret; | ||
| 115 | |||
| 116 | if (!sb_has_quota_active(sb, type)) | ||
| 117 | return -ESRCH; | ||
| 118 | if (!sb->s_qcop->get_info) | ||
| 119 | return -ENOSYS; | ||
| 120 | ret = sb->s_qcop->get_info(sb, type, &info); | ||
| 121 | if (!ret && copy_to_user(addr, &info, sizeof(info))) | ||
| 122 | return -EFAULT; | ||
| 123 | return ret; | ||
| 124 | } | ||
| 125 | |||
| 126 | static int quota_setinfo(struct super_block *sb, int type, void __user *addr) | ||
| 127 | { | ||
| 128 | struct if_dqinfo info; | ||
| 129 | |||
| 130 | if (copy_from_user(&info, addr, sizeof(info))) | ||
| 131 | return -EFAULT; | ||
| 132 | if (!sb_has_quota_active(sb, type)) | ||
| 133 | return -ESRCH; | ||
| 134 | if (!sb->s_qcop->set_info) | ||
| 135 | return -ENOSYS; | ||
| 136 | return sb->s_qcop->set_info(sb, type, &info); | ||
| 137 | } | ||
| 138 | |||
| 139 | static int quota_getquota(struct super_block *sb, int type, qid_t id, | ||
| 140 | void __user *addr) | ||
| 141 | { | ||
| 142 | struct if_dqblk idq; | ||
| 143 | int ret; | ||
| 144 | |||
| 145 | if (!sb_has_quota_active(sb, type)) | ||
| 146 | return -ESRCH; | ||
| 147 | if (!sb->s_qcop->get_dqblk) | ||
| 148 | return -ENOSYS; | ||
| 149 | ret = sb->s_qcop->get_dqblk(sb, type, id, &idq); | ||
| 150 | if (ret) | ||
| 151 | return ret; | ||
| 152 | if (copy_to_user(addr, &idq, sizeof(idq))) | ||
| 153 | return -EFAULT; | ||
| 147 | return 0; | 154 | return 0; |
| 148 | } | 155 | } |
| 149 | 156 | ||
| 150 | static int check_quotactl_valid(struct super_block *sb, int type, int cmd, | 157 | static int quota_setquota(struct super_block *sb, int type, qid_t id, |
| 151 | qid_t id) | 158 | void __user *addr) |
| 152 | { | 159 | { |
| 153 | int error; | 160 | struct if_dqblk idq; |
| 154 | 161 | ||
| 155 | if (XQM_COMMAND(cmd)) | 162 | if (copy_from_user(&idq, addr, sizeof(idq))) |
| 156 | error = xqm_quotactl_valid(sb, type, cmd, id); | 163 | return -EFAULT; |
| 157 | else | 164 | if (!sb_has_quota_active(sb, type)) |
| 158 | error = generic_quotactl_valid(sb, type, cmd, id); | 165 | return -ESRCH; |
| 159 | if (!error) | 166 | if (!sb->s_qcop->set_dqblk) |
| 160 | error = security_quotactl(cmd, type, id, sb); | 167 | return -ENOSYS; |
| 161 | return error; | 168 | return sb->s_qcop->set_dqblk(sb, type, id, &idq); |
| 162 | } | 169 | } |
| 163 | 170 | ||
| 164 | #ifdef CONFIG_QUOTA | 171 | static int quota_setxstate(struct super_block *sb, int cmd, void __user *addr) |
| 165 | void sync_quota_sb(struct super_block *sb, int type) | ||
| 166 | { | 172 | { |
| 167 | int cnt; | 173 | __u32 flags; |
| 168 | 174 | ||
| 169 | if (!sb->s_qcop->quota_sync) | 175 | if (copy_from_user(&flags, addr, sizeof(flags))) |
| 170 | return; | 176 | return -EFAULT; |
| 177 | if (!sb->s_qcop->set_xstate) | ||
| 178 | return -ENOSYS; | ||
| 179 | return sb->s_qcop->set_xstate(sb, flags, cmd); | ||
| 180 | } | ||
| 171 | 181 | ||
| 172 | sb->s_qcop->quota_sync(sb, type); | 182 | static int quota_getxstate(struct super_block *sb, void __user *addr) |
| 183 | { | ||
| 184 | struct fs_quota_stat fqs; | ||
| 185 | int ret; | ||
| 173 | 186 | ||
| 174 | if (sb_dqopt(sb)->flags & DQUOT_QUOTA_SYS_FILE) | 187 | if (!sb->s_qcop->get_xstate) |
| 175 | return; | 188 | return -ENOSYS; |
| 176 | /* This is not very clever (and fast) but currently I don't know about | 189 | ret = sb->s_qcop->get_xstate(sb, &fqs); |
| 177 | * any other simple way of getting quota data to disk and we must get | 190 | if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) |
| 178 | * them there for userspace to be visible... */ | 191 | return -EFAULT; |
| 179 | if (sb->s_op->sync_fs) | 192 | return ret; |
| 180 | sb->s_op->sync_fs(sb, 1); | 193 | } |
| 181 | sync_blockdev(sb->s_bdev); | ||
| 182 | 194 | ||
| 183 | /* | 195 | static int quota_setxquota(struct super_block *sb, int type, qid_t id, |
| 184 | * Now when everything is written we can discard the pagecache so | 196 | void __user *addr) |
| 185 | * that userspace sees the changes. | 197 | { |
| 186 | */ | 198 | struct fs_disk_quota fdq; |
| 187 | mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); | 199 | |
| 188 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) { | 200 | if (copy_from_user(&fdq, addr, sizeof(fdq))) |
| 189 | if (type != -1 && cnt != type) | 201 | return -EFAULT; |
| 190 | continue; | 202 | if (!sb->s_qcop->set_xquota) |
| 191 | if (!sb_has_quota_active(sb, cnt)) | 203 | return -ENOSYS; |
| 192 | continue; | 204 | return sb->s_qcop->set_xquota(sb, type, id, &fdq); |
| 193 | mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex, | ||
| 194 | I_MUTEX_QUOTA); | ||
| 195 | truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0); | ||
| 196 | mutex_unlock(&sb_dqopt(sb)->files[cnt]->i_mutex); | ||
| 197 | } | ||
| 198 | mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); | ||
| 199 | } | 205 | } |
| 200 | #endif | ||
| 201 | 206 | ||
| 202 | static void sync_dquots(int type) | 207 | static int quota_getxquota(struct super_block *sb, int type, qid_t id, |
| 208 | void __user *addr) | ||
| 203 | { | 209 | { |
| 204 | struct super_block *sb; | 210 | struct fs_disk_quota fdq; |
| 205 | int cnt; | 211 | int ret; |
| 206 | 212 | ||
| 207 | spin_lock(&sb_lock); | 213 | if (!sb->s_qcop->get_xquota) |
| 208 | restart: | 214 | return -ENOSYS; |
| 209 | list_for_each_entry(sb, &super_blocks, s_list) { | 215 | ret = sb->s_qcop->get_xquota(sb, type, id, &fdq); |
| 210 | /* This test just improves performance so it needn't be | 216 | if (!ret && copy_to_user(addr, &fdq, sizeof(fdq))) |
| 211 | * reliable... */ | 217 | return -EFAULT; |
| 212 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) { | 218 | return ret; |
| 213 | if (type != -1 && type != cnt) | ||
| 214 | continue; | ||
| 215 | if (!sb_has_quota_active(sb, cnt)) | ||
| 216 | continue; | ||
| 217 | if (!info_dirty(&sb_dqopt(sb)->info[cnt]) && | ||
| 218 | list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list)) | ||
| 219 | continue; | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | if (cnt == MAXQUOTAS) | ||
| 223 | continue; | ||
| 224 | sb->s_count++; | ||
| 225 | spin_unlock(&sb_lock); | ||
| 226 | down_read(&sb->s_umount); | ||
| 227 | if (sb->s_root) | ||
| 228 | sync_quota_sb(sb, type); | ||
| 229 | up_read(&sb->s_umount); | ||
| 230 | spin_lock(&sb_lock); | ||
| 231 | if (__put_super_and_need_restart(sb)) | ||
| 232 | goto restart; | ||
| 233 | } | ||
| 234 | spin_unlock(&sb_lock); | ||
| 235 | } | 219 | } |
| 236 | 220 | ||
| 237 | /* Copy parameters and call proper function */ | 221 | /* Copy parameters and call proper function */ |
| @@ -240,117 +224,55 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, | |||
| 240 | { | 224 | { |
| 241 | int ret; | 225 | int ret; |
| 242 | 226 | ||
| 227 | if (type >= (XQM_COMMAND(cmd) ? XQM_MAXQUOTAS : MAXQUOTAS)) | ||
| 228 | return -EINVAL; | ||
| 229 | if (!sb->s_qcop) | ||
| 230 | return -ENOSYS; | ||
| 231 | |||
| 232 | ret = check_quotactl_permission(sb, type, cmd, id); | ||
| 233 | if (ret < 0) | ||
| 234 | return ret; | ||
| 235 | |||
| 243 | switch (cmd) { | 236 | switch (cmd) { |
| 244 | case Q_QUOTAON: { | 237 | case Q_QUOTAON: |
| 245 | char *pathname; | 238 | return quota_quotaon(sb, type, cmd, id, addr); |
| 246 | 239 | case Q_QUOTAOFF: | |
| 247 | pathname = getname(addr); | 240 | if (!sb->s_qcop->quota_off) |
| 248 | if (IS_ERR(pathname)) | 241 | return -ENOSYS; |
| 249 | return PTR_ERR(pathname); | 242 | return sb->s_qcop->quota_off(sb, type, 0); |
| 250 | ret = sb->s_qcop->quota_on(sb, type, id, pathname, 0); | 243 | case Q_GETFMT: |
| 251 | putname(pathname); | 244 | return quota_getfmt(sb, type, addr); |
| 252 | return ret; | 245 | case Q_GETINFO: |
| 253 | } | 246 | return quota_getinfo(sb, type, addr); |
| 254 | case Q_QUOTAOFF: | 247 | case Q_SETINFO: |
| 255 | return sb->s_qcop->quota_off(sb, type, 0); | 248 | return quota_setinfo(sb, type, addr); |
| 256 | 249 | case Q_GETQUOTA: | |
| 257 | case Q_GETFMT: { | 250 | return quota_getquota(sb, type, id, addr); |
| 258 | __u32 fmt; | 251 | case Q_SETQUOTA: |
| 259 | 252 | return quota_setquota(sb, type, id, addr); | |
| 260 | down_read(&sb_dqopt(sb)->dqptr_sem); | 253 | case Q_SYNC: |
| 261 | if (!sb_has_quota_active(sb, type)) { | 254 | if (!sb->s_qcop->quota_sync) |
| 262 | up_read(&sb_dqopt(sb)->dqptr_sem); | 255 | return -ENOSYS; |
| 263 | return -ESRCH; | 256 | return sb->s_qcop->quota_sync(sb, type, 1); |
| 264 | } | 257 | case Q_XQUOTAON: |
| 265 | fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; | 258 | case Q_XQUOTAOFF: |
| 266 | up_read(&sb_dqopt(sb)->dqptr_sem); | 259 | case Q_XQUOTARM: |
| 267 | if (copy_to_user(addr, &fmt, sizeof(fmt))) | 260 | return quota_setxstate(sb, cmd, addr); |
| 268 | return -EFAULT; | 261 | case Q_XGETQSTAT: |
| 269 | return 0; | 262 | return quota_getxstate(sb, addr); |
| 270 | } | 263 | case Q_XSETQLIM: |
| 271 | case Q_GETINFO: { | 264 | return quota_setxquota(sb, type, id, addr); |
| 272 | struct if_dqinfo info; | 265 | case Q_XGETQUOTA: |
| 273 | 266 | return quota_getxquota(sb, type, id, addr); | |
| 274 | ret = sb->s_qcop->get_info(sb, type, &info); | 267 | case Q_XQUOTASYNC: |
| 275 | if (ret) | 268 | /* caller already holds s_umount */ |
| 276 | return ret; | 269 | if (sb->s_flags & MS_RDONLY) |
| 277 | if (copy_to_user(addr, &info, sizeof(info))) | 270 | return -EROFS; |
| 278 | return -EFAULT; | 271 | writeback_inodes_sb(sb); |
| 279 | return 0; | 272 | return 0; |
| 280 | } | 273 | default: |
| 281 | case Q_SETINFO: { | 274 | return -EINVAL; |
| 282 | struct if_dqinfo info; | ||
| 283 | |||
| 284 | if (copy_from_user(&info, addr, sizeof(info))) | ||
| 285 | return -EFAULT; | ||
| 286 | return sb->s_qcop->set_info(sb, type, &info); | ||
| 287 | } | ||
| 288 | case Q_GETQUOTA: { | ||
| 289 | struct if_dqblk idq; | ||
| 290 | |||
| 291 | ret = sb->s_qcop->get_dqblk(sb, type, id, &idq); | ||
| 292 | if (ret) | ||
| 293 | return ret; | ||
| 294 | if (copy_to_user(addr, &idq, sizeof(idq))) | ||
| 295 | return -EFAULT; | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | case Q_SETQUOTA: { | ||
| 299 | struct if_dqblk idq; | ||
| 300 | |||
| 301 | if (copy_from_user(&idq, addr, sizeof(idq))) | ||
| 302 | return -EFAULT; | ||
| 303 | return sb->s_qcop->set_dqblk(sb, type, id, &idq); | ||
| 304 | } | ||
| 305 | case Q_SYNC: | ||
| 306 | if (sb) | ||
| 307 | sync_quota_sb(sb, type); | ||
| 308 | else | ||
| 309 | sync_dquots(type); | ||
| 310 | return 0; | ||
| 311 | |||
| 312 | case Q_XQUOTAON: | ||
| 313 | case Q_XQUOTAOFF: | ||
| 314 | case Q_XQUOTARM: { | ||
| 315 | __u32 flags; | ||
| 316 | |||
| 317 | if (copy_from_user(&flags, addr, sizeof(flags))) | ||
| 318 | return -EFAULT; | ||
| 319 | return sb->s_qcop->set_xstate(sb, flags, cmd); | ||
| 320 | } | ||
| 321 | case Q_XGETQSTAT: { | ||
| 322 | struct fs_quota_stat fqs; | ||
| 323 | |||
| 324 | if ((ret = sb->s_qcop->get_xstate(sb, &fqs))) | ||
| 325 | return ret; | ||
| 326 | if (copy_to_user(addr, &fqs, sizeof(fqs))) | ||
| 327 | return -EFAULT; | ||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | case Q_XSETQLIM: { | ||
| 331 | struct fs_disk_quota fdq; | ||
| 332 | |||
| 333 | if (copy_from_user(&fdq, addr, sizeof(fdq))) | ||
| 334 | return -EFAULT; | ||
| 335 | return sb->s_qcop->set_xquota(sb, type, id, &fdq); | ||
| 336 | } | ||
| 337 | case Q_XGETQUOTA: { | ||
| 338 | struct fs_disk_quota fdq; | ||
| 339 | |||
| 340 | ret = sb->s_qcop->get_xquota(sb, type, id, &fdq); | ||
| 341 | if (ret) | ||
| 342 | return ret; | ||
| 343 | if (copy_to_user(addr, &fdq, sizeof(fdq))) | ||
| 344 | return -EFAULT; | ||
| 345 | return 0; | ||
| 346 | } | ||
| 347 | case Q_XQUOTASYNC: | ||
| 348 | return sb->s_qcop->quota_sync(sb, type); | ||
| 349 | /* We never reach here unless validity check is broken */ | ||
| 350 | default: | ||
| 351 | BUG(); | ||
| 352 | } | 275 | } |
| 353 | return 0; | ||
| 354 | } | 276 | } |
| 355 | 277 | ||
| 356 | /* | 278 | /* |
| @@ -397,224 +319,23 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, | |||
| 397 | cmds = cmd >> SUBCMDSHIFT; | 319 | cmds = cmd >> SUBCMDSHIFT; |
| 398 | type = cmd & SUBCMDMASK; | 320 | type = cmd & SUBCMDMASK; |
| 399 | 321 | ||
| 400 | if (cmds != Q_SYNC || special) { | 322 | /* |
| 401 | sb = quotactl_block(special); | 323 | * As a special case Q_SYNC can be called without a specific device. |
| 402 | if (IS_ERR(sb)) | 324 | * It will iterate all superblocks that have quota enabled and call |
| 403 | return PTR_ERR(sb); | 325 | * the sync action on each of them. |
| 326 | */ | ||
| 327 | if (!special) { | ||
| 328 | if (cmds == Q_SYNC) | ||
| 329 | return quota_sync_all(type); | ||
| 330 | return -ENODEV; | ||
| 404 | } | 331 | } |
| 405 | 332 | ||
| 406 | ret = check_quotactl_valid(sb, type, cmds, id); | 333 | sb = quotactl_block(special); |
| 407 | if (ret >= 0) | 334 | if (IS_ERR(sb)) |
| 408 | ret = do_quotactl(sb, type, cmds, id, addr); | 335 | return PTR_ERR(sb); |
| 409 | if (sb) | ||
| 410 | drop_super(sb); | ||
| 411 | 336 | ||
| 412 | return ret; | 337 | ret = do_quotactl(sb, type, cmds, id, addr); |
| 413 | } | ||
| 414 | |||
| 415 | #if defined(CONFIG_COMPAT_FOR_U64_ALIGNMENT) | ||
| 416 | /* | ||
| 417 | * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64) | ||
| 418 | * and is necessary due to alignment problems. | ||
| 419 | */ | ||
| 420 | struct compat_if_dqblk { | ||
| 421 | compat_u64 dqb_bhardlimit; | ||
| 422 | compat_u64 dqb_bsoftlimit; | ||
| 423 | compat_u64 dqb_curspace; | ||
| 424 | compat_u64 dqb_ihardlimit; | ||
| 425 | compat_u64 dqb_isoftlimit; | ||
| 426 | compat_u64 dqb_curinodes; | ||
| 427 | compat_u64 dqb_btime; | ||
| 428 | compat_u64 dqb_itime; | ||
| 429 | compat_uint_t dqb_valid; | ||
| 430 | }; | ||
| 431 | |||
| 432 | /* XFS structures */ | ||
| 433 | struct compat_fs_qfilestat { | ||
| 434 | compat_u64 dqb_bhardlimit; | ||
| 435 | compat_u64 qfs_nblks; | ||
| 436 | compat_uint_t qfs_nextents; | ||
| 437 | }; | ||
| 438 | |||
| 439 | struct compat_fs_quota_stat { | ||
| 440 | __s8 qs_version; | ||
| 441 | __u16 qs_flags; | ||
| 442 | __s8 qs_pad; | ||
| 443 | struct compat_fs_qfilestat qs_uquota; | ||
| 444 | struct compat_fs_qfilestat qs_gquota; | ||
| 445 | compat_uint_t qs_incoredqs; | ||
| 446 | compat_int_t qs_btimelimit; | ||
| 447 | compat_int_t qs_itimelimit; | ||
| 448 | compat_int_t qs_rtbtimelimit; | ||
| 449 | __u16 qs_bwarnlimit; | ||
| 450 | __u16 qs_iwarnlimit; | ||
| 451 | }; | ||
| 452 | |||
| 453 | asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special, | ||
| 454 | qid_t id, void __user *addr) | ||
| 455 | { | ||
| 456 | unsigned int cmds; | ||
| 457 | struct if_dqblk __user *dqblk; | ||
| 458 | struct compat_if_dqblk __user *compat_dqblk; | ||
| 459 | struct fs_quota_stat __user *fsqstat; | ||
| 460 | struct compat_fs_quota_stat __user *compat_fsqstat; | ||
| 461 | compat_uint_t data; | ||
| 462 | u16 xdata; | ||
| 463 | long ret; | ||
| 464 | 338 | ||
| 465 | cmds = cmd >> SUBCMDSHIFT; | 339 | drop_super(sb); |
| 466 | |||
| 467 | switch (cmds) { | ||
| 468 | case Q_GETQUOTA: | ||
| 469 | dqblk = compat_alloc_user_space(sizeof(struct if_dqblk)); | ||
| 470 | compat_dqblk = addr; | ||
| 471 | ret = sys_quotactl(cmd, special, id, dqblk); | ||
| 472 | if (ret) | ||
| 473 | break; | ||
| 474 | if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) || | ||
| 475 | get_user(data, &dqblk->dqb_valid) || | ||
| 476 | put_user(data, &compat_dqblk->dqb_valid)) | ||
| 477 | ret = -EFAULT; | ||
| 478 | break; | ||
| 479 | case Q_SETQUOTA: | ||
| 480 | dqblk = compat_alloc_user_space(sizeof(struct if_dqblk)); | ||
| 481 | compat_dqblk = addr; | ||
| 482 | ret = -EFAULT; | ||
| 483 | if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) || | ||
| 484 | get_user(data, &compat_dqblk->dqb_valid) || | ||
| 485 | put_user(data, &dqblk->dqb_valid)) | ||
| 486 | break; | ||
| 487 | ret = sys_quotactl(cmd, special, id, dqblk); | ||
| 488 | break; | ||
| 489 | case Q_XGETQSTAT: | ||
| 490 | fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat)); | ||
| 491 | compat_fsqstat = addr; | ||
| 492 | ret = sys_quotactl(cmd, special, id, fsqstat); | ||
| 493 | if (ret) | ||
| 494 | break; | ||
| 495 | ret = -EFAULT; | ||
| 496 | /* Copying qs_version, qs_flags, qs_pad */ | ||
| 497 | if (copy_in_user(compat_fsqstat, fsqstat, | ||
| 498 | offsetof(struct compat_fs_quota_stat, qs_uquota))) | ||
| 499 | break; | ||
| 500 | /* Copying qs_uquota */ | ||
| 501 | if (copy_in_user(&compat_fsqstat->qs_uquota, | ||
| 502 | &fsqstat->qs_uquota, | ||
| 503 | sizeof(compat_fsqstat->qs_uquota)) || | ||
| 504 | get_user(data, &fsqstat->qs_uquota.qfs_nextents) || | ||
| 505 | put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents)) | ||
| 506 | break; | ||
| 507 | /* Copying qs_gquota */ | ||
| 508 | if (copy_in_user(&compat_fsqstat->qs_gquota, | ||
| 509 | &fsqstat->qs_gquota, | ||
| 510 | sizeof(compat_fsqstat->qs_gquota)) || | ||
| 511 | get_user(data, &fsqstat->qs_gquota.qfs_nextents) || | ||
| 512 | put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents)) | ||
| 513 | break; | ||
| 514 | /* Copying the rest */ | ||
| 515 | if (copy_in_user(&compat_fsqstat->qs_incoredqs, | ||
| 516 | &fsqstat->qs_incoredqs, | ||
| 517 | sizeof(struct compat_fs_quota_stat) - | ||
| 518 | offsetof(struct compat_fs_quota_stat, qs_incoredqs)) || | ||
| 519 | get_user(xdata, &fsqstat->qs_iwarnlimit) || | ||
| 520 | put_user(xdata, &compat_fsqstat->qs_iwarnlimit)) | ||
| 521 | break; | ||
| 522 | ret = 0; | ||
| 523 | break; | ||
| 524 | default: | ||
| 525 | ret = sys_quotactl(cmd, special, id, addr); | ||
| 526 | } | ||
| 527 | return ret; | 340 | return ret; |
| 528 | } | 341 | } |
| 529 | #endif | ||
| 530 | |||
| 531 | |||
| 532 | #ifdef CONFIG_QUOTA_NETLINK_INTERFACE | ||
| 533 | |||
| 534 | /* Netlink family structure for quota */ | ||
| 535 | static struct genl_family quota_genl_family = { | ||
| 536 | .id = GENL_ID_GENERATE, | ||
| 537 | .hdrsize = 0, | ||
| 538 | .name = "VFS_DQUOT", | ||
| 539 | .version = 1, | ||
| 540 | .maxattr = QUOTA_NL_A_MAX, | ||
| 541 | }; | ||
| 542 | |||
| 543 | /** | ||
| 544 | * quota_send_warning - Send warning to userspace about exceeded quota | ||
| 545 | * @type: The quota type: USRQQUOTA, GRPQUOTA,... | ||
| 546 | * @id: The user or group id of the quota that was exceeded | ||
| 547 | * @dev: The device on which the fs is mounted (sb->s_dev) | ||
| 548 | * @warntype: The type of the warning: QUOTA_NL_... | ||
| 549 | * | ||
| 550 | * This can be used by filesystems (including those which don't use | ||
| 551 | * dquot) to send a message to userspace relating to quota limits. | ||
| 552 | * | ||
| 553 | */ | ||
| 554 | |||
| 555 | void quota_send_warning(short type, unsigned int id, dev_t dev, | ||
| 556 | const char warntype) | ||
| 557 | { | ||
| 558 | static atomic_t seq; | ||
| 559 | struct sk_buff *skb; | ||
| 560 | void *msg_head; | ||
| 561 | int ret; | ||
| 562 | int msg_size = 4 * nla_total_size(sizeof(u32)) + | ||
| 563 | 2 * nla_total_size(sizeof(u64)); | ||
| 564 | |||
| 565 | /* We have to allocate using GFP_NOFS as we are called from a | ||
| 566 | * filesystem performing write and thus further recursion into | ||
| 567 | * the fs to free some data could cause deadlocks. */ | ||
| 568 | skb = genlmsg_new(msg_size, GFP_NOFS); | ||
| 569 | if (!skb) { | ||
| 570 | printk(KERN_ERR | ||
| 571 | "VFS: Not enough memory to send quota warning.\n"); | ||
| 572 | return; | ||
| 573 | } | ||
| 574 | msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq), | ||
| 575 | "a_genl_family, 0, QUOTA_NL_C_WARNING); | ||
| 576 | if (!msg_head) { | ||
| 577 | printk(KERN_ERR | ||
| 578 | "VFS: Cannot store netlink header in quota warning.\n"); | ||
| 579 | goto err_out; | ||
| 580 | } | ||
| 581 | ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type); | ||
| 582 | if (ret) | ||
| 583 | goto attr_err_out; | ||
| 584 | ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, id); | ||
| 585 | if (ret) | ||
| 586 | goto attr_err_out; | ||
| 587 | ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype); | ||
| 588 | if (ret) | ||
| 589 | goto attr_err_out; | ||
| 590 | ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MAJOR, MAJOR(dev)); | ||
| 591 | if (ret) | ||
| 592 | goto attr_err_out; | ||
| 593 | ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev)); | ||
| 594 | if (ret) | ||
| 595 | goto attr_err_out; | ||
| 596 | ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid()); | ||
| 597 | if (ret) | ||
| 598 | goto attr_err_out; | ||
| 599 | genlmsg_end(skb, msg_head); | ||
| 600 | |||
| 601 | genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS); | ||
| 602 | return; | ||
| 603 | attr_err_out: | ||
| 604 | printk(KERN_ERR "VFS: Not enough space to compose quota message!\n"); | ||
| 605 | err_out: | ||
| 606 | kfree_skb(skb); | ||
| 607 | } | ||
| 608 | EXPORT_SYMBOL(quota_send_warning); | ||
| 609 | |||
| 610 | static int __init quota_init(void) | ||
| 611 | { | ||
| 612 | if (genl_register_family("a_genl_family) != 0) | ||
| 613 | printk(KERN_ERR | ||
| 614 | "VFS: Failed to create quota netlink interface.\n"); | ||
| 615 | return 0; | ||
| 616 | }; | ||
| 617 | |||
| 618 | module_init(quota_init); | ||
| 619 | #endif | ||
| 620 | |||
