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