1 /*
   2     libparted
   3     Copyright (C) 1998, 1999, 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 #include <string.h>
  21 
  22 #include "fat.h"
  23 
  24 #ifndef DISCOVER_ONLY
  25 
  26 /* Note: this deals with file system start and end sectors, even if the physical
  27  * devices are different (eg for fat_copy())  Perhaps this is a hack, but it
  28  * works ;-)
  29  */
  30 static int
  31 calc_deltas (FatOpContext* ctx)
  32 {
  33         PedFileSystem*  old_fs = ctx->old_fs;
  34         PedFileSystem*  new_fs = ctx->new_fs;
  35         FatSpecific*    old_fs_info = FAT_SPECIFIC (old_fs);
  36         FatSpecific*    new_fs_info = FAT_SPECIFIC (new_fs);
  37         PedSector       old_cluster_ofs;
  38         PedSector       new_cluster_ofs;
  39         PedSector       sector_delta;
  40 
  41         old_cluster_ofs = old_fs->geom->start + old_fs_info->cluster_offset;
  42         new_cluster_ofs = new_fs->geom->start + new_fs_info->cluster_offset;
  43 
  44         if (new_cluster_ofs > old_cluster_ofs) {
  45                 ctx->start_move_dir = FAT_DIR_FORWARD;
  46                 sector_delta = new_cluster_ofs - old_cluster_ofs;
  47         } else {
  48                 ctx->start_move_dir = FAT_DIR_BACKWARD;
  49                 sector_delta = old_cluster_ofs - new_cluster_ofs;
  50         }
  51 
  52         if (sector_delta % new_fs_info->cluster_sectors) {
  53                 ped_exception_throw (
  54                         PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
  55                         _("Cluster start delta = %d, which is not a multiple "
  56                           "of the cluster size %d."),
  57                         (int) sector_delta,
  58                         (int) new_fs_info->cluster_sectors);
  59                 return 0;
  60         }
  61         
  62         ctx->start_move_delta = sector_delta / ctx->frag_sectors;
  63 
  64 #ifdef PED_VERBOSE
  65         printf ("Start move delta is: %d %s.\n",
  66                 (int) ctx->start_move_delta,
  67                 (ctx->start_move_dir == FAT_DIR_FORWARD)?
  68                         "forwards" : "backwards");
  69 #endif
  70 
  71         return 1;
  72 }
  73 
  74 FatOpContext*
  75 fat_op_context_new (PedFileSystem* new_fs, PedFileSystem* old_fs)
  76 {
  77         FatSpecific*    old_fs_info = FAT_SPECIFIC (old_fs);
  78         FatSpecific*    new_fs_info = FAT_SPECIFIC (new_fs);
  79         FatOpContext*   ctx;
  80 
  81         ctx = (FatOpContext*) ped_malloc (sizeof (FatOpContext));
  82         if (!ctx)
  83                 goto error;
  84 
  85         ctx->frag_sectors = PED_MIN (old_fs_info->cluster_sectors,
  86                                      new_fs_info->cluster_sectors);
  87         if (!fat_set_frag_sectors (new_fs, ctx->frag_sectors))
  88                 goto error;
  89         if (!fat_set_frag_sectors (old_fs, ctx->frag_sectors))
  90                 goto error;
  91 
  92         ctx->buffer_frags = old_fs_info->buffer_sectors / ctx->frag_sectors;
  93         ctx->buffer_map = (FatFragment*) ped_malloc (sizeof (FatFragment)
  94                                                      * ctx->buffer_frags);
  95         if (!ctx->buffer_map)
  96                 goto error_free_ctx;
  97 
  98         ctx->remap = (FatFragment*) ped_malloc (sizeof (FatFragment)
  99                                                    * old_fs_info->frag_count);
 100         if (!ctx->remap)
 101                 goto error_free_buffer_map;
 102 
 103         ctx->new_fs = new_fs;
 104         ctx->old_fs = old_fs;
 105         if (!calc_deltas (ctx))
 106                 goto error_free_buffer_map;
 107 
 108         return ctx;
 109 
 110 error_free_buffer_map:
 111         ped_free (ctx->buffer_map);
 112 error_free_ctx:
 113         ped_free (ctx);
 114 error:
 115         return NULL;
 116 }
 117 
 118 void
 119 fat_op_context_destroy (FatOpContext* ctx)
 120 {
 121         ped_free (ctx->buffer_map);
 122         ped_free (ctx->remap);
 123         ped_free (ctx);
 124 }
 125 
 126 FatFragment
 127 fat_op_context_map_static_fragment (const FatOpContext* ctx, FatFragment frag)
 128 {
 129         FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
 130         FatFragment     result;
 131 
 132         if (ctx->new_fs->geom->dev != ctx->old_fs->geom->dev)
 133                 return -1;
 134 
 135         if (ctx->start_move_dir == FAT_DIR_FORWARD) {
 136                 if (frag < ctx->start_move_delta)
 137                         return -1;
 138                 result = frag - ctx->start_move_delta;
 139         } else {
 140                 result = frag + ctx->start_move_delta;
 141         }
 142 
 143         if (result >= new_fs_info->frag_count)
 144                 return -1;
 145 
 146         return result;
 147 }
 148 
 149 FatCluster
 150 fat_op_context_map_static_cluster (const FatOpContext* ctx, FatCluster clst)
 151 {
 152         FatFragment     mapped_frag;
 153 
 154         mapped_frag = fat_op_context_map_static_fragment (ctx,
 155                                 fat_cluster_to_frag (ctx->old_fs, clst)); 
 156         if (mapped_frag != -1)
 157                 return fat_frag_to_cluster (ctx->new_fs, mapped_frag);
 158         else
 159                 return 0;
 160 }
 161 
 162 FatFragment
 163 fat_op_context_map_fragment (const FatOpContext* ctx, FatFragment frag)
 164 {
 165         return ctx->remap [frag];
 166 }
 167 
 168 FatCluster
 169 fat_op_context_map_cluster (const FatOpContext* ctx, FatCluster clst)
 170 {
 171         FatFragment     mapped_frag;
 172 
 173         mapped_frag = fat_op_context_map_fragment (ctx,
 174                                 fat_cluster_to_frag (ctx->old_fs, clst)); 
 175         if (mapped_frag != -1)
 176                 return fat_frag_to_cluster (ctx->new_fs, mapped_frag);
 177         else
 178                 return 0;
 179 }
 180 
 181 /* This function sets the initial fat for the new resized file system.
 182    This is in *NO WAY* a proper FAT table - all it does is:
 183         a) mark bad clusters as bad.
 184         b) mark used clusters (that is, clusters from the original FS that are
 185            reachable from the resized one).  Marks as EOF (i.e. used, end of
 186            file chain).
 187         c) mark original file system metadata as EOF (i.e. used), to prevent
 188            it from being clobbered.  This will leave the original file system
 189            intact, until the partition table is modified, if the start of
 190            the partition is moved.
 191 
 192    The FATs are rebuilt *properly* after cluster relocation.  This here is
 193    only to mark clusters as used, so when cluster relocation occurs, clusters
 194    aren't relocated on top of ones marked in a, b or c.
 195 */
 196 int
 197 fat_op_context_create_initial_fat (FatOpContext* ctx)
 198 {
 199         FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
 200         FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
 201         FatCluster      clst;
 202         FatCluster      new_clst;
 203         PedSector       sect;
 204         PedSector       new_sect;
 205         FatFragment     frag;
 206         FatFragment     new_frag;
 207         FatClusterFlag  frag_flag;
 208 
 209         new_fs_info->fat = fat_table_new (
 210                 new_fs_info->fat_type,
 211                 new_fs_info->fat_sectors * 512
 212                         / fat_table_entry_size (new_fs_info->fat_type));
 213         if (!new_fs_info->fat)
 214                 return 0;
 215 
 216         if (!fat_table_set_cluster_count (new_fs_info->fat,
 217                                           new_fs_info->cluster_count))
 218                 return 0;
 219 
 220 /* mark bad and used clusters */
 221         for (frag = 0; frag < old_fs_info->frag_count; frag++) {
 222                 frag_flag = fat_get_fragment_flag (ctx->old_fs, frag);
 223                 if (frag_flag == FAT_FLAG_FREE)
 224                         continue;
 225 
 226                 new_frag = fat_op_context_map_static_fragment (ctx, frag);
 227                 if (new_frag == -1)
 228                         continue;
 229 
 230                 new_clst = fat_frag_to_cluster (ctx->new_fs, new_frag);
 231                 PED_ASSERT (new_clst != 0, return 0);
 232 
 233                 if (frag_flag == FAT_FLAG_BAD) {
 234                         if (!fat_table_set_bad (new_fs_info->fat, new_clst))
 235                                 return 0;
 236                 } else {
 237                         if (!fat_table_set_eof (new_fs_info->fat, new_clst))
 238                                 return 0;
 239                 }
 240         }
 241 
 242 /* mark metadata regions that map to clusters on the new FS */
 243         for (sect = 0; sect < old_fs_info->cluster_offset; sect++) {
 244                 new_sect = ped_geometry_map (ctx->new_fs->geom,
 245                                              ctx->old_fs->geom, sect);
 246                 if (new_sect == -1
 247                     || !fat_is_sector_in_clusters (ctx->new_fs, new_sect))
 248                         continue;
 249 
 250                 clst = fat_sector_to_cluster (ctx->new_fs, new_sect);
 251                 PED_ASSERT (clst != 0, return 0);
 252 
 253                 if (!fat_table_set_eof (new_fs_info->fat, clst))
 254                         return 0;
 255         }
 256 
 257         return 1;
 258 }
 259 
 260 #endif /* !DISCOVER_ONLY */