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