diff --git a/headers/private/mount/MountServer.h b/headers/private/mount/MountServer.h new file mode 100644 index 0000000000..83b76a80bc --- /dev/null +++ b/headers/private/mount/MountServer.h @@ -0,0 +1,20 @@ +/* + * Copyright 2007-2009, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _MOUNT_SERVER_H +#define _MOUNT_SERVER_H + +#include + + +const uint32 kMountVolume = 'mntv'; +const uint32 kMountAllNow = 'mntn'; +const uint32 kSetAutomounterParams = 'pmst'; +const uint32 kVolumeMounted = 'vmtd'; +const uint32 kUnmountVolume = 'umnt'; + +const char* kMountServerSignature = "application/x-vnd.Haiku-mount_server"; + + +#endif // _MOUNT_SERVER_H diff --git a/src/servers/Jamfile b/src/servers/Jamfile index 5f1b67f86a..1d0559456c 100644 --- a/src/servers/Jamfile +++ b/src/servers/Jamfile @@ -9,6 +9,7 @@ SubInclude HAIKU_TOP src servers mail ; SubInclude HAIKU_TOP src servers media ; SubInclude HAIKU_TOP src servers media_addon ; SubInclude HAIKU_TOP src servers midi ; +SubInclude HAIKU_TOP src servers mount ; SubInclude HAIKU_TOP src servers net ; SubInclude HAIKU_TOP src servers power ; SubInclude HAIKU_TOP src servers print ; diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h index 7912a097f0..be51e2b540 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h @@ -34,6 +34,16 @@ typedef PixelFormat::agg_buffer agg_buffer; d[2] = (((((r) - _p.data8[2]) * (a)) + (_p.data8[2] << 8)) >> 8); \ d[3] = 255; \ } +//#define BLEND(d, r, g, b, a) \ +//{ \ +// pixel32 _p; \ +// _p.data32 = *(uint32*)d; \ +// d[0] = (((((b) - _p.data8[0]) * (a)) + ((_p.data8[0] << 8) | _p.data8[0])) / 255); \ +// d[1] = (((((g) - _p.data8[1]) * (a)) + ((_p.data8[1] << 8) | _p.data8[1])) / 255); \ +// d[2] = (((((r) - _p.data8[2]) * (a)) + ((_p.data8[2] << 8) | _p.data8[2])) / 255); \ +// d[3] = 255; \ +//} + #define BLEND_SUBPIX(d, r, g, b, a1, a2, a3) \ { \ @@ -44,6 +54,15 @@ typedef PixelFormat::agg_buffer agg_buffer; d[2] = (((((r) - _p.data8[2]) * (a3)) + (_p.data8[2] << 8)) >> 8); \ d[3] = 255; \ } +//#define BLEND_SUBPIX(d, r, g, b, a1, a2, a3) \ +//{ \ +// pixel32 _p; \ +// _p.data32 = *(uint32*)d; \ +// d[0] = (((((b) - _p.data8[0]) * (a1)) + ((_p.data8[0] << 8) | _p.data8[0])) / 255); \ +// d[1] = (((((g) - _p.data8[1]) * (a2)) + ((_p.data8[1] << 8) | _p.data8[1])) / 255); \ +// d[2] = (((((r) - _p.data8[2]) * (a3)) + ((_p.data8[2] << 8) | _p.data8[2])) / 255); \ +// d[3] = 255; \ +//} // BLEND_FROM // @@ -59,6 +78,13 @@ typedef PixelFormat::agg_buffer agg_buffer; d[2] = (((((r2) - (r1)) * (a)) + ((r1) << 8)) >> 8); \ d[3] = 255; \ } +//#define BLEND_FROM(d, r1, g1, b1, r2, g2, b2, a) \ +//{ \ +// d[0] = (((((b2) - (b1)) * (a)) + ((b1) << 8) | b1) / 255); \ +// d[1] = (((((g2) - (g1)) * (a)) + ((g1) << 8) | g1) / 255); \ +// d[2] = (((((r2) - (r1)) * (a)) + ((r1) << 8) | r1) / 255); \ +// d[3] = 255; \ +//} #define BLEND_FROM_SUBPIX(d, r1, g1, b1, r2, g2, b2, a1, a2, a3) \ { \ @@ -67,6 +93,13 @@ typedef PixelFormat::agg_buffer agg_buffer; d[2] = (((((r2) - (r1)) * (a3)) + ((r1) << 8)) >> 8); \ d[3] = 255; \ } +//#define BLEND_FROM_SUBPIX(d, r1, g1, b1, r2, g2, b2, a1, a2, a3) \ +//{ \ +// d[0] = (((((b2) - (b1)) * (a1)) + ((b1) << 8) | b1) / 255); \ +// d[1] = (((((g2) - (g1)) * (a2)) + ((g1) << 8) | g1) / 255); \ +// d[2] = (((((r2) - (r1)) * (a3)) + ((r1) << 8) | r1) / 255); \ +// d[3] = 255; \ +//} // BLEND16 // @@ -83,15 +116,33 @@ typedef PixelFormat::agg_buffer agg_buffer; d[2] = (((((r) - _p.data8[2]) * (a)) + (_p.data8[2] << 16)) >> 16); \ d[3] = 255; \ } +//#define BLEND16(d, r, g, b, a) \ +//{ \ +// pixel32 _p; \ +// _p.data32 = *(uint32*)d; \ +// d[0] = (((((b) - _p.data8[0]) * (a)) + (_p.data8[0] << 16)) / 65025); \ +// d[1] = (((((g) - _p.data8[1]) * (a)) + (_p.data8[1] << 16)) / 65025); \ +// d[2] = (((((r) - _p.data8[2]) * (a)) + (_p.data8[2] << 16)) / 65025); \ +// d[3] = 255; \ +//} // BLEND16_SUBPIX +//#define BLEND16_SUBPIX(d, r, g, b, a1, a2, a3) \ +//{ \ +// pixel32 _p; \ +// _p.data32 = *(uint32*)d; \ +// d[0] = (((((b) - _p.data8[0]) * (a1)) + (_p.data8[0] << 16)) >> 16); \ +// d[1] = (((((g) - _p.data8[1]) * (a2)) + (_p.data8[1] << 16)) >> 16); \ +// d[2] = (((((r) - _p.data8[2]) * (a3)) + (_p.data8[2] << 16)) >> 16); \ +// d[3] = 255; \ +//} #define BLEND16_SUBPIX(d, r, g, b, a1, a2, a3) \ { \ pixel32 _p; \ _p.data32 = *(uint32*)d; \ - d[0] = (((((b) - _p.data8[0]) * (a1)) + (_p.data8[0] << 16)) >> 16); \ - d[1] = (((((g) - _p.data8[1]) * (a2)) + (_p.data8[1] << 16)) >> 16); \ - d[2] = (((((r) - _p.data8[2]) * (a3)) + (_p.data8[2] << 16)) >> 16); \ + d[0] = (((((b) - _p.data8[0]) * (a1)) + (_p.data8[0] * 65025)) / 65025); \ + d[1] = (((((g) - _p.data8[1]) * (a2)) + (_p.data8[1] * 65025)) / 65025); \ + d[2] = (((((r) - _p.data8[2]) * (a3)) + (_p.data8[2] * 65025)) / 65025); \ d[3] = 255; \ } diff --git a/src/servers/mount/AutoMounter.cpp b/src/servers/mount/AutoMounter.cpp new file mode 100644 index 0000000000..cf680545f1 --- /dev/null +++ b/src/servers/mount/AutoMounter.cpp @@ -0,0 +1,908 @@ +/* + * Copyright 2007-2009, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include "AutoMounter.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "AutoMounterSettings.h" +#include "MountServer.h" + + +static const char* kMountServerSettings = "mount_server"; +static const uint32 kMsgInitialScan = 'insc'; +static const char* kMountFlagsKeyExtension = " mount flags"; + + +bool +BootedInSafeMode() +{ + const char *safeMode = getenv("SAFEMODE"); + return (safeMode && strcmp(safeMode, "yes") == 0); +} + + +// #pragma mark - + + +AutoMounter::AutoMounter() + : + BApplication(kMountServerSignature), + fNormalMode(kRestorePreviousVolumes), + fRemovableMode(kAllVolumes), + fEjectWhenUnmounting(true) +{ + set_thread_priority(Thread(), B_LOW_PRIORITY); + + 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); +} + + +AutoMounter::~AutoMounter() +{ + BDiskDeviceRoster().StopWatching(this); +} + + +void +AutoMounter::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_EXECUTE_PROPERTY: + { + int32 index; + BMessage specifier; + int32 what; + const char* property = NULL; + if (message->GetCurrentSpecifier(&index, &specifier, &what, + &property) < B_OK + || !_ScriptReceived(message, index, &specifier, what, + property)) { + BApplication::MessageReceived(message); + } + break; + } + + case kMsgInitialScan: + _MountVolumes(fNormalMode, fRemovableMode, true); + 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); + break; + } + + case kMountAllNow: + _MountVolumes(kAllVolumes, kAllVolumes); + break; + + case B_DEVICE_UPDATE: + int32 event; + if (message->FindInt32("event", &event) != B_OK + || (event != B_DEVICE_MEDIA_CHANGED + && event != B_DEVICE_ADDED)) + break; + + partition_id deviceID; + if (message->FindInt32("id", &deviceID) != B_OK) + break; + + _MountVolumes(kNoVolumes, fRemovableMode, false, deviceID); + 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; +} + + +// #pragma mark - scripting + + +const uint32 kApplication = 0; + +static property_info sPropertyInfo[] = { + { + "InitialScan", + {B_EXECUTE_PROPERTY}, + {B_DIRECT_SPECIFIER}, + NULL, kApplication, + {B_STRING_TYPE}, + {}, + {} + }, + {} +}; + + +BHandler* +AutoMounter::ResolveSpecifier(BMessage* message, int32 index, + BMessage* specifier, int32 what, const char* property) +{ + BPropertyInfo propInfo(sPropertyInfo); + + uint32 data; + if (propInfo.FindMatch(message, 0, specifier, what, property, &data) >= 0) { + if (data == kApplication) + return this; + + BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); + reply.AddInt32("error", B_ERROR); + reply.AddString("message", "Unkown specifier."); + message->SendReply(&reply); + return NULL; + } + + return BApplication::ResolveSpecifier(message, index, specifier, what, + property); +} + + +status_t +AutoMounter::GetSupportedSuites(BMessage* data) +{ + if (data == NULL) + return B_BAD_VALUE; + + status_t status = data->AddString("suites", + "suite/vnd.Haiku-mount_server"); + if (status != B_OK) + return status; + + BPropertyInfo propertyInfo(sPropertyInfo); + status = data->AddFlat("messages", &propertyInfo); + if (status != B_OK) + return status; + + return BApplication::GetSupportedSuites(data); +} + + +bool +AutoMounter::_ScriptReceived(BMessage *message, int32 index, + BMessage *specifier, int32 what, const char *property) +{ + BMessage reply(B_REPLY); + status_t err = B_BAD_SCRIPT_SYNTAX; + + switch (message->what) { + case B_EXECUTE_PROPERTY: + if (strcmp("InitialScan", property) == 0) { +printf("performing initial scan.\n"); + _MountVolumes(fNormalMode, fRemovableMode, true); + err = reply.AddString("result", "Previous volumes mounted."); + } + break; + } + + if (err == B_BAD_SCRIPT_SYNTAX) + return false; + + if (err != B_OK) { + reply.what = B_MESSAGE_NOT_UNDERSTOOD; + reply.AddString("message", strerror(err)); + } + reply.AddInt32("error", err); + message->SendReply(&reply); + return true; +} + + +// #pragma mark - + + +static bool +suggest_mount_flags(const BPartition* partition, uint32* _flags) +{ + 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. + BString string; + string << "Mounting volume "; + if (partition->ContentName() != NULL) + string << "'" << partition->ContentName() << "'\n\n"; + else + string << "\n\n"; + // 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::_MountVolumes(mount_mode normal, mount_mode removable, + bool initialRescan, partition_id deviceID) +{ + if (normal == kNoVolumes && removable == kNoVolumes) + return; + + class InitialMountVisitor : public BDiskDeviceVisitor { + public: + InitialMountVisitor(mount_mode normalMode, mount_mode removableMode, + bool initialRescan, BMessage& previous, + partition_id deviceID) + : + fNormalMode(normalMode), + fRemovableMode(removableMode), + fInitialRescan(initialRescan), + fPrevious(previous), + fOnlyOnDeviceID(deviceID) + { + } + + virtual + ~InitialMountVisitor() + { + } + + virtual bool + Visit(BDiskDevice* device) + { + return Visit(device, 0); + } + + virtual bool + Visit(BPartition* partition, int32 level) + { + if (fOnlyOnDeviceID >= 0) { + // only mount partitions on the given device id + // or if the partition ID is already matched + BPartition* device = partition; + while (device->Parent() != NULL) { + if (device->ID() == fOnlyOnDeviceID) { + // we are happy + break; + } + device = device->Parent(); + } + if (device->ID() != fOnlyOnDeviceID) + return false; + } + + mount_mode mode = !fInitialRescan + && 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; + if (!fInitialRescan) { + // Ask the user about mount flags if this is not the + // initial scan. + if (!suggest_mount_flags(partition, &mountFlags)) + return false; + } else { + 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(¬e); + } + return false; + } + + private: + mount_mode fNormalMode; + mount_mode fRemovableMode; + bool fInitialRescan; + BMessage& fPrevious; + partition_id fOnlyOnDeviceID; + } visitor(normal, removable, initialRescan, fSettings, deviceID); + + BDiskDeviceList devices; + status_t status = devices.Fetch(); + if (status == B_OK) + devices.VisitEachPartition(&visitor); +} + + +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 (!suggest_mount_flags(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"; + text << 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"; + text << 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 (fEjectWhenUnmounting && 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 (find_directory(B_USER_SETTINGS_DIRECTORY, &directoryPath, true) + != B_OK) { + return; + } + + BPath path(directoryPath); + path.Append(kMountServerSettings); + 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(kMountServerSettings, &fPrefsFile); + return; + } + + ssize_t settingsSize = (ssize_t)fPrefsFile.Seek(0, SEEK_END); + if (settingsSize == 0) + return; + + ASSERT(settingsSize != 0); + char *buffer = new(std::nothrow) char[settingsSize]; + if (buffer == NULL) { + PRINT(("error writing automounter settings, out of memory\n")); + return; + } + + 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 automounter 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(std::nothrow) char[settingsSize]; + if (buffer == NULL) { + PRINT(("error writing automounter settings, out of memory\n")); + return; + } + + status_t result = message.Flatten(buffer, settingsSize); + + fPrefsFile.Seek(0, SEEK_SET); + result = fPrefsFile.Write(buffer, (size_t)settingsSize); + if (result != settingsSize) + PRINT(("error writing automounter 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); + + // eject settings + bool eject; + if (message->FindBool("ejectWhenUnmounting", &eject) == B_OK) + fEjectWhenUnmounting = eject; +} + + +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); + + message->AddBool("ejectWhenUnmounting", fEjectWhenUnmounting); + + // 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); + } + } +} + + +// #pragma mark - + + +int +main(int argc, char* argv[]) +{ + AutoMounter app; + app.Run(); + return 0; +} + + diff --git a/src/servers/mount/AutoMounter.h b/src/servers/mount/AutoMounter.h new file mode 100644 index 0000000000..edc90aeb9f --- /dev/null +++ b/src/servers/mount/AutoMounter.h @@ -0,0 +1,74 @@ +/* + * Copyright 2007-2009, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef AUTO_MOUNTER_H +#define AUTO_MOUNTER_H + +#include +#include +#include +#include + +class BPartition; +class BPath; + + +class AutoMounter : public BApplication { +public: + AutoMounter(); + virtual ~AutoMounter(); + + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + virtual BHandler* ResolveSpecifier(BMessage* message, + int32 index, BMessage* specifier, + int32 what, const char* property); + virtual status_t GetSupportedSuites(BMessage* data); + +private: + enum mount_mode { + kNoVolumes, + kOnlyBFSVolumes, + kAllVolumes, + kRestorePreviousVolumes + }; + + void _GetSettings(BMessage* message); + bool _ScriptReceived(BMessage* msg, int32 index, + BMessage* specifier, int32 form, + const char* property); + + void _MountVolumes(mount_mode normal, + mount_mode removable, + bool initialRescan = false, + partition_id deviceID = -1); + void _MountVolume(const BMessage* message); + bool _SuggestForceUnmount(const char* name, + status_t error); + void _ReportUnmountError(const char* name, + status_t error); + void _UnmountAndEjectVolume(BPartition* partition, + BPath& mountPoint, const char* name); + void _UnmountAndEjectVolume(BMessage* message); + + void _FromMode(mount_mode mode, bool& all, + bool& bfs, bool& restore); + mount_mode _ToMode(bool all, bool bfs, + bool restore = false); + + void _UpdateSettingsFromMessage(BMessage* message); + void _ReadSettings(); + void _WriteSettings(); + +private: + mount_mode fNormalMode; + mount_mode fRemovableMode; + bool fEjectWhenUnmounting; + + BFile fPrefsFile; + BMessage fSettings; +}; + +#endif // AUTO_MOUNTER_H diff --git a/src/servers/mount/Jamfile b/src/servers/mount/Jamfile new file mode 100644 index 0000000000..1d432365f3 --- /dev/null +++ b/src/servers/mount/Jamfile @@ -0,0 +1,14 @@ +SubDir HAIKU_TOP src servers mount ; + +UsePrivateHeaders mount shared storage ; + +Server mount_server + : + AutoMounter.cpp +# AutoMounterSettings.cpp + : + libbe.so + $(TARGET_LIBSTDC++) + : + mount_server.rdef +; diff --git a/src/servers/mount/mount_server.rdef b/src/servers/mount/mount_server.rdef new file mode 100644 index 0000000000..b69a4aacf5 --- /dev/null +++ b/src/servers/mount/mount_server.rdef @@ -0,0 +1,57 @@ +/* + * mount_server.rdef + */ + +resource app_signature "application/x-vnd.Haiku-mount_server"; + +resource app_flags B_SINGLE_LAUNCH | B_BACKGROUND_APP; + +resource app_version { + major = 1, + middle = 0, + minor = 0, + + variety = B_APPV_ALPHA, + internal = 0, + + short_info = "mount_server", + long_info = "mount_server ©2009 Haiku" +}; + +resource vector_icon array { + $"6E6369661603010000020016023CC7EE389BC0BA16573E39B04A25E4468B7100" + $"FFFFD3020016023C5A743B25DEBC0A703D0D214B89B149B4E00047FF9E020016" + $"02396BD8394C21BC8C213CA4A24C06D7486315003DFF8102001602BC592FBB29" + $"A73C0CE4BD0B7C4892C04B9966007DFFD40200060238DBB4399733BC4A333BA5" + $"424866664A1B3D00596756FFEBB2B203A7FF0003FF0000040180020006023F20" + $"000000000000003E3000C000000000005BF8F8FFFF5D5BBB020006023A6FFE3A" + $"7CC1BD2F743D1F1D4AACC540BDFEA0372874E4151EC8020006023EAF513C0E74" + $"BAAEAE3D808045F168C49DEB63FFFFFFE8878CE4020016023E72000000000000" + $"003D8000BF800041C00000C5C8FF020006023C67EC3DCD85BDA9DD3C515E49AD" + $"B5C589F5A7837BBDEFD4D5F5020006023F30000000000000003DC000C0000042" + $"000000231587FFDBE2E7020006023E12000000000000003BD00046B000420000" + $"9AF8FCC6FFFBF8A302000602400000000000000000400000C000004200000082" + $"82FDFF124DA702000602400000000000000000400000C0000042000000FFFFFF" + $"FFE8E2E202030605B812A5BE03E13DE784B802104A6BCF479BB000F1F1F136D9" + $"DDF48A9996B9B4B8BEDBFFF4F4F4038B86B803837FA802011602B7AF7C3CA0E1" + $"BF1E0BBA2F7E4A3050487E9E0046FFDA170605E202405F46C949C4CE6045604A" + $"60415C400A062240224D405D5A435A363D2C0A062240404DC73BBE4F5A363D2C" + $"B67B3B0607EA283D2C5A36C73BBE4F483E4E3E423E403A42B67B3B0607EA2822" + $"40404DC73BBE4F483E4E3E423E403A42B67B3B0A04404D405DC746C3C7C746BE" + $"450A04C746C3C75A435A36C746BE450A042240224D405D404D08022747B814C3" + $"8908022647294802044626C76B26BCDC262E302EB72C2EBB93463ABCDC3AC76B" + $"3A5E305EBB935EB72C0608BFFE4626C76B26BCDC262E302EB72C2EBAB7BC9DBC" + $"A0BA2BBBE7BC9DBCA04A2E4C30BF67BD31BF67BD31C045BD4D463AC1303AC76B" + $"3A5E305EBB935EB72C0604BE4A2EBC9DBCA0BC9DBCA0BD74BCE0BF67BD31BE64" + $"BD11BF67BD314C300204462BC3E62BC0612BBEF32EBEF3B7EBBEF3B93C4631C0" + $"6131C3E6314E2E4EB93C4EB7EB0A0621302133393C4B2E4B2B33250A044B2B4B" + $"2E393B39380A04213321303938393B0A0627322D2E2E2E3731373232360A042A" + $"322E2F30302C330806353A35373C322D2D273127340A0427322735323932360A" + $"04BC03B57DB920B73BBE01B8C7C131B6C9080435262D2BBEDAB920462A180A08" + $"0100000A0001011001178400040A010104000A150103000A020105000A030106" + $"000A040107000A0501081001178520040A0701093024B39901178200040A0601" + $"093020B2E601178200040A00010A1001178400040A13010C000A12010B000A14" + $"010D1001178100040A09010E000A0A010F000A0B0110000A0C0111000A100112" + $"000A0E01131001178100040A110114000A0D01161001178100040A0F0115000A" + $"00010E100117820004" +};