quota2.c revision 1.2 1 1.2 bouyer /* $NetBSD: quota2.c,v 1.2 2011/03/06 17:08:16 bouyer Exp $ */
2 1.2 bouyer /*-
3 1.2 bouyer * Copyright (c) 2010 Manuel Bouyer
4 1.2 bouyer * All rights reserved.
5 1.2 bouyer * This software is distributed under the following condiions
6 1.2 bouyer * compliant with the NetBSD foundation policy.
7 1.2 bouyer *
8 1.2 bouyer * Redistribution and use in source and binary forms, with or without
9 1.2 bouyer * modification, are permitted provided that the following conditions
10 1.2 bouyer * are met:
11 1.2 bouyer * 1. Redistributions of source code must retain the above copyright
12 1.2 bouyer * notice, this list of conditions and the following disclaimer.
13 1.2 bouyer * 2. Redistributions in binary form must reproduce the above copyright
14 1.2 bouyer * notice, this list of conditions and the following disclaimer in the
15 1.2 bouyer * documentation and/or other materials provided with the distribution.
16 1.2 bouyer *
17 1.2 bouyer * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 1.2 bouyer * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 1.2 bouyer * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 1.2 bouyer * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 1.2 bouyer * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 1.2 bouyer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 1.2 bouyer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 1.2 bouyer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 1.2 bouyer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 1.2 bouyer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 1.2 bouyer * POSSIBILITY OF SUCH DAMAGE.
28 1.2 bouyer */
29 1.2 bouyer
30 1.2 bouyer #include <sys/param.h>
31 1.2 bouyer #include <sys/time.h>
32 1.2 bouyer
33 1.2 bouyer #include <ufs/ufs/dinode.h>
34 1.2 bouyer #include <ufs/ffs/fs.h>
35 1.2 bouyer #include <ufs/ffs/ffs_extern.h>
36 1.2 bouyer #include <ufs/ufs/ufs_bswap.h>
37 1.2 bouyer
38 1.2 bouyer #include <err.h>
39 1.2 bouyer #include <string.h>
40 1.2 bouyer #include <malloc.h>
41 1.2 bouyer #include <ufs/ufs/quota2.h>
42 1.2 bouyer
43 1.2 bouyer #include "fsutil.h"
44 1.2 bouyer #include "fsck.h"
45 1.2 bouyer #include "extern.h"
46 1.2 bouyer #include "exitvalues.h"
47 1.2 bouyer
48 1.2 bouyer static char **quotamap;
49 1.2 bouyer
50 1.2 bouyer void
51 1.2 bouyer quota2_create_inode(struct fs *fs, int type)
52 1.2 bouyer {
53 1.2 bouyer ino_t ino;
54 1.2 bouyer struct bufarea *bp;
55 1.2 bouyer union dinode *dp;
56 1.2 bouyer
57 1.2 bouyer ino = allocino(0, IFREG);
58 1.2 bouyer dp = ginode(ino);
59 1.2 bouyer DIP_SET(dp, nlink, iswap16(1));
60 1.2 bouyer inodirty();
61 1.2 bouyer
62 1.2 bouyer if (readblk(dp, 0, &bp) != sblock->fs_bsize ||
63 1.2 bouyer bp->b_errs != 0) {
64 1.2 bouyer freeino(ino);
65 1.2 bouyer return;
66 1.2 bouyer }
67 1.2 bouyer quota2_create_blk0(sblock->fs_bsize, bp->b_un.b_buf,
68 1.2 bouyer q2h_hash_shift, type, needswap);
69 1.2 bouyer dirty(bp);
70 1.2 bouyer bp->b_flags &= ~B_INUSE;
71 1.2 bouyer sblock->fs_quotafile[type] = ino;
72 1.2 bouyer sbdirty();
73 1.2 bouyer return;
74 1.2 bouyer }
75 1.2 bouyer
76 1.2 bouyer int
77 1.2 bouyer quota2_alloc_quota(union dinode * dp, struct bufarea *hbp,
78 1.2 bouyer uid_t uid, uint64_t u_b, uint64_t u_i)
79 1.2 bouyer {
80 1.2 bouyer struct bufarea *bp;
81 1.2 bouyer struct quota2_header *q2h = (void *)hbp->b_un.b_buf;
82 1.2 bouyer struct quota2_entry *q2e;
83 1.2 bouyer uint64_t off;
84 1.2 bouyer uint64_t baseoff;
85 1.2 bouyer
86 1.2 bouyer off = iswap64(q2h->q2h_free);
87 1.2 bouyer if (off == 0) {
88 1.2 bouyer baseoff = iswap64(DIP(dp, size));
89 1.2 bouyer if ((bp = expandfile(dp)) == NULL) {
90 1.2 bouyer pfatal("SORRY, CAN'T EXPAND QUOTA INODE\n");
91 1.2 bouyer markclean = 0;
92 1.2 bouyer return (0);
93 1.2 bouyer }
94 1.2 bouyer quota2_addfreeq2e(q2h, bp->b_un.b_buf, baseoff,
95 1.2 bouyer sblock->fs_bsize, needswap);
96 1.2 bouyer dirty(bp);
97 1.2 bouyer bp->b_flags &= ~B_INUSE;
98 1.2 bouyer off = iswap64(q2h->q2h_free);
99 1.2 bouyer if (off == 0)
100 1.2 bouyer errexit("INTERNAL ERROR: "
101 1.2 bouyer "addfreeq2e didn't fill free list\n");
102 1.2 bouyer }
103 1.2 bouyer if (off < sblock->fs_bsize) {
104 1.2 bouyer /* in the header block */
105 1.2 bouyer bp = hbp;
106 1.2 bouyer } else {
107 1.2 bouyer if (readblk(dp, off, &bp) != sblock->fs_bsize ||
108 1.2 bouyer bp->b_errs != 0) {
109 1.2 bouyer pwarn("CAN'T READ QUOTA BLOCK\n");
110 1.2 bouyer return FSCK_EXIT_CHECK_FAILED;
111 1.2 bouyer }
112 1.2 bouyer }
113 1.2 bouyer q2e = (void *)((caddr_t)(bp->b_un.b_buf) +
114 1.2 bouyer (off % sblock->fs_bsize));
115 1.2 bouyer /* remove from free list */
116 1.2 bouyer q2h->q2h_free = q2e->q2e_next;
117 1.2 bouyer
118 1.2 bouyer memcpy(q2e, &q2h->q2h_defentry, sizeof(*q2e));
119 1.2 bouyer q2e->q2e_uid = iswap32(uid);
120 1.2 bouyer q2e->q2e_val[QL_BLOCK].q2v_cur = iswap64(u_b);
121 1.2 bouyer q2e->q2e_val[QL_FILE].q2v_cur = iswap64(u_i);
122 1.2 bouyer /* insert in hash list */
123 1.2 bouyer q2e->q2e_next = q2h->q2h_entries[uid & q2h_hash_mask];
124 1.2 bouyer q2h->q2h_entries[uid & q2h_hash_mask] = iswap64(off);
125 1.2 bouyer dirty(bp);
126 1.2 bouyer dirty(hbp);
127 1.2 bouyer
128 1.2 bouyer if (bp != hbp)
129 1.2 bouyer bp->b_flags &= ~B_INUSE;
130 1.2 bouyer return 0;
131 1.2 bouyer }
132 1.2 bouyer
133 1.2 bouyer /* walk a quota entry list, calling the callback for each entry */
134 1.2 bouyer static int quota2_walk_list(union dinode *, struct bufarea *, uint64_t *,
135 1.2 bouyer void *, int (*func)(uint64_t *, struct quota2_entry *, uint64_t, void *));
136 1.2 bouyer /* flags used by callbacks return */
137 1.2 bouyer #define Q2WL_ABORT 0x01
138 1.2 bouyer #define Q2WL_DIRTY 0x02
139 1.2 bouyer
140 1.2 bouyer static int
141 1.2 bouyer quota2_walk_list(union dinode *dp, struct bufarea *hbp, uint64_t *offp, void *a,
142 1.2 bouyer int (*func)(uint64_t *, struct quota2_entry *, uint64_t, void *))
143 1.2 bouyer {
144 1.2 bouyer daddr_t off = iswap64(*offp);
145 1.2 bouyer struct bufarea *bp, *obp = hbp;
146 1.2 bouyer int ret;
147 1.2 bouyer struct quota2_entry *q2e;
148 1.2 bouyer
149 1.2 bouyer while (off != 0) {
150 1.2 bouyer if (off < sblock->fs_bsize) {
151 1.2 bouyer /* in the header block */
152 1.2 bouyer bp = hbp;
153 1.2 bouyer } else {
154 1.2 bouyer if (readblk(dp, off, &bp) != sblock->fs_bsize ||
155 1.2 bouyer bp->b_errs != 0) {
156 1.2 bouyer pwarn("CAN'T READ QUOTA BLOCK");
157 1.2 bouyer return FSCK_EXIT_CHECK_FAILED;
158 1.2 bouyer }
159 1.2 bouyer }
160 1.2 bouyer q2e = (void *)((caddr_t)(bp->b_un.b_buf) +
161 1.2 bouyer (off % sblock->fs_bsize));
162 1.2 bouyer ret = (*func)(offp, q2e, off, a);
163 1.2 bouyer if (ret & Q2WL_DIRTY)
164 1.2 bouyer dirty(bp);
165 1.2 bouyer if (ret & Q2WL_ABORT)
166 1.2 bouyer return FSCK_EXIT_CHECK_FAILED;
167 1.2 bouyer if (off != iswap64(*offp)) {
168 1.2 bouyer /* callback changed parent's pointer, redo */
169 1.2 bouyer dirty(obp);
170 1.2 bouyer off = iswap64(*offp);
171 1.2 bouyer if (bp != hbp && bp != obp)
172 1.2 bouyer bp->b_flags &= ~B_INUSE;
173 1.2 bouyer } else {
174 1.2 bouyer /* parent if now current */
175 1.2 bouyer if (obp != bp && obp != hbp)
176 1.2 bouyer obp->b_flags &= ~B_INUSE;
177 1.2 bouyer obp = bp;
178 1.2 bouyer offp = &(q2e->q2e_next);
179 1.2 bouyer off = iswap64(*offp);
180 1.2 bouyer }
181 1.2 bouyer }
182 1.2 bouyer if (obp != hbp)
183 1.2 bouyer obp->b_flags &= ~B_INUSE;
184 1.2 bouyer return 0;
185 1.2 bouyer }
186 1.2 bouyer
187 1.2 bouyer static int quota2_list_check(uint64_t *, struct quota2_entry *, uint64_t,
188 1.2 bouyer void *);
189 1.2 bouyer static int
190 1.2 bouyer quota2_list_check(uint64_t *offp, struct quota2_entry *q2e, uint64_t off,
191 1.2 bouyer void *v)
192 1.2 bouyer {
193 1.2 bouyer int *hash = v;
194 1.2 bouyer const int quota2_hash_size = 1 << q2h_hash_shift;
195 1.2 bouyer const int quota2_full_header_size = sizeof(struct quota2_header) +
196 1.2 bouyer sizeof(uint64_t) * quota2_hash_size;
197 1.2 bouyer uint64_t blk = off / sblock->fs_bsize;
198 1.2 bouyer uint64_t boff = off % sblock->fs_bsize;
199 1.2 bouyer int qidx = off2qindex((blk == 0) ? quota2_full_header_size : 0, boff);
200 1.2 bouyer
201 1.2 bouyer /* check that we're not already in a list */
202 1.2 bouyer if (!isset(quotamap[blk], qidx)) {
203 1.2 bouyer pwarn("DUPLICATE QUOTA ENTRY");
204 1.2 bouyer } else {
205 1.2 bouyer clrbit(quotamap[blk], qidx);
206 1.2 bouyer /* check that we're in the right hash entry */
207 1.2 bouyer if (*hash < 0)
208 1.2 bouyer return 0;
209 1.2 bouyer if (*hash == (iswap32(q2e->q2e_uid) & q2h_hash_mask))
210 1.2 bouyer return 0;
211 1.2 bouyer
212 1.2 bouyer pwarn("QUOTA uid %d IN WRONG HASH LIST %d",
213 1.2 bouyer iswap32(q2e->q2e_uid), *hash);
214 1.2 bouyer /*
215 1.2 bouyer * remove from list, but keep the bit so
216 1.2 bouyer * it'll be added back to the free list
217 1.2 bouyer */
218 1.2 bouyer setbit(quotamap[blk], qidx);
219 1.2 bouyer }
220 1.2 bouyer
221 1.2 bouyer if (preen)
222 1.2 bouyer printf(" (FIXED)\n");
223 1.2 bouyer else if (!reply("FIX")) {
224 1.2 bouyer markclean = 0;
225 1.2 bouyer return 0;
226 1.2 bouyer }
227 1.2 bouyer /* remove this entry from the list */
228 1.2 bouyer *offp = q2e->q2e_next;
229 1.2 bouyer q2e->q2e_next = 0;
230 1.2 bouyer return Q2WL_DIRTY;
231 1.2 bouyer }
232 1.2 bouyer
233 1.2 bouyer int
234 1.2 bouyer quota2_check_inode(int type)
235 1.2 bouyer {
236 1.2 bouyer const char *strtype = (type == USRQUOTA) ? "user" : "group";
237 1.2 bouyer const char *capstrtype = (type == USRQUOTA) ? "USER" : "GROUP";
238 1.2 bouyer
239 1.2 bouyer struct bufarea *bp, *hbp;
240 1.2 bouyer union dinode *dp;
241 1.2 bouyer struct quota2_header *q2h;
242 1.2 bouyer struct quota2_entry *q2e;
243 1.2 bouyer int freei = 0;
244 1.2 bouyer int mode;
245 1.2 bouyer daddr_t off;
246 1.2 bouyer int nq2e, nq2map, i, j, ret;
247 1.2 bouyer uint64_t /* blocks, e_blocks, */ filesize;
248 1.2 bouyer
249 1.2 bouyer const int quota2_hash_size = 1 << q2h_hash_shift;
250 1.2 bouyer const int quota2_full_header_size = sizeof(struct quota2_header) +
251 1.2 bouyer sizeof(q2h->q2h_entries[0]) * quota2_hash_size;
252 1.2 bouyer
253 1.2 bouyer if ((sblock->fs_quota_flags & FS_Q2_DO_TYPE(type)) == 0)
254 1.2 bouyer return 0;
255 1.2 bouyer if (sblock->fs_quotafile[type] != 0) {
256 1.2 bouyer struct inostat *info;
257 1.2 bouyer
258 1.2 bouyer info = inoinfo(sblock->fs_quotafile[type]);
259 1.2 bouyer switch(info->ino_state) {
260 1.2 bouyer case FSTATE:
261 1.2 bouyer break;
262 1.2 bouyer case DSTATE:
263 1.2 bouyer freei = 1;
264 1.2 bouyer case DFOUND:
265 1.2 bouyer pwarn("%s QUOTA INODE %" PRIu64 " IS A DIRECTORY",
266 1.2 bouyer capstrtype, sblock->fs_quotafile[type]);
267 1.2 bouyer goto clear;
268 1.2 bouyer case USTATE:
269 1.2 bouyer case DCLEAR:
270 1.2 bouyer case FCLEAR:
271 1.2 bouyer pwarn("UNALLOCATED %s QUOTA INODE %" PRIu64,
272 1.2 bouyer capstrtype, sblock->fs_quotafile[type]);
273 1.2 bouyer goto clear;
274 1.2 bouyer default:
275 1.2 bouyer pfatal("INTERNAL ERROR: wrong quota inode %" PRIu64
276 1.2 bouyer " type %d\n", sblock->fs_quotafile[type],
277 1.2 bouyer info->ino_state);
278 1.2 bouyer exit(FSCK_EXIT_CHECK_FAILED);
279 1.2 bouyer }
280 1.2 bouyer dp = ginode(sblock->fs_quotafile[type]);
281 1.2 bouyer mode = iswap16(DIP(dp, mode)) & IFMT;
282 1.2 bouyer switch(mode) {
283 1.2 bouyer case IFREG:
284 1.2 bouyer break;
285 1.2 bouyer default:
286 1.2 bouyer pwarn("WRONG TYPE %d for %s QUOTA INODE %" PRIu64,
287 1.2 bouyer mode, capstrtype, sblock->fs_quotafile[type]);
288 1.2 bouyer freei = 1;
289 1.2 bouyer goto clear;
290 1.2 bouyer }
291 1.2 bouyer #if 0
292 1.2 bouyer blocks = is_ufs2 ? iswap64(dp->dp2.di_blocks) :
293 1.2 bouyer iswap32(dp->dp1.di_blocks);
294 1.2 bouyer filesize = iswap64(DIP(dp, size));
295 1.2 bouyer e_blocks = btodb(filesize);
296 1.2 bouyer if (btodb(filesize) != blocks) {
297 1.2 bouyer pwarn("%s QUOTA INODE %" PRIu64 " HAS EMPTY BLOCKS",
298 1.2 bouyer capstrtype, sblock->fs_quotafile[type]);
299 1.2 bouyer freei = 1;
300 1.2 bouyer goto clear;
301 1.2 bouyer }
302 1.2 bouyer #endif
303 1.2 bouyer if (readblk(dp, 0, &hbp) != sblock->fs_bsize ||
304 1.2 bouyer hbp->b_errs != 0) {
305 1.2 bouyer freeino(sblock->fs_quotafile[type]);
306 1.2 bouyer sblock->fs_quotafile[type] = 0;
307 1.2 bouyer goto alloc;
308 1.2 bouyer }
309 1.2 bouyer q2h = (void *)hbp->b_un.b_buf;
310 1.2 bouyer if (q2h->q2h_magic_number != iswap32(Q2_HEAD_MAGIC) ||
311 1.2 bouyer q2h->q2h_type != type ||
312 1.2 bouyer q2h->q2h_hash_shift != q2h_hash_shift ||
313 1.2 bouyer q2h->q2h_hash_size != iswap16(quota2_hash_size)) {
314 1.2 bouyer pwarn("CORRUPTED %s QUOTA INODE %" PRIu64, capstrtype,
315 1.2 bouyer sblock->fs_quotafile[type]);
316 1.2 bouyer freei = 1;
317 1.2 bouyer hbp->b_flags &= ~B_INUSE;
318 1.2 bouyer clear:
319 1.2 bouyer if (preen)
320 1.2 bouyer printf(" (CLEARED)\n");
321 1.2 bouyer else {
322 1.2 bouyer if (!reply("CLEAR")) {
323 1.2 bouyer markclean = 0;
324 1.2 bouyer return FSCK_EXIT_CHECK_FAILED;
325 1.2 bouyer }
326 1.2 bouyer }
327 1.2 bouyer if (freei)
328 1.2 bouyer freeino(sblock->fs_quotafile[type]);
329 1.2 bouyer sblock->fs_quotafile[type] = 0;
330 1.2 bouyer }
331 1.2 bouyer }
332 1.2 bouyer alloc:
333 1.2 bouyer if (sblock->fs_quotafile[type] == 0) {
334 1.2 bouyer pwarn("NO %s QUOTA INODE", capstrtype);
335 1.2 bouyer if (preen)
336 1.2 bouyer printf(" (CREATED)\n");
337 1.2 bouyer else {
338 1.2 bouyer if (!reply("CREATE")) {
339 1.2 bouyer markclean = 0;
340 1.2 bouyer return FSCK_EXIT_CHECK_FAILED;
341 1.2 bouyer }
342 1.2 bouyer }
343 1.2 bouyer quota2_create_inode(sblock, type);
344 1.2 bouyer }
345 1.2 bouyer
346 1.2 bouyer dp = ginode(sblock->fs_quotafile[type]);
347 1.2 bouyer if (readblk(dp, 0, &hbp) != sblock->fs_bsize ||
348 1.2 bouyer hbp->b_errs != 0) {
349 1.2 bouyer pfatal("can't re-read %s quota header\n", strtype);
350 1.2 bouyer exit(FSCK_EXIT_CHECK_FAILED);
351 1.2 bouyer }
352 1.2 bouyer q2h = (void *)hbp->b_un.b_buf;
353 1.2 bouyer filesize = iswap64(DIP(dp, size));
354 1.2 bouyer nq2map = filesize / sblock->fs_bsize;
355 1.2 bouyer quotamap = malloc(sizeof(*quotamap) * nq2map);
356 1.2 bouyer /* map for full blocks */
357 1.2 bouyer for (i = 0; i < nq2map; i++) {
358 1.2 bouyer nq2e = (sblock->fs_bsize -
359 1.2 bouyer ((i == 0) ? quota2_full_header_size : 0)) / sizeof(*q2e);
360 1.2 bouyer quotamap[i] = calloc(roundup(howmany(nq2e, NBBY),
361 1.2 bouyer sizeof(int16_t)), sizeof(char));
362 1.2 bouyer for (j = 0; j < nq2e; j++)
363 1.2 bouyer setbit(quotamap[i], j);
364 1.2 bouyer }
365 1.2 bouyer
366 1.2 bouyer /* check that all entries are in the lists (and only once) */
367 1.2 bouyer i = -1;
368 1.2 bouyer ret = quota2_walk_list(dp, hbp, &q2h->q2h_free, &i, quota2_list_check);
369 1.2 bouyer if (ret)
370 1.2 bouyer return ret;
371 1.2 bouyer for (i = 0; i < quota2_hash_size; i++) {
372 1.2 bouyer ret = quota2_walk_list(dp, hbp, &q2h->q2h_entries[i], &i,
373 1.2 bouyer quota2_list_check);
374 1.2 bouyer if (ret)
375 1.2 bouyer return ret;
376 1.2 bouyer }
377 1.2 bouyer for (i = 0; i < nq2map; i++) {
378 1.2 bouyer nq2e = (sblock->fs_bsize -
379 1.2 bouyer ((i == 0) ? quota2_full_header_size : 0)) / sizeof(*q2e);
380 1.2 bouyer for (j = 0; j < nq2e; j++) {
381 1.2 bouyer if (!isset(quotamap[i], j))
382 1.2 bouyer continue;
383 1.2 bouyer pwarn("QUOTA ENTRY NOT IN LIST");
384 1.2 bouyer if (preen)
385 1.2 bouyer printf(" (FIXED)\n");
386 1.2 bouyer else if (!reply("FIX")) {
387 1.2 bouyer markclean = 0;
388 1.2 bouyer break;
389 1.2 bouyer }
390 1.2 bouyer off = qindex2off(
391 1.2 bouyer (i == 0) ? quota2_full_header_size : 0, j);
392 1.2 bouyer if (i == 0)
393 1.2 bouyer bp = hbp;
394 1.2 bouyer else {
395 1.2 bouyer if (readblk(dp, i * sblock->fs_bsize, &bp)
396 1.2 bouyer != sblock->fs_bsize || bp->b_errs != 0) {
397 1.2 bouyer pfatal("can't read %s quota entry\n",
398 1.2 bouyer strtype);
399 1.2 bouyer break;
400 1.2 bouyer }
401 1.2 bouyer }
402 1.2 bouyer q2e = (void *)((caddr_t)(bp->b_un.b_buf) + off);
403 1.2 bouyer q2e->q2e_next = q2h->q2h_free;
404 1.2 bouyer q2h->q2h_free = iswap64(off + i * sblock->fs_bsize);
405 1.2 bouyer dirty(bp);
406 1.2 bouyer dirty(hbp);
407 1.2 bouyer if (bp != hbp)
408 1.2 bouyer bp->b_flags &= ~B_INUSE;
409 1.2 bouyer }
410 1.2 bouyer }
411 1.2 bouyer hbp->b_flags &= ~B_INUSE;
412 1.2 bouyer return 0;
413 1.2 bouyer }
414 1.2 bouyer
415 1.2 bouyer /* compare/update on-disk usages to what we computed */
416 1.2 bouyer
417 1.2 bouyer struct qcheck_arg {
418 1.2 bouyer const char *capstrtype;
419 1.2 bouyer struct uquot_hash *uquot_hash;
420 1.2 bouyer };
421 1.2 bouyer
422 1.2 bouyer static int quota2_list_qcheck(uint64_t *, struct quota2_entry *, uint64_t,
423 1.2 bouyer void *);
424 1.2 bouyer static int
425 1.2 bouyer quota2_list_qcheck(uint64_t *offp, struct quota2_entry *q2e, uint64_t off,
426 1.2 bouyer void *v)
427 1.2 bouyer {
428 1.2 bouyer uid_t uid = iswap32(q2e->q2e_uid);
429 1.2 bouyer struct qcheck_arg *a = v;
430 1.2 bouyer struct uquot *uq;
431 1.2 bouyer struct uquot uq_null;
432 1.2 bouyer
433 1.2 bouyer memset(&uq_null, 0, sizeof(uq_null));
434 1.2 bouyer
435 1.2 bouyer uq = find_uquot(a->uquot_hash, uid, 0);
436 1.2 bouyer
437 1.2 bouyer if (uq == NULL)
438 1.2 bouyer uq = &uq_null;
439 1.2 bouyer else
440 1.2 bouyer remove_uquot(a->uquot_hash, uq);
441 1.2 bouyer
442 1.2 bouyer if (iswap64(q2e->q2e_val[QL_BLOCK].q2v_cur) == uq->uq_b &&
443 1.2 bouyer iswap64(q2e->q2e_val[QL_FILE].q2v_cur) == uq->uq_i)
444 1.2 bouyer return 0;
445 1.2 bouyer pwarn("%s QUOTA MISMATCH FOR ID %d: %" PRIu64 "/%" PRIu64 " SHOULD BE "
446 1.2 bouyer "%" PRIu64 "/%" PRIu64, a->capstrtype, uid,
447 1.2 bouyer iswap64(q2e->q2e_val[QL_BLOCK].q2v_cur),
448 1.2 bouyer iswap64(q2e->q2e_val[QL_FILE].q2v_cur), uq->uq_b, uq->uq_i);
449 1.2 bouyer if (preen) {
450 1.2 bouyer printf(" (FIXED)\n");
451 1.2 bouyer } else if (!reply("FIX")) {
452 1.2 bouyer markclean = 0;
453 1.2 bouyer return 0;
454 1.2 bouyer }
455 1.2 bouyer q2e->q2e_val[QL_BLOCK].q2v_cur = iswap64(uq->uq_b);
456 1.2 bouyer q2e->q2e_val[QL_FILE].q2v_cur = iswap64(uq->uq_i);
457 1.2 bouyer return Q2WL_DIRTY;
458 1.2 bouyer }
459 1.2 bouyer
460 1.2 bouyer int
461 1.2 bouyer quota2_check_usage(int type)
462 1.2 bouyer {
463 1.2 bouyer const char *strtype = (type == USRQUOTA) ? "user" : "group";
464 1.2 bouyer const char *capstrtype = (type == USRQUOTA) ? "USER" : "GROUP";
465 1.2 bouyer
466 1.2 bouyer struct bufarea *hbp;
467 1.2 bouyer union dinode *dp;
468 1.2 bouyer struct quota2_header *q2h;
469 1.2 bouyer struct qcheck_arg a;
470 1.2 bouyer int i, ret;
471 1.2 bouyer const int quota2_hash_size = 1 << q2h_hash_shift;
472 1.2 bouyer
473 1.2 bouyer if ((sblock->fs_quota_flags & FS_Q2_DO_TYPE(type)) == 0)
474 1.2 bouyer return 0;
475 1.2 bouyer
476 1.2 bouyer a.capstrtype = capstrtype;
477 1.2 bouyer a.uquot_hash =
478 1.2 bouyer (type == USRQUOTA) ? uquot_user_hash : uquot_group_hash;
479 1.2 bouyer dp = ginode(sblock->fs_quotafile[type]);
480 1.2 bouyer if (readblk(dp, 0, &hbp) != sblock->fs_bsize ||
481 1.2 bouyer hbp->b_errs != 0) {
482 1.2 bouyer pfatal("can't re-read %s quota header\n", strtype);
483 1.2 bouyer exit(FSCK_EXIT_CHECK_FAILED);
484 1.2 bouyer }
485 1.2 bouyer q2h = (void *)hbp->b_un.b_buf;
486 1.2 bouyer for (i = 0; i < quota2_hash_size; i++) {
487 1.2 bouyer ret = quota2_walk_list(dp, hbp, &q2h->q2h_entries[i], &a,
488 1.2 bouyer quota2_list_qcheck);
489 1.2 bouyer if (ret)
490 1.2 bouyer return ret;
491 1.2 bouyer }
492 1.2 bouyer
493 1.2 bouyer for (i = 0; i < quota2_hash_size; i++) {
494 1.2 bouyer struct uquot *uq;
495 1.2 bouyer SLIST_FOREACH(uq, &a.uquot_hash[i], uq_entries) {
496 1.2 bouyer pwarn("%s QUOTA MISMATCH FOR ID %d: 0/0"
497 1.2 bouyer " SHOULD BE %" PRIu64 "/%" PRIu64, capstrtype,
498 1.2 bouyer uq->uq_uid, uq->uq_b, uq->uq_i);
499 1.2 bouyer if (preen) {
500 1.2 bouyer printf(" (ALLOCATED)\n");
501 1.2 bouyer } else if (!reply("ALLOC")) {
502 1.2 bouyer markclean = 0;
503 1.2 bouyer return 0;
504 1.2 bouyer }
505 1.2 bouyer ret = quota2_alloc_quota(dp, hbp,
506 1.2 bouyer uq->uq_uid, uq->uq_b, uq->uq_i);
507 1.2 bouyer if (ret)
508 1.2 bouyer return ret;
509 1.2 bouyer }
510 1.2 bouyer }
511 1.2 bouyer hbp->b_flags &= ~B_INUSE;
512 1.2 bouyer return 0;
513 1.2 bouyer }
514 1.2 bouyer
515 1.2 bouyer /*
516 1.2 bouyer * check if a quota check needs to be run, regardless of the clean flag
517 1.2 bouyer */
518 1.2 bouyer int
519 1.2 bouyer quota2_check_doquota()
520 1.2 bouyer {
521 1.2 bouyer int retval = 1;
522 1.2 bouyer
523 1.2 bouyer if ((sblock->fs_flags & FS_DOQUOTA2) == 0)
524 1.2 bouyer return 1;
525 1.2 bouyer if (sblock->fs_quota_magic != Q2_HEAD_MAGIC) {
526 1.2 bouyer pfatal("Invalid quota magic number\n");
527 1.2 bouyer if (preen)
528 1.2 bouyer return 0;
529 1.2 bouyer if (reply("CONTINUE") == 0) {
530 1.2 bouyer exit(FSCK_EXIT_CHECK_FAILED);
531 1.2 bouyer }
532 1.2 bouyer return 0;
533 1.2 bouyer }
534 1.2 bouyer if ((sblock->fs_quota_flags & FS_Q2_DO_TYPE(USRQUOTA)) &&
535 1.2 bouyer sblock->fs_quotafile[USRQUOTA] == 0) {
536 1.2 bouyer pwarn("no user quota inode\n");
537 1.2 bouyer retval = 0;
538 1.2 bouyer }
539 1.2 bouyer if ((sblock->fs_quota_flags & FS_Q2_DO_TYPE(GRPQUOTA)) &&
540 1.2 bouyer sblock->fs_quotafile[GRPQUOTA] == 0) {
541 1.2 bouyer pwarn("no group quota inode\n");
542 1.2 bouyer retval = 0;
543 1.2 bouyer }
544 1.2 bouyer if (preen)
545 1.2 bouyer return retval;
546 1.2 bouyer if (!retval) {
547 1.2 bouyer if (reply("CONTINUE") == 0) {
548 1.2 bouyer exit(FSCK_EXIT_CHECK_FAILED);
549 1.2 bouyer }
550 1.2 bouyer return 0;
551 1.2 bouyer }
552 1.2 bouyer return 1;
553 1.2 bouyer }
554