mkquota.c revision f239fefc14226f655477179801c734749a04d4b4
1/* 2 * mkquota.c --- create quota files for a filesystem 3 * 4 * Aditya Kali <adityakali@google.com> 5 */ 6#include <sys/types.h> 7#include <sys/stat.h> 8#include <unistd.h> 9#include <errno.h> 10#include <string.h> 11#include <fcntl.h> 12 13#include "ext2fs/ext2_fs.h" 14#include "ext2fs/ext2fs.h" 15#include "e2p/e2p.h" 16 17#include "quota.h" 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 48int is_quota_on(ext2_filsys fs, int type) 49{ 50 char tmp[1024]; 51 qid_t id = (type == USRQUOTA) ? getuid() : getgid(); 52 53 if (!quotactl(QCMD(Q_V2_GETQUOTA, type), fs->device_name, id, tmp)) 54 return 1; 55 return 0; 56} 57 58/* 59 * Returns 0 if not able to find the quota file, otherwise returns its 60 * inode number. 61 */ 62int quota_file_exists(ext2_filsys fs, int qtype, int fmt) 63{ 64 char qf_name[256]; 65 errcode_t ret; 66 ext2_ino_t ino; 67 68 if (qtype >= MAXQUOTAS || fmt > QFMT_VFS_V1) 69 return -EINVAL; 70 71 get_qf_name(qtype, fmt, qf_name); 72 73 ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0, 74 &ino); 75 if (ret) 76 return 0; 77 78 return ino; 79} 80 81/* 82 * Set the value for reserved quota inode number field in superblock. 83 */ 84void set_sb_quota_inum(ext2_filsys fs, ext2_ino_t ino, int qtype) 85{ 86 ext2_ino_t *inump; 87 88 inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum : 89 &fs->super->s_grp_quota_inum; 90 91 log_debug("setting quota ino in superblock: ino=%u, type=%d", ino, 92 qtype); 93 *inump = ino; 94 ext2fs_mark_super_dirty(fs); 95} 96 97errcode_t remove_quota_inode(ext2_filsys fs, int qtype) 98{ 99 ext2_ino_t qf_ino; 100 101 ext2fs_read_bitmaps(fs); 102 qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum : 103 fs->super->s_grp_quota_inum; 104 set_sb_quota_inum(fs, 0, qtype); 105 /* Truncate the inode only if its a reserved one. */ 106 if (qf_ino < EXT2_FIRST_INODE(fs->super)) 107 truncate_quota_inode(fs, qf_ino); 108 109 ext2fs_mark_super_dirty(fs); 110 ext2fs_write_bitmaps(fs); 111 return 0; 112} 113 114static void write_dquots(dict_t *dict, struct quota_handle *qh) 115{ 116 int i = 0; 117 dnode_t *n; 118 struct dquot *dq; 119 __u32 key; 120 121 for (n = dict_first(dict); n; n = dict_next(dict, n)) { 122 dq = dnode_get(n); 123 if (dq) { 124 dq->dq_h = qh; 125 update_grace_times(dq); 126 qh->qh_ops->commit_dquot(dq, COMMIT_ALL); 127 } 128 } 129} 130 131errcode_t write_quota_inode(quota_ctx_t qctx, int qtype) 132{ 133 int retval, i; 134 unsigned long qf_inums[MAXQUOTAS]; 135 struct dquot *dquot; 136 dict_t *dict; 137 ext2_filsys fs; 138 struct quota_handle *h; 139 int fmt = QFMT_VFS_V1; 140 141 if (!qctx) 142 return; 143 144 fs = qctx->fs; 145 h = smalloc(sizeof(struct quota_handle)); 146 ext2fs_read_bitmaps(fs); 147 148 for (i = 0; i < MAXQUOTAS; i++) { 149 if ((qtype != -1) && (i != qtype)) 150 continue; 151 152 dict = qctx->quota_dict[i]; 153 if (!dict) 154 continue; 155 156 retval = new_io(h, fs, i, fmt); 157 if (retval < 0) { 158 log_err("Cannot initialize io on quotafile", ""); 159 continue; 160 } 161 162 write_dquots(dict, h); 163 retval = end_io(h); 164 if (retval < 0) { 165 log_err("Cannot finish IO on new quotafile: %s", 166 strerror(errno)); 167 if (h->qh_qf.e2_file) 168 ext2fs_file_close(h->qh_qf.e2_file); 169 truncate_quota_inode(fs, h->qh_qf.ino); 170 continue; 171 } 172 173 /* Set quota inode numbers in superblock. */ 174 set_sb_quota_inum(fs, h->qh_qf.ino, i); 175 ext2fs_mark_super_dirty(fs); 176 ext2fs_mark_bb_dirty(fs); 177 fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 178 } 179 180 ext2fs_write_bitmaps(fs); 181out: 182 free(h); 183 return retval; 184} 185 186/******************************************************************/ 187/* Helper functions for computing quota in memory. */ 188/******************************************************************/ 189 190static int dict_uint_cmp(const void *a, const void *b) 191{ 192 unsigned int c, d; 193 194 c = VOIDPTR_TO_UINT(a); 195 d = VOIDPTR_TO_UINT(b); 196 197 return c - d; 198} 199 200static qid_t get_qid(struct ext2_inode *inode, int qtype) 201{ 202 switch (qtype) { 203 case USRQUOTA: 204 return inode_uid(*inode); 205 case GRPQUOTA: 206 return inode_gid(*inode); 207 default: 208 log_err("Invalid quota type: %d", qtype); 209 BUG_ON(1); 210 } 211} 212 213static void quota_dnode_free(dnode_t *node, 214 void *context EXT2FS_ATTR((unused))) 215{ 216 void *ptr = node ? dnode_get(node) : 0; 217 218 free(ptr); 219 free(node); 220} 221 222/* 223 * Called in Pass #1 to set up the quota tracking data structures. 224 */ 225void init_quota_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype) 226{ 227 int i; 228 dict_t *dict; 229 quota_ctx_t ctx; 230 231 ctx = (quota_ctx_t)smalloc(sizeof(struct quota_ctx)); 232 memset(ctx, 0, sizeof(struct quota_ctx)); 233 for (i = 0; i < MAXQUOTAS; i++) { 234 if ((qtype != -1) && (i != qtype)) 235 continue; 236 dict = (dict_t *)smalloc(sizeof(dict_t)); 237 ctx->quota_dict[i] = dict; 238 dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp); 239 dict_set_allocator(dict, NULL, quota_dnode_free, NULL); 240 } 241 242 ctx->fs = fs; 243 *qctx = ctx; 244} 245 246void release_quota_context(quota_ctx_t *qctx) 247{ 248 dict_t *dict; 249 int i; 250 quota_ctx_t ctx; 251 252 if (!qctx) 253 return; 254 255 ctx = *qctx; 256 for (i = 0; i < MAXQUOTAS; i++) { 257 dict = ctx->quota_dict[i]; 258 ctx->quota_dict[i] = 0; 259 if (dict) { 260 dict_free_nodes(dict); 261 free(dict); 262 } 263 } 264 *qctx = NULL; 265 free(ctx); 266} 267 268static struct dquot *get_dq(dict_t *dict, __u32 key) 269{ 270 struct dquot *dq; 271 dnode_t *n; 272 273 n = dict_lookup(dict, UINT_TO_VOIDPTR(key)); 274 if (n) 275 dq = dnode_get(n); 276 else { 277 dq = smalloc(sizeof(struct dquot)); 278 memset(dq, 0, sizeof(struct dquot)); 279 dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq); 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 dq->dq_dqb.dqb_curspace += space; 306 } 307 } 308} 309 310/* 311 * Called to remove some blocks used by a particular inode 312 */ 313void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino, 314 qsize_t space) 315{ 316 struct dquot *dq; 317 dict_t *dict; 318 int i; 319 320 if (!qctx) 321 return; 322 323 log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, 324 inode_uid(*inode), 325 inode_gid(*inode), space); 326 for (i = 0; i < MAXQUOTAS; i++) { 327 dict = qctx->quota_dict[i]; 328 if (dict) { 329 dq = get_dq(dict, get_qid(inode, i)); 330 dq->dq_dqb.dqb_curspace -= space; 331 } 332 } 333} 334 335/* 336 * Called to count the files used by an inode's user/group 337 */ 338void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode, 339 ext2_ino_t ino, int adjust) 340{ 341 struct dquot *dq; 342 dict_t *dict; 343 int i; 344 345 if (!qctx) 346 return; 347 348 log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino, 349 inode_uid(*inode), 350 inode_gid(*inode), adjust); 351 for (i = 0; i < MAXQUOTAS; i++) { 352 dict = qctx->quota_dict[i]; 353 if (dict) { 354 dq = get_dq(dict, get_qid(inode, i)); 355 dq->dq_dqb.dqb_curinodes += adjust; 356 } 357 } 358} 359 360errcode_t compute_quota(quota_ctx_t qctx, int qtype) 361{ 362 ext2_filsys fs; 363 const char *name = "lost+found"; 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; 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 /* Convert i_blocks to # of 1k blocks */ 391 space = (ext2fs_inode_i_blocks(fs, &inode) + 1) >> 1; 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