mkquota.c revision 1527d99d378a4a45f1e36b197bed628ce28d73ee
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#ifdef HAVE_SYS_QUOTA_H 10#include <sys/quota.h> 11#endif 12#include <unistd.h> 13#include <errno.h> 14#include <string.h> 15#include <fcntl.h> 16 17#include "ext2fs/ext2_fs.h" 18#include "ext2fs/ext2fs.h" 19#include "e2p/e2p.h" 20 21#include "quota.h" 22#include "quotaio.h" 23#include "quotaio_v2.h" 24#include "quotaio_tree.h" 25#include "mkquota.h" 26#include "common.h" 27 28/* Needed for architectures where sizeof(int) != sizeof(void *) */ 29#define UINT_TO_VOIDPTR(val) ((void *)(intptr_t)(val)) 30#define VOIDPTR_TO_UINT(ptr) ((unsigned int)(intptr_t)(ptr)) 31 32static void print_inode(struct ext2_inode *inode) 33{ 34 if (!inode) 35 return; 36 37 fprintf(stderr, " i_mode = %d\n", inode->i_mode); 38 fprintf(stderr, " i_uid = %d\n", inode->i_uid); 39 fprintf(stderr, " i_size = %d\n", inode->i_size); 40 fprintf(stderr, " i_atime = %d\n", inode->i_atime); 41 fprintf(stderr, " i_ctime = %d\n", inode->i_ctime); 42 fprintf(stderr, " i_mtime = %d\n", inode->i_mtime); 43 fprintf(stderr, " i_dtime = %d\n", inode->i_dtime); 44 fprintf(stderr, " i_gid = %d\n", inode->i_gid); 45 fprintf(stderr, " i_links_count = %d\n", inode->i_links_count); 46 fprintf(stderr, " i_blocks = %d\n", inode->i_blocks); 47 fprintf(stderr, " i_flags = %d\n", inode->i_flags); 48 49 return; 50} 51 52int quota_is_on(ext2_filsys fs, int type) 53{ 54 char tmp[1024]; 55 qid_t id = (type == USRQUOTA) ? getuid() : getgid(); 56 57#ifdef HAVE_QUOTACTL 58 if (!quotactl(QCMD(Q_V2_GETQUOTA, type), fs->device_name, id, tmp)) 59 return 1; 60#endif 61 return 0; 62} 63 64/* 65 * Returns 0 if not able to find the quota file, otherwise returns its 66 * inode number. 67 */ 68int quota_file_exists(ext2_filsys fs, int qtype, int fmt) 69{ 70 char qf_name[256]; 71 errcode_t ret; 72 ext2_ino_t ino; 73 74 if (qtype >= MAXQUOTAS) 75 return -EINVAL; 76 77 quota_get_qf_name(qtype, fmt, qf_name); 78 79 ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0, 80 &ino); 81 if (ret) 82 return 0; 83 84 return ino; 85} 86 87/* 88 * Set the value for reserved quota inode number field in superblock. 89 */ 90void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype) 91{ 92 ext2_ino_t *inump; 93 94 inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum : 95 &fs->super->s_grp_quota_inum; 96 97 log_debug("setting quota ino in superblock: ino=%u, type=%d", ino, 98 qtype); 99 *inump = ino; 100 ext2fs_mark_super_dirty(fs); 101} 102 103errcode_t quota_remove_inode(ext2_filsys fs, int qtype) 104{ 105 ext2_ino_t qf_ino; 106 107 ext2fs_read_bitmaps(fs); 108 qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum : 109 fs->super->s_grp_quota_inum; 110 quota_set_sb_inum(fs, 0, qtype); 111 /* Truncate the inode only if its a reserved one. */ 112 if (qf_ino < EXT2_FIRST_INODE(fs->super)) 113 quota_inode_truncate(fs, qf_ino); 114 115 ext2fs_mark_super_dirty(fs); 116 ext2fs_write_bitmaps(fs); 117 return 0; 118} 119 120static void write_dquots(dict_t *dict, struct quota_handle *qh) 121{ 122 dnode_t *n; 123 struct dquot *dq; 124 125 for (n = dict_first(dict); n; n = dict_next(dict, n)) { 126 dq = dnode_get(n); 127 if (dq) { 128 dq->dq_h = qh; 129 update_grace_times(dq); 130 qh->qh_ops->commit_dquot(dq); 131 } 132 } 133} 134 135errcode_t quota_write_inode(quota_ctx_t qctx, int qtype) 136{ 137 int retval = 0, i; 138 dict_t *dict; 139 ext2_filsys fs; 140 struct quota_handle *h; 141 int fmt = QFMT_VFS_V1; 142 143 if (!qctx) 144 return 0; 145 146 fs = qctx->fs; 147 retval = ext2fs_get_mem(sizeof(struct quota_handle), &h); 148 if (retval) { 149 log_err("Unable to allocate quota handle", ""); 150 goto out; 151 } 152 153 ext2fs_read_bitmaps(fs); 154 155 for (i = 0; i < MAXQUOTAS; i++) { 156 if ((qtype != -1) && (i != qtype)) 157 continue; 158 159 dict = qctx->quota_dict[i]; 160 if (!dict) 161 continue; 162 163 retval = quota_file_create(h, fs, i, fmt); 164 if (retval < 0) { 165 log_err("Cannot initialize io on quotafile", ""); 166 continue; 167 } 168 169 write_dquots(dict, h); 170 retval = quota_file_close(h); 171 if (retval < 0) { 172 log_err("Cannot finish IO on new quotafile: %s", 173 strerror(errno)); 174 if (h->qh_qf.e2_file) 175 ext2fs_file_close(h->qh_qf.e2_file); 176 quota_inode_truncate(fs, h->qh_qf.ino); 177 continue; 178 } 179 180 /* Set quota inode numbers in superblock. */ 181 quota_set_sb_inum(fs, h->qh_qf.ino, i); 182 ext2fs_mark_super_dirty(fs); 183 ext2fs_mark_bb_dirty(fs); 184 fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 185 } 186 187 ext2fs_write_bitmaps(fs); 188out: 189 if (h) 190 ext2fs_free_mem(&h); 191 return retval; 192} 193 194/******************************************************************/ 195/* Helper functions for computing quota in memory. */ 196/******************************************************************/ 197 198static int dict_uint_cmp(const void *a, const void *b) 199{ 200 unsigned int c, d; 201 202 c = VOIDPTR_TO_UINT(a); 203 d = VOIDPTR_TO_UINT(b); 204 205 return c - d; 206} 207 208static inline qid_t get_qid(struct ext2_inode *inode, int qtype) 209{ 210 if (qtype == USRQUOTA) 211 return inode_uid(*inode); 212 return inode_gid(*inode); 213} 214 215static void quota_dnode_free(dnode_t *node, 216 void *context EXT2FS_ATTR((unused))) 217{ 218 void *ptr = node ? dnode_get(node) : 0; 219 220 ext2fs_free_mem(&ptr); 221 free(node); 222} 223 224/* 225 * Set up the quota tracking data structures. 226 */ 227errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype) 228{ 229 int i, err = 0; 230 dict_t *dict; 231 quota_ctx_t ctx; 232 233 err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx); 234 if (err) { 235 log_err("Failed to allocate quota context", ""); 236 return err; 237 } 238 239 memset(ctx, 0, sizeof(struct quota_ctx)); 240 for (i = 0; i < MAXQUOTAS; i++) { 241 if ((qtype != -1) && (i != qtype)) 242 continue; 243 err = ext2fs_get_mem(sizeof(dict_t), &dict); 244 if (err) { 245 log_err("Failed to allocate dictionary", ""); 246 return err; 247 } 248 ctx->quota_dict[i] = dict; 249 dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp); 250 dict_set_allocator(dict, NULL, quota_dnode_free, NULL); 251 } 252 253 ctx->fs = fs; 254 *qctx = ctx; 255 return 0; 256} 257 258void quota_release_context(quota_ctx_t *qctx) 259{ 260 dict_t *dict; 261 int i; 262 quota_ctx_t ctx; 263 264 if (!qctx) 265 return; 266 267 ctx = *qctx; 268 for (i = 0; i < MAXQUOTAS; i++) { 269 dict = ctx->quota_dict[i]; 270 ctx->quota_dict[i] = 0; 271 if (dict) { 272 dict_free_nodes(dict); 273 free(dict); 274 } 275 } 276 *qctx = NULL; 277 free(ctx); 278} 279 280static struct dquot *get_dq(dict_t *dict, __u32 key) 281{ 282 struct dquot *dq; 283 dnode_t *n; 284 285 n = dict_lookup(dict, UINT_TO_VOIDPTR(key)); 286 if (n) 287 dq = dnode_get(n); 288 else { 289 if (ext2fs_get_mem(sizeof(struct dquot), &dq)) { 290 log_err("Unable to allocate dquot", ""); 291 return NULL; 292 } 293 memset(dq, 0, sizeof(struct dquot)); 294 dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq); 295 dq->dq_id = key; 296 } 297 return dq; 298} 299 300 301/* 302 * Called to update the blocks used by a particular inode 303 */ 304void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino, 305 qsize_t space) 306{ 307 struct dquot *dq; 308 dict_t *dict; 309 int i; 310 311 if (!qctx) 312 return; 313 314 log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 315 inode_uid(*inode), 316 inode_gid(*inode), space); 317 for (i = 0; i < MAXQUOTAS; i++) { 318 dict = qctx->quota_dict[i]; 319 if (dict) { 320 dq = get_dq(dict, get_qid(inode, i)); 321 if (dq) 322 dq->dq_dqb.dqb_curspace += space; 323 } 324 } 325} 326 327/* 328 * Called to remove some blocks used by a particular inode 329 */ 330void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino, 331 qsize_t space) 332{ 333 struct dquot *dq; 334 dict_t *dict; 335 int i; 336 337 if (!qctx) 338 return; 339 340 log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 341 inode_uid(*inode), 342 inode_gid(*inode), space); 343 for (i = 0; i < MAXQUOTAS; i++) { 344 dict = qctx->quota_dict[i]; 345 if (dict) { 346 dq = get_dq(dict, get_qid(inode, i)); 347 dq->dq_dqb.dqb_curspace -= space; 348 } 349 } 350} 351 352/* 353 * Called to count the files used by an inode's user/group 354 */ 355void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode, 356 ext2_ino_t ino, int adjust) 357{ 358 struct dquot *dq; 359 dict_t *dict; 360 int i; 361 362 if (!qctx) 363 return; 364 365 log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino, 366 inode_uid(*inode), 367 inode_gid(*inode), adjust); 368 for (i = 0; i < MAXQUOTAS; i++) { 369 dict = qctx->quota_dict[i]; 370 if (dict) { 371 dq = get_dq(dict, get_qid(inode, i)); 372 dq->dq_dqb.dqb_curinodes += adjust; 373 } 374 } 375} 376 377errcode_t quota_compute_usage(quota_ctx_t qctx) 378{ 379 ext2_filsys fs; 380 ext2_ino_t ino; 381 errcode_t ret; 382 struct ext2_inode inode; 383 qsize_t space; 384 ext2_inode_scan scan; 385 386 if (!qctx) 387 return 0; 388 389 fs = qctx->fs; 390 ret = ext2fs_open_inode_scan(fs, 0, &scan); 391 if (ret) { 392 log_err("while opening inode scan. ret=%ld", ret); 393 return ret; 394 } 395 396 while (1) { 397 ret = ext2fs_get_next_inode(scan, &ino, &inode); 398 if (ret) { 399 log_err("while getting next inode. ret=%ld", ret); 400 ext2fs_close_inode_scan(scan); 401 return ret; 402 } 403 if (ino == 0) 404 break; 405 if (inode.i_links_count) { 406 space = ext2fs_inode_i_blocks(fs, &inode) << 9; 407 quota_data_add(qctx, &inode, ino, space); 408 quota_data_inodes(qctx, &inode, ino, +1); 409 } 410 } 411 412 ext2fs_close_inode_scan(scan); 413 414 return 0; 415} 416 417struct scan_dquots_data { 418 quota_ctx_t qctx; 419 int limit_only; /* read limit only */ 420}; 421 422static int scan_dquots_callback(struct dquot *dquot, void *cb_data) 423{ 424 struct scan_dquots_data *scan_data = 425 (struct scan_dquots_data *)cb_data; 426 quota_ctx_t qctx = scan_data->qctx; 427 struct dquot *dq; 428 429 dq = get_dq(qctx->quota_dict[dquot->dq_h->qh_type], dquot->dq_id); 430 431 dq->dq_id = dquot->dq_id; 432 if (scan_data->limit_only) { 433 dq->dq_dqb.u.v2_mdqb.dqb_off = dquot->dq_dqb.u.v2_mdqb.dqb_off; 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 } else { 439 dq->dq_dqb = dquot->dq_dqb; 440 } 441 return 0; 442} 443 444/* 445 * Read all dquots from quota file into memory 446 */ 447static errcode_t quota_read_all_dquots(struct quota_handle *qh, 448 quota_ctx_t qctx, int limit_only) 449{ 450 struct scan_dquots_data scan_data; 451 452 scan_data.qctx = qctx; 453 scan_data.limit_only = limit_only; 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