/* libparted - a library for manipulating disk partitions Copyright (C) 1999-2000, 2005, 2007-2014, 2019-2022 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 3 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, see . */ /** \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: * * - start + length - 1 == end * - length > 0 * - start >= 0 * - end < dev->length * * @{ */ #include #include #include #if ENABLE_NLS # include # 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); PED_ASSERT (dev != NULL); 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); 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: 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 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); 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); PED_ASSERT (geom->dev != NULL); PED_ASSERT (start >= 0); if (length < 1) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Can't have the end before the start!" " (start sector=%jd length=%jd)"), start, length); 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); PED_ASSERT (b != NULL); 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); PED_ASSERT (b != NULL); 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); PED_ASSERT (b != NULL); 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 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); PED_ASSERT (buffer != NULL); PED_ASSERT (offset >= 0); PED_ASSERT (count >= 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; } /* Like ped_device_read, but read into malloc'd storage. */ int ped_geometry_read_alloc (const PedGeometry* geom, void** buffer, PedSector offset, PedSector count) { char *buf = ped_malloc (count * geom->dev->sector_size); if (buf == NULL) return 0; int ok = ped_geometry_read (geom, buf, offset, count); if (!ok) { free (buf); buf = NULL; } *buffer = buf; return ok; } /** * 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 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 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); PED_ASSERT (buffer != NULL); PED_ASSERT (offset >= 0); PED_ASSERT (count >= 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: * offset + n * granularity * * \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); PED_ASSERT (buffer != NULL); 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); PED_ASSERT (src != NULL); 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; } /** @} */