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