1 /* 2 libparted - a library for manipulating disk partitions 3 Copyright (C) 1999, 2000, 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 /** \file geom.c */ 20 21 22 /** 23 * \addtogroup PedGeometry 24 * 25 * \brief PedGeometry represents a continuous region on a device. All addressing 26 * through a PedGeometry object is in terms of the start of the continuous 27 * region. 28 * 29 * The following conditions are always true on a PedGeometry object manipulated 30 * with the GNU Parted API: 31 * 32 * - <tt>start + length - 1 == end</tt> 33 * - <tt>length > 0</tt> 34 * - <tt>start >= 0</tt> 35 * - <tt>end < dev->length</tt> 36 * 37 * @{ 38 */ 39 40 #include <config.h> 41 42 #include <parted/parted.h> 43 #include <parted/debug.h> 44 45 #if ENABLE_NLS 46 # include <libintl.h> 47 # define _(String) dgettext (PACKAGE, String) 48 #else 49 # define _(String) (String) 50 #endif /* ENABLE_NLS */ 51 52 /** 53 * Initialize the previously allocated PedGeometry \p geom. 54 */ 55 int 56 ped_geometry_init (PedGeometry* geom, const PedDevice* dev, 57 PedSector start, PedSector length) 58 { 59 PED_ASSERT (geom != NULL, return 0); 60 PED_ASSERT (dev != NULL, return 0); 61 62 geom->dev = (PedDevice*) dev; 63 return ped_geometry_set (geom, start, length); 64 } 65 66 /** 67 * Create a new PedGeometry object on \p disk, starting at \p start with a 68 * size of \p length sectors. 69 * 70 * \return NULL on failure. 71 */ 72 PedGeometry* 73 ped_geometry_new (const PedDevice* dev, PedSector start, PedSector length) 74 { 75 PedGeometry* geom; 76 77 PED_ASSERT (dev != NULL, return NULL); 78 79 geom = (PedGeometry*) ped_malloc (sizeof (PedGeometry)); 80 if (!geom) 81 goto error; 82 if (!ped_geometry_init (geom, dev, start, length)) 83 goto error_free_geom; 84 return geom; 85 86 error_free_geom: 87 ped_free (geom); 88 error: 89 return NULL; 90 } 91 92 /** 93 * Duplicate a PedGeometry object. 94 * 95 * This function constructs a PedGeometry object that is an identical but 96 * independent copy of \p geom. Both the input, \p geom, and the output 97 * should be destroyed with ped_geometry_destroy() when they are no 98 * longer needed. 99 * 100 * \return NULL on failure. 101 */ 102 PedGeometry* 103 ped_geometry_duplicate (const PedGeometry* geom) 104 { 105 PED_ASSERT (geom != NULL, return NULL); 106 return ped_geometry_new (geom->dev, geom->start, geom->length); 107 } 108 109 /** 110 * Return a PedGeometry object that refers to the intersection of 111 * \p a and \p b. 112 * 113 * This function constructs a PedGeometry object that describes the 114 * region that is common to both a and b. If there is no such common 115 * region, it returns NULL. (This situation is not treated as an 116 * error by much of GNU Parted.) 117 */ 118 PedGeometry* 119 ped_geometry_intersect (const PedGeometry* a, const PedGeometry* b) 120 { 121 PedSector start; 122 PedSector end; 123 124 if (!a || !b || a->dev != b->dev) 125 return NULL; 126 127 start = PED_MAX (a->start, b->start); 128 end = PED_MIN (a->end, b->end); 129 if (start > end) 130 return NULL; 131 132 return ped_geometry_new (a->dev, start, end - start + 1); 133 } 134 135 /** 136 * Destroy a PedGeometry object. 137 */ 138 void 139 ped_geometry_destroy (PedGeometry* geom) 140 { 141 PED_ASSERT (geom != NULL, return); 142 143 ped_free (geom); 144 } 145 146 /** 147 * Assign a new \p start, \p end (implicitly) and \p length to \p geom. 148 * 149 * \p geom->end is calculated from \p start and \p length. 150 */ 151 int 152 ped_geometry_set (PedGeometry* geom, PedSector start, PedSector length) 153 { 154 PED_ASSERT (geom != NULL, return 0); 155 PED_ASSERT (geom->dev != NULL, return 0); 156 157 if (length < 1) { 158 ped_exception_throw ( 159 PED_EXCEPTION_ERROR, 160 PED_EXCEPTION_CANCEL, 161 _("Can't have the end before the start!")); 162 return 0; 163 } 164 if (start < 0 || start + length - 1 >= geom->dev->length) { 165 ped_exception_throw ( 166 PED_EXCEPTION_ERROR, 167 PED_EXCEPTION_CANCEL, 168 _("Can't have a partition outside the disk!")); 169 return 0; 170 } 171 172 geom->start = start; 173 geom->length = length; 174 geom->end = start + length - 1; 175 176 return 1; 177 } 178 179 /** 180 * Assign a new start to \p geom without changing \p geom->end. 181 * 182 * \p geom->length is updated accordingly. 183 */ 184 int 185 ped_geometry_set_start (PedGeometry* geom, PedSector start) 186 { 187 return ped_geometry_set (geom, start, geom->end - start + 1); 188 } 189 190 /** 191 * Assign a new end to \p geom without changing \p geom->start. 192 * 193 * \p geom->length is updated accordingly. 194 */ 195 int 196 ped_geometry_set_end (PedGeometry* geom, PedSector end) 197 { 198 return ped_geometry_set (geom, geom->start, end - geom->start + 1); 199 } 200 /** 201 * Test if \p a overlaps with \p b. 202 * 203 * That is, they lie on the same physical device, and they share 204 * the same physical region at least partially. 205 * 206 * \return 1 if \p a and \p b overlap. 207 */ 208 int 209 ped_geometry_test_overlap (const PedGeometry* a, const PedGeometry* b) 210 { 211 PED_ASSERT (a != NULL, return 0); 212 PED_ASSERT (b != NULL, return 0); 213 214 if (a->dev != b->dev) 215 return 0; 216 217 if (a->start < b->start) 218 return a->end >= b->start; 219 else 220 return b->end >= a->start; 221 } 222 223 /** 224 * Tests if \p b lies completely within \p a. That is, they lie on the same 225 * physical device, and all of the \p b's region is contained inside 226 * \p a's. 227 * 228 * \return 1 if the region \p b describes is contained entirely inside \p a 229 */ 230 int 231 ped_geometry_test_inside (const PedGeometry* a, const PedGeometry* b) 232 { 233 PED_ASSERT (a != NULL, return 0); 234 PED_ASSERT (b != NULL, return 0); 235 236 if (a->dev != b->dev) 237 return 0; 238 239 return b->start >= a->start && b->end <= a->end; 240 } 241 242 /** 243 * Tests if \a a and \p b refer to the same physical region. 244 * 245 * \return 1 if \p a and \p b describe the same regions 246 * 247 */ 248 int 249 ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b) 250 { 251 PED_ASSERT (a != NULL, return 0); 252 PED_ASSERT (b != NULL, return 0); 253 254 return a->dev == b->dev 255 && a->start == b->start 256 && a->end == b->end; 257 } 258 259 /** 260 * Tests if \p sector is inside \p geom. 261 * 262 * \return 1 if sector lies within the \p region that \p geom describes 263 */ 264 int 265 ped_geometry_test_sector_inside (const PedGeometry* geom, PedSector sector) 266 { 267 PED_ASSERT (geom != NULL, return 0); 268 269 return sector >= geom->start && sector <= geom->end; 270 } 271 272 /** 273 * Reads data from the region represented by \p geom. \p offset is the 274 * location from within the region, not from the start of the disk. 275 * \p count sectors are read into \p buffer. 276 * This is essentially equivalent to: 277 * \code 278 * ped_device_read (geom->disk->dev, buffer, geom->start + offset, count) 279 * \endcode 280 * 281 * \throws PED_EXCEPTION_ERROR when attempting to read sectors outside of 282 * partition 283 * 284 * \return 0 on failure 285 */ 286 int 287 ped_geometry_read (const PedGeometry* geom, void* buffer, PedSector offset, 288 PedSector count) 289 { 290 PedSector real_start; 291 292 PED_ASSERT (geom != NULL, return 0); 293 PED_ASSERT (buffer != NULL, return 0); 294 PED_ASSERT (offset >= 0, return 0); 295 PED_ASSERT (count >= 0, return 0); 296 297 real_start = geom->start + offset; 298 299 if (real_start + count - 1 > geom->end) 300 return 0; 301 302 if (!ped_device_read (geom->dev, buffer, real_start, count)) 303 return 0; 304 return 1; 305 } 306 307 /** 308 * Flushes the cache on \p geom. 309 * 310 * This function flushes all write-behind caches that might be holding 311 * writes made by ped_geometry_write() to \p geom. It is slow, because 312 * it guarantees cache coherency among all relevant caches. 313 * 314 * \return 0 on failure 315 */ 316 int 317 ped_geometry_sync (PedGeometry* geom) 318 { 319 PED_ASSERT (geom != NULL, return 0); 320 return ped_device_sync (geom->dev); 321 } 322 323 /** 324 * Flushes the cache on \p geom. 325 * 326 * This function flushes all write-behind caches that might be holding writes 327 * made by ped_geometry_write() to \p geom. It does NOT ensure cache coherency 328 * with other caches that cache data in the region described by \p geom. 329 * If you need cache coherency, use ped_geometry_sync() instead. 330 * 331 * \return 0 on failure 332 */ 333 int 334 ped_geometry_sync_fast (PedGeometry* geom) 335 { 336 PED_ASSERT (geom != NULL, return 0); 337 return ped_device_sync_fast (geom->dev); 338 } 339 340 /** 341 * Writes data into the region represented by \p geom. \p offset is the 342 * location from within the region, not from the start of the disk. 343 * \p count sectors are written. 344 * 345 * \return 0 on failure 346 */ 347 int 348 ped_geometry_write (PedGeometry* geom, const void* buffer, PedSector offset, 349 PedSector count) 350 { 351 int exception_status; 352 PedSector real_start; 353 354 PED_ASSERT (geom != NULL, return 0); 355 PED_ASSERT (buffer != NULL, return 0); 356 PED_ASSERT (offset >= 0, return 0); 357 PED_ASSERT (count >= 0, return 0); 358 359 real_start = geom->start + offset; 360 361 if (real_start + count - 1 > geom->end) { 362 exception_status = ped_exception_throw ( 363 PED_EXCEPTION_ERROR, 364 PED_EXCEPTION_IGNORE_CANCEL, 365 _("Attempt to write sectors %ld-%ld outside of " 366 "partition on %s."), 367 (long) offset, (long) (offset + count - 1), 368 geom->dev->path); 369 return exception_status == PED_EXCEPTION_IGNORE; 370 } 371 372 if (!ped_device_write (geom->dev, buffer, real_start, count)) 373 return 0; 374 return 1; 375 } 376 377 /** 378 * Checks for physical disk errors. \todo use ped_device_check() 379 * 380 * Checks a region for physical defects on \p geom. \p buffer is used 381 * for temporary storage for ped_geometry_check(), and has an undefined 382 * value. \p buffer is \p buffer_size sectors long. 383 * The region checked starts at \p offset sectors inside the 384 * region represented by \p geom, and is \p count sectors long. 385 * \p granularity specificies how sectors should be grouped 386 * together. The first bad sector to be returned will always be in 387 * the form: 388 * <tt>offset + n * granularity</tt> 389 * 390 * \return the first bad sector, or 0 if there were no physical errors 391 */ 392 PedSector 393 ped_geometry_check (PedGeometry* geom, void* buffer, PedSector buffer_size, 394 PedSector offset, PedSector granularity, PedSector count, 395 PedTimer* timer) 396 { 397 PedSector group; 398 PedSector i; 399 PedSector read_len; 400 401 PED_ASSERT (geom != NULL, return 0); 402 PED_ASSERT (buffer != NULL, return 0); 403 404 ped_timer_reset (timer); 405 ped_timer_set_state_name (timer, _("checking for bad blocks")); 406 407 retry: 408 ped_exception_fetch_all(); 409 for (group = offset; group < offset + count; group += buffer_size) { 410 ped_timer_update (timer, 1.0 * (group - offset) / count); 411 read_len = PED_MIN (buffer_size, offset + count - group); 412 if (!ped_geometry_read (geom, buffer, group, read_len)) 413 goto found_error; 414 } 415 ped_exception_leave_all(); 416 ped_timer_update (timer, 1.0); 417 return 0; 418 419 found_error: 420 ped_exception_catch(); 421 for (i = group; i + granularity < group + count; i += granularity) { 422 if (!ped_geometry_read (geom, buffer, i, granularity)) { 423 ped_exception_catch(); 424 ped_exception_leave_all(); 425 return i; 426 } 427 } 428 ped_exception_leave_all(); 429 goto retry; /* weird: failure on group read, but not individually */ 430 } 431 432 /** 433 * This function takes a \p sector inside the region described by src, and 434 * returns that sector's address inside dst. This means that 435 * 436 * \code 437 * ped_geometry_read (dst, buf, ped_geometry_map(dst, src, sector), 1) 438 * \endcode 439 * 440 * does the same thing as 441 * 442 * \code 443 * ped_geometry_read (src, buf, sector, 1) 444 * \endcode 445 * 446 * Clearly, this will only work if \p src and \p dst overlap. 447 * 448 * \return -1 if \p sector is not within \p dst's space, 449 * or \p sector's address inside \p dst 450 * 451 */ 452 PedSector 453 ped_geometry_map (const PedGeometry* dst, const PedGeometry* src, 454 PedSector sector) 455 { 456 PedSector result; 457 458 PED_ASSERT (dst != NULL, return 0); 459 PED_ASSERT (src != NULL, return 0); 460 461 if (!ped_geometry_test_sector_inside (src, sector)) 462 return -1; 463 if (dst->dev != src->dev) 464 return -1; 465 466 result = src->start + sector - dst->start; 467 if (result < 0 || result > dst->length) 468 return -1; 469 470 return result; 471 } 472 473 /** @} */ 474