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) dq_hash; /* hash list */
66 TAILQ_ENTRY(dquot) 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_LOCK 0x01 /* this quota locked (no MODS) */
80 #define DQ_WANT 0x02 /* wakeup on unlock */
81 #define DQ_MOD 0x04 /* this quota modified since read */
82 #define DQ_FAKE 0x08 /* no limits here, just usage */
83 #define DQ_BLKS 0x10 /* has been warned about blk limit */
84 #define DQ_INODS 0x20 /* has been warned about inode limit */
85
86 /*
87 * Shorthand notation.
88 */
89 #define dq_bhardlimit dq_dqb.dqb_bhardlimit
90 #define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
91 #define dq_curblocks dq_dqb.dqb_curblocks
92 #define dq_ihardlimit dq_dqb.dqb_ihardlimit
93 #define dq_isoftlimit dq_dqb.dqb_isoftlimit
94 #define dq_curinodes dq_dqb.dqb_curinodes
95 #define dq_btime dq_dqb.dqb_btime
96 #define dq_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 NULL
104
105 void dqref(struct dquot *);
106 void dqrele(struct vnode *, struct dquot *);
107 int dqsync(struct vnode *, struct dquot *);
108
109 #ifdef DIAGNOSTIC
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;
132
133 /*
134 * Obtain a reference to a dquot.
135 */
136 void
dqref(struct dquot * dq)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
getinoquota(struct inode * ip)151 getinoquota(struct inode *ip)
152 {
153 struct ufsmount *ump;
154 struct vnode *vp = ITOV(ip);
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[USRQUOTA] == NODQUOT &&
163 (error =
164 dqget(vp, DIP(ip, uid), ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
165 error != EINVAL)
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[GRPQUOTA] == NODQUOT &&
172 (error =
173 dqget(vp, DIP(ip, gid), ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
174 error != EINVAL)
175 return (error);
176 return (0);
177 }
178
179 /*
180 * Update disk usage, and take corrective action.
181 */
182 int
ufs_quota_alloc_blocks2(struct inode * ip,daddr_t change,struct ucred * cred,enum ufs_quota_flags flags)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 DIAGNOSTIC
191 chkdquot(ip);
192 #endif
193
194 if (change == 0)
195 return (0);
196
197 if ((flags & UFS_QUOTA_FORCE) == 0 &&
198 (cred != NOCRED && cred->cr_uid != 0)) {
199 for (i = 0; i < MAXQUOTAS; i++) {
200 if (flags & (1 << i))
201 continue;
202 if ((dq = ip->i_dquot[i]) == NODQUOT)
203 continue;
204 if ((error = chkdqchg(ip, change, cred, i)) != 0)
205 return (error);
206 }
207 }
208 for (i = 0; i < MAXQUOTAS; i++) {
209 if (flags & (1 << i))
210 continue;
211 if ((dq = ip->i_dquot[i]) == NODQUOT)
212 continue;
213 while (dq->dq_flags & DQ_LOCK) {
214 dq->dq_flags |= DQ_WANT;
215 tsleep_nsec(dq, PINOD+1, "chkdq", INFSLP);
216 }
217 dq->dq_curblocks += change;
218 dq->dq_flags |= DQ_MOD;
219 }
220 return (0);
221 }
222
223 int
ufs_quota_free_blocks2(struct inode * ip,daddr_t change,struct ucred * cred,enum ufs_quota_flags flags)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 DIAGNOSTIC
231 if (!VOP_ISLOCKED(ITOV(ip)))
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 < MAXQUOTAS; i++) {
239 if (flags & (1 << i))
240 continue;
241 if ((dq = ip->i_dquot[i]) == NODQUOT)
242 continue;
243 while (dq->dq_flags & DQ_LOCK) {
244 dq->dq_flags |= DQ_WANT;
245 tsleep_nsec(dq, PINOD+1, "chkdq", INFSLP);
246 }
247 if (dq->dq_curblocks >= change)
248 dq->dq_curblocks -= change;
249 else
250 dq->dq_curblocks = 0;
251 dq->dq_flags &= ~DQ_BLKS;
252 dq->dq_flags |= DQ_MOD;
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
chkdqchg(struct inode * ip,long change,struct ucred * cred,int type)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_curblocks + change;
266
267 /*
268 * If user would exceed their hard limit, disallow space allocation.
269 */
270 if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
271 if ((dq->dq_flags & DQ_BLKS) == 0 &&
272 DIP(ip, uid) == cred->cr_uid) {
273 uprintf("\n%s: write failed, %s disk limit reached\n",
274 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
275 quotatypes[type]);
276 dq->dq_flags |= DQ_BLKS;
277 }
278 return (EDQUOT);
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_bsoftlimit && dq->dq_bsoftlimit) {
285 if (dq->dq_curblocks < dq->dq_bsoftlimit) {
286 dq->dq_btime = gettime() + ip->i_ump->um_btime[type];
287 if (DIP(ip, uid) == cred->cr_uid)
288 uprintf("\n%s: warning, %s %s\n",
289 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
290 quotatypes[type], "disk quota exceeded");
291 return (0);
292 }
293 if (gettime() > dq->dq_btime) {
294 if ((dq->dq_flags & DQ_BLKS) == 0 &&
295 DIP(ip, uid) == cred->cr_uid) {
296 uprintf("\n%s: write failed, %s %s\n",
297 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
298 quotatypes[type],
299 "disk quota exceeded for too long");
300 dq->dq_flags |= DQ_BLKS;
301 }
302 return (EDQUOT);
303 }
304 }
305 return (0);
306 }
307
308 /*
309 * Check the inode limit, applying corrective action.
310 */
311 int
ufs_quota_alloc_inode2(struct inode * ip,struct ucred * cred,enum ufs_quota_flags flags)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 DIAGNOSTIC
320 chkdquot(ip);
321 #endif
322
323 if ((flags & UFS_QUOTA_FORCE) == 0 && cred->cr_uid != 0) {
324 for (i = 0; i < MAXQUOTAS; i++) {
325 if (flags & (1 << i))
326 continue;
327 if ((dq = ip->i_dquot[i]) == NODQUOT)
328 continue;
329 if ((error = chkiqchg(ip, 1, cred, i)) != 0)
330 return (error);
331 }
332 }
333 for (i = 0; i < MAXQUOTAS; i++) {
334 if (flags & (1 << i))
335 continue;
336 if ((dq = ip->i_dquot[i]) == NODQUOT)
337 continue;
338 while (dq->dq_flags & DQ_LOCK) {
339 dq->dq_flags |= DQ_WANT;
340 tsleep_nsec(dq, PINOD+1, "chkiq", INFSLP);
341 }
342 dq->dq_curinodes++;
343 dq->dq_flags |= DQ_MOD;
344 }
345 return (0);
346 }
347
348 int
ufs_quota_free_inode2(struct inode * ip,struct ucred * cred,enum ufs_quota_flags flags)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 DIAGNOSTIC
356 if (!VOP_ISLOCKED(ITOV(ip)))
357 panic ("ufs_quota_free_blocks2: vnode is not locked");
358 #endif
359
360 for (i = 0; i < MAXQUOTAS; i++) {
361 if (flags & (1 << i))
362 continue;
363 if ((dq = ip->i_dquot[i]) == NODQUOT)
364 continue;
365 while (dq->dq_flags & DQ_LOCK) {
366 dq->dq_flags |= DQ_WANT;
367 tsleep_nsec(dq, PINOD+1, "chkiq", INFSLP);
368 }
369 if (dq->dq_curinodes > 0)
370 dq->dq_curinodes--;
371 dq->dq_flags &= ~DQ_INODS;
372 dq->dq_flags |= DQ_MOD;
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
chkiqchg(struct inode * ip,long change,struct ucred * cred,int type)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_curinodes + change;
386
387 /*
388 * If user would exceed their hard limit, disallow inode allocation.
389 */
390 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
391 if ((dq->dq_flags & DQ_INODS) == 0 &&
392 DIP(ip, uid) == cred->cr_uid) {
393 uprintf("\n%s: write failed, %s inode limit reached\n",
394 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
395 quotatypes[type]);
396 dq->dq_flags |= DQ_INODS;
397 }
398 return (EDQUOT);
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_isoftlimit && dq->dq_isoftlimit) {
405 if (dq->dq_curinodes < dq->dq_isoftlimit) {
406 dq->dq_itime = gettime() + ip->i_ump->um_itime[type];
407 if (DIP(ip, uid) == cred->cr_uid)
408 uprintf("\n%s: warning, %s %s\n",
409 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
410 quotatypes[type], "inode quota exceeded");
411 return (0);
412 }
413 if (gettime() > dq->dq_itime) {
414 if ((dq->dq_flags & DQ_INODS) == 0 &&
415 DIP(ip, uid) == cred->cr_uid) {
416 uprintf("\n%s: write failed, %s %s\n",
417 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
418 quotatypes[type],
419 "inode quota exceeded for too long");
420 dq->dq_flags |= DQ_INODS;
421 }
422 return (EDQUOT);
423 }
424 }
425 return (0);
426 }
427
428 #ifdef DIAGNOSTIC
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
chkdquot(struct inode * ip)434 chkdquot(struct inode *ip)
435 {
436 struct ufsmount *ump = ip->i_ump;
437 int i;
438 struct vnode *vp = ITOV(ip);
439
440 if (!VOP_ISLOCKED(vp))
441 panic ("chkdquot: vnode is not locked");
442
443 for (i = 0; i < MAXQUOTAS; i++) {
444 if (ump->um_quotas[i] == NULLVP ||
445 (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
446 continue;
447 if (ip->i_dquot[i] == NODQUOT) {
448 vprint("chkdquot: missing dquot", ITOV(ip));
449 panic("missing dquot");
450 }
451 }
452 }
453 #endif
454
455 /*
456 * Code to process quotactl commands.
457 */
458
459 int
quotaon_vnode(struct vnode * vp,void * arg)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_EXCLUSIVE)) {
468 return (0);
469 }
470
471 error = getinoquota(VTOI(vp));
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
quotaon(struct proc * p,struct mount * mp,int type,caddr_t fname)481 quotaon(struct proc *p, struct mount *mp, int type, caddr_t fname)
482 {
483 struct ufsmount *ump = VFSTOUFS(mp);
484 struct vnode *vp, **vpp;
485 struct dquot *dq;
486 int error;
487 struct nameidata nd;
488
489 #ifdef DIAGNOSTIC
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);
496 if ((error = vn_open(&nd, FREAD|FWRITE, 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, FREAD|FWRITE, p->p_ucred, p);
502 return (EACCES);
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, FREAD|FWRITE, 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_OPENING;
525 mp->mnt_flag |= MNT_QUOTA;
526 vp->v_flag |= VSYSTEM;
527 /*
528 * Set up the time limits for this quota.
529 */
530 ump->um_btime[type] = MAX_DQ_TIME;
531 ump->um_itime[type] = MAX_IQ_TIME;
532 if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
533 if (dq->dq_btime > 0)
534 ump->um_btime[type] = dq->dq_btime;
535 if (dq->dq_itime > 0)
536 ump->um_itime[type] = dq->dq_itime;
537 dqrele(NULLVP, 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);
545
546 ump->um_qflags[type] &= ~QTF_OPENING;
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
quotaoff_vnode(struct vnode * vp,void * arg)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_EXCLUSIVE))
568 return (0);
569 ip = VTOI(vp);
570 dq = ip->i_dquot[qa->type];
571 ip->i_dquot[qa->type] = NODQUOT;
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
quotaoff(struct proc * p,struct mount * mp,int type)581 quotaoff(struct proc *p, struct mount *mp, int type)
582 {
583 struct vnode *qvp;
584 struct ufsmount *ump = VFSTOUFS(mp);
585 struct quotaoff_arg qa;
586 int error;
587
588 #ifdef DIAGNOSTIC
589 if (!vfs_isbusy(mp))
590 panic ("quotaoff: mount point not busy");
591 #endif
592 if ((qvp = ump->um_quotas[type]) == NULLVP)
593 return (0);
594 ump->um_qflags[type] |= QTF_CLOSING;
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, FREAD|FWRITE, p->p_ucred, p);
604 ump->um_quotas[type] = NULLVP;
605 crfree(ump->um_cred[type]);
606 ump->um_cred[type] = NOCRED;
607 ump->um_qflags[type] &= ~QTF_CLOSING;
608 for (type = 0; type < MAXQUOTAS; type++)
609 if (ump->um_quotas[type] != NULLVP)
610 break;
611 if (type == MAXQUOTAS)
612 mp->mnt_flag &= ~MNT_QUOTA;
613 return (error);
614 }
615
616 /*
617 * Q_GETQUOTA - return current values in a dqblk structure.
618 */
619 int
getquota(struct mount * mp,u_long id,int type,caddr_t addr)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, id, VFSTOUFS(mp), type, &dq)) != 0)
626 return (error);
627 error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
628 #ifdef KTRACE
629 if (error == 0) {
630 struct proc *p = curproc;
631 if (KTRPOINT(p, KTR_STRUCT))
632 ktrquota(p, &dq->dq_dqb);
633 }
634 #endif
635
636 dqrele(NULLVP, dq);
637 return (error);
638 }
639
640 /*
641 * Q_SETQUOTA - assign an entire dqblk structure.
642 */
643 int
setquota(struct mount * mp,u_long id,int type,caddr_t addr)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);
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 KTRACE
656 {
657 struct proc *p = curproc;
658 if (KTRPOINT(p, KTR_STRUCT))
659 ktrquota(p, &newlim);
660 }
661 #endif
662
663 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
664 return (error);
665 dq = ndq;
666 while (dq->dq_flags & DQ_LOCK) {
667 dq->dq_flags |= DQ_WANT;
668 tsleep_nsec(dq, PINOD+1, "setquota", INFSLP);
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_curblocks;
676 newlim.dqb_curinodes = dq->dq_curinodes;
677 if (dq->dq_id != 0) {
678 newlim.dqb_btime = dq->dq_btime;
679 newlim.dqb_itime = dq->dq_itime;
680 }
681 if (newlim.dqb_bsoftlimit &&
682 dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
683 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
684 newlim.dqb_btime = gettime() + ump->um_btime[type];
685 if (newlim.dqb_isoftlimit &&
686 dq->dq_curinodes >= newlim.dqb_isoftlimit &&
687 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
688 newlim.dqb_itime = gettime() + ump->um_itime[type];
689 dq->dq_dqb = newlim;
690 if (dq->dq_curblocks < dq->dq_bsoftlimit)
691 dq->dq_flags &= ~DQ_BLKS;
692 if (dq->dq_curinodes < dq->dq_isoftlimit)
693 dq->dq_flags &= ~DQ_INODS;
694 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
695 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
696 dq->dq_flags |= DQ_FAKE;
697 else
698 dq->dq_flags &= ~DQ_FAKE;
699 dq->dq_flags |= DQ_MOD;
700 dqrele(NULLVP, dq);
701 return (0);
702 }
703
704 /*
705 * Q_SETUSE - set current inode and block usage.
706 */
707 int
setuse(struct mount * mp,u_long id,int type,caddr_t addr)708 setuse(struct mount *mp, u_long id, int type, caddr_t addr)
709 {
710 struct dquot *dq;
711 struct ufsmount *ump = VFSTOUFS(mp);
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 KTRACE
720 {
721 struct proc *p = curproc;
722 if (KTRPOINT(p, KTR_STRUCT))
723 ktrquota(p, &usage);
724 }
725 #endif
726
727 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
728 return (error);
729 dq = ndq;
730 while (dq->dq_flags & DQ_LOCK) {
731 dq->dq_flags |= DQ_WANT;
732 tsleep_nsec(dq, PINOD+1, "setuse", INFSLP);
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_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
739 usage.dqb_curblocks >= dq->dq_bsoftlimit)
740 dq->dq_btime = gettime() + ump->um_btime[type];
741 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
742 usage.dqb_curinodes >= dq->dq_isoftlimit)
743 dq->dq_itime = gettime() + ump->um_itime[type];
744 dq->dq_curblocks = usage.dqb_curblocks;
745 dq->dq_curinodes = usage.dqb_curinodes;
746 if (dq->dq_curblocks < dq->dq_bsoftlimit)
747 dq->dq_flags &= ~DQ_BLKS;
748 if (dq->dq_curinodes < dq->dq_isoftlimit)
749 dq->dq_flags &= ~DQ_INODS;
750 dq->dq_flags |= DQ_MOD;
751 dqrele(NULLVP, dq);
752 return (0);
753 }
754
755 int
qsync_vnode(struct vnode * vp,void * arg)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_EXCLUSIVE | LK_NOWAIT))
765 return (0);
766
767 for (i = 0; i < MAXQUOTAS; i++) {
768 dq = VTOI(vp)->i_dquot[i];
769 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
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
qsync(struct mount * mp)780 qsync(struct mount *mp)
781 {
782 struct ufsmount *ump = VFSTOUFS(mp);
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 < MAXQUOTAS; i++)
790 if (ump->um_quotas[i] != NULLVP)
791 break;
792 if (i == MAXQUOTAS)
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);
799 return (0);
800 }
801
802 /*
803 * Code pertaining to management of the in-core dquot data structures.
804 */
805 LIST_HEAD(dqhash, dquot) *dqhashtbl;
806 SIPHASH_KEY dqhashkey;
807 u_long dqhash;
808
809 /*
810 * Dquot free list.
811 */
812 #define DQUOTINC 5 /* minimum free dquots desired */
813 TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
814 long numdquot, desireddquot = DQUOTINC;
815
816 /*
817 * Initialize the quota system.
818 */
819 void
ufs_quota_init(void)820 ufs_quota_init(void)
821 {
822 dqhashtbl = hashinit(initialvnodes, M_DQUOT, M_WAITOK, &dqhash);
823 arc4random_buf(&dqhashkey, sizeof(dqhashkey));
824 TAILQ_INIT(&dqfreelist);
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
dqget(struct vnode * vp,u_long id,struct ufsmount * ump,int type,struct dquot ** dqp)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 || (ump->um_qflags[type] & QTF_CLOSING)) {
845 *dqp = NODQUOT;
846 return (EINVAL);
847 }
848 /*
849 * Check the cache first.
850 */
851 SipHash24_Init(&ctx, &dqhashkey);
852 SipHash24_Update(&ctx, &dqvp, sizeof(dqvp));
853 SipHash24_Update(&ctx, &id, sizeof(id));
854 dqh = &dqhashtbl[SipHash24_End(&ctx) & dqhash];
855
856 LIST_FOREACH(dq, dqh, dq_hash) {
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);
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) == NODQUOT &&
874 numdquot < MAXQUOTAS * initialvnodes)
875 desireddquot += DQUOTINC;
876 if (numdquot < desireddquot) {
877 dq = malloc(sizeof *dq, M_DQUOT, M_WAITOK | M_ZERO);
878 numdquot++;
879 } else {
880 if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
881 tablefull("dquot");
882 *dqp = NODQUOT;
883 return (EUSERS);
884 }
885 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
886 panic("free dquot isn't");
887 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
888 LIST_REMOVE(dq, dq_hash);
889 crfree(dq->dq_cred);
890 dq->dq_cred = NOCRED;
891 }
892 /*
893 * Initialize the contents of the dquot structure.
894 */
895 if (vp != dqvp)
896 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
897 LIST_INSERT_HEAD(dqh, dq, dq_hash);
898 dqref(dq);
899 dq->dq_flags = DQ_LOCK;
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;
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));
917 if (vp != dqvp)
918 VOP_UNLOCK(dqvp);
919 if (dq->dq_flags & DQ_WANT)
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);
928 dqrele(vp, dq);
929 *dqp = NODQUOT;
930 return (error);
931 }
932 /*
933 * Check for no limit to enforce.
934 * Initialize time values if necessary.
935 */
936 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
937 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
938 dq->dq_flags |= DQ_FAKE;
939 if (dq->dq_id != 0) {
940 if (dq->dq_btime == 0)
941 dq->dq_btime = gettime() + ump->um_btime[type];
942 if (dq->dq_itime == 0)
943 dq->dq_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
dqrele(struct vnode * vp,struct dquot * dq)953 dqrele(struct vnode *vp, struct dquot *dq)
954 {
955
956 if (dq == NODQUOT)
957 return;
958 if (dq->dq_cnt > 1) {
959 dq->dq_cnt--;
960 return;
961 }
962 if (dq->dq_flags & DQ_MOD)
963 (void) dqsync(vp, dq);
964 if (--dq->dq_cnt > 0)
965 return;
966 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
967 }
968
969 /*
970 * Update the disk quota in the quota file.
971 */
972 int
dqsync(struct vnode * vp,struct dquot * dq)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)
981 panic("dqsync: dquot");
982 if ((dq->dq_flags & DQ_MOD) == 0)
983 return (0);
984 if ((dqvp = dq->dq_vp) == NULLVP)
985 panic("dqsync: file");
986
987 if (vp != dqvp)
988 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
989 while (dq->dq_flags & DQ_LOCK) {
990 dq->dq_flags |= DQ_WANT;
991 tsleep_nsec(dq, PINOD+2, "dqsync", INFSLP);
992 if ((dq->dq_flags & DQ_MOD) == 0) {
993 if (vp != dqvp)
994 VOP_UNLOCK(dqvp);
995 return (0);
996 }
997 }
998 dq->dq_flags |= DQ_LOCK;
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;
1008 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_cred);
1009 if (auio.uio_resid && error == 0)
1010 error = EIO;
1011 if (dq->dq_flags & DQ_WANT)
1012 wakeup(dq);
1013 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
1014 if (vp != dqvp)
1015 VOP_UNLOCK(dqvp);
1016 return (error);
1017 }
1018
1019 int
ufs_quota_delete(struct inode * ip)1020 ufs_quota_delete(struct inode *ip)
1021 {
1022 struct vnode *vp = ITOV(ip);
1023 int i;
1024 for (i = 0; i < MAXQUOTAS; i++) {
1025 if (ip->i_dquot[i] != NODQUOT) {
1026 dqrele(vp, ip->i_dquot[i]);
1027 ip->i_dquot[i] = NODQUOT;
1028 }
1029 }
1030
1031 return (0);
1032 }
1033
1034 /*
1035 * Do operations associated with quotas
1036 */
1037 int
ufs_quotactl(struct mount * mp,int cmds,uid_t uid,caddr_t arg,struct proc * p)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 >> SUBCMDSHIFT;
1046
1047 switch (cmd) {
1048 case Q_SYNC:
1049 break;
1050 case Q_GETQUOTA:
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 & SUBCMDMASK;
1060 if ((u_int)type >= MAXQUOTAS)
1061 return (EINVAL);
1062
1063 if (vfs_busy(mp, VB_READ|VB_NOWAIT))
1064 return (0);
1065
1066
1067 switch (cmd) {
1068
1069 case Q_QUOTAON:
1070 error = quotaon(p, mp, type, arg);
1071 break;
1072
1073 case Q_QUOTAOFF:
1074 error = quotaoff(p, mp, type);
1075 break;
1076
1077 case Q_SETQUOTA:
1078 error = setquota(mp, uid, type, arg) ;
1079 break;
1080
1081 case Q_SETUSE:
1082 error = setuse(mp, uid, type, arg);
1083 break;
1084
1085 case Q_GETQUOTA:
1086 error = getquota(mp, uid, type, arg);
1087 break;
1088
1089 case Q_SYNC:
1090 error = qsync(mp);
1091 break;
1092
1093 default:
1094 error = EINVAL;
1095 break;
1096 }
1097
1098 vfs_unbusy(mp);
1099 return (error);
1100 }
1101