blob: bfa15e22a83ed69d18d860a2485c3823749d255f
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * closefs.c --- close an ext2 filesystem |
4 | * |
5 | * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. |
6 | * |
7 | * %Begin-Header% |
8 | * This file may be redistributed under the terms of the GNU Public |
9 | * License. |
10 | * %End-Header% |
11 | */ |
12 | |
13 | #include <stdio.h> |
14 | #if HAVE_UNISTD_H |
15 | #include <unistd.h> |
16 | #endif |
17 | #include <time.h> |
18 | #include <string.h> |
19 | |
20 | #include "ext2_fs.h" |
21 | #include "ext2fsP.h" |
22 | |
23 | static int test_root(int a, int b) |
24 | { |
25 | if (a == 0) |
26 | return 1; |
27 | while (1) { |
28 | if (a == 1) |
29 | return 1; |
30 | if (a % b) |
31 | return 0; |
32 | a = a / b; |
33 | } |
34 | } |
35 | |
36 | int ext2fs_bg_has_super(ext2_filsys fs, int group_block) |
37 | { |
38 | if (!(fs->super->s_feature_ro_compat & |
39 | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) |
40 | return 1; |
41 | |
42 | if (test_root(group_block, 3) || (test_root(group_block, 5)) || |
43 | test_root(group_block, 7)) |
44 | return 1; |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | int ext2fs_super_and_bgd_loc(ext2_filsys fs, |
50 | dgrp_t group, |
51 | blk_t *ret_super_blk, |
52 | blk_t *ret_old_desc_blk, |
53 | blk_t *ret_new_desc_blk, |
54 | int *ret_meta_bg) |
55 | { |
56 | blk_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; |
57 | unsigned int meta_bg, meta_bg_size; |
58 | int numblocks, has_super; |
59 | int old_desc_blocks; |
60 | |
61 | group_block = fs->super->s_first_data_block + |
62 | (group * fs->super->s_blocks_per_group); |
63 | |
64 | if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) |
65 | old_desc_blocks = fs->super->s_first_meta_bg; |
66 | else |
67 | old_desc_blocks = |
68 | fs->desc_blocks + fs->super->s_reserved_gdt_blocks; |
69 | |
70 | if (group == fs->group_desc_count-1) { |
71 | numblocks = (fs->super->s_blocks_count - |
72 | fs->super->s_first_data_block) % |
73 | fs->super->s_blocks_per_group; |
74 | if (!numblocks) |
75 | numblocks = fs->super->s_blocks_per_group; |
76 | } else |
77 | numblocks = fs->super->s_blocks_per_group; |
78 | |
79 | has_super = ext2fs_bg_has_super(fs, group); |
80 | |
81 | if (has_super) { |
82 | super_blk = group_block; |
83 | numblocks--; |
84 | } |
85 | meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc)); |
86 | meta_bg = group / meta_bg_size; |
87 | |
88 | if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || |
89 | (meta_bg < fs->super->s_first_meta_bg)) { |
90 | if (has_super) { |
91 | old_desc_blk = group_block + 1; |
92 | numblocks -= old_desc_blocks; |
93 | } |
94 | } else { |
95 | if (((group % meta_bg_size) == 0) || |
96 | ((group % meta_bg_size) == 1) || |
97 | ((group % meta_bg_size) == (meta_bg_size-1))) { |
98 | if (has_super) |
99 | has_super = 1; |
100 | new_desc_blk = group_block + has_super; |
101 | numblocks--; |
102 | } |
103 | } |
104 | |
105 | numblocks -= 2 + fs->inode_blocks_per_group; |
106 | |
107 | if (ret_super_blk) |
108 | *ret_super_blk = super_blk; |
109 | if (ret_old_desc_blk) |
110 | *ret_old_desc_blk = old_desc_blk; |
111 | if (ret_new_desc_blk) |
112 | *ret_new_desc_blk = new_desc_blk; |
113 | if (ret_meta_bg) |
114 | *ret_meta_bg = meta_bg; |
115 | return numblocks; |
116 | } |
117 | |
118 | |
119 | /* |
120 | * This function forces out the primary superblock. We need to only |
121 | * write out those fields which we have changed, since if the |
122 | * filesystem is mounted, it may have changed some of the other |
123 | * fields. |
124 | * |
125 | * It takes as input a superblock which has already been byte swapped |
126 | * (if necessary). |
127 | * |
128 | */ |
129 | static errcode_t write_primary_superblock(ext2_filsys fs, |
130 | struct ext2_super_block *super) |
131 | { |
132 | __u16 *old_super, *new_super; |
133 | int check_idx, write_idx, size; |
134 | errcode_t retval; |
135 | |
136 | if (!fs->io->manager->write_byte || !fs->orig_super) { |
137 | io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); |
138 | retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE, |
139 | super); |
140 | io_channel_set_blksize(fs->io, fs->blocksize); |
141 | return retval; |
142 | } |
143 | |
144 | old_super = (__u16 *) fs->orig_super; |
145 | new_super = (__u16 *) super; |
146 | |
147 | for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { |
148 | if (old_super[check_idx] == new_super[check_idx]) |
149 | continue; |
150 | write_idx = check_idx; |
151 | for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) |
152 | if (old_super[check_idx] == new_super[check_idx]) |
153 | break; |
154 | size = 2 * (check_idx - write_idx); |
155 | retval = io_channel_write_byte(fs->io, |
156 | SUPERBLOCK_OFFSET + (2 * write_idx), size, |
157 | new_super + write_idx); |
158 | if (retval) |
159 | return retval; |
160 | } |
161 | memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); |
162 | return 0; |
163 | } |
164 | |
165 | |
166 | /* |
167 | * Updates the revision to EXT2_DYNAMIC_REV |
168 | */ |
169 | void ext2fs_update_dynamic_rev(ext2_filsys fs) |
170 | { |
171 | struct ext2_super_block *sb = fs->super; |
172 | |
173 | if (sb->s_rev_level > EXT2_GOOD_OLD_REV) |
174 | return; |
175 | |
176 | sb->s_rev_level = EXT2_DYNAMIC_REV; |
177 | sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; |
178 | sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; |
179 | /* s_uuid is handled by e2fsck already */ |
180 | /* other fields should be left alone */ |
181 | } |
182 | |
183 | static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, |
184 | blk_t group_block, |
185 | struct ext2_super_block *super_shadow) |
186 | { |
187 | dgrp_t sgrp = group; |
188 | |
189 | if (sgrp > ((1 << 16) - 1)) |
190 | sgrp = (1 << 16) - 1; |
191 | #if BB_BIG_ENDIAN |
192 | if (fs->flags & EXT2_FLAG_SWAP_BYTES) |
193 | super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); |
194 | else |
195 | #endif |
196 | fs->super->s_block_group_nr = sgrp; |
197 | |
198 | return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE, |
199 | super_shadow); |
200 | } |
201 | |
202 | |
203 | errcode_t ext2fs_flush(ext2_filsys fs) |
204 | { |
205 | dgrp_t i; |
206 | blk_t group_block; |
207 | errcode_t retval; |
208 | unsigned long fs_state; |
209 | struct ext2_super_block *super_shadow = NULL; |
210 | struct ext2_group_desc *group_shadow = NULL; |
211 | char *group_ptr; |
212 | int old_desc_blocks; |
213 | #if BB_BIG_ENDIAN |
214 | dgrp_t j; |
215 | struct ext2_group_desc *s, *t; |
216 | #endif |
217 | |
218 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
219 | |
220 | fs_state = fs->super->s_state; |
221 | |
222 | fs->super->s_wtime = time(NULL); |
223 | fs->super->s_block_group_nr = 0; |
224 | #if BB_BIG_ENDIAN |
225 | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
226 | retval = EXT2_ET_NO_MEMORY; |
227 | retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); |
228 | if (retval) |
229 | goto errout; |
230 | retval = ext2fs_get_mem((size_t)(fs->blocksize * |
231 | fs->desc_blocks), |
232 | &group_shadow); |
233 | if (retval) |
234 | goto errout; |
235 | memset(group_shadow, 0, (size_t) fs->blocksize * |
236 | fs->desc_blocks); |
237 | |
238 | /* swap the group descriptors */ |
239 | for (j=0, s=fs->group_desc, t=group_shadow; |
240 | j < fs->group_desc_count; j++, t++, s++) { |
241 | *t = *s; |
242 | ext2fs_swap_group_desc(t); |
243 | } |
244 | } else { |
245 | super_shadow = fs->super; |
246 | group_shadow = fs->group_desc; |
247 | } |
248 | #else |
249 | super_shadow = fs->super; |
250 | group_shadow = fs->group_desc; |
251 | #endif |
252 | |
253 | /* |
254 | * If this is an external journal device, don't write out the |
255 | * block group descriptors or any of the backup superblocks |
256 | */ |
257 | if (fs->super->s_feature_incompat & |
258 | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) |
259 | goto write_primary_superblock_only; |
260 | |
261 | /* |
262 | * Set the state of the FS to be non-valid. (The state has |
263 | * already been backed up earlier, and will be restored after |
264 | * we write out the backup superblocks.) |
265 | */ |
266 | fs->super->s_state &= ~EXT2_VALID_FS; |
267 | #if BB_BIG_ENDIAN |
268 | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
269 | *super_shadow = *fs->super; |
270 | ext2fs_swap_super(super_shadow); |
271 | } |
272 | #endif |
273 | |
274 | /* |
275 | * Write out the master group descriptors, and the backup |
276 | * superblocks and group descriptors. |
277 | */ |
278 | group_block = fs->super->s_first_data_block; |
279 | group_ptr = (char *) group_shadow; |
280 | if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) |
281 | old_desc_blocks = fs->super->s_first_meta_bg; |
282 | else |
283 | old_desc_blocks = fs->desc_blocks; |
284 | |
285 | for (i = 0; i < fs->group_desc_count; i++) { |
286 | blk_t super_blk, old_desc_blk, new_desc_blk; |
287 | int meta_bg; |
288 | |
289 | ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk, |
290 | &new_desc_blk, &meta_bg); |
291 | |
292 | if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { |
293 | retval = write_backup_super(fs, i, super_blk, |
294 | super_shadow); |
295 | if (retval) |
296 | goto errout; |
297 | } |
298 | if (fs->flags & EXT2_FLAG_SUPER_ONLY) |
299 | continue; |
300 | if ((old_desc_blk) && |
301 | (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { |
302 | retval = io_channel_write_blk(fs->io, |
303 | old_desc_blk, old_desc_blocks, group_ptr); |
304 | if (retval) |
305 | goto errout; |
306 | } |
307 | if (new_desc_blk) { |
308 | retval = io_channel_write_blk(fs->io, new_desc_blk, |
309 | 1, group_ptr + (meta_bg*fs->blocksize)); |
310 | if (retval) |
311 | goto errout; |
312 | } |
313 | } |
314 | fs->super->s_block_group_nr = 0; |
315 | fs->super->s_state = fs_state; |
316 | #if BB_BIG_ENDIAN |
317 | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
318 | *super_shadow = *fs->super; |
319 | ext2fs_swap_super(super_shadow); |
320 | } |
321 | #endif |
322 | |
323 | /* |
324 | * If the write_bitmaps() function is present, call it to |
325 | * flush the bitmaps. This is done this way so that a simple |
326 | * program that doesn't mess with the bitmaps doesn't need to |
327 | * drag in the bitmaps.c code. |
328 | */ |
329 | if (fs->write_bitmaps) { |
330 | retval = fs->write_bitmaps(fs); |
331 | if (retval) |
332 | goto errout; |
333 | } |
334 | |
335 | write_primary_superblock_only: |
336 | /* |
337 | * Write out master superblock. This has to be done |
338 | * separately, since it is located at a fixed location |
339 | * (SUPERBLOCK_OFFSET). We flush all other pending changes |
340 | * out to disk first, just to avoid a race condition with an |
341 | * insy-tinsy window.... |
342 | */ |
343 | retval = io_channel_flush(fs->io); |
344 | retval = write_primary_superblock(fs, super_shadow); |
345 | if (retval) |
346 | goto errout; |
347 | |
348 | fs->flags &= ~EXT2_FLAG_DIRTY; |
349 | |
350 | retval = io_channel_flush(fs->io); |
351 | errout: |
352 | fs->super->s_state = fs_state; |
353 | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
354 | if (super_shadow) |
355 | ext2fs_free_mem(&super_shadow); |
356 | if (group_shadow) |
357 | ext2fs_free_mem(&group_shadow); |
358 | } |
359 | return retval; |
360 | } |
361 | |
362 | errcode_t ext2fs_close(ext2_filsys fs) |
363 | { |
364 | errcode_t retval; |
365 | |
366 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
367 | |
368 | if (fs->flags & EXT2_FLAG_DIRTY) { |
369 | retval = ext2fs_flush(fs); |
370 | if (retval) |
371 | return retval; |
372 | } |
373 | if (fs->write_bitmaps) { |
374 | retval = fs->write_bitmaps(fs); |
375 | if (retval) |
376 | return retval; |
377 | } |
378 | ext2fs_free(fs); |
379 | return 0; |
380 | } |
381 |