File: | ufs/ufs/ufs_quota.c |
Warning: | line 327, column 9 Although the value stored to 'dq' is used in the enclosing expression, the value is never actually read from 'dq' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ufs_quota.c,v 1.47 2020/06/24 22:03:45 cheloha Exp $ */ |
2 | /* $NetBSD: ufs_quota.c,v 1.8 1996/02/09 22:36:09 christos Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 1982, 1986, 1990, 1993, 1995 |
6 | * The Regents of the University of California. All rights reserved. |
7 | * |
8 | * This code is derived from software contributed to Berkeley by |
9 | * Robert Elz at The University of Melbourne. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * 3. Neither the name of the University nor the names of its contributors |
20 | * may be used to endorse or promote products derived from this software |
21 | * without specific prior written permission. |
22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 | * SUCH DAMAGE. |
34 | * |
35 | * @(#)ufs_quota.c 8.5 (Berkeley) 8/19/94 |
36 | */ |
37 | |
38 | #include <sys/param.h> |
39 | #include <sys/kernel.h> |
40 | #include <sys/systm.h> |
41 | #include <sys/namei.h> |
42 | #include <sys/malloc.h> |
43 | #include <sys/fcntl.h> |
44 | #include <sys/proc.h> |
45 | #include <sys/vnode.h> |
46 | #include <sys/mount.h> |
47 | #include <sys/ktrace.h> |
48 | |
49 | #include <ufs/ufs/quota.h> |
50 | #include <ufs/ufs/inode.h> |
51 | #include <ufs/ufs/ufsmount.h> |
52 | #include <ufs/ufs/ufs_extern.h> |
53 | |
54 | #include <sys/queue.h> |
55 | |
56 | #include <crypto/siphash.h> |
57 | |
58 | /* |
59 | * The following structure records disk usage for a user or group on a |
60 | * filesystem. There is one allocated for each quota that exists on any |
61 | * filesystem for the current user or group. A cache is kept of recently |
62 | * used entries. |
63 | */ |
64 | struct dquot { |
65 | LIST_ENTRY(dquot)struct { struct dquot *le_next; struct dquot **le_prev; } dq_hash; /* hash list */ |
66 | TAILQ_ENTRY(dquot)struct { struct dquot *tqe_next; struct dquot **tqe_prev; } dq_freelist; /* free list */ |
67 | u_int16_t dq_flags; /* flags, see below */ |
68 | u_int16_t dq_type; /* quota type of this dquot */ |
69 | u_int32_t dq_cnt; /* count of active references */ |
70 | u_int32_t dq_id; /* identifier this applies to */ |
71 | struct vnode *dq_vp; /* file backing this quota */ |
72 | struct ucred *dq_cred; /* credentials for writing file */ |
73 | struct dqblk dq_dqb; /* actual usage & quotas */ |
74 | }; |
75 | |
76 | /* |
77 | * Flag values. |
78 | */ |
79 | #define DQ_LOCK0x01 0x01 /* this quota locked (no MODS) */ |
80 | #define DQ_WANT0x02 0x02 /* wakeup on unlock */ |
81 | #define DQ_MOD0x04 0x04 /* this quota modified since read */ |
82 | #define DQ_FAKE0x08 0x08 /* no limits here, just usage */ |
83 | #define DQ_BLKS0x10 0x10 /* has been warned about blk limit */ |
84 | #define DQ_INODS0x20 0x20 /* has been warned about inode limit */ |
85 | |
86 | /* |
87 | * Shorthand notation. |
88 | */ |
89 | #define dq_bhardlimitdq_dqb.dqb_bhardlimit dq_dqb.dqb_bhardlimit |
90 | #define dq_bsoftlimitdq_dqb.dqb_bsoftlimit dq_dqb.dqb_bsoftlimit |
91 | #define dq_curblocksdq_dqb.dqb_curblocks dq_dqb.dqb_curblocks |
92 | #define dq_ihardlimitdq_dqb.dqb_ihardlimit dq_dqb.dqb_ihardlimit |
93 | #define dq_isoftlimitdq_dqb.dqb_isoftlimit dq_dqb.dqb_isoftlimit |
94 | #define dq_curinodesdq_dqb.dqb_curinodes dq_dqb.dqb_curinodes |
95 | #define dq_btimedq_dqb.dqb_btime dq_dqb.dqb_btime |
96 | #define dq_itimedq_dqb.dqb_itime dq_dqb.dqb_itime |
97 | |
98 | /* |
99 | * If the system has never checked for a quota for this file, then it is |
100 | * set to NODQUOT. Once a write attempt is made the inode pointer is set |
101 | * to reference a dquot structure. |
102 | */ |
103 | #define NODQUOT((void *)0) NULL((void *)0) |
104 | |
105 | void dqref(struct dquot *); |
106 | void dqrele(struct vnode *, struct dquot *); |
107 | int dqsync(struct vnode *, struct dquot *); |
108 | |
109 | #ifdef DIAGNOSTIC1 |
110 | void chkdquot(struct inode *); |
111 | #endif |
112 | |
113 | int getquota(struct mount *, u_long, int, caddr_t); |
114 | int quotaon(struct proc *, struct mount *, int, caddr_t); |
115 | int setquota(struct mount *, u_long, int, caddr_t); |
116 | int setuse(struct mount *, u_long, int, caddr_t); |
117 | |
118 | int chkdqchg(struct inode *, long, struct ucred *, int); |
119 | int chkiqchg(struct inode *, long, struct ucred *, int); |
120 | |
121 | int dqget(struct vnode *, u_long, struct ufsmount *, int, |
122 | struct dquot **); |
123 | |
124 | int quotaon_vnode(struct vnode *, void *); |
125 | int quotaoff_vnode(struct vnode *, void *); |
126 | int qsync_vnode(struct vnode *, void *); |
127 | |
128 | /* |
129 | * Quota name to error message mapping. |
130 | */ |
131 | static char *quotatypes[] = INITQFNAMES{ "user", "group", "undefined", }; |
132 | |
133 | /* |
134 | * Obtain a reference to a dquot. |
135 | */ |
136 | void |
137 | dqref(struct dquot *dq) |
138 | { |
139 | dq->dq_cnt++; |
140 | } |
141 | |
142 | /* |
143 | * Set up the quotas for an inode. |
144 | * |
145 | * This routine completely defines the semantics of quotas. |
146 | * If other criterion want to be used to establish quotas, the |
147 | * MAXQUOTAS value in quotas.h should be increased, and the |
148 | * additional dquots set up here. |
149 | */ |
150 | int |
151 | getinoquota(struct inode *ip) |
152 | { |
153 | struct ufsmount *ump; |
154 | struct vnode *vp = ITOV(ip)((ip)->i_vnode); |
155 | int error; |
156 | |
157 | ump = ip->i_ump; |
158 | /* |
159 | * Set up the user quota based on file uid. |
160 | * EINVAL means that quotas are not enabled. |
161 | */ |
162 | if (ip->i_dquot[USRQUOTA0] == NODQUOT((void *)0) && |
163 | (error = |
164 | dqget(vp, DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din ->di_uid : (ip)->dinode_u.ffs2_din->di_uid), ump, USRQUOTA0, &ip->i_dquot[USRQUOTA0])) && |
165 | error != EINVAL22) |
166 | return (error); |
167 | /* |
168 | * Set up the group quota based on file gid. |
169 | * EINVAL means that quotas are not enabled. |
170 | */ |
171 | if (ip->i_dquot[GRPQUOTA1] == NODQUOT((void *)0) && |
172 | (error = |
173 | dqget(vp, DIP(ip, gid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din ->di_gid : (ip)->dinode_u.ffs2_din->di_gid), ump, GRPQUOTA1, &ip->i_dquot[GRPQUOTA1])) && |
174 | error != EINVAL22) |
175 | return (error); |
176 | return (0); |
177 | } |
178 | |
179 | /* |
180 | * Update disk usage, and take corrective action. |
181 | */ |
182 | int |
183 | ufs_quota_alloc_blocks2(struct inode *ip, daddr_t change, |
184 | struct ucred *cred, enum ufs_quota_flags flags) |
185 | { |
186 | struct dquot *dq; |
187 | int i; |
188 | int error; |
189 | |
190 | #ifdef DIAGNOSTIC1 |
191 | chkdquot(ip); |
192 | #endif |
193 | |
194 | if (change == 0) |
195 | return (0); |
196 | |
197 | if ((flags & UFS_QUOTA_FORCE) == 0 && |
198 | (cred != NOCRED((struct ucred *)-1) && cred->cr_uid != 0)) { |
199 | for (i = 0; i < MAXQUOTAS2; i++) { |
200 | if (flags & (1 << i)) |
201 | continue; |
202 | if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0)) |
203 | continue; |
204 | if ((error = chkdqchg(ip, change, cred, i)) != 0) |
205 | return (error); |
206 | } |
207 | } |
208 | for (i = 0; i < MAXQUOTAS2; i++) { |
209 | if (flags & (1 << i)) |
210 | continue; |
211 | if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0)) |
212 | continue; |
213 | while (dq->dq_flags & DQ_LOCK0x01) { |
214 | dq->dq_flags |= DQ_WANT0x02; |
215 | tsleep_nsec(dq, PINOD8+1, "chkdq", INFSLP0xffffffffffffffffULL); |
216 | } |
217 | dq->dq_curblocksdq_dqb.dqb_curblocks += change; |
218 | dq->dq_flags |= DQ_MOD0x04; |
219 | } |
220 | return (0); |
221 | } |
222 | |
223 | int |
224 | ufs_quota_free_blocks2(struct inode *ip, daddr_t change, |
225 | struct ucred *cred, enum ufs_quota_flags flags) |
226 | { |
227 | struct dquot *dq; |
228 | int i; |
229 | |
230 | #ifdef DIAGNOSTIC1 |
231 | if (!VOP_ISLOCKED(ITOV(ip)((ip)->i_vnode))) |
232 | panic ("ufs_quota_free_blocks2: vnode is not locked"); |
233 | #endif |
234 | |
235 | if (change == 0) |
236 | return (0); |
237 | |
238 | for (i = 0; i < MAXQUOTAS2; i++) { |
239 | if (flags & (1 << i)) |
240 | continue; |
241 | if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0)) |
242 | continue; |
243 | while (dq->dq_flags & DQ_LOCK0x01) { |
244 | dq->dq_flags |= DQ_WANT0x02; |
245 | tsleep_nsec(dq, PINOD8+1, "chkdq", INFSLP0xffffffffffffffffULL); |
246 | } |
247 | if (dq->dq_curblocksdq_dqb.dqb_curblocks >= change) |
248 | dq->dq_curblocksdq_dqb.dqb_curblocks -= change; |
249 | else |
250 | dq->dq_curblocksdq_dqb.dqb_curblocks = 0; |
251 | dq->dq_flags &= ~DQ_BLKS0x10; |
252 | dq->dq_flags |= DQ_MOD0x04; |
253 | } |
254 | return (0); |
255 | } |
256 | |
257 | /* |
258 | * Check for a valid change to a users allocation. |
259 | * Issue an error message if appropriate. |
260 | */ |
261 | int |
262 | chkdqchg(struct inode *ip, long change, struct ucred *cred, int type) |
263 | { |
264 | struct dquot *dq = ip->i_dquot[type]; |
265 | long ncurblocks = dq->dq_curblocksdq_dqb.dqb_curblocks + change; |
266 | |
267 | /* |
268 | * If user would exceed their hard limit, disallow space allocation. |
269 | */ |
270 | if (ncurblocks >= dq->dq_bhardlimitdq_dqb.dqb_bhardlimit && dq->dq_bhardlimitdq_dqb.dqb_bhardlimit) { |
271 | if ((dq->dq_flags & DQ_BLKS0x10) == 0 && |
272 | DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din ->di_uid : (ip)->dinode_u.ffs2_din->di_uid) == cred->cr_uid) { |
273 | uprintf("\n%s: write failed, %s disk limit reached\n", |
274 | ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname, |
275 | quotatypes[type]); |
276 | dq->dq_flags |= DQ_BLKS0x10; |
277 | } |
278 | return (EDQUOT69); |
279 | } |
280 | /* |
281 | * If user is over their soft limit for too long, disallow space |
282 | * allocation. Reset time limit as they cross their soft limit. |
283 | */ |
284 | if (ncurblocks >= dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit && dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit) { |
285 | if (dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit) { |
286 | dq->dq_btimedq_dqb.dqb_btime = gettime() + ip->i_ump->um_btime[type]; |
287 | if (DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din ->di_uid : (ip)->dinode_u.ffs2_din->di_uid) == cred->cr_uid) |
288 | uprintf("\n%s: warning, %s %s\n", |
289 | ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname, |
290 | quotatypes[type], "disk quota exceeded"); |
291 | return (0); |
292 | } |
293 | if (gettime() > dq->dq_btimedq_dqb.dqb_btime) { |
294 | if ((dq->dq_flags & DQ_BLKS0x10) == 0 && |
295 | DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din ->di_uid : (ip)->dinode_u.ffs2_din->di_uid) == cred->cr_uid) { |
296 | uprintf("\n%s: write failed, %s %s\n", |
297 | ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname, |
298 | quotatypes[type], |
299 | "disk quota exceeded for too long"); |
300 | dq->dq_flags |= DQ_BLKS0x10; |
301 | } |
302 | return (EDQUOT69); |
303 | } |
304 | } |
305 | return (0); |
306 | } |
307 | |
308 | /* |
309 | * Check the inode limit, applying corrective action. |
310 | */ |
311 | int |
312 | ufs_quota_alloc_inode2(struct inode *ip, struct ucred *cred, |
313 | enum ufs_quota_flags flags) |
314 | { |
315 | struct dquot *dq; |
316 | int i; |
317 | int error; |
318 | |
319 | #ifdef DIAGNOSTIC1 |
320 | chkdquot(ip); |
321 | #endif |
322 | |
323 | if ((flags & UFS_QUOTA_FORCE) == 0 && cred->cr_uid != 0) { |
324 | for (i = 0; i < MAXQUOTAS2; i++) { |
325 | if (flags & (1 << i)) |
326 | continue; |
327 | if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0)) |
Although the value stored to 'dq' is used in the enclosing expression, the value is never actually read from 'dq' | |
328 | continue; |
329 | if ((error = chkiqchg(ip, 1, cred, i)) != 0) |
330 | return (error); |
331 | } |
332 | } |
333 | for (i = 0; i < MAXQUOTAS2; i++) { |
334 | if (flags & (1 << i)) |
335 | continue; |
336 | if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0)) |
337 | continue; |
338 | while (dq->dq_flags & DQ_LOCK0x01) { |
339 | dq->dq_flags |= DQ_WANT0x02; |
340 | tsleep_nsec(dq, PINOD8+1, "chkiq", INFSLP0xffffffffffffffffULL); |
341 | } |
342 | dq->dq_curinodesdq_dqb.dqb_curinodes++; |
343 | dq->dq_flags |= DQ_MOD0x04; |
344 | } |
345 | return (0); |
346 | } |
347 | |
348 | int |
349 | ufs_quota_free_inode2(struct inode *ip, struct ucred *cred, |
350 | enum ufs_quota_flags flags) |
351 | { |
352 | struct dquot *dq; |
353 | int i; |
354 | |
355 | #ifdef DIAGNOSTIC1 |
356 | if (!VOP_ISLOCKED(ITOV(ip)((ip)->i_vnode))) |
357 | panic ("ufs_quota_free_blocks2: vnode is not locked"); |
358 | #endif |
359 | |
360 | for (i = 0; i < MAXQUOTAS2; i++) { |
361 | if (flags & (1 << i)) |
362 | continue; |
363 | if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0)) |
364 | continue; |
365 | while (dq->dq_flags & DQ_LOCK0x01) { |
366 | dq->dq_flags |= DQ_WANT0x02; |
367 | tsleep_nsec(dq, PINOD8+1, "chkiq", INFSLP0xffffffffffffffffULL); |
368 | } |
369 | if (dq->dq_curinodesdq_dqb.dqb_curinodes > 0) |
370 | dq->dq_curinodesdq_dqb.dqb_curinodes--; |
371 | dq->dq_flags &= ~DQ_INODS0x20; |
372 | dq->dq_flags |= DQ_MOD0x04; |
373 | } |
374 | return (0); |
375 | } |
376 | |
377 | /* |
378 | * Check for a valid change to a users allocation. |
379 | * Issue an error message if appropriate. |
380 | */ |
381 | int |
382 | chkiqchg(struct inode *ip, long change, struct ucred *cred, int type) |
383 | { |
384 | struct dquot *dq = ip->i_dquot[type]; |
385 | long ncurinodes = dq->dq_curinodesdq_dqb.dqb_curinodes + change; |
386 | |
387 | /* |
388 | * If user would exceed their hard limit, disallow inode allocation. |
389 | */ |
390 | if (ncurinodes >= dq->dq_ihardlimitdq_dqb.dqb_ihardlimit && dq->dq_ihardlimitdq_dqb.dqb_ihardlimit) { |
391 | if ((dq->dq_flags & DQ_INODS0x20) == 0 && |
392 | DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din ->di_uid : (ip)->dinode_u.ffs2_din->di_uid) == cred->cr_uid) { |
393 | uprintf("\n%s: write failed, %s inode limit reached\n", |
394 | ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname, |
395 | quotatypes[type]); |
396 | dq->dq_flags |= DQ_INODS0x20; |
397 | } |
398 | return (EDQUOT69); |
399 | } |
400 | /* |
401 | * If user is over their soft limit for too long, disallow inode |
402 | * allocation. Reset time limit as they cross their soft limit. |
403 | */ |
404 | if (ncurinodes >= dq->dq_isoftlimitdq_dqb.dqb_isoftlimit && dq->dq_isoftlimitdq_dqb.dqb_isoftlimit) { |
405 | if (dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit) { |
406 | dq->dq_itimedq_dqb.dqb_itime = gettime() + ip->i_ump->um_itime[type]; |
407 | if (DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din ->di_uid : (ip)->dinode_u.ffs2_din->di_uid) == cred->cr_uid) |
408 | uprintf("\n%s: warning, %s %s\n", |
409 | ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname, |
410 | quotatypes[type], "inode quota exceeded"); |
411 | return (0); |
412 | } |
413 | if (gettime() > dq->dq_itimedq_dqb.dqb_itime) { |
414 | if ((dq->dq_flags & DQ_INODS0x20) == 0 && |
415 | DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din ->di_uid : (ip)->dinode_u.ffs2_din->di_uid) == cred->cr_uid) { |
416 | uprintf("\n%s: write failed, %s %s\n", |
417 | ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname, |
418 | quotatypes[type], |
419 | "inode quota exceeded for too long"); |
420 | dq->dq_flags |= DQ_INODS0x20; |
421 | } |
422 | return (EDQUOT69); |
423 | } |
424 | } |
425 | return (0); |
426 | } |
427 | |
428 | #ifdef DIAGNOSTIC1 |
429 | /* |
430 | * On filesystems with quotas enabled, it is an error for a file to change |
431 | * size and not to have a dquot structure associated with it. |
432 | */ |
433 | void |
434 | chkdquot(struct inode *ip) |
435 | { |
436 | struct ufsmount *ump = ip->i_ump; |
437 | int i; |
438 | struct vnode *vp = ITOV(ip)((ip)->i_vnode); |
439 | |
440 | if (!VOP_ISLOCKED(vp)) |
441 | panic ("chkdquot: vnode is not locked"); |
442 | |
443 | for (i = 0; i < MAXQUOTAS2; i++) { |
444 | if (ump->um_quotas[i] == NULLVP((struct vnode *)((void *)0)) || |
445 | (ump->um_qflags[i] & (QTF_OPENING0x01|QTF_CLOSING0x02))) |
446 | continue; |
447 | if (ip->i_dquot[i] == NODQUOT((void *)0)) { |
448 | vprint("chkdquot: missing dquot", ITOV(ip)((ip)->i_vnode)); |
449 | panic("missing dquot"); |
450 | } |
451 | } |
452 | } |
453 | #endif |
454 | |
455 | /* |
456 | * Code to process quotactl commands. |
457 | */ |
458 | |
459 | int |
460 | quotaon_vnode(struct vnode *vp, void *arg) |
461 | { |
462 | int error; |
463 | |
464 | if (vp->v_type == VNON || vp->v_writecount == 0) |
465 | return (0); |
466 | |
467 | if (vget(vp, LK_EXCLUSIVE0x0001UL)) { |
468 | return (0); |
469 | } |
470 | |
471 | error = getinoquota(VTOI(vp)((struct inode *)(vp)->v_data)); |
472 | vput(vp); |
473 | |
474 | return (error); |
475 | } |
476 | |
477 | /* |
478 | * Q_QUOTAON - set up a quota file for a particular file system. |
479 | */ |
480 | int |
481 | quotaon(struct proc *p, struct mount *mp, int type, caddr_t fname) |
482 | { |
483 | struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data)); |
484 | struct vnode *vp, **vpp; |
485 | struct dquot *dq; |
486 | int error; |
487 | struct nameidata nd; |
488 | |
489 | #ifdef DIAGNOSTIC1 |
490 | if (!vfs_isbusy(mp)) |
491 | panic ("quotaon: mount point not busy"); |
492 | #endif |
493 | |
494 | vpp = &ump->um_quotas[type]; |
495 | NDINIT(&nd, 0, 0, UIO_USERSPACE, fname, p)ndinitat(&nd, 0, 0, UIO_USERSPACE, -100, fname, p); |
496 | if ((error = vn_open(&nd, FREAD0x0001|FWRITE0x0002, 0)) != 0) |
497 | return (error); |
498 | vp = nd.ni_vp; |
499 | VOP_UNLOCK(vp); |
500 | if (vp->v_type != VREG) { |
501 | (void) vn_close(vp, FREAD0x0001|FWRITE0x0002, p->p_ucred, p); |
502 | return (EACCES13); |
503 | } |
504 | |
505 | /* |
506 | * Update the vnode and ucred for quota file updates |
507 | */ |
508 | if (*vpp != vp) { |
509 | quotaoff(p, mp, type); |
510 | *vpp = vp; |
511 | crhold(p->p_ucred); |
512 | ump->um_cred[type] = p->p_ucred; |
513 | } else { |
514 | struct ucred *ocred = ump->um_cred[type]; |
515 | |
516 | (void) vn_close(vp, FREAD0x0001|FWRITE0x0002, ocred, p); |
517 | if (ocred != p->p_ucred) { |
518 | crhold(p->p_ucred); |
519 | ump->um_cred[type] = p->p_ucred; |
520 | crfree(ocred); |
521 | } |
522 | } |
523 | |
524 | ump->um_qflags[type] |= QTF_OPENING0x01; |
525 | mp->mnt_flag |= MNT_QUOTA0x00002000; |
526 | vp->v_flag |= VSYSTEM0x0004; |
527 | /* |
528 | * Set up the time limits for this quota. |
529 | */ |
530 | ump->um_btime[type] = MAX_DQ_TIME(7*24*60*60); |
531 | ump->um_itime[type] = MAX_IQ_TIME(7*24*60*60); |
532 | if (dqget(NULLVP((struct vnode *)((void *)0)), 0, ump, type, &dq) == 0) { |
533 | if (dq->dq_btimedq_dqb.dqb_btime > 0) |
534 | ump->um_btime[type] = dq->dq_btimedq_dqb.dqb_btime; |
535 | if (dq->dq_itimedq_dqb.dqb_itime > 0) |
536 | ump->um_itime[type] = dq->dq_itimedq_dqb.dqb_itime; |
537 | dqrele(NULLVP((struct vnode *)((void *)0)), dq); |
538 | } |
539 | /* |
540 | * Search vnodes associated with this mount point, |
541 | * adding references to quota file being opened. |
542 | * NB: only need to add dquot's for inodes being modified. |
543 | */ |
544 | error = vfs_mount_foreach_vnode(mp, quotaon_vnode, NULL((void *)0)); |
545 | |
546 | ump->um_qflags[type] &= ~QTF_OPENING0x01; |
547 | if (error) |
548 | quotaoff(p, mp, type); |
549 | return (error); |
550 | } |
551 | |
552 | struct quotaoff_arg { |
553 | struct proc *p; |
554 | int type; |
555 | }; |
556 | |
557 | int |
558 | quotaoff_vnode(struct vnode *vp, void *arg) |
559 | { |
560 | struct quotaoff_arg *qa = (struct quotaoff_arg *)arg; |
561 | struct inode *ip; |
562 | struct dquot *dq; |
563 | |
564 | if (vp->v_type == VNON) |
565 | return (0); |
566 | |
567 | if (vget(vp, LK_EXCLUSIVE0x0001UL)) |
568 | return (0); |
569 | ip = VTOI(vp)((struct inode *)(vp)->v_data); |
570 | dq = ip->i_dquot[qa->type]; |
571 | ip->i_dquot[qa->type] = NODQUOT((void *)0); |
572 | dqrele(vp, dq); |
573 | vput(vp); |
574 | return (0); |
575 | } |
576 | |
577 | /* |
578 | * Q_QUOTAOFF - turn off disk quotas for a filesystem. |
579 | */ |
580 | int |
581 | quotaoff(struct proc *p, struct mount *mp, int type) |
582 | { |
583 | struct vnode *qvp; |
584 | struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data)); |
585 | struct quotaoff_arg qa; |
586 | int error; |
587 | |
588 | #ifdef DIAGNOSTIC1 |
589 | if (!vfs_isbusy(mp)) |
590 | panic ("quotaoff: mount point not busy"); |
591 | #endif |
592 | if ((qvp = ump->um_quotas[type]) == NULLVP((struct vnode *)((void *)0))) |
593 | return (0); |
594 | ump->um_qflags[type] |= QTF_CLOSING0x02; |
595 | /* |
596 | * Search vnodes associated with this mount point, |
597 | * deleting any references to quota file being closed. |
598 | */ |
599 | qa.p = p; |
600 | qa.type = type; |
601 | vfs_mount_foreach_vnode(mp, quotaoff_vnode, &qa); |
602 | |
603 | error = vn_close(qvp, FREAD0x0001|FWRITE0x0002, p->p_ucred, p); |
604 | ump->um_quotas[type] = NULLVP((struct vnode *)((void *)0)); |
605 | crfree(ump->um_cred[type]); |
606 | ump->um_cred[type] = NOCRED((struct ucred *)-1); |
607 | ump->um_qflags[type] &= ~QTF_CLOSING0x02; |
608 | for (type = 0; type < MAXQUOTAS2; type++) |
609 | if (ump->um_quotas[type] != NULLVP((struct vnode *)((void *)0))) |
610 | break; |
611 | if (type == MAXQUOTAS2) |
612 | mp->mnt_flag &= ~MNT_QUOTA0x00002000; |
613 | return (error); |
614 | } |
615 | |
616 | /* |
617 | * Q_GETQUOTA - return current values in a dqblk structure. |
618 | */ |
619 | int |
620 | getquota(struct mount *mp, u_long id, int type, caddr_t addr) |
621 | { |
622 | struct dquot *dq; |
623 | int error; |
624 | |
625 | if ((error = dqget(NULLVP((struct vnode *)((void *)0)), id, VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data)), type, &dq)) != 0) |
626 | return (error); |
627 | error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk)); |
628 | #ifdef KTRACE1 |
629 | if (error == 0) { |
630 | struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc; |
631 | if (KTRPOINT(p, KTR_STRUCT)((p)->p_p->ps_traceflag & (1<<(8)) && ((p)->p_flag & 0x00000001) == 0)) |
632 | ktrquota(p, &dq->dq_dqb)ktrstruct((p), "quota", (&dq->dq_dqb), sizeof(struct dqblk )); |
633 | } |
634 | #endif |
635 | |
636 | dqrele(NULLVP((struct vnode *)((void *)0)), dq); |
637 | return (error); |
638 | } |
639 | |
640 | /* |
641 | * Q_SETQUOTA - assign an entire dqblk structure. |
642 | */ |
643 | int |
644 | setquota(struct mount *mp, u_long id, int type, caddr_t addr) |
645 | { |
646 | struct dquot *dq; |
647 | struct dquot *ndq; |
648 | struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data)); |
649 | struct dqblk newlim; |
650 | int error; |
651 | |
652 | error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)); |
653 | if (error) |
654 | return (error); |
655 | #ifdef KTRACE1 |
656 | { |
657 | struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc; |
658 | if (KTRPOINT(p, KTR_STRUCT)((p)->p_p->ps_traceflag & (1<<(8)) && ((p)->p_flag & 0x00000001) == 0)) |
659 | ktrquota(p, &newlim)ktrstruct((p), "quota", (&newlim), sizeof(struct dqblk)); |
660 | } |
661 | #endif |
662 | |
663 | if ((error = dqget(NULLVP((struct vnode *)((void *)0)), id, ump, type, &ndq)) != 0) |
664 | return (error); |
665 | dq = ndq; |
666 | while (dq->dq_flags & DQ_LOCK0x01) { |
667 | dq->dq_flags |= DQ_WANT0x02; |
668 | tsleep_nsec(dq, PINOD8+1, "setquota", INFSLP0xffffffffffffffffULL); |
669 | } |
670 | /* |
671 | * Copy all but the current values. |
672 | * Reset time limit if previously had no soft limit or were |
673 | * under it, but now have a soft limit and are over it. |
674 | */ |
675 | newlim.dqb_curblocks = dq->dq_curblocksdq_dqb.dqb_curblocks; |
676 | newlim.dqb_curinodes = dq->dq_curinodesdq_dqb.dqb_curinodes; |
677 | if (dq->dq_id != 0) { |
678 | newlim.dqb_btime = dq->dq_btimedq_dqb.dqb_btime; |
679 | newlim.dqb_itime = dq->dq_itimedq_dqb.dqb_itime; |
680 | } |
681 | if (newlim.dqb_bsoftlimit && |
682 | dq->dq_curblocksdq_dqb.dqb_curblocks >= newlim.dqb_bsoftlimit && |
683 | (dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit == 0 || dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit)) |
684 | newlim.dqb_btime = gettime() + ump->um_btime[type]; |
685 | if (newlim.dqb_isoftlimit && |
686 | dq->dq_curinodesdq_dqb.dqb_curinodes >= newlim.dqb_isoftlimit && |
687 | (dq->dq_isoftlimitdq_dqb.dqb_isoftlimit == 0 || dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit)) |
688 | newlim.dqb_itime = gettime() + ump->um_itime[type]; |
689 | dq->dq_dqb = newlim; |
690 | if (dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit) |
691 | dq->dq_flags &= ~DQ_BLKS0x10; |
692 | if (dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit) |
693 | dq->dq_flags &= ~DQ_INODS0x20; |
694 | if (dq->dq_isoftlimitdq_dqb.dqb_isoftlimit == 0 && dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit == 0 && |
695 | dq->dq_ihardlimitdq_dqb.dqb_ihardlimit == 0 && dq->dq_bhardlimitdq_dqb.dqb_bhardlimit == 0) |
696 | dq->dq_flags |= DQ_FAKE0x08; |
697 | else |
698 | dq->dq_flags &= ~DQ_FAKE0x08; |
699 | dq->dq_flags |= DQ_MOD0x04; |
700 | dqrele(NULLVP((struct vnode *)((void *)0)), dq); |
701 | return (0); |
702 | } |
703 | |
704 | /* |
705 | * Q_SETUSE - set current inode and block usage. |
706 | */ |
707 | int |
708 | setuse(struct mount *mp, u_long id, int type, caddr_t addr) |
709 | { |
710 | struct dquot *dq; |
711 | struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data)); |
712 | struct dquot *ndq; |
713 | struct dqblk usage; |
714 | int error; |
715 | |
716 | error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)); |
717 | if (error) |
718 | return (error); |
719 | #ifdef KTRACE1 |
720 | { |
721 | struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc; |
722 | if (KTRPOINT(p, KTR_STRUCT)((p)->p_p->ps_traceflag & (1<<(8)) && ((p)->p_flag & 0x00000001) == 0)) |
723 | ktrquota(p, &usage)ktrstruct((p), "quota", (&usage), sizeof(struct dqblk)); |
724 | } |
725 | #endif |
726 | |
727 | if ((error = dqget(NULLVP((struct vnode *)((void *)0)), id, ump, type, &ndq)) != 0) |
728 | return (error); |
729 | dq = ndq; |
730 | while (dq->dq_flags & DQ_LOCK0x01) { |
731 | dq->dq_flags |= DQ_WANT0x02; |
732 | tsleep_nsec(dq, PINOD8+1, "setuse", INFSLP0xffffffffffffffffULL); |
733 | } |
734 | /* |
735 | * Reset time limit if have a soft limit and were |
736 | * previously under it, but are now over it. |
737 | */ |
738 | if (dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit && dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit && |
739 | usage.dqb_curblocks >= dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit) |
740 | dq->dq_btimedq_dqb.dqb_btime = gettime() + ump->um_btime[type]; |
741 | if (dq->dq_isoftlimitdq_dqb.dqb_isoftlimit && dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit && |
742 | usage.dqb_curinodes >= dq->dq_isoftlimitdq_dqb.dqb_isoftlimit) |
743 | dq->dq_itimedq_dqb.dqb_itime = gettime() + ump->um_itime[type]; |
744 | dq->dq_curblocksdq_dqb.dqb_curblocks = usage.dqb_curblocks; |
745 | dq->dq_curinodesdq_dqb.dqb_curinodes = usage.dqb_curinodes; |
746 | if (dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit) |
747 | dq->dq_flags &= ~DQ_BLKS0x10; |
748 | if (dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit) |
749 | dq->dq_flags &= ~DQ_INODS0x20; |
750 | dq->dq_flags |= DQ_MOD0x04; |
751 | dqrele(NULLVP((struct vnode *)((void *)0)), dq); |
752 | return (0); |
753 | } |
754 | |
755 | int |
756 | qsync_vnode(struct vnode *vp, void *arg) |
757 | { |
758 | int i; |
759 | struct dquot *dq; |
760 | |
761 | if (vp->v_type == VNON) |
762 | return (0); |
763 | |
764 | if (vget(vp, LK_EXCLUSIVE0x0001UL | LK_NOWAIT0x0040UL)) |
765 | return (0); |
766 | |
767 | for (i = 0; i < MAXQUOTAS2; i++) { |
768 | dq = VTOI(vp)((struct inode *)(vp)->v_data)->i_dquot[i]; |
769 | if (dq != NODQUOT((void *)0) && (dq->dq_flags & DQ_MOD0x04)) |
770 | dqsync(vp, dq); |
771 | } |
772 | vput(vp); |
773 | return (0); |
774 | } |
775 | |
776 | /* |
777 | * Q_SYNC - sync quota files to disk. |
778 | */ |
779 | int |
780 | qsync(struct mount *mp) |
781 | { |
782 | struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data)); |
783 | int i; |
784 | |
785 | /* |
786 | * Check if the mount point has any quotas. |
787 | * If not, simply return. |
788 | */ |
789 | for (i = 0; i < MAXQUOTAS2; i++) |
790 | if (ump->um_quotas[i] != NULLVP((struct vnode *)((void *)0))) |
791 | break; |
792 | if (i == MAXQUOTAS2) |
793 | return (0); |
794 | /* |
795 | * Search vnodes associated with this mount point, |
796 | * synchronizing any modified dquot structures. |
797 | */ |
798 | vfs_mount_foreach_vnode(mp, qsync_vnode, NULL((void *)0)); |
799 | return (0); |
800 | } |
801 | |
802 | /* |
803 | * Code pertaining to management of the in-core dquot data structures. |
804 | */ |
805 | LIST_HEAD(dqhash, dquot)struct dqhash { struct dquot *lh_first; } *dqhashtbl; |
806 | SIPHASH_KEY dqhashkey; |
807 | u_long dqhash; |
808 | |
809 | /* |
810 | * Dquot free list. |
811 | */ |
812 | #define DQUOTINC5 5 /* minimum free dquots desired */ |
813 | TAILQ_HEAD(dqfreelist, dquot)struct dqfreelist { struct dquot *tqh_first; struct dquot **tqh_last ; } dqfreelist; |
814 | long numdquot, desireddquot = DQUOTINC5; |
815 | |
816 | /* |
817 | * Initialize the quota system. |
818 | */ |
819 | void |
820 | ufs_quota_init(void) |
821 | { |
822 | dqhashtbl = hashinit(initialvnodes, M_DQUOT27, M_WAITOK0x0001, &dqhash); |
823 | arc4random_buf(&dqhashkey, sizeof(dqhashkey)); |
824 | TAILQ_INIT(&dqfreelist)do { (&dqfreelist)->tqh_first = ((void *)0); (&dqfreelist )->tqh_last = &(&dqfreelist)->tqh_first; } while (0); |
825 | } |
826 | |
827 | /* |
828 | * Obtain a dquot structure for the specified identifier and quota file |
829 | * reading the information from the file if necessary. |
830 | */ |
831 | int |
832 | dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type, |
833 | struct dquot **dqp) |
834 | { |
835 | SIPHASH_CTX ctx; |
836 | struct dquot *dq; |
837 | struct dqhash *dqh; |
838 | struct vnode *dqvp; |
839 | struct iovec aiov; |
840 | struct uio auio; |
841 | int error; |
842 | |
843 | dqvp = ump->um_quotas[type]; |
844 | if (dqvp == NULLVP((struct vnode *)((void *)0)) || (ump->um_qflags[type] & QTF_CLOSING0x02)) { |
845 | *dqp = NODQUOT((void *)0); |
846 | return (EINVAL22); |
847 | } |
848 | /* |
849 | * Check the cache first. |
850 | */ |
851 | SipHash24_Init(&ctx, &dqhashkey)SipHash_Init((&ctx), (&dqhashkey)); |
852 | SipHash24_Update(&ctx, &dqvp, sizeof(dqvp))SipHash_Update((&ctx), 2, 4, (&dqvp), (sizeof(dqvp))); |
853 | SipHash24_Update(&ctx, &id, sizeof(id))SipHash_Update((&ctx), 2, 4, (&id), (sizeof(id))); |
854 | dqh = &dqhashtbl[SipHash24_End(&ctx)SipHash_End((&ctx), 2, 4) & dqhash]; |
855 | |
856 | LIST_FOREACH(dq, dqh, dq_hash)for((dq) = ((dqh)->lh_first); (dq)!= ((void *)0); (dq) = ( (dq)->dq_hash.le_next)) { |
857 | if (dq->dq_id != id || |
858 | dq->dq_vp != dqvp) |
859 | continue; |
860 | /* |
861 | * Cache hit with no references. Take |
862 | * the structure off the free list. |
863 | */ |
864 | if (dq->dq_cnt == 0) |
865 | TAILQ_REMOVE(&dqfreelist, dq, dq_freelist)do { if (((dq)->dq_freelist.tqe_next) != ((void *)0)) (dq) ->dq_freelist.tqe_next->dq_freelist.tqe_prev = (dq)-> dq_freelist.tqe_prev; else (&dqfreelist)->tqh_last = ( dq)->dq_freelist.tqe_prev; *(dq)->dq_freelist.tqe_prev = (dq)->dq_freelist.tqe_next; ((dq)->dq_freelist.tqe_prev ) = ((void *)-1); ((dq)->dq_freelist.tqe_next) = ((void *) -1); } while (0); |
866 | dqref(dq); |
867 | *dqp = dq; |
868 | return (0); |
869 | } |
870 | /* |
871 | * Not in cache, allocate a new one. |
872 | */ |
873 | if (TAILQ_FIRST(&dqfreelist)((&dqfreelist)->tqh_first) == NODQUOT((void *)0) && |
874 | numdquot < MAXQUOTAS2 * initialvnodes) |
875 | desireddquot += DQUOTINC5; |
876 | if (numdquot < desireddquot) { |
877 | dq = malloc(sizeof *dq, M_DQUOT27, M_WAITOK0x0001 | M_ZERO0x0008); |
878 | numdquot++; |
879 | } else { |
880 | if ((dq = TAILQ_FIRST(&dqfreelist)((&dqfreelist)->tqh_first)) == NULL((void *)0)) { |
881 | tablefull("dquot"); |
882 | *dqp = NODQUOT((void *)0); |
883 | return (EUSERS68); |
884 | } |
885 | if (dq->dq_cnt || (dq->dq_flags & DQ_MOD0x04)) |
886 | panic("free dquot isn't"); |
887 | TAILQ_REMOVE(&dqfreelist, dq, dq_freelist)do { if (((dq)->dq_freelist.tqe_next) != ((void *)0)) (dq) ->dq_freelist.tqe_next->dq_freelist.tqe_prev = (dq)-> dq_freelist.tqe_prev; else (&dqfreelist)->tqh_last = ( dq)->dq_freelist.tqe_prev; *(dq)->dq_freelist.tqe_prev = (dq)->dq_freelist.tqe_next; ((dq)->dq_freelist.tqe_prev ) = ((void *)-1); ((dq)->dq_freelist.tqe_next) = ((void *) -1); } while (0); |
888 | LIST_REMOVE(dq, dq_hash)do { if ((dq)->dq_hash.le_next != ((void *)0)) (dq)->dq_hash .le_next->dq_hash.le_prev = (dq)->dq_hash.le_prev; *(dq )->dq_hash.le_prev = (dq)->dq_hash.le_next; ((dq)->dq_hash .le_prev) = ((void *)-1); ((dq)->dq_hash.le_next) = ((void *)-1); } while (0); |
889 | crfree(dq->dq_cred); |
890 | dq->dq_cred = NOCRED((struct ucred *)-1); |
891 | } |
892 | /* |
893 | * Initialize the contents of the dquot structure. |
894 | */ |
895 | if (vp != dqvp) |
896 | vn_lock(dqvp, LK_EXCLUSIVE0x0001UL | LK_RETRY0x2000UL); |
897 | LIST_INSERT_HEAD(dqh, dq, dq_hash)do { if (((dq)->dq_hash.le_next = (dqh)->lh_first) != ( (void *)0)) (dqh)->lh_first->dq_hash.le_prev = &(dq )->dq_hash.le_next; (dqh)->lh_first = (dq); (dq)->dq_hash .le_prev = &(dqh)->lh_first; } while (0); |
898 | dqref(dq); |
899 | dq->dq_flags = DQ_LOCK0x01; |
900 | dq->dq_id = id; |
901 | dq->dq_vp = dqvp; |
902 | dq->dq_type = type; |
903 | crhold(ump->um_cred[type]); |
904 | dq->dq_cred = ump->um_cred[type]; |
905 | auio.uio_iov = &aiov; |
906 | auio.uio_iovcnt = 1; |
907 | aiov.iov_base = (caddr_t)&dq->dq_dqb; |
908 | aiov.iov_len = sizeof (struct dqblk); |
909 | auio.uio_resid = sizeof (struct dqblk); |
910 | auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); |
911 | auio.uio_segflg = UIO_SYSSPACE; |
912 | auio.uio_rw = UIO_READ; |
913 | auio.uio_procp = NULL((void *)0); |
914 | error = VOP_READ(dqvp, &auio, 0, dq->dq_cred); |
915 | if (auio.uio_resid == sizeof(struct dqblk) && error == 0) |
916 | memset(&dq->dq_dqb, 0, sizeof(struct dqblk))__builtin_memset((&dq->dq_dqb), (0), (sizeof(struct dqblk ))); |
917 | if (vp != dqvp) |
918 | VOP_UNLOCK(dqvp); |
919 | if (dq->dq_flags & DQ_WANT0x02) |
920 | wakeup(dq); |
921 | dq->dq_flags = 0; |
922 | /* |
923 | * I/O error in reading quota file, release |
924 | * quota structure and reflect problem to caller. |
925 | */ |
926 | if (error) { |
927 | LIST_REMOVE(dq, dq_hash)do { if ((dq)->dq_hash.le_next != ((void *)0)) (dq)->dq_hash .le_next->dq_hash.le_prev = (dq)->dq_hash.le_prev; *(dq )->dq_hash.le_prev = (dq)->dq_hash.le_next; ((dq)->dq_hash .le_prev) = ((void *)-1); ((dq)->dq_hash.le_next) = ((void *)-1); } while (0); |
928 | dqrele(vp, dq); |
929 | *dqp = NODQUOT((void *)0); |
930 | return (error); |
931 | } |
932 | /* |
933 | * Check for no limit to enforce. |
934 | * Initialize time values if necessary. |
935 | */ |
936 | if (dq->dq_isoftlimitdq_dqb.dqb_isoftlimit == 0 && dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit == 0 && |
937 | dq->dq_ihardlimitdq_dqb.dqb_ihardlimit == 0 && dq->dq_bhardlimitdq_dqb.dqb_bhardlimit == 0) |
938 | dq->dq_flags |= DQ_FAKE0x08; |
939 | if (dq->dq_id != 0) { |
940 | if (dq->dq_btimedq_dqb.dqb_btime == 0) |
941 | dq->dq_btimedq_dqb.dqb_btime = gettime() + ump->um_btime[type]; |
942 | if (dq->dq_itimedq_dqb.dqb_itime == 0) |
943 | dq->dq_itimedq_dqb.dqb_itime = gettime() + ump->um_itime[type]; |
944 | } |
945 | *dqp = dq; |
946 | return (0); |
947 | } |
948 | |
949 | /* |
950 | * Release a reference to a dquot. |
951 | */ |
952 | void |
953 | dqrele(struct vnode *vp, struct dquot *dq) |
954 | { |
955 | |
956 | if (dq == NODQUOT((void *)0)) |
957 | return; |
958 | if (dq->dq_cnt > 1) { |
959 | dq->dq_cnt--; |
960 | return; |
961 | } |
962 | if (dq->dq_flags & DQ_MOD0x04) |
963 | (void) dqsync(vp, dq); |
964 | if (--dq->dq_cnt > 0) |
965 | return; |
966 | TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist)do { (dq)->dq_freelist.tqe_next = ((void *)0); (dq)->dq_freelist .tqe_prev = (&dqfreelist)->tqh_last; *(&dqfreelist )->tqh_last = (dq); (&dqfreelist)->tqh_last = & (dq)->dq_freelist.tqe_next; } while (0); |
967 | } |
968 | |
969 | /* |
970 | * Update the disk quota in the quota file. |
971 | */ |
972 | int |
973 | dqsync(struct vnode *vp, struct dquot *dq) |
974 | { |
975 | struct vnode *dqvp; |
976 | struct iovec aiov; |
977 | struct uio auio; |
978 | int error; |
979 | |
980 | if (dq == NODQUOT((void *)0)) |
981 | panic("dqsync: dquot"); |
982 | if ((dq->dq_flags & DQ_MOD0x04) == 0) |
983 | return (0); |
984 | if ((dqvp = dq->dq_vp) == NULLVP((struct vnode *)((void *)0))) |
985 | panic("dqsync: file"); |
986 | |
987 | if (vp != dqvp) |
988 | vn_lock(dqvp, LK_EXCLUSIVE0x0001UL | LK_RETRY0x2000UL); |
989 | while (dq->dq_flags & DQ_LOCK0x01) { |
990 | dq->dq_flags |= DQ_WANT0x02; |
991 | tsleep_nsec(dq, PINOD8+2, "dqsync", INFSLP0xffffffffffffffffULL); |
992 | if ((dq->dq_flags & DQ_MOD0x04) == 0) { |
993 | if (vp != dqvp) |
994 | VOP_UNLOCK(dqvp); |
995 | return (0); |
996 | } |
997 | } |
998 | dq->dq_flags |= DQ_LOCK0x01; |
999 | auio.uio_iov = &aiov; |
1000 | auio.uio_iovcnt = 1; |
1001 | aiov.iov_base = (caddr_t)&dq->dq_dqb; |
1002 | aiov.iov_len = sizeof (struct dqblk); |
1003 | auio.uio_resid = sizeof (struct dqblk); |
1004 | auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); |
1005 | auio.uio_segflg = UIO_SYSSPACE; |
1006 | auio.uio_rw = UIO_WRITE; |
1007 | auio.uio_procp = NULL((void *)0); |
1008 | error = VOP_WRITE(dqvp, &auio, 0, dq->dq_cred); |
1009 | if (auio.uio_resid && error == 0) |
1010 | error = EIO5; |
1011 | if (dq->dq_flags & DQ_WANT0x02) |
1012 | wakeup(dq); |
1013 | dq->dq_flags &= ~(DQ_MOD0x04|DQ_LOCK0x01|DQ_WANT0x02); |
1014 | if (vp != dqvp) |
1015 | VOP_UNLOCK(dqvp); |
1016 | return (error); |
1017 | } |
1018 | |
1019 | int |
1020 | ufs_quota_delete(struct inode *ip) |
1021 | { |
1022 | struct vnode *vp = ITOV(ip)((ip)->i_vnode); |
1023 | int i; |
1024 | for (i = 0; i < MAXQUOTAS2; i++) { |
1025 | if (ip->i_dquot[i] != NODQUOT((void *)0)) { |
1026 | dqrele(vp, ip->i_dquot[i]); |
1027 | ip->i_dquot[i] = NODQUOT((void *)0); |
1028 | } |
1029 | } |
1030 | |
1031 | return (0); |
1032 | } |
1033 | |
1034 | /* |
1035 | * Do operations associated with quotas |
1036 | */ |
1037 | int |
1038 | ufs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg, |
1039 | struct proc *p) |
1040 | { |
1041 | int cmd, type, error; |
1042 | |
1043 | if (uid == -1) |
1044 | uid = p->p_ucred->cr_ruid; |
1045 | cmd = cmds >> SUBCMDSHIFT8; |
1046 | |
1047 | switch (cmd) { |
1048 | case Q_SYNC0x0600: |
1049 | break; |
1050 | case Q_GETQUOTA0x0300: |
1051 | if (uid == p->p_ucred->cr_ruid) |
1052 | break; |
1053 | /* FALLTHROUGH */ |
1054 | default: |
1055 | if ((error = suser(p)) != 0) |
1056 | return (error); |
1057 | } |
1058 | |
1059 | type = cmds & SUBCMDMASK0x00ff; |
1060 | if ((u_int)type >= MAXQUOTAS2) |
1061 | return (EINVAL22); |
1062 | |
1063 | if (vfs_busy(mp, VB_READ0x01|VB_NOWAIT0x04)) |
1064 | return (0); |
1065 | |
1066 | |
1067 | switch (cmd) { |
1068 | |
1069 | case Q_QUOTAON0x0100: |
1070 | error = quotaon(p, mp, type, arg); |
1071 | break; |
1072 | |
1073 | case Q_QUOTAOFF0x0200: |
1074 | error = quotaoff(p, mp, type); |
1075 | break; |
1076 | |
1077 | case Q_SETQUOTA0x0400: |
1078 | error = setquota(mp, uid, type, arg) ; |
1079 | break; |
1080 | |
1081 | case Q_SETUSE0x0500: |
1082 | error = setuse(mp, uid, type, arg); |
1083 | break; |
1084 | |
1085 | case Q_GETQUOTA0x0300: |
1086 | error = getquota(mp, uid, type, arg); |
1087 | break; |
1088 | |
1089 | case Q_SYNC0x0600: |
1090 | error = qsync(mp); |
1091 | break; |
1092 | |
1093 | default: |
1094 | error = EINVAL22; |
1095 | break; |
1096 | } |
1097 | |
1098 | vfs_unbusy(mp); |
1099 | return (error); |
1100 | } |