Skip to content

Commit

Permalink
lib/vfscore: Fix create through broken symlink
Browse files Browse the repository at this point in the history
Previously a vfs syscall on a broken symlink would fail with ENOENT
instead of attempting the syscall on the symlink target. This would make
open(O_CREAT) to fail instead of creating the missing target file.
This change fixes this behavior, bringing it in line with expectations.

Signed-off-by: Andrei Tatar <andrei@unikraft.io>
  • Loading branch information
andreittr committed Aug 2, 2023
1 parent b9104d2 commit 257ccba
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 12 deletions.
40 changes: 30 additions & 10 deletions lib/vfscore/lookup.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,14 @@ namei_follow_link(struct dentry *dp, char *node, char *name, char *fp, size_t mo
return (0);
}
/*
* Convert a pathname into a pointer to a dentry
* Resolve a pathname into a pointer to a dentry and a realpath.
*
* @path: full path name.
* @dpp: dentry to be returned.
* @realpath: if not NULL, return path after resolving all symlinks
*/
int
namei(const char *path, struct dentry **dpp)
namei_resolve(const char *path, struct dentry **dpp, char *realpath)
{
char *p;
char node[PATH_MAX];
Expand All @@ -122,14 +123,16 @@ namei(const char *path, struct dentry **dpp)
memset(fp, 0, PATH_MAX);
strlcpy(fp, path, PATH_MAX);

error = 0;
do {
need_continue = 0;
/*
* Convert a full path name to its mount point and
* the local node in the file system.
*/
if (vfs_findroot(fp, &mp, &p)) {
return ENOTDIR;
error = ENOTDIR;
goto out;
}

size_t mountpoint_len = p - fp;
Expand All @@ -140,7 +143,7 @@ namei(const char *path, struct dentry **dpp)
if (dp) {
/* vnode is already active. */
*dpp = dp;
return 0;
goto out;
}
/*
* Find target vnode, started from root directory.
Expand Down Expand Up @@ -189,7 +192,7 @@ namei(const char *path, struct dentry **dpp)
if (error) {
vn_unlock(dvp);
drele(ddp);
return error;
goto out;
}

dp = dentry_alloc(ddp, vp, node);
Expand All @@ -198,7 +201,8 @@ namei(const char *path, struct dentry **dpp)
if (!dp) {
vn_unlock(dvp);
drele(ddp);
return ENOMEM;
error = ENOMEM;
goto out;
}
}
vn_unlock(dvp);
Expand All @@ -209,7 +213,7 @@ namei(const char *path, struct dentry **dpp)
error = namei_follow_link(dp, node, name, fp, mountpoint_len);
if (error) {
drele(dp);
return (error);
goto out;
}

drele(dp);
Expand All @@ -223,21 +227,37 @@ namei(const char *path, struct dentry **dpp)
node[0] = 0;

if (++links_followed >= MAXSYMLINKS) {
return (ELOOP);
error = ELOOP;
goto out;
}
need_continue = 1;
break;
}

if (*p == '/' && ddp->d_vnode->v_type != VDIR) {
drele(ddp);
return ENOTDIR;
error = ENOTDIR;
goto out;
}
}
} while (need_continue);

*dpp = dp;
return 0;
out:
if (realpath)
strlcpy(realpath, fp, PATH_MAX);
return error;
}
/*
* Convert a pathname into a pointer to a dentry
*
* @path: full path name.
* @dpp: dentry to be returned.
*/
int
namei(const char *path, struct dentry **dpp)
{
return namei_resolve(path, dpp, NULL);
}

/*
Expand Down
5 changes: 3 additions & 2 deletions lib/vfscore/syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,18 @@ sys_open(char *path, int flags, mode_t mode, struct vfscore_file **fpp)
struct dentry *dp, *ddp;
struct vnode *vp;
char *filename;
char realpath[PATH_MAX];
int error;

DPRINTF(VFSDB_SYSCALL, ("sys_open: path=%s flags=%x mode=%x\n",
path, flags, mode));

flags = vfscore_fflags(flags);
if (flags & O_CREAT) {
error = namei(path, &dp);
error = namei_resolve(path, &dp, realpath);
if (error == ENOENT) {
/* Create new struct vfscore_file. */
if ((error = lookup(path, &ddp, &filename)) != 0)
if ((error = lookup(realpath, &ddp, &filename)) != 0)
return error;

vn_lock(ddp->d_vnode);
Expand Down
15 changes: 15 additions & 0 deletions lib/vfscore/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,21 @@ int sec_vnode_permission(char *path);
*/
int namei(const char *path, struct dentry **dpp);

/**
* Resolve a pathname into a pointer to a dentry and a realpath.
*
* @param path
* The full path name
* @param[out] dpp
* Dentry to be returned
* @param[out] realpath
* if not NULL, return path after resolving all symlinks
* @return
* - (0): Completed successfully
* - (<0): Negative value with error code
*/
int namei_resolve(const char *path, struct dentry **dpp, char *realpath);

/**
* Converts the last component in the path to a pointer to a dentry.
*
Expand Down

0 comments on commit 257ccba

Please sign in to comment.