1 /*
   2     ext2_meta.c -- ext2 metadata mover
   3     Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
   4 
   5     This program is free software; you can redistribute it and/or modify
   6     it under the terms of the GNU General Public License as published by
   7     the Free Software Foundation; either version 3 of the License, or
   8     (at your option) any later version.
   9 
  10     This program is distributed in the hope that it will be useful,
  11     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13     GNU General Public License for more details.
  14 
  15     You should have received a copy of the GNU General Public License
  16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18 
  19 #include <config.h>
  20 
  21 #ifndef DISCOVER_ONLY
  22 
  23 #include <stdio.h>
  24 #include <stdlib.h>
  25 #include "ext2.h"
  26 
  27 int ext2_metadata_push(struct ext2_fs *fs, blk_t newsize)
  28 {
  29         int   i;
  30         int   newgdblocks;
  31         blk_t newitoffset;
  32 
  33         newgdblocks = ped_div_round_up (newsize
  34                         - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb),
  35                               EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
  36         newgdblocks = ped_div_round_up (newgdblocks
  37                         * sizeof(struct ext2_group_desc),
  38                               fs->blocksize);
  39         newitoffset = newgdblocks + 3;
  40 
  41         if (newitoffset <= fs->itoffset)
  42                 return 1;
  43 
  44         for (i=0;i<fs->numgroups;i++)
  45         {
  46                 blk_t diff;
  47                 blk_t j;
  48                 blk_t fromblock;
  49                 blk_t start;
  50 
  51                 start = (i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
  52                         + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
  53 
  54                 if (EXT2_GROUP_INODE_TABLE(fs->gd[i]) >= start + newitoffset
  55                     && EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) >= start + newitoffset - 2
  56                     && EXT2_GROUP_INODE_BITMAP(fs->gd[i]) >= start + newitoffset - 1)
  57                         continue;
  58 
  59                 diff = newitoffset - (EXT2_GROUP_INODE_TABLE(fs->gd[i]) - start);
  60 
  61                 /* inode table */
  62                 fromblock = EXT2_GROUP_INODE_TABLE(fs->gd[i]) + fs->inodeblocks;
  63 
  64                 if (fs->opt_debug)
  65                 {
  66                         for (j=0;j<diff;j++)
  67                                 if (!ext2_get_block_state(fs, fromblock+j))
  68                                 {
  69                                         fprintf(stderr,
  70                                                 "error: block relocator "
  71                                                 "should have relocated "
  72                                                 "%i\n",
  73                                                 fromblock);
  74 
  75                                         return 0;
  76                                 }
  77                 }
  78 
  79                 for (j=0;j<diff;j++)
  80                         if (!ext2_set_block_state(fs, fromblock+j, 1, 0))
  81                                 return 0;
  82 
  83                 if (!ext2_move_blocks(fs,
  84                                       EXT2_GROUP_INODE_TABLE(fs->gd[i]),
  85                                       fs->inodeblocks,
  86                                       EXT2_GROUP_INODE_TABLE(fs->gd[i]) + diff))
  87                         return 0;
  88                 fs->gd[i].bg_inode_table = PED_CPU_TO_LE32 (
  89                         EXT2_GROUP_INODE_TABLE(fs->gd[i]) + diff);
  90                 fs->metadirty |= EXT2_META_GD;
  91 
  92                 if (fs->opt_safe)
  93                         if (!ext2_sync(fs))
  94                                 return 0;
  95 
  96                 /* block bitmap and inode bitmap */
  97                 fromblock = EXT2_GROUP_INODE_TABLE(fs->gd[i]);
  98                 if (ext2_is_group_sparse(fs, i))
  99                 {
 100                         if (!ext2_copy_block(fs,
 101                                 EXT2_GROUP_INODE_BITMAP(fs->gd[i]),
 102                                 EXT2_GROUP_INODE_BITMAP(fs->gd[i]) + diff))
 103                                 return 0;
 104                         fs->gd[i].bg_inode_bitmap = PED_CPU_TO_LE32 (
 105                                 EXT2_GROUP_INODE_BITMAP(fs->gd[i]) + diff);
 106                         fs->metadirty |= EXT2_META_GD;
 107 
 108                         if (fs->opt_safe)
 109                                 if (!ext2_sync(fs))
 110                                         return 0;
 111 
 112                         if (!ext2_copy_block(fs,
 113                                 EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]),
 114                                 EXT2_GROUP_BLOCK_BITMAP(fs->gd[i])+diff))
 115                                 return 0;
 116                         fs->gd[i].bg_block_bitmap = PED_CPU_TO_LE32 (
 117                                 EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) + diff);
 118                         fs->metadirty |= EXT2_META_GD;
 119 
 120                         if (fs->opt_safe)
 121                                 if (!ext2_sync(fs))
 122                                         return 0;
 123 
 124                         fromblock = EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]);
 125                 }
 126 
 127                 ext2_zero_blocks(fs, fromblock-diff, diff);
 128                 for (j=0;j<diff;j++)
 129                         if (!ext2_set_block_state(fs, fromblock+j-diff, 0, 0))
 130                                 return 0;
 131 
 132                 if (fs->opt_verbose)
 133                         fprintf(stderr,
 134                                 "ext2_metadata_push: group %i/%i\r",
 135                                 i+1, fs->numgroups);
 136         }
 137 
 138         fs->itoffset = newitoffset;
 139 
 140         if (fs->opt_verbose)
 141                 fputc ('\n', stderr);
 142 
 143         return 1;
 144 }
 145 #endif /* !DISCOVER_ONLY */