mkquota.c revision d1154eb460efe588eaed3d439c1caaca149fa362
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 is_quota_on(ext2_filsys fs, int type) 50{ 51 char tmp[1024]; 52 qid_t id = (type == USRQUOTA) ? getuid() : getgid(); 53 54 if (!quotactl(QCMD(Q_V2_GETQUOTA, type), fs->device_name, id, tmp)) 55 return 1; 56 return 0; 57} 58 59/* 60 * Returns 0 if not able to find the quota file, otherwise returns its 61 * inode number. 62 */ 63int quota_file_exists(ext2_filsys fs, int qtype, int fmt) 64{ 65 char qf_name[256]; 66 errcode_t ret; 67 ext2_ino_t ino; 68 69 if (qtype >= MAXQUOTAS || fmt > QFMT_VFS_V1) 70 return -EINVAL; 71 72 get_qf_name(qtype, fmt, qf_name); 73 74 ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0, 75 &ino); 76 if (ret) 77 return 0; 78 79 return ino; 80} 81 82/* 83 * Set the value for reserved quota inode number field in superblock. 84 */ 85void set_sb_quota_inum(ext2_filsys fs, ext2_ino_t ino, int qtype) 86{ 87 ext2_ino_t *inump; 88 89 inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum : 90 &fs->super->s_grp_quota_inum; 91 92 log_debug("setting quota ino in superblock: ino=%u, type=%d", ino, 93 qtype); 94 *inump = ino; 95 ext2fs_mark_super_dirty(fs); 96} 97 98errcode_t remove_quota_inode(ext2_filsys fs, int qtype) 99{ 100 ext2_ino_t qf_ino; 101 102 ext2fs_read_bitmaps(fs); 103 qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum : 104 fs->super->s_grp_quota_inum; 105 set_sb_quota_inum(fs, 0, qtype); 106 /* Truncate the inode only if its a reserved one. */ 107 if (qf_ino < EXT2_FIRST_INODE(fs->super)) 108 truncate_quota_inode(fs, qf_ino); 109 110 ext2fs_mark_super_dirty(fs); 111 ext2fs_write_bitmaps(fs); 112 return 0; 113} 114 115static void write_dquots(dict_t *dict, struct quota_handle *qh) 116{ 117 int i = 0; 118 dnode_t *n; 119 struct dquot *dq; 120 __u32 key; 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, COMMIT_ALL); 128 } 129 } 130} 131 132errcode_t write_quota_inode(quota_ctx_t qctx, int qtype) 133{ 134 int retval, i; 135 unsigned long qf_inums[MAXQUOTAS]; 136 struct dquot *dquot; 137 dict_t *dict; 138 ext2_filsys fs; 139 struct quota_handle *h; 140 int fmt = QFMT_VFS_V1; 141 142 if (!qctx) 143 return; 144 145 fs = qctx->fs; 146 h = smalloc(sizeof(struct quota_handle)); 147 ext2fs_read_bitmaps(fs); 148 149 for (i = 0; i < MAXQUOTAS; i++) { 150 if ((qtype != -1) && (i != qtype)) 151 continue; 152 153 dict = qctx->quota_dict[i]; 154 if (!dict) 155 continue; 156 157 retval = new_io(h, fs, i, fmt); 158 if (retval < 0) { 159 log_err("Cannot initialize io on quotafile", ""); 160 continue; 161 } 162 163 write_dquots(dict, h); 164 retval = end_io(h); 165 if (retval < 0) { 166 log_err("Cannot finish IO on new quotafile: %s", 167 strerror(errno)); 168 if (h->qh_qf.e2_file) 169 ext2fs_file_close(h->qh_qf.e2_file); 170 truncate_quota_inode(fs, h->qh_qf.ino); 171 continue; 172 } 173 174 /* Set quota inode numbers in superblock. */ 175 set_sb_quota_inum(fs, h->qh_qf.ino, i); 176 ext2fs_mark_super_dirty(fs); 177 ext2fs_mark_bb_dirty(fs); 178 fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 179 } 180 181 ext2fs_write_bitmaps(fs); 182out: 183 free(h); 184 return retval; 185} 186 187/******************************************************************/ 188/* Helper functions for computing quota in memory. */ 189/******************************************************************/ 190 191static int dict_uint_cmp(const void *a, const void *b) 192{ 193 unsigned int c, d; 194 195 c = VOIDPTR_TO_UINT(a); 196 d = VOIDPTR_TO_UINT(b); 197 198 return c - d; 199} 200 201static qid_t get_qid(struct ext2_inode *inode, int qtype) 202{ 203 switch (qtype) { 204 case USRQUOTA: 205 return inode_uid(*inode); 206 case GRPQUOTA: 207 return inode_gid(*inode); 208 default: 209 log_err("Invalid quota type: %d", qtype); 210 BUG_ON(1); 211 } 212} 213 214static void quota_dnode_free(dnode_t *node, 215 void *context EXT2FS_ATTR((unused))) 216{ 217 void *ptr = node ? dnode_get(node) : 0; 218 219 free(ptr); 220 free(node); 221} 222 223/* 224 * Called in Pass #1 to set up the quota tracking data structures. 225 */ 226void init_quota_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype) 227{ 228 int i; 229 dict_t *dict; 230 quota_ctx_t ctx; 231 232 ctx = (quota_ctx_t)smalloc(sizeof(struct quota_ctx)); 233 memset(ctx, 0, sizeof(struct quota_ctx)); 234 for (i = 0; i < MAXQUOTAS; i++) { 235 if ((qtype != -1) && (i != qtype)) 236 continue; 237 dict = (dict_t *)smalloc(sizeof(dict_t)); 238 ctx->quota_dict[i] = dict; 239 dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp); 240 dict_set_allocator(dict, NULL, quota_dnode_free, NULL); 241 } 242 243 ctx->fs = fs; 244 *qctx = ctx; 245} 246 247void release_quota_context(quota_ctx_t *qctx) 248{ 249 dict_t *dict; 250 int i; 251 quota_ctx_t ctx; 252 253 if (!qctx) 254 return; 255 256 ctx = *qctx; 257 for (i = 0; i < MAXQUOTAS; i++) { 258 dict = ctx->quota_dict[i]; 259 ctx->quota_dict[i] = 0; 260 if (dict) { 261 dict_free_nodes(dict); 262 free(dict); 263 } 264 } 265 *qctx = NULL; 266 free(ctx); 267} 268 269static struct dquot *get_dq(dict_t *dict, __u32 key) 270{ 271 struct dquot *dq; 272 dnode_t *n; 273 274 n = dict_lookup(dict, UINT_TO_VOIDPTR(key)); 275 if (n) 276 dq = dnode_get(n); 277 else { 278 dq = smalloc(sizeof(struct dquot)); 279 memset(dq, 0, sizeof(struct dquot)); 280 dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq); 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 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 compute_quota(quota_ctx_t qctx, int qtype) 362{ 363 ext2_filsys fs; 364 const char *name = "lost+found"; 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; 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 /* Convert i_blocks to # of 1k blocks */ 392 space = (ext2fs_inode_i_blocks(fs, &inode) + 1) >> 1; 393 quota_data_add(qctx, &inode, ino, space); 394 quota_data_inodes(qctx, &inode, ino, +1); 395 } 396 } 397 398 ext2fs_close_inode_scan(scan); 399 400 return 0; 401} 402