1 /*
   2     libparted - a library for manipulating disk partitions
   3     Copyright (C) 2004, 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 #ifndef DISCOVER_ONLY
  20 
  21 #include <config.h>
  22 
  23 #include <parted/parted.h>
  24 #include <parted/endian.h>
  25 #include <parted/debug.h>
  26 #include <stdint.h>
  27 
  28 #if ENABLE_NLS
  29 #  include <libintl.h>
  30 #  define _(String) dgettext (PACKAGE, String)
  31 #else
  32 #  define _(String) (String)
  33 #endif /* ENABLE_NLS */
  34 
  35 #include "hfs.h"
  36 #include "advfs.h"
  37 
  38 #include "file.h"
  39 
  40 /* Open the data fork of a file with its first three extents and its CNID */
  41 HfsPrivateFile*
  42 hfs_file_open (PedFileSystem *fs, uint32_t CNID,
  43                HfsExtDataRec ext_desc, PedSector sect_nb)
  44 {
  45         HfsPrivateFile* file;
  46 
  47         file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile));
  48         if (!file) return NULL;
  49 
  50         file->fs = fs;
  51         file->sect_nb = sect_nb;
  52         file->CNID = CNID;
  53         memcpy(file->first, ext_desc, sizeof (HfsExtDataRec));
  54         file->start_cache = 0;
  55         
  56         return file;
  57 }
  58 
  59 /* Close an HFS file */
  60 void
  61 hfs_file_close (HfsPrivateFile* file)
  62 {
  63         ped_free (file);
  64 }
  65 
  66 /* warning : only works on data forks */
  67 static int
  68 hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block,
  69                            HfsExtDataRec cache, uint16_t* ptr_start_cache)
  70 {
  71         uint8_t                 record[sizeof (HfsExtentKey)
  72                                        + sizeof (HfsExtDataRec)];
  73         HfsExtentKey            search;
  74         HfsExtentKey*           ret_key = (HfsExtentKey*) record;
  75         HfsExtDescriptor*       ret_cache = (HfsExtDescriptor*)
  76                                               (record + sizeof (HfsExtentKey));
  77         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
  78                                               file->fs->type_specific;
  79 
  80         search.key_length = sizeof (HfsExtentKey) - 1;
  81         search.type = HFS_DATA_FORK;
  82         search.file_ID = file->CNID;
  83         search.start = PED_CPU_TO_BE16 (block);
  84 
  85         if (!hfs_btree_search (priv_data->extent_file, 
  86                                (HfsPrivateGenericKey*) &search,
  87                                record, sizeof (record), NULL))
  88                 return 0;
  89 
  90         if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
  91                 return 0;
  92 
  93         memcpy (cache, ret_cache, sizeof(HfsExtDataRec));
  94         *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start);
  95 
  96         return 1;
  97 }
  98 
  99 /* find and return the nth sector of a file */
 100 /* return 0 on error */
 101 static PedSector
 102 hfs_file_find_sector (HfsPrivateFile* file, PedSector sector)
 103 {
 104         HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
 105                                       file->fs->type_specific;
 106         unsigned int    sect_by_block = PED_BE32_TO_CPU (
 107                                             priv_data->mdb->block_size)
 108                                         / PED_SECTOR_SIZE_DEFAULT;
 109         unsigned int    i, s, vol_block;
 110         unsigned int    block  = sector / sect_by_block;
 111         unsigned int    offset = sector % sect_by_block;
 112 
 113         /* in the three first extent */
 114         for (s = 0, i = 0; i < HFS_EXT_NB; i++) {
 115                         if ((block >= s) && ( block < s + PED_BE16_TO_CPU (
 116                                                 file->first[i].block_count))) {
 117                         vol_block = (block - s) + PED_BE16_TO_CPU (
 118                                                     file->first[i].start_block);
 119                         goto sector_found;
 120                 }
 121                 s += PED_BE16_TO_CPU (file->first[i].block_count);
 122         }
 123 
 124         /* in the three cached extent */
 125         if (file->start_cache && block >= file->start_cache)
 126         for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) {
 127                 if ((block >= s) && (block < s + PED_BE16_TO_CPU (
 128                                                 file->cache[i].block_count))) {
 129                         vol_block = (block - s) + PED_BE16_TO_CPU (
 130                                                     file->cache[i].start_block);
 131                         goto sector_found;
 132                 }
 133                 s += PED_BE16_TO_CPU (file->cache[i].block_count);
 134         }
 135 
 136         /* update cache */
 137         if (!hfs_get_extent_containing (file, block, file->cache,
 138                                         &(file->start_cache))) {
 139                 ped_exception_throw (
 140                         PED_EXCEPTION_WARNING,
 141                         PED_EXCEPTION_CANCEL,
 142                         _("Could not update the extent cache for HFS file with "
 143                           "CNID %X."),
 144                         PED_BE32_TO_CPU(file->CNID));
 145                 return 0;
 146         }
 147 
 148         /* in the three cached extent */
 149         PED_ASSERT(file->start_cache && block >= file->start_cache, return 0);
 150         for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) {
 151                 if ((block >= s) && (block < s + PED_BE16_TO_CPU (
 152                                                 file->cache[i].block_count))) {
 153                         vol_block = (block - s) + PED_BE16_TO_CPU (
 154                                                     file->cache[i].start_block);
 155                         goto sector_found;
 156                 }
 157                 s += PED_BE16_TO_CPU (file->cache[i].block_count);
 158         }
 159 
 160         return 0;
 161 
 162     sector_found:
 163         return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block)
 164                 + (PedSector) vol_block * sect_by_block
 165                 + offset;
 166 }
 167 
 168 /* Read the nth sector of a file */
 169 /* return 0 on error */
 170 int
 171 hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector)
 172 {
 173         PedSector       abs_sector;
 174 
 175         if (sector >= file->sect_nb) {
 176                 ped_exception_throw (
 177                         PED_EXCEPTION_ERROR,
 178                         PED_EXCEPTION_CANCEL,
 179                         _("Trying to read HFS file with CNID %X behind EOF."),
 180                         PED_BE32_TO_CPU(file->CNID));
 181                 return 0;
 182         }
 183 
 184         abs_sector = hfs_file_find_sector (file, sector);
 185         if (!abs_sector) {
 186                 ped_exception_throw (
 187                         PED_EXCEPTION_ERROR,
 188                         PED_EXCEPTION_CANCEL,
 189                         _("Could not find sector %lli of HFS file with "
 190                           "CNID %X."),
 191                         sector, PED_BE32_TO_CPU(file->CNID));
 192                 return 0;
 193         }
 194 
 195         return ped_geometry_read (file->fs->geom, buf, abs_sector, 1);
 196 }
 197 
 198 /* Write the nth sector of a file */
 199 /* return 0 on error */
 200 int
 201 hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector)
 202 {
 203         PedSector       abs_sector;
 204 
 205         if (sector >= file->sect_nb) {
 206                 ped_exception_throw (
 207                         PED_EXCEPTION_ERROR,
 208                         PED_EXCEPTION_CANCEL,
 209                         _("Trying to write HFS file with CNID %X behind EOF."),
 210                           PED_BE32_TO_CPU(file->CNID));              
 211                 return 0;
 212         }
 213 
 214         abs_sector = hfs_file_find_sector (file, sector);
 215         if (!abs_sector) {
 216                 ped_exception_throw (
 217                         PED_EXCEPTION_ERROR,
 218                         PED_EXCEPTION_CANCEL,
 219                         _("Could not find sector %lli of HFS file with "
 220                           "CNID %X."),
 221                         sector, PED_BE32_TO_CPU(file->CNID));
 222                 return 0;
 223         }
 224 
 225         return ped_geometry_write (file->fs->geom, buf, abs_sector, 1);
 226 }
 227 
 228 #endif /* !DISCOVER_ONLY */