| /* |
| libparted |
| Copyright (C) 1998, 1999, 2000, 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <config.h> |
| #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) file systems. |
| */ |
| 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) file systems. |
| */ |
| 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_FAT12: |
| PED_ASSERT (0, (void) 0); |
| break; |
| |
| 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 file system. |
| */ |
| 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 you 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; |
| |
| default: |
| PED_ASSERT (0, (void) 0); |
| break; |
| } |
| } |
| |
| 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 cannot 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 file system 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); |
| 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 */ |