• 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 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