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; }
/* 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); 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; }
/* 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); PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT16); 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; }
/* 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); 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; }
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; }
/* 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); }
static int read_next_dir_buffer (FatTraverseInfo* trav_info) { FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); PED_ASSERT (!trav_info->is_legacy_root_dir); trav_info->this_buffer = trav_info->next_buffer; if (trav_info->this_buffer < 2 || trav_info->this_buffer >= fs_info->cluster_count + 2) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, "Cluster %ld in directory %s is outside file system!", (long) trav_info->this_buffer, trav_info->dir_name); return 0; } trav_info->next_buffer = fat_table_get (fs_info->fat, trav_info->this_buffer); return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries, trav_info->this_buffer); }
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); }
FatFragment fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); return (cluster - 2) * fs_info->cluster_frags; }
/* returns 1 if there are no more directory entries in the directory being * traversed, 0 otherwise. */ static int is_last_buffer (FatTraverseInfo* trav_info) { FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); if (trav_info->is_legacy_root_dir) return 1; else return fat_table_is_eof (fs_info->fat, trav_info->next_buffer); }
PedSector fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); return frag * fs_info->frag_sectors + fs_info->cluster_offset; }
FatFragment fat_sector_to_frag (const PedFileSystem* fs, PedSector sector) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (sector >= fs_info->cluster_offset); return (sector - fs_info->cluster_offset) / fs_info->frag_sectors; }
int fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector) { FatSpecific* fs_info = FAT_SPECIFIC (fs); return sector >= fs_info->cluster_offset && sector < fs_info->cluster_offset + fs_info->cluster_sectors * fs_info->cluster_count; }
FatCluster fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); return frag / fs_info->cluster_frags + 2; }
/* Calculates the number of sectors needed to be added to cluster_offset, to make the cluster on the new file system match up with the ones on the old file system. However, some space is reserved by fat_calc_resize_sizes() and friends, to allow room for this space. If too much of this space is left over, everyone will complain, so we have to be greedy, and use it all up... */ PedSector fat_calc_align_sectors (const PedFileSystem* new_fs, const PedFileSystem* old_fs) { FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); PedSector raw_old_meta_data_end; PedSector new_meta_data_size; PedSector min_new_meta_data_end; PedSector new_data_size; PedSector new_clusters_size; PedSector align; new_meta_data_size = fat_min_reserved_sector_count (new_fs_info->fat_type) + new_fs_info->fat_sectors * 2; if (new_fs_info->fat_type == FAT_TYPE_FAT16) new_meta_data_size += new_fs_info->root_dir_sector_count; raw_old_meta_data_end = old_fs->geom->start + old_fs_info->cluster_offset; min_new_meta_data_end = new_fs->geom->start + new_meta_data_size; if (raw_old_meta_data_end > min_new_meta_data_end) align = (raw_old_meta_data_end - min_new_meta_data_end) % new_fs_info->cluster_sectors; else align = (new_fs_info->cluster_sectors - ( (min_new_meta_data_end - raw_old_meta_data_end) % new_fs_info->cluster_sectors )) % new_fs_info->cluster_sectors; new_data_size = new_fs->geom->length - new_meta_data_size; new_clusters_size = new_fs_info->cluster_count * new_fs_info->cluster_sectors; while (new_clusters_size + align + new_fs_info->cluster_sectors <= new_data_size) align += new_fs_info->cluster_sectors; return align; }
FatCluster fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (sector >= fs_info->cluster_offset); return (sector - fs_info->cluster_offset) / fs_info->cluster_sectors + 2; }
PedSector fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); return (cluster - 2) * fs_info->cluster_sectors + fs_info->cluster_offset; }
FatTraverseInfo* fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster, const char* dir_name) { FatSpecific* fs_info = FAT_SPECIFIC (fs); FatTraverseInfo* trav_info; trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo)); if (!trav_info) goto error; trav_info->dir_name = strdup (dir_name); if (!trav_info->dir_name) goto error_free_trav_info; trav_info->fs = fs; trav_info->is_legacy_root_dir = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0); trav_info->dirty = 0; trav_info->eof = 0; trav_info->current_entry = -1; if (trav_info->is_legacy_root_dir) { trav_info->buffer_size = 512 * fs_info->root_dir_sector_count; } else { trav_info->next_buffer = start_cluster; trav_info->buffer_size = fs_info->cluster_size; } trav_info->dir_entries = (FatDirEntry*) ped_malloc (trav_info->buffer_size); if (!trav_info->dir_entries) goto error_free_dir_name; if (trav_info->is_legacy_root_dir) { if (!ped_geometry_read (fs->geom, trav_info->dir_entries, fs_info->root_dir_offset, fs_info->root_dir_sector_count)) goto error_free_dir_entries; } else { if (!read_next_dir_buffer (trav_info)) goto error_free_dir_entries; } return trav_info; error_free_dir_entries: free (trav_info->dir_entries); error_free_dir_name: free (trav_info->dir_name); error_free_trav_info: free (trav_info); error: return NULL; }
int fat_info_sector_write (const FatInfoSector* is, PedFileSystem *fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (is != NULL); if (!ped_geometry_write (fs->geom, is, fs_info->info_sector_offset, 1)) return 0; return ped_geometry_sync (fs->geom); }
/* 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; }
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); 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; }
int fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag, FatFragment count) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PedSector sector = fat_frag_to_sector (fs, frag); PedSector sector_count = count * fs_info->frag_sectors; PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); return ped_geometry_write (fs->geom, buf, sector, sector_count); }
int fat_read_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, FatCluster count) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PedSector sector = fat_cluster_to_sector (fs, cluster); PedSector sector_count = count * fs_info->cluster_sectors; PED_ASSERT (cluster >= 2 && cluster + count - 1 < fs_info->cluster_count + 2); return ped_geometry_read (fs->geom, buf, sector, sector_count); }
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); 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; free (ctx->new_fs); fat_op_context_destroy (ctx); return 1; }
static int write_root_dir (FatTraverseInfo* trav_info) { FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries, fs_info->root_dir_offset, fs_info->root_dir_sector_count)) return 0; if (!ped_geometry_sync (trav_info->fs->geom)) return 0; trav_info->dirty = 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); 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 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); free (new_fs_info); ped_geometry_destroy (ctx->new_fs->geom); free (ctx->new_fs); fat_op_context_destroy (ctx); return 1; }
int fat_boot_sector_set_boot_code (FatBootSector** bsp, const PedFileSystem* fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (bsp != NULL); *bsp = ped_malloc (fs->geom->dev->sector_size); FatBootSector *bs = *bsp; PED_ASSERT (bs != NULL); memset (bs, 0, 512); memcpy (bs->boot_jump, FAT_BOOT_JUMP, 3); memcpy (bs->u.fat32.boot_code, FAT_BOOT_CODE, FAT_BOOT_CODE_LENGTH); return 1; }
int fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (bs != NULL); if (!ped_geometry_write (fs->geom, bs, 0, 1)) return 0; if (fs_info->fat_type == FAT_TYPE_FAT32) { if (!ped_geometry_write (fs->geom, bs, fs_info->boot_sector_backup_offset, 1)) return 0; } return ped_geometry_sync (fs->geom); }
int fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); FatCluster first_cluster; if (!fat_dir_entry_is_file (dir_entry) && !fat_dir_entry_is_directory (dir_entry)) return 0; first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs); if (first_cluster == 0 || fat_table_is_eof (fs_info->fat, first_cluster)) return 0; return 1; }