xref: /onnv-gate/usr/src/lib/libparted/common/libparted/cs/geom.c (revision 9663:ace9a2ac3683)
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