/* libparted - a library for manipulating disk partitions Copyright (C) 2004-2005, 2007, 2009-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 . */ #ifndef DISCOVER_ONLY #include #include #include #include #include #if ENABLE_NLS # include # define _(String) dgettext (PACKAGE, String) #else # define _(String) (String) #endif /* ENABLE_NLS */ #include "hfs.h" #include "advfs.h" #include "file.h" /* Open the data fork of a file with its first three extents and its CNID */ HfsPrivateFile* hfs_file_open (PedFileSystem *fs, uint32_t CNID, HfsExtDataRec ext_desc, PedSector sect_nb) { HfsPrivateFile* file; file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile)); if (!file) return NULL; file->fs = fs; file->sect_nb = sect_nb; file->CNID = CNID; memcpy(file->first, ext_desc, sizeof (HfsExtDataRec)); file->start_cache = 0; return file; } /* Close an HFS file */ void hfs_file_close (HfsPrivateFile* file) { free (file); } /* warning : only works on data forks */ static int hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block, HfsExtDataRec cache, uint16_t* ptr_start_cache) { uint8_t record[sizeof (HfsExtentKey) + sizeof (HfsExtDataRec)]; HfsExtentKey search; HfsExtentKey* ret_key = (HfsExtentKey*) record; HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) (record + sizeof (HfsExtentKey)); HfsPrivateFSData* priv_data = (HfsPrivateFSData*) file->fs->type_specific; search.key_length = sizeof (HfsExtentKey) - 1; search.type = HFS_DATA_FORK; search.file_ID = file->CNID; search.start = PED_CPU_TO_BE16 (block); if (!hfs_btree_search (priv_data->extent_file, (HfsPrivateGenericKey*) &search, record, sizeof (record), NULL)) return 0; if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) return 0; memcpy (cache, ret_cache, sizeof(HfsExtDataRec)); *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start); return 1; } /* find and return the nth sector of a file */ /* return 0 on error */ static PedSector hfs_file_find_sector (HfsPrivateFile* file, PedSector sector) { HfsPrivateFSData* priv_data = (HfsPrivateFSData*) file->fs->type_specific; unsigned int sect_by_block = PED_BE32_TO_CPU ( priv_data->mdb->block_size) / PED_SECTOR_SIZE_DEFAULT; unsigned int i, s, vol_block; unsigned int block = sector / sect_by_block; unsigned int offset = sector % sect_by_block; /* in the three first extent */ for (s = 0, i = 0; i < HFS_EXT_NB; i++) { if ((block >= s) && ( block < s + PED_BE16_TO_CPU ( file->first[i].block_count))) { vol_block = (block - s) + PED_BE16_TO_CPU ( file->first[i].start_block); goto sector_found; } s += PED_BE16_TO_CPU (file->first[i].block_count); } /* in the three cached extent */ if (file->start_cache && block >= file->start_cache) for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { if ((block >= s) && (block < s + PED_BE16_TO_CPU ( file->cache[i].block_count))) { vol_block = (block - s) + PED_BE16_TO_CPU ( file->cache[i].start_block); goto sector_found; } s += PED_BE16_TO_CPU (file->cache[i].block_count); } /* update cache */ if (!hfs_get_extent_containing (file, block, file->cache, &(file->start_cache))) { ped_exception_throw ( PED_EXCEPTION_WARNING, PED_EXCEPTION_CANCEL, _("Could not update the extent cache for HFS file with " "CNID %X."), PED_BE32_TO_CPU(file->CNID)); return 0; } /* in the three cached extent */ PED_ASSERT(file->start_cache && block >= file->start_cache); for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { if ((block >= s) && (block < s + PED_BE16_TO_CPU ( file->cache[i].block_count))) { vol_block = (block - s) + PED_BE16_TO_CPU ( file->cache[i].start_block); goto sector_found; } s += PED_BE16_TO_CPU (file->cache[i].block_count); } return 0; sector_found: return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block) + (PedSector) vol_block * sect_by_block + offset; } /* Read the nth sector of a file */ /* return 0 on error */ int hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector) { PedSector abs_sector; if (sector >= file->sect_nb) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Trying to read HFS file with CNID %X behind EOF."), PED_BE32_TO_CPU(file->CNID)); return 0; } abs_sector = hfs_file_find_sector (file, sector); if (!abs_sector) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Could not find sector %lli of HFS file with " "CNID %X."), sector, PED_BE32_TO_CPU(file->CNID)); return 0; } return ped_geometry_read (file->fs->geom, buf, abs_sector, 1); } /* Write the nth sector of a file */ /* return 0 on error */ int hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector) { PedSector abs_sector; if (sector >= file->sect_nb) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Trying to write HFS file with CNID %X behind EOF."), PED_BE32_TO_CPU(file->CNID)); return 0; } abs_sector = hfs_file_find_sector (file, sector); if (!abs_sector) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Could not find sector %lli of HFS file with " "CNID %X."), sector, PED_BE32_TO_CPU(file->CNID)); return 0; } return ped_geometry_write (file->fs->geom, buf, abs_sector, 1); } #endif /* !DISCOVER_ONLY */