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