1
2
3
4
5
6
7
8#include <linux/fs.h>
9#include <linux/namei.h>
10#include <linux/slab.h>
11#include <asm/current.h>
12#include <asm/uaccess.h>
13#include <linux/kernel.h>
14#include <linux/smp_lock.h>
15#include <linux/security.h>
16
17
18static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
19{
20 if (type >= MAXQUOTAS)
21 return -EINVAL;
22
23 if (!sb->s_qcop)
24 return -ENOSYS;
25
26 switch (cmd) {
27 case Q_GETFMT:
28 break;
29 case Q_QUOTAON:
30 if (!sb->s_qcop->quota_on)
31 return -ENOSYS;
32 break;
33 case Q_QUOTAOFF:
34 if (!sb->s_qcop->quota_off)
35 return -ENOSYS;
36 break;
37 case Q_SETINFO:
38 if (!sb->s_qcop->set_info)
39 return -ENOSYS;
40 break;
41 case Q_GETINFO:
42 if (!sb->s_qcop->get_info)
43 return -ENOSYS;
44 break;
45 case Q_SETQUOTA:
46 if (!sb->s_qcop->set_dqblk)
47 return -ENOSYS;
48 break;
49 case Q_GETQUOTA:
50 if (!sb->s_qcop->get_dqblk)
51 return -ENOSYS;
52 break;
53 case Q_SYNC:
54 if (!sb->s_qcop->quota_sync)
55 return -ENOSYS;
56 break;
57 case Q_XQUOTAON:
58 case Q_XQUOTAOFF:
59 case Q_XQUOTARM:
60 if (!sb->s_qcop->set_xstate)
61 return -ENOSYS;
62 break;
63 case Q_XGETQSTAT:
64 if (!sb->s_qcop->get_xstate)
65 return -ENOSYS;
66 break;
67 case Q_XSETQLIM:
68 if (!sb->s_qcop->set_xquota)
69 return -ENOSYS;
70 break;
71 case Q_XGETQUOTA:
72 if (!sb->s_qcop->get_xquota)
73 return -ENOSYS;
74 break;
75 default:
76 return -EINVAL;
77 }
78
79
80 switch (cmd) {
81 case Q_GETFMT:
82 case Q_GETINFO:
83 case Q_QUOTAOFF:
84 case Q_SETINFO:
85 case Q_SETQUOTA:
86 case Q_GETQUOTA:
87 if (!sb_has_quota_enabled(sb, type))
88 return -ESRCH;
89 }
90
91 if (cmd == Q_GETQUOTA || cmd == Q_XGETQUOTA) {
92 if (((type == USRQUOTA && current->euid != id) ||
93 (type == GRPQUOTA && !in_egroup_p(id))) &&
94 !capable(CAP_SYS_ADMIN))
95 return -EPERM;
96 }
97 else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO && cmd != Q_XGETQSTAT)
98 if (!capable(CAP_SYS_ADMIN))
99 return -EPERM;
100
101 return security_ops->quotactl (cmd, type, id, sb);
102}
103
104
105static struct super_block *resolve_dev(const char *path)
106{
107 int ret;
108 mode_t mode;
109 struct nameidata nd;
110 struct block_device *bdev;
111 struct super_block *sb;
112
113 ret = user_path_walk(path, &nd);
114 if (ret)
115 goto out;
116
117 bdev = nd.dentry->d_inode->i_bdev;
118 mode = nd.dentry->d_inode->i_mode;
119 path_release(&nd);
120
121 ret = -ENOTBLK;
122 if (!S_ISBLK(mode))
123 goto out;
124 ret = -ENODEV;
125 sb = get_super(bdev);
126 if (!sb)
127 goto out;
128 return sb;
129out:
130 return ERR_PTR(ret);
131}
132
133
134static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
135{
136 int ret;
137
138 switch (cmd) {
139 case Q_QUOTAON: {
140 char *pathname;
141
142 if (IS_ERR(pathname = getname(addr)))
143 return PTR_ERR(pathname);
144 ret = sb->s_qcop->quota_on(sb, type, id, pathname);
145 putname(pathname);
146 return ret;
147 }
148 case Q_QUOTAOFF:
149 return sb->s_qcop->quota_off(sb, type);
150
151 case Q_GETFMT: {
152 __u32 fmt;
153
154 fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
155 if (copy_to_user(addr, &fmt, sizeof(fmt)))
156 return -EFAULT;
157 return 0;
158 }
159 case Q_GETINFO: {
160 struct if_dqinfo info;
161
162 if ((ret = sb->s_qcop->get_info(sb, type, &info)))
163 return ret;
164 if (copy_to_user(addr, &info, sizeof(info)))
165 return -EFAULT;
166 return 0;
167 }
168 case Q_SETINFO: {
169 struct if_dqinfo info;
170
171 if (copy_from_user(&info, addr, sizeof(info)))
172 return -EFAULT;
173 return sb->s_qcop->set_info(sb, type, &info);
174 }
175 case Q_GETQUOTA: {
176 struct if_dqblk idq;
177
178 if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)))
179 return ret;
180 if (copy_to_user(addr, &idq, sizeof(idq)))
181 return -EFAULT;
182 return 0;
183 }
184 case Q_SETQUOTA: {
185 struct if_dqblk idq;
186
187 if (copy_from_user(&idq, addr, sizeof(idq)))
188 return -EFAULT;
189 return sb->s_qcop->set_dqblk(sb, type, id, &idq);
190 }
191 case Q_SYNC:
192 return sb->s_qcop->quota_sync(sb, type);
193
194 case Q_XQUOTAON:
195 case Q_XQUOTAOFF:
196 case Q_XQUOTARM: {
197 __u32 flags;
198
199 if (copy_from_user(&flags, addr, sizeof(flags)))
200 return -EFAULT;
201 return sb->s_qcop->set_xstate(sb, flags, cmd);
202 }
203 case Q_XGETQSTAT: {
204 struct fs_quota_stat fqs;
205
206 if ((ret = sb->s_qcop->get_xstate(sb, &fqs)))
207 return ret;
208 if (copy_to_user(addr, &fqs, sizeof(fqs)))
209 return -EFAULT;
210 return 0;
211 }
212 case Q_XSETQLIM: {
213 struct fs_disk_quota fdq;
214
215 if (copy_from_user(&fdq, addr, sizeof(fdq)))
216 return -EFAULT;
217 return sb->s_qcop->set_xquota(sb, type, id, &fdq);
218 }
219 case Q_XGETQUOTA: {
220 struct fs_disk_quota fdq;
221
222 if ((ret = sb->s_qcop->get_xquota(sb, type, id, &fdq)))
223 return ret;
224 if (copy_to_user(addr, &fdq, sizeof(fdq)))
225 return -EFAULT;
226 return 0;
227 }
228
229 default:
230 BUG();
231 }
232 return 0;
233}
234
235
236
237
238
239
240
241asmlinkage long sys_quotactl(unsigned int cmd, const char *special, qid_t id, caddr_t addr)
242{
243 uint cmds, type;
244 struct super_block *sb = NULL;
245 int ret = -EINVAL;
246
247 lock_kernel();
248 cmds = cmd >> SUBCMDSHIFT;
249 type = cmd & SUBCMDMASK;
250
251 if (IS_ERR(sb = resolve_dev(special))) {
252 ret = PTR_ERR(sb);
253 sb = NULL;
254 goto out;
255 }
256 if ((ret = check_quotactl_valid(sb, type, cmds, id)) < 0)
257 goto out;
258 ret = do_quotactl(sb, type, cmds, id, addr);
259out:
260 if (sb)
261 drop_super(sb);
262 unlock_kernel();
263 return ret;
264}
265