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