* 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
This commit is contained in:
Ingo Weinhold 2008-09-26 23:30:28 +00:00
parent 0914d6da5a
commit e8d3eff968
4 changed files with 147 additions and 144 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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)
{

View File

@ -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;
}