1 /* 2 libparted - a library for manipulating disk partitions 3 Copyright (C) 1999, 2000, 2001, 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 filesys.c */ 20 21 /** 22 * \addtogroup PedFileSystem 23 * 24 * \note File systems exist on a PedGeometry - NOT a PedPartition. 25 * 26 * @{ 27 */ 28 29 #include <config.h> 30 31 #include <parted/parted.h> 32 #include <parted/debug.h> 33 34 #if ENABLE_NLS 35 # include <libintl.h> 36 # define _(String) dgettext (PACKAGE, String) 37 #else 38 # define _(String) (String) 39 #endif /* ENABLE_NLS */ 40 41 #define BUFFER_SIZE 4096 /* in sectors */ 42 43 static PedFileSystemType* fs_types = NULL; 44 45 void 46 ped_file_system_type_register (PedFileSystemType* fs_type) 47 { 48 PED_ASSERT (fs_type != NULL, return); 49 PED_ASSERT (fs_type->ops != NULL, return); 50 PED_ASSERT (fs_type->name != NULL, return); 51 52 /* pretend that "next" isn't part of the struct :-) */ 53 ((struct _PedFileSystemType*) fs_type)->next = fs_types; 54 fs_types = (struct _PedFileSystemType*) fs_type; 55 } 56 57 void 58 ped_file_system_type_unregister (PedFileSystemType* fs_type) 59 { 60 PedFileSystemType* walk; 61 PedFileSystemType* last = NULL; 62 63 PED_ASSERT (fs_types != NULL, return); 64 PED_ASSERT (fs_type != NULL, return); 65 66 for (walk = fs_types; walk && walk != fs_type; 67 last = walk, walk = walk->next); 68 69 PED_ASSERT (walk != NULL, return); 70 if (last) 71 ((struct _PedFileSystemType*) last)->next = fs_type->next; 72 else 73 fs_types = fs_type->next; 74 } 75 76 /** 77 * Get a PedFileSystemType by its @p name. 78 * 79 * @return @c NULL if none found. 80 */ 81 PedFileSystemType* 82 ped_file_system_type_get (const char* name) 83 { 84 PedFileSystemType* walk; 85 86 PED_ASSERT (name != NULL, return NULL); 87 88 for (walk = fs_types; walk != NULL; walk = walk->next) { 89 if (!strcasecmp (walk->name, name)) 90 break; 91 } 92 return walk; 93 } 94 95 /** 96 * Get the next PedFileSystemType after @p fs_type. 97 * 98 * @return @c NULL if @p fs_type is the last item in the list. 99 */ 100 PedFileSystemType* 101 ped_file_system_type_get_next (const PedFileSystemType* fs_type) 102 { 103 if (fs_type) 104 return fs_type->next; 105 else 106 return fs_types; 107 } 108 109 /** 110 * Attempt to find a file system and return the region it occupies. 111 * 112 * @param fs_type The file system type to probe for. 113 * @param geom The region to be searched. 114 * 115 * @return @p NULL if @p fs_type file system wasn't detected 116 */ 117 PedGeometry* 118 ped_file_system_probe_specific ( 119 const PedFileSystemType* fs_type, PedGeometry* geom) 120 { 121 PedGeometry* result; 122 123 PED_ASSERT (fs_type != NULL, return NULL); 124 PED_ASSERT (fs_type->ops->probe != NULL, return NULL); 125 PED_ASSERT (geom != NULL, return NULL); 126 127 if (!ped_device_open (geom->dev)) 128 return 0; 129 result = fs_type->ops->probe (geom); 130 ped_device_close (geom->dev); 131 return result; 132 } 133 134 static int 135 _test_open (PedFileSystemType* fs_type, PedGeometry* geom) 136 { 137 PedFileSystem* fs; 138 139 ped_exception_fetch_all (); 140 fs = fs_type->ops->open (geom); 141 if (fs) 142 fs_type->ops->close (fs); 143 else 144 ped_exception_catch (); 145 ped_exception_leave_all (); 146 return fs != NULL; 147 } 148 149 static PedFileSystemType* 150 _probe_with_open (PedGeometry* geom, int detected_count, 151 PedFileSystemType* detected[]) 152 { 153 int i; 154 PedFileSystemType* open_detected = NULL; 155 156 ped_device_open (geom->dev); 157 158 /* If one and only one file system that Parted is able to open 159 * can be successfully opened on this geometry, return it. 160 * If more than one can be, return NULL. 161 */ 162 for (i=0; i<detected_count; i++) { 163 if (!detected[i]->ops->open || !_test_open (detected [i], geom)) 164 continue; 165 166 if (open_detected) { 167 ped_device_close (geom->dev); 168 return NULL; 169 } else { 170 open_detected = detected [i]; 171 } 172 } 173 174 /* If no file system has been successfully opened, and 175 * if Parted has detected at most one unopenable file system, 176 * return it. 177 */ 178 if (!open_detected) 179 for (i=0; i<detected_count; i++) { 180 if (detected[i]->ops->open) 181 continue; 182 if (open_detected) { 183 ped_device_close (geom->dev); 184 return NULL; 185 } else { 186 open_detected = detected [i]; 187 } 188 } 189 190 ped_device_close (geom->dev); 191 return open_detected; 192 } 193 194 static int 195 _geometry_error (const PedGeometry* a, const PedGeometry* b) 196 { 197 PedSector start_delta = a->start - b->start; 198 PedSector end_delta = a->end - b->end; 199 200 return abs (start_delta) + abs (end_delta); 201 } 202 203 static PedFileSystemType* 204 _best_match (const PedGeometry* geom, PedFileSystemType* detected [], 205 const int detected_error [], int detected_count) 206 { 207 int best_match = 0; 208 int i; 209 PedSector min_error; 210 211 min_error = PED_MAX (4096, geom->length / 100); 212 213 for (i = 1; i < detected_count; i++) { 214 if (detected_error [i] < detected_error [best_match]) 215 best_match = i; 216 } 217 218 /* make sure the best match is significantly better than all the 219 * other matches 220 */ 221 for (i = 0; i < detected_count; i++) { 222 if (i == best_match) 223 continue; 224 225 if (abs (detected_error [best_match] - detected_error [i]) 226 < min_error) 227 return NULL; 228 } 229 230 return detected [best_match]; 231 } 232 233 234 /** 235 * Attempt to detect a file system in region \p geom. 236 * This function tries to be clever at dealing with ambiguous 237 * situations, such as when one file system was not completely erased before a 238 * new file system was created on top of it. 239 * 240 * \return a new PedFileSystem on success, \c NULL on failure 241 */ 242 PedFileSystemType* 243 ped_file_system_probe (PedGeometry* geom) 244 { 245 PedFileSystemType* detected[32]; 246 int detected_error[32]; 247 int detected_count = 0; 248 PedFileSystemType* walk = NULL; 249 250 PED_ASSERT (geom != NULL, return NULL); 251 252 if (!ped_device_open (geom->dev)) 253 return NULL; 254 255 ped_exception_fetch_all (); 256 while ( (walk = ped_file_system_type_get_next (walk)) ) { 257 PedGeometry* probed; 258 259 probed = ped_file_system_probe_specific (walk, geom); 260 if (probed) { 261 detected [detected_count] = walk; 262 detected_error [detected_count] 263 = _geometry_error (geom, probed); 264 detected_count++; 265 ped_geometry_destroy (probed); 266 } else { 267 ped_exception_catch (); 268 } 269 } 270 ped_exception_leave_all (); 271 272 ped_device_close (geom->dev); 273 274 if (!detected_count) 275 return NULL; 276 walk = _best_match (geom, detected, detected_error, detected_count); 277 if (walk) 278 return walk; 279 return _probe_with_open (geom, detected_count, detected); 280 } 281 282 /** 283 * This function erases all file system signatures that indicate that a 284 * file system occupies a given region described by \p geom. 285 * After this operation ped_file_system_probe() won't detect any file system. 286 * 287 * \note ped_file_system_create() calls this before creating a new file system. 288 * 289 * \return \c 1 on success, \c 0 on failure 290 */ 291 int 292 ped_file_system_clobber (PedGeometry* geom) 293 { 294 PedFileSystemType* fs_type = NULL; 295 296 PED_ASSERT (geom != NULL, return 0); 297 298 if (!ped_device_open (geom->dev)) 299 goto error; 300 301 ped_exception_fetch_all (); 302 while ((fs_type = ped_file_system_type_get_next (fs_type))) { 303 PedGeometry* probed; 304 305 if (!fs_type->ops->clobber) 306 continue; 307 308 probed = ped_file_system_probe_specific (fs_type, geom); 309 if (!probed) { 310 ped_exception_catch (); 311 continue; 312 } 313 ped_geometry_destroy (probed); 314 315 if (fs_type->ops->clobber && !fs_type->ops->clobber (geom)) { 316 ped_exception_leave_all (); 317 goto error_close_dev; 318 } 319 } 320 ped_device_close (geom->dev); 321 ped_exception_leave_all (); 322 return 1; 323 324 error_close_dev: 325 ped_device_close (geom->dev); 326 error: 327 return 0; 328 } 329 330 /* This function erases all signatures that indicate the presence of 331 * a file system in a particular region, without erasing any data 332 * contained inside the "exclude" region. 333 */ 334 static int 335 ped_file_system_clobber_exclude (PedGeometry* geom, 336 const PedGeometry* exclude) 337 { 338 PedGeometry* clobber_geom; 339 int status; 340 341 if (ped_geometry_test_sector_inside (exclude, geom->start)) 342 return 1; 343 344 clobber_geom = ped_geometry_duplicate (geom); 345 if (ped_geometry_test_overlap (clobber_geom, exclude)) 346 ped_geometry_set_end (clobber_geom, exclude->start - 1); 347 348 status = ped_file_system_clobber (clobber_geom); 349 ped_geometry_destroy (clobber_geom); 350 return status; 351 } 352 353 /** 354 * This function opens the file system stored on \p geom, if it 355 * can find one. 356 * It is often called in the following manner: 357 * \code 358 * fs = ped_file_system_open (&part.geom) 359 * \endcode 360 * 361 * \throws PED_EXCEPTION_ERROR if file system could not be detected 362 * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume 363 * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on 364 * \p geom is not implemented 365 * 366 * \return a PedFileSystem on success, \c NULL on failure. 367 */ 368 PedFileSystem* 369 ped_file_system_open (PedGeometry* geom) 370 { 371 PedFileSystemType* type; 372 PedFileSystem* fs; 373 PedGeometry* probed_geom; 374 375 PED_ASSERT (geom != NULL, return NULL); 376 377 if (!ped_device_open (geom->dev)) 378 goto error; 379 380 type = ped_file_system_probe (geom); 381 if (!type) { 382 ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 383 _("Could not detect file system.")); 384 goto error_close_dev; 385 } 386 387 probed_geom = ped_file_system_probe_specific (type, geom); 388 if (!probed_geom) 389 goto error_close_dev; 390 if (!ped_geometry_test_inside (geom, probed_geom)) { 391 if (ped_exception_throw ( 392 PED_EXCEPTION_ERROR, 393 PED_EXCEPTION_IGNORE_CANCEL, 394 _("The file system is bigger than its volume!")) 395 != PED_EXCEPTION_IGNORE) 396 goto error_destroy_probed_geom; 397 } 398 399 if (!type->ops->open) { 400 ped_exception_throw (PED_EXCEPTION_NO_FEATURE, 401 PED_EXCEPTION_CANCEL, 402 _("Support for opening %s file systems " 403 "is not implemented yet."), 404 type->name); 405 goto error_destroy_probed_geom; 406 } 407 408 fs = type->ops->open (probed_geom); 409 if (!fs) 410 goto error_destroy_probed_geom; 411 ped_geometry_destroy (probed_geom); 412 return fs; 413 414 error_destroy_probed_geom: 415 ped_geometry_destroy (probed_geom); 416 error_close_dev: 417 ped_device_close (geom->dev); 418 error: 419 return 0; 420 } 421 422 /** 423 * This function initializes a new file system of type \p type on 424 * a region described by \p geom, writing out appropriate metadata and 425 * signatures. If \p timer is non-NULL, it is used as the progress meter. 426 * 427 * \throws PED_EXCEPTION_NO_FEATURE if creating file system type \p type 428 * is not implemented yet 429 * 430 * \return a PedFileSystem on success, \c NULL on failure 431 */ 432 PedFileSystem* 433 ped_file_system_create (PedGeometry* geom, const PedFileSystemType* type, 434 PedTimer* timer) 435 { 436 PedFileSystem* fs; 437 438 PED_ASSERT (geom != NULL, return NULL); 439 PED_ASSERT (type != NULL, return NULL); 440 441 if (!type->ops->create) { 442 ped_exception_throw (PED_EXCEPTION_NO_FEATURE, 443 PED_EXCEPTION_CANCEL, 444 _("Support for creating %s file systems " 445 "is not implemented yet."), 446 type->name); 447 goto error; 448 } 449 450 if (!ped_device_open (geom->dev)) 451 goto error; 452 453 if (!ped_file_system_clobber (geom)) 454 goto error_close_dev; 455 fs = type->ops->create (geom, timer); 456 if (!fs) 457 goto error_close_dev; 458 return fs; 459 460 error_close_dev: 461 ped_device_close (geom->dev); 462 error: 463 return 0; 464 } 465 466 /** 467 * Close file system \p fs. 468 * 469 * \return \c 1 on success, \c 0 on failure 470 */ 471 int 472 ped_file_system_close (PedFileSystem* fs) 473 { 474 PedDevice* dev = fs->geom->dev; 475 476 PED_ASSERT (fs != NULL, goto error_close_dev); 477 478 if (!fs->type->ops->close (fs)) 479 goto error_close_dev; 480 ped_device_close (dev); 481 return 1; 482 483 error_close_dev: 484 ped_device_close (dev); 485 return 0; 486 } 487 488 /** 489 * Check \p fs file system for errors. 490 * 491 * \throws PED_EXCEPTION_NO_FEATURE if checking file system \p fs is 492 * not implemented yet 493 * 494 * \return \c 0 on failure (i.e. unfixed errors) 495 */ 496 int 497 ped_file_system_check (PedFileSystem* fs, PedTimer* timer) 498 { 499 PED_ASSERT (fs != NULL, return 0); 500 501 if (!fs->type->ops->check) { 502 ped_exception_throw (PED_EXCEPTION_NO_FEATURE, 503 PED_EXCEPTION_CANCEL, 504 _("Support for checking %s file systems " 505 "is not implemented yet."), 506 fs->type->name); 507 return 0; 508 } 509 return fs->type->ops->check (fs, timer); 510 } 511 512 static int 513 _raw_copy (const PedGeometry* src, PedGeometry* dest, PedTimer* timer) 514 { 515 char* buf; 516 PedSector pos; 517 518 PED_ASSERT (src != NULL, goto error); 519 PED_ASSERT (dest != NULL, goto error); 520 PED_ASSERT (src->length <= dest->length, goto error); 521 522 buf = ped_malloc (BUFFER_SIZE * 512); /* FIXME */ 523 if (!buf) 524 goto error; 525 526 if (!ped_device_open (src->dev)) 527 goto error_free_buf; 528 if (!ped_device_open (dest->dev)) 529 goto error_close_src; 530 531 for (pos = 0; pos + BUFFER_SIZE < src->length; pos += BUFFER_SIZE) { 532 ped_timer_update (timer, 1.0 * pos / src->length); 533 if (!ped_geometry_read (src, buf, pos, BUFFER_SIZE)) 534 goto error_close_dest; 535 if (!ped_geometry_write (dest, buf, pos, BUFFER_SIZE)) 536 goto error_close_dest; 537 } 538 if (pos < src->length) { 539 ped_timer_update (timer, 1.0 * pos / src->length); 540 if (!ped_geometry_read (src, buf, pos, src->length - pos)) 541 goto error_close_dest; 542 if (!ped_geometry_write (dest, buf, pos, src->length - pos)) 543 goto error_close_dest; 544 } 545 ped_timer_update (timer, 1.0); 546 547 ped_device_close (src->dev); 548 ped_device_close (dest->dev); 549 ped_free (buf); 550 return 1; 551 552 error_close_dest: 553 ped_device_close (dest->dev); 554 error_close_src: 555 ped_device_close (src->dev); 556 error_free_buf: 557 ped_free (buf); 558 error: 559 return 0; 560 } 561 562 static PedFileSystem* 563 _raw_copy_and_resize (const PedFileSystem* fs, PedGeometry* geom, 564 PedTimer* timer) 565 { 566 PedFileSystem* new_fs; 567 PedTimer* sub_timer = NULL; 568 569 ped_timer_reset (timer); 570 ped_timer_set_state_name (timer, _("raw block copying")); 571 572 sub_timer = ped_timer_new_nested (timer, 0.95); 573 if (!_raw_copy (fs->geom, geom, sub_timer)) 574 goto error; 575 ped_timer_destroy_nested (sub_timer); 576 577 new_fs = ped_file_system_open (geom); 578 if (!new_fs) 579 goto error; 580 581 ped_timer_set_state_name (timer, _("growing file system")); 582 583 sub_timer = ped_timer_new_nested (timer, 0.05); 584 if (!ped_file_system_resize (new_fs, geom, sub_timer)) 585 goto error_close_new_fs; 586 ped_timer_destroy_nested (sub_timer); 587 return new_fs; 588 589 error_close_new_fs: 590 ped_file_system_close (new_fs); 591 error: 592 ped_timer_destroy_nested (sub_timer); 593 return NULL; 594 } 595 596 /** 597 * Create a new file system (of the same type) on \p geom, and 598 * copy the contents of \p fs into the new filesystem. 599 * If \p timer is non-NULL, it is used as the progress meter. 600 * 601 * \throws PED_EXCEPTION_ERROR when trying to copy onto an overlapping partition 602 * \throws PED_EXCEPTION_NO_FEATURE if copying of file system \p fs 603 * is not implemented yet 604 * 605 * \return a new PedFileSystem on success, \c NULL on failure 606 */ 607 PedFileSystem* 608 ped_file_system_copy (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) 609 { 610 PedFileSystem* new_fs; 611 612 PED_ASSERT (fs != NULL, return 0); 613 PED_ASSERT (geom != NULL, return 0); 614 615 if (!ped_device_open (geom->dev)) 616 goto error; 617 618 if (ped_geometry_test_overlap (fs->geom, geom)) { 619 ped_exception_throw ( 620 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 621 _("Can't copy onto an overlapping partition.")); 622 goto error_close_dev; 623 } 624 625 if (!fs->checked && fs->type->ops->check) { 626 if (!ped_file_system_check (fs, timer)) 627 goto error_close_dev; 628 } 629 630 if (!ped_file_system_clobber_exclude (geom, fs->geom)) 631 goto error_close_dev; 632 633 if (!fs->type->ops->copy) { 634 if (fs->type->ops->resize) { 635 if (fs->geom->length <= geom->length) 636 return _raw_copy_and_resize ( 637 fs, (PedGeometry*) geom, 638 timer); 639 640 ped_exception_throw ( 641 PED_EXCEPTION_NO_FEATURE, 642 PED_EXCEPTION_CANCEL, 643 _("Direct support for copying file systems is " 644 "not yet implemented for %s. However, " 645 "support for resizing is implemented. " 646 "Therefore, the file system can be copied if " 647 "the new partition is at least as big as the " 648 "old one. So, either shrink the partition " 649 "you are trying to copy, or copy to a bigger " 650 "partition."), 651 fs->type->name); 652 goto error_close_dev; 653 } else { 654 ped_exception_throw ( 655 PED_EXCEPTION_NO_FEATURE, 656 PED_EXCEPTION_CANCEL, 657 _("Support for copying %s file systems is not " 658 "implemented yet."), 659 fs->type->name); 660 goto error_close_dev; 661 } 662 } 663 new_fs = fs->type->ops->copy (fs, geom, timer); 664 if (!new_fs) 665 goto error_close_dev; 666 return new_fs; 667 668 error_close_dev: 669 ped_device_close (geom->dev); 670 error: 671 return NULL;; 672 } 673 674 /** 675 * Resize \p fs to new geometry \p geom. 676 * 677 * \p geom should satisfy the ped_file_system_get_resize_constraint(). 678 * (This isn't asserted, so it's not a bug not to... just it's likely 679 * to fail ;) If \p timer is non-NULL, it is used as the progress meter. 680 * 681 * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs 682 * is not implemented yet 683 * 684 * \return \c 0 on failure 685 */ 686 int 687 ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) 688 { 689 PED_ASSERT (fs != NULL, return 0); 690 PED_ASSERT (geom != NULL, return 0); 691 692 if (!fs->type->ops->resize) { 693 ped_exception_throw (PED_EXCEPTION_NO_FEATURE, 694 PED_EXCEPTION_CANCEL, 695 _("Support for resizing %s file systems " 696 "is not implemented yet."), 697 fs->type->name); 698 return 0; 699 } 700 if (!fs->checked && fs->type->ops->check) { 701 if (!ped_file_system_check (fs, timer)) 702 return 0; 703 } 704 if (!ped_file_system_clobber_exclude (geom, fs->geom)) 705 return 0; 706 707 return fs->type->ops->resize (fs, geom, timer); 708 } 709 710 /** 711 * This function returns a constraint on the region that all file systems 712 * of a particular type \p fs_type created on device \p dev with 713 * ped_file_system_create() must satisfy. For example, FAT16 file systems must 714 * be at least 32 megabytes. 715 * 716 * \return \c NULL on failure 717 */ 718 PedConstraint* 719 ped_file_system_get_create_constraint (const PedFileSystemType* fs_type, 720 const PedDevice* dev) 721 { 722 PED_ASSERT (fs_type != NULL, return NULL); 723 PED_ASSERT (dev != NULL, return NULL); 724 725 if (!fs_type->ops->get_create_constraint) 726 return NULL; 727 return fs_type->ops->get_create_constraint (dev); 728 } 729 /** 730 * Return a constraint, that represents all of the possible ways the 731 * file system \p fs can be resized with ped_file_system_resize(). 732 * This takes into account the amount of used space on 733 * the filesystem \p fs and the capabilities of the resize algorithm. 734 * Hints: 735 * -# if constraint->start_align->grain_size == 0, or 736 * constraint->start_geom->length == 1, then the start can not be moved 737 * -# constraint->min_size is the minimum size you can resize the partition 738 * to. You might want to tell the user this ;-). 739 * 740 * \return a PedConstraint on success, \c NULL on failure 741 */ 742 PedConstraint* 743 ped_file_system_get_resize_constraint (const PedFileSystem* fs) 744 { 745 PED_ASSERT (fs != NULL, return 0); 746 747 if (!fs->type->ops->get_resize_constraint) 748 return NULL; 749 return fs->type->ops->get_resize_constraint (fs); 750 } 751 752 /** 753 * Get the constraint on copying \p fs with ped_file_system_copy() 754 * to somewhere on \p dev. 755 * 756 * \return a PedConstraint on success, \c NULL on failure 757 */ 758 PedConstraint* 759 ped_file_system_get_copy_constraint (const PedFileSystem* fs, 760 const PedDevice* dev) 761 { 762 PedGeometry full_dev; 763 764 PED_ASSERT (fs != NULL, return NULL); 765 PED_ASSERT (dev != NULL, return NULL); 766 767 if (fs->type->ops->get_copy_constraint) 768 return fs->type->ops->get_copy_constraint (fs, dev); 769 770 if (fs->type->ops->resize) { 771 if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) 772 return NULL; 773 return ped_constraint_new ( 774 ped_alignment_any, ped_alignment_any, 775 &full_dev, &full_dev, 776 fs->geom->length, dev->length); 777 } 778 779 return NULL; 780 } 781 782 /** @} */