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 }