]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - lib/support/mkquota.c
Merge remote-tracking branch 'origin/maint' into next
[thirdparty/e2fsprogs.git] / lib / support / mkquota.c
CommitLineData
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
29static 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
49static 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
60static 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 70int 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 92void 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 105errcode_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
132static 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 148errcode_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
210out:
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
220static 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 235static 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
242static 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 254errcode_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 288void 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
319static 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
343void 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
370void 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 */
396void 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 418errcode_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
460struct 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
467static 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 */
510static 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
526static 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 545errcode_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 }
574out:
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 */
584errcode_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 625out_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
632out:
633 return err;
634}