diff --git a/headers/private/kernel/disk_device_manager/KDiskDevice.h b/headers/private/kernel/disk_device_manager/KDiskDevice.h index fba729ef0f..ddb5cfcf3a 100644 --- a/headers/private/kernel/disk_device_manager/KDiskDevice.h +++ b/headers/private/kernel/disk_device_manager/KDiskDevice.h @@ -45,6 +45,7 @@ public: virtual status_t PublishDevice(); virtual status_t UnpublishDevice(); + virtual status_t RepublishDevice(); void SetDeviceFlags(uint32 flags); // comprises the ones below uint32 DeviceFlags() const; @@ -63,6 +64,7 @@ public: // TODO: Remove this method or make it private. Once initialized the // path must not be changed. const char *Path() const; + virtual status_t GetFileName(char* buffer, size_t size) const; virtual status_t GetPath(KPath *path) const; // File descriptor: Set only from a kernel thread, valid only for diff --git a/headers/private/kernel/disk_device_manager/KPartition.h b/headers/private/kernel/disk_device_manager/KPartition.h index e9a8cd8492..c831b6a6e0 100644 --- a/headers/private/kernel/disk_device_manager/KPartition.h +++ b/headers/private/kernel/disk_device_manager/KPartition.h @@ -48,6 +48,7 @@ public: virtual status_t Open(int flags, int *fd); virtual status_t PublishDevice(); virtual status_t UnpublishDevice(); + virtual status_t RepublishDevice(); bool IsPublished() const; void SetBusy(bool busy); @@ -106,6 +107,7 @@ public: virtual void SetID(partition_id id); partition_id ID() const; + virtual status_t GetFileName(char* buffer, size_t size) const; virtual status_t GetPath(KPath *path) const; // no setter (see BDiskDevice) -- built on the fly @@ -207,7 +209,7 @@ protected: void FireContentCookieChanged(void *cookie); private: - void _UpdateChildIndices(int32 index); + void _UpdateChildIndices(int32 start, int32 end); static int32 _NextID(); protected: @@ -226,7 +228,7 @@ protected: uint32 fAlgorithmData; int32 fReferenceCount; bool fObsolete; - bool fPublished; + char *fPublishedName; static int32 fNextID; }; diff --git a/headers/private/kernel/fs/devfs.h b/headers/private/kernel/fs/devfs.h index f5aad502d4..d53c7dc9c4 100644 --- a/headers/private/kernel/fs/devfs.h +++ b/headers/private/kernel/fs/devfs.h @@ -19,8 +19,10 @@ extern "C" { status_t devfs_unpublish_file_device(const char *path); status_t devfs_publish_file_device(const char *path, const char *filePath); -status_t devfs_unpublish_partition(const char *path); -status_t devfs_publish_partition(const char *path, const partition_info *info); +status_t devfs_unpublish_partition(const char *devicePath, const char *name); +status_t devfs_publish_partition(const char *name, const partition_info *info); +status_t devfs_rename_partition(const char *devicePath, const char *oldName, + const char *newName); status_t devfs_unpublish_device(const char *path, bool disconnect); status_t devfs_publish_device(const char *path, device_hooks *calls); diff --git a/src/system/kernel/device_manager/devfs.cpp b/src/system/kernel/device_manager/devfs.cpp index 7d121bece3..d7891f726e 100644 --- a/src/system/kernel/device_manager/devfs.cpp +++ b/src/system/kernel/device_manager/devfs.cpp @@ -321,7 +321,8 @@ devfs_find_in_dir(struct devfs_vnode* dir, const char* path) static status_t -devfs_insert_in_dir(struct devfs_vnode* dir, struct devfs_vnode* vnode) +devfs_insert_in_dir(struct devfs_vnode* dir, struct devfs_vnode* vnode, + bool notify = true) { if (!S_ISDIR(dir->stream.type)) return B_BAD_VALUE; @@ -347,17 +348,19 @@ devfs_insert_in_dir(struct devfs_vnode* dir, struct devfs_vnode* vnode) vnode->parent = dir; dir->modification_time = current_timespec(); - notify_entry_created(sDeviceFileSystem->id, dir->id, vnode->name, - vnode->id); - notify_stat_changed(sDeviceFileSystem->id, dir->id, - B_STAT_MODIFICATION_TIME); - + if (notify) { + notify_entry_created(sDeviceFileSystem->id, dir->id, vnode->name, + vnode->id); + notify_stat_changed(sDeviceFileSystem->id, dir->id, + B_STAT_MODIFICATION_TIME); + } return B_OK; } static status_t -devfs_remove_from_dir(struct devfs_vnode* dir, struct devfs_vnode* removeNode) +devfs_remove_from_dir(struct devfs_vnode* dir, struct devfs_vnode* removeNode, + bool notify = true) { struct devfs_vnode *vnode = dir->stream.u.dir.dir_head; struct devfs_vnode *lastNode = NULL; @@ -374,10 +377,12 @@ devfs_remove_from_dir(struct devfs_vnode* dir, struct devfs_vnode* removeNode) vnode->dir_next = NULL; dir->modification_time = current_timespec(); - notify_entry_removed(sDeviceFileSystem->id, dir->id, vnode->name, - vnode->id); - notify_stat_changed(sDeviceFileSystem->id, dir->id, - B_STAT_MODIFICATION_TIME); + if (notify) { + notify_entry_removed(sDeviceFileSystem->id, dir->id, vnode->name, + vnode->id); + notify_stat_changed(sDeviceFileSystem->id, dir->id, + B_STAT_MODIFICATION_TIME); + } return B_OK; } } @@ -2026,30 +2031,43 @@ devfs_publish_file_device(const char *path, const char *filePath) extern "C" status_t -devfs_unpublish_partition(const char *path) +devfs_unpublish_partition(const char *devicePath, const char *name) { - return unpublish_node(sDeviceFileSystem, path, S_IFCHR); + // get the device node + devfs_vnode* deviceNode; + status_t status = get_node_for_path(sDeviceFileSystem, devicePath, + &deviceNode); + if (status != B_OK) + return status; + + // get the partition node and temporarily increment its ref count + RecursiveLocker locker(sDeviceFileSystem->lock); + devfs_vnode* node = devfs_find_in_dir(deviceNode->parent, name); + if (node != NULL) + status = get_vnode(sDeviceFileSystem->volume, node->id, (void**)&node); + else + status = B_ENTRY_NOT_FOUND; + locker.Unlock(); + + // unpublish the partition node + if (status == B_OK) { + status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); + put_vnode(sDeviceFileSystem->volume, node->id); + } + + put_vnode(sDeviceFileSystem->volume, deviceNode->id); + + return status; } extern "C" status_t -devfs_publish_partition(const char* path, const partition_info* info) +devfs_publish_partition(const char* name, const partition_info* info) { - if (path == NULL || info == NULL) + if (name == NULL || info == NULL) return B_BAD_VALUE; - TRACE(("publish partition: %s (device \"%s\", offset %Ld, size %Ld)\n", - path, info->device, info->offset, info->size)); - - // the partition and device paths must be the same until the leaves - const char* lastPath = strrchr(path, '/'); - const char* lastDevice = strrchr(info->device, '/'); - if (lastPath == NULL || lastDevice == NULL) - return B_BAD_VALUE; - - size_t length = lastDevice - (lastPath - path) - info->device; - if (strncmp(path, info->device + length, lastPath - path)) - return B_BAD_VALUE; + name, info->device, info->offset, info->size)); devfs_vnode* device; status_t status = get_node_for_path(sDeviceFileSystem, info->device, @@ -2057,13 +2075,55 @@ devfs_publish_partition(const char* path, const partition_info* info) if (status != B_OK) return status; - status = add_partition(sDeviceFileSystem, device, lastPath + 1, *info); + status = add_partition(sDeviceFileSystem, device, name, *info); put_vnode(sDeviceFileSystem->volume, device->id); return status; } +extern "C" status_t +devfs_rename_partition(const char *devicePath, const char* oldName, + const char *newName) +{ + status_t status; + devfs_vnode *device, *node; + if (oldName == NULL || newName == NULL) + return B_BAD_VALUE; + + status = get_node_for_path(sDeviceFileSystem, devicePath, &device); + if (status != B_OK) + return status; + + RecursiveLocker locker(sDeviceFileSystem->lock); + node = devfs_find_in_dir(device->parent, oldName); + if (node == NULL) + return B_ENTRY_NOT_FOUND; + + // check if the new path already exists + if (devfs_find_in_dir(device->parent, newName)) + return B_BAD_VALUE; + + char *name = strdup(newName); + if (name == NULL) + return B_NO_MEMORY; + + devfs_remove_from_dir(device->parent, node, false); + + free(node->name); + node->name = name; + + devfs_insert_in_dir(device->parent, node, false); + + notify_entry_moved(sDeviceFileSystem->id, device->parent->id, oldName, + device->parent->id, newName, node->id); + notify_stat_changed(sDeviceFileSystem->id, device->parent->id, + B_STAT_MODIFICATION_TIME); + + return B_OK; +} + + extern "C" status_t devfs_publish_directory(const char* path) { diff --git a/src/system/kernel/disk_device_manager/KDiskDevice.cpp b/src/system/kernel/disk_device_manager/KDiskDevice.cpp index 567feba020..3a99dc9bbf 100644 --- a/src/system/kernel/disk_device_manager/KDiskDevice.cpp +++ b/src/system/kernel/disk_device_manager/KDiskDevice.cpp @@ -1,4 +1,8 @@ -// KDiskDevice.cpp +/* + * Copyright 2003-2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + #include #include @@ -29,7 +33,7 @@ KDiskDevice::KDiskDevice(partition_id id) { Unset(); fDevice = this; - fPublished = true; + fPublishedName = (char*)"raw"; } // destructor @@ -160,8 +164,8 @@ KDiskDevice::SetID(partition_id id) status_t KDiskDevice::PublishDevice() { - // PublishDevice() and UnpublishDevice() are no-ops for KDiskDevices, - // since they are always published. + // PublishDevice(), UnpublishDevice() and Republish are no-ops + // for KDiskDevices, since they are always published. return B_OK; } @@ -169,11 +173,19 @@ KDiskDevice::PublishDevice() status_t KDiskDevice::UnpublishDevice() { - // PublishDevice() and UnpublishDevice() are no-ops for KDiskDevices, - // since they are always published. + // PublishDevice(), UnpublishDevice() and Republish are no-ops + // for KDiskDevices, since they are always published. return B_OK; } +// RepublishDevice +status_t +KDiskDevice::RepublishDevice() +{ + // PublishDevice(), UnpublishDevice() and Republish are no-ops + // for KDiskDevices, since they are always published. + return B_OK; +} // SetDeviceFlags void @@ -269,6 +281,16 @@ KDiskDevice::Path() const return fDeviceData.path; } + +status_t +KDiskDevice::GetFileName(char *buffer, size_t size) const +{ + if (strlcpy(buffer, "raw", size) >= size) + return B_NAME_TOO_LONG; + return B_OK; +} + + // GetPath status_t KDiskDevice::GetPath(KPath *path) const diff --git a/src/system/kernel/disk_device_manager/KPartition.cpp b/src/system/kernel/disk_device_manager/KPartition.cpp index b05897db82..4b43a62201 100644 --- a/src/system/kernel/disk_device_manager/KPartition.cpp +++ b/src/system/kernel/disk_device_manager/KPartition.cpp @@ -1,6 +1,7 @@ /* + * Copyright 2009, Bryce Groff, bgroff@hawaii.edu. * Copyright 2004-2008, Haiku, Inc. All rights reserved. - * Copyright 2003-2004, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. + * Copyright 2003-2009, Ingo Weinhold, ingo_weinhold@gmx.de. * * Distributed under the terms of the MIT License. */ @@ -57,7 +58,7 @@ KPartition::KPartition(partition_id id) fAlgorithmData(0), fReferenceCount(0), fObsolete(false), - fPublished(false) + fPublishedName(NULL) { fPartitionData.id = id >= 0 ? id : _NextID(); fPartitionData.offset = 0; @@ -179,17 +180,14 @@ KPartition::Open(int flags, int *fd) status_t KPartition::PublishDevice() { - if (fPublished) + if (fPublishedName) return B_OK; - // get the path - KPath path; - status_t error = GetPath(&path); - if (error != B_OK) { - dprintf("KPartition::PublishDevice(): Failed to get path for partition " - "%ld: %s\n", ID(), strerror(error)); + // get the name to publish + char buffer[B_FILE_NAME_LENGTH]; + status_t error = GetFileName(buffer, B_FILE_NAME_LENGTH); + if (error != B_OK) return error; - } // prepare a partition_info partition_info info; @@ -198,21 +196,24 @@ KPartition::PublishDevice() info.logical_block_size = BlockSize(); info.session = 0; info.partition = ID(); - if (strlcpy(info.device, Device()->Path(), B_PATH_NAME_LENGTH) - >= B_PATH_NAME_LENGTH) { + if (strlcpy(info.device, Device()->Path(), sizeof(info.device)) + >= sizeof(info.device)) { return B_NAME_TOO_LONG; } - error = devfs_publish_partition(path.Path() + 5, &info); - // we need to remove the "/dev/" part from the path + fPublishedName = strdup(buffer); + if (!fPublishedName) + return B_NO_MEMORY; + + error = devfs_publish_partition(buffer, &info); if (error != B_OK) { dprintf("KPartition::PublishDevice(): Failed to publish partition " "%ld: %s\n", ID(), strerror(error)); + free(fPublishedName); + fPublishedName = NULL; return error; } - fPublished = true; - return B_OK; } @@ -220,27 +221,63 @@ KPartition::PublishDevice() status_t KPartition::UnpublishDevice() { - if (!fPublished) + if (!fPublishedName) return B_OK; - // get the path - KPath path; - status_t error = GetPath(&path); + status_t error = devfs_unpublish_partition(Device()->Path(), + fPublishedName); if (error != B_OK) { - dprintf("KPartition::UnpublishDevice(): Failed to get path for " - "partition %ld: %s\n", ID(), strerror(error)); + dprintf("KPartition::UnpublishDevice(): Failed to unpublish partition " + "%ld: %s\n", ID(), strerror(error)); + } + + free(fPublishedName); + fPublishedName = NULL; + + return error; +} + + +// RepublishDevice +status_t +KPartition::RepublishDevice() +{ + if (!fPublishedName) + return B_OK; + + char newNameBuffer[B_FILE_NAME_LENGTH]; + status_t error = GetFileName(newNameBuffer, B_FILE_NAME_LENGTH); + if (error != B_OK) { + UnpublishDevice(); return error; } - fPublished = false; + if (strcmp(fPublishedName, newNameBuffer) == 0) + return B_OK; - error = devfs_unpublish_partition(path.Path() + 5); - // we need to remove the "/dev/" part from the path - if (error != B_OK) { - dprintf("KPartition::UnpublishDevice(): Failed to unpublish " - "partition %ld: %s\n", ID(), strerror(error)); + for (int i = 0; i < CountChildren(); i++) + ChildAt(i)->RepublishDevice(); + + char* newName = strdup(newNameBuffer); + if (!newName) { + UnpublishDevice(); + return B_NO_MEMORY; } - return error; + + error = devfs_rename_partition(Device()->Path(), fPublishedName, newName); + + if (error != B_OK) { + free(newName); + UnpublishDevice(); + dprintf("KPartition::RepublishDevice(): Failed to republish partition " + "%ld: %s\n", ID(), strerror(error)); + return error; + } + + free(fPublishedName); + fPublishedName = newName; + + return B_OK; } @@ -248,7 +285,7 @@ KPartition::UnpublishDevice() bool KPartition::IsPublished() const { - return fPublished; + return fPublishedName != NULL; } @@ -607,7 +644,30 @@ KPartition::ID() const return fPartitionData.id; } -// GetPath + +status_t +KPartition::GetFileName(char *buffer, size_t size) const +{ + // If the parent is the device, the name is the index of the partition. + if (Parent() == NULL || Parent()->IsDevice()) { + if (snprintf(buffer, size, "%ld", Index()) >= (int)size) + return B_NAME_TOO_LONG; + return B_OK; + } + + // The partition has a non-device parent, so we append the index to the + // parent partition's name. + status_t error = Parent()->GetFileName(buffer, size); + if (error != B_OK) + return error; + + size_t len = strlen(buffer); + if (snprintf(buffer + len, size - len, "_%ld", Index()) >= int(size - len)) + return B_NAME_TOO_LONG; + return B_OK; +} + + status_t KPartition::GetPath(KPath *path) const { @@ -615,34 +675,22 @@ KPartition::GetPath(KPath *path) const // Parent() is correct. if (!path || path->InitCheck() != B_OK || !Parent() || Index() < 0) return B_BAD_VALUE; - // get the parent's path - status_t error = Parent()->GetPath(path); + + // init the path with the device path + status_t error = path->SetPath(Device()->Path()); if (error != B_OK) return error; - if (Parent()->IsDevice()) { - // Our parent is a device, so we replace `raw' by our index. - const char *leaf = path->Leaf(); - if (!leaf || strcmp(leaf, "raw") != B_OK) - return B_ERROR; - #ifdef _KERNEL_MODE - char indexBuffer[12]; - snprintf(indexBuffer, sizeof(indexBuffer), "%ld", Index()); - #else - const char *prefix = "haiku_"; - char indexBuffer[strlen(prefix) + 12]; - snprintf(indexBuffer, sizeof(indexBuffer), "%s%ld", prefix, - Index()); - #endif - error = path->ReplaceLeaf(indexBuffer); - } else { - // Our parent is a normal partition, no device: Append our index. - char indexBuffer[13]; - snprintf(indexBuffer, sizeof(indexBuffer), "_%ld", Index()); - error = path->Append(indexBuffer, false); - } + + // replace the leaf name with the partition's file name + char name[B_FILE_NAME_LENGTH]; + error = GetFileName(name, sizeof(name)); + if (error == B_OK) + error = path->ReplaceLeaf(name); + return error; } + // SetVolumeID void KPartition::SetVolumeID(dev_t volumeID) @@ -780,9 +828,11 @@ KPartition::AddChild(KPartition *partition, int32 index) fChildren.Erase(index); return B_NO_MEMORY; } + // update siblings index's partition->SetIndex(index); - _UpdateChildIndices(index); + _UpdateChildIndices(count, index); fPartitionData.child_count++; + partition->SetParent(this); partition->SetDevice(Device()); @@ -843,7 +893,7 @@ KPartition::RemoveChild(int32 index) || !fChildren.Erase(index)) { return false; } - _UpdateChildIndices(index + 1); + _UpdateChildIndices(index, fChildren.Count()); partition->SetIndex(-1); fPartitionData.child_count--; partition->SetParent(NULL); @@ -1489,12 +1539,22 @@ KPartition::FireContentCookieChanged(void *cookie) // _UpdateChildIndices void -KPartition::_UpdateChildIndices(int32 index) +KPartition::_UpdateChildIndices(int32 start, int32 end) { - for (int32 i = index; i < fChildren.Count(); i++) - fChildren.ElementAt(i)->SetIndex(i); + if (start < end) { + for (int32 i = start; i < end; i++) { + fChildren.ElementAt(i)->SetIndex(i); + fChildren.ElementAt(i)->RepublishDevice(); + } + } else { + for (int32 i = start; i > end; i--) { + fChildren.ElementAt(i)->SetIndex(i); + fChildren.ElementAt(i)->RepublishDevice(); + } + } } + // _NextID int32 KPartition::_NextID()