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