1 /* $NetBSD: ufs_quota1.c,v 1.26 2023/02/22 21:49:45 riastradh Exp $ */
2
3 /*
4 * Copyright (c) 1982, 1986, 1990, 1993, 1995
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Robert Elz at The University of Melbourne.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
35 */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: ufs_quota1.c,v 1.26 2023/02/22 21:49:45 riastradh Exp $");
39
40 #include <sys/param.h>
41 #include <sys/kernel.h>
42 #include <sys/systm.h>
43 #include <sys/namei.h>
44 #include <sys/file.h>
45 #include <sys/proc.h>
46 #include <sys/vnode.h>
47 #include <sys/mount.h>
48 #include <sys/kauth.h>
49
50 #include <ufs/ufs/quota1.h>
51 #include <ufs/ufs/inode.h>
52 #include <ufs/ufs/ufsmount.h>
53 #include <ufs/ufs/ufs_extern.h>
54 #include <ufs/ufs/ufs_quota.h>
55
56 static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int);
57 static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int);
58
59 /*
60 * Update disk usage, and take corrective action.
61 */
62 int
chkdq1(struct inode * ip,int64_t change,kauth_cred_t cred,int flags)63 chkdq1(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
64 {
65 struct dquot *dq;
66 int i;
67 int ncurblocks, error;
68
69 if ((error = getinoquota(ip)) != 0)
70 return error;
71 if (change == 0)
72 return (0);
73 if (change < 0) {
74 for (i = 0; i < MAXQUOTAS; i++) {
75 if ((dq = ip->i_dquot[i]) == NODQUOT)
76 continue;
77 mutex_enter(&dq->dq_interlock);
78 ncurblocks = dq->dq_curblocks + change;
79 if (ncurblocks >= 0)
80 dq->dq_curblocks = ncurblocks;
81 else
82 dq->dq_curblocks = 0;
83 dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
84 dq->dq_flags |= DQ_MOD;
85 mutex_exit(&dq->dq_interlock);
86 }
87 return (0);
88 }
89 for (i = 0; i < MAXQUOTAS; i++) {
90 if ((dq = ip->i_dquot[i]) == NODQUOT)
91 continue;
92 if ((flags & FORCE) == 0 &&
93 kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
94 KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, KAUTH_ARG(i),
95 KAUTH_ARG(QL_BLOCK), NULL) != 0) {
96 mutex_enter(&dq->dq_interlock);
97 error = chkdqchg(ip, change, cred, i);
98 mutex_exit(&dq->dq_interlock);
99 if (error != 0)
100 return (error);
101 }
102 }
103 for (i = 0; i < MAXQUOTAS; i++) {
104 if ((dq = ip->i_dquot[i]) == NODQUOT)
105 continue;
106 mutex_enter(&dq->dq_interlock);
107 dq->dq_curblocks += change;
108 dq->dq_flags |= DQ_MOD;
109 mutex_exit(&dq->dq_interlock);
110 }
111 return (0);
112 }
113
114 /*
115 * Check for a valid change to a users allocation.
116 * Issue an error message if appropriate.
117 */
118 static int
chkdqchg(struct inode * ip,int64_t change,kauth_cred_t cred,int type)119 chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type)
120 {
121 struct dquot *dq = ip->i_dquot[type];
122 long ncurblocks = dq->dq_curblocks + change;
123
124 KASSERT(mutex_owned(&dq->dq_interlock));
125 /*
126 * If user would exceed their hard limit, disallow space allocation.
127 */
128 if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
129 if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
130 ip->i_uid == kauth_cred_geteuid(cred)) {
131 uprintf("\n%s: write failed, %s disk limit reached\n",
132 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
133 quotatypes[type]);
134 dq->dq_flags |= DQ_WARN(QL_BLOCK);
135 }
136 return (EDQUOT);
137 }
138 /*
139 * If user is over their soft limit for too long, disallow space
140 * allocation. Reset time limit as they cross their soft limit.
141 */
142 if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
143 if (dq->dq_curblocks < dq->dq_bsoftlimit) {
144 dq->dq_btime =
145 time_second + ip->i_ump->umq1_btime[type];
146 if (ip->i_uid == kauth_cred_geteuid(cred))
147 uprintf("\n%s: warning, %s %s\n",
148 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
149 quotatypes[type], "disk quota exceeded");
150 return (0);
151 }
152 if (time_second > dq->dq_btime) {
153 if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
154 ip->i_uid == kauth_cred_geteuid(cred)) {
155 uprintf("\n%s: write failed, %s %s\n",
156 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
157 quotatypes[type],
158 "disk quota exceeded for too long");
159 dq->dq_flags |= DQ_WARN(QL_BLOCK);
160 }
161 return (EDQUOT);
162 }
163 }
164 return (0);
165 }
166
167 /*
168 * Check the inode limit, applying corrective action.
169 */
170 int
chkiq1(struct inode * ip,int32_t change,kauth_cred_t cred,int flags)171 chkiq1(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
172 {
173 struct dquot *dq;
174 int i;
175 int ncurinodes, error;
176
177 if ((error = getinoquota(ip)) != 0)
178 return error;
179 if (change == 0)
180 return (0);
181 if (change < 0) {
182 for (i = 0; i < MAXQUOTAS; i++) {
183 if ((dq = ip->i_dquot[i]) == NODQUOT)
184 continue;
185 mutex_enter(&dq->dq_interlock);
186 ncurinodes = dq->dq_curinodes + change;
187 if (ncurinodes >= 0)
188 dq->dq_curinodes = ncurinodes;
189 else
190 dq->dq_curinodes = 0;
191 dq->dq_flags &= ~DQ_WARN(QL_FILE);
192 dq->dq_flags |= DQ_MOD;
193 mutex_exit(&dq->dq_interlock);
194 }
195 return (0);
196 }
197 for (i = 0; i < MAXQUOTAS; i++) {
198 if ((dq = ip->i_dquot[i]) == NODQUOT)
199 continue;
200 if ((flags & FORCE) == 0 &&
201 kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
202 KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
203 KAUTH_ARG(i), KAUTH_ARG(QL_FILE), NULL) != 0) {
204 mutex_enter(&dq->dq_interlock);
205 error = chkiqchg(ip, change, cred, i);
206 mutex_exit(&dq->dq_interlock);
207 if (error != 0)
208 return (error);
209 }
210 }
211 for (i = 0; i < MAXQUOTAS; i++) {
212 if ((dq = ip->i_dquot[i]) == NODQUOT)
213 continue;
214 mutex_enter(&dq->dq_interlock);
215 dq->dq_curinodes += change;
216 dq->dq_flags |= DQ_MOD;
217 mutex_exit(&dq->dq_interlock);
218 }
219 return (0);
220 }
221
222 /*
223 * Check for a valid change to a users allocation.
224 * Issue an error message if appropriate.
225 */
226 static int
chkiqchg(struct inode * ip,int32_t change,kauth_cred_t cred,int type)227 chkiqchg(struct inode *ip, int32_t change, kauth_cred_t cred, int type)
228 {
229 struct dquot *dq = ip->i_dquot[type];
230 long ncurinodes = dq->dq_curinodes + change;
231
232 KASSERT(mutex_owned(&dq->dq_interlock));
233 /*
234 * If user would exceed their hard limit, disallow inode allocation.
235 */
236 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
237 if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 &&
238 ip->i_uid == kauth_cred_geteuid(cred)) {
239 uprintf("\n%s: write failed, %s inode limit reached\n",
240 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
241 quotatypes[type]);
242 dq->dq_flags |= DQ_WARN(QL_FILE);
243 }
244 return (EDQUOT);
245 }
246 /*
247 * If user is over their soft limit for too long, disallow inode
248 * allocation. Reset time limit as they cross their soft limit.
249 */
250 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
251 if (dq->dq_curinodes < dq->dq_isoftlimit) {
252 dq->dq_itime =
253 time_second + ip->i_ump->umq1_itime[type];
254 if (ip->i_uid == kauth_cred_geteuid(cred))
255 uprintf("\n%s: warning, %s %s\n",
256 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
257 quotatypes[type], "inode quota exceeded");
258 return (0);
259 }
260 if (time_second > dq->dq_itime) {
261 if ((dq->dq_flags & DQ_WARN(QL_FILE)) == 0 &&
262 ip->i_uid == kauth_cred_geteuid(cred)) {
263 uprintf("\n%s: write failed, %s %s\n",
264 ITOV(ip)->v_mount->mnt_stat.f_mntonname,
265 quotatypes[type],
266 "inode quota exceeded for too long");
267 dq->dq_flags |= DQ_WARN(QL_FILE);
268 }
269 return (EDQUOT);
270 }
271 }
272 return (0);
273 }
274
275 int
quota1_umount(struct mount * mp,int flags)276 quota1_umount(struct mount *mp, int flags)
277 {
278 int i, error;
279 struct ufsmount *ump = VFSTOUFS(mp);
280 struct lwp *l = curlwp;
281
282 if ((ump->um_flags & UFS_QUOTA) == 0)
283 return 0;
284
285 if ((error = vflush(mp, NULLVP, SKIPSYSTEM | flags)) != 0)
286 return (error);
287
288 for (i = 0; i < MAXQUOTAS; i++) {
289 if (ump->um_quotas[i] != NULLVP) {
290 quota1_handle_cmd_quotaoff(l, ump, i);
291 }
292 }
293 return 0;
294 }
295
296 /*
297 * Code to process quotactl commands.
298 */
299
300 /*
301 * set up a quota file for a particular file system.
302 */
303 int
quota1_handle_cmd_quotaon(struct lwp * l,struct ufsmount * ump,int type,const char * fname)304 quota1_handle_cmd_quotaon(struct lwp *l, struct ufsmount *ump, int type,
305 const char *fname)
306 {
307 struct mount *mp = ump->um_mountp;
308 struct vnode *vp, **vpp;
309 struct vnode_iterator *marker;
310 struct dquot *dq;
311 int error;
312 struct pathbuf *pb;
313
314 if (type < 0 || type >= MAXQUOTAS)
315 return EINVAL;
316
317 if (ump->um_flags & UFS_QUOTA2) {
318 uprintf("%s: quotas v2 already enabled\n",
319 mp->mnt_stat.f_mntonname);
320 return (EBUSY);
321 }
322
323 if (mp->mnt_wapbl != NULL) {
324 printf("%s: quota v1 cannot be used with -o log\n",
325 mp->mnt_stat.f_mntonname);
326 return (EOPNOTSUPP);
327 }
328
329 vpp = &ump->um_quotas[type];
330
331 pb = pathbuf_create(fname);
332 if (pb == NULL) {
333 return ENOMEM;
334 }
335 error = vn_open(NULL, pb, 0, FREAD|FWRITE, 0, &vp, NULL, NULL);
336 if (error != 0) {
337 pathbuf_destroy(pb);
338 return error;
339 }
340 pathbuf_destroy(pb);
341
342 VOP_UNLOCK(vp);
343 if (vp->v_type != VREG) {
344 (void) vn_close(vp, FREAD|FWRITE, l->l_cred);
345 return (EACCES);
346 }
347 if (*vpp != vp)
348 quota1_handle_cmd_quotaoff(l, ump, type);
349 mutex_enter(&dqlock);
350 while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
351 cv_wait(&dqcv, &dqlock);
352 ump->umq1_qflags[type] |= QTF_OPENING;
353 mutex_exit(&dqlock);
354 mp->mnt_flag |= MNT_QUOTA;
355 vp->v_vflag |= VV_SYSTEM; /* XXXSMP */
356 *vpp = vp;
357 /*
358 * Save the credential of the process that turned on quotas.
359 * Set up the time limits for this quota.
360 */
361 kauth_cred_hold(l->l_cred);
362 ump->um_cred[type] = l->l_cred;
363 ump->umq1_btime[type] = MAX_DQ_TIME;
364 ump->umq1_itime[type] = MAX_IQ_TIME;
365 if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
366 if (dq->dq_btime > 0)
367 ump->umq1_btime[type] = dq->dq_btime;
368 if (dq->dq_itime > 0)
369 ump->umq1_itime[type] = dq->dq_itime;
370 dqrele(NULLVP, dq);
371 }
372 /*
373 * Search vnodes associated with this mount point,
374 * adding references to quota file being opened.
375 * NB: only need to add dquot's for inodes being modified.
376 */
377 vfs_vnode_iterator_init(mp, &marker);
378 while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
379 error = vn_lock(vp, LK_EXCLUSIVE);
380 if (error) {
381 vrele(vp);
382 continue;
383 }
384 mutex_enter(vp->v_interlock);
385 if (VTOI(vp) == NULL || vp->v_type == VNON ||
386 vp->v_writecount == 0) {
387 mutex_exit(vp->v_interlock);
388 vput(vp);
389 continue;
390 }
391 mutex_exit(vp->v_interlock);
392 if ((error = getinoquota(VTOI(vp))) != 0) {
393 vput(vp);
394 break;
395 }
396 vput(vp);
397 }
398 vfs_vnode_iterator_destroy(marker);
399
400 mutex_enter(&dqlock);
401 ump->umq1_qflags[type] &= ~QTF_OPENING;
402 cv_broadcast(&dqcv);
403 if (error == 0)
404 ump->um_flags |= UFS_QUOTA;
405 mutex_exit(&dqlock);
406 if (error)
407 quota1_handle_cmd_quotaoff(l, ump, type);
408 return (error);
409 }
410
411 /*
412 * turn off disk quotas for a filesystem.
413 */
414 int
quota1_handle_cmd_quotaoff(struct lwp * l,struct ufsmount * ump,int type)415 quota1_handle_cmd_quotaoff(struct lwp *l, struct ufsmount *ump, int type)
416 {
417 struct mount *mp = ump->um_mountp;
418 struct vnode *vp;
419 struct vnode *qvp;
420 struct vnode_iterator *marker;
421 struct dquot *dq;
422 struct inode *ip;
423 kauth_cred_t cred;
424 int i, error;
425
426 if (type < 0 || type >= MAXQUOTAS)
427 return EINVAL;
428
429 mutex_enter(&dqlock);
430 while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
431 cv_wait(&dqcv, &dqlock);
432 if ((qvp = ump->um_quotas[type]) == NULLVP) {
433 mutex_exit(&dqlock);
434 return (0);
435 }
436 ump->umq1_qflags[type] |= QTF_CLOSING;
437 mutex_exit(&dqlock);
438 /*
439 * Search vnodes associated with this mount point,
440 * deleting any references to quota file being closed.
441 */
442 vfs_vnode_iterator_init(mp, &marker);
443 while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
444 error = vn_lock(vp, LK_EXCLUSIVE);
445 if (error) {
446 vrele(vp);
447 continue;
448 }
449 ip = VTOI(vp);
450 if (ip == NULL || vp->v_type == VNON) {
451 vput(vp);
452 continue;
453 }
454 dq = ip->i_dquot[type];
455 ip->i_dquot[type] = NODQUOT;
456 dqrele(vp, dq);
457 vput(vp);
458 }
459 vfs_vnode_iterator_destroy(marker);
460 #ifdef DIAGNOSTIC
461 dqflush(qvp);
462 #endif
463 qvp->v_vflag &= ~VV_SYSTEM;
464 error = vn_close(qvp, FREAD|FWRITE, l->l_cred);
465 mutex_enter(&dqlock);
466 ump->um_quotas[type] = NULLVP;
467 cred = ump->um_cred[type];
468 ump->um_cred[type] = NOCRED;
469 for (i = 0; i < MAXQUOTAS; i++)
470 if (ump->um_quotas[i] != NULLVP)
471 break;
472 ump->umq1_qflags[type] &= ~QTF_CLOSING;
473 if (i == MAXQUOTAS)
474 ump->um_flags &= ~UFS_QUOTA;
475 cv_broadcast(&dqcv);
476 mutex_exit(&dqlock);
477 kauth_cred_free(cred);
478 if (i == MAXQUOTAS)
479 mp->mnt_flag &= ~MNT_QUOTA;
480 return (error);
481 }
482
483 int
quota1_handle_cmd_get(struct ufsmount * ump,const struct quotakey * qk,struct quotaval * qv)484 quota1_handle_cmd_get(struct ufsmount *ump, const struct quotakey *qk,
485 struct quotaval *qv)
486 {
487 struct dquot *dq;
488 int error;
489 struct quotaval blocks, files;
490 int idtype;
491 id_t id;
492
493 idtype = qk->qk_idtype;
494 id = qk->qk_id;
495
496 if (ump->um_quotas[idtype] == NULLVP)
497 return ENODEV;
498
499 if (id == QUOTA_DEFAULTID) { /* we want the grace period of id 0 */
500 if ((error = dqget(NULLVP, 0, ump, idtype, &dq)) != 0)
501 return error;
502
503 } else {
504 if ((error = dqget(NULLVP, id, ump, idtype, &dq)) != 0)
505 return error;
506 }
507 dqblk_to_quotavals(&dq->dq_un.dq1_dqb, &blocks, &files);
508 dqrele(NULLVP, dq);
509 if (id == QUOTA_DEFAULTID) {
510 if (blocks.qv_expiretime > 0)
511 blocks.qv_grace = blocks.qv_expiretime;
512 else
513 blocks.qv_grace = MAX_DQ_TIME;
514 if (files.qv_expiretime > 0)
515 files.qv_grace = files.qv_expiretime;
516 else
517 files.qv_grace = MAX_DQ_TIME;
518 }
519
520 switch (qk->qk_objtype) {
521 case QUOTA_OBJTYPE_BLOCKS:
522 *qv = blocks;
523 break;
524 case QUOTA_OBJTYPE_FILES:
525 *qv = files;
526 break;
527 default:
528 return EINVAL;
529 }
530
531 return 0;
532 }
533
534 static uint32_t
quota1_encode_limit(uint64_t lim)535 quota1_encode_limit(uint64_t lim)
536 {
537 if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) {
538 return 0;
539 }
540 return lim;
541 }
542
543 int
quota1_handle_cmd_put(struct ufsmount * ump,const struct quotakey * key,const struct quotaval * val)544 quota1_handle_cmd_put(struct ufsmount *ump, const struct quotakey *key,
545 const struct quotaval *val)
546 {
547 struct dquot *dq;
548 struct dqblk dqb;
549 int error;
550
551 switch (key->qk_idtype) {
552 case QUOTA_IDTYPE_USER:
553 case QUOTA_IDTYPE_GROUP:
554 break;
555 default:
556 return EINVAL;
557 }
558
559 switch (key->qk_objtype) {
560 case QUOTA_OBJTYPE_BLOCKS:
561 case QUOTA_OBJTYPE_FILES:
562 break;
563 default:
564 return EINVAL;
565 }
566
567 if (ump->um_quotas[key->qk_idtype] == NULLVP)
568 return ENODEV;
569
570 if (key->qk_id == QUOTA_DEFAULTID) {
571 /* just update grace times */
572 id_t id = 0;
573
574 if ((error = dqget(NULLVP, id, ump, key->qk_idtype, &dq)) != 0)
575 return error;
576 mutex_enter(&dq->dq_interlock);
577 if (val->qv_grace != QUOTA_NOTIME) {
578 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS)
579 ump->umq1_btime[key->qk_idtype] = dq->dq_btime =
580 val->qv_grace;
581 if (key->qk_objtype == QUOTA_OBJTYPE_FILES)
582 ump->umq1_itime[key->qk_idtype] = dq->dq_itime =
583 val->qv_grace;
584 }
585 dq->dq_flags |= DQ_MOD;
586 mutex_exit(&dq->dq_interlock);
587 dqrele(NULLVP, dq);
588 return 0;
589 }
590
591 if ((error = dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq)) != 0)
592 return (error);
593 mutex_enter(&dq->dq_interlock);
594 /*
595 * Copy all but the current values.
596 * Reset time limit if previously had no soft limit or were
597 * under it, but now have a soft limit and are over it.
598 */
599 dqb.dqb_curblocks = dq->dq_curblocks;
600 dqb.dqb_curinodes = dq->dq_curinodes;
601 dqb.dqb_btime = dq->dq_btime;
602 dqb.dqb_itime = dq->dq_itime;
603 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
604 dqb.dqb_bsoftlimit = quota1_encode_limit(val->qv_softlimit);
605 dqb.dqb_bhardlimit = quota1_encode_limit(val->qv_hardlimit);
606 dqb.dqb_isoftlimit = dq->dq_isoftlimit;
607 dqb.dqb_ihardlimit = dq->dq_ihardlimit;
608 } else {
609 KASSERT(key->qk_objtype == QUOTA_OBJTYPE_FILES);
610 dqb.dqb_bsoftlimit = dq->dq_bsoftlimit;
611 dqb.dqb_bhardlimit = dq->dq_bhardlimit;
612 dqb.dqb_isoftlimit = quota1_encode_limit(val->qv_softlimit);
613 dqb.dqb_ihardlimit = quota1_encode_limit(val->qv_hardlimit);
614 }
615 if (dq->dq_id == 0 && val->qv_grace != QUOTA_NOTIME) {
616 /* also update grace time if available */
617 if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
618 ump->umq1_btime[key->qk_idtype] = dqb.dqb_btime =
619 val->qv_grace;
620 }
621 if (key->qk_objtype == QUOTA_OBJTYPE_FILES) {
622 ump->umq1_itime[key->qk_idtype] = dqb.dqb_itime =
623 val->qv_grace;
624 }
625 }
626 if (dqb.dqb_bsoftlimit &&
627 dq->dq_curblocks >= dqb.dqb_bsoftlimit &&
628 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
629 dqb.dqb_btime = time_second + ump->umq1_btime[key->qk_idtype];
630 if (dqb.dqb_isoftlimit &&
631 dq->dq_curinodes >= dqb.dqb_isoftlimit &&
632 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
633 dqb.dqb_itime = time_second + ump->umq1_itime[key->qk_idtype];
634 dq->dq_un.dq1_dqb = dqb;
635 if (dq->dq_curblocks < dq->dq_bsoftlimit)
636 dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
637 if (dq->dq_curinodes < dq->dq_isoftlimit)
638 dq->dq_flags &= ~DQ_WARN(QL_FILE);
639 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
640 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
641 dq->dq_flags |= DQ_FAKE;
642 else
643 dq->dq_flags &= ~DQ_FAKE;
644 dq->dq_flags |= DQ_MOD;
645 mutex_exit(&dq->dq_interlock);
646 dqrele(NULLVP, dq);
647 return (0);
648 }
649
650
651 #if 0
652 /*
653 * Q_SETQUOTA - assign an entire dqblk structure.
654 */
655 int
656 setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb)
657 {
658 struct dquot *dq;
659 struct dquot *ndq;
660 struct ufsmount *ump = VFSTOUFS(mp);
661
662
663 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
664 return (error);
665 dq = ndq;
666 mutex_enter(&dq->dq_interlock);
667 /*
668 * Copy all but the current values.
669 * Reset time limit if previously had no soft limit or were
670 * under it, but now have a soft limit and are over it.
671 */
672 dqb->dqb_curblocks = dq->dq_curblocks;
673 dqb->dqb_curinodes = dq->dq_curinodes;
674 if (dq->dq_id != 0) {
675 dqb->dqb_btime = dq->dq_btime;
676 dqb->dqb_itime = dq->dq_itime;
677 }
678 if (dqb->dqb_bsoftlimit &&
679 dq->dq_curblocks >= dqb->dqb_bsoftlimit &&
680 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
681 dqb->dqb_btime = time_second + ump->umq1_btime[type];
682 if (dqb->dqb_isoftlimit &&
683 dq->dq_curinodes >= dqb->dqb_isoftlimit &&
684 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
685 dqb->dqb_itime = time_second + ump->umq1_itime[type];
686 dq->dq_un.dq1_dqb = *dqb;
687 if (dq->dq_curblocks < dq->dq_bsoftlimit)
688 dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
689 if (dq->dq_curinodes < dq->dq_isoftlimit)
690 dq->dq_flags &= ~DQ_WARN(QL_FILE);
691 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
692 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
693 dq->dq_flags |= DQ_FAKE;
694 else
695 dq->dq_flags &= ~DQ_FAKE;
696 dq->dq_flags |= DQ_MOD;
697 mutex_exit(&dq->dq_interlock);
698 dqrele(NULLVP, dq);
699 return (0);
700 }
701
702 /*
703 * Q_SETUSE - set current inode and block usage.
704 */
705 int
706 setuse(struct mount *mp, u_long id, int type, void *addr)
707 {
708 struct dquot *dq;
709 struct ufsmount *ump = VFSTOUFS(mp);
710 struct dquot *ndq;
711 struct dqblk usage;
712 int error;
713
714 error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
715 if (error)
716 return (error);
717 if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
718 return (error);
719 dq = ndq;
720 mutex_enter(&dq->dq_interlock);
721 /*
722 * Reset time limit if have a soft limit and were
723 * previously under it, but are now over it.
724 */
725 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
726 usage.dqb_curblocks >= dq->dq_bsoftlimit)
727 dq->dq_btime = time_second + ump->umq1_btime[type];
728 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
729 usage.dqb_curinodes >= dq->dq_isoftlimit)
730 dq->dq_itime = time_second + ump->umq1_itime[type];
731 dq->dq_curblocks = usage.dqb_curblocks;
732 dq->dq_curinodes = usage.dqb_curinodes;
733 if (dq->dq_curblocks < dq->dq_bsoftlimit)
734 dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
735 if (dq->dq_curinodes < dq->dq_isoftlimit)
736 dq->dq_flags &= ~DQ_WARN(QL_FILE);
737 dq->dq_flags |= DQ_MOD;
738 mutex_exit(&dq->dq_interlock);
739 dqrele(NULLVP, dq);
740 return (0);
741 }
742 #endif
743
744 /*
745 * Q_SYNC - sync quota files to disk.
746 */
747 int
q1sync(struct mount * mp)748 q1sync(struct mount *mp)
749 {
750 struct ufsmount *ump = VFSTOUFS(mp);
751 struct vnode *vp;
752 struct vnode_iterator *marker;
753 struct dquot *dq;
754 int i, error;
755
756 /*
757 * Check if the mount point has any quotas.
758 * If not, simply return.
759 */
760 for (i = 0; i < MAXQUOTAS; i++)
761 if (ump->um_quotas[i] != NULLVP)
762 break;
763 if (i == MAXQUOTAS)
764 return (0);
765
766 /*
767 * Search vnodes associated with this mount point,
768 * synchronizing any modified dquot structures.
769 */
770 vfs_vnode_iterator_init(mp, &marker);
771 while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
772 error = vn_lock(vp, LK_EXCLUSIVE);
773 if (error) {
774 vrele(vp);
775 continue;
776 }
777 if (VTOI(vp) == NULL || vp->v_type == VNON) {
778 vput(vp);
779 continue;
780 }
781 for (i = 0; i < MAXQUOTAS; i++) {
782 dq = VTOI(vp)->i_dquot[i];
783 if (dq == NODQUOT)
784 continue;
785 mutex_enter(&dq->dq_interlock);
786 if (dq->dq_flags & DQ_MOD)
787 dq1sync(vp, dq);
788 mutex_exit(&dq->dq_interlock);
789 }
790 vput(vp);
791 }
792 vfs_vnode_iterator_destroy(marker);
793 return (0);
794 }
795
796 /*
797 * Obtain a dquot structure for the specified identifier and quota file
798 * reading the information from the file if necessary.
799 */
800 int
dq1get(struct vnode * dqvp,u_long id,struct ufsmount * ump,int type,struct dquot * dq)801 dq1get(struct vnode *dqvp, u_long id, struct ufsmount *ump, int type,
802 struct dquot *dq)
803 {
804 struct iovec aiov;
805 struct uio auio;
806 int error;
807
808 KASSERT(mutex_owned(&dq->dq_interlock));
809 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
810 auio.uio_iov = &aiov;
811 auio.uio_iovcnt = 1;
812 aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
813 aiov.iov_len = sizeof (struct dqblk);
814 auio.uio_resid = sizeof (struct dqblk);
815 auio.uio_offset = (off_t)id * sizeof (struct dqblk);
816 auio.uio_rw = UIO_READ;
817 UIO_SETUP_SYSSPACE(&auio);
818 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
819 if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
820 memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk));
821 VOP_UNLOCK(dqvp);
822 /*
823 * I/O error in reading quota file, release
824 * quota structure and reflect problem to caller.
825 */
826 if (error)
827 return (error);
828 /*
829 * Check for no limit to enforce.
830 * Initialize time values if necessary.
831 */
832 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
833 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
834 dq->dq_flags |= DQ_FAKE;
835 if (dq->dq_id != 0) {
836 if (dq->dq_btime == 0)
837 dq->dq_btime = time_second + ump->umq1_btime[type];
838 if (dq->dq_itime == 0)
839 dq->dq_itime = time_second + ump->umq1_itime[type];
840 }
841 return (0);
842 }
843
844 /*
845 * Update the disk quota in the quota file.
846 */
847 int
dq1sync(struct vnode * vp,struct dquot * dq)848 dq1sync(struct vnode *vp, struct dquot *dq)
849 {
850 struct vnode *dqvp;
851 struct iovec aiov;
852 struct uio auio;
853 int error;
854
855 if (dq == NODQUOT)
856 panic("dq1sync: dquot");
857 KASSERT(mutex_owned(&dq->dq_interlock));
858 if ((dq->dq_flags & DQ_MOD) == 0)
859 return (0);
860 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
861 panic("dq1sync: file");
862 KASSERT(dqvp != vp);
863 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
864 auio.uio_iov = &aiov;
865 auio.uio_iovcnt = 1;
866 aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
867 aiov.iov_len = sizeof (struct dqblk);
868 auio.uio_resid = sizeof (struct dqblk);
869 auio.uio_offset = (off_t)dq->dq_id * sizeof (struct dqblk);
870 auio.uio_rw = UIO_WRITE;
871 UIO_SETUP_SYSSPACE(&auio);
872 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
873 if (auio.uio_resid && error == 0)
874 error = EIO;
875 dq->dq_flags &= ~DQ_MOD;
876 VOP_UNLOCK(dqvp);
877 return (error);
878 }
879