1 1.8 riastrad /* $NetBSD: quota2.c,v 1.8 2023/07/04 20:40:53 riastradh Exp $ */ 2 1.8 riastrad 3 1.2 bouyer /*- 4 1.2 bouyer * Copyright (c) 2010 Manuel Bouyer 5 1.2 bouyer * All rights reserved. 6 1.2 bouyer * 7 1.2 bouyer * Redistribution and use in source and binary forms, with or without 8 1.2 bouyer * modification, are permitted provided that the following conditions 9 1.2 bouyer * are met: 10 1.2 bouyer * 1. Redistributions of source code must retain the above copyright 11 1.2 bouyer * notice, this list of conditions and the following disclaimer. 12 1.2 bouyer * 2. Redistributions in binary form must reproduce the above copyright 13 1.2 bouyer * notice, this list of conditions and the following disclaimer in the 14 1.2 bouyer * documentation and/or other materials provided with the distribution. 15 1.2 bouyer * 16 1.2 bouyer * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.2 bouyer * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.2 bouyer * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.2 bouyer * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.2 bouyer * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.2 bouyer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.2 bouyer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.2 bouyer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.2 bouyer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.2 bouyer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.2 bouyer * POSSIBILITY OF SUCH DAMAGE. 27 1.2 bouyer */ 28 1.2 bouyer 29 1.2 bouyer #include <sys/param.h> 30 1.2 bouyer #include <sys/time.h> 31 1.2 bouyer 32 1.2 bouyer #include <ufs/ufs/dinode.h> 33 1.2 bouyer #include <ufs/ffs/fs.h> 34 1.2 bouyer #include <ufs/ffs/ffs_extern.h> 35 1.2 bouyer #include <ufs/ufs/ufs_bswap.h> 36 1.2 bouyer 37 1.2 bouyer #include <err.h> 38 1.2 bouyer #include <string.h> 39 1.6 dholland #include <stdlib.h> 40 1.2 bouyer #include <ufs/ufs/quota2.h> 41 1.2 bouyer 42 1.2 bouyer #include "fsutil.h" 43 1.2 bouyer #include "fsck.h" 44 1.2 bouyer #include "extern.h" 45 1.2 bouyer #include "exitvalues.h" 46 1.2 bouyer 47 1.2 bouyer static char **quotamap; 48 1.2 bouyer 49 1.2 bouyer void 50 1.2 bouyer quota2_create_inode(struct fs *fs, int type) 51 1.2 bouyer { 52 1.2 bouyer ino_t ino; 53 1.2 bouyer struct bufarea *bp; 54 1.2 bouyer union dinode *dp; 55 1.2 bouyer 56 1.2 bouyer ino = allocino(0, IFREG); 57 1.2 bouyer dp = ginode(ino); 58 1.2 bouyer DIP_SET(dp, nlink, iswap16(1)); 59 1.2 bouyer inodirty(); 60 1.2 bouyer 61 1.2 bouyer if (readblk(dp, 0, &bp) != sblock->fs_bsize || 62 1.2 bouyer bp->b_errs != 0) { 63 1.2 bouyer freeino(ino); 64 1.2 bouyer return; 65 1.2 bouyer } 66 1.2 bouyer quota2_create_blk0(sblock->fs_bsize, bp->b_un.b_buf, 67 1.2 bouyer q2h_hash_shift, type, needswap); 68 1.2 bouyer dirty(bp); 69 1.2 bouyer bp->b_flags &= ~B_INUSE; 70 1.2 bouyer sblock->fs_quotafile[type] = ino; 71 1.2 bouyer sbdirty(); 72 1.2 bouyer return; 73 1.2 bouyer } 74 1.2 bouyer 75 1.2 bouyer int 76 1.2 bouyer quota2_alloc_quota(union dinode * dp, struct bufarea *hbp, 77 1.2 bouyer uid_t uid, uint64_t u_b, uint64_t u_i) 78 1.2 bouyer { 79 1.2 bouyer struct bufarea *bp; 80 1.2 bouyer struct quota2_header *q2h = (void *)hbp->b_un.b_buf; 81 1.2 bouyer struct quota2_entry *q2e; 82 1.2 bouyer uint64_t off; 83 1.2 bouyer uint64_t baseoff; 84 1.2 bouyer 85 1.2 bouyer off = iswap64(q2h->q2h_free); 86 1.2 bouyer if (off == 0) { 87 1.2 bouyer baseoff = iswap64(DIP(dp, size)); 88 1.2 bouyer if ((bp = expandfile(dp)) == NULL) { 89 1.2 bouyer pfatal("SORRY, CAN'T EXPAND QUOTA INODE\n"); 90 1.2 bouyer markclean = 0; 91 1.2 bouyer return (0); 92 1.2 bouyer } 93 1.2 bouyer quota2_addfreeq2e(q2h, bp->b_un.b_buf, baseoff, 94 1.2 bouyer sblock->fs_bsize, needswap); 95 1.2 bouyer dirty(bp); 96 1.2 bouyer bp->b_flags &= ~B_INUSE; 97 1.2 bouyer off = iswap64(q2h->q2h_free); 98 1.2 bouyer if (off == 0) 99 1.2 bouyer errexit("INTERNAL ERROR: " 100 1.2 bouyer "addfreeq2e didn't fill free list\n"); 101 1.2 bouyer } 102 1.4 christos if (off < (uint64_t)sblock->fs_bsize) { 103 1.2 bouyer /* in the header block */ 104 1.2 bouyer bp = hbp; 105 1.2 bouyer } else { 106 1.2 bouyer if (readblk(dp, off, &bp) != sblock->fs_bsize || 107 1.2 bouyer bp->b_errs != 0) { 108 1.2 bouyer pwarn("CAN'T READ QUOTA BLOCK\n"); 109 1.2 bouyer return FSCK_EXIT_CHECK_FAILED; 110 1.2 bouyer } 111 1.2 bouyer } 112 1.2 bouyer q2e = (void *)((caddr_t)(bp->b_un.b_buf) + 113 1.2 bouyer (off % sblock->fs_bsize)); 114 1.2 bouyer /* remove from free list */ 115 1.2 bouyer q2h->q2h_free = q2e->q2e_next; 116 1.2 bouyer 117 1.2 bouyer memcpy(q2e, &q2h->q2h_defentry, sizeof(*q2e)); 118 1.2 bouyer q2e->q2e_uid = iswap32(uid); 119 1.2 bouyer q2e->q2e_val[QL_BLOCK].q2v_cur = iswap64(u_b); 120 1.2 bouyer q2e->q2e_val[QL_FILE].q2v_cur = iswap64(u_i); 121 1.2 bouyer /* insert in hash list */ 122 1.2 bouyer q2e->q2e_next = q2h->q2h_entries[uid & q2h_hash_mask]; 123 1.2 bouyer q2h->q2h_entries[uid & q2h_hash_mask] = iswap64(off); 124 1.2 bouyer dirty(bp); 125 1.2 bouyer dirty(hbp); 126 1.8 riastrad 127 1.2 bouyer if (bp != hbp) 128 1.2 bouyer bp->b_flags &= ~B_INUSE; 129 1.2 bouyer return 0; 130 1.2 bouyer } 131 1.2 bouyer 132 1.2 bouyer /* walk a quota entry list, calling the callback for each entry */ 133 1.2 bouyer static int quota2_walk_list(union dinode *, struct bufarea *, uint64_t *, 134 1.2 bouyer void *, int (*func)(uint64_t *, struct quota2_entry *, uint64_t, void *)); 135 1.2 bouyer /* flags used by callbacks return */ 136 1.2 bouyer #define Q2WL_ABORT 0x01 137 1.2 bouyer #define Q2WL_DIRTY 0x02 138 1.2 bouyer 139 1.2 bouyer static int 140 1.2 bouyer quota2_walk_list(union dinode *dp, struct bufarea *hbp, uint64_t *offp, void *a, 141 1.2 bouyer int (*func)(uint64_t *, struct quota2_entry *, uint64_t, void *)) 142 1.2 bouyer { 143 1.2 bouyer daddr_t off = iswap64(*offp); 144 1.2 bouyer struct bufarea *bp, *obp = hbp; 145 1.2 bouyer int ret; 146 1.2 bouyer struct quota2_entry *q2e; 147 1.2 bouyer 148 1.2 bouyer while (off != 0) { 149 1.2 bouyer if (off < sblock->fs_bsize) { 150 1.2 bouyer /* in the header block */ 151 1.2 bouyer bp = hbp; 152 1.2 bouyer } else { 153 1.2 bouyer if (readblk(dp, off, &bp) != sblock->fs_bsize || 154 1.2 bouyer bp->b_errs != 0) { 155 1.2 bouyer pwarn("CAN'T READ QUOTA BLOCK"); 156 1.2 bouyer return FSCK_EXIT_CHECK_FAILED; 157 1.2 bouyer } 158 1.2 bouyer } 159 1.2 bouyer q2e = (void *)((caddr_t)(bp->b_un.b_buf) + 160 1.2 bouyer (off % sblock->fs_bsize)); 161 1.2 bouyer ret = (*func)(offp, q2e, off, a); 162 1.2 bouyer if (ret & Q2WL_DIRTY) 163 1.2 bouyer dirty(bp); 164 1.2 bouyer if (ret & Q2WL_ABORT) 165 1.2 bouyer return FSCK_EXIT_CHECK_FAILED; 166 1.4 christos if ((uint64_t)off != iswap64(*offp)) { 167 1.2 bouyer /* callback changed parent's pointer, redo */ 168 1.2 bouyer dirty(obp); 169 1.2 bouyer off = iswap64(*offp); 170 1.2 bouyer if (bp != hbp && bp != obp) 171 1.2 bouyer bp->b_flags &= ~B_INUSE; 172 1.2 bouyer } else { 173 1.2 bouyer /* parent if now current */ 174 1.2 bouyer if (obp != bp && obp != hbp) 175 1.2 bouyer obp->b_flags &= ~B_INUSE; 176 1.2 bouyer obp = bp; 177 1.2 bouyer offp = &(q2e->q2e_next); 178 1.2 bouyer off = iswap64(*offp); 179 1.2 bouyer } 180 1.2 bouyer } 181 1.2 bouyer if (obp != hbp) 182 1.2 bouyer obp->b_flags &= ~B_INUSE; 183 1.2 bouyer return 0; 184 1.2 bouyer } 185 1.2 bouyer 186 1.2 bouyer static int quota2_list_check(uint64_t *, struct quota2_entry *, uint64_t, 187 1.2 bouyer void *); 188 1.2 bouyer static int 189 1.2 bouyer quota2_list_check(uint64_t *offp, struct quota2_entry *q2e, uint64_t off, 190 1.2 bouyer void *v) 191 1.2 bouyer { 192 1.2 bouyer int *hash = v; 193 1.2 bouyer const int quota2_hash_size = 1 << q2h_hash_shift; 194 1.2 bouyer const int quota2_full_header_size = sizeof(struct quota2_header) + 195 1.2 bouyer sizeof(uint64_t) * quota2_hash_size; 196 1.2 bouyer uint64_t blk = off / sblock->fs_bsize; 197 1.2 bouyer uint64_t boff = off % sblock->fs_bsize; 198 1.2 bouyer int qidx = off2qindex((blk == 0) ? quota2_full_header_size : 0, boff); 199 1.2 bouyer 200 1.2 bouyer /* check that we're not already in a list */ 201 1.2 bouyer if (!isset(quotamap[blk], qidx)) { 202 1.2 bouyer pwarn("DUPLICATE QUOTA ENTRY"); 203 1.2 bouyer } else { 204 1.2 bouyer clrbit(quotamap[blk], qidx); 205 1.2 bouyer /* check that we're in the right hash entry */ 206 1.2 bouyer if (*hash < 0) 207 1.2 bouyer return 0; 208 1.4 christos if ((uint32_t)*hash == (iswap32(q2e->q2e_uid) & q2h_hash_mask)) 209 1.2 bouyer return 0; 210 1.2 bouyer 211 1.2 bouyer pwarn("QUOTA uid %d IN WRONG HASH LIST %d", 212 1.2 bouyer iswap32(q2e->q2e_uid), *hash); 213 1.2 bouyer /* 214 1.2 bouyer * remove from list, but keep the bit so 215 1.2 bouyer * it'll be added back to the free list 216 1.2 bouyer */ 217 1.2 bouyer setbit(quotamap[blk], qidx); 218 1.2 bouyer } 219 1.2 bouyer 220 1.2 bouyer if (preen) 221 1.2 bouyer printf(" (FIXED)\n"); 222 1.2 bouyer else if (!reply("FIX")) { 223 1.2 bouyer markclean = 0; 224 1.2 bouyer return 0; 225 1.2 bouyer } 226 1.2 bouyer /* remove this entry from the list */ 227 1.2 bouyer *offp = q2e->q2e_next; 228 1.2 bouyer q2e->q2e_next = 0; 229 1.2 bouyer return Q2WL_DIRTY; 230 1.2 bouyer } 231 1.2 bouyer 232 1.2 bouyer int 233 1.2 bouyer quota2_check_inode(int type) 234 1.2 bouyer { 235 1.2 bouyer const char *strtype = (type == USRQUOTA) ? "user" : "group"; 236 1.2 bouyer const char *capstrtype = (type == USRQUOTA) ? "USER" : "GROUP"; 237 1.2 bouyer 238 1.2 bouyer struct bufarea *bp, *hbp; 239 1.2 bouyer union dinode *dp; 240 1.2 bouyer struct quota2_header *q2h; 241 1.2 bouyer struct quota2_entry *q2e; 242 1.2 bouyer int freei = 0; 243 1.2 bouyer int mode; 244 1.2 bouyer daddr_t off; 245 1.2 bouyer int nq2e, nq2map, i, j, ret; 246 1.2 bouyer uint64_t /* blocks, e_blocks, */ filesize; 247 1.2 bouyer 248 1.2 bouyer const int quota2_hash_size = 1 << q2h_hash_shift; 249 1.2 bouyer const int quota2_full_header_size = sizeof(struct quota2_header) + 250 1.2 bouyer sizeof(q2h->q2h_entries[0]) * quota2_hash_size; 251 1.8 riastrad 252 1.2 bouyer if ((sblock->fs_quota_flags & FS_Q2_DO_TYPE(type)) == 0) 253 1.2 bouyer return 0; 254 1.2 bouyer if (sblock->fs_quotafile[type] != 0) { 255 1.2 bouyer struct inostat *info; 256 1.2 bouyer 257 1.2 bouyer info = inoinfo(sblock->fs_quotafile[type]); 258 1.2 bouyer switch(info->ino_state) { 259 1.2 bouyer case FSTATE: 260 1.2 bouyer break; 261 1.2 bouyer case DSTATE: 262 1.2 bouyer freei = 1; 263 1.7 mrg /* FALLTHROUGH */ 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.8 riastrad 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.8 riastrad 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.8 riastrad 442 1.8 riastrad 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.8 riastrad 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.8 riastrad 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.5 matt quota2_check_doquota(void) 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