Logo Search packages:      
Sourcecode: parted version File versions

resize.c

/*
    libparted
    Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "fat.h"
#include "traverse.h"
#include "count.h"
#include "fatio.h"
#include "calc.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>

#ifndef DISCOVER_ONLY

/* Recursively builds (i.e. makes consistent) the duplicated directory tree
 * (leaving the original directory tree in tact)
 */
static int
fat_construct_directory (FatOpContext* ctx, FatTraverseInfo* trav_info)
{
      FatTraverseInfo*  sub_dir_info;
      FatDirEntry*            dir_entry;
      FatCluster        old_first_cluster;

      while ( (dir_entry = fat_traverse_next_dir_entry (trav_info)) ) {
            if (fat_dir_entry_is_null_term (dir_entry))
                  break;
            if (!fat_dir_entry_has_first_cluster (dir_entry, ctx->old_fs))
                  continue;

            fat_traverse_mark_dirty (trav_info);

            old_first_cluster = fat_dir_entry_get_first_cluster (dir_entry,
                                    ctx->old_fs);
            fat_dir_entry_set_first_cluster (dir_entry, ctx->new_fs,
                  fat_op_context_map_cluster (ctx, old_first_cluster));

            if (fat_dir_entry_is_directory (dir_entry)
                        && dir_entry->name [0] != '.') {
                  sub_dir_info
                        = fat_traverse_directory (trav_info, dir_entry);
                  if (!sub_dir_info)
                        return 0;
                  if (!fat_construct_directory (ctx, sub_dir_info))
                        return 0;
            }
      }
      /* remove "stale" entries at the end */
      while ((dir_entry = fat_traverse_next_dir_entry (trav_info))) {
            memset (dir_entry, 0, sizeof (FatDirEntry));
            fat_traverse_mark_dirty (trav_info);
      }
      fat_traverse_complete (trav_info);
      return 1;
}

static int
duplicate_legacy_root_dir (FatOpContext* ctx)
{
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);

      PED_ASSERT (old_fs_info->root_dir_sector_count
                  == new_fs_info->root_dir_sector_count, return 0);

      if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer,
                        old_fs_info->root_dir_offset,
                        old_fs_info->root_dir_sector_count))
            return 0;

      if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer,
                         new_fs_info->root_dir_offset,
                         new_fs_info->root_dir_sector_count))
            return 0;

      return 1;
}

/*
    Constructs the new directory tree for legacy (FAT16) filesystems.
*/
static int
fat_construct_legacy_root (FatOpContext* ctx)
{
      FatTraverseInfo*  trav_info;

      if (!duplicate_legacy_root_dir (ctx))
            return 0;
      trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, "\\");
      return fat_construct_directory (ctx, trav_info);
}

/*
    Constructs the new directory tree for new (FAT32) filesystems.
*/
static int
fat_construct_root (FatOpContext* ctx)
{
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      FatTraverseInfo*  trav_info;

      trav_info = fat_traverse_begin (ctx->new_fs, new_fs_info->root_cluster,
                              "\\");
      fat_construct_directory (ctx, trav_info);
      return 1;
}

/* Converts the root directory between FAT16 and FAT32.  NOTE: this code
 * can also do no conversion.  I'm leaving fat_construct_directory(), because
 * it's really pretty :-)  It also leaves a higher chance of deleted file
 * recovery, because it doesn't remove redundant entries.  (We do this here,
 * because brain-damaged FAT16 has an arbitary limit on root directory entries,
 * so we save room)
 */
