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
ped_geometry_init(PedGeometry * geom,const PedDevice * dev,PedSector start,PedSector length)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*
ped_geometry_new(const PedDevice * dev,PedSector start,PedSector length)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*
ped_geometry_duplicate(const PedGeometry * geom)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*
ped_geometry_intersect(const PedGeometry * a,const PedGeometry * b)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
ped_geometry_destroy(PedGeometry * geom)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
ped_geometry_set(PedGeometry * geom,PedSector start,PedSector length)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
ped_geometry_set_start(PedGeometry * geom,PedSector start)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
ped_geometry_set_end(PedGeometry * geom,PedSector end)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
ped_geometry_test_overlap(const PedGeometry * a,const PedGeometry * b)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
ped_geometry_test_inside(const PedGeometry * a,const PedGeometry * b)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
ped_geometry_test_equal(const PedGeometry * a,const PedGeometry * b)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
ped_geometry_test_sector_inside(const PedGeometry * geom,PedSector sector)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
ped_geometry_read(const PedGeometry * geom,void * buffer,PedSector offset,PedSector count)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
ped_geometry_sync(PedGeometry * geom)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
ped_geometry_sync_fast(PedGeometry * geom)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
ped_geometry_write(PedGeometry * geom,const void * buffer,PedSector offset,PedSector count)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
ped_geometry_check(PedGeometry * geom,void * buffer,PedSector buffer_size,PedSector offset,PedSector granularity,PedSector count,PedTimer * timer)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
ped_geometry_map(const PedGeometry * dst,const PedGeometry * src,PedSector sector)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
475