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_plus.h"
  37 
  38 #include "file_plus.h"
  39 
  40 /* Open the data fork of a file with its first eight extents and its CNID */
  41 /* CNID and ext_desc must be in disc order, sect_nb in CPU order */
  42 /* return null on failure */
  43 HfsPPrivateFile*
  44 hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID,
  45                    HfsPExtDataRec ext_desc, PedSector sect_nb)
  46 {
  47         HfsPPrivateFile* file;
  48 
  49         file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile));
  50         if (!file) return NULL;
  51 
  52         file->fs = fs;
  53         file->sect_nb = sect_nb;
  54         file->CNID = CNID;
  55         memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec));
  56         file->start_cache = 0;
  57 
  58         return file;
  59 }
  60 
  61 /* Close an HFS+ file */
  62 void
  63 hfsplus_file_close (HfsPPrivateFile* file)
  64 {
  65         ped_free (file);
  66 }
  67 
  68 /* warning : only works on data forks */
  69 static int
  70 hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block,
  71                                HfsPExtDataRec cache, uint32_t* ptr_start_cache)
  72 {
  73         uint8_t                 record[sizeof (HfsPExtentKey)
  74                                        + sizeof (HfsPExtDataRec)];
  75         HfsPExtentKey           search;
  76         HfsPExtentKey*          ret_key = (HfsPExtentKey*) record;
  77         HfsPExtDescriptor*      ret_cache = (HfsPExtDescriptor*)
  78                                               (record + sizeof (HfsPExtentKey));
  79         HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
  80                                                 file->fs->type_specific;
  81 
  82         search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2);
  83         search.type = HFS_DATA_FORK;
  84         search.pad = 0;
  85         search.file_ID = file->CNID;
  86         search.start = PED_CPU_TO_BE32 (block);
  87 
  88         if (!hfsplus_btree_search (priv_data->extents_file,
  89                                    (HfsPPrivateGenericKey*) &search,
  90                                    record, sizeof (record), NULL))
  91                 return 0;
  92 
  93         if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
  94                 return 0;
  95 
  96         memcpy (cache, ret_cache, sizeof(HfsPExtDataRec));
  97         *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start);
  98 
  99         return 1;
 100 }
 101 
 102 /* find a sub extent contained in the desired area */
 103 /* and with the same starting point */
 104 /* return 0 in sector_count on error, or the physical area */
 105 /* on the volume corresponding to the logical area in the file */
 106 static HfsPPrivateExtent
 107 hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector,
 108                           unsigned int nb)
 109 {
 110         HfsPPrivateExtent ret = {0,0};
 111         HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
 112                                         file->fs->type_specific;
 113         unsigned int    sect_by_block = PED_BE32_TO_CPU (
 114                                             priv_data->vh->block_size)
 115                                         / PED_SECTOR_SIZE_DEFAULT;
 116         unsigned int    i, s, vol_block, size;
 117         PedSector       sect_size;
 118         unsigned int    block  = sector / sect_by_block;
 119         unsigned int    offset = sector % sect_by_block;
 120 
 121         /* in the 8 first extent */
 122         for (s = 0, i = 0; i < HFSP_EXT_NB; i++) {
 123                 if ((block >= s) && (block < s + PED_BE32_TO_CPU (
 124                                                 file->first[i].block_count))) {
 125                         vol_block = (block - s)
 126                                     + PED_BE32_TO_CPU (file->first[i]
 127                                                        .start_block);
 128                         size = PED_BE32_TO_CPU (file->first[i].block_count)
 129                                 + s - block;
 130                         goto plus_sector_found;
 131                 }
 132                 s += PED_BE32_TO_CPU (file->first[i].block_count);
 133         }
 134 
 135         /* in the 8 cached extent */
 136         if (file->start_cache && block >= file->start_cache)
 137         for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
 138                 if ((block >= s) && (block < s + PED_BE32_TO_CPU (
 139                                                 file->cache[i].block_count))) {
 140                         vol_block = (block - s)
 141                                     + PED_BE32_TO_CPU (file->cache[i]
 142                                                        .start_block);
 143                         size = PED_BE32_TO_CPU (file->cache[i].block_count)
 144                                 + s - block;
 145                         goto plus_sector_found;
 146                 }
 147                 s += PED_BE32_TO_CPU (file->cache[i].block_count);
 148         }
 149 
 150         /* update cache */
 151         if (!hfsplus_get_extent_containing (file, block, file->cache,
 152                                             &(file->start_cache))) {
 153                 ped_exception_throw (
 154                         PED_EXCEPTION_WARNING,
 155                         PED_EXCEPTION_CANCEL,
 156                         _("Could not update the extent cache for HFS+ file "
 157                           "with CNID %X."),
 158                         PED_BE32_TO_CPU(file->CNID));
 159                 return ret; /* ret == {0,0} */
 160         }
 161 
 162         /* ret == {0,0} */
 163         PED_ASSERT(file->start_cache && block >= file->start_cache, return ret);
 164 
 165         for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
 166                 if ((block >= s) && (block < s + PED_BE32_TO_CPU (
 167                                                 file->cache[i].block_count))) {
 168                         vol_block = (block - s)
 169                                     + PED_BE32_TO_CPU (file->cache[i]
 170                                                        .start_block);
 171                         size = PED_BE32_TO_CPU (file->cache[i].block_count)
 172                                 + s - block;
 173                         goto plus_sector_found;
 174                 }
 175                 s += PED_BE32_TO_CPU (file->cache[i].block_count);
 176         }
 177 
 178         return ret;
 179 
 180 plus_sector_found:
 181         sect_size = (PedSector) size * sect_by_block - offset;
 182         ret.start_sector = vol_block * sect_by_block + offset;
 183         ret.sector_count = (sect_size < nb) ? sect_size : nb;
 184         return ret;
 185 }
 186 
 187 int
 188 hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector,
 189                   unsigned int nb)
 190 {
 191         HfsPPrivateExtent phy_area;
 192         HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
 193                                         file->fs->type_specific;
 194         char *b = buf;
 195 
 196         if (sector+nb < sector /* detect overflow */
 197             || sector+nb > file->sect_nb) /* out of file */ {
 198                 ped_exception_throw (
 199                         PED_EXCEPTION_ERROR,
 200                         PED_EXCEPTION_CANCEL,
 201                         _("Trying to read HFS+ file with CNID %X behind EOF."),
 202                         PED_BE32_TO_CPU(file->CNID));
 203                 return 0;
 204         }
 205 
 206         while (nb) {
 207                 phy_area = hfsplus_file_find_extent(file, sector, nb);
 208                 if (phy_area.sector_count == 0) {
 209                         ped_exception_throw (
 210                                 PED_EXCEPTION_ERROR,
 211                                 PED_EXCEPTION_CANCEL,
 212                                 _("Could not find sector %lli of HFS+ file "
 213                                   "with CNID %X."),
 214                                 sector, PED_BE32_TO_CPU(file->CNID));
 215                         return 0;
 216                 }
 217                 if (!ped_geometry_read(priv_data->plus_geom, b,
 218                                        phy_area.start_sector,
 219                                        phy_area.sector_count))
 220                         return 0;
 221 
 222                 nb -= phy_area.sector_count; /* < nb anyway ... */
 223                 sector += phy_area.sector_count;
 224                 b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
 225         }
 226 
 227         return 1;
 228 }
 229 
 230 int
 231 hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector,
 232                   unsigned int nb)
 233 {
 234         HfsPPrivateExtent phy_area;
 235         HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
 236                                         file->fs->type_specific;
 237         char *b = buf;
 238 
 239         if (sector+nb < sector /* detect overflow */
 240             || sector+nb > file->sect_nb) /* out of file */ {
 241                 ped_exception_throw (
 242                         PED_EXCEPTION_ERROR,
 243                         PED_EXCEPTION_CANCEL,
 244                         _("Trying to write HFS+ file with CNID %X behind EOF."),
 245                         PED_BE32_TO_CPU(file->CNID));
 246                 return 0;
 247         }
 248 
 249         while (nb) {
 250                 phy_area = hfsplus_file_find_extent(file, sector, nb);
 251                 if (phy_area.sector_count == 0) {
 252                         ped_exception_throw (
 253                                 PED_EXCEPTION_ERROR,
 254                                 PED_EXCEPTION_CANCEL,
 255                                 _("Could not find sector %lli of HFS+ file "
 256                                   "with CNID %X."),
 257                                 sector, PED_BE32_TO_CPU(file->CNID));
 258                         return 0;
 259                 }
 260                 if (!ped_geometry_write(priv_data->plus_geom, b,
 261                                        phy_area.start_sector,
 262                                        phy_area.sector_count))
 263                         return 0;
 264 
 265                 nb -= phy_area.sector_count; /* < nb anyway ... */
 266                 sector += phy_area.sector_count;
 267                 b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
 268         }
 269 
 270         return 1;
 271 }
 272 
 273 #endif /* !DISCOVER_ONLY */