static int
fat_convert_directory (FatOpContext* ctx, FatTraverseInfo* old_trav,
                   FatTraverseInfo* new_trav)
{
      FatTraverseInfo*  sub_old_dir_trav;
      FatTraverseInfo*  sub_new_dir_trav;
      FatDirEntry*            new_dir_entry;
      FatDirEntry*            old_dir_entry;
      FatCluster        old_first_cluster;

      while ( (old_dir_entry = fat_traverse_next_dir_entry (old_trav)) ) {
            if (fat_dir_entry_is_null_term (old_dir_entry))
                  break;
            if (!fat_dir_entry_is_active (old_dir_entry))
                  continue;

            new_dir_entry = fat_traverse_next_dir_entry (new_trav);
            if (!new_dir_entry) {
                  return ped_exception_throw (PED_EXCEPTION_ERROR,
                        PED_EXCEPTION_IGNORE_CANCEL,
                        _("There's not enough room in the root "
                          "directory, for all of the files.  Either "
                          "cancel, or ignore to lose the files."))
                              == PED_EXCEPTION_IGNORE;
            }

            *new_dir_entry = *old_dir_entry;
            fat_traverse_mark_dirty (new_trav);

            if (!fat_dir_entry_has_first_cluster (old_dir_entry,
                                          ctx->old_fs))
                  continue;

            old_first_cluster = fat_dir_entry_get_first_cluster (
                                    old_dir_entry, ctx->old_fs);
            fat_dir_entry_set_first_cluster (new_dir_entry, ctx->new_fs,
                  fat_op_context_map_cluster (ctx, old_first_cluster));

            if (fat_dir_entry_is_directory (old_dir_entry)
                        && old_dir_entry->name [0] != '.') {
                  sub_old_dir_trav
                      = fat_traverse_directory (old_trav, old_dir_entry);
                  sub_new_dir_trav
                      = fat_traverse_directory (new_trav, new_dir_entry);
                  if (!sub_old_dir_trav || !sub_new_dir_trav)
                        return 0;

                  if (!fat_convert_directory (ctx, sub_old_dir_trav,
                                        sub_new_dir_trav))
                        return 0;
            }
      }

      /* remove "stale" entries at the end, just in case there is some
       * overlap
       */
      while ((new_dir_entry = fat_traverse_next_dir_entry (new_trav))) {
            memset (new_dir_entry, 0, sizeof (FatDirEntry));
            fat_traverse_mark_dirty (new_trav);
      }

      fat_traverse_complete (old_trav);
      fat_traverse_complete (new_trav);
      return 1;
}

static void
clear_cluster (PedFileSystem* fs, FatCluster cluster)
{
      FatSpecific*            fs_info = FAT_SPECIFIC (fs);

      memset (fs_info->buffer, 0, fs_info->cluster_size);
      fat_write_cluster (fs, fs_info->buffer, cluster);
}

/* This MUST be called BEFORE the fat_construct_new_fat(), because cluster
 * allocation depend on the old FAT.  The reason is, old clusters may
 * still be needed during the resize, (particularly clusters in the directory
 * tree) even if they will be discarded later.
 */
static int
alloc_root_dir (FatOpContext* ctx)
{
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      FatCluster        i;
      FatCluster        cluster;
      FatCluster        cluster_count;

      PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT32, return 0);

      cluster_count = ped_div_round_up (
                     PED_MAX (16, old_fs_info->root_dir_sector_count),
                     new_fs_info->cluster_sectors);

      for (i = 0; i < cluster_count; i++) {
            cluster = fat_table_alloc_check_cluster (new_fs_info->fat,
                                           ctx->new_fs);
            if (!cluster)
                  return 0;
            ctx->new_root_dir [i] = cluster;
            clear_cluster (ctx->new_fs, cluster);
      }
      ctx->new_root_dir [i] = 0;
      new_fs_info->root_cluster = ctx->new_root_dir [0];
      return 1;
}

/* when converting FAT32 -> FAT16
 * fat_duplicate clusters() duplicated the root directory unnecessarily.
 * Let's free it.
 *
 * This must be called AFTER fat_construct_new_fat().  (otherwise, our
 * changes just get overwritten)
 */
static int
free_root_dir (FatOpContext* ctx)
{
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      FatCluster        old_cluster;
      FatFragment       i;

      PED_ASSERT (old_fs_info->fat_type == FAT_TYPE_FAT32, return 0);
      PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT16, return 0);

      for (old_cluster = old_fs_info->root_cluster;
           !fat_table_is_eof (old_fs_info->fat, old_cluster);
           old_cluster = fat_table_get (old_fs_info->fat, old_cluster)) {
            FatFragment old_frag;
            old_frag = fat_cluster_to_frag (ctx->old_fs, old_cluster);
            for (i = 0; i < new_fs_info->cluster_frags; i++) {
                  FatFragment new_frag;
                  FatCluster new_clst;
                  new_frag = fat_op_context_map_fragment (ctx,
                                                old_frag + i);
                  new_clst = fat_frag_to_cluster (ctx->old_fs, new_frag);
                  if (!fat_table_set_avail (new_fs_info->fat, new_clst))
                        return 0;
            }
      }

      return 1;
}

