2 libparted - a library for manipulating disk partitions
3 Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
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.
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.
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/>.
19 /** \file filesys.c */
22 * \addtogroup PedFileSystem
24 * \note File systems exist on a PedGeometry - NOT a PedPartition.
31 #include <parted/parted.h>
32 #include <parted/debug.h>
36 # define _(String) dgettext (PACKAGE, String)
38 # define _(String) (String)
39 #endif /* ENABLE_NLS */
41 #define BUFFER_SIZE 4096 /* in sectors */
43 static PedFileSystemType* fs_types = NULL;
46 ped_file_system_type_register (PedFileSystemType* fs_type)
48 PED_ASSERT (fs_type != NULL, return);
49 PED_ASSERT (fs_type->ops != NULL, return);
50 PED_ASSERT (fs_type->name != NULL, return);
52 fs_type->next = fs_types;
57 ped_file_system_type_unregister (PedFileSystemType* fs_type)
59 PedFileSystemType* walk;
60 PedFileSystemType* last = NULL;
62 PED_ASSERT (fs_types != NULL, return);
63 PED_ASSERT (fs_type != NULL, return);
65 for (walk = fs_types; walk && walk != fs_type;
66 last = walk, walk = walk->next);
68 PED_ASSERT (walk != NULL, return);
70 ((struct _PedFileSystemType*) last)->next = fs_type->next;
72 fs_types = fs_type->next;
76 * Get a PedFileSystemType by its @p name.
78 * @return @c NULL if none found.
81 ped_file_system_type_get (const char* name)
83 PedFileSystemType* walk;
85 PED_ASSERT (name != NULL, return NULL);
87 for (walk = fs_types; walk != NULL; walk = walk->next) {
88 if (!strcasecmp (walk->name, name))
95 * Get the next PedFileSystemType after @p fs_type.
97 * @return @c NULL if @p fs_type is the last item in the list.
100 ped_file_system_type_get_next (const PedFileSystemType* fs_type)
103 return fs_type->next;
109 * Attempt to find a file system and return the region it occupies.
111 * @param fs_type The file system type to probe for.
112 * @param geom The region to be searched.
114 * @return @p NULL if @p fs_type file system wasn't detected
117 ped_file_system_probe_specific (
118 const PedFileSystemType* fs_type, PedGeometry* geom)
122 PED_ASSERT (fs_type != NULL, return NULL);
123 PED_ASSERT (fs_type->ops->probe != NULL, return NULL);
124 PED_ASSERT (geom != NULL, return NULL);
126 if (!ped_device_open (geom->dev))
128 result = fs_type->ops->probe (geom);
129 ped_device_close (geom->dev);
134 _test_open (PedFileSystemType* fs_type, PedGeometry* geom)
138 ped_exception_fetch_all ();
139 fs = fs_type->ops->open (geom);
141 fs_type->ops->close (fs);
143 ped_exception_catch ();
144 ped_exception_leave_all ();
148 static PedFileSystemType*
149 _probe_with_open (PedGeometry* geom, int detected_count,
150 PedFileSystemType* detected[])
153 PedFileSystemType* open_detected = NULL;
155 ped_device_open (geom->dev);
157 /* If one and only one file system that Parted is able to open
158 * can be successfully opened on this geometry, return it.
159 * If more than one can be, return NULL.
161 for (i=0; i<detected_count; i++) {
162 if (!detected[i]->ops->open || !_test_open (detected [i], geom))
166 ped_device_close (geom->dev);
169 open_detected = detected [i];
173 /* If no file system has been successfully opened, and
174 * if Parted has detected at most one unopenable file system,
178 for (i=0; i<detected_count; i++) {
179 if (detected[i]->ops->open)
182 ped_device_close (geom->dev);
185 open_detected = detected [i];
189 ped_device_close (geom->dev);
190 return open_detected;
194 _geometry_error (const PedGeometry* a, const PedGeometry* b)
196 PedSector start_delta = a->start - b->start;
197 PedSector end_delta = a->end - b->end;
199 return abs (start_delta) + abs (end_delta);
202 static PedFileSystemType*
203 _best_match (const PedGeometry* geom, PedFileSystemType* detected [],
204 const int detected_error [], int detected_count)
210 min_error = PED_MAX (4096, geom->length / 100);
212 for (i = 1; i < detected_count; i++) {
213 if (detected_error [i] < detected_error [best_match])
217 /* make sure the best match is significantly better than all the
220 for (i = 0; i < detected_count; i++) {
224 if (abs (detected_error [best_match] - detected_error [i])
229 return detected [best_match];
234 * Attempt to detect a file system in region \p geom.
235 * This function tries to be clever at dealing with ambiguous
236 * situations, such as when one file system was not completely erased before a
237 * new file system was created on top of it.
239 * \return a new PedFileSystem on success, \c NULL on failure
242 ped_file_system_probe (PedGeometry* geom)
244 PedFileSystemType* detected[32];
245 int detected_error[32];
246 int detected_count = 0;
247 PedFileSystemType* walk = NULL;
249 PED_ASSERT (geom != NULL, return NULL);
251 if (!ped_device_open (geom->dev))
254 ped_exception_fetch_all ();
255 while ( (walk = ped_file_system_type_get_next (walk)) ) {
258 probed = ped_file_system_probe_specific (walk, geom);
260 detected [detected_count] = walk;
261 detected_error [detected_count]
262 = _geometry_error (geom, probed);
264 ped_geometry_destroy (probed);
266 ped_exception_catch ();
269 ped_exception_leave_all ();
271 ped_device_close (geom->dev);
275 walk = _best_match (geom, detected, detected_error, detected_count);
278 return _probe_with_open (geom, detected_count, detected);
282 * This function erases all file system signatures that indicate that a
283 * file system occupies a given region described by \p geom.
284 * After this operation ped_file_system_probe() won't detect any file system.
286 * \note ped_file_system_create() calls this before creating a new file system.
288 * \return \c 1 on success, \c 0 on failure
291 ped_file_system_clobber (PedGeometry* geom)
293 PedFileSystemType* fs_type = NULL;
295 PED_ASSERT (geom != NULL, return 0);
297 if (!ped_device_open (geom->dev))
300 ped_exception_fetch_all ();
301 while ((fs_type = ped_file_system_type_get_next (fs_type))) {
304 if (!fs_type->ops->clobber)
307 probed = ped_file_system_probe_specific (fs_type, geom);
309 ped_exception_catch ();
312 ped_geometry_destroy (probed);
314 if (fs_type->ops->clobber && !fs_type->ops->clobber (geom)) {
315 ped_exception_leave_all ();
316 goto error_close_dev;
319 ped_device_close (geom->dev);
320 ped_exception_leave_all ();
324 ped_device_close (geom->dev);
329 /* This function erases all signatures that indicate the presence of
330 * a file system in a particular region, without erasing any data
331 * contained inside the "exclude" region.
334 ped_file_system_clobber_exclude (PedGeometry* geom,
335 const PedGeometry* exclude)
337 PedGeometry* clobber_geom;
340 if (ped_geometry_test_sector_inside (exclude, geom->start))
343 clobber_geom = ped_geometry_duplicate (geom);
344 if (ped_geometry_test_overlap (clobber_geom, exclude))
345 ped_geometry_set_end (clobber_geom, exclude->start - 1);
347 status = ped_file_system_clobber (clobber_geom);
348 ped_geometry_destroy (clobber_geom);
353 * This function opens the file system stored on \p geom, if it
355 * It is often called in the following manner:
357 * fs = ped_file_system_open (&part.geom)
360 * \throws PED_EXCEPTION_ERROR if file system could not be detected
361 * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume
362 * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on
363 * \p geom is not implemented
365 * \return a PedFileSystem on success, \c NULL on failure.
368 ped_file_system_open (PedGeometry* geom)
370 PedFileSystemType* type;
372 PedGeometry* probed_geom;
374 PED_ASSERT (geom != NULL, return NULL);
376 if (!ped_device_open (geom->dev))
379 type = ped_file_system_probe (geom);
381 ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
382 _("Could not detect file system."));
383 goto error_close_dev;
386 probed_geom = ped_file_system_probe_specific (type, geom);
388 goto error_close_dev;
389 if (!ped_geometry_test_inside (geom, probed_geom)) {
390 if (ped_exception_throw (
392 PED_EXCEPTION_IGNORE_CANCEL,
393 _("The file system is bigger than its volume!"))
394 != PED_EXCEPTION_IGNORE)
395 goto error_destroy_probed_geom;
398 if (!type->ops->open) {
399 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
400 PED_EXCEPTION_CANCEL,
401 _("Support for opening %s file systems "
402 "is not implemented yet."),
404 goto error_destroy_probed_geom;
407 fs = type->ops->open (probed_geom);
409 goto error_destroy_probed_geom;
410 ped_geometry_destroy (probed_geom);
413 error_destroy_probed_geom:
414 ped_geometry_destroy (probed_geom);
416 ped_device_close (geom->dev);
422 * This function initializes a new file system of type \p type on
423 * a region described by \p geom, writing out appropriate metadata and
424 * signatures. If \p timer is non-NULL, it is used as the progress meter.
426 * \throws PED_EXCEPTION_NO_FEATURE if creating file system type \p type
427 * is not implemented yet
429 * \return a PedFileSystem on success, \c NULL on failure
432 ped_file_system_create (PedGeometry* geom, const PedFileSystemType* type,
437 PED_ASSERT (geom != NULL, return NULL);
438 PED_ASSERT (type != NULL, return NULL);
440 if (!type->ops->create) {
441 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
442 PED_EXCEPTION_CANCEL,
443 _("Support for creating %s file systems "
444 "is not implemented yet."),
449 if (!ped_device_open (geom->dev))
452 if (!ped_file_system_clobber (geom))
453 goto error_close_dev;
454 fs = type->ops->create (geom, timer);
456 goto error_close_dev;
460 ped_device_close (geom->dev);
466 * Close file system \p fs.
468 * \return \c 1 on success, \c 0 on failure
471 ped_file_system_close (PedFileSystem* fs)
473 PedDevice* dev = fs->geom->dev;
475 PED_ASSERT (fs != NULL, goto error_close_dev);
477 if (!fs->type->ops->close (fs))
478 goto error_close_dev;
479 ped_device_close (dev);
483 ped_device_close (dev);
488 * Check \p fs file system for errors.
490 * \throws PED_EXCEPTION_NO_FEATURE if checking file system \p fs is
491 * not implemented yet
493 * \return \c 0 on failure (i.e. unfixed errors)
496 ped_file_system_check (PedFileSystem* fs, PedTimer* timer)
498 PED_ASSERT (fs != NULL, return 0);
500 if (!fs->type->ops->check) {
501 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
502 PED_EXCEPTION_CANCEL,
503 _("Support for checking %s file systems "
504 "is not implemented yet."),
508 return fs->type->ops->check (fs, timer);
512 _raw_copy (const PedGeometry* src, PedGeometry* dest, PedTimer* timer)
517 PED_ASSERT (src != NULL, goto error);
518 PED_ASSERT (dest != NULL, goto error);
519 PED_ASSERT (src->length <= dest->length, goto error);
521 buf = ped_malloc (BUFFER_SIZE * 512); /* FIXME */
525 if (!ped_device_open (src->dev))
527 if (!ped_device_open (dest->dev))
528 goto error_close_src;
530 for (pos = 0; pos + BUFFER_SIZE < src->length; pos += BUFFER_SIZE) {
531 ped_timer_update (timer, 1.0 * pos / src->length);
532 if (!ped_geometry_read (src, buf, pos, BUFFER_SIZE))
533 goto error_close_dest;
534 if (!ped_geometry_write (dest, buf, pos, BUFFER_SIZE))
535 goto error_close_dest;
537 if (pos < src->length) {
538 ped_timer_update (timer, 1.0 * pos / src->length);
539 if (!ped_geometry_read (src, buf, pos, src->length - pos))
540 goto error_close_dest;
541 if (!ped_geometry_write (dest, buf, pos, src->length - pos))
542 goto error_close_dest;
544 ped_timer_update (timer, 1.0);
546 ped_device_close (src->dev);
547 ped_device_close (dest->dev);
552 ped_device_close (dest->dev);
554 ped_device_close (src->dev);
561 static PedFileSystem*
562 _raw_copy_and_resize (const PedFileSystem* fs, PedGeometry* geom,
565 PedFileSystem* new_fs;
566 PedTimer* sub_timer = NULL;
568 ped_timer_reset (timer);
569 ped_timer_set_state_name (timer, _("raw block copying"));
571 sub_timer = ped_timer_new_nested (timer, 0.95);
572 if (!_raw_copy (fs->geom, geom, sub_timer))
574 ped_timer_destroy_nested (sub_timer);
576 new_fs = ped_file_system_open (geom);
580 ped_timer_set_state_name (timer, _("growing file system"));
582 sub_timer = ped_timer_new_nested (timer, 0.05);
583 if (!ped_file_system_resize (new_fs, geom, sub_timer))
584 goto error_close_new_fs;
585 ped_timer_destroy_nested (sub_timer);
589 ped_file_system_close (new_fs);
591 ped_timer_destroy_nested (sub_timer);
596 * Create a new file system (of the same type) on \p geom, and
597 * copy the contents of \p fs into the new filesystem.
598 * If \p timer is non-NULL, it is used as the progress meter.
600 * \throws PED_EXCEPTION_ERROR when trying to copy onto an overlapping partition
601 * \throws PED_EXCEPTION_NO_FEATURE if copying of file system \p fs
602 * is not implemented yet
604 * \return a new PedFileSystem on success, \c NULL on failure
607 ped_file_system_copy (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
609 PedFileSystem* new_fs;
611 PED_ASSERT (fs != NULL, return 0);
612 PED_ASSERT (geom != NULL, return 0);
614 if (!ped_device_open (geom->dev))
617 if (ped_geometry_test_overlap (fs->geom, geom)) {
618 ped_exception_throw (
619 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
620 _("Can't copy onto an overlapping partition."));
621 goto error_close_dev;
624 if (!fs->checked && fs->type->ops->check) {
625 if (!ped_file_system_check (fs, timer))
626 goto error_close_dev;
629 if (!ped_file_system_clobber_exclude (geom, fs->geom))
630 goto error_close_dev;
632 if (!fs->type->ops->copy) {
633 if (fs->type->ops->resize) {
634 if (fs->geom->length <= geom->length)
635 return _raw_copy_and_resize (
636 fs, (PedGeometry*) geom,
639 ped_exception_throw (
640 PED_EXCEPTION_NO_FEATURE,
641 PED_EXCEPTION_CANCEL,
642 _("Direct support for copying file systems is "
643 "not yet implemented for %s. However, "
644 "support for resizing is implemented. "
645 "Therefore, the file system can be copied if "
646 "the new partition is at least as big as the "
647 "old one. So, either shrink the partition "
648 "you are trying to copy, or copy to a bigger "
651 goto error_close_dev;
653 ped_exception_throw (
654 PED_EXCEPTION_NO_FEATURE,
655 PED_EXCEPTION_CANCEL,
656 _("Support for copying %s file systems is not "
659 goto error_close_dev;
662 new_fs = fs->type->ops->copy (fs, geom, timer);
664 goto error_close_dev;
668 ped_device_close (geom->dev);
674 * Resize \p fs to new geometry \p geom.
676 * \p geom should satisfy the ped_file_system_get_resize_constraint().
677 * (This isn't asserted, so it's not a bug not to... just it's likely
678 * to fail ;) If \p timer is non-NULL, it is used as the progress meter.
680 * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs
681 * is not implemented yet
683 * \return \c 0 on failure
686 ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
688 PED_ASSERT (fs != NULL, return 0);
689 PED_ASSERT (geom != NULL, return 0);
691 if (!fs->type->ops->resize) {
692 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
693 PED_EXCEPTION_CANCEL,
694 _("Support for resizing %s file systems "
695 "is not implemented yet."),
699 if (!fs->checked && fs->type->ops->check) {
700 if (!ped_file_system_check (fs, timer))
703 if (!ped_file_system_clobber_exclude (geom, fs->geom))
706 return fs->type->ops->resize (fs, geom, timer);
710 * This function returns a constraint on the region that all file systems
711 * of a particular type \p fs_type created on device \p dev with
712 * ped_file_system_create() must satisfy. For example, FAT16 file systems must
713 * be at least 32 megabytes.
715 * \return \c NULL on failure
718 ped_file_system_get_create_constraint (const PedFileSystemType* fs_type,
719 const PedDevice* dev)
721 PED_ASSERT (fs_type != NULL, return NULL);
722 PED_ASSERT (dev != NULL, return NULL);
724 if (!fs_type->ops->get_create_constraint)
726 return fs_type->ops->get_create_constraint (dev);
729 * Return a constraint, that represents all of the possible ways the
730 * file system \p fs can be resized with ped_file_system_resize().
731 * This takes into account the amount of used space on
732 * the filesystem \p fs and the capabilities of the resize algorithm.
734 * -# if constraint->start_align->grain_size == 0, or
735 * constraint->start_geom->length == 1, then the start can not be moved
736 * -# constraint->min_size is the minimum size you can resize the partition
737 * to. You might want to tell the user this ;-).
739 * \return a PedConstraint on success, \c NULL on failure
742 ped_file_system_get_resize_constraint (const PedFileSystem* fs)
744 PED_ASSERT (fs != NULL, return 0);
746 if (!fs->type->ops->get_resize_constraint)
748 return fs->type->ops->get_resize_constraint (fs);
752 * Get the constraint on copying \p fs with ped_file_system_copy()
753 * to somewhere on \p dev.
755 * \return a PedConstraint on success, \c NULL on failure
758 ped_file_system_get_copy_constraint (const PedFileSystem* fs,
759 const PedDevice* dev)
761 PedGeometry full_dev;
763 PED_ASSERT (fs != NULL, return NULL);
764 PED_ASSERT (dev != NULL, return NULL);
766 if (fs->type->ops->get_copy_constraint)
767 return fs->type->ops->get_copy_constraint (fs, dev);
769 if (fs->type->ops->resize) {
770 if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
772 return ped_constraint_new (
773 ped_alignment_any, ped_alignment_any,
774 &full_dev, &full_dev,
775 fs->geom->length, dev->length);