1 /* 2 * Copyright 2007-2018, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus, superstippi@gmx.de 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 11 #include "AutoMounter.h" 12 13 #include <new> 14 15 #include <string.h> 16 #include <unistd.h> 17 18 #include <Alert.h> 19 #include <AutoLocker.h> 20 #include <Catalog.h> 21 #include <Debug.h> 22 #include <Directory.h> 23 #include <DiskDevice.h> 24 #include <DiskDeviceRoster.h> 25 #include <DiskDeviceList.h> 26 #include <DiskDeviceTypes.h> 27 #include <DiskSystem.h> 28 #include <FindDirectory.h> 29 #include <fs_info.h> 30 #include <fs_volume.h> 31 #include <LaunchRoster.h> 32 #include <Locale.h> 33 #include <Message.h> 34 #include <Node.h> 35 #include <NodeMonitor.h> 36 #include <Path.h> 37 #include <PropertyInfo.h> 38 #include <String.h> 39 #include <VolumeRoster.h> 40 41 #include "MountServer.h" 42 43 #include "Utilities.h" 44 45 46 #undef B_TRANSLATION_CONTEXT 47 #define B_TRANSLATION_CONTEXT "AutoMounter" 48 49 50 static const char* kMountServerSettings = "mount_server"; 51 static const char* kMountFlagsKeyExtension = " mount flags"; 52 53 static const char* kInitialMountEvent = "initial_volumes_mounted"; 54 55 56 class MountVisitor : public BDiskDeviceVisitor { 57 public: 58 MountVisitor(mount_mode normalMode, 59 mount_mode removableMode, 60 bool initialRescan, BMessage& previous, 61 partition_id deviceID); 62 virtual ~MountVisitor() 63 {} 64 65 virtual bool Visit(BDiskDevice* device); 66 virtual bool Visit(BPartition* partition, int32 level); 67 68 private: 69 bool _WasPreviouslyMounted(const BPath& path, 70 const BPartition* partition); 71 72 private: 73 mount_mode fNormalMode; 74 mount_mode fRemovableMode; 75 bool fInitialRescan; 76 BMessage& fPrevious; 77 partition_id fOnlyOnDeviceID; 78 }; 79 80 81 class MountArchivedVisitor : public BDiskDeviceVisitor { 82 public: 83 MountArchivedVisitor( 84 const BDiskDeviceList& devices, 85 const BMessage& archived); 86 virtual ~MountArchivedVisitor(); 87 88 virtual bool Visit(BDiskDevice* device); 89 virtual bool Visit(BPartition* partition, int32 level); 90 91 private: 92 int _Score(BPartition* partition); 93 94 private: 95 const BDiskDeviceList& fDevices; 96 const BMessage& fArchived; 97 int fBestScore; 98 partition_id fBestID; 99 }; 100 101 102 static bool 103 BootedInSafeMode() 104 { 105 const char* safeMode = getenv("SAFEMODE"); 106 return safeMode != NULL && strcmp(safeMode, "yes") == 0; 107 } 108 109 110 class ArchiveVisitor : public BDiskDeviceVisitor { 111 public: 112 ArchiveVisitor(BMessage& message); 113 virtual ~ArchiveVisitor(); 114 115 virtual bool Visit(BDiskDevice* device); 116 virtual bool Visit(BPartition* partition, int32 level); 117 118 private: 119 BMessage& fMessage; 120 }; 121 122 123 // #pragma mark - MountVisitor 124 125 126 MountVisitor::MountVisitor(mount_mode normalMode, mount_mode removableMode, 127 bool initialRescan, BMessage& previous, partition_id deviceID) 128 : 129 fNormalMode(normalMode), 130 fRemovableMode(removableMode), 131 fInitialRescan(initialRescan), 132 fPrevious(previous), 133 fOnlyOnDeviceID(deviceID) 134 { 135 } 136 137 138 bool 139 MountVisitor::Visit(BDiskDevice* device) 140 { 141 return Visit(device, 0); 142 } 143 144 145 bool 146 MountVisitor::Visit(BPartition* partition, int32 level) 147 { 148 if (fOnlyOnDeviceID >= 0) { 149 // only mount partitions on the given device id 150 // or if the partition ID is already matched 151 BPartition* device = partition; 152 while (device->Parent() != NULL) { 153 if (device->ID() == fOnlyOnDeviceID) { 154 // we are happy 155 break; 156 } 157 device = device->Parent(); 158 } 159 if (device->ID() != fOnlyOnDeviceID) 160 return false; 161 } 162 163 mount_mode mode = !fInitialRescan && partition->Device()->IsRemovableMedia() 164 ? fRemovableMode : fNormalMode; 165 if (mode == kNoVolumes || partition->IsMounted() 166 || !partition->ContainsFileSystem()) { 167 return false; 168 } 169 170 BPath path; 171 if (partition->GetPath(&path) != B_OK) 172 return false; 173 174 if (mode == kRestorePreviousVolumes) { 175 // mount all volumes that were stored in the settings file 176 if (!_WasPreviouslyMounted(path, partition)) 177 return false; 178 } else if (mode == kOnlyBFSVolumes) { 179 if (partition->ContentType() == NULL 180 || strcmp(partition->ContentType(), kPartitionTypeBFS)) 181 return false; 182 } 183 184 uint32 mountFlags; 185 if (!fInitialRescan) { 186 // Ask the user about mount flags if this is not the 187 // initial scan. 188 if (!AutoMounter::_SuggestMountFlags(partition, &mountFlags)) 189 return false; 190 } else { 191 BString mountFlagsKey(path.Path()); 192 mountFlagsKey << kMountFlagsKeyExtension; 193 if (fPrevious.FindInt32(mountFlagsKey.String(), 194 (int32*)&mountFlags) < B_OK) { 195 mountFlags = 0; 196 } 197 } 198 199 if (partition->Mount(NULL, mountFlags) != B_OK) { 200 // TODO: Error to syslog 201 } 202 return false; 203 } 204 205 206 bool 207 MountVisitor::_WasPreviouslyMounted(const BPath& path, 208 const BPartition* partition) 209 { 210 // We only check the legacy config data here; the current method 211 // is implemented in ArchivedVolumeVisitor -- this can be removed 212 // some day. 213 BString volumeName; 214 if (fPrevious.FindString(path.Path(), &volumeName) != B_OK 215 || volumeName != partition->ContentName()) 216 return false; 217 218 return true; 219 } 220 221 222 // #pragma mark - MountArchivedVisitor 223 224 225 MountArchivedVisitor::MountArchivedVisitor(const BDiskDeviceList& devices, 226 const BMessage& archived) 227 : 228 fDevices(devices), 229 fArchived(archived), 230 fBestScore(-1), 231 fBestID(-1) 232 { 233 } 234 235 236 MountArchivedVisitor::~MountArchivedVisitor() 237 { 238 if (fBestScore >= 6) { 239 uint32 mountFlags = fArchived.GetUInt32("mountFlags", 0); 240 BPartition* partition = fDevices.PartitionWithID(fBestID); 241 if (partition != NULL) 242 partition->Mount(NULL, mountFlags); 243 } 244 } 245 246 247 bool 248 MountArchivedVisitor::Visit(BDiskDevice* device) 249 { 250 return Visit(device, 0); 251 } 252 253 254 bool 255 MountArchivedVisitor::Visit(BPartition* partition, int32 level) 256 { 257 if (partition->IsMounted() || !partition->ContainsFileSystem()) 258 return false; 259 260 int score = _Score(partition); 261 if (score > fBestScore) { 262 fBestScore = score; 263 fBestID = partition->ID(); 264 } 265 266 return false; 267 } 268 269 270 int 271 MountArchivedVisitor::_Score(BPartition* partition) 272 { 273 BPath path; 274 if (partition->GetPath(&path) != B_OK) 275 return false; 276 277 int score = 0; 278 279 int64 capacity = fArchived.GetInt64("capacity", 0); 280 if (capacity == partition->ContentSize()) 281 score += 4; 282 283 BString deviceName = fArchived.GetString("deviceName"); 284 if (deviceName == path.Path()) 285 score += 3; 286 287 BString volumeName = fArchived.GetString("volumeName"); 288 if (volumeName == partition->ContentName()) 289 score += 2; 290 291 BString fsName = fArchived.FindString("fsName"); 292 if (fsName == partition->ContentType()) 293 score += 1; 294 295 uint32 blockSize = fArchived.GetUInt32("blockSize", 0); 296 if (blockSize == partition->BlockSize()) 297 score += 1; 298 299 return score; 300 } 301 302 303 // #pragma mark - ArchiveVisitor 304 305 306 ArchiveVisitor::ArchiveVisitor(BMessage& message) 307 : 308 fMessage(message) 309 { 310 } 311 312 313 ArchiveVisitor::~ArchiveVisitor() 314 { 315 } 316 317 318 bool 319 ArchiveVisitor::Visit(BDiskDevice* device) 320 { 321 return Visit(device, 0); 322 } 323 324 325 bool 326 ArchiveVisitor::Visit(BPartition* partition, int32 level) 327 { 328 if (!partition->ContainsFileSystem()) 329 return false; 330 331 BPath path; 332 if (partition->GetPath(&path) != B_OK) 333 return false; 334 335 BMessage info; 336 info.AddUInt32("blockSize", partition->BlockSize()); 337 info.AddInt64("capacity", partition->ContentSize()); 338 info.AddString("deviceName", path.Path()); 339 info.AddString("volumeName", partition->ContentName()); 340 info.AddString("fsName", partition->ContentType()); 341 342 fMessage.AddMessage("info", &info); 343 return false; 344 } 345 346 347 // #pragma mark - 348 349 350 AutoMounter::AutoMounter() 351 : 352 BServer(kMountServerSignature, false, NULL), 353 fNormalMode(kRestorePreviousVolumes), 354 fRemovableMode(kAllVolumes), 355 fEjectWhenUnmounting(true) 356 { 357 set_thread_priority(Thread(), B_LOW_PRIORITY); 358 359 if (!BootedInSafeMode()) { 360 _ReadSettings(); 361 } else { 362 // defeat automounter in safe mode, don't even care about the settings 363 fNormalMode = kNoVolumes; 364 fRemovableMode = kNoVolumes; 365 } 366 367 BDiskDeviceRoster().StartWatching(this, 368 B_DEVICE_REQUEST_DEVICE | B_DEVICE_REQUEST_DEVICE_LIST); 369 BLaunchRoster().RegisterEvent(this, kInitialMountEvent, B_STICKY_EVENT); 370 } 371 372 373 AutoMounter::~AutoMounter() 374 { 375 BLaunchRoster().UnregisterEvent(this, kInitialMountEvent); 376 BDiskDeviceRoster().StopWatching(this); 377 } 378 379 380 void 381 AutoMounter::ReadyToRun() 382 { 383 // Do initial scan 384 _MountVolumes(fNormalMode, fRemovableMode, true); 385 BLaunchRoster().NotifyEvent(this, kInitialMountEvent); 386 } 387 388 389 void 390 AutoMounter::MessageReceived(BMessage* message) 391 { 392 switch (message->what) { 393 case kMountVolume: 394 _MountVolume(message); 395 break; 396 397 case kUnmountVolume: 398 _UnmountAndEjectVolume(message); 399 break; 400 401 case kSetAutomounterParams: 402 { 403 bool rescanNow = false; 404 message->FindBool("rescanNow", &rescanNow); 405 406 _UpdateSettingsFromMessage(message); 407 _GetSettings(&fSettings); 408 _WriteSettings(); 409 410 if (rescanNow) 411 _MountVolumes(fNormalMode, fRemovableMode); 412 break; 413 } 414 415 case kGetAutomounterParams: 416 { 417 BMessage reply; 418 _GetSettings(&reply); 419 message->SendReply(&reply); 420 break; 421 } 422 423 case kMountAllNow: 424 _MountVolumes(kAllVolumes, kAllVolumes); 425 break; 426 427 case B_DEVICE_UPDATE: 428 int32 event; 429 if (message->FindInt32("event", &event) != B_OK 430 || (event != B_DEVICE_MEDIA_CHANGED 431 && event != B_DEVICE_ADDED)) 432 break; 433 434 partition_id deviceID; 435 if (message->FindInt32("id", &deviceID) != B_OK) 436 break; 437 438 _MountVolumes(kNoVolumes, fRemovableMode, false, deviceID); 439 break; 440 441 #if 0 442 case B_NODE_MONITOR: 443 { 444 int32 opcode; 445 if (message->FindInt32("opcode", &opcode) != B_OK) 446 break; 447 448 switch (opcode) { 449 // The name of a mount point has changed 450 case B_ENTRY_MOVED: { 451 WRITELOG(("*** Received Mount Point Renamed Notification")); 452 453 const char *newName; 454 if (message->FindString("name", &newName) != B_OK) { 455 WRITELOG(("ERROR: Couldn't find name field in update " 456 "message")); 457 PRINT_OBJECT(*message); 458 break ; 459 } 460 461 // 462 // When the node monitor reports a move, it gives the 463 // parent device and inode that moved. The problem is 464 // that the inode is the inode of root *in* the filesystem, 465 // which is generally always the same number for every 466 // filesystem of a type. 467 // 468 // What we'd really like is the device that the moved 469 // volume is mounted on. Find this by using the 470 // *new* name and directory, and then stat()ing that to 471 // find the device. 472 // 473 dev_t parentDevice; 474 if (message->FindInt32("device", &parentDevice) != B_OK) { 475 WRITELOG(("ERROR: Couldn't find 'device' field in " 476 "update message")); 477 PRINT_OBJECT(*message); 478 break; 479 } 480 481 ino_t toDirectory; 482 if (message->FindInt64("to directory", &toDirectory) 483 != B_OK) { 484 WRITELOG(("ERROR: Couldn't find 'to directory' field " 485 "in update message")); 486 PRINT_OBJECT(*message); 487 break; 488 } 489 490 entry_ref root_entry(parentDevice, toDirectory, newName); 491 492 BNode entryNode(&root_entry); 493 if (entryNode.InitCheck() != B_OK) { 494 WRITELOG(("ERROR: Couldn't create mount point entry " 495 "node: %s/n", strerror(entryNode.InitCheck()))); 496 break; 497 } 498 499 node_ref mountPointNode; 500 if (entryNode.GetNodeRef(&mountPointNode) != B_OK) { 501 WRITELOG(("ERROR: Couldn't get node ref for new mount " 502 "point")); 503 break; 504 } 505 506 WRITELOG(("Attempt to rename device %li to %s", 507 mountPointNode.device, newName)); 508 509 Partition *partition = FindPartition(mountPointNode.device); 510 if (partition != NULL) { 511 WRITELOG(("Found device, changing name.")); 512 513 BVolume mountVolume(partition->VolumeDeviceID()); 514 BDirectory mountDir; 515 mountVolume.GetRootDirectory(&mountDir); 516 BPath dirPath(&mountDir, 0); 517 518 partition->SetMountedAt(dirPath.Path()); 519 partition->SetVolumeName(newName); 520 break; 521 } else { 522 WRITELOG(("ERROR: Device %li does not appear to be " 523 "present", mountPointNode.device)); 524 } 525 } 526 } 527 break; 528 } 529 #endif 530 531 default: 532 BLooper::MessageReceived(message); 533 break; 534 } 535 } 536 537 538 bool 539 AutoMounter::QuitRequested() 540 { 541 if (!BootedInSafeMode()) { 542 // Don't write out settings in safe mode - this would overwrite the 543 // normal, non-safe mode settings. 544 _WriteSettings(); 545 } 546 547 return true; 548 } 549 550 551 // #pragma mark - private methods 552 553 554 void 555 AutoMounter::_MountVolumes(mount_mode normal, mount_mode removable, 556 bool initialRescan, partition_id deviceID) 557 { 558 if (normal == kNoVolumes && removable == kNoVolumes) 559 return; 560 561 BDiskDeviceList devices; 562 status_t status = devices.Fetch(); 563 if (status != B_OK) 564 return; 565 566 if (normal == kRestorePreviousVolumes) { 567 BMessage archived; 568 for (int32 index = 0; 569 fSettings.FindMessage("info", index, &archived) == B_OK; 570 index++) { 571 MountArchivedVisitor visitor(devices, archived); 572 devices.VisitEachPartition(&visitor); 573 } 574 } 575 576 MountVisitor visitor(normal, removable, initialRescan, fSettings, deviceID); 577 devices.VisitEachPartition(&visitor); 578 } 579 580 581 void 582 AutoMounter::_MountVolume(const BMessage* message) 583 { 584 int32 id; 585 if (message->FindInt32("id", &id) != B_OK) 586 return; 587 588 BDiskDeviceRoster roster; 589 BPartition *partition; 590 BDiskDevice device; 591 if (roster.GetPartitionWithID(id, &device, &partition) != B_OK) 592 return; 593 594 uint32 mountFlags; 595 if (!_SuggestMountFlags(partition, &mountFlags)) 596 return; 597 598 status_t status = partition->Mount(NULL, mountFlags); 599 if (status < B_OK && InitGUIContext() == B_OK) { 600 char text[512]; 601 snprintf(text, sizeof(text), 602 B_TRANSLATE("Error mounting volume:\n\n%s"), strerror(status)); 603 BAlert* alert = new BAlert(B_TRANSLATE("Mount error"), text, 604 B_TRANSLATE("OK")); 605 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 606 alert->Go(NULL); 607 } 608 } 609 610 611 bool 612 AutoMounter::_SuggestForceUnmount(const char* name, status_t error) 613 { 614 if (InitGUIContext() != B_OK) 615 return false; 616 617 char text[1024]; 618 snprintf(text, sizeof(text), 619 B_TRANSLATE("Could not unmount disk \"%s\":\n\t%s\n\n" 620 "Should unmounting be forced?\n\n" 621 "Note: If an application is currently writing to the volume, " 622 "unmounting it now might result in loss of data.\n"), 623 name, strerror(error)); 624 625 BAlert* alert = new BAlert(B_TRANSLATE("Force unmount"), text, 626 B_TRANSLATE("Cancel"), B_TRANSLATE("Force unmount"), NULL, 627 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 628 alert->SetShortcut(0, B_ESCAPE); 629 int32 choice = alert->Go(); 630 631 return choice == 1; 632 } 633 634 635 void 636 AutoMounter::_ReportUnmountError(const char* name, status_t error) 637 { 638 if (InitGUIContext() != B_OK) 639 return; 640 641 char text[512]; 642 snprintf(text, sizeof(text), B_TRANSLATE("Could not unmount disk " 643 "\"%s\":\n\t%s"), name, strerror(error)); 644 645 BAlert* alert = new BAlert(B_TRANSLATE("Unmount error"), text, 646 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 647 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 648 alert->Go(NULL); 649 } 650 651 652 void 653 AutoMounter::_UnmountAndEjectVolume(BPartition* partition, BPath& mountPoint, 654 const char* name) 655 { 656 BDiskDevice deviceStorage; 657 BDiskDevice* device; 658 if (partition == NULL) { 659 // Try to retrieve partition 660 BDiskDeviceRoster().FindPartitionByMountPoint(mountPoint.Path(), 661 &deviceStorage, &partition); 662 device = &deviceStorage; 663 } else { 664 device = partition->Device(); 665 } 666 667 status_t status; 668 if (partition != NULL) 669 status = partition->Unmount(); 670 else 671 status = fs_unmount_volume(mountPoint.Path(), 0); 672 673 if (status != B_OK) { 674 if (!_SuggestForceUnmount(name, status)) 675 return; 676 677 if (partition != NULL) 678 status = partition->Unmount(B_FORCE_UNMOUNT); 679 else 680 status = fs_unmount_volume(mountPoint.Path(), B_FORCE_UNMOUNT); 681 } 682 683 if (status != B_OK) { 684 _ReportUnmountError(name, status); 685 return; 686 } 687 688 if (fEjectWhenUnmounting && partition != NULL) { 689 // eject device if it doesn't have any mounted partitions left 690 class IsMountedVisitor : public BDiskDeviceVisitor { 691 public: 692 IsMountedVisitor() 693 : 694 fHasMounted(false) 695 { 696 } 697 698 virtual bool Visit(BDiskDevice* device) 699 { 700 return Visit(device, 0); 701 } 702 703 virtual bool Visit(BPartition* partition, int32 level) 704 { 705 if (partition->IsMounted()) { 706 fHasMounted = true; 707 return true; 708 } 709 710 return false; 711 } 712 713 bool HasMountedPartitions() const 714 { 715 return fHasMounted; 716 } 717 718 private: 719 bool fHasMounted; 720 } visitor; 721 722 device->VisitEachDescendant(&visitor); 723 724 if (!visitor.HasMountedPartitions()) 725 device->Eject(); 726 } 727 728 // remove the directory if it's a directory in rootfs 729 if (dev_for_path(mountPoint.Path()) == dev_for_path("/")) 730 rmdir(mountPoint.Path()); 731 } 732 733 734 void 735 AutoMounter::_UnmountAndEjectVolume(BMessage* message) 736 { 737 int32 id; 738 if (message->FindInt32("id", &id) == B_OK) { 739 BDiskDeviceRoster roster; 740 BPartition *partition; 741 BDiskDevice device; 742 if (roster.GetPartitionWithID(id, &device, &partition) != B_OK) 743 return; 744 745 BPath path; 746 if (partition->GetMountPoint(&path) == B_OK) 747 _UnmountAndEjectVolume(partition, path, partition->ContentName()); 748 } else { 749 // see if we got a dev_t 750 751 dev_t device; 752 if (message->FindInt32("device_id", &device) != B_OK) 753 return; 754 755 BVolume volume(device); 756 status_t status = volume.InitCheck(); 757 758 char name[B_FILE_NAME_LENGTH]; 759 if (status == B_OK) 760 status = volume.GetName(name); 761 if (status < B_OK) 762 snprintf(name, sizeof(name), "device:%" B_PRIdDEV, device); 763 764 BPath path; 765 if (status == B_OK) { 766 BDirectory mountPoint; 767 status = volume.GetRootDirectory(&mountPoint); 768 if (status == B_OK) 769 status = path.SetTo(&mountPoint, "."); 770 } 771 772 if (status == B_OK) 773 _UnmountAndEjectVolume(NULL, path, name); 774 } 775 } 776 777 778 void 779 AutoMounter::_FromMode(mount_mode mode, bool& all, bool& bfs, bool& restore) 780 { 781 all = bfs = restore = false; 782 783 switch (mode) { 784 case kAllVolumes: 785 all = true; 786 break; 787 case kOnlyBFSVolumes: 788 bfs = true; 789 break; 790 case kRestorePreviousVolumes: 791 restore = true; 792 break; 793 794 default: 795 break; 796 } 797 } 798 799 800 mount_mode 801 AutoMounter::_ToMode(bool all, bool bfs, bool restore) 802 { 803 if (all) 804 return kAllVolumes; 805 if (bfs) 806 return kOnlyBFSVolumes; 807 if (restore) 808 return kRestorePreviousVolumes; 809 810 return kNoVolumes; 811 } 812 813 814 void 815 AutoMounter::_ReadSettings() 816 { 817 BPath directoryPath; 818 if (find_directory(B_USER_SETTINGS_DIRECTORY, &directoryPath, true) 819 != B_OK) { 820 return; 821 } 822 823 BPath path(directoryPath); 824 path.Append(kMountServerSettings); 825 fPrefsFile.SetTo(path.Path(), O_RDWR); 826 827 if (fPrefsFile.InitCheck() != B_OK) { 828 // no prefs file yet, create a new one 829 830 BDirectory dir(directoryPath.Path()); 831 dir.CreateFile(kMountServerSettings, &fPrefsFile); 832 return; 833 } 834 835 ssize_t settingsSize = (ssize_t)fPrefsFile.Seek(0, SEEK_END); 836 if (settingsSize == 0) 837 return; 838 839 ASSERT(settingsSize != 0); 840 char *buffer = new(std::nothrow) char[settingsSize]; 841 if (buffer == NULL) { 842 PRINT(("error writing automounter settings, out of memory\n")); 843 return; 844 } 845 846 fPrefsFile.Seek(0, 0); 847 if (fPrefsFile.Read(buffer, (size_t)settingsSize) != settingsSize) { 848 PRINT(("error reading automounter settings\n")); 849 delete [] buffer; 850 return; 851 } 852 853 BMessage message('stng'); 854 status_t result = message.Unflatten(buffer); 855 if (result != B_OK) { 856 PRINT(("error %s unflattening automounter settings, size %" B_PRIdSSIZE "\n", 857 strerror(result), settingsSize)); 858 delete [] buffer; 859 return; 860 } 861 862 delete [] buffer; 863 864 // update flags and modes from the message 865 _UpdateSettingsFromMessage(&message); 866 // copy the previously mounted partitions 867 fSettings = message; 868 } 869 870 871 void 872 AutoMounter::_WriteSettings() 873 { 874 if (fPrefsFile.InitCheck() != B_OK) 875 return; 876 877 BMessage message('stng'); 878 _GetSettings(&message); 879 880 ssize_t settingsSize = message.FlattenedSize(); 881 882 char* buffer = new(std::nothrow) char[settingsSize]; 883 if (buffer == NULL) { 884 PRINT(("error writing automounter settings, out of memory\n")); 885 return; 886 } 887 888 status_t result = message.Flatten(buffer, settingsSize); 889 890 fPrefsFile.Seek(0, SEEK_SET); 891 fPrefsFile.SetSize(0); 892 893 result = fPrefsFile.Write(buffer, (size_t)settingsSize); 894 if (result != settingsSize) 895 PRINT(("error writing automounter settings, %s\n", strerror(result))); 896 897 delete [] buffer; 898 } 899 900 901 void 902 AutoMounter::_UpdateSettingsFromMessage(BMessage* message) 903 { 904 // auto mounter settings 905 906 bool all, bfs, restore; 907 if (message->FindBool("autoMountAll", &all) != B_OK) 908 all = true; 909 if (message->FindBool("autoMountAllBFS", &bfs) != B_OK) 910 bfs = false; 911 912 fRemovableMode = _ToMode(all, bfs, false); 913 914 // initial mount settings 915 916 if (message->FindBool("initialMountAll", &all) != B_OK) 917 all = false; 918 if (message->FindBool("initialMountAllBFS", &bfs) != B_OK) 919 bfs = false; 920 if (message->FindBool("initialMountRestore", &restore) != B_OK) 921 restore = true; 922 923 fNormalMode = _ToMode(all, bfs, restore); 924 925 // eject settings 926 bool eject; 927 if (message->FindBool("ejectWhenUnmounting", &eject) == B_OK) 928 fEjectWhenUnmounting = eject; 929 } 930 931 932 void 933 AutoMounter::_GetSettings(BMessage *message) 934 { 935 message->MakeEmpty(); 936 937 bool all, bfs, restore; 938 939 _FromMode(fNormalMode, all, bfs, restore); 940 message->AddBool("initialMountAll", all); 941 message->AddBool("initialMountAllBFS", bfs); 942 message->AddBool("initialMountRestore", restore); 943 944 _FromMode(fRemovableMode, all, bfs, restore); 945 message->AddBool("autoMountAll", all); 946 message->AddBool("autoMountAllBFS", bfs); 947 948 message->AddBool("ejectWhenUnmounting", fEjectWhenUnmounting); 949 950 // Save mounted volumes so we can optionally mount them on next 951 // startup 952 ArchiveVisitor visitor(*message); 953 BDiskDeviceRoster().VisitEachMountedPartition(&visitor); 954 } 955 956 957 /*static*/ bool 958 AutoMounter::_SuggestMountFlags(const BPartition* partition, uint32* _flags) 959 { 960 uint32 mountFlags = 0; 961 962 bool askReadOnly = true; 963 964 if (partition->ContentType() != NULL 965 && strcmp(partition->ContentType(), kPartitionTypeBFS) == 0) { 966 askReadOnly = false; 967 } 968 969 BDiskSystem diskSystem; 970 status_t status = partition->GetDiskSystem(&diskSystem); 971 if (status == B_OK && !diskSystem.SupportsWriting()) 972 askReadOnly = false; 973 974 if (partition->IsReadOnly()) 975 askReadOnly = false; 976 977 if (askReadOnly && ((BServer*)be_app)->InitGUIContext() != B_OK) { 978 // Mount read-only, just to be safe. 979 mountFlags |= B_MOUNT_READ_ONLY; 980 askReadOnly = false; 981 } 982 983 if (askReadOnly) { 984 // Suggest to the user to mount read-only until Haiku is more mature. 985 BString string; 986 string.SetToFormat(B_TRANSLATE("Mounting volume '%s'\n\n"), 987 partition->ContentName().String()); 988 989 // TODO: Use distro name instead of "Haiku"... 990 string << B_TRANSLATE("The file system on this volume is not the " 991 "Be file system. It is recommended to mount it in read-only " 992 "mode, to prevent unintentional data loss because of bugs " 993 "in Haiku."); 994 995 BAlert* alert = new BAlert(B_TRANSLATE("Mount warning"), 996 string.String(), B_TRANSLATE("Mount read/write"), 997 B_TRANSLATE("Cancel"), B_TRANSLATE("Mount read-only"), 998 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT); 999 alert->SetShortcut(1, B_ESCAPE); 1000 int32 choice = alert->Go(); 1001 switch (choice) { 1002 case 0: 1003 break; 1004 case 1: 1005 return false; 1006 case 2: 1007 mountFlags |= B_MOUNT_READ_ONLY; 1008 break; 1009 } 1010 } 1011 1012 *_flags = mountFlags; 1013 return true; 1014 } 1015 1016 1017 // #pragma mark - 1018 1019 1020 int 1021 main(int argc, char* argv[]) 1022 { 1023 AutoMounter app; 1024 1025 app.Run(); 1026 return 0; 1027 } 1028 1029 1030