Bug Summary

File:ufs/ufs/ufs_quota.c
Warning:line 327, column 9
Although the value stored to 'dq' is used in the enclosing expression, the value is never actually read from 'dq'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ufs_quota.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -ffreestanding -mcmodel=kernel -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -target-feature -sse2 -target-feature -sse -target-feature -3dnow -target-feature -mmx -target-feature +save-args -target-feature +retpoline-external-thunk -disable-red-zone -no-implicit-float -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -nostdsysteminc -nobuiltininc -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/sys -I /usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -I /usr/src/sys/arch -I /usr/src/sys/dev/pci/drm/include -I /usr/src/sys/dev/pci/drm/include/uapi -I /usr/src/sys/dev/pci/drm/amd/include/asic_reg -I /usr/src/sys/dev/pci/drm/amd/include -I /usr/src/sys/dev/pci/drm/amd/amdgpu -I /usr/src/sys/dev/pci/drm/amd/display -I /usr/src/sys/dev/pci/drm/amd/display/include -I /usr/src/sys/dev/pci/drm/amd/display/dc -I /usr/src/sys/dev/pci/drm/amd/display/amdgpu_dm -I /usr/src/sys/dev/pci/drm/amd/pm/inc -I /usr/src/sys/dev/pci/drm/amd/pm/legacy-dpm -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu11 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu12 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu13 -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/inc -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/hwmgr -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/smumgr -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc/pmfw_if -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc/hw -I /usr/src/sys/dev/pci/drm/amd/display/dc/clk_mgr -I /usr/src/sys/dev/pci/drm/amd/display/modules/inc -I /usr/src/sys/dev/pci/drm/amd/display/modules/hdcp -I /usr/src/sys/dev/pci/drm/amd/display/dmub/inc -I /usr/src/sys/dev/pci/drm/i915 -D DDB -D DIAGNOSTIC -D KTRACE -D ACCOUNTING -D KMEMSTATS -D PTRACE -D POOL_DEBUG -D CRYPTO -D SYSVMSG -D SYSVSEM -D SYSVSHM -D UVM_SWAP_ENCRYPT -D FFS -D FFS2 -D FFS_SOFTUPDATES -D UFS_DIRHASH -D QUOTA -D EXT2FS -D MFS -D NFSCLIENT -D NFSSERVER -D CD9660 -D UDF -D MSDOSFS -D FIFO -D FUSE -D SOCKET_SPLICE -D TCP_ECN -D TCP_SIGNATURE -D INET6 -D IPSEC -D PPP_BSDCOMP -D PPP_DEFLATE -D PIPEX -D MROUTING -D MPLS -D BOOT_CONFIG -D USER_PCICONF -D APERTURE -D MTRR -D NTFS -D SUSPEND -D HIBERNATE -D PCIVERBOSE -D USBVERBOSE -D WSDISPLAY_COMPAT_USL -D WSDISPLAY_COMPAT_RAWKBD -D WSDISPLAY_DEFAULTSCREENS=6 -D X86EMU -D ONEWIREVERBOSE -D MULTIPROCESSOR -D MAXUSERS=80 -D _KERNEL -O2 -Wno-pointer-sign -Wno-address-of-packed-member -Wno-constant-conversion -Wno-unused-but-set-variable -Wno-gnu-folding-constant -fdebug-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/scan/2024-01-11-110808-61670-1 -x c /usr/src/sys/ufs/ufs/ufs_quota.c
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 */
64struct dquot {
65 LIST_ENTRY(dquot)struct { struct dquot *le_next; struct dquot **le_prev; } dq_hash; /* hash list */
66 TAILQ_ENTRY(dquot)struct { struct dquot *tqe_next; struct dquot **tqe_prev; } dq_freelist; /* free list */
67 u_int16_t dq_flags; /* flags, see below */
68 u_int16_t dq_type; /* quota type of this dquot */
69 u_int32_t dq_cnt; /* count of active references */
70 u_int32_t dq_id; /* identifier this applies to */
71 struct vnode *dq_vp; /* file backing this quota */
72 struct ucred *dq_cred; /* credentials for writing file */
73 struct dqblk dq_dqb; /* actual usage & quotas */
74};
75
76/*
77 * Flag values.
78 */
79#define DQ_LOCK0x01 0x01 /* this quota locked (no MODS) */
80#define DQ_WANT0x02 0x02 /* wakeup on unlock */
81#define DQ_MOD0x04 0x04 /* this quota modified since read */
82#define DQ_FAKE0x08 0x08 /* no limits here, just usage */
83#define DQ_BLKS0x10 0x10 /* has been warned about blk limit */
84#define DQ_INODS0x20 0x20 /* has been warned about inode limit */
85
86/*
87 * Shorthand notation.
88 */
89#define dq_bhardlimitdq_dqb.dqb_bhardlimit dq_dqb.dqb_bhardlimit
90#define dq_bsoftlimitdq_dqb.dqb_bsoftlimit dq_dqb.dqb_bsoftlimit
91#define dq_curblocksdq_dqb.dqb_curblocks dq_dqb.dqb_curblocks
92#define dq_ihardlimitdq_dqb.dqb_ihardlimit dq_dqb.dqb_ihardlimit
93#define dq_isoftlimitdq_dqb.dqb_isoftlimit dq_dqb.dqb_isoftlimit
94#define dq_curinodesdq_dqb.dqb_curinodes dq_dqb.dqb_curinodes
95#define dq_btimedq_dqb.dqb_btime dq_dqb.dqb_btime
96#define dq_itimedq_dqb.dqb_itime dq_dqb.dqb_itime
97
98/*
99 * If the system has never checked for a quota for this file, then it is
100 * set to NODQUOT. Once a write attempt is made the inode pointer is set
101 * to reference a dquot structure.
102 */
103#define NODQUOT((void *)0) NULL((void *)0)
104
105void dqref(struct dquot *);
106void dqrele(struct vnode *, struct dquot *);
107int dqsync(struct vnode *, struct dquot *);
108
109#ifdef DIAGNOSTIC1
110void chkdquot(struct inode *);
111#endif
112
113int getquota(struct mount *, u_long, int, caddr_t);
114int quotaon(struct proc *, struct mount *, int, caddr_t);
115int setquota(struct mount *, u_long, int, caddr_t);
116int setuse(struct mount *, u_long, int, caddr_t);
117
118int chkdqchg(struct inode *, long, struct ucred *, int);
119int chkiqchg(struct inode *, long, struct ucred *, int);
120
121int dqget(struct vnode *, u_long, struct ufsmount *, int,
122 struct dquot **);
123
124int quotaon_vnode(struct vnode *, void *);
125int quotaoff_vnode(struct vnode *, void *);
126int qsync_vnode(struct vnode *, void *);
127
128/*
129 * Quota name to error message mapping.
130 */
131static char *quotatypes[] = INITQFNAMES{ "user", "group", "undefined", };
132
133/*
134 * Obtain a reference to a dquot.
135 */
136void
137dqref(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 */
150int
151getinoquota(struct inode *ip)
152{
153 struct ufsmount *ump;
154 struct vnode *vp = ITOV(ip)((ip)->i_vnode);
155 int error;
156
157 ump = ip->i_ump;
158 /*
159 * Set up the user quota based on file uid.
160 * EINVAL means that quotas are not enabled.
161 */
162 if (ip->i_dquot[USRQUOTA0] == NODQUOT((void *)0) &&
163 (error =
164 dqget(vp, DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din
->di_uid : (ip)->dinode_u.ffs2_din->di_uid)
, ump, USRQUOTA0, &ip->i_dquot[USRQUOTA0])) &&
165 error != EINVAL22)
166 return (error);
167 /*
168 * Set up the group quota based on file gid.
169 * EINVAL means that quotas are not enabled.
170 */
171 if (ip->i_dquot[GRPQUOTA1] == NODQUOT((void *)0) &&
172 (error =
173 dqget(vp, DIP(ip, gid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din
->di_gid : (ip)->dinode_u.ffs2_din->di_gid)
, ump, GRPQUOTA1, &ip->i_dquot[GRPQUOTA1])) &&
174 error != EINVAL22)
175 return (error);
176 return (0);
177}
178
179/*
180 * Update disk usage, and take corrective action.
181 */
182int
183ufs_quota_alloc_blocks2(struct inode *ip, daddr_t change,
184 struct ucred *cred, enum ufs_quota_flags flags)
185{
186 struct dquot *dq;
187 int i;
188 int error;
189
190#ifdef DIAGNOSTIC1
191 chkdquot(ip);
192#endif
193
194 if (change == 0)
195 return (0);
196
197 if ((flags & UFS_QUOTA_FORCE) == 0 &&
198 (cred != NOCRED((struct ucred *)-1) && cred->cr_uid != 0)) {
199 for (i = 0; i < MAXQUOTAS2; i++) {
200 if (flags & (1 << i))
201 continue;
202 if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0))
203 continue;
204 if ((error = chkdqchg(ip, change, cred, i)) != 0)
205 return (error);
206 }
207 }
208 for (i = 0; i < MAXQUOTAS2; i++) {
209 if (flags & (1 << i))
210 continue;
211 if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0))
212 continue;
213 while (dq->dq_flags & DQ_LOCK0x01) {
214 dq->dq_flags |= DQ_WANT0x02;
215 tsleep_nsec(dq, PINOD8+1, "chkdq", INFSLP0xffffffffffffffffULL);
216 }
217 dq->dq_curblocksdq_dqb.dqb_curblocks += change;
218 dq->dq_flags |= DQ_MOD0x04;
219 }
220 return (0);
221}
222
223int
224ufs_quota_free_blocks2(struct inode *ip, daddr_t change,
225 struct ucred *cred, enum ufs_quota_flags flags)
226{
227 struct dquot *dq;
228 int i;
229
230#ifdef DIAGNOSTIC1
231 if (!VOP_ISLOCKED(ITOV(ip)((ip)->i_vnode)))
232 panic ("ufs_quota_free_blocks2: vnode is not locked");
233#endif
234
235 if (change == 0)
236 return (0);
237
238 for (i = 0; i < MAXQUOTAS2; i++) {
239 if (flags & (1 << i))
240 continue;
241 if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0))
242 continue;
243 while (dq->dq_flags & DQ_LOCK0x01) {
244 dq->dq_flags |= DQ_WANT0x02;
245 tsleep_nsec(dq, PINOD8+1, "chkdq", INFSLP0xffffffffffffffffULL);
246 }
247 if (dq->dq_curblocksdq_dqb.dqb_curblocks >= change)
248 dq->dq_curblocksdq_dqb.dqb_curblocks -= change;
249 else
250 dq->dq_curblocksdq_dqb.dqb_curblocks = 0;
251 dq->dq_flags &= ~DQ_BLKS0x10;
252 dq->dq_flags |= DQ_MOD0x04;
253 }
254 return (0);
255}
256
257/*
258 * Check for a valid change to a users allocation.
259 * Issue an error message if appropriate.
260 */
261int
262chkdqchg(struct inode *ip, long change, struct ucred *cred, int type)
263{
264 struct dquot *dq = ip->i_dquot[type];
265 long ncurblocks = dq->dq_curblocksdq_dqb.dqb_curblocks + change;
266
267 /*
268 * If user would exceed their hard limit, disallow space allocation.
269 */
270 if (ncurblocks >= dq->dq_bhardlimitdq_dqb.dqb_bhardlimit && dq->dq_bhardlimitdq_dqb.dqb_bhardlimit) {
271 if ((dq->dq_flags & DQ_BLKS0x10) == 0 &&
272 DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din
->di_uid : (ip)->dinode_u.ffs2_din->di_uid)
== cred->cr_uid) {
273 uprintf("\n%s: write failed, %s disk limit reached\n",
274 ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname,
275 quotatypes[type]);
276 dq->dq_flags |= DQ_BLKS0x10;
277 }
278 return (EDQUOT69);
279 }
280 /*
281 * If user is over their soft limit for too long, disallow space
282 * allocation. Reset time limit as they cross their soft limit.
283 */
284 if (ncurblocks >= dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit && dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit) {
285 if (dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit) {
286 dq->dq_btimedq_dqb.dqb_btime = gettime() + ip->i_ump->um_btime[type];
287 if (DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din
->di_uid : (ip)->dinode_u.ffs2_din->di_uid)
== cred->cr_uid)
288 uprintf("\n%s: warning, %s %s\n",
289 ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname,
290 quotatypes[type], "disk quota exceeded");
291 return (0);
292 }
293 if (gettime() > dq->dq_btimedq_dqb.dqb_btime) {
294 if ((dq->dq_flags & DQ_BLKS0x10) == 0 &&
295 DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din
->di_uid : (ip)->dinode_u.ffs2_din->di_uid)
== cred->cr_uid) {
296 uprintf("\n%s: write failed, %s %s\n",
297 ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname,
298 quotatypes[type],
299 "disk quota exceeded for too long");
300 dq->dq_flags |= DQ_BLKS0x10;
301 }
302 return (EDQUOT69);
303 }
304 }
305 return (0);
306}
307
308/*
309 * Check the inode limit, applying corrective action.
310 */
311int
312ufs_quota_alloc_inode2(struct inode *ip, struct ucred *cred,
313 enum ufs_quota_flags flags)
314{
315 struct dquot *dq;
316 int i;
317 int error;
318
319#ifdef DIAGNOSTIC1
320 chkdquot(ip);
321#endif
322
323 if ((flags & UFS_QUOTA_FORCE) == 0 && cred->cr_uid != 0) {
324 for (i = 0; i < MAXQUOTAS2; i++) {
325 if (flags & (1 << i))
326 continue;
327 if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0))
Although the value stored to 'dq' is used in the enclosing expression, the value is never actually read from 'dq'
328 continue;
329 if ((error = chkiqchg(ip, 1, cred, i)) != 0)
330 return (error);
331 }
332 }
333 for (i = 0; i < MAXQUOTAS2; i++) {
334 if (flags & (1 << i))
335 continue;
336 if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0))
337 continue;
338 while (dq->dq_flags & DQ_LOCK0x01) {
339 dq->dq_flags |= DQ_WANT0x02;
340 tsleep_nsec(dq, PINOD8+1, "chkiq", INFSLP0xffffffffffffffffULL);
341 }
342 dq->dq_curinodesdq_dqb.dqb_curinodes++;
343 dq->dq_flags |= DQ_MOD0x04;
344 }
345 return (0);
346}
347
348int
349ufs_quota_free_inode2(struct inode *ip, struct ucred *cred,
350 enum ufs_quota_flags flags)
351{
352 struct dquot *dq;
353 int i;
354
355#ifdef DIAGNOSTIC1
356 if (!VOP_ISLOCKED(ITOV(ip)((ip)->i_vnode)))
357 panic ("ufs_quota_free_blocks2: vnode is not locked");
358#endif
359
360 for (i = 0; i < MAXQUOTAS2; i++) {
361 if (flags & (1 << i))
362 continue;
363 if ((dq = ip->i_dquot[i]) == NODQUOT((void *)0))
364 continue;
365 while (dq->dq_flags & DQ_LOCK0x01) {
366 dq->dq_flags |= DQ_WANT0x02;
367 tsleep_nsec(dq, PINOD8+1, "chkiq", INFSLP0xffffffffffffffffULL);
368 }
369 if (dq->dq_curinodesdq_dqb.dqb_curinodes > 0)
370 dq->dq_curinodesdq_dqb.dqb_curinodes--;
371 dq->dq_flags &= ~DQ_INODS0x20;
372 dq->dq_flags |= DQ_MOD0x04;
373 }
374 return (0);
375}
376
377/*
378 * Check for a valid change to a users allocation.
379 * Issue an error message if appropriate.
380 */
381int
382chkiqchg(struct inode *ip, long change, struct ucred *cred, int type)
383{
384 struct dquot *dq = ip->i_dquot[type];
385 long ncurinodes = dq->dq_curinodesdq_dqb.dqb_curinodes + change;
386
387 /*
388 * If user would exceed their hard limit, disallow inode allocation.
389 */
390 if (ncurinodes >= dq->dq_ihardlimitdq_dqb.dqb_ihardlimit && dq->dq_ihardlimitdq_dqb.dqb_ihardlimit) {
391 if ((dq->dq_flags & DQ_INODS0x20) == 0 &&
392 DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din
->di_uid : (ip)->dinode_u.ffs2_din->di_uid)
== cred->cr_uid) {
393 uprintf("\n%s: write failed, %s inode limit reached\n",
394 ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname,
395 quotatypes[type]);
396 dq->dq_flags |= DQ_INODS0x20;
397 }
398 return (EDQUOT69);
399 }
400 /*
401 * If user is over their soft limit for too long, disallow inode
402 * allocation. Reset time limit as they cross their soft limit.
403 */
404 if (ncurinodes >= dq->dq_isoftlimitdq_dqb.dqb_isoftlimit && dq->dq_isoftlimitdq_dqb.dqb_isoftlimit) {
405 if (dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit) {
406 dq->dq_itimedq_dqb.dqb_itime = gettime() + ip->i_ump->um_itime[type];
407 if (DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din
->di_uid : (ip)->dinode_u.ffs2_din->di_uid)
== cred->cr_uid)
408 uprintf("\n%s: warning, %s %s\n",
409 ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname,
410 quotatypes[type], "inode quota exceeded");
411 return (0);
412 }
413 if (gettime() > dq->dq_itimedq_dqb.dqb_itime) {
414 if ((dq->dq_flags & DQ_INODS0x20) == 0 &&
415 DIP(ip, uid)(((ip)->i_ump->um_fstype == 1) ? (ip)->dinode_u.ffs1_din
->di_uid : (ip)->dinode_u.ffs2_din->di_uid)
== cred->cr_uid) {
416 uprintf("\n%s: write failed, %s %s\n",
417 ITOV(ip)((ip)->i_vnode)->v_mount->mnt_stat.f_mntonname,
418 quotatypes[type],
419 "inode quota exceeded for too long");
420 dq->dq_flags |= DQ_INODS0x20;
421 }
422 return (EDQUOT69);
423 }
424 }
425 return (0);
426}
427
428#ifdef DIAGNOSTIC1
429/*
430 * On filesystems with quotas enabled, it is an error for a file to change
431 * size and not to have a dquot structure associated with it.
432 */
433void
434chkdquot(struct inode *ip)
435{
436 struct ufsmount *ump = ip->i_ump;
437 int i;
438 struct vnode *vp = ITOV(ip)((ip)->i_vnode);
439
440 if (!VOP_ISLOCKED(vp))
441 panic ("chkdquot: vnode is not locked");
442
443 for (i = 0; i < MAXQUOTAS2; i++) {
444 if (ump->um_quotas[i] == NULLVP((struct vnode *)((void *)0)) ||
445 (ump->um_qflags[i] & (QTF_OPENING0x01|QTF_CLOSING0x02)))
446 continue;
447 if (ip->i_dquot[i] == NODQUOT((void *)0)) {
448 vprint("chkdquot: missing dquot", ITOV(ip)((ip)->i_vnode));
449 panic("missing dquot");
450 }
451 }
452}
453#endif
454
455/*
456 * Code to process quotactl commands.
457 */
458
459int
460quotaon_vnode(struct vnode *vp, void *arg)
461{
462 int error;
463
464 if (vp->v_type == VNON || vp->v_writecount == 0)
465 return (0);
466
467 if (vget(vp, LK_EXCLUSIVE0x0001UL)) {
468 return (0);
469 }
470
471 error = getinoquota(VTOI(vp)((struct inode *)(vp)->v_data));
472 vput(vp);
473
474 return (error);
475}
476
477/*
478 * Q_QUOTAON - set up a quota file for a particular file system.
479 */
480int
481quotaon(struct proc *p, struct mount *mp, int type, caddr_t fname)
482{
483 struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data));
484 struct vnode *vp, **vpp;
485 struct dquot *dq;
486 int error;
487 struct nameidata nd;
488
489#ifdef DIAGNOSTIC1
490 if (!vfs_isbusy(mp))
491 panic ("quotaon: mount point not busy");
492#endif
493
494 vpp = &ump->um_quotas[type];
495 NDINIT(&nd, 0, 0, UIO_USERSPACE, fname, p)ndinitat(&nd, 0, 0, UIO_USERSPACE, -100, fname, p);
496 if ((error = vn_open(&nd, FREAD0x0001|FWRITE0x0002, 0)) != 0)
497 return (error);
498 vp = nd.ni_vp;
499 VOP_UNLOCK(vp);
500 if (vp->v_type != VREG) {
501 (void) vn_close(vp, FREAD0x0001|FWRITE0x0002, p->p_ucred, p);
502 return (EACCES13);
503 }
504
505 /*
506 * Update the vnode and ucred for quota file updates
507 */
508 if (*vpp != vp) {
509 quotaoff(p, mp, type);
510 *vpp = vp;
511 crhold(p->p_ucred);
512 ump->um_cred[type] = p->p_ucred;
513 } else {
514 struct ucred *ocred = ump->um_cred[type];
515
516 (void) vn_close(vp, FREAD0x0001|FWRITE0x0002, ocred, p);
517 if (ocred != p->p_ucred) {
518 crhold(p->p_ucred);
519 ump->um_cred[type] = p->p_ucred;
520 crfree(ocred);
521 }
522 }
523
524 ump->um_qflags[type] |= QTF_OPENING0x01;
525 mp->mnt_flag |= MNT_QUOTA0x00002000;
526 vp->v_flag |= VSYSTEM0x0004;
527 /*
528 * Set up the time limits for this quota.
529 */
530 ump->um_btime[type] = MAX_DQ_TIME(7*24*60*60);
531 ump->um_itime[type] = MAX_IQ_TIME(7*24*60*60);
532 if (dqget(NULLVP((struct vnode *)((void *)0)), 0, ump, type, &dq) == 0) {
533 if (dq->dq_btimedq_dqb.dqb_btime > 0)
534 ump->um_btime[type] = dq->dq_btimedq_dqb.dqb_btime;
535 if (dq->dq_itimedq_dqb.dqb_itime > 0)
536 ump->um_itime[type] = dq->dq_itimedq_dqb.dqb_itime;
537 dqrele(NULLVP((struct vnode *)((void *)0)), dq);
538 }
539 /*
540 * Search vnodes associated with this mount point,
541 * adding references to quota file being opened.
542 * NB: only need to add dquot's for inodes being modified.
543 */
544 error = vfs_mount_foreach_vnode(mp, quotaon_vnode, NULL((void *)0));
545
546 ump->um_qflags[type] &= ~QTF_OPENING0x01;
547 if (error)
548 quotaoff(p, mp, type);
549 return (error);
550}
551
552struct quotaoff_arg {
553 struct proc *p;
554 int type;
555};
556
557int
558quotaoff_vnode(struct vnode *vp, void *arg)
559{
560 struct quotaoff_arg *qa = (struct quotaoff_arg *)arg;
561 struct inode *ip;
562 struct dquot *dq;
563
564 if (vp->v_type == VNON)
565 return (0);
566
567 if (vget(vp, LK_EXCLUSIVE0x0001UL))
568 return (0);
569 ip = VTOI(vp)((struct inode *)(vp)->v_data);
570 dq = ip->i_dquot[qa->type];
571 ip->i_dquot[qa->type] = NODQUOT((void *)0);
572 dqrele(vp, dq);
573 vput(vp);
574 return (0);
575}
576
577/*
578 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
579 */
580int
581quotaoff(struct proc *p, struct mount *mp, int type)
582{
583 struct vnode *qvp;
584 struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data));
585 struct quotaoff_arg qa;
586 int error;
587
588#ifdef DIAGNOSTIC1
589 if (!vfs_isbusy(mp))
590 panic ("quotaoff: mount point not busy");
591#endif
592 if ((qvp = ump->um_quotas[type]) == NULLVP((struct vnode *)((void *)0)))
593 return (0);
594 ump->um_qflags[type] |= QTF_CLOSING0x02;
595 /*
596 * Search vnodes associated with this mount point,
597 * deleting any references to quota file being closed.
598 */
599 qa.p = p;
600 qa.type = type;
601 vfs_mount_foreach_vnode(mp, quotaoff_vnode, &qa);
602
603 error = vn_close(qvp, FREAD0x0001|FWRITE0x0002, p->p_ucred, p);
604 ump->um_quotas[type] = NULLVP((struct vnode *)((void *)0));
605 crfree(ump->um_cred[type]);
606 ump->um_cred[type] = NOCRED((struct ucred *)-1);
607 ump->um_qflags[type] &= ~QTF_CLOSING0x02;
608 for (type = 0; type < MAXQUOTAS2; type++)
609 if (ump->um_quotas[type] != NULLVP((struct vnode *)((void *)0)))
610 break;
611 if (type == MAXQUOTAS2)
612 mp->mnt_flag &= ~MNT_QUOTA0x00002000;
613 return (error);
614}
615
616/*
617 * Q_GETQUOTA - return current values in a dqblk structure.
618 */
619int
620getquota(struct mount *mp, u_long id, int type, caddr_t addr)
621{
622 struct dquot *dq;
623 int error;
624
625 if ((error = dqget(NULLVP((struct vnode *)((void *)0)), id, VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data)), type, &dq)) != 0)
626 return (error);
627 error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
628#ifdef KTRACE1
629 if (error == 0) {
630 struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r"
(__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self)));
__ci;})->ci_curproc
;
631 if (KTRPOINT(p, KTR_STRUCT)((p)->p_p->ps_traceflag & (1<<(8)) &&
((p)->p_flag & 0x00000001) == 0)
)
632 ktrquota(p, &dq->dq_dqb)ktrstruct((p), "quota", (&dq->dq_dqb), sizeof(struct dqblk
))
;
633 }
634#endif
635
636 dqrele(NULLVP((struct vnode *)((void *)0)), dq);
637 return (error);
638}
639
640/*
641 * Q_SETQUOTA - assign an entire dqblk structure.
642 */
643int
644setquota(struct mount *mp, u_long id, int type, caddr_t addr)
645{
646 struct dquot *dq;
647 struct dquot *ndq;
648 struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data));
649 struct dqblk newlim;
650 int error;
651
652 error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk));
653 if (error)
654 return (error);
655#ifdef KTRACE1
656 {
657 struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r"
(__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self)));
__ci;})->ci_curproc
;
658 if (KTRPOINT(p, KTR_STRUCT)((p)->p_p->ps_traceflag & (1<<(8)) &&
((p)->p_flag & 0x00000001) == 0)
)
659 ktrquota(p, &newlim)ktrstruct((p), "quota", (&newlim), sizeof(struct dqblk));
660 }
661#endif
662
663 if ((error = dqget(NULLVP((struct vnode *)((void *)0)), id, ump, type, &ndq)) != 0)
664 return (error);
665 dq = ndq;
666 while (dq->dq_flags & DQ_LOCK0x01) {
667 dq->dq_flags |= DQ_WANT0x02;
668 tsleep_nsec(dq, PINOD8+1, "setquota", INFSLP0xffffffffffffffffULL);
669 }
670 /*
671 * Copy all but the current values.
672 * Reset time limit if previously had no soft limit or were
673 * under it, but now have a soft limit and are over it.
674 */
675 newlim.dqb_curblocks = dq->dq_curblocksdq_dqb.dqb_curblocks;
676 newlim.dqb_curinodes = dq->dq_curinodesdq_dqb.dqb_curinodes;
677 if (dq->dq_id != 0) {
678 newlim.dqb_btime = dq->dq_btimedq_dqb.dqb_btime;
679 newlim.dqb_itime = dq->dq_itimedq_dqb.dqb_itime;
680 }
681 if (newlim.dqb_bsoftlimit &&
682 dq->dq_curblocksdq_dqb.dqb_curblocks >= newlim.dqb_bsoftlimit &&
683 (dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit == 0 || dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit))
684 newlim.dqb_btime = gettime() + ump->um_btime[type];
685 if (newlim.dqb_isoftlimit &&
686 dq->dq_curinodesdq_dqb.dqb_curinodes >= newlim.dqb_isoftlimit &&
687 (dq->dq_isoftlimitdq_dqb.dqb_isoftlimit == 0 || dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit))
688 newlim.dqb_itime = gettime() + ump->um_itime[type];
689 dq->dq_dqb = newlim;
690 if (dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit)
691 dq->dq_flags &= ~DQ_BLKS0x10;
692 if (dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit)
693 dq->dq_flags &= ~DQ_INODS0x20;
694 if (dq->dq_isoftlimitdq_dqb.dqb_isoftlimit == 0 && dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit == 0 &&
695 dq->dq_ihardlimitdq_dqb.dqb_ihardlimit == 0 && dq->dq_bhardlimitdq_dqb.dqb_bhardlimit == 0)
696 dq->dq_flags |= DQ_FAKE0x08;
697 else
698 dq->dq_flags &= ~DQ_FAKE0x08;
699 dq->dq_flags |= DQ_MOD0x04;
700 dqrele(NULLVP((struct vnode *)((void *)0)), dq);
701 return (0);
702}
703
704/*
705 * Q_SETUSE - set current inode and block usage.
706 */
707int
708setuse(struct mount *mp, u_long id, int type, caddr_t addr)
709{
710 struct dquot *dq;
711 struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data));
712 struct dquot *ndq;
713 struct dqblk usage;
714 int error;
715
716 error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk));
717 if (error)
718 return (error);
719#ifdef KTRACE1
720 {
721 struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r"
(__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self)));
__ci;})->ci_curproc
;
722 if (KTRPOINT(p, KTR_STRUCT)((p)->p_p->ps_traceflag & (1<<(8)) &&
((p)->p_flag & 0x00000001) == 0)
)
723 ktrquota(p, &usage)ktrstruct((p), "quota", (&usage), sizeof(struct dqblk));
724 }
725#endif
726
727 if ((error = dqget(NULLVP((struct vnode *)((void *)0)), id, ump, type, &ndq)) != 0)
728 return (error);
729 dq = ndq;
730 while (dq->dq_flags & DQ_LOCK0x01) {
731 dq->dq_flags |= DQ_WANT0x02;
732 tsleep_nsec(dq, PINOD8+1, "setuse", INFSLP0xffffffffffffffffULL);
733 }
734 /*
735 * Reset time limit if have a soft limit and were
736 * previously under it, but are now over it.
737 */
738 if (dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit && dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit &&
739 usage.dqb_curblocks >= dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit)
740 dq->dq_btimedq_dqb.dqb_btime = gettime() + ump->um_btime[type];
741 if (dq->dq_isoftlimitdq_dqb.dqb_isoftlimit && dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit &&
742 usage.dqb_curinodes >= dq->dq_isoftlimitdq_dqb.dqb_isoftlimit)
743 dq->dq_itimedq_dqb.dqb_itime = gettime() + ump->um_itime[type];
744 dq->dq_curblocksdq_dqb.dqb_curblocks = usage.dqb_curblocks;
745 dq->dq_curinodesdq_dqb.dqb_curinodes = usage.dqb_curinodes;
746 if (dq->dq_curblocksdq_dqb.dqb_curblocks < dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit)
747 dq->dq_flags &= ~DQ_BLKS0x10;
748 if (dq->dq_curinodesdq_dqb.dqb_curinodes < dq->dq_isoftlimitdq_dqb.dqb_isoftlimit)
749 dq->dq_flags &= ~DQ_INODS0x20;
750 dq->dq_flags |= DQ_MOD0x04;
751 dqrele(NULLVP((struct vnode *)((void *)0)), dq);
752 return (0);
753}
754
755int
756qsync_vnode(struct vnode *vp, void *arg)
757{
758 int i;
759 struct dquot *dq;
760
761 if (vp->v_type == VNON)
762 return (0);
763
764 if (vget(vp, LK_EXCLUSIVE0x0001UL | LK_NOWAIT0x0040UL))
765 return (0);
766
767 for (i = 0; i < MAXQUOTAS2; i++) {
768 dq = VTOI(vp)((struct inode *)(vp)->v_data)->i_dquot[i];
769 if (dq != NODQUOT((void *)0) && (dq->dq_flags & DQ_MOD0x04))
770 dqsync(vp, dq);
771 }
772 vput(vp);
773 return (0);
774}
775
776/*
777 * Q_SYNC - sync quota files to disk.
778 */
779int
780qsync(struct mount *mp)
781{
782 struct ufsmount *ump = VFSTOUFS(mp)((struct ufsmount *)((mp)->mnt_data));
783 int i;
784
785 /*
786 * Check if the mount point has any quotas.
787 * If not, simply return.
788 */
789 for (i = 0; i < MAXQUOTAS2; i++)
790 if (ump->um_quotas[i] != NULLVP((struct vnode *)((void *)0)))
791 break;
792 if (i == MAXQUOTAS2)
793 return (0);
794 /*
795 * Search vnodes associated with this mount point,
796 * synchronizing any modified dquot structures.
797 */
798 vfs_mount_foreach_vnode(mp, qsync_vnode, NULL((void *)0));
799 return (0);
800}
801
802/*
803 * Code pertaining to management of the in-core dquot data structures.
804 */
805LIST_HEAD(dqhash, dquot)struct dqhash { struct dquot *lh_first; } *dqhashtbl;
806SIPHASH_KEY dqhashkey;
807u_long dqhash;
808
809/*
810 * Dquot free list.
811 */
812#define DQUOTINC5 5 /* minimum free dquots desired */
813TAILQ_HEAD(dqfreelist, dquot)struct dqfreelist { struct dquot *tqh_first; struct dquot **tqh_last
; }
dqfreelist;
814long numdquot, desireddquot = DQUOTINC5;
815
816/*
817 * Initialize the quota system.
818 */
819void
820ufs_quota_init(void)
821{
822 dqhashtbl = hashinit(initialvnodes, M_DQUOT27, M_WAITOK0x0001, &dqhash);
823 arc4random_buf(&dqhashkey, sizeof(dqhashkey));
824 TAILQ_INIT(&dqfreelist)do { (&dqfreelist)->tqh_first = ((void *)0); (&dqfreelist
)->tqh_last = &(&dqfreelist)->tqh_first; } while
(0)
;
825}
826
827/*
828 * Obtain a dquot structure for the specified identifier and quota file
829 * reading the information from the file if necessary.
830 */
831int
832dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
833 struct dquot **dqp)
834{
835 SIPHASH_CTX ctx;
836 struct dquot *dq;
837 struct dqhash *dqh;
838 struct vnode *dqvp;
839 struct iovec aiov;
840 struct uio auio;
841 int error;
842
843 dqvp = ump->um_quotas[type];
844 if (dqvp == NULLVP((struct vnode *)((void *)0)) || (ump->um_qflags[type] & QTF_CLOSING0x02)) {
845 *dqp = NODQUOT((void *)0);
846 return (EINVAL22);
847 }
848 /*
849 * Check the cache first.
850 */
851 SipHash24_Init(&ctx, &dqhashkey)SipHash_Init((&ctx), (&dqhashkey));
852 SipHash24_Update(&ctx, &dqvp, sizeof(dqvp))SipHash_Update((&ctx), 2, 4, (&dqvp), (sizeof(dqvp)));
853 SipHash24_Update(&ctx, &id, sizeof(id))SipHash_Update((&ctx), 2, 4, (&id), (sizeof(id)));
854 dqh = &dqhashtbl[SipHash24_End(&ctx)SipHash_End((&ctx), 2, 4) & dqhash];
855
856 LIST_FOREACH(dq, dqh, dq_hash)for((dq) = ((dqh)->lh_first); (dq)!= ((void *)0); (dq) = (
(dq)->dq_hash.le_next))
{
857 if (dq->dq_id != id ||
858 dq->dq_vp != dqvp)
859 continue;
860 /*
861 * Cache hit with no references. Take
862 * the structure off the free list.
863 */
864 if (dq->dq_cnt == 0)
865 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist)do { if (((dq)->dq_freelist.tqe_next) != ((void *)0)) (dq)
->dq_freelist.tqe_next->dq_freelist.tqe_prev = (dq)->
dq_freelist.tqe_prev; else (&dqfreelist)->tqh_last = (
dq)->dq_freelist.tqe_prev; *(dq)->dq_freelist.tqe_prev =
(dq)->dq_freelist.tqe_next; ((dq)->dq_freelist.tqe_prev
) = ((void *)-1); ((dq)->dq_freelist.tqe_next) = ((void *)
-1); } while (0)
;
866 dqref(dq);
867 *dqp = dq;
868 return (0);
869 }
870 /*
871 * Not in cache, allocate a new one.
872 */
873 if (TAILQ_FIRST(&dqfreelist)((&dqfreelist)->tqh_first) == NODQUOT((void *)0) &&
874 numdquot < MAXQUOTAS2 * initialvnodes)
875 desireddquot += DQUOTINC5;
876 if (numdquot < desireddquot) {
877 dq = malloc(sizeof *dq, M_DQUOT27, M_WAITOK0x0001 | M_ZERO0x0008);
878 numdquot++;
879 } else {
880 if ((dq = TAILQ_FIRST(&dqfreelist)((&dqfreelist)->tqh_first)) == NULL((void *)0)) {
881 tablefull("dquot");
882 *dqp = NODQUOT((void *)0);
883 return (EUSERS68);
884 }
885 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD0x04))
886 panic("free dquot isn't");
887 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist)do { if (((dq)->dq_freelist.tqe_next) != ((void *)0)) (dq)
->dq_freelist.tqe_next->dq_freelist.tqe_prev = (dq)->
dq_freelist.tqe_prev; else (&dqfreelist)->tqh_last = (
dq)->dq_freelist.tqe_prev; *(dq)->dq_freelist.tqe_prev =
(dq)->dq_freelist.tqe_next; ((dq)->dq_freelist.tqe_prev
) = ((void *)-1); ((dq)->dq_freelist.tqe_next) = ((void *)
-1); } while (0)
;
888 LIST_REMOVE(dq, dq_hash)do { if ((dq)->dq_hash.le_next != ((void *)0)) (dq)->dq_hash
.le_next->dq_hash.le_prev = (dq)->dq_hash.le_prev; *(dq
)->dq_hash.le_prev = (dq)->dq_hash.le_next; ((dq)->dq_hash
.le_prev) = ((void *)-1); ((dq)->dq_hash.le_next) = ((void
*)-1); } while (0)
;
889 crfree(dq->dq_cred);
890 dq->dq_cred = NOCRED((struct ucred *)-1);
891 }
892 /*
893 * Initialize the contents of the dquot structure.
894 */
895 if (vp != dqvp)
896 vn_lock(dqvp, LK_EXCLUSIVE0x0001UL | LK_RETRY0x2000UL);
897 LIST_INSERT_HEAD(dqh, dq, dq_hash)do { if (((dq)->dq_hash.le_next = (dqh)->lh_first) != (
(void *)0)) (dqh)->lh_first->dq_hash.le_prev = &(dq
)->dq_hash.le_next; (dqh)->lh_first = (dq); (dq)->dq_hash
.le_prev = &(dqh)->lh_first; } while (0)
;
898 dqref(dq);
899 dq->dq_flags = DQ_LOCK0x01;
900 dq->dq_id = id;
901 dq->dq_vp = dqvp;
902 dq->dq_type = type;
903 crhold(ump->um_cred[type]);
904 dq->dq_cred = ump->um_cred[type];
905 auio.uio_iov = &aiov;
906 auio.uio_iovcnt = 1;
907 aiov.iov_base = (caddr_t)&dq->dq_dqb;
908 aiov.iov_len = sizeof (struct dqblk);
909 auio.uio_resid = sizeof (struct dqblk);
910 auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
911 auio.uio_segflg = UIO_SYSSPACE;
912 auio.uio_rw = UIO_READ;
913 auio.uio_procp = NULL((void *)0);
914 error = VOP_READ(dqvp, &auio, 0, dq->dq_cred);
915 if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
916 memset(&dq->dq_dqb, 0, sizeof(struct dqblk))__builtin_memset((&dq->dq_dqb), (0), (sizeof(struct dqblk
)))
;
917 if (vp != dqvp)
918 VOP_UNLOCK(dqvp);
919 if (dq->dq_flags & DQ_WANT0x02)
920 wakeup(dq);
921 dq->dq_flags = 0;
922 /*
923 * I/O error in reading quota file, release
924 * quota structure and reflect problem to caller.
925 */
926 if (error) {
927 LIST_REMOVE(dq, dq_hash)do { if ((dq)->dq_hash.le_next != ((void *)0)) (dq)->dq_hash
.le_next->dq_hash.le_prev = (dq)->dq_hash.le_prev; *(dq
)->dq_hash.le_prev = (dq)->dq_hash.le_next; ((dq)->dq_hash
.le_prev) = ((void *)-1); ((dq)->dq_hash.le_next) = ((void
*)-1); } while (0)
;
928 dqrele(vp, dq);
929 *dqp = NODQUOT((void *)0);
930 return (error);
931 }
932 /*
933 * Check for no limit to enforce.
934 * Initialize time values if necessary.
935 */
936 if (dq->dq_isoftlimitdq_dqb.dqb_isoftlimit == 0 && dq->dq_bsoftlimitdq_dqb.dqb_bsoftlimit == 0 &&
937 dq->dq_ihardlimitdq_dqb.dqb_ihardlimit == 0 && dq->dq_bhardlimitdq_dqb.dqb_bhardlimit == 0)
938 dq->dq_flags |= DQ_FAKE0x08;
939 if (dq->dq_id != 0) {
940 if (dq->dq_btimedq_dqb.dqb_btime == 0)
941 dq->dq_btimedq_dqb.dqb_btime = gettime() + ump->um_btime[type];
942 if (dq->dq_itimedq_dqb.dqb_itime == 0)
943 dq->dq_itimedq_dqb.dqb_itime = gettime() + ump->um_itime[type];
944 }
945 *dqp = dq;
946 return (0);
947}
948
949/*
950 * Release a reference to a dquot.
951 */
952void
953dqrele(struct vnode *vp, struct dquot *dq)
954{
955
956 if (dq == NODQUOT((void *)0))
957 return;
958 if (dq->dq_cnt > 1) {
959 dq->dq_cnt--;
960 return;
961 }
962 if (dq->dq_flags & DQ_MOD0x04)
963 (void) dqsync(vp, dq);
964 if (--dq->dq_cnt > 0)
965 return;
966 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist)do { (dq)->dq_freelist.tqe_next = ((void *)0); (dq)->dq_freelist
.tqe_prev = (&dqfreelist)->tqh_last; *(&dqfreelist
)->tqh_last = (dq); (&dqfreelist)->tqh_last = &
(dq)->dq_freelist.tqe_next; } while (0)
;
967}
968
969/*
970 * Update the disk quota in the quota file.
971 */
972int
973dqsync(struct vnode *vp, struct dquot *dq)
974{
975 struct vnode *dqvp;
976 struct iovec aiov;
977 struct uio auio;
978 int error;
979
980 if (dq == NODQUOT((void *)0))
981 panic("dqsync: dquot");
982 if ((dq->dq_flags & DQ_MOD0x04) == 0)
983 return (0);
984 if ((dqvp = dq->dq_vp) == NULLVP((struct vnode *)((void *)0)))
985 panic("dqsync: file");
986
987 if (vp != dqvp)
988 vn_lock(dqvp, LK_EXCLUSIVE0x0001UL | LK_RETRY0x2000UL);
989 while (dq->dq_flags & DQ_LOCK0x01) {
990 dq->dq_flags |= DQ_WANT0x02;
991 tsleep_nsec(dq, PINOD8+2, "dqsync", INFSLP0xffffffffffffffffULL);
992 if ((dq->dq_flags & DQ_MOD0x04) == 0) {
993 if (vp != dqvp)
994 VOP_UNLOCK(dqvp);
995 return (0);
996 }
997 }
998 dq->dq_flags |= DQ_LOCK0x01;
999 auio.uio_iov = &aiov;
1000 auio.uio_iovcnt = 1;
1001 aiov.iov_base = (caddr_t)&dq->dq_dqb;
1002 aiov.iov_len = sizeof (struct dqblk);
1003 auio.uio_resid = sizeof (struct dqblk);
1004 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
1005 auio.uio_segflg = UIO_SYSSPACE;
1006 auio.uio_rw = UIO_WRITE;
1007 auio.uio_procp = NULL((void *)0);
1008 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_cred);
1009 if (auio.uio_resid && error == 0)
1010 error = EIO5;
1011 if (dq->dq_flags & DQ_WANT0x02)
1012 wakeup(dq);
1013 dq->dq_flags &= ~(DQ_MOD0x04|DQ_LOCK0x01|DQ_WANT0x02);
1014 if (vp != dqvp)
1015 VOP_UNLOCK(dqvp);
1016 return (error);
1017}
1018
1019int
1020ufs_quota_delete(struct inode *ip)
1021{
1022 struct vnode *vp = ITOV(ip)((ip)->i_vnode);
1023 int i;
1024 for (i = 0; i < MAXQUOTAS2; i++) {
1025 if (ip->i_dquot[i] != NODQUOT((void *)0)) {
1026 dqrele(vp, ip->i_dquot[i]);
1027 ip->i_dquot[i] = NODQUOT((void *)0);
1028 }
1029 }
1030
1031 return (0);
1032}
1033
1034/*
1035 * Do operations associated with quotas
1036 */
1037int
1038ufs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
1039 struct proc *p)
1040{
1041 int cmd, type, error;
1042
1043 if (uid == -1)
1044 uid = p->p_ucred->cr_ruid;
1045 cmd = cmds >> SUBCMDSHIFT8;
1046
1047 switch (cmd) {
1048 case Q_SYNC0x0600:
1049 break;
1050 case Q_GETQUOTA0x0300:
1051 if (uid == p->p_ucred->cr_ruid)
1052 break;
1053 /* FALLTHROUGH */
1054 default:
1055 if ((error = suser(p)) != 0)
1056 return (error);
1057 }
1058
1059 type = cmds & SUBCMDMASK0x00ff;
1060 if ((u_int)type >= MAXQUOTAS2)
1061 return (EINVAL22);
1062
1063 if (vfs_busy(mp, VB_READ0x01|VB_NOWAIT0x04))
1064 return (0);
1065
1066
1067 switch (cmd) {
1068
1069 case Q_QUOTAON0x0100:
1070 error = quotaon(p, mp, type, arg);
1071 break;
1072
1073 case Q_QUOTAOFF0x0200:
1074 error = quotaoff(p, mp, type);
1075 break;
1076
1077 case Q_SETQUOTA0x0400:
1078 error = setquota(mp, uid, type, arg) ;
1079 break;
1080
1081 case Q_SETUSE0x0500:
1082 error = setuse(mp, uid, type, arg);
1083 break;
1084
1085 case Q_GETQUOTA0x0300:
1086 error = getquota(mp, uid, type, arg);
1087 break;
1088
1089 case Q_SYNC0x0600:
1090 error = qsync(mp);
1091 break;
1092
1093 default:
1094 error = EINVAL22;
1095 break;
1096 }
1097
1098 vfs_unbusy(mp);
1099 return (error);
1100}