static int
fat_clear_root_dir (PedFileSystem* fs)
{
      FatSpecific*      fs_info = FAT_SPECIFIC (fs);
      int         i;

      PED_ASSERT (fs_info->fat_type == FAT_TYPE_FAT16, return 0);
      PED_ASSERT (fs_info->root_dir_sector_count, return 0);

      memset (fs_info->buffer, 0, 512);

      for (i = 0; i < fs_info->root_dir_sector_count; i++) {
            if (!ped_geometry_write (fs->geom, fs_info->buffer,
                               fs_info->root_dir_offset + i, 1)) {
                  if (ped_exception_throw (PED_EXCEPTION_ERROR,
                        PED_EXCEPTION_IGNORE_CANCEL,
                        _("Error writing to the root directory."))
                              == PED_EXCEPTION_CANCEL)
                        return 0;
            }
      }
      return 1;
}

static int
fat_construct_converted_tree (FatOpContext* ctx)
{
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      FatTraverseInfo*  old_trav_info;
      FatTraverseInfo*  new_trav_info;

      if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
            new_trav_info = fat_traverse_begin (ctx->new_fs,
                                  new_fs_info->root_cluster, "\\");
            old_trav_info = fat_traverse_begin (ctx->old_fs, FAT_ROOT,
                                        "\\");
      } else {
            fat_clear_root_dir (ctx->new_fs);
            new_trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT,
                                        "\\");
            old_trav_info = fat_traverse_begin (ctx->old_fs,
                                  old_fs_info->root_cluster, "\\");
      }
      if (!new_trav_info || !old_trav_info)
            return 0;
      if (!fat_convert_directory (ctx, old_trav_info, new_trav_info))
            return 0;
      return 1;
}

/*
    Constructs the new directory tree to match the new file locations.
*/
static int
fat_construct_dir_tree (FatOpContext* ctx)
{
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);

      if (new_fs_info->fat_type == old_fs_info->fat_type) {
            switch (old_fs_info->fat_type) {
                  case FAT_TYPE_FAT16:
                  return fat_construct_legacy_root (ctx);

                  case FAT_TYPE_FAT32:
                  return fat_construct_root (ctx);
            }
      } else {
            return fat_construct_converted_tree (ctx);
      }

      return 0;
}

static FatFragment
_get_next_old_frag (FatOpContext* ctx, FatFragment frag)
{
      FatSpecific*      old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatCluster  cluster;
      FatCluster  next_cluster;

      if ((frag + 1) % old_fs_info->cluster_frags != 0) {
            if (fat_is_fragment_active (ctx->old_fs, frag + 1))
                  return frag + 1;
            else
                  return -1;
      } else {
            cluster = fat_frag_to_cluster (ctx->old_fs, frag);
            next_cluster = fat_table_get (old_fs_info->fat, cluster);

            if (fat_table_is_eof (old_fs_info->fat, next_cluster))
                  return -1;
            else
                  return fat_cluster_to_frag (ctx->old_fs, next_cluster);
      }
}

