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