Files
haiku-beta6/src/kits/tracker/AutoMounter.cpp
T
Ingo Weinhold 4fba3522e0 * Removed src/kits/tracker/OpenHashTable.h. The shared version in
headers/private/shared is newer, though with small interface changes.
* Removed the unnecessary Debug.h include in
  headers/private/shared/ObjectList.h.
* Adjusted sources using these headers, mostly by adding missing includes.
* Lots of automatic whitespace cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30123 a95241bf-73f2-0310-859d-f6bbb57e9c96
2009-04-11 21:24:32 +00:00

1898 lines
47 KiB
C++

/*
Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
#include "AutoMounter.h"
#include <Debug.h>
#include "AutoLock.h"
#include "AutoMounterSettings.h"
#include "Commands.h"
#include "FSUtils.h"
#include "Tracker.h"
#include "TrackerSettings.h"
#ifdef __HAIKU__
# include <DiskDeviceRoster.h>
# include <DiskDevice.h>
# include <DiskDeviceList.h>
# include <DiskDeviceTypes.h>
# include <DiskSystem.h>
# include <fs_volume.h>
#endif
#include <Alert.h>
#include <fs_info.h>
#include <Message.h>
#include <Node.h>
#include <NodeMonitor.h>
#include <String.h>
#include <VolumeRoster.h>
#include <string.h>
#include <unistd.h>
static const char *kAutoMounterSettings = "automounter_settings";
#ifdef __HAIKU__
// #pragma mark - Haiku Disk Device API
static const uint32 kMsgInitialScan = 'insc';
static const char* kMountFlagsKeyExtension = " mount flags";
AutoMounter::AutoMounter()
: BLooper("AutoMounter", B_LOW_PRIORITY),
fNormalMode(kRestorePreviousVolumes),
fRemovableMode(kAllVolumes)
{
if (!BootedInSafeMode()) {
_ReadSettings();
} else {
// defeat automounter in safe mode, don't even care about the settings
fNormalMode = kNoVolumes;
fRemovableMode = kNoVolumes;
}
BDiskDeviceRoster().StartWatching(this,
B_DEVICE_REQUEST_DEVICE | B_DEVICE_REQUEST_DEVICE_LIST);
PostMessage(kMsgInitialScan);
}
AutoMounter::~AutoMounter()
{
BDiskDeviceRoster().StopWatching(this);
}
void
AutoMounter::_MountVolumes(mount_mode normal, mount_mode removable,
bool initialRescan)
{
if (normal == kNoVolumes && removable == kNoVolumes)
return;
class InitialMountVisitor : public BDiskDeviceVisitor {
public:
InitialMountVisitor(mount_mode normalMode, mount_mode removableMode,
bool initialRescan, BMessage& previous)
:
fNormalMode(normalMode),
fRemovableMode(removableMode),
fInitialRescan(initialRescan),
fPrevious(previous)
{
}
virtual
~InitialMountVisitor()
{
}
virtual bool
Visit(BDiskDevice* device)
{
return Visit(device, 0);
}
virtual bool
Visit(BPartition* partition, int32 level)
{
mount_mode mode = partition->Device()->IsRemovableMedia()
? fRemovableMode : fNormalMode;
if (mode == kNoVolumes
|| partition->IsMounted()
|| !partition->ContainsFileSystem())
return false;
BPath path;
if (partition->GetPath(&path) != B_OK)
return false;
if (mode == kRestorePreviousVolumes) {
// mount all volumes that were stored in the settings file
const char *volumeName = NULL;
if (partition->ContentName() == NULL
|| fPrevious.FindString(path.Path(), &volumeName)
!= B_OK
|| strcmp(volumeName, partition->ContentName()))
return false;
} else if (mode == kOnlyBFSVolumes) {
if (partition->ContentType() == NULL
|| strcmp(partition->ContentType(), kPartitionTypeBFS))
return false;
}
uint32 mountFlags;
BString mountFlagsKey(path.Path());
mountFlagsKey << kMountFlagsKeyExtension;
if (fPrevious.FindInt32(mountFlagsKey.String(),
(int32*)&mountFlags) < B_OK) {
mountFlags = 0;
}
if (partition->Mount(NULL, mountFlags) == B_OK
&& partition->GetMountPoint(&path) == B_OK) {
// notify Tracker that a new volume has been started
BMessage note(kVolumeMounted);
note.AddString("path", path.Path());
note.AddBool("initial rescan", fInitialRescan);
be_app->PostMessage(&note);
}
return false;
}
private:
mount_mode fNormalMode;
mount_mode fRemovableMode;
bool fInitialRescan;
BMessage& fPrevious;
} visitor(normal, removable, initialRescan, fSettings);
BDiskDeviceList devices;
status_t status = devices.Fetch();
if (status == B_OK)
devices.VisitEachPartition(&visitor);
}
bool
AutoMounter::_SuggestMountFlags(const BPartition* partition,
uint32* _flags) const
{
uint32 mountFlags = 0;
bool askReadOnly = true;
bool isBFS = false;
if (partition->ContentType() != NULL
&& strcmp(partition->ContentType(), kPartitionTypeBFS) == 0) {
#if 0
askReadOnly = false;
#endif
isBFS = true;
}
BDiskSystem diskSystem;
status_t status = partition->GetDiskSystem(&diskSystem);
if (status == B_OK && !diskSystem.SupportsWriting())
askReadOnly = false;
if (partition->IsReadOnly())
askReadOnly = false;
if (askReadOnly) {
// Suggest to the user to mount read-only until Haiku is more mature.
// TODO: would be nice to skip this for file systems which don't have
// support for writing anyways.
BString string;
// TODO: Use distro name instead of "Haiku"...
if (!isBFS) {
string << "The file system on this volume is not the Haiku file "
"system. It is strongly suggested to mount it in read-only "
"mode. ";
} else {
string << "It is suggested to mount all additional Haiku volumes "
"in read-only mode. ";
}
string << "This will prevent unintentional data loss because of "
"errors in Haiku.";
BAlert* alert = new BAlert("Mount Warning", string.String(),
"Mount Read/Write", "Cancel", "Mount Read-only",
B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
int32 choice = alert->Go();
switch (choice) {
case 0:
break;
case 1:
return false;
case 2:
mountFlags |= B_MOUNT_READ_ONLY;
break;
}
}
*_flags = mountFlags;
return true;
}
void
AutoMounter::_MountVolume(const BMessage* message)
{
int32 id;
if (message->FindInt32("id", &id) != B_OK)
return;
BDiskDeviceRoster roster;
BPartition *partition;
BDiskDevice device;
if (roster.GetPartitionWithID(id, &device, &partition) != B_OK)
return;
uint32 mountFlags;
if (!_SuggestMountFlags(partition, &mountFlags))
return;
status_t status = partition->Mount(NULL, mountFlags);
if (status < B_OK) {
BString string;
string << "Error mounting volume. (" << strerror(status) << ")";
(new BAlert("", string.String(), "Ok"))->Go(NULL);
}
}
bool
AutoMounter::_SuggestForceUnmount(const char* name, status_t error)
{
BString text;
text << "Could not unmount disk \"" << name << "\":\n\t" << strerror(error);
text << "\n\nShould unmounting be forced?\n\n"
"Note: if an application is currently writing to the volume, unmounting"
" it now might result in loss of data.\n";
BAlert* alert = new BAlert("", text.String(), "Cancel", "Force Unmount",
NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
int32 choice = alert->Go();
return choice == 1;
}
void
AutoMounter::_ReportUnmountError(const char* name, status_t error)
{
BString text;
text << "Could not unmount disk \"" << name << "\":\n\t" << strerror(error);
(new BAlert("", text.String(), "OK", NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT))->Go(NULL);
}
void
AutoMounter::_UnmountAndEjectVolume(BPartition* partition, BPath& mountPoint,
const char* name)
{
BDiskDevice device;
if (partition == NULL) {
// Try to retrieve partition
BDiskDeviceRoster().FindPartitionByMountPoint(mountPoint.Path(),
&device, &partition);
}
status_t status;
if (partition != NULL)
status = partition->Unmount();
else
status = fs_unmount_volume(mountPoint.Path(), 0);
if (status < B_OK) {
if (!_SuggestForceUnmount(name, status))
return;
if (partition != NULL)
status = partition->Unmount(B_FORCE_UNMOUNT);
else
status = fs_unmount_volume(mountPoint.Path(), B_FORCE_UNMOUNT);
}
if (status < B_OK) {
_ReportUnmountError(partition->ContentName(), status);
return;
}
if (TrackerSettings().EjectWhenUnmounting() && partition != NULL) {
// eject device if it doesn't have any mounted partitions left
class IsMountedVisitor : public BDiskDeviceVisitor {
public:
IsMountedVisitor()
:
fHasMounted(false)
{
}
virtual bool Visit(BDiskDevice* device)
{
return Visit(device, 0);
}
virtual bool Visit(BPartition* partition, int32 level)
{
if (partition->IsMounted()) {
fHasMounted = true;
return true;
}
return false;
}
bool HasMountedPartitions() const
{
return fHasMounted;
}
private:
bool fHasMounted;
} visitor;
partition->Device()->VisitEachDescendant(&visitor);
if (!visitor.HasMountedPartitions())
partition->Device()->Eject();
}
// remove the directory if it's a directory in rootfs
if (dev_for_path(mountPoint.Path()) == dev_for_path("/"))
rmdir(mountPoint.Path());
}
void
AutoMounter::_UnmountAndEjectVolume(BMessage* message)
{
int32 id;
if (message->FindInt32("id", &id) == B_OK) {
BDiskDeviceRoster roster;
BPartition *partition;
BDiskDevice device;
if (roster.GetPartitionWithID(id, &device, &partition) != B_OK)
return;
BPath path;
if (partition->GetMountPoint(&path) == B_OK)
_UnmountAndEjectVolume(partition, path, partition->ContentName());
} else {
// see if we got a dev_t
dev_t device;
if (message->FindInt32("device_id", &device) != B_OK)
return;
BVolume volume(device);
status_t status = volume.InitCheck();
char name[B_FILE_NAME_LENGTH];
if (status == B_OK)
status = volume.GetName(name);
if (status < B_OK)
snprintf(name, sizeof(name), "device:%ld", device);
BPath path;
if (status == B_OK) {
BDirectory mountPoint;
status = volume.GetRootDirectory(&mountPoint);
if (status == B_OK)
status = path.SetTo(&mountPoint, ".");
}
if (status == B_OK)
_UnmountAndEjectVolume(NULL, path, name);
}
}
void
AutoMounter::_FromMode(mount_mode mode, bool& all, bool& bfs, bool& restore)
{
all = bfs = restore = false;
switch (mode) {
case kAllVolumes:
all = true;
break;
case kOnlyBFSVolumes:
bfs = true;
break;
case kRestorePreviousVolumes:
restore = true;
break;
default:
break;
}
}
AutoMounter::mount_mode
AutoMounter::_ToMode(bool all, bool bfs, bool restore)
{
if (all)
return kAllVolumes;
if (bfs)
return kOnlyBFSVolumes;
if (restore)
return kRestorePreviousVolumes;
return kNoVolumes;
}
void
AutoMounter::_ReadSettings()
{
BPath directoryPath;
if (FSFindTrackerSettingsDir(&directoryPath) != B_OK)
return;
BPath path(directoryPath);
path.Append(kAutoMounterSettings);
fPrefsFile.SetTo(path.Path(), O_RDWR);
if (fPrefsFile.InitCheck() != B_OK) {
// no prefs file yet, create a new one
BDirectory dir(directoryPath.Path());
dir.CreateFile(kAutoMounterSettings, &fPrefsFile);
return;
}
ssize_t settingsSize = (ssize_t)fPrefsFile.Seek(0, SEEK_END);
if (settingsSize == 0)
return;
ASSERT(settingsSize != 0);
char *buffer = new char[settingsSize];
fPrefsFile.Seek(0, 0);
if (fPrefsFile.Read(buffer, (size_t)settingsSize) != settingsSize) {
PRINT(("error reading automounter settings\n"));
delete [] buffer;
return;
}
BMessage message('stng');
status_t result = message.Unflatten(buffer);
if (result != B_OK) {
PRINT(("error %s unflattening settings, size %d\n", strerror(result),
settingsSize));
delete [] buffer;
return;
}
delete [] buffer;
// update flags and modes from the message
_UpdateSettingsFromMessage(&message);
// copy the previously mounted partitions
fSettings = message;
}
void
AutoMounter::_WriteSettings()
{
if (fPrefsFile.InitCheck() != B_OK)
return;
BMessage message('stng');
GetSettings(&message);
ssize_t settingsSize = message.FlattenedSize();
char *buffer = new char[settingsSize];
status_t result = message.Flatten(buffer, settingsSize);
fPrefsFile.Seek(0, 0);
result = fPrefsFile.Write(buffer, (size_t)settingsSize);
if (result != settingsSize)
PRINT(("error writing settings, %s\n", strerror(result)));
delete [] buffer;
}
void
AutoMounter::_UpdateSettingsFromMessage(BMessage* message)
{
// auto mounter settings
bool all, bfs, restore;
if (message->FindBool("autoMountAll", &all) != B_OK)
all = true;
if (message->FindBool("autoMountAllBFS", &bfs) != B_OK)
bfs = false;
fRemovableMode = _ToMode(all, bfs, false);
// initial mount settings
if (message->FindBool("initialMountAll", &all) != B_OK)
all = false;
if (message->FindBool("initialMountAllBFS", &bfs) != B_OK)
bfs = false;
if (message->FindBool("initialMountRestore", &restore) != B_OK)
restore = true;
fNormalMode = _ToMode(all, bfs, restore);
}
void
AutoMounter::GetSettings(BMessage *message)
{
message->MakeEmpty();
bool all, bfs, restore;
_FromMode(fNormalMode, all, bfs, restore);
message->AddBool("initialMountAll", all);
message->AddBool("initialMountAllBFS", bfs);
message->AddBool("initialMountRestore", restore);
_FromMode(fRemovableMode, all, bfs, restore);
message->AddBool("autoMountAll", all);
message->AddBool("autoMountAllBFS", bfs);
// Save mounted volumes so we can optionally mount them on next
// startup
BVolumeRoster volumeRoster;
BVolume volume;
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
fs_info info;
if (fs_stat_dev(volume.Device(), &info) == 0
&& (info.flags & (B_FS_IS_REMOVABLE | B_FS_IS_PERSISTENT)) != 0) {
message->AddString(info.device_name, info.volume_name);
BString mountFlagsKey(info.device_name);
mountFlagsKey << kMountFlagsKeyExtension;
uint32 mountFlags = 0;
if (volume.IsReadOnly())
mountFlags |= B_MOUNT_READ_ONLY;
message->AddInt32(mountFlagsKey.String(), mountFlags);
}
}
}
void
AutoMounter::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgInitialScan:
_MountVolumes(fNormalMode, fRemovableMode, true);
be_app->PostMessage(kOpenPreviouslyOpenWindows);
break;
case kMountVolume:
_MountVolume(message);
break;
case kUnmountVolume:
_UnmountAndEjectVolume(message);
break;
case kSetAutomounterParams:
{
bool rescanNow = false;
message->FindBool("rescanNow", &rescanNow);
_UpdateSettingsFromMessage(message);
GetSettings(&fSettings);
_WriteSettings();
if (rescanNow)
_MountVolumes(fNormalMode, fRemovableMode, false);
break;
}
case kMountAllNow:
_MountVolumes(kAllVolumes, kAllVolumes, false);
break;
case B_DEVICE_UPDATE:
message->PrintToStream();
int32 event;
if (message->FindInt32("event", &event) != B_OK
|| (event != B_DEVICE_MEDIA_CHANGED
&& event != B_DEVICE_ADDED))
break;
_MountVolumes(kNoVolumes, fRemovableMode, false);
break;
#if 0
case B_NODE_MONITOR:
{
int32 opcode;
if (message->FindInt32("opcode", &opcode) != B_OK)
break;
switch (opcode) {
// The name of a mount point has changed
case B_ENTRY_MOVED: {
WRITELOG(("*** Received Mount Point Renamed Notification"));
const char *newName;
if (message->FindString("name", &newName) != B_OK) {
WRITELOG(("ERROR: Couldn't find name field in update message"));
PRINT_OBJECT(*message);
break ;
}
//
// When the node monitor reports a move, it gives the
// parent device and inode that moved. The problem is
// that the inode is the inode of root *in* the filesystem,
// which is generally always the same number for every
// filesystem of a type.
//
// What we'd really like is the device that the moved
// volume is mounted on. Find this by using the
// *new* name and directory, and then stat()ing that to
// find the device.
//
dev_t parentDevice;
if (message->FindInt32("device", &parentDevice) != B_OK) {
WRITELOG(("ERROR: Couldn't find 'device' field in update"
" message"));
PRINT_OBJECT(*message);
break;
}
ino_t toDirectory;
if (message->FindInt64("to directory", &toDirectory)!=B_OK){
WRITELOG(("ERROR: Couldn't find 'to directory' field in update"
"message"));
PRINT_OBJECT(*message);
break;
}
entry_ref root_entry(parentDevice, toDirectory, newName);
BNode entryNode(&root_entry);
if (entryNode.InitCheck() != B_OK) {
WRITELOG(("ERROR: Couldn't create mount point entry node: %s/n",
strerror(entryNode.InitCheck())));
break;
}
node_ref mountPointNode;
if (entryNode.GetNodeRef(&mountPointNode) != B_OK) {
WRITELOG(("ERROR: Couldn't get node ref for new mount point"));
break;
}
WRITELOG(("Attempt to rename device %li to %s", mountPointNode.device,
newName));
Partition *partition = FindPartition(mountPointNode.device);
if (partition != NULL) {
WRITELOG(("Found device, changing name."));
BVolume mountVolume(partition->VolumeDeviceID());
BDirectory mountDir;
mountVolume.GetRootDirectory(&mountDir);
BPath dirPath(&mountDir, 0);
partition->SetMountedAt(dirPath.Path());
partition->SetVolumeName(newName);
break;
} else {
WRITELOG(("ERROR: Device %li does not appear to be present",
mountPointNode.device));
}
}
}
break;
}
#endif
default:
BLooper::MessageReceived(message);
break;
}
}
bool
AutoMounter::QuitRequested()
{
if (!BootedInSafeMode()) {
// don't write out settings in safe mode - this would overwrite the
// normal, non-safe mode settings
_WriteSettings();
}
return true;
}
#else // !__HAIKU__
// #pragma mark - R5 DeviceMap API
static const uint32 kStartPolling = 'strp';
static BMessage gSettingsMessage;
static bool gSilentAutoMounter;
static bool sInitialRescan;
struct OneMountFloppyParams {
status_t result;
};
struct MountPartitionParams {
int32 uniqueID;
status_t result;
};
#if xDEBUG
static Partition *
DumpPartition(Partition *partition, void*)
{
partition->Dump();
return 0;
}
#endif
/*! Sets the Tracker Shell's AutoMounter to monitor a node.
n.b. Get's the one AutoMounter and uses Tracker's _special_ WatchNode.
@param nodeToWatch (node_ref const * const) The Node to monitor.
@param flags (uint32) watch_node flags from NodeMonitor.
@return (status_t) watch_node status or B_BAD_TYPE if not a TTracker app.
*/
static status_t
AutoMounterWatchNode(const node_ref *nodeRef, uint32 flags)
{
ASSERT(nodeRef != NULL);
TTracker *tracker = dynamic_cast<TTracker *>(be_app);
if (tracker != NULL) {
return TTracker::WatchNode(nodeRef, flags,
BMessenger(0, tracker->AutoMounterLoop()));
}
return B_BAD_TYPE;
}
/*! Tries to mount the partition and if it can it watches mount point.
@param partition (Partition * const) The partition to mount.
*/
static status_t
MountAndWatch(Partition *partition)
{
ASSERT(partition != NULL);
status_t status = partition->Mount();
if (status != B_OK)
return status;
// Start watching this mount point
node_ref nodeToWatch;
status = partition->GetMountPointNodeRef(&nodeToWatch);
if (status != B_OK) {
PRINT(("Couldn't get mount point node ref: %s\n", strerror(status)));
return status;
}
// notify Tracker that a new volume has been started
BMessage note(kVolumeMounted);
note.AddString("path", partition->MountedAt());
note.AddBool("initial rescan", sInitialRescan);
be_app->PostMessage(&note);
return AutoMounterWatchNode(&nodeToWatch, B_WATCH_NAME);
}
static Partition *
TryMountingEveryOne(Partition *partition, void *castToParams)
{
MountPartitionParams *params = (MountPartitionParams *)castToParams;
if (partition->Mounted() == kMounted) {
if (!gSilentAutoMounter)
PRINT(("%s already mounted\n", partition->VolumeName()));
} else {
status_t result = MountAndWatch(partition);
// return error if caller asked for it
if (params)
params->result = result;
if (!gSilentAutoMounter) {
if (result == B_OK)
PRINT(("%s mounted OK\n", partition->VolumeName()));
else
PRINT(("Error '%s' mounting %s\n",
strerror(result), partition->VolumeName()));
}
if (params && result != B_OK)
// signal an error
return partition;
}
return NULL;
}
static Partition *
OneTryMountingFloppy(Partition *partition, void *castToParams)
{
OneMountFloppyParams *params = (OneMountFloppyParams *)castToParams;
if (partition->GetDevice()->IsFloppy()){
status_t result = MountAndWatch(partition);
// return error if caller asked for it
if (params)
params->result = result;
return partition;
}
return 0;
}
static Partition *
OneMatchFloppy(Partition *partition, void *)
{
if (partition->GetDevice()->IsFloppy())
return partition;
return 0;
}
static Partition *
TryMountingBFSOne(Partition *partition, void *params)
{
if (strcmp(partition->FileSystemShortName(), "bfs") == 0)
return TryMountingEveryOne(partition, params);
return NULL;
}
static Partition *
TryMountingRestoreOne(Partition *partition, void *params)
{
Session *session = partition->GetSession();
Device *device = session->GetDevice();
// create the name for the virtual device
char path[B_FILE_NAME_LENGTH];
int len = (int)strlen(device->Name()) - (int)strlen("/raw");
if (session->CountPartitions() != 1)
sprintf(path, "%.*s/%ld_%ld", len, device->Name(), session->Index(), partition->Index());
else
sprintf(path, "%s", device->Name());
// Find the name of the current device/volume in the saved settings
// and open if found.
const char *volumename;
if (gSettingsMessage.FindString(path, &volumename) == B_OK
&& strcmp(volumename, partition->VolumeName()) == 0)
return TryMountingEveryOne(partition, params);
return NULL;
}
static Partition *
TryMountingHFSOne(Partition *partition, void *params)
{
if (strcmp(partition->FileSystemShortName(), "hfs") == 0)
return TryMountingEveryOne(partition, params);
return NULL;
}
struct FindPartitionByDeviceIDParams {
dev_t dev;
};
static Partition *
FindPartitionByDeviceID(Partition *partition, void *castToParams)
{
FindPartitionByDeviceIDParams *params = (FindPartitionByDeviceIDParams*) castToParams;
if (params->dev == partition->VolumeDeviceID())
return partition;
return 0;
}
static Partition *
TryWatchMountPoint(Partition *partition, void *)
{
node_ref nodeRef;
if (partition->GetMountPointNodeRef(&nodeRef) == B_OK)
AutoMounterWatchNode(&nodeRef, B_WATCH_NAME);
return 0;
}
static Partition *
TryMountVolumeByID(Partition *partition, void *params)
{
PRINT(("Try mounting partition %i\n", partition->UniqueID()));
if (!partition->Hidden() && partition->UniqueID() ==
((MountPartitionParams *)params)->uniqueID) {
Partition *result = TryMountingEveryOne(partition, params);
if (result)
return result;
return partition;
}
return NULL;
}
static Partition *
AutomountOne(Partition *partition, void *castToParams)
{
PRINT(("Partition %s not mounted\n", partition->Name()));
AutomountParams *params = (AutomountParams *)castToParams;
if (params->mountRemovableDisksOnly
&& (!partition->GetDevice()->NoMedia()
&& !partition->GetDevice()->Removable()))
// not removable, don't mount it
return NULL;
if (params->mountAllFS)
return TryMountingEveryOne(partition, NULL);
if (params->mountBFS)
return TryMountingBFSOne(partition, NULL);
if (params->mountHFS)
return TryMountingHFSOne(partition, NULL);
return NULL;
}
struct UnmountDeviceParams {
dev_t device;
status_t result;
};
static Partition *
UnmountIfMatchingID(Partition *partition, void *castToParams)
{
UnmountDeviceParams *params = (UnmountDeviceParams *)castToParams;
if (partition->VolumeDeviceID() == params->device) {
TTracker *tracker = dynamic_cast<TTracker *>(be_app);
if (tracker && tracker->QueryActiveForDevice(params->device)) {
BString text;
text << "To unmount " << partition->VolumeName() << " some query "
"windows have to be closed. Would you like to close the query "
"windows?";
BAlert* alert = new BAlert("", text.String(), "Cancel",
"Close and unmount", NULL, B_WIDTH_FROM_LABEL);
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
return partition;
tracker->CloseActiveQueryWindows(params->device);
}
params->result = partition->Unmount();
Device *device = partition->GetDevice();
bool deviceHasMountedPartitions = false;
if (params->result == B_OK && device->Removable()) {
for (int32 sessionIndex = 0; ; sessionIndex++) {
Session *session = device->SessionAt(sessionIndex);
if (!session)
break;
for (int32 partitionIndex = 0; ; partitionIndex++) {
Partition *partition = session->PartitionAt(partitionIndex);
if (!partition)
break;
if (partition->Mounted() == kMounted) {
deviceHasMountedPartitions = true;
break;
}
}
}
if (!deviceHasMountedPartitions
&& TrackerSettings().EjectWhenUnmounting())
params->result = partition->GetDevice()->Eject();
}
return partition;
}
return NULL;
}
static Partition *
NotifyFloppyNotMountable(Partition *partition, void *)
{
if (partition->Mounted() != kMounted
&& partition->GetDevice()->IsFloppy()
&& !partition->Hidden()) {
(new BAlert("", "The format of the floppy disk in the disk drive is "
"not recognized or the disk has never been formatted.", "OK"))->Go(0);
partition->GetDevice()->Eject();
}
return NULL;
}
static Device *
FindFloppyDevice(Device *device, void *)
{
if (device->IsFloppy())
return device;
return 0;
}
#ifdef MOUNT_MENU_IN_DESKBAR
// just for testing
Partition *
AddMountableItemToMessage(Partition *partition, void *castToParams)
{
BMessage *message = static_cast<BMessage *>(castToParams);
message->AddString("DeviceName", partition->GetDevice()->Name());
const char *name;
if (*partition->VolumeName())
name = partition->VolumeName();
else if (*partition->Type())
name = partition->Type();
else
name = partition->GetDevice()->DisplayName();
message->AddString("DisplayName", name);
BMessage invokeMsg;
if (partition->GetDevice()->IsFloppy())
invokeMsg.what = kTryMountingFloppy;
else
invokeMsg.what = kMountVolume;
invokeMsg.AddInt32("id", partition->UniqueID());
message->AddMessage("InvokeMessage", &invokeMsg);
return NULL;
}
#endif // #ifdef MOUNT_MENU_IN_DESKBAR
AutoMounter::AutoMounter(bool checkRemovableOnly, bool checkCDs,
bool checkFloppies, bool checkOtherRemovable, bool autoMountRemovablesOnly,
bool autoMountAll, bool autoMountAllBFS, bool autoMountAllHFS,
bool initialMountAll, bool initialMountAllBFS, bool initialMountRestore,
bool initialMountAllHFS)
: BLooper("AutoMounter", B_LOW_PRIORITY),
fInitialMountAll(initialMountAll),
fInitialMountAllBFS(initialMountAllBFS),
fInitialMountRestore(initialMountRestore),
fInitialMountAllHFS(initialMountAllHFS),
fSuspended(false),
fQuitting(false)
{
fScanParams.shortestRescanHartbeat = 5000000;
fScanParams.checkFloppies = checkFloppies;
fScanParams.checkCDROMs = checkCDs;
fScanParams.checkOtherRemovable = checkOtherRemovable;
fScanParams.removableOrUnknownOnly = checkRemovableOnly;
fAutomountParams.mountAllFS = autoMountAll;
fAutomountParams.mountBFS = autoMountAllBFS;
fAutomountParams.mountHFS = autoMountAllHFS;
fAutomountParams.mountRemovableDisksOnly = autoMountRemovablesOnly;
gSilentAutoMounter = true;
if (!BootedInSafeMode()) {
ReadSettings();
sInitialRescan = true;
thread_id rescan = spawn_thread(AutoMounter::InitialRescanBinder,
"AutomountInitialScan", B_DISPLAY_PRIORITY, this);
resume_thread(rescan);
} else {
// defeat automounter in safe mode, don't even care about the settings
fAutomountParams.mountAllFS = false;
fAutomountParams.mountBFS = false;
fAutomountParams.mountHFS = false;
fInitialMountAll = false;
fInitialMountAllBFS = false;
fInitialMountRestore = false;
fInitialMountAllHFS = false;
}
// Watch mount/unmount
TTracker::WatchNode(0, B_WATCH_MOUNT, this);
}
AutoMounter::~AutoMounter()
{
}
Partition* AutoMounter::FindPartition(dev_t dev)
{
FindPartitionByDeviceIDParams params;
params.dev = dev;
return fList.EachMountedPartition(FindPartitionByDeviceID, &params);
}
void
AutoMounter::RescanDevices()
{
stop_watching(this);
fList.RescanDevices(true);
fList.UpdateMountingInfo();
fList.EachMountedPartition(TryWatchMountPoint, 0);
TTracker::WatchNode(0, B_WATCH_MOUNT, this);
fList.EachMountedPartition(TryWatchMountPoint, 0);
}
void
AutoMounter::MessageReceived(BMessage *message)
{
switch (message->what) {
case kAutomounterRescan:
RescanDevices();
break;
case kStartPolling:
// PRINT(("starting the automounter\n"));
fScanThread = spawn_thread(AutoMounter::WatchVolumeBinder,
"AutomountScan", B_LOW_PRIORITY, this);
resume_thread(fScanThread);
break;
case kMountVolume:
MountVolume(message);
break;
case kUnmountVolume:
UnmountAndEjectVolume(message);
break;
case kSetAutomounterParams:
{
bool rescanNow = false;
message->FindBool("rescanNow", &rescanNow);
SetParams(message, rescanNow);
WriteSettings();
break;
}
case kMountAllNow:
RescanDevices();
MountAllNow();
break;
case kSuspendAutomounter:
SuspendResume(true);
break;
case kResumeAutomounter:
SuspendResume(false);
break;
case kTryMountingFloppy:
TryMountingFloppy();
break;
case B_NODE_MONITOR:
{
int32 opcode;
if (message->FindInt32("opcode", &opcode) != B_OK)
break;
switch (opcode) {
case B_DEVICE_MOUNTED: {
WRITELOG(("** Received Device Mounted Notification"));
dev_t device;
if (message->FindInt32("new device", &device) == B_OK) {
Partition *partition = FindPartition(device);
if (partition == NULL || partition->Mounted() != kMounted) {
WRITELOG(("Device %i not in device list. Someone mounted it outside "
"of Tracker", device));
//
// This is the worst case. Someone has mounted
// something from outside of tracker.
// Unfortunately, there's no easy way to tell which
// partition was just mounted (or if we even know about the device),
// so stop watching all nodes, rescan to see what is now mounted,
// and start watching again.
//
RescanDevices();
} else
WRITELOG(("Found partition\n"));
} else {
WRITELOG(("ERROR: Could not find mounted device ID in message"));
PRINT_OBJECT(*message);
}
break;
}
case B_DEVICE_UNMOUNTED: {
WRITELOG(("*** Received Device Unmounted Notification"));
dev_t device;
if (message->FindInt32("device", &device) == B_OK) {
Partition *partition = FindPartition(device);
if (partition != 0) {
WRITELOG(("Found device in device list. Updating state to unmounted."));
partition->SetMountState(kNotMounted);
} else
WRITELOG(("Unmounted device %i was not in device list", device));
} else {
WRITELOG(("ERROR: Could not find unmounted device ID in message"));
PRINT_OBJECT(*message);
}
break;
}
// The name of a mount point has changed
case B_ENTRY_MOVED: {
WRITELOG(("*** Received Mount Point Renamed Notification"));
const char *newName;
if (message->FindString("name", &newName) != B_OK) {
WRITELOG(("ERROR: Couldn't find name field in update message"));
PRINT_OBJECT(*message);
break ;
}
//
// When the node monitor reports a move, it gives the
// parent device and inode that moved. The problem is
// that the inode is the inode of root *in* the filesystem,
// which is generally always the same number for every
// filesystem of a type.
//
// What we'd really like is the device that the moved
// volume is mounted on. Find this by using the
// *new* name and directory, and then stat()ing that to
// find the device.
//
dev_t parentDevice;
if (message->FindInt32("device", &parentDevice) != B_OK) {
WRITELOG(("ERROR: Couldn't find 'device' field in update"
" message"));
PRINT_OBJECT(*message);
break;
}
ino_t toDirectory;
if (message->FindInt64("to directory", &toDirectory)!=B_OK){
WRITELOG(("ERROR: Couldn't find 'to directory' field in update"
"message"));
PRINT_OBJECT(*message);
break;
}
entry_ref root_entry(parentDevice, toDirectory, newName);
BNode entryNode(&root_entry);
if (entryNode.InitCheck() != B_OK) {
WRITELOG(("ERROR: Couldn't create mount point entry node: %s/n",
strerror(entryNode.InitCheck())));
break;
}
node_ref mountPointNode;
if (entryNode.GetNodeRef(&mountPointNode) != B_OK) {
WRITELOG(("ERROR: Couldn't get node ref for new mount point"));
break;
}
WRITELOG(("Attempt to rename device %li to %s", mountPointNode.device,
newName));
Partition *partition = FindPartition(mountPointNode.device);
if (partition != NULL) {
WRITELOG(("Found device, changing name."));
BVolume mountVolume(partition->VolumeDeviceID());
BDirectory mountDir;
mountVolume.GetRootDirectory(&mountDir);
BPath dirPath(&mountDir, 0);
partition->SetMountedAt(dirPath.Path());
partition->SetVolumeName(newName);
break;
} else {
WRITELOG(("ERROR: Device %li does not appear to be present",
mountPointNode.device));
}
}
}
break;
}
default:
BLooper::MessageReceived(message);
break;
}
}
status_t
AutoMounter::WatchVolumeBinder(void *castToThis)
{
static_cast<AutoMounter *>(castToThis)->WatchVolumes();
return B_OK;
}
void
AutoMounter::WatchVolumes()
{
for(;;) {
snooze(fScanParams.shortestRescanHartbeat);
AutoLock<BLooper> lock(this);
if (!lock)
break;
if (fQuitting) {
lock.Unlock();
break;
}
if (!fSuspended && fList.CheckDevicesChanged(&fScanParams)) {
fList.UnmountDisappearedPartitions();
fList.UpdateChangedDevices(&fScanParams);
fList.EachMountablePartition(AutomountOne, &fAutomountParams);
}
}
}
void
AutoMounter::EachMountableItemAndFloppy(EachPartitionFunction func, void *passThru)
{
AutoLock<BLooper> lock(this);
#if 0
//
// Rescan now to see if anything has changed.
//
if (fList.CheckDevicesChanged(&fScanParams)) {
fList.UpdateChangedDevices(&fScanParams);
fList.UnmountDisappearedPartitions();
}
#endif
//
// If the floppy has never been mounted, it won't have a partition
// in the device list (but it will have a device entry). If it has, the
// partition will appear, but will be set not mounted. Code here works
// around this.
//
if (!IsFloppyMounted() && !FloppyInList()) {
Device *floppyDevice = fList.EachDevice(FindFloppyDevice, 0);
//
// See comments under 'EachPartition'
//
if (floppyDevice != 0) {
Session session(floppyDevice, "floppy", 0, 0, 0);
Partition partition(&session, "floppy", "unknown",
"unknown", "unknown", "floppy", "", 0, 0, 0, false);
(func)(&partition, passThru);
}
}
fList.EachMountablePartition(func, passThru);
}
void
AutoMounter::EachMountedItem(EachPartitionFunction func, void *passThru)
{
AutoLock<BLooper> lock(this);
fList.EachMountedPartition(func, passThru);
}
Partition *
AutoMounter::EachPartition(EachPartitionFunction func, void *passThru)
{
AutoLock<BLooper> lock(this);
if (!IsFloppyMounted() && !FloppyInList()) {
Device *floppyDevice = fList.EachDevice(FindFloppyDevice, 0);
//
// Add a floppy to the list. It normally doesn't appear
// there when a floppy hasn't been mounted because it
// doesn't have any partitions. Note that this makes sure
// that a floppy device exists before adding it, because
// some systems don't have floppy drives (unbelievable, but
// true).
//
if (floppyDevice != 0) {
Session session(floppyDevice, "floppy", 0, 0, 0);
Partition partition(&session, "floppy", "unknown",
"unknown", "unknown", "floppy", "", 0, 0, 0, false);
Partition *result = func(&partition, passThru);
if (result != NULL)
return result;
}
}
return fList.EachPartition(func, passThru);
}
void
AutoMounter::CheckVolumesNow()
{
AutoLock<BLooper> lock(this);
if (fList.CheckDevicesChanged(&fScanParams)) {
fList.UnmountDisappearedPartitions();
fList.UpdateChangedDevices(&fScanParams);
if (!fSuspended)
fList.EachMountablePartition(AutomountOne, &fAutomountParams);
}
}
void
AutoMounter::SuspendResume(bool suspend)
{
AutoLock<BLooper> lock(this);
fSuspended = suspend;
if (fSuspended)
suspend_thread(fScanThread);
else
resume_thread(fScanThread);
}
void
AutoMounter::MountAllNow()
{
AutoLock<BLooper> lock(this);
DeviceScanParams mountAllParams;
mountAllParams.checkFloppies = true;
mountAllParams.checkCDROMs = true;
mountAllParams.checkOtherRemovable = true;
mountAllParams.removableOrUnknownOnly = true;
fList.UnmountDisappearedPartitions();
fList.UpdateChangedDevices(&mountAllParams);
fList.EachMountablePartition(TryMountingEveryOne, 0);
fList.EachPartition(NotifyFloppyNotMountable, 0);
}
void
AutoMounter::TryMountingFloppy()
{
AutoLock<BLooper> lock(this);
DeviceScanParams mountAllParams;
mountAllParams.checkFloppies = true;
mountAllParams.checkCDROMs = false;
mountAllParams.checkOtherRemovable = false;
mountAllParams.removableOrUnknownOnly = false;
fList.UnmountDisappearedPartitions();
fList.UpdateChangedDevices(&mountAllParams);
OneMountFloppyParams params;
params.result = B_ERROR;
fList.EachMountablePartition(OneTryMountingFloppy, &params);
if (params.result != B_OK)
(new BAlert("", "The format of the floppy disk in the disk drive is "
"not recognized or the disk has never been formatted.", "OK"))
->Go(0);
}
bool
AutoMounter::IsFloppyMounted()
{
AutoLock<BLooper> lock(this);
return fList.EachMountedPartition(OneMatchFloppy, 0) != NULL;
}
bool
AutoMounter::FloppyInList()
{
AutoLock<BLooper> lock(this);
return fList.EachPartition(OneMatchFloppy, 0) != NULL;
}
void
AutoMounter::MountVolume(BMessage *message)
{
int32 uniqueID;
if (message->FindInt32("id", &uniqueID) == B_OK) {
if (uniqueID == kFloppyID) {
TryMountingFloppy();
return;
}
MountPartitionParams params;
params.uniqueID = uniqueID;
params.result = B_OK;
if (EachPartition(TryMountVolumeByID, &params) == NULL) {
(new BAlert("", "The format of this volume is unrecognized, or it has "
"never been formatted", "OK"))->Go(0);
} else if (params.result != B_OK) {
BString string;
string << "Error mounting volume. (" << strerror(params.result) << ")";
(new BAlert("", string.String(), "OK"))->Go(0);
}
}
}
status_t
AutoMounter::InitialRescanBinder(void *castToThis)
{
// maybe this can help the strange Tracker lock-up at startup
snooze(500000LL); // wait half a second
AutoMounter *self = static_cast<AutoMounter *>(castToThis);
self->InitialRescan();
// Start watching nodes that were mounted before tracker started
(self->fList).EachMountedPartition(TryWatchMountPoint, 0);
self->PostMessage(kStartPolling, 0);
return B_OK;
}
void
AutoMounter::InitialRescan()
{
AutoLock<BLooper> lock(this);
fList.RescanDevices(false);
fList.UpdateMountingInfo();
// if called after spawn_thread, must lock fList
if (fInitialMountAll) {
//+ PRINT(("mounting all volumes\n"));
fList.EachMountablePartition(TryMountingEveryOne, NULL);
}
if (fInitialMountAllHFS) {
//+ PRINT(("mounting all hfs volumes\n"));
fList.EachMountablePartition(TryMountingHFSOne, NULL);
}
if (fInitialMountAllBFS) {
//+ PRINT(("mounting all bfs volumes\n"));
fList.EachMountablePartition(TryMountingBFSOne, NULL);
}
if (fInitialMountRestore) {
//+ PRINT(("restoring all volumes\n"));
fList.EachMountablePartition(TryMountingRestoreOne, NULL);
}
sInitialRescan = false;
be_app->PostMessage(kOpenPreviouslyOpenWindows);
}
void
AutoMounter::UnmountAndEjectVolume(BMessage *message)
{
dev_t device;
if (message->FindInt32("device_id", &device) != B_OK)
return;
PRINT(("Unmount device %i\n", device));
AutoLock<BLooper> lock(this);
UnmountDeviceParams params;
params.device = device;
params.result = B_OK;
Partition *partition = fList.EachMountedPartition(UnmountIfMatchingID,
&params);
if (!partition) {
PRINT(("Couldn't unmount partition. Rescan and try again\n"));
// could not find partition - must have been mounted by someone
// else
// sync up and try again
// this should really be handled by watching for mount and unmount
// events like the tracker does, not doing that because it is
// a bigger change and we are close to freezing
fList.UnmountDisappearedPartitions();
DeviceScanParams syncRescanParams;
syncRescanParams.checkFloppies = true;
syncRescanParams.checkCDROMs = true;
syncRescanParams.checkOtherRemovable = true;
syncRescanParams.removableOrUnknownOnly = true;
fList.UpdateChangedDevices(&syncRescanParams);
partition = fList.EachMountedPartition(UnmountIfMatchingID, &params);
}
if (!partition) {
PRINT(("Device not in list, unmounting directly\n"));
char path[B_FILE_NAME_LENGTH];
BVolume vol(device);
status_t err = vol.InitCheck();
if (err == B_OK) {
BDirectory mountPoint;
if (err == B_OK)
err = vol.GetRootDirectory(&mountPoint);
BPath mountPointPath;
if (err == B_OK)
err = mountPointPath.SetTo(&mountPoint, ".");
if (err == B_OK)
strcpy(path, mountPointPath.Path());
}
if (err == B_OK) {
PRINT(("unmounting '%s'\n", path));
err = unmount(path);
}
if (err == B_OK) {
PRINT(("deleting '%s'\n", path));
err = rmdir(path);
}
if (err != B_OK) {
PRINT(("error %s\n", strerror(err)));
BString text;
text << "Could not unmount disk";
(new BAlert("", text.String(), "OK", NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT))->Go(0);
}
} else if (params.result != B_OK) {
BString text;
text << "Could not unmount disk " << partition->VolumeName() <<
". An item on the disk is busy.";
(new BAlert("", text.String(), "OK", NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT))->Go(0);
}
}
bool
AutoMounter::QuitRequested()
{
if (!BootedInSafeMode()) {
// don't write out settings in safe mode - this would overwrite the
// normal, non-safe mode settings
WriteSettings();
}
return true;
}
void
AutoMounter::ReadSettings()
{
BPath directoryPath;
if (FSFindTrackerSettingsDir(&directoryPath) != B_OK)
return;
BPath path(directoryPath);
path.Append(kAutoMounterSettings);
fPrefsFile.SetTo(path.Path(), O_RDWR);
if (fPrefsFile.InitCheck() != B_OK) {
// no prefs file yet, create a new one
BDirectory dir(directoryPath.Path());
dir.CreateFile(kAutoMounterSettings, &fPrefsFile);
return;
}
ssize_t settingsSize = (ssize_t)fPrefsFile.Seek(0, SEEK_END);
if (settingsSize == 0)
return;
ASSERT(settingsSize != 0);
char *buffer = new char[settingsSize];
fPrefsFile.Seek(0, 0);
if (fPrefsFile.Read(buffer, (size_t)settingsSize) != settingsSize) {
PRINT(("error reading automounter settings\n"));
delete [] buffer;
return;
}
BMessage message('stng');
status_t result = message.Unflatten(buffer);
if (result != B_OK) {
PRINT(("error %s unflattening settings, size %d\n", strerror(result),
settingsSize));
delete [] buffer;
return;
}
delete [] buffer;
// PRINT(("done unflattening settings\n"));
SetParams(&message, true);
}
void
AutoMounter::WriteSettings()
{
if (fPrefsFile.InitCheck() != B_OK)
return;
BMessage message('stng');
GetSettings(&message);
ssize_t settingsSize = message.FlattenedSize();
char *buffer = new char[settingsSize];
status_t result = message.Flatten(buffer, settingsSize);
fPrefsFile.Seek(0, 0);
result = fPrefsFile.Write(buffer, (size_t)settingsSize);
if (result != settingsSize)
PRINT(("error writing settings, %s\n", strerror(result)));
delete [] buffer;
}
void
AutoMounter::GetSettings(BMessage *message)
{
message->AddBool("checkRemovableOnly", fScanParams.removableOrUnknownOnly);
message->AddBool("checkCDs", fScanParams.checkCDROMs);
message->AddBool("checkFloppies", fScanParams.checkFloppies);
message->AddBool("checkOtherRemovables", fScanParams.checkOtherRemovable);
message->AddBool("autoMountRemovableOnly", fAutomountParams.mountRemovableDisksOnly);
message->AddBool("autoMountAll", fAutomountParams.mountAllFS);
message->AddBool("autoMountAllBFS", fAutomountParams.mountBFS);
message->AddBool("autoMountAllHFS", fAutomountParams.mountHFS);
message->AddBool("initialMountAll", fInitialMountAll);
message->AddBool("initialMountAllBFS", fInitialMountAllBFS);
message->AddBool("initialMountRestore", fInitialMountRestore);
message->AddBool("initialMountAllHFS", fInitialMountAllHFS);
message->AddBool("suspended", fSuspended);
// Save mounted volumes so we can optionally mount them on next
// startup
BVolumeRoster volumeRoster;
BVolume volume;
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
fs_info info;
if (fs_stat_dev(volume.Device(), &info) == 0
&& info.flags & (B_FS_IS_REMOVABLE | B_FS_IS_PERSISTENT))
message->AddString(info.device_name, info.volume_name);
}
}
void
AutoMounter::SetParams(BMessage *message, bool rescan)
{
bool result;
if (message->FindBool("checkRemovableOnly", &result) == B_OK)
fScanParams.removableOrUnknownOnly = result;
if (message->FindBool("checkCDs", &result) == B_OK)
fScanParams.checkCDROMs = result;
if (message->FindBool("checkFloppies", &result) == B_OK)
fScanParams.checkFloppies = result;
if (message->FindBool("checkOtherRemovables", &result) == B_OK)
fScanParams.checkOtherRemovable = result;
if (message->FindBool("autoMountRemovableOnly", &result) == B_OK)
fAutomountParams.mountRemovableDisksOnly = result;
if (message->FindBool("autoMountAll", &result) == B_OK)
fAutomountParams.mountAllFS = result;
if (message->FindBool("autoMountAllBFS", &result) == B_OK)
fAutomountParams.mountBFS = result;
if (message->FindBool("autoMountAllHFS", &result) == B_OK)
fAutomountParams.mountHFS = result;
if (message->FindBool("initialMountAll", &result) == B_OK)
fInitialMountAll = result;
if (message->FindBool("initialMountAllBFS", &result) == B_OK)
fInitialMountAllBFS = result;
if (message->FindBool("initialMountRestore", &result) == B_OK) {
fInitialMountRestore = result;
if (fInitialMountRestore)
gSettingsMessage = *message;
}
if (message->FindBool("initialMountAllHFS", &result) == B_OK)
fInitialMountAllHFS = result;
if (message->FindBool("suspended", &result) == B_OK)
SuspendResume(result);
if (rescan)
CheckVolumesNow();
}
#endif // !__HAIKU__