1 /*
2 * mkquota.c --- create quota files for a filesystem
3 *
4 * Aditya Kali <adityakali@google.com>
5 * Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
6 */
7 #include "config.h"
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <fcntl.h>
16
17 #include "quotaio.h"
18 #include "quotaio_v2.h"
19 #include "quotaio_tree.h"
20 #include "common.h"
21 #include "dict.h"
22
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
28 #if DEBUG_QUOTA
print_dquot(const char * desc,struct dquot * dq)29 static void print_dquot(const char *desc, struct dquot *dq)
30 {
31 if (desc)
32 fprintf(stderr, "%s: ", desc);
33 fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n",
34 dq->dq_id, (long long) dq->dq_dqb.dqb_curspace,
35 (long long) dq->dq_dqb.dqb_bsoftlimit,
36 (long long) dq->dq_dqb.dqb_bhardlimit,
37 (long long) dq->dq_dqb.dqb_curinodes,
38 (long long) dq->dq_dqb.dqb_isoftlimit,
39 (long long) dq->dq_dqb.dqb_ihardlimit);
40 }
41 #else
42 #define print_dquot(...)
43 #endif
44
write_dquots(dict_t * dict,struct quota_handle * qh)45 static void write_dquots(dict_t *dict, struct quota_handle *qh)
46 {
47 dnode_t *n;
48 struct dquot *dq;
49
50 for (n = dict_first(dict); n; n = dict_next(dict, n)) {
51 dq = dnode_get(n);
52 if (dq) {
53 print_dquot("write", dq);
54 dq->dq_h = qh;
55 update_grace_times(dq);
56 qh->qh_ops->commit_dquot(dq);
57 }
58 }
59 }
60
quota_write_inode(struct f2fs_sb_info * sbi,enum quota_type qtype)61 errcode_t quota_write_inode(struct f2fs_sb_info *sbi, enum quota_type qtype)
62 {
63 struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
64 struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
65 quota_ctx_t qctx = fsck->qctx;
66 struct quota_handle *h = NULL;
67 int retval = 0;
68 dict_t *dict;
69
70 if ((!qctx) || (!sb->qf_ino[qtype]))
71 return 0;
72
73 retval = quota_get_mem(sizeof(struct quota_handle), &h);
74 if (retval) {
75 log_debug("Unable to allocate quota handle");
76 goto out;
77 }
78
79 dict = qctx->quota_dict[qtype];
80 if (dict) {
81 retval = quota_file_create(sbi, h, qtype);
82 if (retval) {
83 log_debug("Cannot initialize io on quotafile");
84 } else {
85 write_dquots(dict, h);
86 quota_file_close(sbi, h, 1);
87 }
88 }
89 out:
90 if (h)
91 quota_free_mem(&h);
92 return retval;
93 }
94
95 /******************************************************************/
96 /* Helper functions for computing quota in memory. */
97 /******************************************************************/
98
dict_uint_cmp(const void * a,const void * b)99 static int dict_uint_cmp(const void *a, const void *b)
100 {
101 unsigned int c, d;
102
103 c = VOIDPTR_TO_UINT(a);
104 d = VOIDPTR_TO_UINT(b);
105
106 if (c == d)
107 return 0;
108 else if (c > d)
109 return 1;
110 else
111 return -1;
112 }
113
get_qid(struct f2fs_inode * inode,enum quota_type qtype)114 static inline qid_t get_qid(struct f2fs_inode *inode, enum quota_type qtype)
115 {
116 switch (qtype) {
117 case USRQUOTA:
118 return inode->i_uid;
119 case GRPQUOTA:
120 return inode->i_gid;
121 case PRJQUOTA:
122 return inode->i_projid;
123 default:
124 return 0;
125 }
126
127 return 0;
128 }
129
quota_dnode_free(dnode_t * node,void * UNUSED (context))130 static void quota_dnode_free(dnode_t *node, void *UNUSED(context))
131 {
132 void *ptr = node ? dnode_get(node) : 0;
133
134 quota_free_mem(&ptr);
135 free(node);
136 }
137
138 /*
139 * Set up the quota tracking data structures.
140 */
quota_init_context(struct f2fs_sb_info * sbi)141 errcode_t quota_init_context(struct f2fs_sb_info *sbi)
142 {
143 struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
144 struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
145 errcode_t err;
146 dict_t *dict;
147 quota_ctx_t ctx;
148 enum quota_type qtype;
149
150 err = quota_get_mem(sizeof(struct quota_ctx), &ctx);
151 if (err) {
152 log_debug("Failed to allocate quota context");
153 return err;
154 }
155
156 memset(ctx, 0, sizeof(struct quota_ctx));
157 dict_init(&ctx->linked_inode_dict, DICTCOUNT_T_MAX, dict_uint_cmp);
158 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
159 ctx->quota_file[qtype] = NULL;
160 if (!sb->qf_ino[qtype])
161 continue;
162 err = quota_get_mem(sizeof(dict_t), &dict);
163 if (err) {
164 log_debug("Failed to allocate dictionary");
165 quota_release_context(&ctx);
166 return err;
167 }
168 ctx->quota_dict[qtype] = dict;
169 dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
170 dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
171 }
172 ctx->sbi = sbi;
173 fsck->qctx = ctx;
174 return 0;
175 }
176
quota_release_context(quota_ctx_t * qctx)177 void quota_release_context(quota_ctx_t *qctx)
178 {
179 dict_t *dict;
180 enum quota_type qtype;
181 quota_ctx_t ctx;
182
183 if (!qctx)
184 return;
185
186 ctx = *qctx;
187 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
188 dict = ctx->quota_dict[qtype];
189 ctx->quota_dict[qtype] = 0;
190 if (dict) {
191 dict_free_nodes(dict);
192 free(dict);
193 }
194 }
195 dict_free_nodes(&ctx->linked_inode_dict);
196 *qctx = NULL;
197 free(ctx);
198 }
199
get_dq(dict_t * dict,__u32 key)200 static struct dquot *get_dq(dict_t *dict, __u32 key)
201 {
202 struct dquot *dq;
203 dnode_t *n;
204
205 n = dict_lookup(dict, UINT_TO_VOIDPTR(key));
206 if (n)
207 dq = dnode_get(n);
208 else {
209 if (quota_get_mem(sizeof(struct dquot), &dq)) {
210 log_err("Unable to allocate dquot");
211 return NULL;
212 }
213 memset(dq, 0, sizeof(struct dquot));
214 dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq);
215 dq->dq_id = key;
216 }
217 return dq;
218 }
219
220 /*
221 * Called to update the blocks used by a particular inode
222 */
quota_data_add(quota_ctx_t qctx,struct f2fs_inode * inode,qsize_t space)223 void quota_data_add(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space)
224 {
225 struct dquot *dq;
226 dict_t *dict;
227 enum quota_type qtype;
228
229 if (!qctx)
230 return;
231
232 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
233 dict = qctx->quota_dict[qtype];
234 if (dict) {
235 dq = get_dq(dict, get_qid(inode, qtype));
236 if (dq)
237 dq->dq_dqb.dqb_curspace += space;
238 }
239 }
240 }
241
242 /*
243 * Called to remove some blocks used by a particular inode
244 */
quota_data_sub(quota_ctx_t qctx,struct f2fs_inode * inode,qsize_t space)245 void quota_data_sub(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space)
246 {
247 struct dquot *dq;
248 dict_t *dict;
249 enum quota_type qtype;
250
251 if (!qctx)
252 return;
253
254 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
255 dict = qctx->quota_dict[qtype];
256 if (dict) {
257 dq = get_dq(dict, get_qid(inode, qtype));
258 dq->dq_dqb.dqb_curspace -= space;
259 }
260 }
261 }
262
263 /*
264 * Called to count the files used by an inode's user/group
265 */
quota_data_inodes(quota_ctx_t qctx,struct f2fs_inode * inode,int adjust)266 void quota_data_inodes(quota_ctx_t qctx, struct f2fs_inode *inode, int adjust)
267 {
268 struct dquot *dq;
269 dict_t *dict; enum quota_type qtype;
270
271 if (!qctx)
272 return;
273
274 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
275 dict = qctx->quota_dict[qtype];
276 if (dict) {
277 dq = get_dq(dict, get_qid(inode, qtype));
278 dq->dq_dqb.dqb_curinodes += adjust;
279 }
280 }
281 }
282
283 /*
284 * Called from fsck to count quota.
285 */
quota_add_inode_usage(quota_ctx_t qctx,f2fs_ino_t ino,struct f2fs_inode * inode)286 void quota_add_inode_usage(quota_ctx_t qctx, f2fs_ino_t ino,
287 struct f2fs_inode* inode)
288 {
289 if (qctx) {
290 /* Handle hard linked inodes */
291 if (inode->i_links > 1) {
292 if (dict_lookup(&qctx->linked_inode_dict,
293 UINT_TO_VOIDPTR(ino))) {
294 return;
295 }
296 dict_alloc_insert(&qctx->linked_inode_dict,
297 UINT_TO_VOIDPTR(ino), NULL);
298 }
299
300 qsize_t space = (inode->i_blocks - 1) * BLOCK_SZ;
301 quota_data_add(qctx, inode, space);
302 quota_data_inodes(qctx, inode, +1);
303 }
304 }
305
306 struct scan_dquots_data {
307 dict_t *quota_dict;
308 int update_limits; /* update limits from disk */
309 int update_usage;
310 int usage_is_inconsistent;
311 };
312
scan_dquots_callback(struct dquot * dquot,void * cb_data)313 static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
314 {
315 struct scan_dquots_data *scan_data = cb_data;
316 dict_t *quota_dict = scan_data->quota_dict;
317 struct dquot *dq;
318
319 dq = get_dq(quota_dict, dquot->dq_id);
320 dq->dq_id = dquot->dq_id;
321 dq->dq_flags |= DQF_SEEN;
322
323 print_dquot("mem", dq);
324 print_dquot("dsk", dquot);
325 /* Check if there is inconsistency */
326 if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
327 dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) {
328 scan_data->usage_is_inconsistent = 1;
329 log_debug("[QUOTA WARNING] Usage inconsistent for ID %u:"
330 "actual (%lld, %lld) != expected (%lld, %lld)\n",
331 dq->dq_id, (long long) dq->dq_dqb.dqb_curspace,
332 (long long) dq->dq_dqb.dqb_curinodes,
333 (long long) dquot->dq_dqb.dqb_curspace,
334 (long long) dquot->dq_dqb.dqb_curinodes);
335 }
336
337 if (scan_data->update_limits) {
338 dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
339 dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
340 dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
341 dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
342 }
343
344 if (scan_data->update_usage) {
345 dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
346 dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
347 }
348
349 return 0;
350 }
351
352 /*
353 * Compares the measured quota in qctx->quota_dict with that in the quota inode
354 * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
355 * set to 1 if the supplied and on-disk quota usage values are not identical.
356 */
quota_compare_and_update(struct f2fs_sb_info * sbi,enum quota_type qtype,int * usage_inconsistent,int preserve_limits)357 errcode_t quota_compare_and_update(struct f2fs_sb_info *sbi,
358 enum quota_type qtype, int *usage_inconsistent,
359 int preserve_limits)
360 {
361 struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
362 quota_ctx_t qctx = fsck->qctx;
363 struct quota_handle qh;
364 struct scan_dquots_data scan_data;
365 struct dquot *dq;
366 dnode_t *n;
367 dict_t *dict = qctx->quota_dict[qtype];
368 errcode_t err = 0;
369
370 if (!dict)
371 goto out;
372
373 err = quota_file_open(sbi, &qh, qtype, 0);
374 if (err) {
375 log_debug("Open quota file failed");
376 goto out;
377 }
378
379 scan_data.quota_dict = qctx->quota_dict[qtype];
380 scan_data.update_limits = preserve_limits;
381 scan_data.update_usage = 0;
382 scan_data.usage_is_inconsistent = 0;
383 err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
384 if (err) {
385 log_debug("Error scanning dquots");
386 goto out;
387 }
388
389 for (n = dict_first(dict); n; n = dict_next(dict, n)) {
390 dq = dnode_get(n);
391 if (!dq)
392 continue;
393 if ((dq->dq_flags & DQF_SEEN) == 0) {
394 log_debug("[QUOTA WARNING] "
395 "Missing quota entry ID %d\n", dq->dq_id);
396 scan_data.usage_is_inconsistent = 1;
397 }
398 }
399 *usage_inconsistent = scan_data.usage_is_inconsistent;
400
401 out:
402 return err;
403 }
404
405