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