VTreeFS library
This commit is contained in:
313
lib/libvtreefs/path.c
Normal file
313
lib/libvtreefs/path.c
Normal file
@@ -0,0 +1,313 @@
|
||||
/* VTreeFS - path.c - by Alen Stojanov and David van Moolenbroek */
|
||||
|
||||
#include "inc.h"
|
||||
|
||||
/*===========================================================================*
|
||||
* access_as_dir *
|
||||
*===========================================================================*/
|
||||
PRIVATE int access_as_dir(struct inode *node, vfs_ucred_t *ucred)
|
||||
{
|
||||
/* Check whether the given inode may be accessed as directory.
|
||||
* Return OK or an appropriate error code.
|
||||
*/
|
||||
mode_t mask;
|
||||
int i;
|
||||
|
||||
/* The inode must be a directory to begin with. */
|
||||
if (!S_ISDIR(node->i_stat.mode)) return ENOTDIR;
|
||||
|
||||
/* The caller must have search access to the directory.
|
||||
* Root always does.
|
||||
*/
|
||||
if (ucred->vu_uid == SUPER_USER) return OK;
|
||||
|
||||
if (ucred->vu_uid == node->i_stat.uid) mask = S_IXUSR;
|
||||
else if (ucred->vu_gid == node->i_stat.gid) mask = S_IXGRP;
|
||||
else {
|
||||
mask = S_IXOTH;
|
||||
|
||||
for (i = 0; i < ucred->vu_ngroups; i++) {
|
||||
if (ucred->vu_sgroups[i] == node->i_stat.gid) {
|
||||
mask = S_IXGRP;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (node->i_stat.mode & mask) ? OK : EACCES;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* next_name *
|
||||
*===========================================================================*/
|
||||
PRIVATE int next_name(char **ptr, char **start, char name[PNAME_MAX+1])
|
||||
{
|
||||
/* Get the next path component from a path.
|
||||
*/
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
for (p = *ptr; *p == '/'; p++);
|
||||
|
||||
*start = p;
|
||||
|
||||
if (*p) {
|
||||
for (i = 0; *p && *p != '/' && i <= PNAME_MAX; p++, i++)
|
||||
name[i] = *p;
|
||||
|
||||
if (i > PNAME_MAX)
|
||||
return ENAMETOOLONG;
|
||||
|
||||
name[i] = 0;
|
||||
} else {
|
||||
strcpy(name, ".");
|
||||
}
|
||||
|
||||
*ptr = p;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* go_up *
|
||||
*===========================================================================*/
|
||||
PRIVATE int go_up(struct inode *node, struct inode **parent)
|
||||
{
|
||||
/* Given a directory inode, progress into the parent directory.
|
||||
*/
|
||||
|
||||
*parent = get_parent_inode(node);
|
||||
|
||||
/* Trapped in a deleted directory? Should not be possible. */
|
||||
if (*parent == NULL)
|
||||
return ENOENT;
|
||||
|
||||
ref_inode(*parent);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* go_down *
|
||||
*===========================================================================*/
|
||||
PRIVATE int go_down(struct inode *parent, char *name, struct inode **child)
|
||||
{
|
||||
/* Given a directory inode and a name, progress into a directory entry.
|
||||
*/
|
||||
int r;
|
||||
|
||||
/* Call the lookup hook, if present, before doing the actual lookup. */
|
||||
if (!is_inode_deleted(parent) && vtreefs_hooks->lookup_hook != NULL) {
|
||||
r = vtreefs_hooks->lookup_hook(parent, name,
|
||||
get_inode_cbdata(parent));
|
||||
if (r != OK) return r;
|
||||
}
|
||||
|
||||
if ((*child = get_inode_by_name(parent, name)) == NULL)
|
||||
return ENOENT;
|
||||
|
||||
ref_inode(*child);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* resolve_link *
|
||||
*===========================================================================*/
|
||||
PRIVATE int resolve_link(struct inode *node, char *pptr, char *tail)
|
||||
{
|
||||
/* Given a symbolic link, resolve and return the contents of the link.
|
||||
*/
|
||||
char path[PATH_MAX];
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
assert(vtreefs_hooks->rdlink_hook != NULL);
|
||||
assert(!is_inode_deleted(node));
|
||||
|
||||
r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path),
|
||||
get_inode_cbdata(node));
|
||||
if (r != OK) return r;
|
||||
|
||||
len = strlen(path);
|
||||
assert(len > 0 && len < sizeof(path));
|
||||
|
||||
if (len + strlen(tail) >= sizeof(path))
|
||||
return ENAMETOOLONG;
|
||||
|
||||
strcat(path, tail);
|
||||
|
||||
strcpy(pptr, path);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* fs_lookup *
|
||||
*===========================================================================*/
|
||||
PUBLIC int fs_lookup(void)
|
||||
{
|
||||
/* Resolve a path string to an inode.
|
||||
*/
|
||||
ino_t dir_ino_nr, root_ino_nr;
|
||||
struct inode *cur_ino, *next_ino, *root_ino;
|
||||
char path[PATH_MAX], name[PNAME_MAX+1];
|
||||
char *ptr, *last;
|
||||
vfs_ucred_t ucred;
|
||||
size_t len;
|
||||
int r, r2, symloop;
|
||||
|
||||
dir_ino_nr = fs_m_in.REQ_DIR_INO;
|
||||
root_ino_nr = fs_m_in.REQ_ROOT_INO;
|
||||
len = fs_m_in.REQ_PATH_LEN;
|
||||
|
||||
/* Fetch the path name. */
|
||||
if (len < 1 || len > PATH_MAX)
|
||||
return EINVAL;
|
||||
|
||||
r = sys_safecopyfrom(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0,
|
||||
(vir_bytes) path, (phys_bytes) len, D);
|
||||
if (r != OK) return r;
|
||||
|
||||
if (path[len-1] != 0) return EINVAL;
|
||||
|
||||
/* Fetch the caller's credentials. */
|
||||
if (fs_m_in.REQ_FLAGS & PATH_GET_UCRED) {
|
||||
assert(fs_m_in.REQ_UCRED_SIZE == sizeof(ucred));
|
||||
|
||||
r = sys_safecopyfrom(fs_m_in.m_source, fs_m_in.REQ_GRANT2, 0,
|
||||
(vir_bytes) &ucred, fs_m_in.REQ_UCRED_SIZE, D);
|
||||
|
||||
if (r != OK)
|
||||
return r;
|
||||
}
|
||||
else {
|
||||
ucred.vu_uid = fs_m_in.REQ_UID;
|
||||
ucred.vu_gid = fs_m_in.REQ_GID;
|
||||
ucred.vu_ngroups = 0;
|
||||
}
|
||||
|
||||
/* Start the actual lookup. */
|
||||
if ((cur_ino = get_inode(dir_ino_nr)) == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/* Chroot'ed environment? */
|
||||
if (root_ino_nr > 0)
|
||||
root_ino = find_inode(root_ino_nr);
|
||||
else
|
||||
root_ino = NULL;
|
||||
|
||||
symloop = 0;
|
||||
|
||||
for (ptr = last = path; ptr[0] != 0; ) {
|
||||
/* There is more path to process. That means that the current
|
||||
* file is now being accessed as a directory. Check type and
|
||||
* permissions.
|
||||
*/
|
||||
if ((r = access_as_dir(cur_ino, &ucred)) != OK)
|
||||
break;
|
||||
|
||||
/* Get the next path component. The result is a non-empty
|
||||
* string.
|
||||
*/
|
||||
if ((r = next_name(&ptr, &last, name)) != OK)
|
||||
break;
|
||||
|
||||
if (!strcmp(name, ".") ||
|
||||
(cur_ino == root_ino && !strcmp(name, "..")))
|
||||
continue;
|
||||
|
||||
if (!strcmp(name, "..")) {
|
||||
if (cur_ino == get_root_inode())
|
||||
r = ELEAVEMOUNT;
|
||||
else
|
||||
r = go_up(cur_ino, &next_ino);
|
||||
} else {
|
||||
r = go_down(cur_ino, name, &next_ino);
|
||||
|
||||
/* Perform symlink resolution if we have to. */
|
||||
if (r == OK && S_ISLNK(next_ino->i_stat.mode) &&
|
||||
(ptr[0] != '\0' ||
|
||||
!(fs_m_in.REQ_FLAGS & PATH_RET_SYMLINK))) {
|
||||
|
||||
if (++symloop == SYMLOOP_MAX) {
|
||||
put_inode(next_ino);
|
||||
|
||||
r = ELOOP;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Resolve the symlink, and append the
|
||||
* remaining unresolved part of the path.
|
||||
*/
|
||||
r = resolve_link(next_ino, path, ptr);
|
||||
|
||||
put_inode(next_ino);
|
||||
|
||||
if (r != OK)
|
||||
break;
|
||||
|
||||
/* If the symlink is absolute, return it to
|
||||
* VFS.
|
||||
*/
|
||||
if (path[0] == '/') {
|
||||
r = ESYMLINK;
|
||||
last = path;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ptr = path;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (r != OK)
|
||||
break;
|
||||
|
||||
/* We have found a new file. Continue from this file. */
|
||||
assert(next_ino != NULL);
|
||||
|
||||
put_inode(cur_ino);
|
||||
|
||||
cur_ino = next_ino;
|
||||
}
|
||||
|
||||
/* If an error occurred, close the file and return error information.
|
||||
*/
|
||||
if (r != OK) {
|
||||
put_inode(cur_ino);
|
||||
|
||||
/* We'd need support for this here. */
|
||||
assert(r != EENTERMOUNT);
|
||||
|
||||
/* Copy back the path if we resolved at least one symlink. */
|
||||
if (symloop > 0 && (r == ELEAVEMOUNT || r == ESYMLINK)) {
|
||||
r2 = sys_safecopyto(fs_m_in.m_source,
|
||||
fs_m_in.REQ_GRANT, 0, (vir_bytes) path,
|
||||
strlen(path) + 1, D);
|
||||
|
||||
if (r2 != OK)
|
||||
r = r2;
|
||||
}
|
||||
|
||||
if (r == ELEAVEMOUNT || r == ESYMLINK) {
|
||||
fs_m_out.RES_OFFSET = (int) (last - path);
|
||||
fs_m_out.RES_SYMLOOP = symloop;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* On success, leave the resulting file open and return its details. */
|
||||
fs_m_out.RES_INODE_NR = get_inode_number(cur_ino);
|
||||
fs_m_out.RES_MODE = cur_ino->i_stat.mode;
|
||||
fs_m_out.RES_FILE_SIZE_HI = 0;
|
||||
fs_m_out.RES_FILE_SIZE_LO = cur_ino->i_stat.size;
|
||||
fs_m_out.RES_UID = cur_ino->i_stat.uid;
|
||||
fs_m_out.RES_GID = cur_ino->i_stat.gid;
|
||||
fs_m_out.RES_DEV = cur_ino->i_stat.dev;
|
||||
|
||||
return OK;
|
||||
}
|
||||
Reference in New Issue
Block a user