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