mkquota.c revision 00eb0eee0addfd3b7ede98b85e00dff1547838a0
1/* 2 * mkquota.c --- create quota files for a filesystem 3 * 4 * Aditya Kali <adityakali@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 "mkquota.h" 22#include "common.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 29static 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#endif 49 50/* 51 * Returns 0 if not able to find the quota file, otherwise returns its 52 * inode number. 53 */ 54int quota_file_exists(ext2_filsys fs, int qtype, int fmt) 55{ 56 char qf_name[256]; 57 errcode_t ret; 58 ext2_ino_t ino; 59 60 if (qtype >= MAXQUOTAS) 61 return -EINVAL; 62 63 quota_get_qf_name(qtype, QFMT_VFS_V1, qf_name); 64 65 ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0, 66 &ino); 67 if (ret) 68 return 0; 69 70 return ino; 71} 72 73/* 74 * Set the value for reserved quota inode number field in superblock. 75 */ 76void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype) 77{ 78 ext2_ino_t *inump; 79 80 inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum : 81 &fs->super->s_grp_quota_inum; 82 83 log_debug("setting quota ino in superblock: ino=%u, type=%d", ino, 84 qtype); 85 *inump = ino; 86 ext2fs_mark_super_dirty(fs); 87} 88 89errcode_t quota_remove_inode(ext2_filsys fs, int qtype) 90{ 91 ext2_ino_t qf_ino; 92 93 ext2fs_read_bitmaps(fs); 94 qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum : 95 fs->super->s_grp_quota_inum; 96 quota_set_sb_inum(fs, 0, qtype); 97 /* Truncate the inode only if its a reserved one. */ 98 if (qf_ino < EXT2_FIRST_INODE(fs->super)) 99 quota_inode_truncate(fs, qf_ino); 100 101 ext2fs_mark_super_dirty(fs); 102 ext2fs_write_bitmaps(fs); 103 return 0; 104} 105 106static void write_dquots(dict_t *dict, struct quota_handle *qh) 107{ 108 dnode_t *n; 109 struct dquot *dq; 110 111 for (n = dict_first(dict); n; n = dict_next(dict, n)) { 112 dq = dnode_get(n); 113 if (dq) { 114 dq->dq_h = qh; 115 update_grace_times(dq); 116 qh->qh_ops->commit_dquot(dq); 117 } 118 } 119} 120 121errcode_t quota_write_inode(quota_ctx_t qctx, int qtype) 122{ 123 int retval = 0, i; 124 dict_t *dict; 125 ext2_filsys fs; 126 struct quota_handle *h = NULL; 127 int fmt = QFMT_VFS_V1; 128 129 if (!qctx) 130 return 0; 131 132 fs = qctx->fs; 133 retval = ext2fs_get_mem(sizeof(struct quota_handle), &h); 134 if (retval) { 135 log_err("Unable to allocate quota handle", ""); 136 goto out; 137 } 138 139 ext2fs_read_bitmaps(fs); 140 141 for (i = 0; i < MAXQUOTAS; i++) { 142 if ((qtype != -1) && (i != qtype)) 143 continue; 144 145 dict = qctx->quota_dict[i]; 146 if (!dict) 147 continue; 148 149 retval = quota_file_create(h, fs, i, fmt); 150 if (retval < 0) { 151 log_err("Cannot initialize io on quotafile", ""); 152 continue; 153 } 154 155 write_dquots(dict, h); 156 retval = quota_file_close(h); 157 if (retval < 0) { 158 log_err("Cannot finish IO on new quotafile: %s", 159 strerror(errno)); 160 if (h->qh_qf.e2_file) 161 ext2fs_file_close(h->qh_qf.e2_file); 162 quota_inode_truncate(fs, h->qh_qf.ino); 163 continue; 164 } 165 166 /* Set quota inode numbers in superblock. */ 167 quota_set_sb_inum(fs, h->qh_qf.ino, i); 168 ext2fs_mark_super_dirty(fs); 169 ext2fs_mark_bb_dirty(fs); 170 fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 171 } 172 173 ext2fs_write_bitmaps(fs); 174out: 175 if (h) 176 ext2fs_free_mem(&h); 177 return retval; 178} 179 180/******************************************************************/ 181/* Helper functions for computing quota in memory. */ 182/******************************************************************/ 183 184static int dict_uint_cmp(const void *a, const void *b) 185{ 186 unsigned int c, d; 187 188 c = VOIDPTR_TO_UINT(a); 189 d = VOIDPTR_TO_UINT(b); 190 191 return c - d; 192} 193 194static inline qid_t get_qid(struct ext2_inode *inode, int qtype) 195{ 196 if (qtype == USRQUOTA) 197 return inode_uid(*inode); 198 return inode_gid(*inode); 199} 200 201static void quota_dnode_free(dnode_t *node, 202 void *context EXT2FS_ATTR((unused))) 203{ 204 void *ptr = node ? dnode_get(node) : 0; 205 206 ext2fs_free_mem(&ptr); 207 free(node); 208} 209 210/* 211 * Set up the quota tracking data structures. 212 */ 213errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype) 214{ 215 int i, err = 0; 216 dict_t *dict; 217 quota_ctx_t ctx; 218 219 err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx); 220 if (err) { 221 log_err("Failed to allocate quota context", ""); 222 return err; 223 } 224 225 memset(ctx, 0, sizeof(struct quota_ctx)); 226 for (i = 0; i < MAXQUOTAS; i++) { 227 if ((qtype != -1) && (i != qtype)) 228 continue; 229 err = ext2fs_get_mem(sizeof(dict_t), &dict); 230 if (err) { 231 log_err("Failed to allocate dictionary", ""); 232 return err; 233 } 234 ctx->quota_dict[i] = dict; 235 dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp); 236 dict_set_allocator(dict, NULL, quota_dnode_free, NULL); 237 } 238 239 ctx->fs = fs; 240 *qctx = ctx; 241 return 0; 242} 243 244void quota_release_context(quota_ctx_t *qctx) 245{ 246 dict_t *dict; 247 int i; 248 quota_ctx_t ctx; 249 250 if (!qctx) 251 return; 252 253 ctx = *qctx; 254 for (i = 0; i < MAXQUOTAS; i++) { 255 dict = ctx->quota_dict[i]; 256 ctx->quota_dict[i] = 0; 257 if (dict) { 258 dict_free_nodes(dict); 259 free(dict); 260 } 261 } 262 *qctx = NULL; 263 free(ctx); 264} 265 266static struct dquot *get_dq(dict_t *dict, __u32 key) 267{ 268 struct dquot *dq; 269 dnode_t *n; 270 271 n = dict_lookup(dict, UINT_TO_VOIDPTR(key)); 272 if (n) 273 dq = dnode_get(n); 274 else { 275 if (ext2fs_get_mem(sizeof(struct dquot), &dq)) { 276 log_err("Unable to allocate dquot", ""); 277 return NULL; 278 } 279 memset(dq, 0, sizeof(struct dquot)); 280 dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq); 281 dq->dq_id = key; 282 } 283 return dq; 284} 285 286 287/* 288 * Called to update the blocks used by a particular inode 289 */ 290void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino, 291 qsize_t space) 292{ 293 struct dquot *dq; 294 dict_t *dict; 295 int i; 296 297 if (!qctx) 298 return; 299 300 log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 301 inode_uid(*inode), 302 inode_gid(*inode), space); 303 for (i = 0; i < MAXQUOTAS; i++) { 304 dict = qctx->quota_dict[i]; 305 if (dict) { 306 dq = get_dq(dict, get_qid(inode, i)); 307 if (dq) 308 dq->dq_dqb.dqb_curspace += space; 309 } 310 } 311} 312 313/* 314 * Called to remove some blocks used by a particular inode 315 */ 316void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino, 317 qsize_t space) 318{ 319 struct dquot *dq; 320 dict_t *dict; 321 int i; 322 323 if (!qctx) 324 return; 325 326 log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 327 inode_uid(*inode), 328 inode_gid(*inode), space); 329 for (i = 0; i < MAXQUOTAS; i++) { 330 dict = qctx->quota_dict[i]; 331 if (dict) { 332 dq = get_dq(dict, get_qid(inode, i)); 333 dq->dq_dqb.dqb_curspace -= space; 334 } 335 } 336} 337 338/* 339 * Called to count the files used by an inode's user/group 340 */ 341void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode, 342 ext2_ino_t ino, int adjust) 343{ 344 struct dquot *dq; 345 dict_t *dict; 346 int i; 347 348 if (!qctx) 349 return; 350 351 log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino, 352 inode_uid(*inode), 353 inode_gid(*inode), adjust); 354 for (i = 0; i < MAXQUOTAS; i++) { 355 dict = qctx->quota_dict[i]; 356 if (dict) { 357 dq = get_dq(dict, get_qid(inode, i)); 358 dq->dq_dqb.dqb_curinodes += adjust; 359 } 360 } 361} 362 363errcode_t quota_compute_usage(quota_ctx_t qctx) 364{ 365 ext2_filsys fs; 366 ext2_ino_t ino; 367 errcode_t ret; 368 struct ext2_inode inode; 369 qsize_t space; 370 ext2_inode_scan scan; 371 372 if (!qctx) 373 return 0; 374 375 fs = qctx->fs; 376 ret = ext2fs_open_inode_scan(fs, 0, &scan); 377 if (ret) { 378 log_err("while opening inode scan. ret=%ld", ret); 379 return ret; 380 } 381 382 while (1) { 383 ret = ext2fs_get_next_inode(scan, &ino, &inode); 384 if (ret) { 385 log_err("while getting next inode. ret=%ld", ret); 386 ext2fs_close_inode_scan(scan); 387 return ret; 388 } 389 if (ino == 0) 390 break; 391 if (inode.i_links_count && 392 (ino == EXT2_ROOT_INO || 393 ino >= EXT2_FIRST_INODE(fs->super))) { 394 space = ext2fs_inode_i_blocks(fs, &inode) << 9; 395 quota_data_add(qctx, &inode, ino, space); 396 quota_data_inodes(qctx, &inode, ino, +1); 397 } 398 } 399 400 ext2fs_close_inode_scan(scan); 401 402 return 0; 403} 404 405struct scan_dquots_data { 406 dict_t *quota_dict; 407 int update_limits; /* update limits from disk */ 408 int update_usage; 409 int usage_is_inconsistent; 410}; 411 412static int scan_dquots_callback(struct dquot *dquot, void *cb_data) 413{ 414 struct scan_dquots_data *scan_data = cb_data; 415 dict_t *quota_dict = scan_data->quota_dict; 416 struct dquot *dq; 417 418 dq = get_dq(quota_dict, dquot->dq_id); 419 dq->dq_id = dquot->dq_id; 420 dq->dq_dqb.u.v2_mdqb.dqb_off = dquot->dq_dqb.u.v2_mdqb.dqb_off; 421 422 /* Check if there is inconsistancy. */ 423 if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace || 424 dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) { 425 scan_data->usage_is_inconsistent = 1; 426 fprintf(stderr, "[QUOTA WARNING] Usage inconsistent for ID %d:" 427 "actual (%llu, %llu) != expected (%llu, %llu)\n", 428 dq->dq_id, dq->dq_dqb.dqb_curspace, 429 dq->dq_dqb.dqb_curinodes, dquot->dq_dqb.dqb_curspace, 430 dquot->dq_dqb.dqb_curinodes); 431 } 432 433 if (scan_data->update_limits) { 434 dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit; 435 dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit; 436 dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit; 437 dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit; 438 } 439 440 if (scan_data->update_usage) { 441 dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace; 442 dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes; 443 } 444 445 return 0; 446} 447 448/* 449 * Read all dquots from quota file into memory 450 */ 451static errcode_t quota_read_all_dquots(struct quota_handle *qh, 452 quota_ctx_t qctx, int update_limits) 453{ 454 struct scan_dquots_data scan_data; 455 456 scan_data.quota_dict = qctx->quota_dict[qh->qh_type]; 457 scan_data.update_limits = update_limits; 458 scan_data.update_usage = 0; 459 460 return qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data); 461} 462 463/* 464 * Write all memory dquots into quota file 465 */ 466static errcode_t quota_write_all_dquots(struct quota_handle *qh, 467 quota_ctx_t qctx) 468{ 469 errcode_t err; 470 471 err = ext2fs_read_bitmaps(qctx->fs); 472 if (err) 473 return err; 474 write_dquots(qctx->quota_dict[qh->qh_type], qh); 475 ext2fs_mark_bb_dirty(qctx->fs); 476 qctx->fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 477 ext2fs_write_bitmaps(qctx->fs); 478 return 0; 479} 480 481/* 482 * Updates the in-memory quota limits from the given quota inode. 483 */ 484errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino, int type) 485{ 486 struct quota_handle *qh; 487 errcode_t err; 488 489 if (!qctx) 490 return 0; 491 492 err = ext2fs_get_mem(sizeof(struct quota_handle), &qh); 493 if (err) { 494 log_err("Unable to allocate quota handle", ""); 495 return err; 496 } 497 498 err = quota_file_open(qh, qctx->fs, qf_ino, type, -1, 0); 499 if (err) { 500 log_err("Open quota file failed", ""); 501 goto out; 502 } 503 504 quota_read_all_dquots(qh, qctx, 1); 505 506 err = quota_file_close(qh); 507 if (err) { 508 log_err("Cannot finish IO on new quotafile: %s", 509 strerror(errno)); 510 if (qh->qh_qf.e2_file) 511 ext2fs_file_close(qh->qh_qf.e2_file); 512 } 513out: 514 ext2fs_free_mem(&qh); 515 return err; 516} 517 518/* 519 * Compares the measured quota in qctx->quota_dict with that in the quota inode 520 * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is 521 * set to 1 if the supplied and on-disk quota usage values are not identical. 522 */ 523errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype, 524 int *usage_inconsistent) 525{ 526 ext2_filsys fs = qctx->fs; 527 struct quota_handle qh; 528 struct scan_dquots_data scan_data; 529 ext2_ino_t qf_ino; 530 errcode_t err = 0; 531 532 if (!qctx->quota_dict[qtype]) 533 goto out; 534 535 qf_ino = qtype == USRQUOTA ? fs->super->s_usr_quota_inum : 536 fs->super->s_grp_quota_inum; 537 err = quota_file_open(&qh, fs, qf_ino, qtype, -1, 0); 538 if (err) { 539 log_err("Open quota file failed", ""); 540 goto out; 541 } 542 543 scan_data.quota_dict = qctx->quota_dict[qtype]; 544 scan_data.update_limits = 1; 545 scan_data.update_usage = 0; 546 scan_data.usage_is_inconsistent = 0; 547 err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data); 548 if (err) { 549 log_err("Error scanning dquots", ""); 550 goto out; 551 } 552 *usage_inconsistent = scan_data.usage_is_inconsistent; 553 554out: 555 return err; 556} 557