diff --git a/headers/private/kernel/disk_device_manager/KDiskDevice.h b/headers/private/kernel/disk_device_manager/KDiskDevice.h index 80a72f43fe..9e4849a56a 100644 --- a/headers/private/kernel/disk_device_manager/KDiskDevice.h +++ b/headers/private/kernel/disk_device_manager/KDiskDevice.h @@ -36,8 +36,10 @@ public: // manager lock owners can be sure, that it won't change. bool ReadLock(); void ReadUnlock(); + bool IsReadLocked(bool orWriteLocked = true); bool WriteLock(); void WriteUnlock(); + bool IsWriteLocked(); virtual void SetID(partition_id id); diff --git a/headers/private/kernel/disk_device_manager/KDiskDeviceManager.h b/headers/private/kernel/disk_device_manager/KDiskDeviceManager.h index 7e3117f2e3..125b2f75b5 100644 --- a/headers/private/kernel/disk_device_manager/KDiskDeviceManager.h +++ b/headers/private/kernel/disk_device_manager/KDiskDeviceManager.h @@ -53,7 +53,7 @@ public: KFileDiskDevice *RegisterFileDevice(const char *filePath, bool noShadow = true); - status_t CreateFileDevice(const char *filePath, int32 *device = 0); + status_t CreateFileDevice(const char *filePath, partition_id *device = 0); status_t DeleteFileDevice(const char *filePath); // manager must be locked @@ -62,6 +62,7 @@ public: bool PartitionAdded(KPartition *partition); // implementation internal bool PartitionRemoved(KPartition *partition); // + bool DeletePartition(KPartition *partition); // // Jobs @@ -106,9 +107,10 @@ private: status_t _ScanPartition(KPartition *partition); BLocker fLock; - List fDevices; // TODO: Optimize! - List fPartitions; // - List fDiskSystems; // + List fDevices; // TODO: Optimize! + List fPartitions; // + List fDiskSystems; // + List fObsoletePartitions; // static KDiskDeviceManager *fDefaultManager; }; diff --git a/headers/private/kernel/disk_device_manager/KFileDiskDevice.h b/headers/private/kernel/disk_device_manager/KFileDiskDevice.h index b53ebe3d6e..12f23a4822 100644 --- a/headers/private/kernel/disk_device_manager/KFileDiskDevice.h +++ b/headers/private/kernel/disk_device_manager/KFileDiskDevice.h @@ -26,9 +26,9 @@ public: // virtual void Dump(bool deep = true, int32 level = 0); -protected: - virtual status_t GetMediaStatus(status_t *mediaStatus); - virtual status_t GetGeometry(device_geometry *geometry); +private: + static status_t _RegisterDevice(const char *file, const char *device); + static status_t _UnregisterDevice(const char *device); private: char *fFilePath; diff --git a/headers/private/kernel/disk_device_manager/KPartition.h b/headers/private/kernel/disk_device_manager/KPartition.h index 4a3e265551..2502c79df8 100644 --- a/headers/private/kernel/disk_device_manager/KPartition.h +++ b/headers/private/kernel/disk_device_manager/KPartition.h @@ -36,6 +36,13 @@ public: void Unregister(); int32 CountReferences() const; + void MarkObsolete(); + // called by the manager only + bool IsObsolete() const; + + virtual bool PrepareForRemoval(); + virtual bool PrepareForDeletion(); + status_t Open(int flags, int *fd); virtual status_t PublishDevice(); virtual status_t UnpublishDevice(); @@ -122,7 +129,9 @@ public: status_t AddChild(KPartition *partition, int32 index = -1); status_t CreateChild(partition_id id, int32 index, KPartition **child = NULL); - KPartition *RemoveChild(int32 index); + bool RemoveChild(int32 index); + bool RemoveChild(KPartition *child); + bool RemoveAllChildren(); KPartition *ChildAt(int32 index) const; int32 CountChildren() const; @@ -137,7 +146,6 @@ public: void SetDiskSystem(KDiskSystem *diskSystem); KDiskSystem *DiskSystem() const; - void SetParentDiskSystem(KDiskSystem *diskSystem); KDiskSystem *ParentDiskSystem() const; // When setting a disk system, it must already be loaded. // The partition will load it too, hence it won't be unloaded before @@ -161,8 +169,8 @@ protected: KDiskDevice *fDevice; KPartition *fParent; KDiskSystem *fDiskSystem; - KDiskSystem *fParentDiskSystem; int32 fReferenceCount; + bool fObsolete; static int32 fNextID; }; diff --git a/src/kernel/core/disk_device_manager/KDiskDevice.cpp b/src/kernel/core/disk_device_manager/KDiskDevice.cpp index fe861db725..35d5ba948c 100644 --- a/src/kernel/core/disk_device_manager/KDiskDevice.cpp +++ b/src/kernel/core/disk_device_manager/KDiskDevice.cpp @@ -111,6 +111,13 @@ KDiskDevice::ReadUnlock() fLocker.ReadUnlock(); } +// IsReadLocked +bool +KDiskDevice::IsReadLocked(bool orWriteLocked) +{ + return fLocker.IsReadLocked(orWriteLocked); +} + // WriteLock bool KDiskDevice::WriteLock() @@ -125,6 +132,13 @@ KDiskDevice::WriteUnlock() fLocker.WriteUnlock(); } +// IsWriteLocked +bool +KDiskDevice::IsWriteLocked() +{ + return fLocker.IsWriteLocked(); +} + // SetID void KDiskDevice::SetID(partition_id id) diff --git a/src/kernel/core/disk_device_manager/KDiskDeviceManager.cpp b/src/kernel/core/disk_device_manager/KDiskDeviceManager.cpp index 06f15656dd..67ab4ac05f 100644 --- a/src/kernel/core/disk_device_manager/KDiskDeviceManager.cpp +++ b/src/kernel/core/disk_device_manager/KDiskDeviceManager.cpp @@ -35,7 +35,8 @@ KDiskDeviceManager::KDiskDeviceManager() : fLock("disk device manager"), fDevices(20), fPartitions(100), - fDiskSystems(20) + fDiskSystems(20), + fObsoletePartitions(20) { // add partitioning systems if (void *list = open_module_list(kPartitioningSystemPrefix)) { @@ -66,9 +67,35 @@ DBG(OUT("number of disk systems: %ld\n", CountDiskSystems())); // destructor KDiskDeviceManager::~KDiskDeviceManager() { - // unpublish all partitions (only needed for testing) - for (int32 i = 0; KPartition *partition = fPartitions.ItemAt(i); i++) - partition->UnpublishDevice(); + // remove all devices + int32 count = CountDevices(); + for (int32 i = count - 1; i >= 0; i--) { + if (KDiskDevice *device = DeviceAt(i)) { + PartitionRegistrar _(device); + _RemoveDevice(device); + } + } + // some sanity checks + if (fPartitions.CountItems() > 0) { + DBG(OUT("WARNING: There are still %ld unremoved partitions!\n", + fPartitions.CountItems())); + } + if (fObsoletePartitions.CountItems() > 0) { + DBG(OUT("WARNING: There are still %ld obsolete partitions!\n", + fObsoletePartitions.CountItems())); + } + // remove all disk systems + count = CountDiskSystems(); + for (int32 i = count - 1; i >= 0; i--) { + if (KDiskSystem *diskSystem = DiskSystemAt(i)) { + fDiskSystems.RemoveItem(i); + if (diskSystem->IsLoaded()) { + DBG(OUT("WARNING: Disk system `%s' (%ld) is still loaded!\n", + diskSystem->Name(), diskSystem->ID())); + } else + delete diskSystem; + } + } } // InitCheck @@ -280,7 +307,8 @@ KDiskDeviceManager::RegisterFileDevice(const char *filePath, bool noShadow) // CreateFileDevice status_t -KDiskDeviceManager::CreateFileDevice(const char *filePath, int32 *deviceID) +KDiskDeviceManager::CreateFileDevice(const char *filePath, + partition_id *deviceID) { if (!filePath) return B_BAD_VALUE; @@ -296,7 +324,7 @@ KDiskDeviceManager::CreateFileDevice(const char *filePath, int32 *deviceID) return B_NO_MEMORY; // initialize and add the device error = device->SetTo(filePath); - if (error == B_OK && !fDevices.AddItem(device)) + if (error == B_OK && !_AddDevice(device)) error = B_NO_MEMORY; // set result / cleanup und failure if (error != B_OK) { @@ -319,7 +347,13 @@ KDiskDeviceManager::CreateFileDevice(const char *filePath, int32 *deviceID) status_t KDiskDeviceManager::DeleteFileDevice(const char *filePath) { - // not implemented + if (KFileDiskDevice *device = RegisterFileDevice(filePath)) { + PartitionRegistrar _(device, true); + if (DeviceWriteLocker locker = device) { + if (_RemoveDevice(device)) + return B_OK; + } + } return B_ERROR; } @@ -352,7 +386,29 @@ KDiskDeviceManager::PartitionAdded(KPartition *partition) bool KDiskDeviceManager::PartitionRemoved(KPartition *partition) { - return (partition && fPartitions.RemoveItem(partition)); + if (partition && partition->PrepareForRemoval() + && fPartitions.RemoveItem(partition)) { + // If adding the partition to the obsolete list fails (due to lack + // of memory), we can't do anything about it. We will leak memory then. + fObsoletePartitions.AddItem(partition); + partition->MarkObsolete(); + return true; + } + return false; +} + +// DeletePartition +bool +KDiskDeviceManager::DeletePartition(KPartition *partition) +{ + if (partition && partition->IsObsolete() + && partition->CountReferences() == 0 + && partition->PrepareForDeletion() + && fObsoletePartitions.RemoveItem(partition)) { + delete partition; + return true; + } + return false; } // JobWithID @@ -551,7 +607,7 @@ KDiskDeviceManager::_AddDevice(KDiskDevice *device) bool KDiskDeviceManager::_RemoveDevice(KDiskDevice *device) { - return (device && PartitionRemoved(device) && fDevices.RemoveItem(device)); + return (device && fDevices.RemoveItem(device) && PartitionRemoved(device)); } // _Scan @@ -667,13 +723,9 @@ DBG(OUT(" returned: %f\n", priority)); DBG(OUT(" scanning with: %s\n", bestDiskSystem->Name())); error = bestDiskSystem->Scan(partition, bestCookie); if (error == B_OK) { -// TODO: Maybe better move setting the partition's and children's disk system -// in K{File,Partitioning}System::Scan()? partition->SetDiskSystem(bestDiskSystem); - for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++) { - child->SetParentDiskSystem(bestDiskSystem); + for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++) _ScanPartition(child); - } } else { // TODO: Handle the error. DBG(OUT(" scanning failed: %s\n", strerror(error))); diff --git a/src/kernel/core/disk_device_manager/KFileDiskDevice.cpp b/src/kernel/core/disk_device_manager/KFileDiskDevice.cpp index 5bff0267bf..801cccee48 100644 --- a/src/kernel/core/disk_device_manager/KFileDiskDevice.cpp +++ b/src/kernel/core/disk_device_manager/KFileDiskDevice.cpp @@ -10,6 +10,13 @@ #include #include +#include "virtualdrive.h" + +// debugging +//#define DBG(x) +#define DBG(x) x +#define OUT printf + static const char *kFileDevicesDir = "/dev/disk/virtual/files"; // constructor @@ -58,9 +65,6 @@ KFileDiskDevice::SetTo(const char *filePath, const char *devicePath) char tmpDevicePath[B_PATH_NAME_LENGTH]; if (!devicePath) { // no device path: we shall create a new device entry - // create the device entry - // TODO: Now we simply create a link. Replace that with the - // respective kernel magic! // make the file devices dir if (mkdir(kFileDevicesDir, 0777) != 0) { if (errno != B_FILE_EXISTS) @@ -73,8 +77,10 @@ KFileDiskDevice::SetTo(const char *filePath, const char *devicePath) // get the device path name sprintf(tmpDevicePath, "%s/%ld/raw", kFileDevicesDir, ID()); devicePath = tmpDevicePath; - if (symlink(filePath, devicePath) != 0) - return errno; + // register the file as virtual drive + status_t error = _RegisterDevice(filePath, devicePath); + if (error != B_OK) + return error; } status_t error = set_string(fFilePath, filePath); if (error != B_OK) @@ -86,6 +92,14 @@ KFileDiskDevice::SetTo(const char *filePath, const char *devicePath) void KFileDiskDevice::Unset() { + // remove the device and the directory it resides in + if (Path() && ID() >= 0) { + _UnregisterDevice(Path()); + char dirPath[B_PATH_NAME_LENGTH]; + sprintf(dirPath, "%s/%ld", kFileDevicesDir, ID()); + rmdir(dirPath); + } + // free file path free(fFilePath); fFilePath = NULL; } @@ -112,41 +126,76 @@ KFileDiskDevice::CreateShadowPartition() return NULL; } -// GetMediaStatus +// _RegisterDevice status_t -KFileDiskDevice::GetMediaStatus(status_t *mediaStatus) +KFileDiskDevice::_RegisterDevice(const char *file, const char *device) { - *mediaStatus = B_OK; - return B_OK; -} + // TODO: For now we use the virtualdrive driver to register a file + // as a device and then simply symlink the assigned device to the + // desired device location. Replace that with the + // respective kernel magic for the OBOS kernel! -// GetGeometry -status_t -KFileDiskDevice::GetGeometry(device_geometry *geometry) -{ - // get the file size - struct stat st; - if (stat(fFilePath, &st) != 0) + // open the virtualdrive control device + int fd = open(VIRTUAL_DRIVE_CONTROL_DEVICE, O_RDONLY); + if (fd < 0) { + DBG(OUT("Failed to open virtualdrive control device: %s\n", + strerror(errno))); return errno; - // default to 512 bytes block size - off_t size = st.st_size; - uint32 blockSize = 512; - // Optimally we have only 1 block per sector and only one head. - // Since we have only a uint32 for the cylinder count, this won't work - // for files > 2TB. So, we set the head count to the minimally possible - // value. - off_t blocks = size / blockSize; - uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX; - if (heads == 0) - heads = 1; - geometry->bytes_per_sector = blockSize; - geometry->sectors_per_track = 1; - geometry->cylinder_count = blocks / heads; - geometry->head_count = heads; - geometry->device_type = B_DISK; // TODO: Add a new constant. - geometry->removable = false; - geometry->read_only = false; - geometry->write_once = false; - return B_OK; + } + // set up the info + virtual_drive_info info; + strcpy(info.file_name, file); + info.use_geometry = false; + status_t error = B_OK; + if (ioctl(fd, VIRTUAL_DRIVE_REGISTER_FILE, &info) != 0) { + error = errno; + DBG(OUT("Failed to install file device: %s\n", strerror(error))); + } + // close the control device + close(fd); + // create a symlink + if (error == B_OK) { + if (symlink(info.device_name, device) != 0) { + DBG(OUT("Failed to create file device symlink: %s\n", + strerror(error))); + error = errno; + // unregister the file device + // open the device + int deviceFD = open(info.device_name, O_RDONLY); + if (deviceFD >= 0) { + ioctl(deviceFD, VIRTUAL_DRIVE_UNREGISTER_FILE); + close(deviceFD); + } + } + } + return error; +} + +// _UnregisterDevice +status_t +KFileDiskDevice::_UnregisterDevice(const char *_device) +{ + // read the symlink to get the path of the virtualdrive device + char device[B_PATH_NAME_LENGTH]; + ssize_t bytesRead = readlink(_device, device, sizeof(device) - 1); + if (bytesRead < 0) + return errno; + device[bytesRead] = '\0'; + // open the device + int fd = open(device, O_RDONLY); + if (fd < 0) + return errno; + // issue the ioctl + status_t error = B_OK; + if (ioctl(fd, VIRTUAL_DRIVE_UNREGISTER_FILE) != 0) + error = errno; + // close the control device + close(fd); + // remove the symlink + if (error == B_OK) { + if (remove(_device) < 0) + error = errno; + } + return error; } diff --git a/src/kernel/core/disk_device_manager/KPartition.cpp b/src/kernel/core/disk_device_manager/KPartition.cpp index d61c66cdb6..dbf30944b2 100644 --- a/src/kernel/core/disk_device_manager/KPartition.cpp +++ b/src/kernel/core/disk_device_manager/KPartition.cpp @@ -10,11 +10,6 @@ #include #include -// debugging -//#define DBG(x) -#define DBG(x) x -#define OUT printf - #include "KDiskDevice.h" #include "KDiskDeviceManager.h" #include "KDiskDeviceUtils.h" @@ -24,14 +19,19 @@ using namespace std; // constructor +// debugging +//#define DBG(x) +#define DBG(x) x +#define OUT printf + KPartition::KPartition(partition_id id) : fPartitionData(), fChildren(), fDevice(NULL), fParent(NULL), fDiskSystem(NULL), - fParentDiskSystem(NULL), - fReferenceCount(0) + fReferenceCount(0), + fObsolete(false) { fPartitionData.id = (id >= 0 ? id : _NextID()); fPartitionData.offset = 0; @@ -55,6 +55,7 @@ KPartition::KPartition(partition_id id) // destructor KPartition::~KPartition() { + SetDiskSystem(NULL); free(fPartitionData.name); free(fPartitionData.content_name); free(fPartitionData.type); @@ -74,8 +75,13 @@ KPartition::Register() void KPartition::Unregister() { - ManagerLocker locker(KDiskDeviceManager::Default()); + KDiskDeviceManager *manager = KDiskDeviceManager::Default(); + ManagerLocker locker(manager); fReferenceCount--; + if (IsObsolete() && fReferenceCount == 0) { + // let the manager delete object + manager->DeletePartition(this); + } } // CountReferences @@ -85,6 +91,36 @@ KPartition::CountReferences() const return fReferenceCount; } +// MarkObsolete +void +KPartition::MarkObsolete() +{ + fObsolete = true; +} + +// IsObsolete +bool +KPartition::IsObsolete() const +{ + return fObsolete; +} + +// PrepareForRemoval +bool +KPartition::PrepareForRemoval() +{ + bool result = RemoveAllChildren(); + UnpublishDevice(); + return result; +} + +// PrepareForDeletion +bool +KPartition::PrepareForDeletion() +{ + return true; +} + // Open status_t KPartition::Open(int flags, int *fd) @@ -566,26 +602,51 @@ KPartition::CreateChild(partition_id id, int32 index, KPartition **_child) } // RemoveChild -KPartition * +bool KPartition::RemoveChild(int32 index) { - KPartition *partition = NULL; - if (index >= 0 && index < fPartitionData.child_count) { - KDiskDeviceManager *manager = KDiskDeviceManager::Default(); - if (ManagerLocker locker = manager) { - partition = fChildren.ItemAt(index); - if (!partition || !manager->PartitionRemoved(partition)) - return NULL; - if (fChildren.RemoveItem(index)) { - _UpdateChildIndices(index + 1); - partition->SetIndex(-1); - fPartitionData.child_count--; - partition->SetParent(NULL); - partition->SetDevice(NULL); - } + if (index < 0 || index >= fPartitionData.child_count) + return false; + KDiskDeviceManager *manager = KDiskDeviceManager::Default(); + if (ManagerLocker locker = manager) { + KPartition *partition = fChildren.ItemAt(index); + PartitionRegistrar _(partition); + if (!partition || !manager->PartitionRemoved(partition) + || !fChildren.RemoveItem(index)) { + return false; } + _UpdateChildIndices(index + 1); + partition->SetIndex(-1); + fPartitionData.child_count--; + partition->SetParent(NULL); + partition->SetDevice(NULL); + return true; } - return partition; + return false; +} + +// RemoveChild +bool +KPartition::RemoveChild(KPartition *child) +{ + if (child) { + int32 index = fChildren.IndexOf(child); + if (index >= 0) + return RemoveChild(index); + } + return false; +} + +// RemoveAllChildren +bool +KPartition::RemoveAllChildren() +{ + int32 count = CountChildren(); + for (int32 i = count - 1; i >= 0; i--) { + if (!RemoveChild(i)) + return false; + } + return true; } // ChildAt @@ -655,26 +716,11 @@ KPartition::DiskSystem() const return fDiskSystem; } -// SetParentDiskSystem -void -KPartition::SetParentDiskSystem(KDiskSystem *diskSystem) -{ - // unload former disk system - if (fParentDiskSystem) { - fParentDiskSystem->Unload(); - fParentDiskSystem = NULL; - } - // set and load new one - fParentDiskSystem = diskSystem; - if (fParentDiskSystem) - fParentDiskSystem->Load(); -} - // ParentDiskSystem KDiskSystem * KPartition::ParentDiskSystem() const { - return fParentDiskSystem; + return (Parent() ? Parent()->DiskSystem() : NULL); } // SetCookie