2 * mkquota.c --- create quota files for a filesystem
4 * Aditya Kali <adityakali@google.com>
14 #include "ext2fs/ext2_fs.h"
15 #include "ext2fs/ext2fs.h"
19 #include "quotaio_v2.h"
20 #include "quotaio_tree.h"
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))
29 static void print_inode(struct ext2_inode
*inode
)
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
);
49 static void print_dquot(const char *desc
, struct dquot
*dq
)
52 fprintf(stderr
, "%s: ", desc
);
53 fprintf(stderr
, "%u %lld:%lld:%lld %lld:%lld:%lld\n",
54 dq
->dq_id
, (long long) dq
->dq_dqb
.dqb_curspace
,
55 (long long) dq
->dq_dqb
.dqb_bsoftlimit
,
56 (long long) dq
->dq_dqb
.dqb_bhardlimit
,
57 (long long) dq
->dq_dqb
.dqb_curinodes
,
58 (long long) dq
->dq_dqb
.dqb_isoftlimit
,
59 (long long) dq
->dq_dqb
.dqb_ihardlimit
);
62 static void print_dquot(const char *desc
EXT2FS_ATTR((unused
)),
63 struct dquot
*dq
EXT2FS_ATTR((unused
)))
69 * Returns 0 if not able to find the quota file, otherwise returns its
72 int quota_file_exists(ext2_filsys fs
, enum quota_type qtype
)
78 if (qtype
>= MAXQUOTAS
)
81 quota_get_qf_name(qtype
, QFMT_VFS_V1
, qf_name
);
83 ret
= ext2fs_lookup(fs
, EXT2_ROOT_INO
, qf_name
, strlen(qf_name
), 0,
92 * Set the value for reserved quota inode number field in superblock.
94 void quota_set_sb_inum(ext2_filsys fs
, ext2_ino_t ino
, enum quota_type qtype
)
98 inump
= quota_sb_inump(fs
->super
, qtype
);
100 log_debug("setting quota ino in superblock: ino=%u, type=%d", ino
,
103 ext2fs_mark_super_dirty(fs
);
106 errcode_t
quota_remove_inode(ext2_filsys fs
, enum quota_type qtype
)
111 retval
= ext2fs_read_bitmaps(fs
);
113 log_debug("Couldn't read bitmaps: %s", error_message(retval
));
117 qf_ino
= *quota_sb_inump(fs
->super
, qtype
);
120 retval
= quota_inode_truncate(fs
, qf_ino
);
123 if (qf_ino
>= EXT2_FIRST_INODE(fs
->super
)) {
124 struct ext2_inode inode
;
126 retval
= ext2fs_read_inode(fs
, qf_ino
, &inode
);
128 memset(&inode
, 0, sizeof(struct ext2_inode
));
129 ext2fs_write_inode(fs
, qf_ino
, &inode
);
131 ext2fs_inode_alloc_stats2(fs
, qf_ino
, -1, 0);
132 ext2fs_mark_ib_dirty(fs
);
135 quota_set_sb_inum(fs
, 0, qtype
);
137 ext2fs_mark_super_dirty(fs
);
138 fs
->flags
&= ~EXT2_FLAG_SUPER_ONLY
;
139 retval
= ext2fs_write_bitmaps(fs
);
141 log_debug("Couldn't write bitmaps: %s", error_message(retval
));
147 static void write_dquots(dict_t
*dict
, struct quota_handle
*qh
)
152 for (n
= dict_first(dict
); n
; n
= dict_next(dict
, n
)) {
155 print_dquot("write", dq
);
157 update_grace_times(dq
);
158 qh
->qh_ops
->commit_dquot(dq
);
163 errcode_t
quota_write_inode(quota_ctx_t qctx
, unsigned int qtype_bits
)
166 enum quota_type qtype
;
169 struct quota_handle
*h
= NULL
;
170 int fmt
= QFMT_VFS_V1
;
176 retval
= ext2fs_get_mem(sizeof(struct quota_handle
), &h
);
178 log_debug("Unable to allocate quota handle: %s",
179 error_message(retval
));
183 retval
= ext2fs_read_bitmaps(fs
);
185 log_debug("Couldn't read bitmaps: %s", error_message(retval
));
189 for (qtype
= 0; qtype
< MAXQUOTAS
; qtype
++) {
190 if (((1 << qtype
) & qtype_bits
) == 0)
193 dict
= qctx
->quota_dict
[qtype
];
197 retval
= quota_file_create(h
, fs
, qtype
, fmt
);
199 log_debug("Cannot initialize io on quotafile: %s",
200 error_message(retval
));
204 write_dquots(dict
, h
);
205 retval
= quota_file_close(qctx
, h
);
207 log_debug("Cannot finish IO on new quotafile: %s",
209 if (h
->qh_qf
.e2_file
)
210 ext2fs_file_close(h
->qh_qf
.e2_file
);
211 (void) quota_inode_truncate(fs
, h
->qh_qf
.ino
);
215 /* Set quota inode numbers in superblock. */
216 quota_set_sb_inum(fs
, h
->qh_qf
.ino
, qtype
);
217 ext2fs_mark_super_dirty(fs
);
218 ext2fs_mark_bb_dirty(fs
);
219 fs
->flags
&= ~EXT2_FLAG_SUPER_ONLY
;
222 retval
= ext2fs_write_bitmaps(fs
);
224 log_debug("Couldn't write bitmaps: %s", error_message(retval
));
233 /******************************************************************/
234 /* Helper functions for computing quota in memory. */
235 /******************************************************************/
237 static int dict_uint_cmp(const void *a
, const void *b
)
241 c
= VOIDPTR_TO_UINT(a
);
242 d
= VOIDPTR_TO_UINT(b
);
252 static inline int project_quota_valid(quota_ctx_t qctx
)
254 return (EXT2_INODE_SIZE(qctx
->fs
->super
) > EXT2_GOOD_OLD_INODE_SIZE
);
257 static inline qid_t
get_qid(struct ext2_inode_large
*inode
, enum quota_type qtype
)
259 unsigned int inode_size
;
263 return inode_uid(*inode
);
265 return inode_gid(*inode
);
267 inode_size
= EXT2_GOOD_OLD_INODE_SIZE
+
268 inode
->i_extra_isize
;
269 if (inode_includes(inode_size
, i_projid
))
270 return inode_projid(*inode
);
279 static void quota_dnode_free(dnode_t
*node
,
280 void *context
EXT2FS_ATTR((unused
)))
282 void *ptr
= node
? dnode_get(node
) : 0;
284 ext2fs_free_mem(&ptr
);
289 * Set up the quota tracking data structures.
291 errcode_t
quota_init_context(quota_ctx_t
*qctx
, ext2_filsys fs
,
292 unsigned int qtype_bits
)
297 enum quota_type qtype
;
299 err
= ext2fs_get_mem(sizeof(struct quota_ctx
), &ctx
);
301 log_debug("Failed to allocate quota context");
305 memset(ctx
, 0, sizeof(struct quota_ctx
));
306 for (qtype
= 0; qtype
< MAXQUOTAS
; qtype
++) {
307 ctx
->quota_file
[qtype
] = NULL
;
309 if (((1 << qtype
) & qtype_bits
) == 0)
312 if (*quota_sb_inump(fs
->super
, qtype
) == 0)
315 err
= ext2fs_get_mem(sizeof(dict_t
), &dict
);
317 log_debug("Failed to allocate dictionary");
318 quota_release_context(&ctx
);
321 ctx
->quota_dict
[qtype
] = dict
;
322 dict_init(dict
, DICTCOUNT_T_MAX
, dict_uint_cmp
);
323 dict_set_allocator(dict
, NULL
, quota_dnode_free
, NULL
);
331 void quota_release_context(quota_ctx_t
*qctx
)
335 enum quota_type qtype
;
342 for (qtype
= 0; qtype
< MAXQUOTAS
; qtype
++) {
343 dict
= ctx
->quota_dict
[qtype
];
344 ctx
->quota_dict
[qtype
] = 0;
346 dict_free_nodes(dict
);
349 if (ctx
->quota_file
[qtype
]) {
350 err
= quota_file_close(ctx
, ctx
->quota_file
[qtype
]);
352 log_err("Cannot close quotafile: %s",
354 ext2fs_free_mem(&ctx
->quota_file
[qtype
]);
362 static struct dquot
*get_dq(dict_t
*dict
, __u32 key
)
367 n
= dict_lookup(dict
, UINT_TO_VOIDPTR(key
));
371 if (ext2fs_get_mem(sizeof(struct dquot
), &dq
)) {
372 log_err("Unable to allocate dquot");
375 memset(dq
, 0, sizeof(struct dquot
));
376 dict_alloc_insert(dict
, UINT_TO_VOIDPTR(key
), dq
);
384 * Called to update the blocks used by a particular inode
386 void quota_data_add(quota_ctx_t qctx
, struct ext2_inode_large
*inode
,
387 ext2_ino_t ino
EXT2FS_ATTR((unused
)),
392 enum quota_type qtype
;
397 log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino
,
399 inode_gid(*inode
), space
);
400 for (qtype
= 0; qtype
< MAXQUOTAS
; qtype
++) {
401 if (qtype
== PRJQUOTA
&& !project_quota_valid(qctx
))
403 dict
= qctx
->quota_dict
[qtype
];
405 dq
= get_dq(dict
, get_qid(inode
, qtype
));
407 dq
->dq_dqb
.dqb_curspace
+= space
;
413 * Called to remove some blocks used by a particular inode
415 void quota_data_sub(quota_ctx_t qctx
, struct ext2_inode_large
*inode
,
416 ext2_ino_t ino
EXT2FS_ATTR((unused
)),
421 enum quota_type qtype
;
426 log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino
,
428 inode_gid(*inode
), space
);
429 for (qtype
= 0; qtype
< MAXQUOTAS
; qtype
++) {
430 if (qtype
== PRJQUOTA
&& !project_quota_valid(qctx
))
432 dict
= qctx
->quota_dict
[qtype
];
434 dq
= get_dq(dict
, get_qid(inode
, qtype
));
435 dq
->dq_dqb
.dqb_curspace
-= space
;
441 * Called to count the files used by an inode's user/group
443 void quota_data_inodes(quota_ctx_t qctx
, struct ext2_inode_large
*inode
,
444 ext2_ino_t ino
EXT2FS_ATTR((unused
)), int adjust
)
448 enum quota_type qtype
;
453 log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino
,
455 inode_gid(*inode
), adjust
);
456 for (qtype
= 0; qtype
< MAXQUOTAS
; qtype
++) {
457 if (qtype
== PRJQUOTA
&& !project_quota_valid(qctx
))
459 dict
= qctx
->quota_dict
[qtype
];
461 dq
= get_dq(dict
, get_qid(inode
, qtype
));
462 dq
->dq_dqb
.dqb_curinodes
+= adjust
;
467 errcode_t
quota_compute_usage(quota_ctx_t qctx
)
472 struct ext2_inode_large
*inode
;
475 ext2_inode_scan scan
;
481 ret
= ext2fs_open_inode_scan(fs
, 0, &scan
);
483 log_err("while opening inode scan. ret=%ld", ret
);
486 inode_size
= fs
->super
->s_inode_size
;
487 inode
= malloc(inode_size
);
489 ext2fs_close_inode_scan(scan
);
493 ret
= ext2fs_get_next_inode_full(scan
, &ino
,
494 EXT2_INODE(inode
), inode_size
);
496 log_err("while getting next inode. ret=%ld", ret
);
497 ext2fs_close_inode_scan(scan
);
503 if (inode
->i_links_count
&&
504 (ino
== EXT2_ROOT_INO
||
505 ino
>= EXT2_FIRST_INODE(fs
->super
))) {
506 space
= ext2fs_inode_i_blocks(fs
,
507 EXT2_INODE(inode
)) << 9;
508 quota_data_add(qctx
, inode
, ino
, space
);
509 quota_data_inodes(qctx
, inode
, ino
, +1);
513 ext2fs_close_inode_scan(scan
);
518 struct scan_dquots_data
{
520 int update_limits
; /* update limits from disk */
522 int check_consistency
;
523 int usage_is_inconsistent
;
526 static int scan_dquots_callback(struct dquot
*dquot
, void *cb_data
)
528 struct scan_dquots_data
*scan_data
= cb_data
;
529 dict_t
*quota_dict
= scan_data
->quota_dict
;
532 dq
= get_dq(quota_dict
, dquot
->dq_id
);
533 dq
->dq_id
= dquot
->dq_id
;
534 dq
->dq_flags
|= DQF_SEEN
;
536 print_dquot("mem", dq
);
537 print_dquot("dsk", dquot
);
539 /* Check if there is inconsistency */
540 if (scan_data
->check_consistency
&&
541 (dq
->dq_dqb
.dqb_curspace
!= dquot
->dq_dqb
.dqb_curspace
||
542 dq
->dq_dqb
.dqb_curinodes
!= dquot
->dq_dqb
.dqb_curinodes
)) {
543 scan_data
->usage_is_inconsistent
= 1;
544 fprintf(stderr
, "[QUOTA WARNING] Usage inconsistent for ID %u:"
545 "actual (%lld, %lld) != expected (%lld, %lld)\n",
546 dq
->dq_id
, (long long) dq
->dq_dqb
.dqb_curspace
,
547 (long long) dq
->dq_dqb
.dqb_curinodes
,
548 (long long) dquot
->dq_dqb
.dqb_curspace
,
549 (long long) dquot
->dq_dqb
.dqb_curinodes
);
552 if (scan_data
->update_limits
) {
553 dq
->dq_dqb
.dqb_ihardlimit
= dquot
->dq_dqb
.dqb_ihardlimit
;
554 dq
->dq_dqb
.dqb_isoftlimit
= dquot
->dq_dqb
.dqb_isoftlimit
;
555 dq
->dq_dqb
.dqb_bhardlimit
= dquot
->dq_dqb
.dqb_bhardlimit
;
556 dq
->dq_dqb
.dqb_bsoftlimit
= dquot
->dq_dqb
.dqb_bsoftlimit
;
559 if (scan_data
->update_usage
) {
560 dq
->dq_dqb
.dqb_curspace
= dquot
->dq_dqb
.dqb_curspace
;
561 dq
->dq_dqb
.dqb_curinodes
= dquot
->dq_dqb
.dqb_curinodes
;
568 * Read all dquots from quota file into memory
570 static errcode_t
quota_read_all_dquots(struct quota_handle
*qh
,
572 int update_limits
EXT2FS_ATTR((unused
)))
574 struct scan_dquots_data scan_data
;
576 scan_data
.quota_dict
= qctx
->quota_dict
[qh
->qh_type
];
577 scan_data
.check_consistency
= 0;
578 scan_data
.update_limits
= 0;
579 scan_data
.update_usage
= 1;
581 return qh
->qh_ops
->scan_dquots(qh
, scan_dquots_callback
, &scan_data
);
585 * Write all memory dquots into quota file
587 #if 0 /* currently unused, but may be useful in the future? */
588 static errcode_t
quota_write_all_dquots(struct quota_handle
*qh
,
593 err
= ext2fs_read_bitmaps(qctx
->fs
);
596 write_dquots(qctx
->quota_dict
[qh
->qh_type
], qh
);
597 ext2fs_mark_bb_dirty(qctx
->fs
);
598 qctx
->fs
->flags
&= ~EXT2_FLAG_SUPER_ONLY
;
599 ext2fs_write_bitmaps(qctx
->fs
);
605 * Updates the in-memory quota limits from the given quota inode.
607 errcode_t
quota_update_limits(quota_ctx_t qctx
, ext2_ino_t qf_ino
,
608 enum quota_type qtype
)
610 struct quota_handle
*qh
;
616 err
= ext2fs_get_mem(sizeof(struct quota_handle
), &qh
);
618 log_debug("Unable to allocate quota handle");
622 err
= quota_file_open(qctx
, qh
, qf_ino
, qtype
, -1, 0);
624 log_debug("Open quota file failed");
628 quota_read_all_dquots(qh
, qctx
, 1);
630 err
= quota_file_close(qctx
, qh
);
632 log_debug("Cannot finish IO on new quotafile: %s",
634 if (qh
->qh_qf
.e2_file
)
635 ext2fs_file_close(qh
->qh_qf
.e2_file
);
638 ext2fs_free_mem(&qh
);
643 * Compares the measured quota in qctx->quota_dict with that in the quota inode
644 * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
645 * set to 1 if the supplied and on-disk quota usage values are not identical.
647 errcode_t
quota_compare_and_update(quota_ctx_t qctx
, enum quota_type qtype
,
648 int *usage_inconsistent
)
650 struct quota_handle qh
;
651 struct scan_dquots_data scan_data
;
654 dict_t
*dict
= qctx
->quota_dict
[qtype
];
660 err
= quota_file_open(qctx
, &qh
, 0, qtype
, -1, 0);
662 log_debug("Open quota file failed");
666 scan_data
.quota_dict
= qctx
->quota_dict
[qtype
];
667 scan_data
.update_limits
= 1;
668 scan_data
.update_usage
= 0;
669 scan_data
.check_consistency
= 1;
670 scan_data
.usage_is_inconsistent
= 0;
671 err
= qh
.qh_ops
->scan_dquots(&qh
, scan_dquots_callback
, &scan_data
);
673 log_debug("Error scanning dquots");
674 *usage_inconsistent
= 1;
678 for (n
= dict_first(dict
); n
; n
= dict_next(dict
, n
)) {
682 if ((dq
->dq_flags
& DQF_SEEN
) == 0) {
683 fprintf(stderr
, "[QUOTA WARNING] "
684 "Missing quota entry ID %d\n", dq
->dq_id
);
685 scan_data
.usage_is_inconsistent
= 1;
688 *usage_inconsistent
= scan_data
.usage_is_inconsistent
;
691 err
= quota_file_close(qctx
, &qh
);
693 log_debug("Cannot close quotafile: %s", error_message(errno
));
694 if (qh
.qh_qf
.e2_file
)
695 ext2fs_file_close(qh
.qh_qf
.e2_file
);
701 int parse_quota_opts(const char *opts
, int (*func
)(char *))
703 char *buf
, *token
, *next
, *p
;
708 buf
= malloc(len
+ 1);
711 "Couldn't allocate memory to parse quota options!\n");
715 for (token
= buf
; token
&& *token
; token
= next
) {
716 p
= strchr(token
, ',');