* Implemented support for chroot:

- Added a "root" vnode to the io_context. It is used for resolving
    paths and converting nodes to paths instead of sRoot. Some more
    passing around of io_context structures was necessary.
  - Introduced a new lock sIOContextRootLock to protect
    io_context::root. The current uses of io_context::io_mutex
    (put_vnode(), remove_vnode() while holding it) looked too suspicious
    to use that mutex in vnode_path_to_vnode().
  - Added _kern_change_root() syscall and chroot() libroot function.
  - Added chroot coreutils program to the image. Funnily it seems to be
    much easier to set up a little jail than under Linux (just copy
    bash and libroot.so into respective subdirs; mount another pipefs
    if you want pipe support).
    With Haiku allowing direct access to directories via inode IDs
    jailing is obviously not very secure at the moment.
  - Added /var/empty to the image. It will be the chroot target for ssh.
* Changed vfs.cpp:get_cwd() so that the io_context::io_mutex is no
  longer held when calling dir_vnode_to_path().


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24673 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2008-03-30 05:59:54 +00:00
parent 2df0ed7856
commit 360be1fc45
10 changed files with 325 additions and 101 deletions

View File

@ -7,6 +7,7 @@ AddDirectoryToHaikuImage home Desktop ;
AddDirectoryToHaikuImage home config bin ;
AddDirectoryToHaikuImage home config lib ;
AddDirectoryToHaikuImage home mail ;
AddDirectoryToHaikuImage var empty ;
AddDirectoryToHaikuImage var log ;
AddDirectoryToHaikuImage var tmp ;
@ -24,8 +25,8 @@ if $(INCLUDE_GPL_ADDONS) = 1 {
}
BEOS_BIN = "[" addattr alert arp base64 basename bc beep bzip2 cal cat
catattr chgrp chmod chop chown cksum clear clockconfig cmp comm compress
cp copyattr $(X86_ONLY)CortexAddOnHost
catattr chgrp chmod chop chown chroot cksum clear clockconfig cmp comm
compress cp copyattr $(X86_ONLY)CortexAddOnHost
csplit ctags cut date dc dd desklink df diff diff3 dircolors dirname
driveinfo dstcheck du echo eject env error expand expr
expr factor false fdinfo ffm find finddir fmt fold fortune frcode ftp ftpd

View File

@ -1,5 +1,5 @@
/*
* Copyright 2004-2007, Haiku Inc. All Rights Reserved.
* Copyright 2004-2008, Haiku Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _UNISTD_H_
@ -155,6 +155,8 @@ extern pid_t setsid(void);
extern int setpgid(pid_t pid, pid_t pgid);
extern pid_t setpgrp(void);
extern int chroot(const char *path);
/* access permissions */
extern gid_t getegid(void);
extern uid_t geteuid(void);

View File