/*
    constructs the new fat for the resized filesystem.
*/
static int
fat_construct_new_fat (FatOpContext* ctx)
{
      FatSpecific*      old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*      new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      FatFragment old_frag;
      FatCluster  new_cluster;
      FatFragment new_frag;
      FatFragment old_next_frag;
      FatFragment new_next_frag;
      FatCluster  new_next_cluster;
      FatClusterFlag    flag;
      int         i;

      fat_table_clear (new_fs_info->fat);
      if (!fat_table_set_cluster_count (new_fs_info->fat,
                                new_fs_info->cluster_count))
            return 0;

      for (old_frag = 0; old_frag < old_fs_info->frag_count; old_frag++) {
            flag = fat_get_fragment_flag (ctx->old_fs, old_frag);
            if (flag == FAT_FLAG_FREE)
                  continue;
            if (flag == FAT_FLAG_BAD) {
                  new_frag = fat_op_context_map_static_fragment (
                                    ctx, old_frag);
                  if (new_frag == -1)
                        continue;
                  new_cluster = fat_frag_to_cluster (ctx->new_fs,
                                             new_frag);
                  fat_table_set_bad (new_fs_info->fat, new_cluster);
                  continue;
            }

            new_frag = fat_op_context_map_fragment (ctx, old_frag);
            new_cluster = fat_frag_to_cluster (ctx->new_fs, new_frag);

            old_next_frag = _get_next_old_frag (ctx, old_frag);
            if (old_next_frag == -1) {
                  fat_table_set_eof (new_fs_info->fat, new_cluster);
                  continue;
            }

            new_next_frag = fat_op_context_map_fragment (ctx,
                                               old_next_frag);
            PED_ASSERT (new_next_frag != -1, return 0);

            new_next_cluster = fat_frag_to_cluster (ctx->new_fs,
                                          new_next_frag);
            PED_ASSERT (new_next_cluster != new_cluster, return 0);

            fat_table_set (new_fs_info->fat, new_cluster, new_next_cluster);
      }

#if 0
#ifdef PED_VERBOSE
      for (old_cluster=2; old_cluster < old_fs_info->cluster_count+2;
           old_cluster++) {
            if (fat_table_is_available (old_fs_info->fat, old_cluster))
                  continue;

            printf ("%d->%d\t(next: %d->%d)\n",
                  old_cluster,
                  ctx->remap [old_cluster],
                  fat_table_get (old_fs_info->fat, old_cluster),
                  fat_table_get (new_fs_info->fat,
                               ctx->remap [old_cluster]));
      }
#endif /* PED_VERBOSE */
#endif

      if (old_fs_info->fat_type == FAT_TYPE_FAT32
          && new_fs_info->fat_type == FAT_TYPE_FAT32) {
            new_fs_info->root_cluster
                  = fat_op_context_map_cluster (ctx,
                              old_fs_info->root_cluster);
      }

      if (old_fs_info->fat_type == FAT_TYPE_FAT16
          && new_fs_info->fat_type == FAT_TYPE_FAT32) {
            for (i=0; ctx->new_root_dir[i+1]; i++) {
                  fat_table_set (new_fs_info->fat,
                               ctx->new_root_dir[i],
                               ctx->new_root_dir[i+1]);
            }
            fat_table_set_eof (new_fs_info->fat, ctx->new_root_dir[i]);
      }

      return 1;
}

