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 #include <config.h>
  20 
  21 #include <parted/parted.h>
  22 #include <parted/endian.h>
  23 #include <parted/debug.h>
  24 #include <stdint.h>
  25 
  26 #if ENABLE_NLS
  27 #  include <libintl.h>
  28 #  define _(String) dgettext (PACKAGE, String)
  29 #else
  30 #  define _(String) (String)
  31 #endif /* ENABLE_NLS */
  32 
  33 #include "hfs.h"
  34 
  35 #include "probe.h"
  36 
  37 int
  38 hfsc_can_use_geom (PedGeometry* geom)
  39 {
  40         PedDevice* dev;
  41 
  42         dev = geom->dev;
  43         PED_ASSERT (geom != NULL, return 0);
  44         PED_ASSERT (dev != NULL, return 0);
  45 
  46         if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) {
  47                 ped_exception_throw (
  48                         PED_EXCEPTION_ERROR,
  49                         PED_EXCEPTION_CANCEL,
  50                         _("Parted can't use HFS file systems on disks "
  51                           "with a sector size not equal to %d bytes."),
  52                         (int)PED_SECTOR_SIZE_DEFAULT );
  53                 return 0;
  54         }
  55 
  56         return 1;
  57 }
  58 
  59 /* Probe an HFS volume, detecting it even if
  60 it is in fact a wrapper to an HFS+ volume */
  61 /* Used by hfsplus_probe and hfs_probe */
  62 PedGeometry*
  63 hfs_and_wrapper_probe (PedGeometry* geom)
  64 {
  65         uint8_t         buf[PED_SECTOR_SIZE_DEFAULT];
  66         HfsMasterDirectoryBlock *mdb;
  67         PedGeometry*    geom_ret;
  68         PedSector       search, max;
  69 
  70         PED_ASSERT (geom != NULL, return NULL);
  71         PED_ASSERT (hfsc_can_use_geom (geom), return NULL);
  72 
  73         mdb = (HfsMasterDirectoryBlock *) buf;
  74 
  75         /* is 5 an intelligent value ? */
  76         if ((geom->length < 5)
  77             || (!ped_geometry_read (geom, buf, 2, 1))
  78             || (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) )
  79                 return NULL;
  80 
  81         search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block)
  82                   + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks)
  83                      * (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT )));
  84         max = search + (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT);
  85         if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2)))
  86                 return NULL;
  87 
  88         for (; search < max; search++) {
  89                 if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2)
  90                     || !ped_geometry_read (geom_ret, buf, search, 1))
  91                         break;
  92                 if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE))
  93                         return geom_ret;
  94         }
  95 
  96         ped_geometry_destroy (geom_ret);
  97         return NULL;
  98 }
  99 
 100 PedGeometry*
 101 hfsplus_probe (PedGeometry* geom)
 102 {
 103         PedGeometry*    geom_ret;
 104         uint8_t         buf[PED_SECTOR_SIZE_DEFAULT];
 105 
 106         PED_ASSERT (geom != NULL, return NULL);
 107 
 108         if (!hfsc_can_use_geom (geom))
 109                 return NULL;
 110 
 111         if ((geom_ret = hfs_and_wrapper_probe(geom))) {
 112                 /* HFS+ is embedded in an HFS volume ? */
 113                 HfsMasterDirectoryBlock *mdb;
 114                 mdb = (HfsMasterDirectoryBlock *) buf;
 115 
 116                 if (!ped_geometry_read (geom, buf, 2, 1)
 117                     || (mdb->old_new.embedded.signature
 118                         != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) {
 119                         ped_geometry_destroy (geom_ret);
 120                         return NULL;
 121                 } else
 122                         return geom_ret;
 123         } else {
 124                 /* This is a standalone HFS+ volume ? */
 125                 PedSector       search, max;
 126                 HfsPVolumeHeader *vh;
 127                 vh = (HfsPVolumeHeader *) buf;
 128 
 129                 if ((geom->length < 5)
 130                     || !ped_geometry_read (geom, buf, 2, 1)
 131                     || (vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)))
 132                         return NULL;
 133 
 134                 /* Correct range is indeed [ blocks*sz-2;(blocs+1)*sz-2 ( */
 135                 /* But previous versions of my implementation used to */
 136                 /* assume range is [(blocks-1)*sz-1;(blocks*sz) ( */
 137                 /* (blocks-1)*sz-1 has to be scanned last, because */
 138                 /* it can belong to a regular file */
 139                 max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1)
 140                       * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT )
 141                       - 2;
 142                 search = max - 2 * ( PED_BE32_TO_CPU (vh->block_size)
 143                                      / PED_SECTOR_SIZE_DEFAULT ) + 2;
 144                 if (!(geom_ret = ped_geometry_new (geom->dev, geom->start,
 145                                                    search + 2)))
 146                         return NULL;
 147 
 148                 for (; search < max; search++) {
 149                         if (!ped_geometry_set (geom_ret, geom_ret->start,
 150                                                search + 2)
 151                             || !ped_geometry_read (geom_ret, buf, search, 1))
 152                                 break;
 153                         if (vh->signature == PED_CPU_TO_BE16 (HFSP_SIGNATURE))
 154                                 return geom_ret;
 155                 }
 156                 search = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) - 1)
 157                       * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT )
 158                       - 1;
 159                 if (!ped_geometry_set (geom_ret, geom_ret->start,
 160                                                search + 2)
 161                     || !ped_geometry_read (geom_ret, buf, search, 1)
 162                     || vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)) {
 163                         ped_geometry_destroy (geom_ret);
 164                         return NULL;
 165                 } else
 166                         return geom_ret;
 167         }
 168 }
 169 
 170 PedGeometry*
 171 hfs_probe (PedGeometry* geom)
 172 {
 173         PedGeometry*    geom_base;
 174         PedGeometry*    geom_plus = NULL;
 175         
 176         PED_ASSERT (geom != NULL, return NULL);
 177         
 178         if (!hfsc_can_use_geom (geom))
 179                 return NULL;
 180 
 181         if ((geom_base = hfs_and_wrapper_probe(geom))
 182             && (!(geom_plus = hfsplus_probe(geom_base))))
 183                 return geom_base;
 184         else {
 185                 if (geom_base) ped_geometry_destroy (geom_base);
 186                 if (geom_plus) ped_geometry_destroy (geom_plus);
 187                 return NULL;
 188         }
 189 }
 190 
 191 PedGeometry*
 192 hfsx_probe (PedGeometry* geom)
 193 {
 194         PedGeometry*    geom_ret;
 195         uint8_t         buf[PED_SECTOR_SIZE_DEFAULT];
 196         PedSector       search, max;
 197         HfsPVolumeHeader *vh = (HfsPVolumeHeader *) buf;
 198 
 199         PED_ASSERT (geom != NULL, return NULL);
 200 
 201         if (!hfsc_can_use_geom (geom))
 202                 return NULL;
 203 
 204         if ((geom->length < 5)
 205             || !ped_geometry_read (geom, buf, 2, 1)
 206             || (vh->signature != PED_CPU_TO_BE16 (HFSX_SIGNATURE)))
 207                 return NULL;
 208 
 209         /* unlike the hfs+ code, which should be kept compatible
 210         with my old previous implementations, we only care here
 211         about legal alternate VH positions, like TN1150 describes them */
 212         max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1)
 213                       * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT )
 214                       - 2;
 215         search = max - ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT );
 216         if (!(geom_ret = ped_geometry_new (geom->dev, geom->start,
 217                                            search + 2)))
 218                 return NULL;
 219         for (; search < max; search++) {
 220                 if (!ped_geometry_set (geom_ret, geom_ret->start,
 221                                        search + 2)
 222                     || !ped_geometry_read (geom_ret, buf, search, 1))
 223                         break;
 224                 if (vh->signature == PED_CPU_TO_BE16 (HFSX_SIGNATURE))
 225                         return geom_ret;
 226         }
 227 
 228         ped_geometry_destroy (geom_ret);
 229         return NULL;
 230 }