]>
Commit | Line | Data |
---|---|---|
f239fefc AK |
1 | /* |
2 | * mkquota.c --- create quota files for a filesystem | |
3 | * | |
4 | * Aditya Kali <adityakali@google.com> | |
5 | */ | |
d1154eb4 | 6 | #include "config.h" |
f239fefc AK |
7 | #include <sys/types.h> |
8 | #include <sys/stat.h> | |
9 | #include <unistd.h> | |
10 | #include <errno.h> | |
11 | #include <string.h> | |
12 | #include <fcntl.h> | |
13 | ||
14 | #include "ext2fs/ext2_fs.h" | |
15 | #include "ext2fs/ext2fs.h" | |
16 | #include "e2p/e2p.h" | |
17 | ||
f239fefc AK |
18 | #include "quotaio.h" |
19 | #include "quotaio_v2.h" | |
20 | #include "quotaio_tree.h" | |
f239fefc | 21 | #include "common.h" |
3dca12fb | 22 | #include "dict.h" |
f239fefc AK |
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 | ||
00eb0eee | 28 | #if DEBUG_QUOTA |
f239fefc AK |
29 | static void print_inode(struct ext2_inode *inode) |
30 | { | |
31 | if (!inode) | |
32 | return; | |
33 | ||
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); | |
45 | ||
46 | return; | |
47 | } | |
a0811c1a TT |
48 | |
49 | static void print_dquot(const char *desc, struct dquot *dq) | |
50 | { | |
51 | if (desc) | |
52 | fprintf(stderr, "%s: ", desc); | |
53 | fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n", | |
54 | dq->dq_id, dq->dq_dqb.dqb_curspace, | |
55 | dq->dq_dqb.dqb_bsoftlimit, dq->dq_dqb.dqb_bhardlimit, | |
56 | dq->dq_dqb.dqb_curinodes, | |
57 | dq->dq_dqb.dqb_isoftlimit, dq->dq_dqb.dqb_ihardlimit); | |
58 | } | |
59 | #else | |
25f291c9 TT |
60 | static void print_dquot(const char *desc EXT2FS_ATTR((unused)), |
61 | struct dquot *dq EXT2FS_ATTR((unused))) | |
a0811c1a TT |
62 | { |
63 | } | |
00eb0eee | 64 | #endif |
f239fefc | 65 | |
f239fefc AK |
66 | /* |
67 | * Returns 0 if not able to find the quota file, otherwise returns its | |
68 | * inode number. | |
69 | */ | |
25f291c9 | 70 | int quota_file_exists(ext2_filsys fs, int qtype) |
f239fefc AK |
71 | { |
72 | char qf_name[256]; | |
73 | errcode_t ret; | |
74 | ext2_ino_t ino; | |
75 | ||
36e4e21f | 76 | if (qtype >= MAXQUOTAS) |
f239fefc AK |
77 | return -EINVAL; |
78 | ||
2ae58b6d | 79 | quota_get_qf_name(qtype, QFMT_VFS_V1, qf_name); |
f239fefc AK |
80 | |
81 | ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0, | |
82 | &ino); | |
83 | if (ret) | |
84 | return 0; | |
85 | ||
86 | return ino; | |
87 | } | |
88 | ||
89 | /* | |
90 | * Set the value for reserved quota inode number field in superblock. | |
91 | */ | |
a86d55da | 92 | void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype) |
f239fefc AK |
93 | { |
94 | ext2_ino_t *inump; | |
95 | ||
96 | inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum : | |
97 | &fs->super->s_grp_quota_inum; | |
98 | ||
99 | log_debug("setting quota ino in superblock: ino=%u, type=%d", ino, | |
100 | qtype); | |
101 | *inump = ino; | |
102 | ext2fs_mark_super_dirty(fs); | |
103 | } | |
104 | ||
a86d55da | 105 | errcode_t quota_remove_inode(ext2_filsys fs, int qtype) |
f239fefc AK |
106 | { |
107 | ext2_ino_t qf_ino; | |
fa8b1c02 | 108 | errcode_t retval; |
f239fefc | 109 | |
fa8b1c02 TT |
110 | retval = ext2fs_read_bitmaps(fs); |
111 | if (retval) { | |
112 | log_err("Couldn't read bitmaps: %s", error_message(retval)); | |
113 | return retval; | |
114 | } | |
f239fefc AK |
115 | qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum : |
116 | fs->super->s_grp_quota_inum; | |
a86d55da | 117 | quota_set_sb_inum(fs, 0, qtype); |
f239fefc AK |
118 | /* Truncate the inode only if its a reserved one. */ |
119 | if (qf_ino < EXT2_FIRST_INODE(fs->super)) | |
a86d55da | 120 | quota_inode_truncate(fs, qf_ino); |
f239fefc AK |
121 | |
122 | ext2fs_mark_super_dirty(fs); | |
43075b42 | 123 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; |
fa8b1c02 TT |
124 | retval = ext2fs_write_bitmaps(fs); |
125 | if (retval) { | |
126 | log_err("Couldn't write bitmaps: %s", error_message(retval)); | |
127 | return retval; | |
128 | } | |
f239fefc AK |
129 | return 0; |
130 | } | |
131 | ||
132 | static void write_dquots(dict_t *dict, struct quota_handle *qh) | |
133 | { | |
f239fefc AK |
134 | dnode_t *n; |
135 | struct dquot *dq; | |
f239fefc AK |
136 | |
137 | for (n = dict_first(dict); n; n = dict_next(dict, n)) { | |
138 | dq = dnode_get(n); | |
139 | if (dq) { | |
a0811c1a | 140 | print_dquot("write", dq); |
f239fefc AK |
141 | dq->dq_h = qh; |
142 | update_grace_times(dq); | |
b5ba6f5b | 143 | qh->qh_ops->commit_dquot(dq); |
f239fefc AK |
144 | } |
145 | } | |
146 | } | |
147 | ||
a86d55da | 148 | errcode_t quota_write_inode(quota_ctx_t qctx, int qtype) |
f239fefc | 149 | { |
edbfd75d | 150 | int retval = 0, i; |
f239fefc AK |
151 | dict_t *dict; |
152 | ext2_filsys fs; | |
00eb0eee | 153 | struct quota_handle *h = NULL; |
f239fefc AK |
154 | int fmt = QFMT_VFS_V1; |
155 | ||
156 | if (!qctx) | |
edbfd75d | 157 | return 0; |
f239fefc AK |
158 | |
159 | fs = qctx->fs; | |
a86d55da AK |
160 | retval = ext2fs_get_mem(sizeof(struct quota_handle), &h); |
161 | if (retval) { | |
3f10707d TT |
162 | log_err("Unable to allocate quota handle: %s", |
163 | error_message(retval)); | |
a86d55da AK |
164 | goto out; |
165 | } | |
166 | ||
3f10707d TT |
167 | retval = ext2fs_read_bitmaps(fs); |
168 | if (retval) { | |
169 | log_err("Couldn't read bitmaps: %s", error_message(retval)); | |
170 | goto out; | |
171 | } | |
f239fefc AK |
172 | |
173 | for (i = 0; i < MAXQUOTAS; i++) { | |
174 | if ((qtype != -1) && (i != qtype)) | |
175 | continue; | |
176 | ||
177 | dict = qctx->quota_dict[i]; | |
178 | if (!dict) | |
179 | continue; | |
180 | ||
a86d55da | 181 | retval = quota_file_create(h, fs, i, fmt); |
f239fefc | 182 | if (retval < 0) { |
0ce01729 | 183 | log_err("Cannot initialize io on quotafile"); |
f239fefc AK |
184 | continue; |
185 | } | |
186 | ||
187 | write_dquots(dict, h); | |
cbc1280d | 188 | retval = quota_file_close(qctx, h); |
f239fefc AK |
189 | if (retval < 0) { |
190 | log_err("Cannot finish IO on new quotafile: %s", | |
191 | strerror(errno)); | |
192 | if (h->qh_qf.e2_file) | |
193 | ext2fs_file_close(h->qh_qf.e2_file); | |
a86d55da | 194 | quota_inode_truncate(fs, h->qh_qf.ino); |
f239fefc AK |
195 | continue; |
196 | } | |
197 | ||
198 | /* Set quota inode numbers in superblock. */ | |
a86d55da | 199 | quota_set_sb_inum(fs, h->qh_qf.ino, i); |
f239fefc AK |
200 | ext2fs_mark_super_dirty(fs); |
201 | ext2fs_mark_bb_dirty(fs); | |
202 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
203 | } | |
204 | ||
3f10707d TT |
205 | retval = ext2fs_write_bitmaps(fs); |
206 | if (retval) { | |
207 | log_err("Couldn't write bitmaps: %s", error_message(retval)); | |
208 | goto out; | |
209 | } | |
a86d55da AK |
210 | out: |
211 | if (h) | |
212 | ext2fs_free_mem(&h); | |
f239fefc AK |
213 | return retval; |
214 | } | |
215 | ||
216 | /******************************************************************/ | |
217 | /* Helper functions for computing quota in memory. */ | |
218 | /******************************************************************/ | |
219 | ||
220 | static int dict_uint_cmp(const void *a, const void *b) | |
221 | { | |
222 | unsigned int c, d; | |
223 | ||
224 | c = VOIDPTR_TO_UINT(a); | |
225 | d = VOIDPTR_TO_UINT(b); | |
226 | ||
4cf0b0fe NY |
227 | if (c == d) |
228 | return 0; | |
229 | else if (c > d) | |
230 | return 1; | |
231 | else | |
232 | return -1; | |
f239fefc AK |
233 | } |
234 | ||
a86d55da | 235 | static inline qid_t get_qid(struct ext2_inode *inode, int qtype) |
f239fefc | 236 | { |
a86d55da | 237 | if (qtype == USRQUOTA) |
f239fefc | 238 | return inode_uid(*inode); |
a86d55da | 239 | return inode_gid(*inode); |
f239fefc AK |
240 | } |
241 | ||
242 | static void quota_dnode_free(dnode_t *node, | |
243 | void *context EXT2FS_ATTR((unused))) | |
244 | { | |
245 | void *ptr = node ? dnode_get(node) : 0; | |
246 | ||
a86d55da | 247 | ext2fs_free_mem(&ptr); |
f239fefc AK |
248 | free(node); |
249 | } | |
250 | ||
251 | /* | |
a86d55da | 252 | * Set up the quota tracking data structures. |
f239fefc | 253 | */ |
a86d55da | 254 | errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype) |
f239fefc | 255 | { |
cbc1280d | 256 | errcode_t err; |
f239fefc AK |
257 | dict_t *dict; |
258 | quota_ctx_t ctx; | |
cbc1280d | 259 | int i; |
f239fefc | 260 | |
a86d55da AK |
261 | err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx); |
262 | if (err) { | |
0ce01729 | 263 | log_err("Failed to allocate quota context"); |
a86d55da AK |
264 | return err; |
265 | } | |
266 | ||
f239fefc AK |
267 | memset(ctx, 0, sizeof(struct quota_ctx)); |
268 | for (i = 0; i < MAXQUOTAS; i++) { | |
cbc1280d | 269 | ctx->quota_file[i] = NULL; |
f239fefc AK |
270 | if ((qtype != -1) && (i != qtype)) |
271 | continue; | |
a86d55da AK |
272 | err = ext2fs_get_mem(sizeof(dict_t), &dict); |
273 | if (err) { | |
0ce01729 | 274 | log_err("Failed to allocate dictionary"); |
b2778bcb | 275 | quota_release_context(&ctx); |
a86d55da AK |
276 | return err; |
277 | } | |
f239fefc AK |
278 | ctx->quota_dict[i] = dict; |
279 | dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp); | |
280 | dict_set_allocator(dict, NULL, quota_dnode_free, NULL); | |
281 | } | |
282 | ||
283 | ctx->fs = fs; | |
284 | *qctx = ctx; | |
a86d55da | 285 | return 0; |
f239fefc AK |
286 | } |
287 | ||
a86d55da | 288 | void quota_release_context(quota_ctx_t *qctx) |
f239fefc | 289 | { |
cbc1280d | 290 | errcode_t err; |
f239fefc AK |
291 | dict_t *dict; |
292 | int i; | |
293 | quota_ctx_t ctx; | |
294 | ||
295 | if (!qctx) | |
296 | return; | |
297 | ||
298 | ctx = *qctx; | |
299 | for (i = 0; i < MAXQUOTAS; i++) { | |
300 | dict = ctx->quota_dict[i]; | |
301 | ctx->quota_dict[i] = 0; | |
302 | if (dict) { | |
303 | dict_free_nodes(dict); | |
304 | free(dict); | |
305 | } | |
cbc1280d TT |
306 | if (ctx->quota_file[i]) { |
307 | err = quota_file_close(ctx, ctx->quota_file[i]); | |
308 | if (err) { | |
309 | log_err("Cannot close quotafile: %s", | |
310 | strerror(errno)); | |
311 | ext2fs_free_mem(&ctx->quota_file[i]); | |
312 | } | |
313 | } | |
f239fefc AK |
314 | } |
315 | *qctx = NULL; | |
316 | free(ctx); | |
317 | } | |
318 | ||
319 | static struct dquot *get_dq(dict_t *dict, __u32 key) | |
320 | { | |
321 | struct dquot *dq; | |
322 | dnode_t *n; | |
323 | ||
324 | n = dict_lookup(dict, UINT_TO_VOIDPTR(key)); | |
325 | if (n) | |
326 | dq = dnode_get(n); | |
327 | else { | |
a86d55da | 328 | if (ext2fs_get_mem(sizeof(struct dquot), &dq)) { |
0ce01729 | 329 | log_err("Unable to allocate dquot"); |
a86d55da AK |
330 | return NULL; |
331 | } | |
f239fefc AK |
332 | memset(dq, 0, sizeof(struct dquot)); |
333 | dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq); | |
1527d99d | 334 | dq->dq_id = key; |
f239fefc AK |
335 | } |
336 | return dq; | |
337 | } | |
338 | ||
339 | ||
340 | /* | |
341 | * Called to update the blocks used by a particular inode | |
342 | */ | |
25f291c9 TT |
343 | void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, |
344 | ext2_ino_t ino EXT2FS_ATTR((unused)), | |
f239fefc AK |
345 | qsize_t space) |
346 | { | |
347 | struct dquot *dq; | |
348 | dict_t *dict; | |
349 | int i; | |
350 | ||
351 | if (!qctx) | |
352 | return; | |
353 | ||
354 | log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, | |
355 | inode_uid(*inode), | |
356 | inode_gid(*inode), space); | |
357 | for (i = 0; i < MAXQUOTAS; i++) { | |
358 | dict = qctx->quota_dict[i]; | |
359 | if (dict) { | |
360 | dq = get_dq(dict, get_qid(inode, i)); | |
a86d55da AK |
361 | if (dq) |
362 | dq->dq_dqb.dqb_curspace += space; | |
f239fefc AK |
363 | } |
364 | } | |
365 | } | |
366 | ||
367 | /* | |
368 | * Called to remove some blocks used by a particular inode | |
369 | */ | |
25f291c9 TT |
370 | void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, |
371 | ext2_ino_t ino EXT2FS_ATTR((unused)), | |
f239fefc AK |
372 | qsize_t space) |
373 | { | |
374 | struct dquot *dq; | |
375 | dict_t *dict; | |
376 | int i; | |
377 | ||
378 | if (!qctx) | |
379 | return; | |
380 | ||
381 | log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino, | |
382 | inode_uid(*inode), | |
383 | inode_gid(*inode), space); | |
384 | for (i = 0; i < MAXQUOTAS; i++) { | |
385 | dict = qctx->quota_dict[i]; | |
386 | if (dict) { | |
387 | dq = get_dq(dict, get_qid(inode, i)); | |
388 | dq->dq_dqb.dqb_curspace -= space; | |
389 | } | |
390 | } | |
391 | } | |
392 | ||
393 | /* | |
394 | * Called to count the files used by an inode's user/group | |
395 | */ | |
396 | void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode, | |
25f291c9 | 397 | ext2_ino_t ino EXT2FS_ATTR((unused)), int adjust) |
f239fefc AK |
398 | { |
399 | struct dquot *dq; | |
400 | dict_t *dict; | |
401 | int i; | |
402 | ||
403 | if (!qctx) | |
404 | return; | |
405 | ||
406 | log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino, | |
407 | inode_uid(*inode), | |
408 | inode_gid(*inode), adjust); | |
409 | for (i = 0; i < MAXQUOTAS; i++) { | |
410 | dict = qctx->quota_dict[i]; | |
411 | if (dict) { | |
412 | dq = get_dq(dict, get_qid(inode, i)); | |
413 | dq->dq_dqb.dqb_curinodes += adjust; | |
414 | } | |
415 | } | |
416 | } | |
417 | ||
a86d55da | 418 | errcode_t quota_compute_usage(quota_ctx_t qctx) |
f239fefc AK |
419 | { |
420 | ext2_filsys fs; | |
f239fefc AK |
421 | ext2_ino_t ino; |
422 | errcode_t ret; | |
423 | struct ext2_inode inode; | |
424 | qsize_t space; | |
425 | ext2_inode_scan scan; | |
426 | ||
427 | if (!qctx) | |
edbfd75d | 428 | return 0; |
f239fefc AK |
429 | |
430 | fs = qctx->fs; | |
431 | ret = ext2fs_open_inode_scan(fs, 0, &scan); | |
432 | if (ret) { | |
433 | log_err("while opening inode scan. ret=%ld", ret); | |
434 | return ret; | |
435 | } | |
436 | ||
437 | while (1) { | |
438 | ret = ext2fs_get_next_inode(scan, &ino, &inode); | |
439 | if (ret) { | |
440 | log_err("while getting next inode. ret=%ld", ret); | |
441 | ext2fs_close_inode_scan(scan); | |
442 | return ret; | |
443 | } | |
444 | if (ino == 0) | |
445 | break; | |
50277515 AK |
446 | if (inode.i_links_count && |
447 | (ino == EXT2_ROOT_INO || | |
448 | ino >= EXT2_FIRST_INODE(fs->super))) { | |
7bed9a78 | 449 | space = ext2fs_inode_i_blocks(fs, &inode) << 9; |
f239fefc AK |
450 | quota_data_add(qctx, &inode, ino, space); |
451 | quota_data_inodes(qctx, &inode, ino, +1); | |
452 | } | |
453 | } | |
454 | ||
455 | ext2fs_close_inode_scan(scan); | |
456 | ||
457 | return 0; | |
458 | } | |
198d20fc N |
459 | |
460 | struct scan_dquots_data { | |
7943ccf5 AK |
461 | dict_t *quota_dict; |
462 | int update_limits; /* update limits from disk */ | |
463 | int update_usage; | |
464 | int usage_is_inconsistent; | |
198d20fc N |
465 | }; |
466 | ||
467 | static int scan_dquots_callback(struct dquot *dquot, void *cb_data) | |
468 | { | |
7943ccf5 AK |
469 | struct scan_dquots_data *scan_data = cb_data; |
470 | dict_t *quota_dict = scan_data->quota_dict; | |
198d20fc N |
471 | struct dquot *dq; |
472 | ||
7943ccf5 | 473 | dq = get_dq(quota_dict, dquot->dq_id); |
198d20fc | 474 | dq->dq_id = dquot->dq_id; |
68ba77ca | 475 | dq->dq_flags |= DQF_SEEN; |
7943ccf5 | 476 | |
a0811c1a TT |
477 | print_dquot("mem", dq); |
478 | print_dquot("dsk", dquot); | |
479 | ||
7943ccf5 AK |
480 | /* Check if there is inconsistancy. */ |
481 | if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace || | |
482 | dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) { | |
483 | scan_data->usage_is_inconsistent = 1; | |
50277515 AK |
484 | fprintf(stderr, "[QUOTA WARNING] Usage inconsistent for ID %d:" |
485 | "actual (%llu, %llu) != expected (%llu, %llu)\n", | |
0ce01729 AD |
486 | dq->dq_id, (long long)dq->dq_dqb.dqb_curspace, |
487 | (long long)dq->dq_dqb.dqb_curinodes, | |
488 | (long long)dquot->dq_dqb.dqb_curspace, | |
489 | (long long)dquot->dq_dqb.dqb_curinodes); | |
7943ccf5 AK |
490 | } |
491 | ||
492 | if (scan_data->update_limits) { | |
198d20fc N |
493 | dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit; |
494 | dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit; | |
495 | dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit; | |
496 | dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit; | |
198d20fc | 497 | } |
7943ccf5 AK |
498 | |
499 | if (scan_data->update_usage) { | |
500 | dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace; | |
501 | dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes; | |
502 | } | |
503 | ||
198d20fc N |
504 | return 0; |
505 | } | |
506 | ||
507 | /* | |
508 | * Read all dquots from quota file into memory | |
509 | */ | |
510 | static errcode_t quota_read_all_dquots(struct quota_handle *qh, | |
7943ccf5 | 511 | quota_ctx_t qctx, int update_limits) |
198d20fc N |
512 | { |
513 | struct scan_dquots_data scan_data; | |
514 | ||
7943ccf5 AK |
515 | scan_data.quota_dict = qctx->quota_dict[qh->qh_type]; |
516 | scan_data.update_limits = update_limits; | |
517 | scan_data.update_usage = 0; | |
198d20fc N |
518 | |
519 | return qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data); | |
520 | } | |
521 | ||
522 | /* | |
523 | * Write all memory dquots into quota file | |
524 | */ | |
0ce01729 | 525 | #if 0 /* currently unused, but may be useful in the future? */ |
198d20fc N |
526 | static errcode_t quota_write_all_dquots(struct quota_handle *qh, |
527 | quota_ctx_t qctx) | |
528 | { | |
529 | errcode_t err; | |
530 | ||
531 | err = ext2fs_read_bitmaps(qctx->fs); | |
532 | if (err) | |
533 | return err; | |
534 | write_dquots(qctx->quota_dict[qh->qh_type], qh); | |
535 | ext2fs_mark_bb_dirty(qctx->fs); | |
536 | qctx->fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
537 | ext2fs_write_bitmaps(qctx->fs); | |
538 | return 0; | |
539 | } | |
0ce01729 | 540 | #endif |
198d20fc N |
541 | |
542 | /* | |
50277515 | 543 | * Updates the in-memory quota limits from the given quota inode. |
198d20fc | 544 | */ |
50277515 | 545 | errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino, int type) |
198d20fc N |
546 | { |
547 | struct quota_handle *qh; | |
548 | errcode_t err; | |
549 | ||
550 | if (!qctx) | |
551 | return 0; | |
552 | ||
553 | err = ext2fs_get_mem(sizeof(struct quota_handle), &qh); | |
554 | if (err) { | |
0ce01729 | 555 | log_err("Unable to allocate quota handle"); |
198d20fc N |
556 | return err; |
557 | } | |
558 | ||
cbc1280d | 559 | err = quota_file_open(qctx, qh, qf_ino, type, -1, 0); |
198d20fc | 560 | if (err) { |
0ce01729 | 561 | log_err("Open quota file failed"); |
198d20fc N |
562 | goto out; |
563 | } | |
564 | ||
565 | quota_read_all_dquots(qh, qctx, 1); | |
198d20fc | 566 | |
cbc1280d | 567 | err = quota_file_close(qctx, qh); |
198d20fc N |
568 | if (err) { |
569 | log_err("Cannot finish IO on new quotafile: %s", | |
570 | strerror(errno)); | |
571 | if (qh->qh_qf.e2_file) | |
572 | ext2fs_file_close(qh->qh_qf.e2_file); | |
573 | } | |
574 | out: | |
575 | ext2fs_free_mem(&qh); | |
576 | return err; | |
577 | } | |
7943ccf5 AK |
578 | |
579 | /* | |
580 | * Compares the measured quota in qctx->quota_dict with that in the quota inode | |
581 | * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is | |
582 | * set to 1 if the supplied and on-disk quota usage values are not identical. | |
583 | */ | |
584 | errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype, | |
585 | int *usage_inconsistent) | |
586 | { | |
7943ccf5 AK |
587 | struct quota_handle qh; |
588 | struct scan_dquots_data scan_data; | |
68ba77ca TT |
589 | struct dquot *dq; |
590 | dnode_t *n; | |
591 | dict_t *dict = qctx->quota_dict[qtype]; | |
7943ccf5 AK |
592 | errcode_t err = 0; |
593 | ||
68ba77ca | 594 | if (!dict) |
7943ccf5 AK |
595 | goto out; |
596 | ||
cbc1280d | 597 | err = quota_file_open(qctx, &qh, 0, qtype, -1, 0); |
7943ccf5 | 598 | if (err) { |
0ce01729 | 599 | log_err("Open quota file failed"); |
7943ccf5 AK |
600 | goto out; |
601 | } | |
602 | ||
603 | scan_data.quota_dict = qctx->quota_dict[qtype]; | |
604 | scan_data.update_limits = 1; | |
605 | scan_data.update_usage = 0; | |
606 | scan_data.usage_is_inconsistent = 0; | |
607 | err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data); | |
608 | if (err) { | |
0ce01729 | 609 | log_err("Error scanning dquots"); |
4af2b156 | 610 | goto out_close_qh; |
7943ccf5 | 611 | } |
68ba77ca TT |
612 | |
613 | for (n = dict_first(dict); n; n = dict_next(dict, n)) { | |
614 | dq = dnode_get(n); | |
615 | if (!dq) | |
616 | continue; | |
617 | if ((dq->dq_flags & DQF_SEEN) == 0) { | |
618 | fprintf(stderr, "[QUOTA WARNING] " | |
619 | "Missing quota entry ID %d\n", dq->dq_id); | |
620 | scan_data.usage_is_inconsistent = 1; | |
621 | } | |
622 | } | |
7943ccf5 AK |
623 | *usage_inconsistent = scan_data.usage_is_inconsistent; |
624 | ||
4af2b156 | 625 | out_close_qh: |
cbc1280d | 626 | err = quota_file_close(qctx, &qh); |
4af2b156 TT |
627 | if (err) { |
628 | log_err("Cannot close quotafile: %s", error_message(errno)); | |
629 | if (qh.qh_qf.e2_file) | |
630 | ext2fs_file_close(qh.qh_qf.e2_file); | |
631 | } | |
7943ccf5 AK |
632 | out: |
633 | return err; | |
634 | } |