static int
ask_type (PedFileSystem* fs, int fat16_ok, int fat32_ok, FatType* out_fat_type)
{
      FatSpecific*            fs_info = FAT_SPECIFIC (fs);
      PedExceptionOption      status;
      char*             fat16_msg;
      char*             fat32_msg;

      if (fs_info->fat_type == FAT_TYPE_FAT16)
            fat16_msg = _("If leave your file system as FAT16, "
                        "then you will have no problems.");
      else
            fat16_msg = _("If you convert to FAT16, and MS Windows "
                        "is installed on this partition, then "
                        "you must re-install the MS Windows boot "
                        "loader.  If you want to do this, you "
                        "should consult the Parted manual (or "
                        "your distribution's manual).");

      if (fs_info->fat_type == FAT_TYPE_FAT32)
            fat32_msg = _("If you leave your file system as FAT32, "
                        "then you will not introduce any new "
                        "problems.");
      else
            fat32_msg = _("If you convert to FAT32, and MS Windows "
                        "is installed on this partition, then "
                        "you must re-install the MS Windows boot "
                        "loader.  If you want to do this, you "
                        "should consult the Parted manual (or "
                        "your distribution's manual).  Also, "
                        "converting to FAT32 will make the file "
                        "system unreadable by MS DOS, MS Windows "
                        "95a, and MS Windows NT.");

      if (fat16_ok && fat32_ok) {
            status = ped_exception_throw (
                   PED_EXCEPTION_INFORMATION,
                   PED_EXCEPTION_YES_NO_CANCEL,
                   _("%s  %s  %s"),
                   _("Would you like to use FAT32?"),
                   fat16_msg,
                   fat32_msg);

            switch (status) {
            case PED_EXCEPTION_YES:
                  *out_fat_type = FAT_TYPE_FAT32;
                  return 1;

            case PED_EXCEPTION_NO:
                  *out_fat_type = FAT_TYPE_FAT16;
                  return 1;

            case PED_EXCEPTION_UNHANDLED:
                  *out_fat_type = fs_info->fat_type;
                  return 1;

            case PED_EXCEPTION_CANCEL:
                  return 0;
            }
      }

      if (fat16_ok) {
            if (fs_info->fat_type != FAT_TYPE_FAT16) {
                  status = ped_exception_throw (
                        PED_EXCEPTION_WARNING,
                        PED_EXCEPTION_OK_CANCEL,
                        _("%s  %s"),
                        _("The file system can only be resized to this "
                          "size by converting to FAT16."),
                        fat16_msg);
                  if (status == PED_EXCEPTION_CANCEL)
                        return 0;
            }
            *out_fat_type = FAT_TYPE_FAT16;
            return 1;
      }

      if (fat32_ok) {
            if (fs_info->fat_type != FAT_TYPE_FAT32) {
                  status = ped_exception_throw (
                        PED_EXCEPTION_WARNING,
                        PED_EXCEPTION_OK_CANCEL,
                        _("%s  %s"),
                        _("The file system can only be resized to this "
                          "size by converting to FAT32."),
                        fat32_msg);
                  if (status == PED_EXCEPTION_CANCEL)
                        return 0;
            }
            *out_fat_type = FAT_TYPE_FAT32;
            return 1;
      }
      
      ped_exception_throw (
            PED_EXCEPTION_NO_FEATURE,
            PED_EXCEPTION_CANCEL,
            _("GNU Parted can not resize this partition to this size.  "
            "We're working on it!"));

      return 0;
}

/*  For resize operations: determine if the file system must be FAT16 or FAT32,
 *  or either.  If the new file system must be FAT32, then query for
 *  confirmation.  If either filesystem can be used, query for which one.
 */
static int
get_fat_type (PedFileSystem* fs, const PedGeometry* new_geom,
            FatType* out_fat_type)
{
      FatSpecific*            fs_info = FAT_SPECIFIC (fs);
      PedExceptionOption      status;
      PedSector         fat16_cluster_sectors;
      PedSector         fat32_cluster_sectors;
      FatCluster        dummy_cluster_count;
      PedSector         dummy_fat_sectors;
      int               fat16_ok;
      int               fat32_ok;

      fat16_ok = fat_calc_resize_sizes (
                            new_geom,
                            fs_info->cluster_sectors,
                            FAT_TYPE_FAT16,
                            fs_info->root_dir_sector_count,
                            fs_info->cluster_sectors,
                            &fat16_cluster_sectors,
                            &dummy_cluster_count,
                            &dummy_fat_sectors);

      fat32_ok = fat_calc_resize_sizes (
                            new_geom,
                            fs_info->cluster_sectors,
                            FAT_TYPE_FAT32,
                            fs_info->root_dir_sector_count,
                            fs_info->cluster_sectors,
                            &fat32_cluster_sectors,
                            &dummy_cluster_count,
                            &dummy_fat_sectors);

      return ask_type (fs, fat16_ok, fat32_ok, out_fat_type);
}

/*  Creates the PedFileSystem struct for the new resized file system, and
    sticks it in a FatOpContext.  At the end of the process, the original
    (ctx->old_fs) is destroyed, and replaced with the new one (ctx->new_fs).
 */
