[LibOS] Rewrite rename* syscalls

Present implementation is utterly broken, does not work even
in simplest cases.
This commit is contained in:
borysp
2019-09-03 21:52:55 +02:00
committed by Michał Kowalczyk
parent e83a5ec6fe
commit e23684bc3d
8 changed files with 131 additions and 181 deletions
+12 -6
View File
@@ -375,14 +375,15 @@ int __path_lookupat (struct shim_dentry * start, const char * path, int flags,
int path_lookupat (struct shim_dentry * start, const char * name, int flags,
struct shim_dentry ** dent, struct shim_mount *fs);
/* This function initializes dir to before a search, to either point
* to the current working directory (if dfd == AT_FDCWD), or to the handle pointed to by dfd,
* depending on the argument.
/*
* This function returns a dentry (in *dir) from a handle corresponding to dirfd.
* If dirfd == AT_FDCWD returns current working directory.
*
* Returns -EBADF if dfd is <0 or not a valid handle.
* Returns -ENOTDIR if dfd is not a directory.
* Returned dentry must be a directory.
*
* Increments dentry ref count by one.
*/
int path_startat (int dfd, struct shim_dentry ** dir);
int get_dirfd_dentry(int dirfd, struct shim_dentry** dir);
/* Open path with given flags, in mode, similar to Unix open.
*
@@ -529,6 +530,11 @@ __lookup_dcache (struct shim_dentry * start, const char * name, int namelen,
*/
int __del_dentry_tree(struct shim_dentry * root);
/*
* Returns true if `anc` is an ancestor of `dent`.
*/
bool dentry_is_ancestor(struct shim_dentry* anc, struct shim_dentry* dent);
/* XXX: Future work: current dcache never shrinks. Would be nice
* to be able to do something like LRU under space pressure, although
* for a single app, this may be over-kill. */
+12 -9
View File
@@ -1252,22 +1252,23 @@ out:
return ret;
}
static int chroot_rename (struct shim_dentry * old, struct shim_dentry * new)
{
static int chroot_rename(struct shim_dentry* old, struct shim_dentry* new) {
int ret;
struct shim_file_data * old_data;
if ((ret = try_create_data(old, NULL, 0, &old_data)) < 0)
struct shim_file_data* old_data;
if ((ret = try_create_data(old, NULL, 0, &old_data)) < 0) {
return ret;
}
struct shim_file_data * new_data;
if ((ret = try_create_data(new, NULL, 0, &new_data)) < 0)
struct shim_file_data* new_data;
if ((ret = try_create_data(new, NULL, 0, &new_data)) < 0) {
return ret;
}
PAL_HANDLE pal_hdl = DkStreamOpen(qstrgetstr(&old_data->host_uri),
0, 0, 0, 0);
if (!pal_hdl)
PAL_HANDLE pal_hdl = DkStreamOpen(qstrgetstr(&old_data->host_uri), 0, 0, 0, 0);
if (!pal_hdl) {
return -PAL_ERRNO;
}
if (!DkStreamChangeName(pal_hdl, qstrgetstr(&new_data->host_uri))) {
DkObjectClose(pal_hdl);
@@ -1278,6 +1279,8 @@ static int chroot_rename (struct shim_dentry * old, struct shim_dentry * new)
old->mode = NO_MODE;
old_data->mode = 0;
new->type = old->type;
DkObjectClose(pal_hdl);
atomic_inc(&old_data->version);
+10
View File
@@ -314,6 +314,16 @@ int __del_dentry_tree(struct shim_dentry * root) {
return 0;
}
bool dentry_is_ancestor(struct shim_dentry* anc, struct shim_dentry* dent) {
while (dent) {
if (dent == anc) {
return true;
}
dent = dent->parent;
}
return false;
}
BEGIN_CP_FUNC(dentry)
{
assert(size == sizeof(struct shim_dentry));
+23 -31
View File
@@ -487,7 +487,7 @@ int path_lookupat (struct shim_dentry * start, const char * name, int flags,
*
* The start dentry specifies where to begin the search, and can be null. If
* specified, we assume the caller has incremented the ref count on the start,
* but not the associated mount (probably using path_startat)
* but not the associated mount (probably using get_dirfd_dentry)
*
* hdl is an optional argument; if passed in, it is initialized to
* refer to the opened path.
@@ -824,38 +824,30 @@ int list_directory_handle (struct shim_dentry * dent, struct shim_handle * hdl)
return 0;
}
/* This function initializes dir to before a search, to either point
* to the current working directory (if dfd == AT_FDCWD), or to the handle
* pointed to by dfd, depending on the argument.
*
* Increments dentry ref count by one.
*
* Returns -EBADF if dfd is <0 or not a valid handle.
* Returns -ENOTDIR if dfd is not a directory.
*/
int path_startat (int dfd, struct shim_dentry ** dir)
{
if (dfd == AT_FDCWD) {
struct shim_thread * cur = get_cur_thread();
int get_dirfd_dentry(int dirfd, struct shim_dentry** dir) {
if (dirfd == AT_FDCWD) {
struct shim_thread* cur = get_cur_thread();
get_dentry(cur->cwd);
*dir = cur->cwd;
return 0;
} else if (dfd < 0) {
return -EBADF;
} else {
struct shim_handle * hdl = get_fd_handle(dfd, NULL, NULL);
if (!hdl)
return -EBADF;
if (hdl->type != TYPE_DIR) {
put_handle(hdl);
return -ENOTDIR;
}
get_dentry(hdl->dentry);
put_handle(hdl);
*dir = hdl->dentry;
return 0;
}
if (dirfd < 0) {
return -EBADF;
}
struct shim_handle* hdl = get_fd_handle(dirfd, NULL, NULL);
if (!hdl) {
return -EBADF;
}
if (hdl->type != TYPE_DIR) {
put_handle(hdl);
return -ENOTDIR;
}
get_dentry(hdl->dentry);
*dir = hdl->dentry;
put_handle(hdl);
return 0;
}
+1 -1
View File
@@ -61,7 +61,7 @@ int shim_do_faccessat (int dfd, const char * filename, mode_t mode)
struct shim_dentry * dir = NULL, * dent = NULL;
int ret = 0;
if ((ret = path_startat(dfd, &dir)) < 0)
if ((ret = get_dirfd_dentry(dfd, &dir)) < 0)
return ret;
ret = path_lookupat(dir, filename, LOOKUP_ACCESS|LOOKUP_FOLLOW, &dent, NULL);
+71 -132
View File
@@ -35,6 +35,7 @@
#include <errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <asm/mman.h>
@@ -87,7 +88,7 @@ int shim_do_unlinkat (int dfd, const char * pathname, int flag)
struct shim_dentry * dir = NULL, * dent = NULL;
int ret = 0;
if ((ret = path_startat(dfd, &dir)) < 0)
if ((ret = get_dirfd_dentry(dfd, &dir)) < 0)
return ret;
if ((ret = path_lookupat(dir, pathname, LOOKUP_OPEN, &dent, NULL)) < 0)
@@ -141,7 +142,7 @@ int shim_do_mkdirat (int dfd, const char * pathname, int mode)
struct shim_dentry * dir = NULL;
int ret = 0;
if ((ret = path_startat(dfd, &dir)) < 0)
if ((ret = get_dirfd_dentry(dfd, &dir)) < 0)
return ret;
ret = open_namei(NULL, dir, pathname, O_CREAT|O_EXCL|O_DIRECTORY,
@@ -235,7 +236,7 @@ int shim_do_fchmodat (int dfd, const char * filename, mode_t mode)
struct shim_dentry * dir = NULL, * dent = NULL;
int ret = 0;
if ((ret = path_startat(dfd, &dir)) < 0)
if ((ret = get_dirfd_dentry(dfd, &dir)) < 0)
return ret;
if ((ret = path_lookupat(dir, filename, LOOKUP_OPEN, &dent, NULL)) < 0)
@@ -315,7 +316,7 @@ int shim_do_fchownat (int dfd, const char * filename, uid_t uid, gid_t gid,
struct shim_dentry * dir = NULL, * dent = NULL;
int ret = 0;
if ((ret = path_startat(dfd, &dir)) < 0)
if ((ret = get_dirfd_dentry(dfd, &dir)) < 0)
return ret;
if ((ret = path_lookupat(dir, filename, LOOKUP_OPEN, &dent, NULL)) < 0)
@@ -574,119 +575,94 @@ static ssize_t handle_copy (struct shim_handle * hdli, off_t * offseti,
return bytes;
}
static int do_rename (struct shim_dentry * old_dent,
struct shim_dentry * new_dent)
{
int ret = 0;
if (old_dent->fs && old_dent->fs->d_ops &&
old_dent->fs->d_ops->rename &&
old_dent->type == new_dent->type) {
ret = old_dent->fs->d_ops->rename(old_dent, new_dent);
if (!ret) {
old_dent->state |= DENTRY_NEGATIVE;
new_dent->state &= ~DENTRY_NEGATIVE;
goto out;
}
if (ret == -EACCES)
goto out;
static int do_rename(struct shim_dentry* old_dent, struct shim_dentry* new_dent) {
if ((old_dent->type != S_IFREG)
|| (!(new_dent->state & DENTRY_NEGATIVE) && (new_dent->type != S_IFREG))) {
/* Current implementation of fs does not allow for renaming anything but regular files */
return -ENOSYS;
}
if (!(new_dent->state & DENTRY_NEGATIVE)) {
if (!new_dent->parent ||
!new_dent->fs || !new_dent->fs->d_ops ||
!new_dent->fs->d_ops->unlink) {
ret = -EACCES;
goto out;
}
if (old_dent->fs != new_dent->fs) {
/* Disallow cross mount renames */
return -EXDEV;
}
if ((ret = new_dent->fs->d_ops->unlink(new_dent->parent,
new_dent)) < 0)
goto out;
new_dent->state |= DENTRY_NEGATIVE;
if (!old_dent->fs || !old_dent->fs->d_ops || !old_dent->fs->d_ops->rename) {
return -EPERM;
}
/* TODO: we are not able to handle directory copy yet */
if (old_dent->state & DENTRY_ISDIRECTORY) {
ret = -ENOSYS;
goto out;
}
struct shim_handle * old_hdl = NULL, * new_hdl = NULL;
if (!(old_hdl = get_new_handle())) {
ret = -ENOMEM;
goto out_hdl;
}
if ((ret = dentry_open(old_hdl, old_dent, O_RDONLY)) < 0)
goto out_hdl;
if (!(new_hdl = get_new_handle())) {
ret = -ENOMEM;
goto out_hdl;
}
if ((ret = dentry_open(new_hdl, new_dent, O_WRONLY|O_CREAT)) < 0)
goto out_hdl;
off_t old_offset = 0, new_offset = 0;
if ((ret = handle_copy(old_hdl, &old_offset,
new_hdl, &new_offset, -1)) < 0) {
if (new_dent->fs && new_dent->fs->d_ops &&
new_dent->fs->d_ops->unlink) {
ret = new_dent->fs->d_ops->unlink(new_dent->parent,
new_dent);
if (!(new_dent->state & DENTRY_NEGATIVE)) {
if (!(new_dent->state & DENTRY_ISDIRECTORY)) {
return -ENOTDIR;
}
if (new_dent->nchildren > 0) {
return -ENOTEMPTY;
}
} else {
/* destination is a negative dentry and needs to be marked as a directory,
* since source is a directory */
new_dent->state |= DENTRY_ISDIRECTORY;
}
goto out_hdl;
} else if (new_dent->state & DENTRY_ISDIRECTORY) {
return -EISDIR;
}
new_dent->state &= ~DENTRY_NEGATIVE;
if (old_dent->fs && old_dent->fs->d_ops &&
old_dent->fs->d_ops->unlink) {
if ((ret = old_dent->fs->d_ops->unlink(old_dent->parent,
old_dent)) < 0)
goto out_hdl;
if (dentry_is_ancestor(old_dent, new_dent) || dentry_is_ancestor(new_dent, old_dent)) {
return -EINVAL;
}
old_dent->state |= DENTRY_NEGATIVE;
/* TODO: Add appropriate checks for hardlinks once they get implemented. */
out_hdl:
if (old_hdl) {
put_handle(old_hdl);
int ret = old_dent->fs->d_ops->rename(old_dent, new_dent);
if (!ret) {
old_dent->state |= DENTRY_NEGATIVE;
new_dent->state &= ~DENTRY_NEGATIVE;
}
if (new_hdl) {
put_handle(new_hdl);
}
out:
return ret;
}
int shim_do_rename (const char * oldname, const char * newname)
{
struct shim_dentry * old_dent = NULL, * new_dent = NULL;
int shim_do_rename(const char* oldpath, const char* newpath) {
return shim_do_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath);
}
int shim_do_renameat(int olddirfd, const char* oldpath, int newdirfd, const char* newpath) {
struct shim_dentry* old_dir_dent = NULL;
struct shim_dentry* old_dent = NULL;
struct shim_dentry* new_dir_dent = NULL;
struct shim_dentry* new_dent = NULL;
int ret = 0;
if ((ret = path_lookupat(NULL, oldname, LOOKUP_OPEN, &old_dent, NULL)) < 0)
if (!oldpath || test_user_string(oldpath) || !newpath || test_user_string(newpath)) {
return -EFAULT;
}
if ((ret = get_dirfd_dentry(olddirfd, &old_dir_dent)) < 0) {
goto out;
}
if ((ret = path_lookupat(old_dir_dent, oldpath, LOOKUP_OPEN, &old_dent, NULL)) < 0) {
goto out;
}
if (old_dent->state & DENTRY_NEGATIVE) {
ret = -ENOENT;
goto out;
}
if ((ret = path_lookupat(NULL, newname, LOOKUP_OPEN|LOOKUP_CREATE,
&new_dent, NULL)) < 0) {
// It is now ok for pathlookupat to return ENOENT with a negative DETRY
if (!(ret == -ENOENT && new_dent
&& (new_dent->state & (DENTRY_NEGATIVE|DENTRY_VALID))))
if ((ret = get_dirfd_dentry(newdirfd, &new_dir_dent)) < 0) {
goto out;
}
ret = path_lookupat(new_dir_dent, newpath, LOOKUP_OPEN|LOOKUP_CREATE, &new_dent, NULL);
if (ret < 0) {
if (ret != -ENOENT
|| !new_dent
|| (new_dent->state & (DENTRY_NEGATIVE|DENTRY_VALID))
!= (DENTRY_NEGATIVE|DENTRY_VALID)) {
goto out;
}
}
// Both dentries should have a ref count of at least 2 at this point
@@ -696,49 +672,12 @@ int shim_do_rename (const char * oldname, const char * newname)
ret = do_rename(old_dent, new_dent);
out:
if (old_dir_dent)
put_dentry(old_dir_dent);
if (old_dent)
put_dentry(old_dent);
if (new_dent)
put_dentry(new_dent);
return ret;
}
int shim_do_renameat (int olddfd, const char * pathname, int newdfd,
const char * newname)
{
struct shim_dentry * old_dir = NULL, * old_dent = NULL;
struct shim_dentry * new_dir = NULL, * new_dent = NULL;
int ret = 0;
if ((ret = path_startat(olddfd, &old_dir)) < 0)
goto out;
if ((ret = path_lookupat(old_dir, pathname, LOOKUP_OPEN, &old_dent, NULL)) < 0)
goto out;
if (old_dent->state & DENTRY_NEGATIVE) {
ret = -ENOENT;
goto out;
}
if ((ret = path_startat(newdfd, &new_dir)) < 0)
goto out;
if ((ret = path_lookupat(new_dir, newname, LOOKUP_OPEN|LOOKUP_CREATE,
&new_dent, NULL)) < 0)
goto out;
ret = do_rename(old_dent, new_dent);
out:
if (old_dir)
put_dentry(old_dir);
if (old_dent)
put_dentry(old_dent);
if (new_dir)
put_dentry(new_dir);
if (new_dir_dent)
put_dentry(new_dir_dent);
if (new_dent)
put_dentry(new_dent);
return ret;
+1 -1
View File
@@ -137,7 +137,7 @@ int shim_do_openat (int dfd, const char * filename, int flags, int mode)
struct shim_dentry * dir = NULL;
int ret = 0;
if ((ret = path_startat(dfd, &dir)) < 0)
if ((ret = get_dirfd_dentry(dfd, &dir)) < 0)
return ret;
struct shim_handle * hdl = get_new_handle();
+1 -1
View File
@@ -233,7 +233,7 @@ int shim_do_newfstatat(int dirfd, const char* pathname,
struct shim_dentry* dir = NULL;
if (*pathname != '/') {
int ret = path_startat(dirfd, &dir);
int ret = get_dirfd_dentry(dirfd, &dir);
if (ret < 0)
return ret;
}