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