@ -16,6 +16,7 @@ extern "C" {
#endif
struct file_descriptor;
struct io_context;
struct selectsync;
struct select_info;
@ -28,7 +29,9 @@ struct fd_ops {
struct selectsync *sync);
status_t (*fd_deselect)(struct file_descriptor *, uint8 event,
struct selectsync *sync);
status_t (*fd_read_dir)(struct file_descriptor *, struct dirent *buffer, size_t bufferSize, uint32 *_count);
status_t (*fd_read_dir)(struct io_context* ioContext,
struct file_descriptor *, struct dirent *buffer,
size_t bufferSize, uint32 *_count);
status_t (*fd_rewind_dir)(struct file_descriptor *);
status_t (*fd_read_stat)(struct file_descriptor *, struct stat *);
status_t (*fd_write_stat)(struct file_descriptor *, const struct stat *, int statMask);

View File

@ -90,6 +90,7 @@ extern thread_id _kern_fork(void);
extern pid_t _kern_process_info(pid_t process, int32 which);
extern pid_t _kern_setpgid(pid_t process, pid_t group);
extern pid_t _kern_setsid(void);
extern status_t _kern_change_root(const char *path);
extern thread_id _kern_spawn_thread(int32 (*func)(thread_func, void *),
const char *name, int32 priority, void *data1, void *data2);

View File

@ -39,6 +39,7 @@ struct vnode;
/** The I/O context of a process/team, holds the fd array among others */
typedef struct io_context {
struct vnode *root;
struct vnode *cwd;
mutex io_mutex;
uint32 table_size;
@ -184,6 +185,7 @@ status_t _user_read_index_stat(dev_t device, const char *name, struct stat *stat
status_t _user_remove_index(dev_t device, const char *name);
status_t _user_getcwd(char *buffer, size_t size);
status_t _user_setcwd(int fd, const char *path);
status_t _user_change_root(const char *path);
int _user_open_query(dev_t device, const char *query, size_t queryLength, uint32 flags,
port_id port, int32 token);

View File

@ -89,6 +89,7 @@ StdBinCommands
StdBinCommands
chmod.c
chroot.c
du.c
mkdir.c
pwd.c
@ -96,7 +97,6 @@ StdBinCommands
sort.c
: libfetish.a libroot.so : $(coreutils_rsrc) ;
# chroot.c
# df.c
# hostid.c
# pinky.c

View File

@ -840,13 +840,15 @@ _user_read_dir(int fd, struct dirent *buffer, size_t bufferSize, uint32 maxCount
TRACE(("user_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = %lu)\n", fd, buffer, bufferSize, maxCount));
descriptor = get_fd(get_current_io_context(false), fd);
struct io_context* ioContext = get_current_io_context(false);
descriptor = get_fd(ioContext, fd);
if (descriptor == NULL)
return B_FILE_ERROR;
if (descriptor->ops->fd_read_dir) {
uint32 count = maxCount;
retval = descriptor->ops->fd_read_dir(descriptor, buffer, bufferSize, &count);
retval = descriptor->ops->fd_read_dir(ioContext, descriptor, buffer,
bufferSize, &count);
if (retval >= 0)
retval = count;
} else
@ -1138,13 +1140,15 @@ _kern_read_dir(int fd, struct dirent *buffer, size_t bufferSize, uint32 maxCount
TRACE(("sys_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = %lu)\n",fd, buffer, bufferSize, maxCount));
descriptor = get_fd(get_current_io_context(true), fd);
struct io_context* ioContext = get_current_io_context(true);
descriptor = get_fd(ioContext, fd);
if (descriptor == NULL)
return B_FILE_ERROR;
if (descriptor->ops->fd_read_dir) {
uint32 count = maxCount;
retval = descriptor->ops->fd_read_dir(descriptor, buffer, bufferSize, &count);
retval = descriptor->ops->fd_read_dir(ioContext, descriptor, buffer,
bufferSize, &count);
if (retval >= 0)
retval = count;
} else

View File

@ -1,4 +1,5 @@
/*
* Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
@ -186,6 +187,14 @@ static mutex sVnodeCoveredByMutex;
*/
static mutex sVnodeMutex;
/*! \brief Guards io_context::root.
Must be held when setting or getting the io_context::root field.
The only operation allowed while holding this lock besides getting or
setting the field is inc_vnode_ref_count() on io_context::root.
*/
static benaphore sIOContextRootLock;
#define VNODE_HASH_TABLE_SIZE 1024
static hash_table *sVnodeTable;
static list sUnusedVnodeList;
@ -212,12 +221,15 @@ static status_t file_select(struct file_descriptor *, uint8 event,
struct selectsync *sync);
static status_t file_deselect(struct file_descriptor *, uint8 event,
struct selectsync *sync);
static status_t dir_read(struct file_descriptor *, struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t dir_read(struct vnode *vnode, fs_cookie cookie, struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t dir_read(struct io_context *, struct file_descriptor *,
struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t dir_read(struct io_context* ioContext, struct vnode *vnode,
fs_cookie cookie, struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t dir_rewind(struct file_descriptor *);
static void dir_free_fd(struct file_descriptor *);
static status_t dir_close(struct file_descriptor *);
static status_t attr_dir_read(struct file_descriptor *, struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t attr_dir_read(struct io_context *, struct file_descriptor *,
struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t attr_dir_rewind(struct file_descriptor *);
static void attr_dir_free_fd(struct file_descriptor *);
static status_t attr_dir_close(struct file_descriptor *);
@ -228,11 +240,13 @@ static void attr_free_fd(struct file_descriptor *);
static status_t attr_close(struct file_descriptor *);
static status_t attr_read_stat(struct file_descriptor *, struct stat *);
static status_t attr_write_stat(struct file_descriptor *, const struct stat *, int statMask);
static status_t index_dir_read(struct file_descriptor *, struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t index_dir_read(struct io_context *, struct file_descriptor *,
struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t index_dir_rewind(struct file_descriptor *);
static void index_dir_free_fd(struct file_descriptor *);
static status_t index_dir_close(struct file_descriptor *);
static status_t query_read(struct file_descriptor *, struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t query_read(struct io_context *, struct file_descriptor *,
struct dirent *buffer, size_t bufferSize, uint32 *_count);
static status_t query_rewind(struct file_descriptor *);
static void query_free_fd(struct file_descriptor *);
static status_t query_close(struct file_descriptor *);
@ -245,8 +259,10 @@ static status_t common_path_read_stat(int fd, char *path, bool traverseLeafLink,
struct stat *stat, bool kernel);
static status_t vnode_path_to_vnode(struct vnode *vnode, char *path,
bool traverseLeafLink, int count, struct vnode **_vnode, ino_t *_parentID, int *_type);
static status_t dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize);
bool traverseLeafLink, int count, bool kernel,
struct vnode **_vnode, ino_t *_parentID, int *_type);
static status_t dir_vnode_to_path(struct vnode *vnode, char *buffer,
size_t bufferSize, bool kernel);
static status_t fd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink,
struct vnode **_vnode, ino_t *_parentID, bool kernel);
static void inc_vnode_ref_count(struct vnode *vnode);
@ -1393,6 +1409,38 @@ normalize_flock(struct file_descriptor *descriptor, struct flock *flock)
}
static void
replace_vnode_if_disconnected(struct fs_mount* mount,
struct vnode* vnodeToDisconnect, struct vnode*& vnode,
struct vnode* fallBack, bool lockRootLock)
{
if (lockRootLock)
benaphore_lock(&sIOContextRootLock);
struct vnode* obsoleteVnode = NULL;
if (vnode != NULL && vnode->mount == mount
&& (vnodeToDisconnect == NULL || vnodeToDisconnect == vnode)) {
obsoleteVnode = vnode;
if (vnode == mount->root_vnode) {
// redirect the vnode to the covered vnode
vnode = mount->covers_vnode;
} else
vnode = fallBack;
if (vnode != NULL)
inc_vnode_ref_count(vnode);
}
if (lockRootLock)
benaphore_unlock(&sIOContextRootLock);
if (obsoleteVnode != NULL)
put_vnode(obsoleteVnode);
}
/*! Disconnects all file descriptors that are associated with the
\a vnodeToDisconnect, or if this is NULL, all vnodes of the specified
\a mount object.
@ -1455,20 +1503,10 @@ disconnect_mount_or_vnode_fds(struct fs_mount *mount,
context->io_mutex.holder = thread_get_current_thread_id();
if (context->cwd != NULL && context->cwd->mount == mount
&& (vnodeToDisconnect == NULL
|| vnodeToDisconnect == context->cwd)) {
put_vnode(context->cwd);
// Note: We're only accessing the pointer, not the vnode itself
// in the lines below.
if (context->cwd == mount->root_vnode) {
// redirect the current working directory to the covered vnode
context->cwd = mount->covers_vnode;
inc_vnode_ref_count(context->cwd);
} else
context->cwd = NULL;
}
replace_vnode_if_disconnected(mount, vnodeToDisconnect, context->root,
sRoot, true);
replace_vnode_if_disconnected(mount, vnodeToDisconnect, context->cwd,
sRoot, false);
for (uint32 i = 0; i < context->table_size; i++) {
if (struct file_descriptor *descriptor = context->fds[i]) {
@ -1493,6 +1531,38 @@ disconnect_mount_or_vnode_fds(struct fs_mount *mount,
}
/*! \brief Gets the root node of the current IO context.
If \a kernel is \c true, the kernel IO context will be used.
The caller obtains a reference to the returned node.
*/
struct vnode*
get_root_vnode(bool kernel)
{
if (!kernel) {
// Get current working directory from io context
struct io_context* context = get_current_io_context(kernel);
benaphore_lock(&sIOContextRootLock);
struct vnode* root = context->root;
if (root != NULL)
inc_vnode_ref_count(root);
benaphore_unlock(&sIOContextRootLock);
if (root != NULL)
return root;
// That should never happen.
dprintf("get_root_vnode(): IO context for team %ld doesn't have a "
"root\n", team_get_current_team_id());
}
inc_vnode_ref_count(sRoot);
return sRoot;
}
/*! \brief Resolves a mount point vnode to the volume root vnode it is covered
by.
@ -1652,7 +1722,7 @@ get_dir_path_and_leaf(char *path, char *filename)
static status_t
entry_ref_to_vnode(dev_t mountID, ino_t directoryID, const char *name,
bool traverse, struct vnode **_vnode)
bool traverse, bool kernel, struct vnode **_vnode)
{
char clonedName[B_FILE_NAME_LENGTH + 1];
if (strlcpy(clonedName, name, B_FILE_NAME_LENGTH) >= B_FILE_NAME_LENGTH)
@ -1665,8 +1735,8 @@ entry_ref_to_vnode(dev_t mountID, ino_t directoryID, const char *name,
if (status < 0)
return status;
return vnode_path_to_vnode(directory, clonedName, traverse, 0, _vnode, NULL,
NULL);
return vnode_path_to_vnode(directory, clonedName, traverse, 0, kernel,
_vnode, NULL, NULL);
}
@ -1680,7 +1750,8 @@ entry_ref_to_vnode(dev_t mountID, ino_t directoryID, const char *name,
*/
static status_t
vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
int count, struct vnode **_vnode, ino_t *_parentID, int *_type)
int count, struct io_context *ioContext, struct vnode **_vnode,
ino_t *_parentID, int *_type)
{
status_t status = 0;
ino_t lastParentID = vnode->id;
@ -1721,14 +1792,20 @@ vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
}
// See if the '..' is at the root of a mount and move to the covered
// vnode so we pass the '..' path to the underlying filesystem
if (!strcmp("..", path)
&& vnode->mount->root_vnode == vnode
&& vnode->mount->covers_vnode) {
nextVnode = vnode->mount->covers_vnode;
inc_vnode_ref_count(nextVnode);
put_vnode(vnode);
vnode = nextVnode;
// vnode so we pass the '..' path to the underlying filesystem.
// Also prevent breaking the root of the IO context.
if (strcmp("..", path) == 0) {
if (vnode == ioContext->root) {
// Attempted prison break! Keep it contained.
path = nextPath;
continue;
} else if (vnode->mount->root_vnode == vnode
&& vnode->mount->covers_vnode) {
nextVnode = vnode->mount->covers_vnode;
inc_vnode_ref_count(nextVnode);
put_vnode(vnode);
vnode = nextVnode;
}
}
// Check if we have the right to search the current directory vnode.
@ -1815,8 +1892,11 @@ vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
while (*++path == '/')
;
vnode = sRoot;
benaphore_lock(&sIOContextRootLock);
vnode = ioContext->root;
inc_vnode_ref_count(vnode);
benaphore_unlock(&sIOContextRootLock);
absoluteSymlink = true;
}
@ -1830,7 +1910,7 @@ vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
nextVnode = vnode;
} else {
status = vnode_path_to_vnode(vnode, path, traverseLeafLink,
count + 1, &nextVnode, &lastParentID, _type);
count + 1, ioContext, &nextVnode, &lastParentID, _type);
}
free(buffer);
@ -1866,6 +1946,16 @@ vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
}
static status_t
vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
int count, bool kernel, struct vnode **_vnode, ino_t *_parentID,
int *_type)
{
return vnode_path_to_vnode(vnode, path, traverseLeafLink, count,
get_current_io_context(kernel), _vnode, _parentID, _type);
}
static status_t
path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
ino_t *_parentID, bool kernel)
@ -1889,8 +1979,7 @@ path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
while (*++path == '/')
;
start = sRoot;
inc_vnode_ref_count(start);
start = get_root_vnode(kernel);
if (*path == '\0') {
*_vnode = start;
@ -1910,7 +1999,8 @@ path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
return B_ERROR;
}
return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID, NULL);
return vnode_path_to_vnode(start, path, traverseLink, 0, kernel, _vnode,
_parentID, NULL);
}
@ -2014,7 +2104,8 @@ vnode_and_path_to_dir_vnode(struct vnode* vnode, char *path,
inc_vnode_ref_count(vnode);
// vnode_path_to_vnode() always decrements the ref count
return vnode_path_to_vnode(vnode, path, true, 0, _vnode, NULL, NULL);
return vnode_path_to_vnode(vnode, path, true, 0, kernel, _vnode, NULL,
NULL);
}
@ -2022,7 +2113,7 @@ vnode_and_path_to_dir_vnode(struct vnode* vnode, char *path,
*/
static status_t
get_vnode_name(struct vnode *vnode, struct vnode *parent, struct dirent *buffer,
size_t bufferSize)
size_t bufferSize, struct io_context* ioContext)
{
if (bufferSize < sizeof(struct dirent))
return B_BAD_VALUE;
@ -2056,7 +2147,8 @@ get_vnode_name(struct vnode *vnode, struct vnode *parent, struct dirent *buffer,
if (status >= B_OK) {
while (true) {
uint32 num = 1;
status = dir_read(parent, cookie, buffer, bufferSize, &num);
status = dir_read(ioContext, parent, cookie, buffer, bufferSize,
&num);
if (status < B_OK)
break;
if (num == 0) {
@ -2081,12 +2173,13 @@ get_vnode_name(struct vnode *vnode, struct vnode *parent, struct dirent *buffer,
static status_t
get_vnode_name(struct vnode *vnode, struct vnode *parent, char *name,
size_t nameSize)
size_t nameSize, bool kernel)
{
char buffer[sizeof(struct dirent) + B_FILE_NAME_LENGTH];
struct dirent *dirent = (struct dirent *)buffer;
status_t status = get_vnode_name(vnode, parent, buffer, sizeof(buffer));
status_t status = get_vnode_name(vnode, parent, buffer, sizeof(buffer),
get_current_io_context(kernel));
if (status != B_OK)
return status;
@ -2113,7 +2206,8 @@ get_vnode_name(struct vnode *vnode, struct vnode *parent, char *name,
in the calling function (it's not done here because of efficiency)
*/
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,
bool kernel)
{
FUNCTION(("dir_vnode_to_path(%p, %p, %lu)\n", vnode, buffer, bufferSize));
@ -2144,6 +2238,8 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize)
path[--insert] = '\0';
struct io_context* ioContext = get_current_io_context(kernel);
while (true) {
// the name buffer is also used for fs_read_dir()
char nameBuffer[sizeof(struct dirent) + B_FILE_NAME_LENGTH];
@ -2153,14 +2249,20 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize)
int type;
// lookup the parent vnode
status = FS_CALL(vnode, lookup)(vnode->mount->cookie, vnode->private_node, "..",
&parentID, &type);
if (status < B_OK)
goto out;
if (vnode == ioContext->root) {
// we hit the IO context root
parentVnode = vnode;
inc_vnode_ref_count(vnode);
} else {
status = FS_CALL(vnode, lookup)(vnode->mount->cookie,
vnode->private_node, "..", &parentID, &type);
if (status < B_OK)
goto out;
mutex_lock(&sVnodeMutex);
parentVnode = lookup_vnode(vnode->device, parentID);
mutex_unlock(&sVnodeMutex);
mutex_lock(&sVnodeMutex);
parentVnode = lookup_vnode(vnode->device, parentID);
mutex_unlock(&sVnodeMutex);
}
if (parentVnode == NULL) {
panic("dir_vnode_to_path: could not lookup vnode (mountid 0x%lx vnid 0x%Lx)\n",
@ -2171,7 +2273,7 @@ dir_vnode_to_path(struct vnode *vnode, char *buffer, size_t bufferSize)
// get the node's name
status = get_vnode_name(vnode, parentVnode, (struct dirent*)nameBuffer,
sizeof(nameBuffer));
sizeof(nameBuffer), ioContext);
// resolve a volume root to its mount point
mountPoint = resolve_volume_root_to_mount_point(parentVnode);
@ -2344,7 +2446,7 @@ fd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink,
return B_FILE_ERROR;
if (path != NULL) {
return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0,
return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0, kernel,
_vnode, _parentID, NULL);
}
@ -2677,6 +2779,7 @@ dump_io_context(int argc, char **argv)
context = get_current_io_context(true);
kprintf("I/O CONTEXT: %p\n", context);
kprintf(" root vnode:\t%p\n", context->root);
kprintf(" cwd vnode:\t%p\n", context->cwd);
kprintf(" used fds:\t%lu\n", context->num_used_fds);
kprintf(" max fds:\t%lu\n", context->table_size);
@ -3228,7 +3331,7 @@ extern "C" status_t
vfs_entry_ref_to_vnode(dev_t mountID, ino_t directoryID,
const char *name, struct vnode **_vnode)
{
return entry_ref_to_vnode(mountID, directoryID, name, false, _vnode);
return entry_ref_to_vnode(mountID, directoryID, name, false, true, _vnode);
}
@ -3286,7 +3389,8 @@ vfs_get_fs_node_from_path(dev_t mountID, const char *path, bool kernel,
else {
inc_vnode_ref_count(vnode);
// vnode_path_to_vnode() releases a reference to the starting vnode
status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL, NULL);
status = vnode_path_to_vnode(vnode, buffer, true, 0, kernel, &vnode,
NULL, NULL);
}
put_mount(mount);
@ -3391,7 +3495,8 @@ vfs_get_module_path(const char *basePath, const char *moduleName,
path[length] = '\0';
moduleName = nextPath;
status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL, &type);
status = vnode_path_to_vnode(dir, path, true, 0, true, &file, NULL,
&type);
if (status < B_OK) {
// vnode_path_to_vnode() has already released the reference to dir
return status;
@ -3481,8 +3586,10 @@ vfs_normalize_path(const char *path, char *buffer, size_t bufferSize,
// 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, NULL);
if (isDir) {
error = vnode_path_to_vnode(dirNode, leaf, false, 0, kernel, &dirNode,
NULL, NULL);
}
if (error != B_OK) {
TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n",
strerror(error)));
@ -3490,7 +3597,7 @@ vfs_normalize_path(const char *path, char *buffer, size_t bufferSize,
}
// get the directory path
error = dir_vnode_to_path(dirNode, buffer, bufferSize);
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)));
@ -3678,7 +3785,7 @@ vfs_stat_vnode(struct vnode *vnode, struct stat *stat)
status_t
vfs_get_vnode_name(struct vnode *vnode, char *name, size_t nameSize)
{
return get_vnode_name(vnode, NULL, name, nameSize);
return get_vnode_name(vnode, NULL, name, nameSize, true);
}
@ -3697,7 +3804,7 @@ vfs_entry_ref_to_path(dev_t device, ino_t inode, const char *leaf,
if (leaf && (strcmp(leaf, ".") == 0 || strcmp(leaf, "..") == 0)) {
// special cases "." and "..": we can directly get the vnode of the
// referenced directory
status = entry_ref_to_vnode(device, inode, leaf, false, &vnode);
status = entry_ref_to_vnode(device, inode, leaf, false, true, &vnode);
leaf = NULL;
} else
status = get_vnode(device, inode, &vnode, true, false);
@ -3705,7 +3812,7 @@ vfs_entry_ref_to_path(dev_t device, ino_t inode, const char *leaf,
return status;
// get the directory path
status = dir_vnode_to_path(vnode, path, pathLength);
status = dir_vnode_to_path(vnode, path, pathLength, true);
put_vnode(vnode);
// we don't need the vnode anymore
if (status < B_OK)
@ -3820,6 +3927,12 @@ vfs_new_io_context(void *_parentContext)
mutex_lock(&parentContext->io_mutex);
benaphore_lock(&sIOContextRootLock);
context->root = parentContext->root;
if (context->root)
inc_vnode_ref_count(context->root);
benaphore_unlock(&sIOContextRootLock);
context->cwd = parentContext->cwd;
if (context->cwd)
inc_vnode_ref_count(context->cwd);
@ -3840,8 +3953,12 @@ vfs_new_io_context(void *_parentContext)
mutex_unlock(&parentContext->io_mutex);
} else {
context->root = sRoot;
context->cwd = sRoot;
if (context->root)
inc_vnode_ref_count(context->root);
if (context->cwd)
inc_vnode_ref_count(context->cwd);
}
@ -3861,6 +3978,9 @@ vfs_free_io_context(void *_ioContext)
struct io_context *context = (struct io_context *)_ioContext;
uint32 i;
if (context->root)
dec_vnode_ref_count(context->root, false);
if (context->cwd)
dec_vnode_ref_count(context->cwd, false);
@ -4071,6 +4191,9 @@ vfs_init(kernel_args *args)
if (mutex_init(&sVnodeMutex, "vfs_vnode_lock") < 0)
panic("vfs_init: error allocating vnode lock\n");
if (benaphore_init(&sIOContextRootLock, "io_context::root lock") < 0)
panic("vfs_init: error allocating io_context::root lock\n");
if (block_cache_init() != B_OK)
return B_ERROR;
@ -4274,7 +4397,8 @@ file_open_entry_ref(dev_t mountID, ino_t directoryID, const char *name,
mountID, directoryID, name, openMode));
// get the vnode matching the entry_ref
status = entry_ref_to_vnode(mountID, directoryID, name, traverse, &vnode);
status = entry_ref_to_vnode(mountID, directoryID, name, traverse, kernel,
&vnode);
if (status < B_OK)
return status;
@ -4521,9 +4645,10 @@ dir_open_entry_ref(dev_t mountID, ino_t parentID, const char *name, bool kernel)
return B_BAD_VALUE;
// get the vnode matching the entry_ref/node_ref
if (name)
status = entry_ref_to_vnode(mountID, parentID, name, true, &vnode);
else
if (name) {
status = entry_ref_to_vnode(mountID, parentID, name, true, kernel,
&vnode);
} else
status = get_vnode(mountID, parentID, &vnode, true, false);
if (status < B_OK)
return status;
@ -4590,14 +4715,17 @@ dir_free_fd(struct file_descriptor *descriptor)
static status_t
dir_read(struct file_descriptor *descriptor, struct dirent *buffer, size_t bufferSize, uint32 *_count)
dir_read(struct io_context* ioContext, struct file_descriptor *descriptor,
struct dirent *buffer, size_t bufferSize, uint32 *_count)
{
return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count);
return dir_read(ioContext, descriptor->u.vnode, descriptor->cookie, buffer,
bufferSize, _count);
}
static void
fix_dirent(struct vnode *parent, struct dirent *entry)
fix_dirent(struct vnode *parent, struct dirent *entry,
struct io_context* ioContext)
{
// set d_pdev and d_pino
entry->d_pdev = parent->device;
@ -4611,14 +4739,20 @@ fix_dirent(struct vnode *parent, struct dirent *entry)
inc_vnode_ref_count(parent);
// vnode_path_to_vnode() puts the node
// ".." is guaranteed to to be clobbered by this call
struct vnode *vnode;
status_t status = vnode_path_to_vnode(parent, (char*)"..", false, 0,
&vnode, NULL, NULL);
// Make sure the IO context root is not bypassed.
if (parent == ioContext->root) {
entry->d_dev = parent->device;
entry->d_ino = parent->id;
} else {
// ".." is guaranteed not to be clobbered by this call
struct vnode *vnode;
status_t status = vnode_path_to_vnode(parent, (char*)"..", false, 0,
ioContext, &vnode, NULL, NULL);
if (status == B_OK) {
entry->d_dev = vnode->device;
entry->d_ino = vnode->id;
if (status == B_OK) {
entry->d_dev = vnode->device;
entry->d_ino = vnode->id;
}
}
} else {
// resolve mount points
@ -4641,7 +4775,8 @@ fix_dirent(struct vnode *parent, struct dirent *entry)
static status_t
dir_read(struct vnode *vnode, fs_cookie cookie, struct dirent *buffer, size_t bufferSize, uint32 *_count)
dir_read(struct io_context* ioContext, struct vnode *vnode, fs_cookie cookie,
struct dirent *buffer, size_t bufferSize, uint32 *_count)
{
if (!FS_CALL(vnode, read_dir))
return EOPNOTSUPP;
@ -4653,7 +4788,7 @@ dir_read(struct vnode *vnode, fs_cookie cookie, struct dirent *buffer, size_t bu
// we need to adjust the read dirents
if (*_count > 0) {
// XXX: Currently reading only one dirent is supported. Make this a loop!
fix_dirent(vnode, buffer);
fix_dirent(vnode, buffer, ioContext);
}
return error;
@ -5231,7 +5366,8 @@ attr_dir_free_fd(struct file_descriptor *descriptor)
static status_t
attr_dir_read(struct file_descriptor *descriptor, struct dirent *buffer, size_t bufferSize, uint32 *_count)
attr_dir_read(struct io_context* ioContext, struct file_descriptor *descriptor,
struct dirent *buffer, size_t bufferSize, uint32 *_count)
{
struct vnode *vnode = descriptor->u.vnode;
@ -5590,7 +5726,8 @@ index_dir_free_fd(struct file_descriptor *descriptor)
static status_t
index_dir_read(struct file_descriptor *descriptor, struct dirent *buffer, size_t bufferSize, uint32 *_count)
index_dir_read(struct io_context* ioContext, struct file_descriptor *descriptor,
struct dirent *buffer, size_t bufferSize, uint32 *_count)
{
struct fs_mount *mount = descriptor->u.mount;
@ -5781,7 +5918,8 @@ query_free_fd(struct file_descriptor *descriptor)
static status_t
query_read(struct file_descriptor *descriptor, struct dirent *buffer, size_t bufferSize, uint32 *_count)
query_read(struct io_context *ioContext, struct file_descriptor *descriptor,
struct dirent *buffer, size_t bufferSize, uint32 *_count)
{
struct fs_mount *mount = descriptor->u.mount;
@ -6029,8 +6167,13 @@ fs_mount(char *path, const char *device, const char *fsName, uint32 flags,
mount->covers_vnode->covered_by = mount->root_vnode;
mutex_unlock(&sVnodeCoveredByMutex);
if (!sRoot)
if (!sRoot) {
sRoot = mount->root_vnode;
benaphore_lock(&sIOContextRootLock);
get_current_io_context(true)->root = sRoot;
benaphore_unlock(&sIOContextRootLock);
inc_vnode_ref_count(sRoot);
}
// supply the partition (if any) with the mount cookie and mark it mounted
if (partition) {
@ -6414,12 +6557,18 @@ get_cwd(char *buffer, size_t size, bool kernel)
mutex_lock(&context->io_mutex);
if (context->cwd)
status = dir_vnode_to_path(context->cwd, buffer, size);
else
status = B_ERROR;
struct vnode* vnode = context->cwd;
if (vnode)
inc_vnode_ref_count(vnode);
mutex_unlock(&context->io_mutex);
if (vnode) {
status = dir_vnode_to_path(vnode, buffer, size, kernel);
put_vnode(vnode);
} else
status = B_ERROR;
return status;
}
@ -7370,8 +7519,8 @@ _user_normalize_path(const char* userPath, bool traverseLink, char* buffer)
inc_vnode_ref_count(dir);
struct vnode* fileVnode;
int type;
error = vnode_path_to_vnode(dir, path, false, 0, &fileVnode, NULL,
&type);
error = vnode_path_to_vnode(dir, path, false, 0, false, &fileVnode,
NULL, &type);
if (error != B_OK)
return error;
VNodePutter fileVnodePutter(fileVnode);
@ -7382,8 +7531,8 @@ _user_normalize_path(const char* userPath, bool traverseLink, char* buffer)
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, &nextDir, NULL,
NULL);
error = vnode_path_to_vnode(dir, leaf, false, 0, false,
&nextDir, NULL, NULL);
if (error != B_OK)
return error;
dir = nextDir;
@ -7392,7 +7541,7 @@ _user_normalize_path(const char* userPath, bool traverseLink, char* buffer)
}
// get the directory path
error = dir_vnode_to_path(dir, path, B_PATH_NAME_LENGTH);
error = dir_vnode_to_path(dir, path, B_PATH_NAME_LENGTH, false);
if (error != B_OK)
return error;
@ -7552,7 +7701,7 @@ _user_open_parent_dir(int fd, char *userName, size_t nameLength)
char _buffer[sizeof(struct dirent) + B_FILE_NAME_LENGTH];
struct dirent *buffer = (struct dirent*)_buffer;
status_t status = get_vnode_name(dirVNode, parentVNode, buffer,
sizeof(_buffer));
sizeof(_buffer), get_current_io_context(false));
if (status != B_OK)
return status;
@ -8133,6 +8282,45 @@ _user_setcwd(int fd, const char *userPath)
}
status_t
_user_change_root(const char *userPath)
{
// only root is allowed to chroot()
if (geteuid() != 0)
return EPERM;
// alloc path buffer
KPath pathBuffer(B_PATH_NAME_LENGTH);
if (pathBuffer.InitCheck() != B_OK)
return B_NO_MEMORY;
// copy userland path to kernel
char *path = pathBuffer.LockBuffer();
if (userPath != NULL) {
if (!IS_USER_ADDRESS(userPath)
|| user_strlcpy(path, userPath, B_PATH_NAME_LENGTH) < B_OK)
return B_BAD_ADDRESS;
}
// get the vnode
struct vnode* vnode;
status_t status = path_to_vnode(path, true, &vnode, NULL, false);
if (status != B_OK)
return status;
// set the new root
struct io_context* context = get_current_io_context(false);
benaphore_lock(&sIOContextRootLock);
struct vnode* oldRoot = context->root;
context->root = vnode;
benaphore_unlock(&sIOContextRootLock);
put_vnode(oldRoot);
return B_OK;
}
int
_user_open_query(dev_t device, const char *userQuery, size_t queryLength,
uint32 flags, port_id port, int32 token)

View File

@ -6,6 +6,7 @@ MergeObject posix_unistd.o :
access.c
alarm.c
chown.c
chroot.cpp
close.c
conf.c
directory.c

View File

@ -0,0 +1,22 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <syscalls.h>
#include <errno.h>
#include <unistd.h>
int
chroot(const char *path)
{
status_t error = _kern_change_root(path);
if (error != B_OK) {
errno = error;
return -1;
}
return 0;
}