1 /* 2 * mkquota.c --- create quota files for a filesystem 3 * 4 * Aditya Kali <adityakali (at) google.com> 5 */ 6 #include "config.h" 7 #include <sys/types.h> 8 #include <sys/stat.h> 9 #include <unistd.h> 10 #include <errno.h> 11 #include <string.h> 12 #include <fcntl.h> 13 14 #include "ext2fs/ext2_fs.h" 15 #include "ext2fs/ext2fs.h" 16 #include "e2p/e2p.h" 17 18 #include "quotaio.h" 19 #include "quotaio_v2.h" 20 #include "quotaio_tree.h" 21 #include "common.h" 22 #include "dict.h" 23 24 /* Needed for architectures where sizeof(int) != sizeof(void *) */ 25 #define UINT_TO_VOIDPTR(val) ((void *)(intptr_t)(val)) 26 #define VOIDPTR_TO_UINT(ptr) ((unsigned int)(intptr_t)(ptr)) 27 28 #if DEBUG_QUOTA 29 static void print_inode(struct ext2_inode *inode) 30 { 31 if (!inode) 32 return; 33 34 fprintf(stderr, " i_mode = %d\n", inode->i_mode); 35 fprintf(stderr, " i_uid = %d\n", inode->i_uid); 36 fprintf(stderr, " i_size = %d\n", inode->i_size); 37 fprintf(stderr, " i_atime = %d\n", inode->i_atime); 38 fprintf(stderr, " i_ctime = %d\n", inode->i_ctime); 39 fprintf(stderr, " i_mtime = %d\n", inode->i_mtime); 40 fprintf(stderr, " i_dtime = %d\n", inode->i_dtime); 41 fprintf(stderr, " i_gid = %d\n", inode->i_gid); 42 fprintf(stderr, " i_links_count = %d\n", inode->i_links_count); 43 fprintf(stderr, " i_blocks = %d\n", inode->i_blocks); 44 fprintf(stderr, " i_flags = %d\n", inode->i_flags); 45 46 return; 47 } 48 49 static void print_dquot(const char *desc, struct dquot *dq) 50 { 51 if (desc) 52 fprintf(stderr, "%s: ", desc); 53 fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n", 54 dq->dq_id, dq->dq_dqb.dqb_curspace, 55 dq->dq_dqb.dqb_bsoftlimit, dq->dq_dqb.dqb_bhardlimit, 56 dq->dq_dqb.dqb_curinodes, 57 dq->dq_dqb.dqb_isoftlimit, dq->dq_dqb.dqb_ihardlimit); 58 } 59 #else 60 static void print_dquot(const char *desc EXT2FS_ATTR((unused)), 61 struct dquot *dq EXT2FS_ATTR((unused))) 62 { 63 } 64 #endif 65 66 /* 67 * Returns 0 if not able to find the quota file, otherwise returns its 68 * inode number. 69 */ 70 int quota_file_exists(ext2_filsys fs, enum quota_type qtype) 71 { 72 char qf_name[256]; 73 errcode_t ret; 74 ext2_ino_t ino; 75 76 if (qtype >= MAXQUOTAS) 77 return -EINVAL; 78 79 quota_get_qf_name(qtype, QFMT_VFS_V1, qf_name); 80 81 ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0, 82 &ino); 83 if (ret) 84 return 0; 85 86 return ino; 87 } 88 89 /* 90 * Set the value for reserved quota inode number field in superblock. 91 */ 92 void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype) 93 { 94 ext2_ino_t *inump; 95 96 inump = quota_sb_inump(fs->super, qtype); 97 98 log_debug("setting quota ino in superblock: ino=%u, type=%d", ino, 99 qtype); 100 *inump = ino; 101 ext2fs_mark_super_dirty(fs); 102 } 103 104 errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype) 105 { 106 ext2_ino_t qf_ino; 107 errcode_t retval; 108 109 retval = ext2fs_read_bitmaps(fs); 110 if (retval) { 111 log_debug("Couldn't read bitmaps: %s", error_message(retval)); 112 return retval; 113 } 114 115 qf_ino = *quota_sb_inump(fs->super, qtype); 116 if (qf_ino == 0) 117 return 0; 118 retval = quota_inode_truncate(fs, qf_ino); 119 if (retval) 120 return retval; 121 if (qf_ino >= EXT2_FIRST_INODE(fs->super)) { 122 struct ext2_inode inode; 123 124 retval = ext2fs_read_inode(fs, qf_ino, &inode); 125 if (!retval) { 126 memset(&inode, 0, sizeof(struct ext2_inode)); 127 ext2fs_write_inode(fs, qf_ino, &inode); 128 } 129 ext2fs_inode_alloc_stats2(fs, qf_ino, -1, 0); 130 ext2fs_mark_ib_dirty(fs); 131 132 } 133 quota_set_sb_inum(fs, 0, qtype); 134 135 ext2fs_mark_super_dirty(fs); 136 fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 137 retval = ext2fs_write_bitmaps(fs); 138 if (retval) { 139 log_debug("Couldn't write bitmaps: %s", error_message(retval)); 140 return retval; 141 } 142 return 0; 143 } 144 145 static void write_dquots(dict_t *dict, struct quota_handle *qh) 146 { 147 dnode_t *n; 148 struct dquot *dq; 149 150 for (n = dict_first(dict); n; n = dict_next(dict, n)) { 151 dq = dnode_get(n); 152 if (dq) { 153 print_dquot("write", dq); 154 dq->dq_h = qh; 155 update_grace_times(dq); 156 qh->qh_ops->commit_dquot(dq); 157 } 158 } 159 } 160 161 errcode_t quota_write_inode(quota_ctx_t qctx, unsigned int qtype_bits) 162 { 163 int retval = 0; 164 enum quota_type qtype; 165 dict_t *dict; 166 ext2_filsys fs; 167 struct quota_handle *h = NULL; 168 int fmt = QFMT_VFS_V1; 169 170 if (!qctx) 171 return 0; 172 173 fs = qctx->fs; 174 retval = ext2fs_get_mem(sizeof(struct quota_handle), &h); 175 if (retval) { 176 log_debug("Unable to allocate quota handle: %s", 177 error_message(retval)); 178 goto out; 179 } 180 181 retval = ext2fs_read_bitmaps(fs); 182 if (retval) { 183 log_debug("Couldn't read bitmaps: %s", error_message(retval)); 184 goto out; 185 } 186 187 for (qtype = 0; qtype < MAXQUOTAS; qtype++) { 188 if (((1 << qtype) & qtype_bits) == 0) 189 continue; 190 191 dict = qctx->quota_dict[qtype]; 192 if (!dict) 193 continue; 194 195 retval = quota_file_create(h, fs, qtype, fmt); 196 if (retval < 0) { 197 log_debug("Cannot initialize io on quotafile"); 198 continue; 199 } 200 201 write_dquots(dict, h); 202 retval = quota_file_close(qctx, h); 203 if (retval < 0) { 204 log_err("Cannot finish IO on new quotafile: %s", 205 strerror(errno)); 206 if (h->qh_qf.e2_file) 207 ext2fs_file_close(h->qh_qf.e2_file); 208 (void) quota_inode_truncate(fs, h->qh_qf.ino); 209 continue; 210 } 211 212 /* Set quota inode numbers in superblock. */ 213 quota_set_sb_inum(fs, h->qh_qf.ino, qtype); 214 ext2fs_mark_super_dirty(fs); 215 ext2fs_mark_bb_dirty(fs); 216 fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 217 } 218 219 retval = ext2fs_write_bitmaps(fs); 220 if (retval) { 221 log_debug("Couldn't write bitmaps: %s", error_message(retval)); 222 goto out; 223 } 224 out: 225 if (h) 226 ext2fs_free_mem(&h); 227 return retval; 228 } 229 230 /******************************************************************/ 231 /* Helper functions for computing quota in memory. */ 232 /******************************************************************/ 233 234 static int dict_uint_cmp(const void *a, const void *b) 235 { 236 unsigned int c, d; 237 238 c = VOIDPTR_TO_UINT(a); 239 d = VOIDPTR_TO_UINT(b); 240 241 if (c == d) 242 return 0; 243 else if (c > d) 244 return 1; 245 else 246 return -1; 247 } 248 249 static inline qid_t get_qid(struct ext2_inode_large *inode, enum quota_type qtype) 250 { 251 unsigned int inode_size; 252 253 switch (qtype) { 254 case USRQUOTA: 255 return inode_uid(*inode); 256 case GRPQUOTA: 257 return inode_gid(*inode); 258 case PRJQUOTA: 259 inode_size = EXT2_GOOD_OLD_INODE_SIZE + 260 inode->i_extra_isize; 261 if (inode_includes(inode_size, i_projid)) 262 return inode_projid(*inode); 263 default: 264 return 0; 265 } 266 267 return 0; 268 } 269 270 static void quota_dnode_free(dnode_t *node, 271 void *context EXT2FS_ATTR((unused))) 272 { 273 void *ptr = node ? dnode_get(node) : 0; 274 275 ext2fs_free_mem(&ptr); 276 free(node); 277 } 278 279 /* 280 * Set up the quota tracking data structures. 281 */ 282 errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, 283 unsigned int qtype_bits) 284 { 285 errcode_t err; 286 dict_t *dict; 287 quota_ctx_t ctx; 288 enum quota_type qtype; 289 290 err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx); 291 if (err) { 292 log_debug("Failed to allocate quota context"); 293 return err; 294 } 295 296 memset(ctx, 0, sizeof(struct quota_ctx)); 297 for (qtype = 0; qtype < MAXQUOTAS; qtype++) { 298 ctx->quota_file[qtype] = NULL; 299 if (((1 << qtype) & qtype_bits) == 0) 300 continue; 301 err = ext2fs_get_mem(sizeof(dict_t), &dict); 302 if (err) { 303 log_debug("Failed to allocate dictionary"); 304 quota_release_context(&ctx); 305 return err; 306 } 307 ctx->quota_dict[qtype] = dict; 308 dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp); 309 dict_set_allocator(dict, NULL, quota_dnode_free, NULL); 310 } 311 312 ctx->fs = fs; 313 *qctx = ctx; 314 return 0; 315 } 316 317 void quota_release_context(quota_ctx_t *qctx) 318 { 319 errcode_t err; 320 dict_t *dict; 321 enum quota_type qtype; 322 quota_ctx_t ctx; 323 324 if (!qctx) 325 return; 326 327 ctx = *qctx; 328 for (qtype = 0; qtype < MAXQUOTAS; qtype++) { 329 dict = ctx->quota_dict[qtype]; 330 ctx->quota_dict[qtype] = 0; 331 if (dict) { 332 dict_free_nodes(dict); 333 free(dict); 334 } 335 if (ctx->quota_file[qtype]) { 336 err = quota_file_close(ctx, ctx->quota_file[qtype]); 337 if (err) { 338 log_err("Cannot close quotafile: %s", 339 strerror(errno)); 340 ext2fs_free_mem(&ctx->quota_file[qtype]); 341 } 342 } 343 } 344 *qctx = NULL; 345 free(ctx); 346 } 347 348 static struct dquot *get_dq(dict_t *dict, __u32 key) 349 { 350 struct dquot *dq; 351 dnode_t *n; 352 353 n = dict_lookup(dict, UINT_TO_VOIDPTR(key)); 354 if (n) 355 dq = dnode_get(n); 356 else { 357 if (ext2fs_get_mem(sizeof(struct dquot), &dq)) { 358 log_err("Unable to allocate dquot"); 359 return NULL; 360 } 361 memset(dq, 0, sizeof(struct dquot)); 362 dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq); 363 dq->dq_id = key; 364 } 365 return dq; 366 } 367 368 369 /* 370 * Called to update the blocks used by a particular inode 371 */ 372 void quota_data_add(quota_ctx_t qctx, struct ext2_inode_large *inode, 373 ext2_ino_t ino EXT2FS_ATTR((unused)), 374 qsize_t space) 375 { 376 struct dquot *dq; 377 dict_t *dict; 378 enum quota_type qtype; 379 380 if (!qctx) 381 return; 382 383 log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 384 inode_uid(*inode), 385 inode_gid(*inode), space); 386 for (qtype = 0; qtype < MAXQUOTAS; qtype++) { 387 dict = qctx->quota_dict[qtype]; 388 if (dict) { 389 dq = get_dq(dict, get_qid(inode, qtype)); 390 if (dq) 391 dq->dq_dqb.dqb_curspace += space; 392 } 393 } 394 } 395 396 /* 397 * Called to remove some blocks used by a particular inode 398 */ 399 void quota_data_sub(quota_ctx_t qctx, struct ext2_inode_large *inode, 400 ext2_ino_t ino EXT2FS_ATTR((unused)), 401 qsize_t space) 402 { 403 struct dquot *dq; 404 dict_t *dict; 405 enum quota_type qtype; 406 407 if (!qctx) 408 return; 409 410 log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 411 inode_uid(*inode), 412 inode_gid(*inode), space); 413 for (qtype = 0; qtype < MAXQUOTAS; qtype++) { 414 dict = qctx->quota_dict[qtype]; 415 if (dict) { 416 dq = get_dq(dict, get_qid(inode, qtype)); 417 dq->dq_dqb.dqb_curspace -= space; 418 } 419 } 420 } 421 422 /* 423 * Called to count the files used by an inode's user/group 424 */ 425 void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode_large *inode, 426 ext2_ino_t ino EXT2FS_ATTR((unused)), int adjust) 427 { 428 struct dquot *dq; 429 dict_t *dict; 430 enum quota_type qtype; 431 432 if (!qctx) 433 return; 434 435 log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino, 436 inode_uid(*inode), 437 inode_gid(*inode), adjust); 438 for (qtype = 0; qtype < MAXQUOTAS; qtype++) { 439 dict = qctx->quota_dict[qtype]; 440 if (dict) { 441 dq = get_dq(dict, get_qid(inode, qtype)); 442 dq->dq_dqb.dqb_curinodes += adjust; 443 } 444 } 445 } 446 447 errcode_t quota_compute_usage(quota_ctx_t qctx) 448 { 449 ext2_filsys fs; 450 ext2_ino_t ino; 451 errcode_t ret; 452 struct ext2_inode_large *inode; 453 int inode_size; 454 qsize_t space; 455 ext2_inode_scan scan; 456 457 if (!qctx) 458 return 0; 459 460 fs = qctx->fs; 461 ret = ext2fs_open_inode_scan(fs, 0, &scan); 462 if (ret) { 463 log_err("while opening inode scan. ret=%ld", ret); 464 return ret; 465 } 466 inode_size = fs->super->s_inode_size; 467 inode = malloc(inode_size); 468 if (!inode) 469 return ENOMEM; 470 while (1) { 471 ret = ext2fs_get_next_inode_full(scan, &ino, 472 EXT2_INODE(inode), inode_size); 473 if (ret) { 474 log_err("while getting next inode. ret=%ld", ret); 475 ext2fs_close_inode_scan(scan); 476 free(inode); 477 return ret; 478 } 479 if (ino == 0) 480 break; 481 if (inode->i_links_count && 482 (ino == EXT2_ROOT_INO || 483 ino >= EXT2_FIRST_INODE(fs->super))) { 484 space = ext2fs_inode_i_blocks(fs, 485 EXT2_INODE(inode)) << 9; 486 quota_data_add(qctx, inode, ino, space); 487 quota_data_inodes(qctx, inode, ino, +1); 488 } 489 } 490 491 ext2fs_close_inode_scan(scan); 492 free(inode); 493 return 0; 494 } 495 496 struct scan_dquots_data { 497 dict_t *quota_dict; 498 int update_limits; /* update limits from disk */ 499 int update_usage; 500 int usage_is_inconsistent; 501 }; 502 503 static int scan_dquots_callback(struct dquot *dquot, void *cb_data) 504 { 505 struct scan_dquots_data *scan_data = cb_data; 506 dict_t *quota_dict = scan_data->quota_dict; 507 struct dquot *dq; 508 509 dq = get_dq(quota_dict, dquot->dq_id); 510 dq->dq_id = dquot->dq_id; 511 dq->dq_flags |= DQF_SEEN; 512 513 print_dquot("mem", dq); 514 print_dquot("dsk", dquot); 515 516 /* Check if there is inconsistancy. */ 517 if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace || 518 dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) { 519 scan_data->usage_is_inconsistent = 1; 520 fprintf(stderr, "[QUOTA WARNING] Usage inconsistent for ID %d:" 521 "actual (%llu, %llu) != expected (%llu, %llu)\n", 522 dq->dq_id, (long long)dq->dq_dqb.dqb_curspace, 523 (long long)dq->dq_dqb.dqb_curinodes, 524 (long long)dquot->dq_dqb.dqb_curspace, 525 (long long)dquot->dq_dqb.dqb_curinodes); 526 } 527 528 if (scan_data->update_limits) { 529 dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit; 530 dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit; 531 dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit; 532 dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit; 533 } 534 535 if (scan_data->update_usage) { 536 dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace; 537 dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes; 538 } 539 540 return 0; 541 } 542 543 /* 544 * Read all dquots from quota file into memory 545 */ 546 static errcode_t quota_read_all_dquots(struct quota_handle *qh, 547 quota_ctx_t qctx, int update_limits) 548 { 549 struct scan_dquots_data scan_data; 550 551 scan_data.quota_dict = qctx->quota_dict[qh->qh_type]; 552 scan_data.update_limits = update_limits; 553 scan_data.update_usage = 0; 554 555 return qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data); 556 } 557 558 /* 559 * Write all memory dquots into quota file 560 */ 561 #if 0 /* currently unused, but may be useful in the future? */ 562 static errcode_t quota_write_all_dquots(struct quota_handle *qh, 563 quota_ctx_t qctx) 564 { 565 errcode_t err; 566 567 err = ext2fs_read_bitmaps(qctx->fs); 568 if (err) 569 return err; 570 write_dquots(qctx->quota_dict[qh->qh_type], qh); 571 ext2fs_mark_bb_dirty(qctx->fs); 572 qctx->fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 573 ext2fs_write_bitmaps(qctx->fs); 574 return 0; 575 } 576 #endif 577 578 /* 579 * Updates the in-memory quota limits from the given quota inode. 580 */ 581 errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino, 582 enum quota_type qtype) 583 { 584 struct quota_handle *qh; 585 errcode_t err; 586 587 if (!qctx) 588 return 0; 589 590 err = ext2fs_get_mem(sizeof(struct quota_handle), &qh); 591 if (err) { 592 log_debug("Unable to allocate quota handle"); 593 return err; 594 } 595 596 err = quota_file_open(qctx, qh, qf_ino, qtype, -1, 0); 597 if (err) { 598 log_debug("Open quota file failed"); 599 goto out; 600 } 601 602 quota_read_all_dquots(qh, qctx, 1); 603 604 err = quota_file_close(qctx, qh); 605 if (err) { 606 log_debug("Cannot finish IO on new quotafile: %s", 607 strerror(errno)); 608 if (qh->qh_qf.e2_file) 609 ext2fs_file_close(qh->qh_qf.e2_file); 610 } 611 out: 612 ext2fs_free_mem(&qh); 613 return err; 614 } 615 616 /* 617 * Compares the measured quota in qctx->quota_dict with that in the quota inode 618 * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is 619 * set to 1 if the supplied and on-disk quota usage values are not identical. 620 */ 621 errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype, 622 int *usage_inconsistent) 623 { 624 struct quota_handle qh; 625 struct scan_dquots_data scan_data; 626 struct dquot *dq; 627 dnode_t *n; 628 dict_t *dict = qctx->quota_dict[qtype]; 629 errcode_t err = 0; 630 631 if (!dict) 632 goto out; 633 634 err = quota_file_open(qctx, &qh, 0, qtype, -1, 0); 635 if (err) { 636 log_debug("Open quota file failed"); 637 goto out; 638 } 639 640 scan_data.quota_dict = qctx->quota_dict[qtype]; 641 scan_data.update_limits = 1; 642 scan_data.update_usage = 0; 643 scan_data.usage_is_inconsistent = 0; 644 err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data); 645 if (err) { 646 log_debug("Error scanning dquots"); 647 goto out_close_qh; 648 } 649 650 for (n = dict_first(dict); n; n = dict_next(dict, n)) { 651 dq = dnode_get(n); 652 if (!dq) 653 continue; 654 if ((dq->dq_flags & DQF_SEEN) == 0) { 655 fprintf(stderr, "[QUOTA WARNING] " 656 "Missing quota entry ID %d\n", dq->dq_id); 657 scan_data.usage_is_inconsistent = 1; 658 } 659 } 660 *usage_inconsistent = scan_data.usage_is_inconsistent; 661 662 out_close_qh: 663 err = quota_file_close(qctx, &qh); 664 if (err) { 665 log_debug("Cannot close quotafile: %s", error_message(errno)); 666 if (qh.qh_qf.e2_file) 667 ext2fs_file_close(qh.qh_qf.e2_file); 668 } 669 out: 670 return err; 671 } 672 673 int parse_quota_opts(const char *opts, int (*func)(char *)) 674 { 675 char *buf, *token, *next, *p; 676 int len; 677 int ret = 0; 678 679 len = strlen(opts); 680 buf = malloc(len + 1); 681 if (!buf) { 682 fprintf(stderr, 683 "Couldn't allocate memory to parse quota options!\n"); 684 return -ENOMEM; 685 } 686 strcpy(buf, opts); 687 for (token = buf; token && *token; token = next) { 688 p = strchr(token, ','); 689 next = 0; 690 if (p) { 691 *p = 0; 692 next = p + 1; 693 } 694 ret = func(token); 695 if (ret) 696 break; 697 } 698 free(buf); 699 return ret; 700 } 701