* 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:
@@ -81,6 +81,10 @@ status_t vfs_get_fs_node_from_path(mount_id mountID, const char *path, bool kern
|
|||||||
/* special module convenience call */
|
/* special module convenience call */
|
||||||
status_t vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer, size_t bufferSize);
|
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 */
|
/* 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,
|
status_t _user_mount(const char *path, const char *device, const char *fs_name,
|
||||||
uint32 flags, void *args);
|
uint32 flags, void *args);
|
||||||
|
|||||||
+209
-25
@@ -16,6 +16,8 @@
|
|||||||
#include <disk_device_manager/KDiskDevice.h>
|
#include <disk_device_manager/KDiskDevice.h>
|
||||||
#include <disk_device_manager/KDiskDeviceManager.h>
|
#include <disk_device_manager/KDiskDeviceManager.h>
|
||||||
#include <disk_device_manager/KDiskDeviceUtils.h>
|
#include <disk_device_manager/KDiskDeviceUtils.h>
|
||||||
|
#include <disk_device_manager/KDiskSystem.h>
|
||||||
|
#include <KPath.h>
|
||||||
#include <syscalls.h>
|
#include <syscalls.h>
|
||||||
#include <boot/kernel_args.h>
|
#include <boot/kernel_args.h>
|
||||||
#include <vfs.h>
|
#include <vfs.h>
|
||||||
@@ -96,6 +98,7 @@ struct fs_mount {
|
|||||||
KPartition *partition;
|
KPartition *partition;
|
||||||
struct list vnodes;
|
struct list vnodes;
|
||||||
bool unmounting;
|
bool unmounting;
|
||||||
|
bool owns_file_device;
|
||||||
};
|
};
|
||||||
|
|
||||||
// RecursiveLockLocking
|
// 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.
|
/** \brief Resolves a volume root vnode to the underlying mount point vnode.
|
||||||
|
|
||||||
Given an arbitrary vnode, the function checks, whether the node is the
|
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;
|
struct vnode *mountPoint = NULL;
|
||||||
|
|
||||||
recursive_lock_lock(&sMountOpLock);
|
recursive_lock_lock(&sMountOpLock);
|
||||||
if (vnode->covered_by) {
|
struct fs_mount *mount = vnode->mount;
|
||||||
mountPoint = vnode->covered_by;
|
if (vnode == mount->root_vnode && mount->covers_vnode) {
|
||||||
|
mountPoint = mount->covers_vnode;
|
||||||
inc_vnode_ref_count(mountPoint);
|
inc_vnode_ref_count(mountPoint);
|
||||||
}
|
}
|
||||||
recursive_lock_unlock(&sMountOpLock);
|
recursive_lock_unlock(&sMountOpLock);
|
||||||
@@ -812,14 +846,19 @@ get_dir_path_and_leaf(char *path, char *filename)
|
|||||||
return B_NAME_TOO_LONG;
|
return B_NAME_TOO_LONG;
|
||||||
strcpy(path, ".");
|
strcpy(path, ".");
|
||||||
} else {
|
} else {
|
||||||
// replace the filename portion of the path with a '.'
|
p++;
|
||||||
if (strlcpy(filename, ++p, B_FILE_NAME_LENGTH) >= B_FILE_NAME_LENGTH)
|
if (*p == '\0') {
|
||||||
return B_NAME_TOO_LONG;
|
// special case: the path ends in '/'
|
||||||
|
strcpy(filename, ".");
|
||||||
if (p[0] != '\0'){
|
} else {
|
||||||
p[0] = '.';
|
// normal leaf: replace the leaf portion of the path with a '.'
|
||||||
p[1] = '\0';
|
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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -983,7 +1022,7 @@ vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
|
|||||||
vnode = nextVnode;
|
vnode = nextVnode;
|
||||||
|
|
||||||
// see if we hit a mount point
|
// 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) {
|
if (mountPoint) {
|
||||||
put_vnode(vnode);
|
put_vnode(vnode);
|
||||||
vnode = mountPoint;
|
vnode = mountPoint;
|
||||||
@@ -1149,7 +1188,7 @@ get_vnode_name(struct vnode *vnode, struct vnode *parent,
|
|||||||
static status_t
|
static status_t
|
||||||
dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize)
|
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 */
|
/* this implementation is currently bound to SYS_MAX_PATH_LEN */
|
||||||
char path[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) {
|
if (mountPoint) {
|
||||||
put_vnode(parentVnode);
|
put_vnode(parentVnode);
|
||||||
parentVnode = mountPoint;
|
parentVnode = mountPoint;
|
||||||
|
parentID = parentVnode->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hitRoot = (parentVnode == vnode);
|
||||||
|
|
||||||
// Does the file system support getting the name of a vnode?
|
// Does the file system support getting the name of a vnode?
|
||||||
// If so, get it here...
|
// If so, get it here...
|
||||||
if (status == B_OK && FS_CALL(vnode, get_vnode_name))
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentID == id) {
|
if (hitRoot) {
|
||||||
// we have reached "/", which means we have constructed the full
|
// we have reached "/", which means we have constructed the full
|
||||||
// path
|
// path
|
||||||
break;
|
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
|
int
|
||||||
vfs_put_vnode_ptr(void *_vnode)
|
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
|
// 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.
|
// 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
|
// A NULL fsName is OK, if a device was given and the FS is not virtual.
|
||||||
// will know, what FS to use.
|
// We'll get it from the DDM later.
|
||||||
if (fsName == NULL || fsName[0] == '\0')
|
if (fsName == NULL) {
|
||||||
|
if (!device || flags & B_MOUNT_VIRTUAL_DEVICE)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
} else if (fsName[0] == '\0')
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
RecursiveLocker mountOpLocker(sMountOpLock);
|
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
|
// If the file system is not a "virtual" one, the device argument should
|
||||||
// point to a real file/device (if given at all).
|
// point to a real file/device (if given at all).
|
||||||
// get the partition
|
// get the partition
|
||||||
KDiskDeviceManager* ddm = KDiskDeviceManager::Default();
|
KDiskDeviceManager* ddm = KDiskDeviceManager::Default();
|
||||||
KPartition* partition = NULL;
|
KPartition* partition = NULL;
|
||||||
|
bool newlyCreatedFileDevice = false;
|
||||||
if (!(flags & B_MOUNT_VIRTUAL_DEVICE) && device) {
|
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
|
// get a corresponding partition from the DDM
|
||||||
partition = ddm->RegisterPartition(device, true);
|
partition = ddm->RegisterPartition(normalizedDevice.Path(), true);
|
||||||
// TODO: Normalize the path!
|
|
||||||
// TODO: Support for mounting images!
|
|
||||||
if (!partition) {
|
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;
|
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);
|
DeviceWriteLocker writeLocker(diskDevice, true);
|
||||||
|
|
||||||
// make sure, that the partition is not busy
|
|
||||||
if (partition) {
|
if (partition) {
|
||||||
|
// make sure, that the partition is not busy
|
||||||
if (partition->IsBusy() || partition->IsDescendantBusy()) {
|
if (partition->IsBusy() || partition->IsDescendantBusy()) {
|
||||||
PRINT(("fs_mount(): Partition is busy.\n"));
|
PRINT(("fs_mount(): Partition is busy.\n"));
|
||||||
return B_BUSY;
|
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));
|
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;
|
goto err4;
|
||||||
|
|
||||||
mount->id = sNextMountID++;
|
mount->id = sNextMountID++;
|
||||||
mount->unmounting = false;
|
|
||||||
mount->partition = NULL;
|
mount->partition = NULL;
|
||||||
|
mount->unmounting = false;
|
||||||
|
mount->owns_file_device = false;
|
||||||
|
|
||||||
mutex_lock(&sMountMutex);
|
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
|
// keep a partition reference as long as the partition is mounted
|
||||||
partitionRegistrar.Detach();
|
partitionRegistrar.Detach();
|
||||||
mount->partition = partition;
|
mount->partition = partition;
|
||||||
|
mount->owns_file_device = newlyCreatedFileDevice;
|
||||||
|
fileDeviceDeleter.id = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -3933,14 +4114,17 @@ fs_unmount(char *path, bool kernel)
|
|||||||
// release the file system
|
// release the file system
|
||||||
put_file_system(mount->fs);
|
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->device_name);
|
||||||
free(mount->fs_name);
|
free(mount->fs_name);
|
||||||
free(mount);
|
free(mount);
|
||||||
|
|
||||||
// dereference the partition
|
|
||||||
if (partition)
|
|
||||||
partition->Unregister();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user