| /* |
| libparted - a library for manipulating disk partitions |
| Copyright (C) 1999, 2000, 2005, 2007 Free Software Foundation, Inc. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| /** \file geom.c */ |
| |
| |
| /** |
| * \addtogroup PedGeometry |
| * |
| * \brief PedGeometry represents a continuous region on a device. All addressing |
| * through a PedGeometry object is in terms of the start of the continuous |
| * region. |
| * |
| * The following conditions are always true on a PedGeometry object manipulated |
| * with the GNU Parted API: |
| * |
| * - <tt>start + length - 1 == end</tt> |
| * - <tt>length > 0</tt> |
| * - <tt>start >= 0</tt> |
| * - <tt>end < dev->length</tt> |
| * |
| * @{ |
| */ |
| |
| #include <config.h> |
| |
| #include <parted/parted.h> |
| #include <parted/debug.h> |
| |
| #if ENABLE_NLS |
| # include <libintl.h> |
| # define _(String) dgettext (PACKAGE, String) |
| #else |
| # define _(String) (String) |
| #endif /* ENABLE_NLS */ |
| |
| /** |
| * Initialize the previously allocated PedGeometry \p geom. |
| */ |
| int |
| ped_geometry_init (PedGeometry* geom, const PedDevice* dev, |
| PedSector start, PedSector length) |
| { |
| PED_ASSERT (geom != NULL, return 0); |
| PED_ASSERT (dev != NULL, return 0); |
| |
| geom->dev = (PedDevice*) dev; |
| return ped_geometry_set (geom, start, length); |
| } |
| |
| /** |
| * Create a new PedGeometry object on \p disk, starting at \p start with a |
| * size of \p length sectors. |
| * |
| * \return NULL on failure. |
| */ |
| PedGeometry* |
| ped_geometry_new (const PedDevice* dev, PedSector start, PedSector length) |
| { |
| PedGeometry* geom; |
| |
| PED_ASSERT (dev != NULL, return NULL); |
| |
| geom = (PedGeometry*) ped_malloc (sizeof (PedGeometry)); |
| if (!geom) |
| goto error; |
| if (!ped_geometry_init (geom, dev, start, length)) |
| goto error_free_geom; |
| return geom; |
| |
| error_free_geom: |
| ped_free (geom); |
| error: |
| return NULL; |
| } |
| |
| /** |
| * Duplicate a PedGeometry object. |
| * |
| * This function constructs a PedGeometry object that is an identical but |
| * independent copy of \p geom. Both the input, \p geom, and the output |
| * should be destroyed with ped_geometry_destroy() when they are no |
| * longer needed. |
| * |
| * \return NULL on failure. |
| */ |
| PedGeometry* |
| ped_geometry_duplicate (const PedGeometry* geom) |
| { |
| PED_ASSERT (geom != NULL, return NULL); |
| return ped_geometry_new (geom->dev, geom->start, geom->length); |
| } |
| |
| /** |
| * Return a PedGeometry object that refers to the intersection of |
| * \p a and \p b. |
| * |
| * This function constructs a PedGeometry object that describes the |
| * region that is common to both a and b. If there is no such common |
| * region, it returns NULL. (This situation is not treated as an |
| * error by much of GNU Parted.) |
| */ |
| PedGeometry* |
| ped_geometry_intersect (const PedGeometry* a, const PedGeometry* b) |
| { |
| PedSector start; |
| PedSector end; |
| |
| if (!a || !b || a->dev != b->dev) |
| return NULL; |
| |
| start = PED_MAX (a->start, b->start); |
| end = PED_MIN (a->end, b->end); |
| if (start > end) |
| return NULL; |
| |
| return ped_geometry_new (a->dev, start, end - start + 1); |
| } |
| |
| /** |
| * Destroy a PedGeometry object. |
| */ |
| void |
| ped_geometry_destroy (PedGeometry* geom) |
| { |
| PED_ASSERT (geom != NULL, return); |
| |
| ped_free (geom); |
| } |
| |
| /** |
| * Assign a new \p start, \p end (implicitly) and \p length to \p geom. |
| * |
| * \p geom->end is calculated from \p start and \p length. |
| */ |
| int |
| ped_geometry_set (PedGeometry* geom, PedSector start, PedSector length) |
| { |
| PED_ASSERT (geom != NULL, return 0); |
| PED_ASSERT (geom->dev != NULL, return 0); |
| |
| if (length < 1) { |
| ped_exception_throw ( |
| PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_CANCEL, |
| _("Can't have the end before the start!")); |
| return 0; |
| } |
| if (start < 0 || start + length - 1 >= geom->dev->length) { |
| ped_exception_throw ( |
| PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_CANCEL, |
| _("Can't have a partition outside the disk!")); |
| return 0; |
| } |
| |
| geom->start = start; |
| geom->length = length; |
| geom->end = start + length - 1; |
| |
| return 1; |
| } |
| |
| /** |
| * Assign a new start to \p geom without changing \p geom->end. |
| * |
| * \p geom->length is updated accordingly. |
| */ |
| int |
| ped_geometry_set_start (PedGeometry* geom, PedSector start) |
| { |
| return ped_geometry_set (geom, start, geom->end - start + 1); |
| } |
| |
| /** |
| * Assign a new end to \p geom without changing \p geom->start. |
| * |
| * \p geom->length is updated accordingly. |
| */ |
| int |
| ped_geometry_set_end (PedGeometry* geom, PedSector end) |
| { |
| return ped_geometry_set (geom, geom->start, end - geom->start + 1); |
| } |
| /** |
| * Test if \p a overlaps with \p b. |
| * |
| * That is, they lie on the same physical device, and they share |
| * the same physical region at least partially. |
| * |
| * \return 1 if \p a and \p b overlap. |
| */ |
| int |
| ped_geometry_test_overlap (const PedGeometry* a, const PedGeometry* b) |
| { |
| PED_ASSERT (a != NULL, return 0); |
| PED_ASSERT (b != NULL, return 0); |
| |
| if (a->dev != b->dev) |
| return 0; |
| |
| if (a->start < b->start) |
| return a->end >= b->start; |
| else |
| return b->end >= a->start; |
| } |
| |
| /** |
| * Tests if \p b lies completely within \p a. That is, they lie on the same |
| * physical device, and all of the \p b's region is contained inside |
| * \p a's. |
| * |
| * \return 1 if the region \p b describes is contained entirely inside \p a |
| */ |
| int |
| ped_geometry_test_inside (const PedGeometry* a, const PedGeometry* b) |
| { |
| PED_ASSERT (a != NULL, return 0); |
| PED_ASSERT (b != NULL, return 0); |
| |
| if (a->dev != b->dev) |
| return 0; |
| |
| return b->start >= a->start && b->end <= a->end; |
| } |
| |
| /** |
| * Tests if \a a and \p b refer to the same physical region. |
| * |
| * \return 1 if \p a and \p b describe the same regions |
| * |
| */ |
| int |
| ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b) |
| { |
| PED_ASSERT (a != NULL, return 0); |
| PED_ASSERT (b != NULL, return 0); |
| |
| return a->dev == b->dev |
| && a->start == b->start |
| && a->end == b->end; |
| } |
| |
| /** |
| * Tests if \p sector is inside \p geom. |
| * |
| * \return 1 if sector lies within the \p region that \p geom describes |
| */ |
| int |
| ped_geometry_test_sector_inside (const PedGeometry* geom, PedSector sector) |
| { |
| PED_ASSERT (geom != NULL, return 0); |
| |
| return sector >= geom->start && sector <= geom->end; |
| } |
| |
| /** |
| * Reads data from the region represented by \p geom. \p offset is the |
| * location from within the region, not from the start of the disk. |
| * \p count sectors are read into \p buffer. |
| * This is essentially equivalent to: |
| * \code |
| * ped_device_read (geom->disk->dev, buffer, geom->start + offset, count) |
| * \endcode |
| * |
| * \throws PED_EXCEPTION_ERROR when attempting to read sectors outside of |
| * partition |
| * |
| * \return 0 on failure |
| */ |
| int |
| ped_geometry_read (const PedGeometry* geom, void* buffer, PedSector offset, |
| PedSector count) |
| { |
| PedSector real_start; |
| |
| PED_ASSERT (geom != NULL, return 0); |
| PED_ASSERT (buffer != NULL, return 0); |
| PED_ASSERT (offset >= 0, return 0); |
| PED_ASSERT (count >= 0, return 0); |
| |
| real_start = geom->start + offset; |
| |
| if (real_start + count - 1 > geom->end) |
| return 0; |
| |
| if (!ped_device_read (geom->dev, buffer, real_start, count)) |
| return 0; |
| return 1; |
| } |
| |
| /** |
| * Flushes the cache on \p geom. |
| * |
| * This function flushes all write-behind caches that might be holding |
| * writes made by ped_geometry_write() to \p geom. It is slow, because |
| * it guarantees cache coherency among all relevant caches. |
| * |
| * \return 0 on failure |
| */ |
| int |
| ped_geometry_sync (PedGeometry* geom) |
| { |
| PED_ASSERT (geom != NULL, return 0); |
| return ped_device_sync (geom->dev); |
| } |
| |
| /** |
| * Flushes the cache on \p geom. |
| * |
| * This function flushes all write-behind caches that might be holding writes |
| * made by ped_geometry_write() to \p geom. It does NOT ensure cache coherency |
| * with other caches that cache data in the region described by \p geom. |
| * If you need cache coherency, use ped_geometry_sync() instead. |
| * |
| * \return 0 on failure |
| */ |
| int |
| ped_geometry_sync_fast (PedGeometry* geom) |
| { |
| PED_ASSERT (geom != NULL, return 0); |
| return ped_device_sync_fast (geom->dev); |
| } |
| |
| /** |
| * Writes data into the region represented by \p geom. \p offset is the |
| * location from within the region, not from the start of the disk. |
| * \p count sectors are written. |
| * |
| * \return 0 on failure |
| */ |
| int |
| ped_geometry_write (PedGeometry* geom, const void* buffer, PedSector offset, |
| PedSector count) |
| { |
| int exception_status; |
| PedSector real_start; |
| |
| PED_ASSERT (geom != NULL, return 0); |
| PED_ASSERT (buffer != NULL, return 0); |
| PED_ASSERT (offset >= 0, return 0); |
| PED_ASSERT (count >= 0, return 0); |
| |
| real_start = geom->start + offset; |
| |
| if (real_start + count - 1 > geom->end) { |
| exception_status = ped_exception_throw ( |
| PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_IGNORE_CANCEL, |
| _("Attempt to write sectors %ld-%ld outside of " |
| "partition on %s."), |
| (long) offset, (long) (offset + count - 1), |
| geom->dev->path); |
| return exception_status == PED_EXCEPTION_IGNORE; |
| } |
| |
| if (!ped_device_write (geom->dev, buffer, real_start, count)) |
| return 0; |
| return 1; |
| } |
| |
| /** |
| * Checks for physical disk errors. \todo use ped_device_check() |
| * |
| * Checks a region for physical defects on \p geom. \p buffer is used |
| * for temporary storage for ped_geometry_check(), and has an undefined |
| * value. \p buffer is \p buffer_size sectors long. |
| * The region checked starts at \p offset sectors inside the |
| * region represented by \p geom, and is \p count sectors long. |
| * \p granularity specificies how sectors should be grouped |
| * together. The first bad sector to be returned will always be in |
| * the form: |
| * <tt>offset + n * granularity</tt> |
| * |
| * \return the first bad sector, or 0 if there were no physical errors |
| */ |
| PedSector |
| ped_geometry_check (PedGeometry* geom, void* buffer, PedSector buffer_size, |
| PedSector offset, PedSector granularity, PedSector count, |
| PedTimer* timer) |
| { |
| PedSector group; |
| PedSector i; |
| PedSector read_len; |
| |
| PED_ASSERT (geom != NULL, return 0); |
| PED_ASSERT (buffer != NULL, return 0); |
| |
| ped_timer_reset (timer); |
| ped_timer_set_state_name (timer, _("checking for bad blocks")); |
| |
| retry: |
| ped_exception_fetch_all(); |
| for (group = offset; group < offset + count; group += buffer_size) { |
| ped_timer_update (timer, 1.0 * (group - offset) / count); |
| read_len = PED_MIN (buffer_size, offset + count - group); |
| if (!ped_geometry_read (geom, buffer, group, read_len)) |
| goto found_error; |
| } |
| ped_exception_leave_all(); |
| ped_timer_update (timer, 1.0); |
| return 0; |
| |
| found_error: |
| ped_exception_catch(); |
| for (i = group; i + granularity < group + count; i += granularity) { |
| if (!ped_geometry_read (geom, buffer, i, granularity)) { |
| ped_exception_catch(); |
| ped_exception_leave_all(); |
| return i; |
| } |
| } |
| ped_exception_leave_all(); |
| goto retry; /* weird: failure on group read, but not individually */ |
| } |
| |
| /** |
| * This function takes a \p sector inside the region described by src, and |
| * returns that sector's address inside dst. This means that |
| * |
| * \code |
| * ped_geometry_read (dst, buf, ped_geometry_map(dst, src, sector), 1) |
| * \endcode |
| * |
| * does the same thing as |
| * |
| * \code |
| * ped_geometry_read (src, buf, sector, 1) |
| * \endcode |
| * |
| * Clearly, this will only work if \p src and \p dst overlap. |
| * |
| * \return -1 if \p sector is not within \p dst's space, |
| * or \p sector's address inside \p dst |
| * |
| */ |
| PedSector |
| ped_geometry_map (const PedGeometry* dst, const PedGeometry* src, |
| PedSector sector) |
| { |
| PedSector result; |
| |
| PED_ASSERT (dst != NULL, return 0); |
| PED_ASSERT (src != NULL, return 0); |
| |
| if (!ped_geometry_test_sector_inside (src, sector)) |
| return -1; |
| if (dst->dev != src->dev) |
| return -1; |
| |
| result = src->start + sector - dst->start; |
| if (result < 0 || result > dst->length) |
| return -1; |
| |
| return result; |
| } |
| |
| /** @} */ |
| |