mkquota.c revision 36e4e21f511423450285568ceb26b3ddb233e286
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 is_quota_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) 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 snprintf(qf_name, sizeof(qf_name), "aquota.%s", type2name(qtype)); 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 set_sb_quota_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 remove_quota_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 set_sb_quota_inum(fs, 0, qtype); 111 /* Truncate the inode only if its a reserved one. */ 112 if (qf_ino < EXT2_FIRST_INODE(fs->super)) 113 truncate_quota_inode(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, COMMIT_ALL); 131 } 132 } 133} 134 135errcode_t write_quota_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 h = smalloc(sizeof(struct quota_handle)); 148 ext2fs_read_bitmaps(fs); 149 150 for (i = 0; i < MAXQUOTAS; i++) { 151 if ((qtype != -1) && (i != qtype)) 152 continue; 153 154 dict = qctx->quota_dict[i]; 155 if (!dict) 156 continue; 157 158 retval = new_io(h, fs, i, fmt); 159 if (retval < 0) { 160 log_err("Cannot initialize io on quotafile", ""); 161 continue; 162 } 163 164 write_dquots(dict, h); 165 retval = end_io(h); 166 if (retval < 0) { 167 log_err("Cannot finish IO on new quotafile: %s", 168 strerror(errno)); 169 if (h->qh_qf.e2_file) 170 ext2fs_file_close(h->qh_qf.e2_file); 171 truncate_quota_inode(fs, h->qh_qf.ino); 172 continue; 173 } 174 175 /* Set quota inode numbers in superblock. */ 176 set_sb_quota_inum(fs, h->qh_qf.ino, i); 177 ext2fs_mark_super_dirty(fs); 178 ext2fs_mark_bb_dirty(fs); 179 fs->flags &= ~EXT2_FLAG_SUPER_ONLY; 180 } 181 182 ext2fs_write_bitmaps(fs); 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 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 /* 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