static FatOpContext*
create_resize_context (PedFileSystem* fs, const PedGeometry* new_geom)
{
      FatSpecific*      fs_info = FAT_SPECIFIC (fs);
      FatSpecific*      new_fs_info;
      PedFileSystem*    new_fs;
      PedSector   new_cluster_sectors;
      FatCluster  new_cluster_count;
      PedSector   new_fat_sectors;
      FatType           new_fat_type;
      PedSector   root_dir_sector_count;
      FatOpContext*     context;

      /* hypothetical number of root dir sectors, if we end up using
       * FAT16
       */
      if (fs_info->root_dir_sector_count)
            root_dir_sector_count = fs_info->root_dir_sector_count;
      else
            root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT
                                    * sizeof (FatDirEntry) / 512;

      if (!get_fat_type (fs, new_geom, &new_fat_type))
            return 0;

      fat_calc_resize_sizes (new_geom, fs_info->cluster_sectors, new_fat_type,
            root_dir_sector_count, fs_info->cluster_sectors,
            &new_cluster_sectors, &new_cluster_count, &new_fat_sectors);

      if (!fat_check_resize_geometry (fs, new_geom, new_cluster_sectors,
                                new_cluster_count))
            goto error;

      new_fs = fat_alloc (new_geom);
      if (!new_fs)
            goto error;

      new_fs_info = FAT_SPECIFIC (new_fs);
      if (!new_fs_info)
            goto error_free_new_fs;

/* preserve boot code, etc. */
      memcpy (&new_fs_info->boot_sector, &fs_info->boot_sector,
            sizeof (FatBootSector));
      memcpy (&new_fs_info->info_sector, &fs_info->info_sector,
            sizeof (FatInfoSector));

      new_fs_info->logical_sector_size = fs_info->logical_sector_size;
      new_fs_info->sector_count = new_geom->length;

      new_fs_info->sectors_per_track = fs_info->sectors_per_track;
      new_fs_info->heads = fs_info->heads;

      new_fs_info->cluster_size = new_cluster_sectors * 512;
      new_fs_info->cluster_sectors = new_cluster_sectors;
      new_fs_info->cluster_count = new_cluster_count;
      new_fs_info->dir_entries_per_cluster = fs_info->dir_entries_per_cluster;

      new_fs_info->fat_type = new_fat_type; 
      new_fs_info->fat_table_count = 2;
      new_fs_info->fat_sectors = new_fat_sectors;

      /* what about copying? */
      new_fs_info->serial_number = fs_info->serial_number;

      if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
            new_fs_info->info_sector_offset     = 1;
            new_fs_info->boot_sector_backup_offset = 6;

            new_fs_info->root_dir_offset = 0;
            new_fs_info->root_dir_entry_count = 0;
            new_fs_info->root_dir_sector_count = 0;

            /* we add calc_align_sectors to push the cluster_offset
               forward, to keep the clusters aligned between the new
               and old file systems
             */
            new_fs_info->fat_offset
                  = fat_min_reserved_sector_count (FAT_TYPE_FAT32)
                    + fat_calc_align_sectors (new_fs, fs);

            new_fs_info->cluster_offset
                  = new_fs_info->fat_offset
                    + 2 * new_fs_info->fat_sectors;
      } else {
            new_fs_info->root_dir_sector_count = root_dir_sector_count;
            new_fs_info->root_dir_entry_count 
                  = root_dir_sector_count * 512 / sizeof (FatDirEntry);

            new_fs_info->fat_offset
                  = fat_min_reserved_sector_count (FAT_TYPE_FAT16)
                    + fat_calc_align_sectors (new_fs, fs);

            new_fs_info->root_dir_offset = new_fs_info->fat_offset
                                     + 2 * new_fs_info->fat_sectors;

            new_fs_info->cluster_offset = new_fs_info->root_dir_offset
                                + new_fs_info->root_dir_sector_count;
      }
      
      new_fs_info->total_dir_clusters = fs_info->total_dir_clusters;

      context = fat_op_context_new (new_fs, fs);
      if (!context)
            goto error_free_new_fs_info;

      if (!fat_op_context_create_initial_fat (context))
            goto error_free_context;

      if (!fat_alloc_buffers (new_fs))
            goto error_free_fat;

      return context;

error_free_fat:
      fat_table_destroy (new_fs_info->fat);
