mkquota.c revision 7bed9a78828e56ddb687cf2b5f1d6ec1f5e5ecc6
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 } 296 return dq; 297} 298 299 300/* 301 * Called to update the blocks used by a particular inode 302 */ 303void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino, 304 qsize_t space) 305{ 306 struct dquot *dq; 307 dict_t *dict; 308 int i; 309 310 if (!qctx) 311 return; 312 313 log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 314 inode_uid(*inode), 315 inode_gid(*inode), space); 316 for (i = 0; i < MAXQUOTAS; i++) { 317 dict = qctx->quota_dict[i]; 318 if (dict) { 319 dq = get_dq(dict, get_qid(inode, i)); 320 if (dq) 321 dq->dq_dqb.dqb_curspace += space; 322 } 323 } 324} 325 326/* 327 * Called to remove some blocks used by a particular inode 328 */ 329void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino, 330 qsize_t space) 331{ 332 struct dquot *dq; 333 dict_t *dict; 334 int i; 335 336 if (!qctx) 337 return; 338 339 log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 340 inode_uid(*inode), 341 inode_gid(*inode), space); 342 for (i = 0; i < MAXQUOTAS; i++) { 343 dict = qctx->quota_dict[i]; 344 if (dict) { 345 dq = get_dq(dict, get_qid(inode, i)); 346 dq->dq_dqb.dqb_curspace -= space; 347 } 348 } 349} 350 351/* 352 * Called to count the files used by an inode's user/group 353 */ 354void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode, 355 ext2_ino_t ino, int adjust) 356{ 357 struct dquot *dq; 358 dict_t *dict; 359 int i; 360 361 if (!qctx) 362 return; 363 364 log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino, 365 inode_uid(*inode), 366 inode_gid(*inode), adjust); 367 for (i = 0; i < MAXQUOTAS; i++) { 368 dict = qctx->quota_dict[i]; 369 if (dict) { 370 dq = get_dq(dict, get_qid(inode, i)); 371 dq->dq_dqb.dqb_curinodes += adjust; 372 } 373 } 374} 375 376errcode_t quota_compute_usage(quota_ctx_t qctx) 377{ 378 ext2_filsys fs; 379 ext2_ino_t ino; 380 errcode_t ret; 381 struct ext2_inode inode; 382 qsize_t space; 383 ext2_inode_scan scan; 384 385 if (!qctx) 386 return 0; 387 388 fs = qctx->fs; 389 ret = ext2fs_open_inode_scan(fs, 0, &scan); 390 if (ret) { 391 log_err("while opening inode scan. ret=%ld", ret); 392 return ret; 393 } 394 395 while (1) { 396 ret = ext2fs_get_next_inode(scan, &ino, &inode); 397 if (ret) { 398 log_err("while getting next inode. ret=%ld", ret); 399 ext2fs_close_inode_scan(scan); 400 return ret; 401 } 402 if (ino == 0) 403 break; 404 if (inode.i_links_count) { 405 space = ext2fs_inode_i_blocks(fs, &inode) << 9; 406 quota_data_add(qctx, &inode, ino, space); 407 quota_data_inodes(qctx, &inode, ino, +1); 408 } 409 } 410 411 ext2fs_close_inode_scan(scan); 412 413 return 0; 414} 415 416struct scan_dquots_data { 417 quota_ctx_t qctx; 418 int limit_only; /* read limit only */ 419}; 420 421static int scan_dquots_callback(struct dquot *dquot, void *cb_data) 422{ 423 struct scan_dquots_data *scan_data = 424 (struct scan_dquots_data *)cb_data; 425 quota_ctx_t qctx = scan_data->qctx; 426 struct dquot *dq; 427 428 dq = get_dq(qctx->quota_dict[dquot->dq_h->qh_type], dquot->dq_id); 429 430 dq->dq_id = dquot->dq_id; 431 if (scan_data->limit_only) { 432 dq->dq_dqb.u.v2_mdqb.dqb_off = dquot->dq_dqb.u.v2_mdqb.dqb_off; 433 dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit; 434 dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit; 435 dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit; 436 dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit; 437 } else { 438 dq->dq_dqb = dquot->dq_dqb; 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 limit_only) 448{ 449 struct scan_dquots_data scan_data; 450 451 scan_data.qctx = qctx; 452 scan_data.limit_only = limit_only; 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