* Added vfs_normalize_path() service call.

* Added resolve_mount_point_to_volume_root() which does what
  resolve_volume_root_to_mount_point() did. IOW the latter one didn't
  do what it advertised.
* Fixed dir_vnode_to_path(). Basically broken due to the broken
  resolve_volume_root_to_mount_point(), but also compared potentially
  unrelated vnode IDs (belonging to different volumes).
* Fixed get_dir_path_and_leaf(). It didn't deal correctly with paths
  ending in '/' (including the root dir).
* fs_mount() does now accept a NULL fsName, getting the FS name from
  the DDM in this case.
* fs_mount() now also supports mounting file images; it lets the DDM
  create a file device for them.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@9629 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2004-10-28 22:31:43 +00:00
parent 93b4337b18
commit 7fb2963ae4
2 changed files with 213 additions and 25 deletions
+4
View File
@@ -81,6 +81,10 @@ status_t vfs_get_fs_node_from_path(mount_id mountID, const char *path, bool kern
/* special module convenience call */
status_t vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer, size_t bufferSize);
/* service call for whoever needs a normalized path */
status_t vfs_normalize_path(const char *path, char *buffer, size_t bufferSize,
bool kernel);
/* calls the syscall dispatcher should use for user file I/O */
status_t _user_mount(const char *path, const char *device, const char *fs_name,
uint32 flags, void *args);
+209 -25
View File
@@ -16,6 +16,8 @@
#include <disk_device_manager/KDiskDevice.h>
#include <disk_device_manager/KDiskDeviceManager.h>
#include <disk_device_manager/KDiskDeviceUtils.h>
#include <disk_device_manager/KDiskSystem.h>
#include <KPath.h>
#include <syscalls.h>
#include <boot/kernel_args.h>
#include <vfs.h>
@@ -96,6 +98,7 @@ struct fs_mount {
KPartition *partition;
struct list vnodes;
bool unmounting;
bool owns_file_device;
};
// RecursiveLockLocking
@@ -751,6 +754,36 @@ put_vnode(struct vnode *vnode)
}
/** \brief Resolves a mount point vnode to the volume root vnode it is covered
by.
Given an arbitrary vnode, the function checks, whether the node is covered
by the root of a volume. If it is the function obtains a reference to the
volume root node and returns it.
\param vnode The vnode in question.
\return The volume root vnode the vnode cover is covered by, if it is
indeed a mount point, or \c NULL otherwise.
*/
static
struct vnode*
resolve_mount_point_to_volume_root(struct vnode *vnode)
{
if (!vnode)
return NULL;
struct vnode *volumeRoot = NULL;
recursive_lock_lock(&sMountOpLock);
if (vnode->covered_by) {
volumeRoot = vnode->covered_by;
inc_vnode_ref_count(volumeRoot);
}
recursive_lock_unlock(&sMountOpLock);
return volumeRoot;
}
/** \brief Resolves a volume root vnode to the underlying mount point vnode.
Given an arbitrary vnode, the function checks, whether the node is the
@@ -771,8 +804,9 @@ resolve_volume_root_to_mount_point(struct vnode *vnode)
struct vnode *mountPoint = NULL;
recursive_lock_lock(&sMountOpLock);
if (vnode->covered_by) {
mountPoint = vnode->covered_by;
struct fs_mount *mount = vnode->mount;
if (vnode == mount->root_vnode && mount->covers_vnode) {
mountPoint = mount->covers_vnode;
inc_vnode_ref_count(mountPoint);
}
recursive_lock_unlock(&sMountOpLock);
@@ -812,14 +846,19 @@ get_dir_path_and_leaf(char *path, char *filename)
return B_NAME_TOO_LONG;
strcpy(path, ".");
} else {
// replace the filename portion of the path with a '.'
if (strlcpy(filename, ++p, B_FILE_NAME_LENGTH) >= B_FILE_NAME_LENGTH)
return B_NAME_TOO_LONG;
if (p[0] != '\0'){
p[0] = '.';
p[1] = '\0';
p++;
if (*p == '\0') {
// special case: the path ends in '/'
strcpy(filename, ".");
} else {
// normal leaf: replace the leaf portion of the path with a '.'
if (strlcpy(filename, p, B_FILE_NAME_LENGTH)
>= B_FILE_NAME_LENGTH) {
return B_NAME_TOO_LONG;
}
}
p[0] = '.';
p[1] = '\0';
}
return B_OK;
}
@@ -983,7 +1022,7 @@ vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
vnode = nextVnode;
// see if we hit a mount point
struct vnode *mountPoint = resolve_volume_root_to_mount_point(vnode);
struct vnode *mountPoint = resolve_mount_point_to_volume_root(vnode);
if (mountPoint) {
put_vnode(vnode);
vnode = mountPoint;
@@ -1149,7 +1188,7 @@ get_vnode_name(struct vnode *vnode, struct vnode *parent,
static status_t
dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize)
{
FUNCTION(("dir_vnode_to_path(%p, %p, %lu\n)", vnode, buffer, bufferSize));
FUNCTION(("dir_vnode_to_path(%p, %p, %lu)\n", vnode, buffer, bufferSize));
/* this implementation is currently bound to SYS_MAX_PATH_LEN */
char path[SYS_MAX_PATH_LEN];
@@ -1202,8 +1241,11 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize)
if (mountPoint) {
put_vnode(parentVnode);
parentVnode = mountPoint;
parentID = parentVnode->id;
}
bool hitRoot = (parentVnode == vnode);
// Does the file system support getting the name of a vnode?
// If so, get it here...
if (status == B_OK && FS_CALL(vnode, get_vnode_name))
@@ -1229,7 +1271,7 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize)
goto out;
}
if (parentID == id) {
if (hitRoot) {
// we have reached "/", which means we have constructed the full
// path
break;
@@ -1737,6 +1779,85 @@ err:
}
/** \brief Normalizes a given path.
The path must refer to an existing or non-existing entry in an existing
directory, that is chopping off the leaf component the remaining path must
refer to an existing directory.
The returned will be canonical in that it will be absolute, will not
contain any "." or ".." components or duplicate occurrences of '/'s,
and none of the directory components will by symbolic links.
Any two paths referring to the same entry, will result in the same
normalized path (well, that is pretty much the definition of `normalized',
isn't it :-).
\param path The path to be normalized.
\param buffer The buffer into which the normalized path will be written.
\param bufferSize The size of \a buffer.
\param kernel \c true, if the IO context of the kernel shall be used,
otherwise that of the team this thread belongs to. Only relevant,
if the path is relative (to get the CWD).
\return \c B_OK if everything went fine, another error code otherwise.
*/
status_t
vfs_normalize_path(const char *path, char *buffer, size_t bufferSize,
bool kernel)
{
if (!path || !buffer || bufferSize < 1)
return B_BAD_VALUE;
PRINT(("vfs_normalize_path(`%s')\n", path));
// copy the supplied path to the stack, so it can be modified
char mutablePath[B_PATH_NAME_LENGTH + 1];
if (strlcpy(mutablePath, path, B_PATH_NAME_LENGTH) >= B_PATH_NAME_LENGTH)
return B_NAME_TOO_LONG;
// get the dir vnode and the leaf name
struct vnode *dirNode;
char leaf[B_FILE_NAME_LENGTH];
status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel);
if (error != B_OK)
{
PRINT(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error)));
return error;
}
// if the leaf is "." or "..", we directly get the correct directory
// vnode and ignore the leaf later
bool isDir = (strcmp(leaf, ".") == 0 || strcmp(leaf, "..") == 0);
if (isDir)
error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL);
if (error != B_OK)
{
PRINT(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n", strerror(error)));
return error;
}
// get the directory path
error = dir_vnode_to_path(dirNode, buffer, bufferSize);
put_vnode(dirNode);
if (error < B_OK)
{
PRINT(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error)));
return error;
}
// append the leaf name
if (!isDir) {
// insert a directory separator only if this is not the file system root
if ((strcmp(buffer, "/") != 0
&& strlcat(buffer, "/", bufferSize) >= bufferSize)
|| strlcat(buffer, leaf, bufferSize) >= bufferSize) {
return B_NAME_TOO_LONG;
}
}
PRINT(("vfs_normalize_path() -> `%s'\n", buffer));
return B_OK;
}
int
vfs_put_vnode_ptr(void *_vnode)
{
@@ -3650,25 +3771,61 @@ fs_mount(char *path, const char *device, const char *fsName, uint32 flags,
// The path is always safe, we just have to make sure that fsName is
// almost valid - we can't make any assumptions about args, though.
// TODO: fsName == NULL is OK, if device refers to a partition. Then the DDM
// will know, what FS to use.
if (fsName == NULL || fsName[0] == '\0')
// A NULL fsName is OK, if a device was given and the FS is not virtual.
// We'll get it from the DDM later.
if (fsName == NULL) {
if (!device || flags & B_MOUNT_VIRTUAL_DEVICE)
return B_BAD_VALUE;
} else if (fsName[0] == '\0')
return B_BAD_VALUE;
RecursiveLocker mountOpLocker(sMountOpLock);
// Helper to delete a newly created file device on failure.
// Not exactly beautiful, but helps to keep the code below cleaner.
struct FileDeviceDeleter {
FileDeviceDeleter() : id(-1) {}
~FileDeviceDeleter()
{
KDiskDeviceManager::Default()->DeleteFileDevice(id);
}
partition_id id;
} fileDeviceDeleter;
// If the file system is not a "virtual" one, the device argument should
// point to a real file/device (if given at all).
// get the partition
KDiskDeviceManager* ddm = KDiskDeviceManager::Default();
KPartition* partition = NULL;
bool newlyCreatedFileDevice = false;
if (!(flags & B_MOUNT_VIRTUAL_DEVICE) && device) {
// normalize the device path
KPath normalizedDevice;
err = normalizedDevice.SetTo(device, true);
if (err != B_OK)
return err;
// get a corresponding partition from the DDM
partition = ddm->RegisterPartition(device, true);
// TODO: Normalize the path!
// TODO: Support for mounting images!
partition = ddm->RegisterPartition(normalizedDevice.Path(), true);
if (!partition) {
PRINT(("fs_mount(): Partition `%s' not found.\n", device));
// Partition not found: This either means, the user supplied
// an invalid path, or the path refers to an image file. We try
// to let the DDM create a file device for the path.
partition_id deviceID = ddm->CreateFileDevice(
normalizedDevice.Path(), &newlyCreatedFileDevice);
if (deviceID >= 0) {
partition = ddm->RegisterPartition(deviceID, true);
if (newlyCreatedFileDevice)
fileDeviceDeleter.id = deviceID;
// TODO: We must wait here, until the partition scan job is done.
}
}
if (!partition) {
PRINT(("fs_mount(): Partition `%s' not found.\n",
normalizedDevice.Path()));
return B_ENTRY_NOT_FOUND;
}
}
@@ -3688,12 +3845,33 @@ fs_mount(char *path, const char *device, const char *fsName, uint32 flags,
}
DeviceWriteLocker writeLocker(diskDevice, true);
// make sure, that the partition is not busy
if (partition) {
// make sure, that the partition is not busy
if (partition->IsBusy() || partition->IsDescendantBusy()) {
PRINT(("fs_mount(): Partition is busy.\n"));
return B_BUSY;
}
// if no FS name had been supplied, we get it from the partition
if (!fsName) {
KDiskSystem *diskSystem = partition->DiskSystem();
if (!diskSystem) {
PRINT(("fs_mount(): No FS name was given, and the DDM didn't "
"recognize it.\n"));
return B_BAD_VALUE;
}
if (!diskSystem->IsFileSystem()) {
PRINT(("fs_mount(): No FS name was given, and the DDM found a "
"partitioning system.\n"));
return B_BAD_VALUE;
}
// The disk system name will not change, and the KDiskSystem
// object will not go away while the disk device is locked (and
// the partition has a reference to it), so this is safe.
fsName = diskSystem->Name();
}
}
mount = (struct fs_mount *)malloc(sizeof(struct fs_mount));
@@ -3722,8 +3900,9 @@ fs_mount(char *path, const char *device, const char *fsName, uint32 flags,
goto err4;
mount->id = sNextMountID++;
mount->unmounting = false;
mount->partition = NULL;
mount->unmounting = false;
mount->owns_file_device = false;
mutex_lock(&sMountMutex);
@@ -3802,6 +3981,8 @@ fs_mount(char *path, const char *device, const char *fsName, uint32 flags,
// keep a partition reference as long as the partition is mounted
partitionRegistrar.Detach();
mount->partition = partition;
mount->owns_file_device = newlyCreatedFileDevice;
fileDeviceDeleter.id = -1;
}
return B_OK;
@@ -3933,14 +4114,17 @@ fs_unmount(char *path, bool kernel)
// release the file system
put_file_system(mount->fs);
// dereference the partition
if (partition) {
if (mount->owns_file_device)
KDiskDeviceManager::Default()->DeleteFileDevice(partition->ID());
partition->Unregister();
}
free(mount->device_name);
free(mount->fs_name);
free(mount);
// dereference the partition
if (partition)
partition->Unregister();
return 0;
}