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