2 libparted - a library for manipulating disk partitions
3 Copyright (C) 1999-2001, 2007-2010 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;
44 static PedFileSystemAlias* fs_aliases = NULL;
47 ped_file_system_type_register (PedFileSystemType* fs_type)
49 PED_ASSERT (fs_type != NULL, return);
50 PED_ASSERT (fs_type->ops != NULL, return);
51 PED_ASSERT (fs_type->name != NULL, return);
53 fs_type->next = fs_types;
58 ped_file_system_type_unregister (PedFileSystemType* fs_type)
60 PedFileSystemType* walk;
61 PedFileSystemType* last = NULL;
63 PED_ASSERT (fs_types != NULL, return);
64 PED_ASSERT (fs_type != NULL, return);
66 for (walk = fs_types; walk && walk != fs_type;
67 last = walk, walk = walk->next);
69 PED_ASSERT (walk != NULL, return);
71 ((struct _PedFileSystemType*) last)->next = fs_type->next;
73 fs_types = fs_type->next;
77 ped_file_system_alias_register (PedFileSystemType* fs_type, const char* alias,
80 PedFileSystemAlias* fs_alias;
82 PED_ASSERT (fs_type != NULL, return);
83 PED_ASSERT (alias != NULL, return);
85 fs_alias = ped_malloc (sizeof *fs_alias);
89 fs_alias->next = fs_aliases;
90 fs_alias->fs_type = fs_type;
91 fs_alias->alias = alias;
92 fs_alias->deprecated = deprecated;
93 fs_aliases = fs_alias;
97 ped_file_system_alias_unregister (PedFileSystemType* fs_type,
100 PedFileSystemAlias* walk;
101 PedFileSystemAlias* last = NULL;
103 PED_ASSERT (fs_aliases != NULL, return);
104 PED_ASSERT (fs_type != NULL, return);
105 PED_ASSERT (alias != NULL, return);
107 for (walk = fs_aliases; walk; last = walk, walk = walk->next) {
108 if (walk->fs_type == fs_type && !strcmp (walk->alias, alias))
112 PED_ASSERT (walk != NULL, return);
114 last->next = walk->next;
116 fs_aliases = walk->next;
121 * Get a PedFileSystemType by its @p name.
123 * @return @c NULL if none found.
126 ped_file_system_type_get (const char* name)
128 PedFileSystemType* walk;
129 PedFileSystemAlias* alias_walk;
131 PED_ASSERT (name != NULL, return NULL);
133 for (walk = fs_types; walk != NULL; walk = walk->next) {
134 if (!strcasecmp (walk->name, name))
140 for (alias_walk = fs_aliases; alias_walk != NULL;
141 alias_walk = alias_walk->next) {
142 if (!strcasecmp (alias_walk->alias, name))
145 if (alias_walk != NULL) {
146 if (alias_walk->deprecated)
147 PED_DEBUG (0, "File system alias %s is deprecated",
149 return alias_walk->fs_type;
156 * Get the next PedFileSystemType after @p fs_type.
158 * @return @c NULL if @p fs_type is the last item in the list.
161 ped_file_system_type_get_next (const PedFileSystemType* fs_type)
164 return fs_type->next;
170 * Get the next PedFileSystemAlias after @p fs_alias.
172 * @return @c NULL if @p fs_alias is the last item in the list.
175 ped_file_system_alias_get_next (const PedFileSystemAlias* fs_alias)
178 return fs_alias->next;
184 * Attempt to find a file system and return the region it occupies.
186 * @param fs_type The file system type to probe for.
187 * @param geom The region to be searched.
189 * @return @p NULL if @p fs_type file system wasn't detected
192 ped_file_system_probe_specific (
193 const PedFileSystemType* fs_type, PedGeometry* geom)
197 PED_ASSERT (fs_type != NULL, return NULL);
198 PED_ASSERT (fs_type->ops->probe != NULL, return NULL);
199 PED_ASSERT (geom != NULL, return NULL);
201 /* Fail all fs-specific probe-related tests when sector size
202 is not the default. */
203 if (geom->dev->sector_size != PED_SECTOR_SIZE_DEFAULT)
206 if (!ped_device_open (geom->dev))
208 result = fs_type->ops->probe (geom);
209 ped_device_close (geom->dev);
214 _test_open (PedFileSystemType* fs_type, PedGeometry* geom)
218 ped_exception_fetch_all ();
219 fs = fs_type->ops->open (geom);
221 fs_type->ops->close (fs);
223 ped_exception_catch ();
224 ped_exception_leave_all ();
228 static PedFileSystemType*
229 _probe_with_open (PedGeometry* geom, int detected_count,
230 PedFileSystemType* detected[])
233 PedFileSystemType* open_detected = NULL;
235 ped_device_open (geom->dev);
237 /* If one and only one file system that Parted is able to open
238 * can be successfully opened on this geometry, return it.
239 * If more than one can be, return NULL.
241 for (i=0; i<detected_count; i++) {
242 if (!detected[i]->ops->open || !_test_open (detected [i], geom))
246 ped_device_close (geom->dev);
249 open_detected = detected [i];
253 /* If no file system has been successfully opened, and
254 * if Parted has detected at most one unopenable file system,
258 for (i=0; i<detected_count; i++) {
259 if (detected[i]->ops->open)
262 ped_device_close (geom->dev);
265 open_detected = detected [i];
269 ped_device_close (geom->dev);
270 return open_detected;
274 _geometry_error (const PedGeometry* a, const PedGeometry* b)
276 PedSector start_delta = a->start - b->start;
277 PedSector end_delta = a->end - b->end;
279 return abs (start_delta) + abs (end_delta);
282 static PedFileSystemType*
283 _best_match (const PedGeometry* geom, PedFileSystemType* detected [],
284 const int detected_error [], int detected_count)
290 min_error = PED_MAX (4096, geom->length / 100);
292 for (i = 1; i < detected_count; i++) {
293 if (detected_error [i] < detected_error [best_match])
297 /* make sure the best match is significantly better than all the
300 for (i = 0; i < detected_count; i++) {
304 if (abs (detected_error [best_match] - detected_error [i])
309 return detected [best_match];
314 * Attempt to detect a file system in region \p geom.
315 * This function tries to be clever at dealing with ambiguous
316 * situations, such as when one file system was not completely erased before a
317 * new file system was created on top of it.
319 * \return a new PedFileSystem on success, \c NULL on failure
322 ped_file_system_probe (PedGeometry* geom)
324 PedFileSystemType* detected[32];
325 int detected_error[32];
326 int detected_count = 0;
327 PedFileSystemType* walk = NULL;
329 PED_ASSERT (geom != NULL, return NULL);
331 if (!ped_device_open (geom->dev))
334 ped_exception_fetch_all ();
335 while ( (walk = ped_file_system_type_get_next (walk)) ) {
338 probed = ped_file_system_probe_specific (walk, geom);
340 detected [detected_count] = walk;
341 detected_error [detected_count]
342 = _geometry_error (geom, probed);
344 ped_geometry_destroy (probed);
346 ped_exception_catch ();
349 ped_exception_leave_all ();
351 ped_device_close (geom->dev);
355 walk = _best_match (geom, detected, detected_error, detected_count);
358 return _probe_with_open (geom, detected_count, detected);
362 * This function erases all file system signatures that indicate that a
363 * file system occupies a given region described by \p geom.
364 * After this operation ped_file_system_probe() won't detect any file system.
366 * \note ped_file_system_create() calls this before creating a new file system.
368 * \return \c 1 on success, \c 0 on failure
371 ped_file_system_clobber (PedGeometry* geom)
373 PedFileSystemType* fs_type = NULL;
375 PED_ASSERT (geom != NULL, return 0);
377 if (!ped_device_open (geom->dev))
380 ped_exception_fetch_all ();
381 while ((fs_type = ped_file_system_type_get_next (fs_type))) {
384 if (!fs_type->ops->clobber)
387 probed = ped_file_system_probe_specific (fs_type, geom);
389 ped_exception_catch ();
392 ped_geometry_destroy (probed);
394 if (fs_type->ops->clobber && !fs_type->ops->clobber (geom)) {
395 ped_exception_leave_all ();
396 goto error_close_dev;
399 ped_device_close (geom->dev);
400 ped_exception_leave_all ();
404 ped_device_close (geom->dev);
409 /* This function erases all signatures that indicate the presence of
410 * a file system in a particular region, without erasing any data
411 * contained inside the "exclude" region.
414 ped_file_system_clobber_exclude (PedGeometry* geom,
415 const PedGeometry* exclude)
417 PedGeometry* clobber_geom;
420 if (ped_geometry_test_sector_inside (exclude, geom->start))
423 clobber_geom = ped_geometry_duplicate (geom);
424 if (ped_geometry_test_overlap (clobber_geom, exclude))
425 ped_geometry_set_end (clobber_geom, exclude->start - 1);
427 status = ped_file_system_clobber (clobber_geom);
428 ped_geometry_destroy (clobber_geom);
433 * This function opens the file system stored on \p geom, if it
435 * It is often called in the following manner:
437 * fs = ped_file_system_open (&part.geom)
440 * \throws PED_EXCEPTION_ERROR if file system could not be detected
441 * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume
442 * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on
443 * \p geom is not implemented
445 * \return a PedFileSystem on success, \c NULL on failure.
448 ped_file_system_open (PedGeometry* geom)
450 PedFileSystemType* type;
452 PedGeometry* probed_geom;
454 PED_ASSERT (geom != NULL, return NULL);
456 if (!ped_device_open (geom->dev))
459 type = ped_file_system_probe (geom);
461 ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
462 _("Could not detect file system."));
463 goto error_close_dev;
466 probed_geom = ped_file_system_probe_specific (type, geom);
468 goto error_close_dev;
469 if (!ped_geometry_test_inside (geom, probed_geom)) {
470 if (ped_exception_throw (
472 PED_EXCEPTION_IGNORE_CANCEL,
473 _("The file system is bigger than its volume!"))
474 != PED_EXCEPTION_IGNORE)
475 goto error_destroy_probed_geom;
478 if (!type->ops->open) {
479 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
480 PED_EXCEPTION_CANCEL,
481 _("Support for opening %s file systems "
482 "is not implemented yet."),
484 goto error_destroy_probed_geom;
487 fs = type->ops->open (probed_geom);
489 goto error_destroy_probed_geom;
490 ped_geometry_destroy (probed_geom);
493 error_destroy_probed_geom:
494 ped_geometry_destroy (probed_geom);
496 ped_device_close (geom->dev);
502 * This function initializes a new file system of type \p type on
503 * a region described by \p geom, writing out appropriate metadata and
504 * signatures. If \p timer is non-NULL, it is used as the progress meter.
506 * \throws PED_EXCEPTION_NO_FEATURE if creating file system type \p type
507 * is not implemented yet
509 * \return a PedFileSystem on success, \c NULL on failure
512 ped_file_system_create (PedGeometry* geom, const PedFileSystemType* type,
517 PED_ASSERT (geom != NULL, return NULL);
518 PED_ASSERT (type != NULL, return NULL);
520 if (!type->ops->create) {
521 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
522 PED_EXCEPTION_CANCEL,
523 _("Support for creating %s file systems "
524 "is not implemented yet."),
529 if (!ped_device_open (geom->dev))
532 if (!ped_file_system_clobber (geom))
533 goto error_close_dev;
534 fs = type->ops->create (geom, timer);
536 goto error_close_dev;
540 ped_device_close (geom->dev);
546 * Close file system \p fs.
548 * \return \c 1 on success, \c 0 on failure
551 ped_file_system_close (PedFileSystem* fs)
553 PedDevice* dev = fs->geom->dev;
555 PED_ASSERT (fs != NULL, goto error_close_dev);
557 if (!fs->type->ops->close (fs))
558 goto error_close_dev;
559 ped_device_close (dev);
563 ped_device_close (dev);
568 * Check \p fs file system for errors.
570 * \throws PED_EXCEPTION_NO_FEATURE if checking file system \p fs is
571 * not implemented yet
573 * \return \c 0 on failure (i.e. unfixed errors)
576 ped_file_system_check (PedFileSystem* fs, PedTimer* timer)
578 PED_ASSERT (fs != NULL, return 0);
580 if (!fs->type->ops->check) {
581 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
582 PED_EXCEPTION_CANCEL,
583 _("Support for checking %s file systems "
584 "is not implemented yet."),
588 return fs->type->ops->check (fs, timer);
592 _raw_copy (const PedGeometry* src, PedGeometry* dest, PedTimer* timer)
597 PED_ASSERT (src != NULL, goto error);
598 PED_ASSERT (dest != NULL, goto error);
599 PED_ASSERT (src->length <= dest->length, goto error);
601 buf = ped_malloc (BUFFER_SIZE * 512); /* FIXME */
605 if (!ped_device_open (src->dev))
607 if (!ped_device_open (dest->dev))
608 goto error_close_src;
610 for (pos = 0; pos + BUFFER_SIZE < src->length; pos += BUFFER_SIZE) {
611 ped_timer_update (timer, 1.0 * pos / src->length);
612 if (!ped_geometry_read (src, buf, pos, BUFFER_SIZE))
613 goto error_close_dest;
614 if (!ped_geometry_write (dest, buf, pos, BUFFER_SIZE))
615 goto error_close_dest;
617 if (pos < src->length) {
618 ped_timer_update (timer, 1.0 * pos / src->length);
619 if (!ped_geometry_read (src, buf, pos, src->length - pos))
620 goto error_close_dest;
621 if (!ped_geometry_write (dest, buf, pos, src->length - pos))
622 goto error_close_dest;
624 ped_timer_update (timer, 1.0);
626 ped_device_close (src->dev);
627 ped_device_close (dest->dev);
632 ped_device_close (dest->dev);
634 ped_device_close (src->dev);
641 static PedFileSystem*
642 _raw_copy_and_resize (const PedFileSystem* fs, PedGeometry* geom,
645 PedFileSystem* new_fs;
646 PedTimer* sub_timer = NULL;
648 ped_timer_reset (timer);
649 ped_timer_set_state_name (timer, _("raw block copying"));
651 sub_timer = ped_timer_new_nested (timer, 0.95);
652 if (!_raw_copy (fs->geom, geom, sub_timer))
654 ped_timer_destroy_nested (sub_timer);
656 new_fs = ped_file_system_open (geom);
660 ped_timer_set_state_name (timer, _("growing file system"));
662 sub_timer = ped_timer_new_nested (timer, 0.05);
663 if (!ped_file_system_resize (new_fs, geom, sub_timer))
664 goto error_close_new_fs;
665 ped_timer_destroy_nested (sub_timer);
669 ped_file_system_close (new_fs);
671 ped_timer_destroy_nested (sub_timer);
676 * Create a new file system (of the same type) on \p geom, and
677 * copy the contents of \p fs into the new filesystem.
678 * If \p timer is non-NULL, it is used as the progress meter.
680 * \throws PED_EXCEPTION_ERROR when trying to copy onto an overlapping partition
681 * \throws PED_EXCEPTION_NO_FEATURE if copying of file system \p fs
682 * is not implemented yet
684 * \return a new PedFileSystem on success, \c NULL on failure
687 ped_file_system_copy (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
689 PedFileSystem* new_fs;
691 PED_ASSERT (fs != NULL, return 0);
692 PED_ASSERT (geom != NULL, return 0);
694 if (!ped_device_open (geom->dev))
697 if (ped_geometry_test_overlap (fs->geom, geom)) {
698 ped_exception_throw (
699 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
700 _("Can't copy onto an overlapping partition."));
701 goto error_close_dev;
704 if (!fs->checked && fs->type->ops->check) {
705 if (!ped_file_system_check (fs, timer))
706 goto error_close_dev;
709 if (!ped_file_system_clobber_exclude (geom, fs->geom))
710 goto error_close_dev;
712 if (!fs->type->ops->copy) {
713 if (fs->type->ops->resize) {
714 if (fs->geom->length <= geom->length)
715 return _raw_copy_and_resize (
716 fs, (PedGeometry*) geom,
719 ped_exception_throw (
720 PED_EXCEPTION_NO_FEATURE,
721 PED_EXCEPTION_CANCEL,
722 _("Direct support for copying file systems is "
723 "not yet implemented for %s. However, "
724 "support for resizing is implemented. "
725 "Therefore, the file system can be copied if "
726 "the new partition is at least as big as the "
727 "old one. So, either shrink the partition "
728 "you are trying to copy, or copy to a bigger "
731 goto error_close_dev;
733 ped_exception_throw (
734 PED_EXCEPTION_NO_FEATURE,
735 PED_EXCEPTION_CANCEL,
736 _("Support for copying %s file systems is not "
739 goto error_close_dev;
742 new_fs = fs->type->ops->copy (fs, geom, timer);
744 goto error_close_dev;
748 ped_device_close (geom->dev);
754 * Resize \p fs to new geometry \p geom.
756 * \p geom should satisfy the ped_file_system_get_resize_constraint().
757 * (This isn't asserted, so it's not a bug not to... just it's likely
758 * to fail ;) If \p timer is non-NULL, it is used as the progress meter.
760 * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs
761 * is not implemented yet
763 * \return \c 0 on failure
766 ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
768 PED_ASSERT (fs != NULL, return 0);
769 PED_ASSERT (geom != NULL, return 0);
771 if (!fs->type->ops->resize) {
772 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
773 PED_EXCEPTION_CANCEL,
774 _("Support for resizing %s file systems "
775 "is not implemented yet."),
779 if (!fs->checked && fs->type->ops->check) {
780 if (!ped_file_system_check (fs, timer))
783 if (!ped_file_system_clobber_exclude (geom, fs->geom))
786 return fs->type->ops->resize (fs, geom, timer);
790 * This function returns a constraint on the region that all file systems
791 * of a particular type \p fs_type created on device \p dev with
792 * ped_file_system_create() must satisfy. For example, FAT16 file systems must
793 * be at least 32 megabytes.
795 * \return \c NULL on failure
798 ped_file_system_get_create_constraint (const PedFileSystemType* fs_type,
799 const PedDevice* dev)
801 PED_ASSERT (fs_type != NULL, return NULL);
802 PED_ASSERT (dev != NULL, return NULL);
804 if (!fs_type->ops->get_create_constraint)
806 return fs_type->ops->get_create_constraint (dev);
809 * Return a constraint, that represents all of the possible ways the
810 * file system \p fs can be resized with ped_file_system_resize().
811 * This takes into account the amount of used space on
812 * the filesystem \p fs and the capabilities of the resize algorithm.
814 * -# if constraint->start_align->grain_size == 0, or
815 * constraint->start_geom->length == 1, then the start can not be moved
816 * -# constraint->min_size is the minimum size you can resize the partition
817 * to. You might want to tell the user this ;-).
819 * \return a PedConstraint on success, \c NULL on failure
822 ped_file_system_get_resize_constraint (const PedFileSystem* fs)
824 PED_ASSERT (fs != NULL, return 0);
826 if (!fs->type->ops->get_resize_constraint)
828 return fs->type->ops->get_resize_constraint (fs);
832 * Get the constraint on copying \p fs with ped_file_system_copy()
833 * to somewhere on \p dev.
835 * \return a PedConstraint on success, \c NULL on failure
838 ped_file_system_get_copy_constraint (const PedFileSystem* fs,
839 const PedDevice* dev)
841 PedGeometry full_dev;
843 PED_ASSERT (fs != NULL, return NULL);
844 PED_ASSERT (dev != NULL, return NULL);
846 if (fs->type->ops->get_copy_constraint)
847 return fs->type->ops->get_copy_constraint (fs, dev);
849 if (fs->type->ops->resize) {
850 if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
852 return ped_constraint_new (
853 ped_alignment_any, ped_alignment_any,
854 &full_dev, &full_dev,
855 fs->geom->length, dev->length);