]>
Commit | Line | Data |
---|---|---|
3839e657 TT |
1 | /* |
2 | * closefs.c --- close an ext2 filesystem | |
efc6f628 | 3 | * |
21c84b71 TT |
4 | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
5 | * | |
6 | * %Begin-Header% | |
543547a5 TT |
7 | * This file may be redistributed under the terms of the GNU Library |
8 | * General Public License, version 2. | |
21c84b71 | 9 | * %End-Header% |
3839e657 TT |
10 | */ |
11 | ||
d1154eb4 | 12 | #include "config.h" |
3839e657 | 13 | #include <stdio.h> |
4cbe8af4 | 14 | #if HAVE_UNISTD_H |
3839e657 | 15 | #include <unistd.h> |
4cbe8af4 | 16 | #endif |
3839e657 | 17 | #include <time.h> |
50e1e10f | 18 | #include <string.h> |
3839e657 | 19 | |
b5abe6fa | 20 | #include "ext2_fs.h" |
21c84b71 | 21 | #include "ext2fsP.h" |
3839e657 | 22 | |
47183951 | 23 | static int test_root(unsigned int a, unsigned int b) |
1b4cd9c7 | 24 | { |
1b4cd9c7 | 25 | while (1) { |
47183951 TT |
26 | if (a < b) |
27 | return 0; | |
28 | if (a == b) | |
1b4cd9c7 TT |
29 | return 1; |
30 | if (a % b) | |
31 | return 0; | |
32 | a = a / b; | |
33 | } | |
34 | } | |
35 | ||
47183951 | 36 | int ext2fs_bg_has_super(ext2_filsys fs, dgrp_t group) |
1b4cd9c7 | 37 | { |
65c6c3e0 TT |
38 | if (group == 0) |
39 | return 1; | |
77b3e987 | 40 | if (ext2fs_has_feature_sparse_super2(fs->super)) { |
65c6c3e0 TT |
41 | if (group == fs->super->s_backup_bgs[0] || |
42 | group == fs->super->s_backup_bgs[1]) | |
43 | return 1; | |
44 | return 0; | |
45 | } | |
77b3e987 | 46 | if ((group <= 1) || !ext2fs_has_feature_sparse_super(fs->super)) |
1b4cd9c7 | 47 | return 1; |
47183951 TT |
48 | if (!(group & 1)) |
49 | return 0; | |
50 | if (test_root(group, 3) || (test_root(group, 5)) || | |
51 | test_root(group, 7)) | |
1b4cd9c7 TT |
52 | return 1; |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
4a568505 | 57 | /* |
71300f35 JS |
58 | * ext2fs_super_and_bgd_loc2() |
59 | * @fs: ext2 fs pointer | |
60 | * @group given block group | |
61 | * @ret_super_blk: if !NULL, returns super block location | |
62 | * @ret_old_desc_blk: if !NULL, returns location of the old block | |
63 | * group descriptor | |
64 | * @ret_new_desc_blk: if !NULL, returns location of meta_bg block | |
65 | * group descriptor | |
66 | * @ret_used_blks: if !NULL, returns number of blocks used by | |
67 | * super block and group_descriptors. | |
4a568505 | 68 | * |
71300f35 | 69 | * Returns errcode_t of 0 |
4a568505 | 70 | */ |
71300f35 JS |
71 | errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs, |
72 | dgrp_t group, | |
73 | blk64_t *ret_super_blk, | |
74 | blk64_t *ret_old_desc_blk, | |
75 | blk64_t *ret_new_desc_blk, | |
76 | blk_t *ret_used_blks) | |
ef344e13 | 77 | { |
71300f35 | 78 | blk64_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; |
54434927 | 79 | unsigned int meta_bg, meta_bg_size; |
71300f35 JS |
80 | blk_t numblocks = 0; |
81 | blk64_t old_desc_blocks; | |
5d38ef1d | 82 | int has_super; |
ef344e13 | 83 | |
71300f35 | 84 | group_block = ext2fs_group_first_block2(fs, group); |
25567a7b TT |
85 | if (group_block == 0 && fs->blocksize == 1024) |
86 | group_block = 1; /* Deal with 1024 blocksize && bigalloc */ | |
ef344e13 | 87 | |
77b3e987 | 88 | if (ext2fs_has_feature_meta_bg(fs->super)) |
ef344e13 TT |
89 | old_desc_blocks = fs->super->s_first_meta_bg; |
90 | else | |
efc6f628 | 91 | old_desc_blocks = |
d323f8fb | 92 | fs->desc_blocks + fs->super->s_reserved_gdt_blocks; |
ef344e13 | 93 | |
ef344e13 TT |
94 | has_super = ext2fs_bg_has_super(fs, group); |
95 | ||
96 | if (has_super) { | |
97 | super_blk = group_block; | |
71300f35 | 98 | numblocks++; |
ef344e13 | 99 | } |
f2de1d38 | 100 | meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); |
ef344e13 TT |
101 | meta_bg = group / meta_bg_size; |
102 | ||
77b3e987 | 103 | if (!ext2fs_has_feature_meta_bg(fs->super) || |
ef344e13 TT |
104 | (meta_bg < fs->super->s_first_meta_bg)) { |
105 | if (has_super) { | |
106 | old_desc_blk = group_block + 1; | |
71300f35 | 107 | numblocks += old_desc_blocks; |
ef344e13 TT |
108 | } |
109 | } else { | |
110 | if (((group % meta_bg_size) == 0) || | |
111 | ((group % meta_bg_size) == 1) || | |
112 | ((group % meta_bg_size) == (meta_bg_size-1))) { | |
113 | if (has_super) | |
114 | has_super = 1; | |
115 | new_desc_blk = group_block + has_super; | |
71300f35 | 116 | numblocks++; |
ef344e13 TT |
117 | } |
118 | } | |
efc6f628 | 119 | |
ef344e13 TT |
120 | if (ret_super_blk) |
121 | *ret_super_blk = super_blk; | |
122 | if (ret_old_desc_blk) | |
123 | *ret_old_desc_blk = old_desc_blk; | |
124 | if (ret_new_desc_blk) | |
125 | *ret_new_desc_blk = new_desc_blk; | |
71300f35 JS |
126 | if (ret_used_blks) |
127 | *ret_used_blks = numblocks; | |
128 | ||
129 | return 0; | |
ef344e13 TT |
130 | } |
131 | ||
71300f35 JS |
132 | /* |
133 | * This function returns the location of the superblock, block group | |
134 | * descriptors for a given block group. It currently returns the | |
135 | * number of free blocks assuming that inode table and allocation | |
136 | * bitmaps will be in the group. This is not necessarily the case | |
137 | * when the flex_bg feature is enabled, so callers should take care! | |
138 | * It was only really intended for use by mke2fs, and even there it's | |
139 | * not that useful. | |
140 | * | |
141 | * The ext2fs_super_and_bgd_loc2() function is 64-bit block number | |
142 | * capable and returns the number of blocks used by super block and | |
143 | * group descriptors. | |
144 | */ | |
145 | int ext2fs_super_and_bgd_loc(ext2_filsys fs, | |
146 | dgrp_t group, | |
147 | blk_t *ret_super_blk, | |
148 | blk_t *ret_old_desc_blk, | |
149 | blk_t *ret_new_desc_blk, | |
150 | int *ret_meta_bg) | |
151 | { | |
152 | blk64_t ret_super_blk2; | |
153 | blk64_t ret_old_desc_blk2; | |
154 | blk64_t ret_new_desc_blk2; | |
155 | blk_t ret_used_blks; | |
156 | blk_t numblocks; | |
157 | unsigned int meta_bg_size; | |
158 | ||
159 | ext2fs_super_and_bgd_loc2(fs, group, &ret_super_blk2, | |
160 | &ret_old_desc_blk2, | |
161 | &ret_new_desc_blk2, | |
162 | &ret_used_blks); | |
163 | ||
98f45471 | 164 | numblocks = ext2fs_group_blocks_count(fs, group); |
71300f35 JS |
165 | |
166 | if (ret_super_blk) | |
167 | *ret_super_blk = (blk_t)ret_super_blk2; | |
168 | if (ret_old_desc_blk) | |
169 | *ret_old_desc_blk = (blk_t)ret_old_desc_blk2; | |
170 | if (ret_new_desc_blk) | |
171 | *ret_new_desc_blk = (blk_t)ret_new_desc_blk2; | |
172 | if (ret_meta_bg) { | |
173 | meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); | |
174 | *ret_meta_bg = group / meta_bg_size; | |
175 | } | |
176 | ||
177 | numblocks -= 2 + fs->inode_blocks_per_group + ret_used_blks; | |
178 | ||
179 | return numblocks; | |
180 | } | |
ef344e13 | 181 | |
c180ac86 TT |
182 | /* |
183 | * This function forces out the primary superblock. We need to only | |
184 | * write out those fields which we have changed, since if the | |
185 | * filesystem is mounted, it may have changed some of the other | |
186 | * fields. | |
187 | * | |
188 | * It takes as input a superblock which has already been byte swapped | |
189 | * (if necessary). | |
190 | * | |
191 | */ | |
192 | static errcode_t write_primary_superblock(ext2_filsys fs, | |
193 | struct ext2_super_block *super) | |
194 | { | |
195 | __u16 *old_super, *new_super; | |
196 | int check_idx, write_idx, size; | |
197 | errcode_t retval; | |
198 | ||
199 | if (!fs->io->manager->write_byte || !fs->orig_super) { | |
7f1a1fbf | 200 | fallback: |
c180ac86 | 201 | io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); |
24a117ab | 202 | retval = io_channel_write_blk64(fs->io, 1, -SUPERBLOCK_SIZE, |
c180ac86 TT |
203 | super); |
204 | io_channel_set_blksize(fs->io, fs->blocksize); | |
205 | return retval; | |
206 | } | |
207 | ||
208 | old_super = (__u16 *) fs->orig_super; | |
209 | new_super = (__u16 *) super; | |
210 | ||
211 | for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { | |
212 | if (old_super[check_idx] == new_super[check_idx]) | |
213 | continue; | |
214 | write_idx = check_idx; | |
215 | for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) | |
216 | if (old_super[check_idx] == new_super[check_idx]) | |
217 | break; | |
218 | size = 2 * (check_idx - write_idx); | |
219 | #if 0 | |
220 | printf("Writing %d bytes starting at %d\n", | |
221 | size, write_idx*2); | |
222 | #endif | |
223 | retval = io_channel_write_byte(fs->io, | |
224 | SUPERBLOCK_OFFSET + (2 * write_idx), size, | |
225 | new_super + write_idx); | |
7f1a1fbf TT |
226 | if (retval == EXT2_ET_UNIMPLEMENTED) |
227 | goto fallback; | |
c180ac86 TT |
228 | if (retval) |
229 | return retval; | |
230 | } | |
3c6b8977 | 231 | memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); |
c180ac86 TT |
232 | return 0; |
233 | } | |
234 | ||
235 | ||
3fe973b3 TT |
236 | /* |
237 | * Updates the revision to EXT2_DYNAMIC_REV | |
238 | */ | |
a917d1cc | 239 | void ext2fs_update_dynamic_rev(ext2_filsys fs) |
3fe973b3 TT |
240 | { |
241 | struct ext2_super_block *sb = fs->super; | |
242 | ||
243 | if (sb->s_rev_level > EXT2_GOOD_OLD_REV) | |
244 | return; | |
245 | ||
246 | sb->s_rev_level = EXT2_DYNAMIC_REV; | |
247 | sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; | |
248 | sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; | |
249 | /* s_uuid is handled by e2fsck already */ | |
250 | /* other fields should be left alone */ | |
251 | } | |
252 | ||
c046ac7f | 253 | static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, |
4dbfd79d | 254 | blk64_t group_block, |
c046ac7f TT |
255 | struct ext2_super_block *super_shadow) |
256 | { | |
d4ca3e40 | 257 | errcode_t retval; |
c046ac7f | 258 | dgrp_t sgrp = group; |
efc6f628 | 259 | |
c046ac7f TT |
260 | if (sgrp > ((1 << 16) - 1)) |
261 | sgrp = (1 << 16) - 1; | |
d4ca3e40 | 262 | |
ebc8d3ca TT |
263 | super_shadow->s_block_group_nr = ext2fs_cpu_to_le16(sgrp); |
264 | ||
d4ca3e40 DW |
265 | retval = ext2fs_superblock_csum_set(fs, super_shadow); |
266 | if (retval) | |
267 | return retval; | |
c046ac7f | 268 | |
24a117ab | 269 | return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE, |
c046ac7f TT |
270 | super_shadow); |
271 | } | |
272 | ||
3839e657 | 273 | errcode_t ext2fs_flush(ext2_filsys fs) |
9d9a53e6 RJ |
274 | { |
275 | return ext2fs_flush2(fs, 0); | |
276 | } | |
277 | ||
278 | errcode_t ext2fs_flush2(ext2_filsys fs, int flags) | |
3839e657 | 279 | { |
2d328bb7 | 280 | dgrp_t i; |
3839e657 | 281 | errcode_t retval; |
f3db3566 | 282 | unsigned long fs_state; |
9a083af7 | 283 | __u32 feature_incompat; |
50e1e10f | 284 | struct ext2_super_block *super_shadow = 0; |
c8a1566d | 285 | struct opaque_ext2_group_desc *group_shadow = 0; |
2d328bb7 | 286 | #ifdef WORDS_BIGENDIAN |
1d18a55c | 287 | struct ext2_group_desc *gdp; |
2d328bb7 TT |
288 | dgrp_t j; |
289 | #endif | |
ef344e13 | 290 | char *group_ptr; |
49d0fe2a | 291 | blk64_t old_desc_blocks; |
95fd65bb | 292 | struct ext2fs_numeric_progress_struct progress; |
efc6f628 | 293 | |
f3db3566 TT |
294 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
295 | ||
c8651b4b TT |
296 | if ((fs->flags & EXT2_FLAG_SUPER_ONLY) == 0 && |
297 | !ext2fs_has_feature_journal_dev(fs->super) && | |
298 | fs->group_desc == NULL) | |
299 | return EXT2_ET_NO_GDESC; | |
300 | ||
50e1e10f | 301 | fs_state = fs->super->s_state; |
9a083af7 | 302 | feature_incompat = fs->super->s_feature_incompat; |
50e1e10f | 303 | |
32138187 | 304 | fs->super->s_wtime = fs->now ? fs->now : time(NULL); |
e5b38a5f | 305 | fs->super->s_block_group_nr = 0; |
a80ea340 DW |
306 | |
307 | /* | |
308 | * If the write_bitmaps() function is present, call it to | |
309 | * flush the bitmaps. This is done this way so that a simple | |
310 | * program that doesn't mess with the bitmaps doesn't need to | |
311 | * drag in the bitmaps.c code. | |
312 | * | |
313 | * Bitmap checksums live in the group descriptor, so the | |
314 | * bitmaps need to be written before the descriptors. | |
315 | */ | |
316 | if (fs->write_bitmaps) { | |
317 | retval = fs->write_bitmaps(fs); | |
318 | if (retval) | |
319 | goto errout; | |
320 | } | |
321 | ||
ebc8d3ca TT |
322 | /* |
323 | * Set the state of the FS to be non-valid. (The state has | |
324 | * already been backed up earlier, and will be restored after | |
325 | * we write out the backup superblocks.) | |
326 | */ | |
327 | fs->super->s_state &= ~EXT2_VALID_FS; | |
328 | ext2fs_clear_feature_journal_needs_recovery(fs->super); | |
329 | ||
330 | /* Byte swap the superblock and the group descriptors if necessary */ | |
126a291c TT |
331 | #ifdef WORDS_BIGENDIAN |
332 | retval = EXT2_ET_NO_MEMORY; | |
333 | retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); | |
126a291c TT |
334 | if (retval) |
335 | goto errout; | |
d4ca3e40 | 336 | memcpy(super_shadow, fs->super, sizeof(struct ext2_super_block)); |
ebc8d3ca | 337 | ext2fs_swap_super(super_shadow); |
df659444 TT |
338 | |
339 | if (((fs->flags & EXT2_FLAG_SUPER_ONLY) == 0) && | |
340 | !ext2fs_has_feature_journal_dev(fs->super)) { | |
341 | retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, | |
342 | &group_shadow); | |
343 | if (retval) | |
344 | goto errout; | |
345 | memcpy(group_shadow, fs->group_desc, (size_t) fs->blocksize * | |
346 | fs->desc_blocks); | |
347 | ||
348 | for (j = 0; j < fs->group_desc_count; j++) { | |
349 | gdp = ext2fs_group_desc(fs, group_shadow, j); | |
350 | ext2fs_swap_group_desc2(fs, gdp); | |
351 | } | |
50e1e10f | 352 | } |
5df55d7f TT |
353 | #else |
354 | super_shadow = fs->super; | |
c8a1566d | 355 | group_shadow = fs->group_desc; |
5df55d7f | 356 | #endif |
efc6f628 | 357 | |
0d961040 TT |
358 | /* |
359 | * If this is an external journal device, don't write out the | |
360 | * block group descriptors or any of the backup superblocks | |
361 | */ | |
77b3e987 | 362 | if (ext2fs_has_feature_journal_dev(fs->super)) |
0d961040 TT |
363 | goto write_primary_superblock_only; |
364 | ||
3839e657 TT |
365 | /* |
366 | * Write out the master group descriptors, and the backup | |
367 | * superblocks and group descriptors. | |
368 | */ | |
ef344e13 | 369 | group_ptr = (char *) group_shadow; |
77b3e987 | 370 | if (ext2fs_has_feature_meta_bg(fs->super)) { |
ef344e13 | 371 | old_desc_blocks = fs->super->s_first_meta_bg; |
49d0fe2a | 372 | if (old_desc_blocks > fs->desc_blocks) |
f66e6ce4 TT |
373 | old_desc_blocks = fs->desc_blocks; |
374 | } else | |
ef344e13 TT |
375 | old_desc_blocks = fs->desc_blocks; |
376 | ||
dfe74c5c TT |
377 | if (fs->progress_ops && fs->progress_ops->init) |
378 | (fs->progress_ops->init)(fs, &progress, NULL, | |
379 | fs->group_desc_count); | |
95fd65bb VAH |
380 | |
381 | ||
bfd41883 | 382 | for (i = 0; i < fs->group_desc_count; i++) { |
20f2ccb3 | 383 | blk64_t super_blk, old_desc_blk, new_desc_blk; |
ef344e13 | 384 | |
dfe74c5c TT |
385 | if (fs->progress_ops && fs->progress_ops->update) |
386 | (fs->progress_ops->update)(fs, &progress, i); | |
20f2ccb3 JS |
387 | ext2fs_super_and_bgd_loc2(fs, i, &super_blk, &old_desc_blk, |
388 | &new_desc_blk, 0); | |
ef344e13 TT |
389 | |
390 | if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { | |
391 | retval = write_backup_super(fs, i, super_blk, | |
c046ac7f | 392 | super_shadow); |
50e1e10f TT |
393 | if (retval) |
394 | goto errout; | |
3839e657 | 395 | } |
ef344e13 TT |
396 | if (fs->flags & EXT2_FLAG_SUPER_ONLY) |
397 | continue; | |
efc6f628 | 398 | if ((old_desc_blk) && |
ef344e13 | 399 | (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { |
24a117ab | 400 | retval = io_channel_write_blk64(fs->io, |
ef344e13 TT |
401 | old_desc_blk, old_desc_blocks, group_ptr); |
402 | if (retval) | |
403 | goto errout; | |
404 | } | |
405 | if (new_desc_blk) { | |
20f2ccb3 JS |
406 | int meta_bg = i / EXT2_DESC_PER_BLOCK(fs->super); |
407 | ||
24a117ab | 408 | retval = io_channel_write_blk64(fs->io, new_desc_blk, |
ef344e13 TT |
409 | 1, group_ptr + (meta_bg*fs->blocksize)); |
410 | if (retval) | |
50e1e10f | 411 | goto errout; |
3839e657 | 412 | } |
3839e657 TT |
413 | } |
414 | ||
dfe74c5c TT |
415 | if (fs->progress_ops && fs->progress_ops->close) |
416 | (fs->progress_ops->close)(fs, &progress, NULL); | |
95fd65bb | 417 | |
a63d1267 | 418 | write_primary_superblock_only: |
b5abe6fa | 419 | /* |
a63d1267 TT |
420 | * Write out master superblock. This has to be done |
421 | * separately, since it is located at a fixed location | |
422 | * (SUPERBLOCK_OFFSET). We flush all other pending changes | |
423 | * out to disk first, just to avoid a race condition with an | |
424 | * insy-tinsy window.... | |
b5abe6fa | 425 | */ |
0d961040 TT |
426 | |
427 | fs->super->s_block_group_nr = 0; | |
428 | fs->super->s_state = fs_state; | |
9a083af7 | 429 | fs->super->s_feature_incompat = feature_incompat; |
126a291c TT |
430 | #ifdef WORDS_BIGENDIAN |
431 | *super_shadow = *fs->super; | |
432 | ext2fs_swap_super(super_shadow); | |
0d961040 TT |
433 | #endif |
434 | ||
d4ca3e40 DW |
435 | retval = ext2fs_superblock_csum_set(fs, super_shadow); |
436 | if (retval) | |
437 | return retval; | |
438 | ||
025d31b1 | 439 | if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) { |
9d9a53e6 | 440 | retval = io_channel_flush(fs->io); |
025d31b1 ES |
441 | if (retval) |
442 | goto errout; | |
443 | } | |
a63d1267 TT |
444 | retval = write_primary_superblock(fs, super_shadow); |
445 | if (retval) | |
446 | goto errout; | |
447 | ||
448 | fs->flags &= ~EXT2_FLAG_DIRTY; | |
449 | ||
025d31b1 | 450 | if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) { |
9d9a53e6 | 451 | retval = io_channel_flush(fs->io); |
025d31b1 ES |
452 | if (retval) |
453 | goto errout; | |
454 | } | |
50e1e10f TT |
455 | errout: |
456 | fs->super->s_state = fs_state; | |
126a291c TT |
457 | #ifdef WORDS_BIGENDIAN |
458 | if (super_shadow) | |
459 | ext2fs_free_mem(&super_shadow); | |
460 | if (group_shadow) | |
461 | ext2fs_free_mem(&group_shadow); | |
462 | #endif | |
50e1e10f | 463 | return retval; |
3839e657 TT |
464 | } |
465 | ||
47fee2ef LC |
466 | errcode_t ext2fs_close_free(ext2_filsys *fs_ptr) |
467 | { | |
468 | errcode_t ret; | |
469 | ext2_filsys fs = *fs_ptr; | |
470 | ||
471 | ret = ext2fs_close2(fs, 0); | |
472 | if (ret) | |
473 | ext2fs_free(fs); | |
474 | *fs_ptr = NULL; | |
475 | return ret; | |
476 | } | |
477 | ||
3839e657 | 478 | errcode_t ext2fs_close(ext2_filsys fs) |
9d9a53e6 RJ |
479 | { |
480 | return ext2fs_close2(fs, 0); | |
481 | } | |
482 | ||
483 | errcode_t ext2fs_close2(ext2_filsys fs, int flags) | |
3839e657 TT |
484 | { |
485 | errcode_t retval; | |
b7c5b403 TT |
486 | int meta_blks; |
487 | io_stats stats = 0; | |
efc6f628 | 488 | |
f3db3566 TT |
489 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
490 | ||
b7c5b403 TT |
491 | if (fs->write_bitmaps) { |
492 | retval = fs->write_bitmaps(fs); | |
3839e657 TT |
493 | if (retval) |
494 | return retval; | |
495 | } | |
b7c5b403 TT |
496 | if (fs->super->s_kbytes_written && |
497 | fs->io->manager->get_stats) | |
498 | fs->io->manager->get_stats(fs->io, &stats); | |
499 | if (stats && stats->bytes_written && (fs->flags & EXT2_FLAG_RW)) { | |
500 | fs->super->s_kbytes_written += stats->bytes_written >> 10; | |
501 | meta_blks = fs->desc_blocks + 1; | |
502 | if (!(fs->flags & EXT2_FLAG_SUPER_ONLY)) | |
503 | fs->super->s_kbytes_written += meta_blks / | |
504 | (fs->blocksize / 1024); | |
6d67ee30 TT |
505 | if ((fs->flags & EXT2_FLAG_DIRTY) == 0) |
506 | fs->flags |= EXT2_FLAG_SUPER_ONLY | EXT2_FLAG_DIRTY; | |
b7c5b403 TT |
507 | } |
508 | if (fs->flags & EXT2_FLAG_DIRTY) { | |
9d9a53e6 | 509 | retval = ext2fs_flush2(fs, flags); |
21c84b71 TT |
510 | if (retval) |
511 | return retval; | |
512 | } | |
0f5eba75 AD |
513 | |
514 | retval = ext2fs_mmp_stop(fs); | |
515 | if (retval) | |
516 | return retval; | |
517 | ||
3839e657 TT |
518 | ext2fs_free(fs); |
519 | return 0; | |
520 | } | |
21c84b71 | 521 |