• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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