* 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:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2006, Ingo Weinhold, [email protected].
|
* Copyright 2004-2008, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef _K_PATH_H
|
#ifndef _K_PATH_H
|
||||||
@@ -21,18 +21,21 @@ class KPath {
|
|||||||
~KPath();
|
~KPath();
|
||||||
|
|
||||||
status_t SetTo(const char *path, bool normalize = false,
|
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);
|
void Adopt(KPath& other);
|
||||||
|
|
||||||
status_t InitCheck() const;
|
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;
|
const char *Path() const;
|
||||||
size_t Length() const { return fPathLength; }
|
size_t Length() const { return fPathLength; }
|
||||||
|
|
||||||
size_t BufferSize() const { return fBufferSize; }
|
size_t BufferSize() const { return fBufferSize; }
|
||||||
char *LockBuffer();
|
char *LockBuffer();
|
||||||
void UnlockBuffer();
|
void UnlockBuffer();
|
||||||
|
char* DetachBuffer();
|
||||||
|
|
||||||
const char *Leaf() const;
|
const char *Leaf() const;
|
||||||
status_t ReplaceLeaf(const char *newLeaf);
|
status_t ReplaceLeaf(const char *newLeaf);
|
||||||
@@ -41,6 +44,8 @@ class KPath {
|
|||||||
|
|
||||||
status_t Append(const char *toAppend, bool isComponent = true);
|
status_t Append(const char *toAppend, bool isComponent = true);
|
||||||
|
|
||||||
|
status_t Normalize(bool traverseLeafLink);
|
||||||
|
|
||||||
KPath& operator=(const KPath& other);
|
KPath& operator=(const KPath& other);
|
||||||
KPath& operator=(const char* path);
|
KPath& operator=(const char* path);
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ status_t vfs_get_module_path(const char *basePath, const char *moduleName,
|
|||||||
|
|
||||||
/* service call for whoever needs a normalized path */
|
/* service call for whoever needs a normalized path */
|
||||||
status_t vfs_normalize_path(const char *path, char *buffer, size_t bufferSize,
|
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 */
|
/* service call for whoever wants to create a special node */
|
||||||
status_t vfs_create_special_node(const char *path, fs_vnode *subVnode,
|
status_t vfs_create_special_node(const char *path, fs_vnode *subVnode,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2006, Ingo Weinhold, [email protected].
|
* Copyright 2004-2008, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -59,7 +59,8 @@ KPath::~KPath()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
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)
|
if (bufferSize == 0)
|
||||||
bufferSize = B_PATH_NAME_LENGTH;
|
bufferSize = B_PATH_NAME_LENGTH;
|
||||||
@@ -82,7 +83,7 @@ KPath::SetTo(const char* path, bool normalize, size_t bufferSize)
|
|||||||
fBufferSize = bufferSize;
|
fBufferSize = bufferSize;
|
||||||
fBuffer[0] = '\0';
|
fBuffer[0] = '\0';
|
||||||
}
|
}
|
||||||
return SetPath(path, normalize);
|
return SetPath(path, normalize, traverseLeafLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -106,7 +107,7 @@ KPath::InitCheck() const
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
KPath::SetPath(const char *path, bool normalize)
|
KPath::SetPath(const char *path, bool normalize, bool traverseLeafLink)
|
||||||
{
|
{
|
||||||
if (!fBuffer)
|
if (!fBuffer)
|
||||||
return B_NO_INIT;
|
return B_NO_INIT;
|
||||||
@@ -115,6 +116,7 @@ KPath::SetPath(const char *path, bool normalize)
|
|||||||
if (normalize) {
|
if (normalize) {
|
||||||
// normalize path
|
// normalize path
|
||||||
status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
|
status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
|
||||||
|
traverseLeafLink,
|
||||||
team_get_kernel_team_id() == team_get_current_team_id());
|
team_get_kernel_team_id() == team_get_current_team_id());
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
SetPath(NULL);
|
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 *
|
const char *
|
||||||
KPath::Leaf() const
|
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&
|
||||||
KPath::operator=(const KPath& other)
|
KPath::operator=(const KPath& other)
|
||||||
{
|
{
|
||||||
|
|||||||
+102
-135
@@ -2857,6 +2857,91 @@ get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode,
|
|||||||
return fd;
|
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
|
#ifdef ADD_DEBUGGER_COMMANDS
|
||||||
|
|
||||||
|
|
||||||
@@ -4020,6 +4105,7 @@ err:
|
|||||||
\param buffer The buffer into which the normalized path will be written.
|
\param buffer The buffer into which the normalized path will be written.
|
||||||
May be the same one as \a path.
|
May be the same one as \a path.
|
||||||
\param bufferSize The size of \a buffer.
|
\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,
|
\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,
|
otherwise that of the team this thread belongs to. Only relevant,
|
||||||
if the path is relative (to get the CWD).
|
if the path is relative (to get the CWD).
|
||||||
@@ -4027,64 +4113,17 @@ err:
|
|||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
vfs_normalize_path(const char *path, char *buffer, size_t bufferSize,
|
vfs_normalize_path(const char *path, char *buffer, size_t bufferSize,
|
||||||
bool kernel)
|
bool traverseLink, bool kernel)
|
||||||
{
|
{
|
||||||
if (!path || !buffer || bufferSize < 1)
|
if (!path || !buffer || bufferSize < 1)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
TRACE(("vfs_normalize_path(`%s')\n", path));
|
if (path != buffer) {
|
||||||
|
if (strlcpy(buffer, path, bufferSize) >= bufferSize)
|
||||||
// copy the supplied path to the stack, so it can be modified
|
return B_BUFFER_OVERFLOW;
|
||||||
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 the leaf is "." or "..", we directly get the correct directory
|
return normalize_path(buffer, bufferSize, traverseLink, kernel);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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)
|
if (user_strlcpy(path, userPath, B_PATH_NAME_LENGTH) < B_OK)
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
// buffer for the leaf part
|
status_t error = normalize_path(path, pathBuffer.BufferSize(), traverseLink,
|
||||||
KPath leafBuffer(B_PATH_NAME_LENGTH + 1);
|
false);
|
||||||
if (leafBuffer.InitCheck() != B_OK)
|
if (error != B_OK)
|
||||||
return B_NO_MEMORY;
|
return error;
|
||||||
char* leaf = leafBuffer.LockBuffer();
|
|
||||||
|
|
||||||
VNodePutter dirPutter;
|
// copy back to userland
|
||||||
struct vnode* dir = NULL;
|
int len = user_strlcpy(buffer, path, B_PATH_NAME_LENGTH);
|
||||||
status_t error;
|
if (len < 0)
|
||||||
|
return len;
|
||||||
|
if (len >= B_PATH_NAME_LENGTH)
|
||||||
|
return B_BUFFER_OVERFLOW;
|
||||||
|
|
||||||
for (int i = 0; i < B_MAX_SYMLINKS; i++) {
|
return B_OK;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user