error_free_context:
      ped_free (context);
error_free_new_fs_info:
      ped_free (new_fs_info);
error_free_new_fs:
      ped_free (new_fs);
error:
      return NULL;
}

static int
resize_context_assimilate (FatOpContext* ctx)
{
      FatSpecific*      old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*      new_fs_info = FAT_SPECIFIC (ctx->new_fs);

      fat_free_buffers (ctx->old_fs);
      fat_table_destroy (old_fs_info->fat);
      ped_free (old_fs_info);
      ped_geometry_destroy (ctx->old_fs->geom);

      ctx->old_fs->type_specific = ctx->new_fs->type_specific;
      ctx->old_fs->geom = ctx->new_fs->geom;
      ctx->old_fs->type = (new_fs_info->fat_type == FAT_TYPE_FAT16)
                        ? &fat16_type
                              : &fat32_type;

      ped_free (ctx->new_fs);

      fat_op_context_destroy (ctx);

      return 1;
}

static int
resize_context_abort (FatOpContext* ctx)
{
      FatSpecific*      new_fs_info = FAT_SPECIFIC (ctx->new_fs);

      fat_free_buffers (ctx->new_fs);
      fat_table_destroy (new_fs_info->fat);
      ped_free (new_fs_info);
      ped_geometry_destroy (ctx->new_fs->geom);
      ped_free (ctx->new_fs);

      fat_op_context_destroy (ctx);

      return 1;
}

/* copies the "hidden" sectors, between the boot sector and the FAT.  Required,
 * for the Windows 98 FAT32 boot loader
 */
int
_copy_hidden_sectors (FatOpContext* ctx)
{
      FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      PedSector       first = 1;
      PedSector       last;
      PedSector       count;

      /* nothing to copy for FAT16 */
      if (old_fs_info->fat_type == FAT_TYPE_FAT16
                  || new_fs_info->fat_type == FAT_TYPE_FAT16)
            return 1;

      last = PED_MIN (old_fs_info->fat_offset, new_fs_info->fat_offset) - 1;
      count = last - first + 1;

      PED_ASSERT (count < BUFFER_SIZE, return 0);

      if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer,
                        first, count))
            return 0;
      if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer,
                         first, count))
            return 0;
      return 1;
}

int
fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
{
      FatSpecific*      fs_info = FAT_SPECIFIC (fs);
      FatSpecific*      new_fs_info;
      FatOpContext*     ctx;
      PedFileSystem*    new_fs;

      ctx = create_resize_context (fs, geom);
      if (!ctx)
            goto error;
      new_fs = ctx->new_fs;
      new_fs_info = FAT_SPECIFIC (new_fs);

      if (!fat_duplicate_clusters (ctx, timer))
            goto error_abort_ctx;
      if (fs_info->fat_type == FAT_TYPE_FAT16
                  && new_fs_info->fat_type == FAT_TYPE_FAT32) {
            if (!alloc_root_dir (ctx))
                  goto error_abort_ctx;
      }
      if (!fat_construct_new_fat (ctx))
            goto error_abort_ctx;
      if (fs_info->fat_type == FAT_TYPE_FAT32
                  && new_fs_info->fat_type == FAT_TYPE_FAT16) {
            if (!free_root_dir (ctx))
                  goto error_abort_ctx;
      }
      if (!fat_construct_dir_tree (ctx))
            goto error_abort_ctx;
      if (!fat_table_write_all (new_fs_info->fat, new_fs))
            goto error_abort_ctx;

      _copy_hidden_sectors (ctx);
      fat_boot_sector_generate (&new_fs_info->boot_sector, new_fs);
      fat_boot_sector_write (&new_fs_info->boot_sector, new_fs);
      if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
            fat_info_sector_generate (&new_fs_info->info_sector, new_fs);
            fat_info_sector_write (&new_fs_info->info_sector, new_fs);
      }

      if (!resize_context_assimilate (ctx))
            goto error;

      return 1;

error_abort_ctx:
      resize_context_abort (ctx);
error:
      return 0;
}

#endif /* !DISCOVER_ONLY */

Generated by  Doxygen 1.6.0   Back to index