/* libparted Copyright (C) 1998-2000, 2005, 2007-2014, 2019-2022 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 3 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, see . */ #include #include "fat.h" #include "traverse.h" #include #include #include #ifndef DISCOVER_ONLY #define NO_CLUSTER -1 static char tmp_buffer [4096]; int _GL_ATTRIBUTE_PURE fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info) { return trav_info->buffer_size / sizeof (FatDirEntry); } /* 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); } 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; } static int write_dir_cluster (FatTraverseInfo* trav_info) { if (!fat_write_sync_cluster (trav_info->fs, (void*) trav_info->dir_entries, trav_info->this_buffer)) return 0; trav_info->dirty = 0; return 1; } static int write_dir_buffer (FatTraverseInfo* trav_info) { if (trav_info->is_legacy_root_dir) return write_root_dir (trav_info); else return write_dir_cluster (trav_info); } 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); } /* FIXME: put into fat_dir_entry_* operations */ void fat_traverse_mark_dirty (FatTraverseInfo* trav_info) { trav_info->dirty = 1; } 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_traverse_complete (FatTraverseInfo* trav_info) { if (trav_info->dirty) { if (!write_dir_buffer (trav_info)) return 0; } free (trav_info->dir_entries); free (trav_info->dir_name); free (trav_info); return 1; } FatTraverseInfo* fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent) { strcpy (tmp_buffer, trav_info->dir_name); fat_dir_entry_get_name (parent, tmp_buffer + strlen (trav_info->dir_name)); strcat (tmp_buffer, "\\"); return fat_traverse_begin (trav_info->fs, fat_dir_entry_get_first_cluster (parent, trav_info->fs), tmp_buffer); } FatDirEntry* fat_traverse_next_dir_entry (FatTraverseInfo *trav_info) { if (trav_info->eof) return NULL; trav_info->current_entry++; if (trav_info->current_entry >= fat_traverse_entries_per_buffer (trav_info)) { if (trav_info->dirty) { if (!write_dir_buffer (trav_info)) return NULL; } trav_info->current_entry = 0; if (is_last_buffer (trav_info)) { trav_info->eof = 1; return NULL; } if (!read_next_dir_buffer (trav_info)) return NULL; } return trav_info->dir_entries + trav_info->current_entry; } FatCluster _GL_ATTRIBUTE_PURE fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); switch (fs_info->fat_type) { case FAT_TYPE_FAT12: case FAT_TYPE_FAT16: return PED_LE16_TO_CPU (dir_entry->first_cluster); case FAT_TYPE_FAT32: return PED_LE16_TO_CPU (dir_entry->first_cluster_high) * 65536L + PED_LE16_TO_CPU (dir_entry->first_cluster); } return 0; } void fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs, FatCluster cluster) { FatSpecific* fs_info = FAT_SPECIFIC (fs); switch (fs_info->fat_type) { case FAT_TYPE_FAT12: PED_ASSERT (0); break; case FAT_TYPE_FAT16: dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster); break; case FAT_TYPE_FAT32: dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster & 0xffff); dir_entry->first_cluster_high = PED_CPU_TO_LE16 (cluster / 0x10000); break; } } uint32_t _GL_ATTRIBUTE_PURE fat_dir_entry_get_length (FatDirEntry* dir_entry) { return PED_LE32_TO_CPU (dir_entry->length); } int fat_dir_entry_is_null_term (const FatDirEntry* dir_entry) { FatDirEntry null_entry; memset (&null_entry, 0, sizeof (null_entry)); return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0; } int _GL_ATTRIBUTE_PURE fat_dir_entry_is_active (FatDirEntry* dir_entry) { if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0; if ((unsigned char) dir_entry->name[0] == 0) return 0; if ((unsigned char) dir_entry->name[0] == 0xF6) return 0; return 1; } int _GL_ATTRIBUTE_PURE fat_dir_entry_is_file (FatDirEntry* dir_entry) { if (dir_entry->attributes == VFAT_ATTR) return 0; if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; if (!fat_dir_entry_is_active (dir_entry)) return 0; if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0; return 1; } int _GL_ATTRIBUTE_PURE fat_dir_entry_is_system_file (FatDirEntry* dir_entry) { if (!fat_dir_entry_is_file (dir_entry)) return 0; return (dir_entry->attributes & SYSTEM_ATTR) || (dir_entry->attributes & HIDDEN_ATTR); } int _GL_ATTRIBUTE_PURE fat_dir_entry_is_directory (FatDirEntry* dir_entry) { if (dir_entry->attributes == VFAT_ATTR) return 0; if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; if (!fat_dir_entry_is_active (dir_entry)) return 0; return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR; } 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; } /* decrypts silly DOS names to FILENAME.EXT */ void fat_dir_entry_get_name (const FatDirEntry *dir_entry, char *result) { size_t i; const char *src; const char *ext; src = dir_entry->name; for (i=0; i < sizeof dir_entry->name; i++) { if (src[i] == ' ' || src[i] == 0) break; *result++ = src[i]; } ext = (const char *) dir_entry->extension; if (ext[0] != ' ' && ext[0] != 0) { *result++ = '.'; for (i=0; i < sizeof dir_entry->extension; i++) { if (ext[i] == ' ' || ext[i] == 0) break; *result++ = ext[i]; } } *result = 0; } #endif /* !DISCOVER_ONLY */