From e8d3eff9689f3436efa2863b9bf4afc84f79e0a3 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 26 Sep 2008 23:30:28 +0000 Subject: [PATCH] * vfs_normalize_path() and _user_normalize_path() use a common helper function (normalize_path()), now. There was some code duplication before. * Added "bool traverseLink" parameter to vfs_normalize_path(). When true and the leaf component is a symlink, it will be resolved. * KPath: - Added similar leaf link traversal parameter to SetTo() and SetPath(). - Added Normalize(). - Added DetachBuffer(), which returns the object's current buffer and unsets itself. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27751 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/fs/KPath.h | 11 +- headers/private/kernel/vfs.h | 2 +- src/system/kernel/fs/KPath.cpp | 41 +++++- src/system/kernel/fs/vfs.cpp | 237 +++++++++++++----------------- 4 files changed, 147 insertions(+), 144 deletions(-) diff --git a/headers/private/kernel/fs/KPath.h b/headers/private/kernel/fs/KPath.h index 6474868126..e917cb392a 100644 --- a/headers/private/kernel/fs/KPath.h +++ b/headers/private/kernel/fs/KPath.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2006, Ingo Weinhold, bonefish@users.sf.net. + * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #ifndef _K_PATH_H @@ -21,18 +21,21 @@ class KPath { ~KPath(); status_t SetTo(const char *path, bool normalize = false, - size_t bufferSize = B_PATH_NAME_LENGTH); + size_t bufferSize = B_PATH_NAME_LENGTH, + bool traverseLeafLink = false); void Adopt(KPath& other); status_t InitCheck() const; - status_t SetPath(const char *path, bool normalize = false); + status_t SetPath(const char *path, bool normalize = false, + bool traverseLeafLink = false); const char *Path() const; size_t Length() const { return fPathLength; } size_t BufferSize() const { return fBufferSize; } char *LockBuffer(); void UnlockBuffer(); + char* DetachBuffer(); const char *Leaf() const; status_t ReplaceLeaf(const char *newLeaf); @@ -41,6 +44,8 @@ class KPath { status_t Append(const char *toAppend, bool isComponent = true); + status_t Normalize(bool traverseLeafLink); + KPath& operator=(const KPath& other); KPath& operator=(const char* path); diff --git a/headers/private/kernel/vfs.h b/headers/private/kernel/vfs.h index 0c4467a1c6..eea8a79f47 100644 --- a/headers/private/kernel/vfs.h +++ b/headers/private/kernel/vfs.h @@ -128,7 +128,7 @@ status_t vfs_get_module_path(const char *basePath, const char *moduleName, /* service call for whoever needs a normalized path */ status_t vfs_normalize_path(const char *path, char *buffer, size_t bufferSize, - bool kernel); + bool traverseLink, bool kernel); /* service call for whoever wants to create a special node */ status_t vfs_create_special_node(const char *path, fs_vnode *subVnode, diff --git a/src/system/kernel/fs/KPath.cpp b/src/system/kernel/fs/KPath.cpp index 7fea779131..fc84ff7693 100644 --- a/src/system/kernel/fs/KPath.cpp +++ b/src/system/kernel/fs/KPath.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2004-2006, Ingo Weinhold, bonefish@users.sf.net. + * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -59,7 +59,8 @@ KPath::~KPath() status_t -KPath::SetTo(const char* path, bool normalize, size_t bufferSize) +KPath::SetTo(const char* path, bool normalize, size_t bufferSize, + bool traverseLeafLink) { if (bufferSize == 0) bufferSize = B_PATH_NAME_LENGTH; @@ -82,7 +83,7 @@ KPath::SetTo(const char* path, bool normalize, size_t bufferSize) fBufferSize = bufferSize; fBuffer[0] = '\0'; } - return SetPath(path, normalize); + return SetPath(path, normalize, traverseLeafLink); } @@ -94,7 +95,7 @@ KPath::Adopt(KPath& other) fBuffer = other.fBuffer; fBufferSize = other.fBufferSize; - other.fBuffer = NULL; + other.fBuffer = NULL; } @@ -106,7 +107,7 @@ KPath::InitCheck() const status_t -KPath::SetPath(const char *path, bool normalize) +KPath::SetPath(const char *path, bool normalize, bool traverseLeafLink) { if (!fBuffer) return B_NO_INIT; @@ -115,6 +116,7 @@ KPath::SetPath(const char *path, bool normalize) if (normalize) { // normalize path status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, + traverseLeafLink, team_get_kernel_team_id() == team_get_current_team_id()); if (error != B_OK) { SetPath(NULL); @@ -175,6 +177,22 @@ KPath::UnlockBuffer() } +char* +KPath::DetachBuffer() +{ + char* buffer = fBuffer; + + if (fBuffer != NULL) { + fBuffer = NULL; + fBufferSize = 0; + fPathLength = 0; + fLocked = false; + } + + return buffer; +} + + const char * KPath::Leaf() const { @@ -267,6 +285,19 @@ KPath::Append(const char *component, bool isComponent) } +status_t +KPath::Normalize(bool traverseLeafLink) +{ + if (fBuffer == NULL) + return B_NO_INIT; + if (fPathLength == 0) + return B_BAD_VALUE; + + return vfs_normalize_path(fBuffer, fBuffer, fBufferSize, traverseLeafLink, + team_get_kernel_team_id() == team_get_current_team_id()); +} + + KPath& KPath::operator=(const KPath& other) { diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 76949cfea4..d40c85f743 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -2857,6 +2857,91 @@ get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode, return fd; } + +/*! In-place normalizes \a path. It's otherwise semantically equivalent to + vfs_normalize_path(). See there for more documentation. +*/ +static status_t +normalize_path(char* path, size_t pathSize, bool traverseLink, bool kernel) +{ + VNodePutter dirPutter; + struct vnode* dir = NULL; + status_t error; + + for (int i = 0; i < B_MAX_SYMLINKS; i++) { + // get dir vnode + leaf name + struct vnode* nextDir; + char leaf[B_FILE_NAME_LENGTH]; + error = vnode_and_path_to_dir_vnode(dir, path, &nextDir, leaf, kernel); + if (error != B_OK) + return error; + + dir = nextDir; + strcpy(path, leaf); + dirPutter.SetTo(dir); + + // get file vnode, if we shall resolve links + bool fileExists = false; + struct vnode* fileVnode; + VNodePutter fileVnodePutter; + if (traverseLink) { + inc_vnode_ref_count(dir); + if (vnode_path_to_vnode(dir, path, false, 0, kernel, &fileVnode, + NULL) == B_OK) { + fileVnodePutter.SetTo(fileVnode); + fileExists = true; + } + } + + if (!fileExists || !traverseLink || !S_ISLNK(fileVnode->type)) { + // we're done -- construct the path + bool hasLeaf = true; + if (strcmp(leaf, ".") == 0 || strcmp(leaf, "..") == 0) { + // special cases "." and ".." -- get the dir, forget the leaf + inc_vnode_ref_count(dir); + error = vnode_path_to_vnode(dir, leaf, false, 0, kernel, + &nextDir, NULL); + if (error != B_OK) + return error; + dir = nextDir; + dirPutter.SetTo(dir); + hasLeaf = false; + } + + // get the directory path + error = dir_vnode_to_path(dir, path, B_PATH_NAME_LENGTH, kernel); + if (error != B_OK) + return error; + + // append the leaf name + if (hasLeaf) { + // insert a directory separator if this is not the file system + // root + if ((strcmp(path, "/") != 0 + && strlcat(path, "/", pathSize) >= pathSize) + || strlcat(path, leaf, pathSize) >= pathSize) { + return B_NAME_TOO_LONG; + } + } + + return B_OK; + } + + // read link + if (HAS_FS_CALL(fileVnode, read_symlink)) { + size_t bufferSize = B_PATH_NAME_LENGTH - 1; + error = FS_CALL(fileVnode, read_symlink, path, &bufferSize); + if (error != B_OK) + return error; + path[bufferSize] = '\0'; + } else + return B_BAD_VALUE; + } + + return B_LINK_LIMIT; +} + + #ifdef ADD_DEBUGGER_COMMANDS @@ -4020,6 +4105,7 @@ err: \param buffer The buffer into which the normalized path will be written. May be the same one as \a path. \param bufferSize The size of \a buffer. + \param traverseLink If \c true, the function also resolves leaf symlinks. \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). @@ -4027,64 +4113,17 @@ err: */ status_t vfs_normalize_path(const char *path, char *buffer, size_t bufferSize, - bool kernel) + bool traverseLink, bool kernel) { if (!path || !buffer || bufferSize < 1) return B_BAD_VALUE; - TRACE(("vfs_normalize_path(`%s')\n", path)); - - // copy the supplied path to the stack, so it can be modified - KPath mutablePathBuffer(B_PATH_NAME_LENGTH + 1); - if (mutablePathBuffer.InitCheck() != B_OK) - return B_NO_MEMORY; - - char *mutablePath = mutablePathBuffer.LockBuffer(); - 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) { - TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error))); - return error; + if (path != buffer) { + if (strlcpy(buffer, path, bufferSize) >= bufferSize) + return B_BUFFER_OVERFLOW; } - // 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, kernel, &dirNode, - NULL); - } - if (error != B_OK) { - TRACE(("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, kernel); - put_vnode(dirNode); - if (error < B_OK) { - TRACE(("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; - } - } - - TRACE(("vfs_normalize_path() -> `%s'\n", buffer)); - return B_OK; + return normalize_path(buffer, bufferSize, traverseLink, kernel); } @@ -8184,91 +8223,19 @@ _user_normalize_path(const char* userPath, bool traverseLink, char* buffer) if (user_strlcpy(path, userPath, B_PATH_NAME_LENGTH) < B_OK) return B_BAD_ADDRESS; - // buffer for the leaf part - KPath leafBuffer(B_PATH_NAME_LENGTH + 1); - if (leafBuffer.InitCheck() != B_OK) - return B_NO_MEMORY; - char* leaf = leafBuffer.LockBuffer(); + status_t error = normalize_path(path, pathBuffer.BufferSize(), traverseLink, + false); + if (error != B_OK) + return error; - VNodePutter dirPutter; - struct vnode* dir = NULL; - status_t error; + // copy back to userland + int len = user_strlcpy(buffer, path, B_PATH_NAME_LENGTH); + if (len < 0) + return len; + if (len >= B_PATH_NAME_LENGTH) + return B_BUFFER_OVERFLOW; - for (int i = 0; i < B_MAX_SYMLINKS; i++) { - // get dir vnode + leaf name - struct vnode* nextDir; - error = vnode_and_path_to_dir_vnode(dir, path, &nextDir, leaf, false); - if (error != B_OK) - return error; - - dir = nextDir; - strcpy(path, leaf); - dirPutter.SetTo(dir); - - // get file vnode - inc_vnode_ref_count(dir); - struct vnode* fileVnode; - error = vnode_path_to_vnode(dir, path, false, 0, false, &fileVnode, - NULL); - if (error != B_OK) - return error; - VNodePutter fileVnodePutter(fileVnode); - - if (!traverseLink || !S_ISLNK(fileVnode->type)) { - // we're done -- construct the path - bool hasLeaf = true; - if (strcmp(leaf, ".") == 0 || strcmp(leaf, "..") == 0) { - // special cases "." and ".." -- get the dir, forget the leaf - inc_vnode_ref_count(dir); - error = vnode_path_to_vnode(dir, leaf, false, 0, false, - &nextDir, NULL); - if (error != B_OK) - return error; - dir = nextDir; - dirPutter.SetTo(dir); - hasLeaf = false; - } - - // get the directory path - error = dir_vnode_to_path(dir, path, B_PATH_NAME_LENGTH, false); - if (error != B_OK) - return error; - - // append the leaf name - if (hasLeaf) { - // insert a directory separator if this is not the file system - // root - if ((strcmp(path, "/") != 0 - && strlcat(path, "/", pathBuffer.BufferSize()) - >= pathBuffer.BufferSize()) - || strlcat(path, leaf, pathBuffer.BufferSize()) - >= pathBuffer.BufferSize()) { - return B_NAME_TOO_LONG; - } - } - - // copy back to userland - int len = user_strlcpy(buffer, path, B_PATH_NAME_LENGTH); - if (len < 0) - return len; - if (len >= B_PATH_NAME_LENGTH) - return B_BUFFER_OVERFLOW; - - return B_OK; - } - - // read link - if (HAS_FS_CALL(fileVnode, read_symlink)) { - size_t bufferSize = B_PATH_NAME_LENGTH - 1; - error = FS_CALL(fileVnode, read_symlink, path, &bufferSize); - if (error != B_OK) - return error; - path[bufferSize] = '\0'; - } else - return B_BAD_VALUE; - } - - return B_LINK_LIMIT; + return B_OK; }