1 /* 2 libparted 3 Copyright (C) 1998, 1999, 2000, 2005, 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 "fat.h" 21 #include "traverse.h" 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #ifndef DISCOVER_ONLY 28 29 #define NO_CLUSTER -1 30 31 static char tmp_buffer [4096]; 32 33 int 34 fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info) 35 { 36 return trav_info->buffer_size / sizeof (FatDirEntry); 37 } 38 39 /* returns 1 if there are no more directory entries in the directory being 40 * traversed, 0 otherwise. 41 */ 42 static int 43 is_last_buffer (FatTraverseInfo* trav_info) { 44 FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); 45 46 if (trav_info->is_legacy_root_dir) 47 return 1; 48 else 49 return fat_table_is_eof (fs_info->fat, trav_info->next_buffer); 50 } 51 52 static int 53 write_root_dir (FatTraverseInfo* trav_info) 54 { 55 FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); 56 57 if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries, 58 fs_info->root_dir_offset, 59 fs_info->root_dir_sector_count)) 60 return 0; 61 if (!ped_geometry_sync (trav_info->fs->geom)) 62 return 0; 63 trav_info->dirty = 0; 64 return 1; 65 } 66 67 static int 68 write_dir_cluster (FatTraverseInfo* trav_info) 69 { 70 if (!fat_write_sync_cluster (trav_info->fs, 71 (void*) trav_info->dir_entries, 72 trav_info->this_buffer)) 73 return 0; 74 trav_info->dirty = 0; 75 return 1; 76 } 77 78 static int 79 write_dir_buffer (FatTraverseInfo* trav_info) 80 { 81 if (trav_info->is_legacy_root_dir) 82 return write_root_dir (trav_info); 83 else 84 return write_dir_cluster (trav_info); 85 } 86 87 static int 88 read_next_dir_buffer (FatTraverseInfo* trav_info) 89 { 90 FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); 91 92 PED_ASSERT (!trav_info->is_legacy_root_dir, return 0); 93 94 trav_info->this_buffer = trav_info->next_buffer; 95 96 if (trav_info->this_buffer < 2 97 || trav_info->this_buffer >= fs_info->cluster_count + 2) { 98 ped_exception_throw ( 99 PED_EXCEPTION_ERROR, 100 PED_EXCEPTION_CANCEL, 101 "Cluster %ld in directory %s is outside file system!", 102 (long) trav_info->this_buffer, 103 trav_info->dir_name); 104 return 0; 105 } 106 107 trav_info->next_buffer 108 = fat_table_get (fs_info->fat, trav_info->this_buffer); 109 110 return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries, 111 trav_info->this_buffer); 112 } 113 114 /* FIXME: put into fat_dir_entry_* operations */ 115 void 116 fat_traverse_mark_dirty (FatTraverseInfo* trav_info) 117 { 118 trav_info->dirty = 1; 119 } 120 121 FatTraverseInfo* 122 fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster, 123 char* dir_name) 124 { 125 FatSpecific* fs_info = FAT_SPECIFIC (fs); 126 FatTraverseInfo* trav_info; 127 128 trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo)); 129 if (!trav_info) 130 goto error; 131 132 trav_info->dir_name = strdup (dir_name); 133 if (!trav_info->dir_name) 134 goto error_free_trav_info; 135 136 trav_info->fs = fs; 137 trav_info->is_legacy_root_dir 138 = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0); 139 trav_info->dirty = 0; 140 trav_info->eof = 0; 141 trav_info->current_entry = -1; 142 143 if (trav_info->is_legacy_root_dir) { 144 trav_info->buffer_size = 512 * fs_info->root_dir_sector_count; 145 } else { 146 trav_info->next_buffer = start_cluster; 147 trav_info->buffer_size = fs_info->cluster_size; 148 } 149 150 trav_info->dir_entries 151 = (FatDirEntry*) ped_malloc (trav_info->buffer_size); 152 if (!trav_info->dir_entries) 153 goto error_free_dir_name; 154 155 if (trav_info->is_legacy_root_dir) { 156 if (!ped_geometry_read (fs->geom, trav_info->dir_entries, 157 fs_info->root_dir_offset, 158 fs_info->root_dir_sector_count)) 159 goto error_free_dir_entries; 160 } else { 161 if (!read_next_dir_buffer (trav_info)) 162 goto error_free_dir_entries; 163 } 164 165 return trav_info; 166 167 error_free_dir_entries: 168 ped_free (trav_info->dir_entries); 169 error_free_dir_name: 170 ped_free (trav_info->dir_name); 171 error_free_trav_info: 172 ped_free (trav_info); 173 error: 174 return NULL; 175 } 176 177 int 178 fat_traverse_complete (FatTraverseInfo* trav_info) 179 { 180 if (trav_info->dirty) { 181 if (!write_dir_buffer (trav_info)) 182 return 0; 183 } 184 ped_free (trav_info->dir_entries); 185 ped_free (trav_info->dir_name); 186 ped_free (trav_info); 187 return 1; 188 } 189 190 FatTraverseInfo* 191 fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent) 192 { 193 strcpy (tmp_buffer, trav_info->dir_name); 194 fat_dir_entry_get_name (parent, 195 tmp_buffer + strlen (trav_info->dir_name)); 196 strcat (tmp_buffer, "\\"); 197 198 return fat_traverse_begin (trav_info->fs, 199 fat_dir_entry_get_first_cluster (parent, trav_info->fs), 200 tmp_buffer); 201 } 202 203 FatDirEntry* 204 fat_traverse_next_dir_entry (FatTraverseInfo *trav_info) 205 { 206 if (trav_info->eof) 207 return NULL; 208 209 trav_info->current_entry++; 210 if (trav_info->current_entry 211 >= fat_traverse_entries_per_buffer (trav_info)) { 212 if (trav_info->dirty) { 213 if (!write_dir_buffer (trav_info)) 214 return NULL; 215 } 216 217 trav_info->current_entry = 0; 218 if (is_last_buffer (trav_info)) { 219 trav_info->eof = 1; 220 return NULL; 221 } 222 if (!read_next_dir_buffer (trav_info)) 223 return NULL; 224 } 225 return trav_info->dir_entries + trav_info->current_entry; 226 } 227 228 FatCluster 229 fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs) 230 { 231 FatSpecific* fs_info = FAT_SPECIFIC (fs); 232 233 switch (fs_info->fat_type) { 234 case FAT_TYPE_FAT12: 235 case FAT_TYPE_FAT16: 236 return PED_LE16_TO_CPU (dir_entry->first_cluster); 237 238 case FAT_TYPE_FAT32: 239 return PED_LE16_TO_CPU (dir_entry->first_cluster_high) 240 * 65536L 241 + PED_LE16_TO_CPU (dir_entry->first_cluster); 242 } 243 244 return 0; 245 } 246 247 void 248 fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs, 249 FatCluster cluster) 250 { 251 FatSpecific* fs_info = FAT_SPECIFIC (fs); 252 253 switch (fs_info->fat_type) { 254 case FAT_TYPE_FAT12: 255 PED_ASSERT (0, (void) 0); 256 break; 257 258 case FAT_TYPE_FAT16: 259 dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster); 260 break; 261 262 case FAT_TYPE_FAT32: 263 dir_entry->first_cluster 264 = PED_CPU_TO_LE16 (cluster & 0xffff); 265 dir_entry->first_cluster_high 266 = PED_CPU_TO_LE16 (cluster / 0x10000); 267 break; 268 } 269 } 270 271 uint32_t 272 fat_dir_entry_get_length (FatDirEntry* dir_entry) 273 { 274 return PED_LE32_TO_CPU (dir_entry->length); 275 } 276 277 int 278 fat_dir_entry_is_null_term (const FatDirEntry* dir_entry) 279 { 280 FatDirEntry null_entry; 281 282 memset (&null_entry, 0, sizeof (null_entry)); 283 return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0; 284 } 285 286 int 287 fat_dir_entry_is_active (FatDirEntry* dir_entry) 288 { 289 if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0; 290 if ((unsigned char) dir_entry->name[0] == 0) return 0; 291 if ((unsigned char) dir_entry->name[0] == 0xF6) return 0; 292 return 1; 293 } 294 295 int 296 fat_dir_entry_is_file (FatDirEntry* dir_entry) { 297 if (dir_entry->attributes == VFAT_ATTR) return 0; 298 if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; 299 if (!fat_dir_entry_is_active (dir_entry)) return 0; 300 if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0; 301 return 1; 302 } 303 304 int 305 fat_dir_entry_is_system_file (FatDirEntry* dir_entry) 306 { 307 if (!fat_dir_entry_is_file (dir_entry)) return 0; 308 return (dir_entry->attributes & SYSTEM_ATTR) 309 || (dir_entry->attributes & HIDDEN_ATTR); 310 } 311 312 int 313 fat_dir_entry_is_directory (FatDirEntry* dir_entry) 314 { 315 if (dir_entry->attributes == VFAT_ATTR) return 0; 316 if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; 317 if (!fat_dir_entry_is_active (dir_entry)) return 0; 318 return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR; 319 } 320 321 int 322 fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs) 323 { 324 FatSpecific* fs_info = FAT_SPECIFIC (fs); 325 FatCluster first_cluster; 326 327 if (!fat_dir_entry_is_file (dir_entry) 328 && !fat_dir_entry_is_directory (dir_entry)) 329 return 0; 330 331 first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs); 332 if (first_cluster == 0 333 || fat_table_is_eof (fs_info->fat, first_cluster)) 334 return 0; 335 336 return 1; 337 } 338 339 /* 340 decrypts silly DOS names to FILENAME.EXT 341 */ 342 void 343 fat_dir_entry_get_name (FatDirEntry*dir_entry, char *result) { 344 int i; 345 char *src; 346 347 src = dir_entry->name; 348 349 for (i=0; i<8; i++) { 350 if (src[i] == ' ' || src[i] == 0) break; 351 *result++ = src[i]; 352 } 353 354 if (src[8] != ' ' && src[8] != 0) { 355 *result++ = '.'; 356 for (i=8; i<11; i++) { 357 if (src[i] == ' ' || src[i] == 0) break; 358 *result++ = src[i]; 359 } 360 } 361 362 *result = 0; 363 } 364 365 #endif /* !DISCOVER_ONLY */