1 /*        $NetBSD: ulfs_quota1.c,v 1.12 2021/06/29 22:40:54 dholland Exp $      */
2 /*  from NetBSD: ufs_quota1.c,v 1.22 2016/06/20 00:52:04 dholland 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) 5/20/95
36  */
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: ulfs_quota1.c,v 1.12 2021/06/29 22:40:54 dholland Exp $");
40 
41 #include <sys/param.h>
42 #include <sys/kernel.h>
43 #include <sys/systm.h>
44 #include <sys/namei.h>
45 #include <sys/file.h>
46 #include <sys/proc.h>
47 #include <sys/vnode.h>
48 #include <sys/mount.h>
49 #include <sys/kauth.h>
50 
51 #include <ufs/lfs/ulfs_quota1.h>
52 #include <ufs/lfs/ulfs_inode.h>
53 #include <ufs/lfs/ulfsmount.h>
54 #include <ufs/lfs/ulfs_extern.h>
55 #include <ufs/lfs/ulfs_quota.h>
56 
57 static int chkdqchg(struct inode *, int64_t, kauth_cred_t, int);
58 static int chkiqchg(struct inode *, int32_t, kauth_cred_t, int);
59 
60 /*
61  * Update disk usage, and take corrective action.
62  */
63 int
lfs_chkdq1(struct inode * ip,int64_t change,kauth_cred_t cred,int flags)64 lfs_chkdq1(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
65 {
66           struct dquot *dq;
67           int i;
68           int ncurblocks, error;
69 
70           if ((error = lfs_getinoquota(ip)) != 0)
71                     return error;
72           if (change == 0)
73                     return (0);
74           if (change < 0) {
75                     for (i = 0; i < ULFS_MAXQUOTAS; i++) {
76                               if ((dq = ip->i_dquot[i]) == NODQUOT)
77                                         continue;
78                               mutex_enter(&dq->dq_interlock);
79                               ncurblocks = dq->dq_curblocks + change;
80                               if (ncurblocks >= 0)
81                                         dq->dq_curblocks = ncurblocks;
82                               else
83                                         dq->dq_curblocks = 0;
84                               dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
85                               dq->dq_flags |= DQ_MOD;
86                               mutex_exit(&dq->dq_interlock);
87                     }
88                     return (0);
89           }
90           for (i = 0; i < ULFS_MAXQUOTAS; i++) {
91                     if ((dq = ip->i_dquot[i]) == NODQUOT)
92                               continue;
93                     if ((flags & FORCE) == 0 &&
94                         kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
95                         KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, KAUTH_ARG(i),
96                         KAUTH_ARG(QL_BLOCK), NULL) != 0) {
97                               mutex_enter(&dq->dq_interlock);
98                               error = chkdqchg(ip, change, cred, i);
99                               mutex_exit(&dq->dq_interlock);
100                               if (error != 0)
101                                         return (error);
102                     }
103           }
104           for (i = 0; i < ULFS_MAXQUOTAS; i++) {
105                     if ((dq = ip->i_dquot[i]) == NODQUOT)
106                               continue;
107                     mutex_enter(&dq->dq_interlock);
108                     dq->dq_curblocks += change;
109                     dq->dq_flags |= DQ_MOD;
110                     mutex_exit(&dq->dq_interlock);
111           }
112           return (0);
113 }
114 
115 /*
116  * Check for a valid change to a users allocation.
117  * Issue an error message if appropriate.
118  */
119 static int
chkdqchg(struct inode * ip,int64_t change,kauth_cred_t cred,int type)120 chkdqchg(struct inode *ip, int64_t change, kauth_cred_t cred, int type)
121 {
122           struct dquot *dq = ip->i_dquot[type];
123           long ncurblocks = dq->dq_curblocks + change;
124 
125           KASSERT(mutex_owned(&dq->dq_interlock));
126           /*
127            * If user would exceed their hard limit, disallow space allocation.
128            */
129           if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
130                     if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
131                         ip->i_uid == kauth_cred_geteuid(cred)) {
132                               uprintf("\n%s: write failed, %s disk limit reached\n",
133                                   ITOV(ip)->v_mount->mnt_stat.f_mntonname,
134                                   lfs_quotatypes[type]);
135                               dq->dq_flags |= DQ_WARN(QL_BLOCK);
136                     }
137                     return (EDQUOT);
138           }
139           /*
140            * If user is over their soft limit for too long, disallow space
141            * allocation. Reset time limit as they cross their soft limit.
142            */
143           if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
144                     if (dq->dq_curblocks < dq->dq_bsoftlimit) {
145                               dq->dq_btime =
146                                   time_second + ip->i_ump->umq1_btime[type];
147                               if (ip->i_uid == kauth_cred_geteuid(cred))
148                                         uprintf("\n%s: warning, %s %s\n",
149                                             ITOV(ip)->v_mount->mnt_stat.f_mntonname,
150                                             lfs_quotatypes[type], "disk quota exceeded");
151                               return (0);
152                     }
153                     if (time_second > dq->dq_btime) {
154                               if ((dq->dq_flags & DQ_WARN(QL_BLOCK)) == 0 &&
155                                   ip->i_uid == kauth_cred_geteuid(cred)) {
156                                         uprintf("\n%s: write failed, %s %s\n",
157                                             ITOV(ip)->v_mount->mnt_stat.f_mntonname,
158                                             lfs_quotatypes[type],
159                                             "disk quota exceeded for too long");
160                                         dq->dq_flags |= DQ_WARN(QL_BLOCK);
161                               }
162                               return (EDQUOT);
163                     }
164           }
165           return (0);
166 }
167 
168 /*
169  * Check the inode limit, applying corrective action.
170  */
171 int
lfs_chkiq1(struct inode * ip,int32_t change,kauth_cred_t cred,int flags)172 lfs_chkiq1(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
173 {
174           struct dquot *dq;
175           int i;
176           int ncurinodes, error;
177 
178           if ((error = lfs_getinoquota(ip)) != 0)
179                     return error;
180           if (change == 0)
181                     return (0);
182           if (change < 0) {
183                     for (i = 0; i < ULFS_MAXQUOTAS; i++) {
184                               if ((dq = ip->i_dquot[i]) == NODQUOT)
185                                         continue;
186                               mutex_enter(&dq->dq_interlock);
187                               ncurinodes = dq->dq_curinodes + change;
188                               if (ncurinodes >= 0)
189                                         dq->dq_curinodes = ncurinodes;
190                               else
191                                         dq->dq_curinodes = 0;
192                               dq->dq_flags &= ~DQ_WARN(QL_FILE);
193                               dq->dq_flags |= DQ_MOD;
194                               mutex_exit(&dq->dq_interlock);
195                     }
196                     return (0);
197           }
198           for (i = 0; i < ULFS_MAXQUOTAS; i++) {
199                     if ((dq = ip->i_dquot[i]) == NODQUOT)
200                               continue;
201                     if ((flags & FORCE) == 0 && kauth_authorize_system(cred,
202                         KAUTH_SYSTEM_FS_QUOTA, 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 < ULFS_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                                   lfs_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                                             lfs_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                                             lfs_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
lfsquota1_umount(struct mount * mp,int flags)276 lfsquota1_umount(struct mount *mp, int flags)
277 {
278           int i, error;
279           struct ulfsmount *ump = VFSTOULFS(mp);
280           struct lfs *fs = ump->um_lfs;
281           struct lwp *l = curlwp;
282 
283           if ((fs->um_flags & ULFS_QUOTA) == 0)
284                     return 0;
285 
286           if ((error = vflush(mp, NULLVP, SKIPSYSTEM | flags)) != 0)
287                     return (error);
288 
289           for (i = 0; i < ULFS_MAXQUOTAS; i++) {
290                     if (ump->um_quotas[i] != NULLVP) {
291                               lfsquota1_handle_cmd_quotaoff(l, ump, i);
292                     }
293           }
294           return 0;
295 }
296 
297 /*
298  * Code to process quotactl commands.
299  */
300 
301 /*
302  * set up a quota file for a particular file system.
303  */
304 int
lfsquota1_handle_cmd_quotaon(struct lwp * l,struct ulfsmount * ump,int type,const char * fname)305 lfsquota1_handle_cmd_quotaon(struct lwp *l, struct ulfsmount *ump, int type,
306     const char *fname)
307 {
308           struct mount *mp = ump->um_mountp;
309           struct lfs *fs = ump->um_lfs;
310           struct vnode *vp, **vpp;
311           struct vnode_iterator *marker;
312           struct dquot *dq;
313           int error;
314           struct pathbuf *pb;
315 
316           if (fs->um_flags & ULFS_QUOTA2) {
317                     uprintf("%s: quotas v2 already enabled\n",
318                         mp->mnt_stat.f_mntonname);
319                     return (EBUSY);
320           }
321 
322           vpp = &ump->um_quotas[type];
323 
324           pb = pathbuf_create(fname);
325           if (pb == NULL) {
326                     return ENOMEM;
327           }
328           error = vn_open(NULL, pb, 0, FREAD|FWRITE, 0, &vp, NULL, NULL);
329           if (error != 0) {
330                     pathbuf_destroy(pb);
331                     return error;
332           }
333           pathbuf_destroy(pb);
334 
335           VOP_UNLOCK(vp);
336           if (vp->v_type != VREG) {
337                     (void) vn_close(vp, FREAD|FWRITE, l->l_cred);
338                     return (EACCES);
339           }
340           if (*vpp != vp)
341                     lfsquota1_handle_cmd_quotaoff(l, ump, type);
342           mutex_enter(&lfs_dqlock);
343           while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
344                     cv_wait(&lfs_dqcv, &lfs_dqlock);
345           ump->umq1_qflags[type] |= QTF_OPENING;
346           mutex_exit(&lfs_dqlock);
347           mp->mnt_flag |= MNT_QUOTA;
348           vp->v_vflag |= VV_SYSTEM;     /* XXXSMP */
349           *vpp = vp;
350           /*
351            * Save the credential of the process that turned on quotas.
352            * Set up the time limits for this quota.
353            */
354           kauth_cred_hold(l->l_cred);
355           ump->um_cred[type] = l->l_cred;
356           ump->umq1_btime[type] = MAX_DQ_TIME;
357           ump->umq1_itime[type] = MAX_IQ_TIME;
358           if (lfs_dqget(NULLVP, 0, ump, type, &dq) == 0) {
359                     if (dq->dq_btime > 0)
360                               ump->umq1_btime[type] = dq->dq_btime;
361                     if (dq->dq_itime > 0)
362                               ump->umq1_itime[type] = dq->dq_itime;
363                     lfs_dqrele(NULLVP, dq);
364           }
365           /*
366            * Search vnodes associated with this mount point,
367            * adding references to quota file being opened.
368            * NB: only need to add dquot's for inodes being modified.
369            */
370           vfs_vnode_iterator_init(mp, &marker);
371           while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
372                     error = vn_lock(vp, LK_EXCLUSIVE);
373                     if (error) {
374                               vrele(vp);
375                               continue;
376                     }
377                     mutex_enter(vp->v_interlock);
378                     if (VTOI(vp) == NULL || vp->v_type == VNON ||
379                         vp->v_writecount == 0) {
380                               mutex_exit(vp->v_interlock);
381                               vput(vp);
382                               continue;
383                     }
384                     mutex_exit(vp->v_interlock);
385                     if ((error = lfs_getinoquota(VTOI(vp))) != 0) {
386                               vput(vp);
387                               break;
388                     }
389                     vput(vp);
390           }
391           vfs_vnode_iterator_destroy(marker);
392 
393           mutex_enter(&lfs_dqlock);
394           ump->umq1_qflags[type] &= ~QTF_OPENING;
395           cv_broadcast(&lfs_dqcv);
396           if (error == 0)
397                     fs->um_flags |= ULFS_QUOTA;
398           mutex_exit(&lfs_dqlock);
399           if (error)
400                     lfsquota1_handle_cmd_quotaoff(l, ump, type);
401           return (error);
402 }
403 
404 /*
405  * turn off disk quotas for a filesystem.
406  */
407 int
lfsquota1_handle_cmd_quotaoff(struct lwp * l,struct ulfsmount * ump,int type)408 lfsquota1_handle_cmd_quotaoff(struct lwp *l, struct ulfsmount *ump, int type)
409 {
410           struct mount *mp = ump->um_mountp;
411           struct lfs *fs = ump->um_lfs;
412           struct vnode *vp;
413           struct vnode *qvp;
414           struct vnode_iterator *marker;
415           struct dquot *dq;
416           struct inode *ip;
417           kauth_cred_t cred;
418           int i, error;
419 
420           mutex_enter(&lfs_dqlock);
421           while ((ump->umq1_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
422                     cv_wait(&lfs_dqcv, &lfs_dqlock);
423           if ((qvp = ump->um_quotas[type]) == NULLVP) {
424                     mutex_exit(&lfs_dqlock);
425                     return (0);
426           }
427           ump->umq1_qflags[type] |= QTF_CLOSING;
428           fs->um_flags &= ~ULFS_QUOTA;
429           mutex_exit(&lfs_dqlock);
430           /*
431            * Search vnodes associated with this mount point,
432            * deleting any references to quota file being closed.
433            */
434           vfs_vnode_iterator_init(mp, &marker);
435           while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
436                     error = vn_lock(vp, LK_EXCLUSIVE);
437                     if (error) {
438                               vrele(vp);
439                               continue;
440                     }
441                     ip = VTOI(vp);
442                     if (ip == NULL || vp->v_type == VNON) {
443                               vput(vp);
444                               continue;
445                     }
446                     dq = ip->i_dquot[type];
447                     ip->i_dquot[type] = NODQUOT;
448                     lfs_dqrele(vp, dq);
449                     vput(vp);
450           }
451           vfs_vnode_iterator_destroy(marker);
452 #ifdef DIAGNOSTIC
453           lfs_dqflush(qvp);
454 #endif
455           qvp->v_vflag &= ~VV_SYSTEM;
456           error = vn_close(qvp, FREAD|FWRITE, l->l_cred);
457           mutex_enter(&lfs_dqlock);
458           ump->um_quotas[type] = NULLVP;
459           cred = ump->um_cred[type];
460           ump->um_cred[type] = NOCRED;
461           for (i = 0; i < ULFS_MAXQUOTAS; i++)
462                     if (ump->um_quotas[i] != NULLVP)
463                               break;
464           ump->umq1_qflags[type] &= ~QTF_CLOSING;
465           cv_broadcast(&lfs_dqcv);
466           mutex_exit(&lfs_dqlock);
467           kauth_cred_free(cred);
468           if (i == ULFS_MAXQUOTAS)
469                     mp->mnt_flag &= ~MNT_QUOTA;
470           return (error);
471 }
472 
473 int
lfsquota1_handle_cmd_get(struct ulfsmount * ump,const struct quotakey * qk,struct quotaval * qv)474 lfsquota1_handle_cmd_get(struct ulfsmount *ump, const struct quotakey *qk,
475     struct quotaval *qv)
476 {
477           struct dquot *dq;
478           int error;
479           struct quotaval blocks, files;
480           int idtype;
481           id_t id;
482 
483           idtype = qk->qk_idtype;
484           id = qk->qk_id;
485 
486           if (ump->um_quotas[idtype] == NULLVP)
487                     return ENODEV;
488 
489           if (id == QUOTA_DEFAULTID) { /* we want the grace period of id 0 */
490                     if ((error = lfs_dqget(NULLVP, 0, ump, idtype, &dq)) != 0)
491                               return error;
492 
493           } else {
494                     if ((error = lfs_dqget(NULLVP, id, ump, idtype, &dq)) != 0)
495                               return error;
496           }
497           lfs_dqblk_to_quotavals(&dq->dq_un.dq1_dqb, &blocks, &files);
498           lfs_dqrele(NULLVP, dq);
499           if (id == QUOTA_DEFAULTID) {
500                     if (blocks.qv_expiretime > 0)
501                               blocks.qv_grace = blocks.qv_expiretime;
502                     else
503                               blocks.qv_grace = MAX_DQ_TIME;
504                     if (files.qv_expiretime > 0)
505                               files.qv_grace = files.qv_expiretime;
506                     else
507                               files.qv_grace = MAX_DQ_TIME;
508           }
509 
510           switch (qk->qk_objtype) {
511               case QUOTA_OBJTYPE_BLOCKS:
512                     *qv = blocks;
513                     break;
514               case QUOTA_OBJTYPE_FILES:
515                     *qv = files;
516                     break;
517               default:
518                     return EINVAL;
519           }
520 
521           return 0;
522 }
523 
524 static uint32_t
quota1_encode_limit(uint64_t lim)525 quota1_encode_limit(uint64_t lim)
526 {
527           if (lim == QUOTA_NOLIMIT || lim >= 0xffffffff) {
528                     return 0;
529           }
530           return lim;
531 }
532 
533 int
lfsquota1_handle_cmd_put(struct ulfsmount * ump,const struct quotakey * key,const struct quotaval * val)534 lfsquota1_handle_cmd_put(struct ulfsmount *ump, const struct quotakey *key,
535     const struct quotaval *val)
536 {
537           struct dquot *dq;
538           struct dqblk dqb;
539           int error;
540 
541           switch (key->qk_idtype) {
542               case QUOTA_IDTYPE_USER:
543               case QUOTA_IDTYPE_GROUP:
544                     break;
545               default:
546                     return EINVAL;
547           }
548 
549           switch (key->qk_objtype) {
550               case QUOTA_OBJTYPE_BLOCKS:
551               case QUOTA_OBJTYPE_FILES:
552                     break;
553               default:
554                     return EINVAL;
555           }
556 
557           if (ump->um_quotas[key->qk_idtype] == NULLVP)
558                     return ENODEV;
559 
560           if (key->qk_id == QUOTA_DEFAULTID) {
561                     /* just update grace times */
562                     id_t id = 0;
563 
564                     if ((error = lfs_dqget(NULLVP, id, ump, key->qk_idtype, &dq)) != 0)
565                               return error;
566                     mutex_enter(&dq->dq_interlock);
567                     if (val->qv_grace != QUOTA_NOTIME) {
568                               if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS)
569                                         ump->umq1_btime[key->qk_idtype] = dq->dq_btime =
570                                                   val->qv_grace;
571                               if (key->qk_objtype == QUOTA_OBJTYPE_FILES)
572                                         ump->umq1_itime[key->qk_idtype] = dq->dq_itime =
573                                                   val->qv_grace;
574                     }
575                     dq->dq_flags |= DQ_MOD;
576                     mutex_exit(&dq->dq_interlock);
577                     lfs_dqrele(NULLVP, dq);
578                     return 0;
579           }
580 
581           if ((error = lfs_dqget(NULLVP, key->qk_id, ump, key->qk_idtype, &dq)) != 0)
582                     return (error);
583           mutex_enter(&dq->dq_interlock);
584           /*
585            * Copy all but the current values.
586            * Reset time limit if previously had no soft limit or were
587            * under it, but now have a soft limit and are over it.
588            */
589           dqb.dqb_curblocks = dq->dq_curblocks;
590           dqb.dqb_curinodes = dq->dq_curinodes;
591           dqb.dqb_btime = dq->dq_btime;
592           dqb.dqb_itime = dq->dq_itime;
593           if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
594                     dqb.dqb_bsoftlimit = quota1_encode_limit(val->qv_softlimit);
595                     dqb.dqb_bhardlimit = quota1_encode_limit(val->qv_hardlimit);
596                     dqb.dqb_isoftlimit = dq->dq_isoftlimit;
597                     dqb.dqb_ihardlimit = dq->dq_ihardlimit;
598           } else {
599                     KASSERT(key->qk_objtype == QUOTA_OBJTYPE_FILES);
600                     dqb.dqb_bsoftlimit = dq->dq_bsoftlimit;
601                     dqb.dqb_bhardlimit = dq->dq_bhardlimit;
602                     dqb.dqb_isoftlimit = quota1_encode_limit(val->qv_softlimit);
603                     dqb.dqb_ihardlimit = quota1_encode_limit(val->qv_hardlimit);
604           }
605           if (dq->dq_id == 0 && val->qv_grace != QUOTA_NOTIME) {
606                     /* also update grace time if available */
607                     if (key->qk_objtype == QUOTA_OBJTYPE_BLOCKS) {
608                               ump->umq1_btime[key->qk_idtype] = dqb.dqb_btime =
609                                         val->qv_grace;
610                     }
611                     if (key->qk_objtype == QUOTA_OBJTYPE_FILES) {
612                               ump->umq1_itime[key->qk_idtype] = dqb.dqb_itime =
613                                         val->qv_grace;
614                     }
615           }
616           if (dqb.dqb_bsoftlimit &&
617               dq->dq_curblocks >= dqb.dqb_bsoftlimit &&
618               (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
619                     dqb.dqb_btime = time_second + ump->umq1_btime[key->qk_idtype];
620           if (dqb.dqb_isoftlimit &&
621               dq->dq_curinodes >= dqb.dqb_isoftlimit &&
622               (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
623                     dqb.dqb_itime = time_second + ump->umq1_itime[key->qk_idtype];
624           dq->dq_un.dq1_dqb = dqb;
625           if (dq->dq_curblocks < dq->dq_bsoftlimit)
626                     dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
627           if (dq->dq_curinodes < dq->dq_isoftlimit)
628                     dq->dq_flags &= ~DQ_WARN(QL_FILE);
629           if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
630               dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
631                     dq->dq_flags |= DQ_FAKE;
632           else
633                     dq->dq_flags &= ~DQ_FAKE;
634           dq->dq_flags |= DQ_MOD;
635           mutex_exit(&dq->dq_interlock);
636           lfs_dqrele(NULLVP, dq);
637           return (0);
638 }
639 
640 
641 #if 0
642 /*
643  * Q_SETQUOTA - assign an entire dqblk structure.
644  */
645 int
646 setquota1(struct mount *mp, u_long id, int type, struct dqblk *dqb)
647 {
648           struct dquot *dq;
649           struct dquot *ndq;
650           struct ulfsmount *ump = VFSTOULFS(mp);
651 
652 
653           if ((error = lfs_dqget(NULLVP, id, ump, type, &ndq)) != 0)
654                     return (error);
655           dq = ndq;
656           mutex_enter(&dq->dq_interlock);
657           /*
658            * Copy all but the current values.
659            * Reset time limit if previously had no soft limit or were
660            * under it, but now have a soft limit and are over it.
661            */
662           dqb->dqb_curblocks = dq->dq_curblocks;
663           dqb->dqb_curinodes = dq->dq_curinodes;
664           if (dq->dq_id != 0) {
665                     dqb->dqb_btime = dq->dq_btime;
666                     dqb->dqb_itime = dq->dq_itime;
667           }
668           if (dqb->dqb_bsoftlimit &&
669               dq->dq_curblocks >= dqb->dqb_bsoftlimit &&
670               (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
671                     dqb->dqb_btime = time_second + ump->umq1_btime[type];
672           if (dqb->dqb_isoftlimit &&
673               dq->dq_curinodes >= dqb->dqb_isoftlimit &&
674               (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
675                     dqb->dqb_itime = time_second + ump->umq1_itime[type];
676           dq->dq_un.dq1_dqb = *dqb;
677           if (dq->dq_curblocks < dq->dq_bsoftlimit)
678                     dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
679           if (dq->dq_curinodes < dq->dq_isoftlimit)
680                     dq->dq_flags &= ~DQ_WARN(QL_FILE);
681           if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
682               dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
683                     dq->dq_flags |= DQ_FAKE;
684           else
685                     dq->dq_flags &= ~DQ_FAKE;
686           dq->dq_flags |= DQ_MOD;
687           mutex_exit(&dq->dq_interlock);
688           lfs_dqrele(NULLVP, dq);
689           return (0);
690 }
691 
692 /*
693  * Q_SETUSE - set current inode and block usage.
694  */
695 int
696 setuse(struct mount *mp, u_long id, int type, void *addr)
697 {
698           struct dquot *dq;
699           struct ulfsmount *ump = VFSTOULFS(mp);
700           struct dquot *ndq;
701           struct dqblk usage;
702           int error;
703 
704           error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
705           if (error)
706                     return (error);
707           if ((error = lfs_dqget(NULLVP, id, ump, type, &ndq)) != 0)
708                     return (error);
709           dq = ndq;
710           mutex_enter(&dq->dq_interlock);
711           /*
712            * Reset time limit if have a soft limit and were
713            * previously under it, but are now over it.
714            */
715           if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
716               usage.dqb_curblocks >= dq->dq_bsoftlimit)
717                     dq->dq_btime = time_second + ump->umq1_btime[type];
718           if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
719               usage.dqb_curinodes >= dq->dq_isoftlimit)
720                     dq->dq_itime = time_second + ump->umq1_itime[type];
721           dq->dq_curblocks = usage.dqb_curblocks;
722           dq->dq_curinodes = usage.dqb_curinodes;
723           if (dq->dq_curblocks < dq->dq_bsoftlimit)
724                     dq->dq_flags &= ~DQ_WARN(QL_BLOCK);
725           if (dq->dq_curinodes < dq->dq_isoftlimit)
726                     dq->dq_flags &= ~DQ_WARN(QL_FILE);
727           dq->dq_flags |= DQ_MOD;
728           mutex_exit(&dq->dq_interlock);
729           lfs_dqrele(NULLVP, dq);
730           return (0);
731 }
732 #endif
733 
734 /*
735  * Q_SYNC - sync quota files to disk.
736  */
737 int
lfs_q1sync(struct mount * mp)738 lfs_q1sync(struct mount *mp)
739 {
740           struct ulfsmount *ump = VFSTOULFS(mp);
741           struct vnode *vp;
742           struct vnode_iterator *marker;
743           struct dquot *dq;
744           int i, error;
745 
746           /*
747            * Check if the mount point has any quotas.
748            * If not, simply return.
749            */
750           for (i = 0; i < ULFS_MAXQUOTAS; i++)
751                     if (ump->um_quotas[i] != NULLVP)
752                               break;
753           if (i == ULFS_MAXQUOTAS)
754                     return (0);
755 
756           /*
757            * Search vnodes associated with this mount point,
758            * synchronizing any modified dquot structures.
759            */
760           vfs_vnode_iterator_init(mp, &marker);
761           while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
762                     error = vn_lock(vp, LK_EXCLUSIVE);
763                     if (error) {
764                               vrele(vp);
765                               continue;
766                     }
767                     if (VTOI(vp) == NULL || vp->v_type == VNON) {
768                               vput(vp);
769                               continue;
770                     }
771                     for (i = 0; i < ULFS_MAXQUOTAS; i++) {
772                               dq = VTOI(vp)->i_dquot[i];
773                               if (dq == NODQUOT)
774                                         continue;
775                               mutex_enter(&dq->dq_interlock);
776                               if (dq->dq_flags & DQ_MOD)
777                                         lfs_dq1sync(vp, dq);
778                               mutex_exit(&dq->dq_interlock);
779                     }
780                     vput(vp);
781           }
782           vfs_vnode_iterator_destroy(marker);
783           return (0);
784 }
785 
786 /*
787  * Obtain a dquot structure for the specified identifier and quota file
788  * reading the information from the file if necessary.
789  */
790 int
lfs_dq1get(struct vnode * dqvp,u_long id,struct ulfsmount * ump,int type,struct dquot * dq)791 lfs_dq1get(struct vnode *dqvp, u_long id, struct ulfsmount *ump, int type,
792     struct dquot *dq)
793 {
794           struct iovec aiov;
795           struct uio auio;
796           int error;
797 
798           KASSERT(mutex_owned(&dq->dq_interlock));
799           vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
800           auio.uio_iov = &aiov;
801           auio.uio_iovcnt = 1;
802           aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
803           aiov.iov_len = sizeof (struct dqblk);
804           auio.uio_resid = sizeof (struct dqblk);
805           auio.uio_offset = (off_t)id * sizeof (struct dqblk);
806           auio.uio_rw = UIO_READ;
807           UIO_SETUP_SYSSPACE(&auio);
808           error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
809           if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
810                     memset((void *)&dq->dq_un.dq1_dqb, 0, sizeof(struct dqblk));
811           VOP_UNLOCK(dqvp);
812           /*
813            * I/O error in reading quota file, release
814            * quota structure and reflect problem to caller.
815            */
816           if (error)
817                     return (error);
818           /*
819            * Check for no limit to enforce.
820            * Initialize time values if necessary.
821            */
822           if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
823               dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
824                     dq->dq_flags |= DQ_FAKE;
825           if (dq->dq_id != 0) {
826                     if (dq->dq_btime == 0)
827                               dq->dq_btime = time_second + ump->umq1_btime[type];
828                     if (dq->dq_itime == 0)
829                               dq->dq_itime = time_second + ump->umq1_itime[type];
830           }
831           return (0);
832 }
833 
834 /*
835  * Update the disk quota in the quota file.
836  */
837 int
lfs_dq1sync(struct vnode * vp,struct dquot * dq)838 lfs_dq1sync(struct vnode *vp, struct dquot *dq)
839 {
840           struct vnode *dqvp;
841           struct iovec aiov;
842           struct uio auio;
843           int error;
844 
845           if (dq == NODQUOT)
846                     panic("dq1sync: dquot");
847           KASSERT(mutex_owned(&dq->dq_interlock));
848           if ((dq->dq_flags & DQ_MOD) == 0)
849                     return (0);
850           if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
851                     panic("dq1sync: file");
852           KASSERT(dqvp != vp);
853           vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
854           auio.uio_iov = &aiov;
855           auio.uio_iovcnt = 1;
856           aiov.iov_base = (void *)&dq->dq_un.dq1_dqb;
857           aiov.iov_len = sizeof (struct dqblk);
858           auio.uio_resid = sizeof (struct dqblk);
859           auio.uio_offset = (off_t)dq->dq_id * sizeof (struct dqblk);
860           auio.uio_rw = UIO_WRITE;
861           UIO_SETUP_SYSSPACE(&auio);
862           error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
863           if (auio.uio_resid && error == 0)
864                     error = EIO;
865           dq->dq_flags &= ~DQ_MOD;
866           VOP_UNLOCK(dqvp);
867           return (error);
868 }
869