AVFS: Return actual last dir when path is named by a symlink
Last_dir didn't consider paths that end in a symlink and hence didn't actually return the last_dir when provided with one. For example, /var/log is a symlink to /usr/log. Issuing `>/var/log' would trigger an assert in AVFS, because /var/ is not the actual last directory; /usr/ is. Last_dir now verifies the final component is not a symlink. If it is, it follows the symlink and restarts finding of the last the directory.
This commit is contained in:
parent
c89aaf7a87
commit
e6c98c3c55
3 changed files with 168 additions and 51 deletions
|
@ -97,7 +97,7 @@ PUBLIC int do_unlink()
|
||||||
char fullpath[PATH_MAX];
|
char fullpath[PATH_MAX];
|
||||||
struct lookup resolve;
|
struct lookup resolve;
|
||||||
|
|
||||||
lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &dirp);
|
lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &dirp);
|
||||||
resolve.l_vmnt_lock = VMNT_WRITE;
|
resolve.l_vmnt_lock = VMNT_WRITE;
|
||||||
resolve.l_vnode_lock = VNODE_READ;
|
resolve.l_vnode_lock = VNODE_READ;
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ PUBLIC int do_unlink()
|
||||||
return(err_code);
|
return(err_code);
|
||||||
|
|
||||||
if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
|
if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
|
||||||
|
assert(vmp != NULL);
|
||||||
|
|
||||||
/* Make sure that the object is a directory */
|
/* Make sure that the object is a directory */
|
||||||
if ((dirp->v_mode & I_TYPE) != I_DIRECTORY) {
|
if ((dirp->v_mode & I_TYPE) != I_DIRECTORY) {
|
||||||
|
@ -149,6 +150,7 @@ PUBLIC int do_unlink()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(vmp != NULL);
|
||||||
tll_upgrade(&vmp->m_lock);
|
tll_upgrade(&vmp->m_lock);
|
||||||
|
|
||||||
if (call_nr == UNLINK)
|
if (call_nr == UNLINK)
|
||||||
|
|
|
@ -322,7 +322,9 @@ PRIVATE struct vnode *new_node(struct lookup *resolve, int oflags, mode_t bits)
|
||||||
findnode.l_vnode_lock = (oflags & O_TRUNC) ? VNODE_WRITE : VNODE_OPCL;
|
findnode.l_vnode_lock = (oflags & O_TRUNC) ? VNODE_WRITE : VNODE_OPCL;
|
||||||
vp = advance(dirp, &findnode, fp);
|
vp = advance(dirp, &findnode, fp);
|
||||||
assert(vp_vmp == NULL); /* Lookup to last dir should have yielded lock
|
assert(vp_vmp == NULL); /* Lookup to last dir should have yielded lock
|
||||||
* on vmp or final component does not exist. */
|
* on vmp or final component does not exist.
|
||||||
|
* Either way, vp_vmp ought to be not set.
|
||||||
|
*/
|
||||||
|
|
||||||
/* The combination of a symlink with absolute path followed by a danglink
|
/* The combination of a symlink with absolute path followed by a danglink
|
||||||
* symlink results in a new path that needs to be re-resolved entirely. */
|
* symlink results in a new path that needs to be re-resolved entirely. */
|
||||||
|
|
|
@ -133,7 +133,6 @@ struct fproc *rfp;
|
||||||
return(vp);
|
return(vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
* eat_path *
|
* eat_path *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
|
@ -148,7 +147,6 @@ struct fproc *rfp;
|
||||||
return advance(start_dir, resolve, rfp);
|
return advance(start_dir, resolve, rfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
* last_dir *
|
* last_dir *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
|
@ -168,13 +166,25 @@ struct fproc *rfp;
|
||||||
size_t len;
|
size_t len;
|
||||||
char *cp;
|
char *cp;
|
||||||
char dir_entry[NAME_MAX+1];
|
char dir_entry[NAME_MAX+1];
|
||||||
struct vnode *start_dir, *res;
|
struct vnode *start_dir, *res_vp, *sym_vp, *new_res_vp, *loop_start;
|
||||||
int r;
|
struct vmnt *sym_vmp = NULL;
|
||||||
|
int r, symloop = 0, ret_on_symlink = 0;
|
||||||
|
struct lookup symlink;
|
||||||
|
|
||||||
*resolve->l_vnode = NULL;
|
*resolve->l_vnode = NULL;
|
||||||
*resolve->l_vmp = NULL;
|
*resolve->l_vmp = NULL;
|
||||||
|
loop_start = NULL;
|
||||||
|
sym_vp = NULL;
|
||||||
|
|
||||||
/* Is the path absolute or relative? Initialize 'start_dir' accordingly. */
|
ret_on_symlink = !!(resolve->l_flags & PATH_RET_SYMLINK);
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* Is the path absolute or relative? Initialize 'start_dir'
|
||||||
|
* accordingly. Use loop_start in case we're looping.
|
||||||
|
*/
|
||||||
|
if (loop_start != NULL)
|
||||||
|
start_dir = loop_start;
|
||||||
|
else
|
||||||
start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd:rfp->fp_wd);
|
start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd:rfp->fp_wd);
|
||||||
|
|
||||||
len = strlen(resolve->l_path);
|
len = strlen(resolve->l_path);
|
||||||
|
@ -182,7 +192,8 @@ struct fproc *rfp;
|
||||||
/* If path is empty, return ENOENT. */
|
/* If path is empty, return ENOENT. */
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
err_code = ENOENT;
|
err_code = ENOENT;
|
||||||
return(NULL);
|
res_vp = NULL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !DO_POSIX_PATHNAME_RES
|
#if !DO_POSIX_PATHNAME_RES
|
||||||
|
@ -195,21 +206,25 @@ struct fproc *rfp;
|
||||||
|
|
||||||
cp = strrchr(resolve->l_path, '/');
|
cp = strrchr(resolve->l_path, '/');
|
||||||
if (cp == NULL) {
|
if (cp == NULL) {
|
||||||
/* Just one entry in the current working directory */
|
/* Just an entry in the current working directory */
|
||||||
struct vmnt *vmp;
|
struct vmnt *vmp;
|
||||||
|
|
||||||
vmp = find_vmnt(start_dir->v_fs_e);
|
vmp = find_vmnt(start_dir->v_fs_e);
|
||||||
r = lock_vmnt(vmp, resolve->l_vmnt_lock);
|
r = lock_vmnt(vmp, resolve->l_vmnt_lock);
|
||||||
if (r == EDEADLK)
|
if (r == EDEADLK) {
|
||||||
return(NULL);
|
res_vp = NULL;
|
||||||
else if (r == OK)
|
break;
|
||||||
|
} else if (r == OK)
|
||||||
*resolve->l_vmp = vmp;
|
*resolve->l_vmp = vmp;
|
||||||
|
|
||||||
lock_vnode(start_dir, resolve->l_vnode_lock);
|
lock_vnode(start_dir, resolve->l_vnode_lock);
|
||||||
*resolve->l_vnode = start_dir;
|
*resolve->l_vnode = start_dir;
|
||||||
dup_vnode(start_dir);
|
dup_vnode(start_dir);
|
||||||
|
if (loop_start != NULL) {
|
||||||
|
unlock_vnode(loop_start);
|
||||||
|
put_vnode(loop_start);
|
||||||
|
}
|
||||||
return(start_dir);
|
return(start_dir);
|
||||||
|
|
||||||
} else if (cp[1] == '\0') {
|
} else if (cp[1] == '\0') {
|
||||||
/* Path ends in a slash. The directory entry is '.' */
|
/* Path ends in a slash. The directory entry is '.' */
|
||||||
strcpy(dir_entry, ".");
|
strcpy(dir_entry, ".");
|
||||||
|
@ -226,14 +241,112 @@ struct fproc *rfp;
|
||||||
cp--;
|
cp--;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve->l_flags = PATH_NOFLAGS;
|
/* Resolve up to and including the last directory of the path. Turn off
|
||||||
res = advance(start_dir, resolve, rfp);
|
* PATH_RET_SYMLINK, because we do want to follow the symlink in this
|
||||||
if (res == NULL) return(NULL);
|
* case. That is, the flag is meant for the actual filename of the path,
|
||||||
|
* not the last directory.
|
||||||
|
*/
|
||||||
|
resolve->l_flags &= ~PATH_RET_SYMLINK;
|
||||||
|
if ((res_vp = advance(start_dir, resolve, rfp)) == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the directory entry is not a symlink we're done now. If it is a
|
||||||
|
* symlink, then we're not at the last directory, yet. */
|
||||||
|
|
||||||
/* Copy the directory entry back to user_fullpath */
|
/* Copy the directory entry back to user_fullpath */
|
||||||
strncpy(resolve->l_path, dir_entry, NAME_MAX + 1);
|
strncpy(resolve->l_path, dir_entry, NAME_MAX + 1);
|
||||||
|
|
||||||
return(res);
|
/* Look up the directory entry, but do not follow the symlink when it
|
||||||
|
* is one.
|
||||||
|
*/
|
||||||
|
lookup_init(&symlink, resolve->l_path,
|
||||||
|
resolve->l_flags|PATH_RET_SYMLINK, &sym_vmp, &sym_vp);
|
||||||
|
symlink.l_vnode_lock = VNODE_READ;
|
||||||
|
symlink.l_vmnt_lock = VMNT_READ;
|
||||||
|
sym_vp = advance(res_vp, &symlink, rfp);
|
||||||
|
|
||||||
|
/* Advance caused us to either switch to a different vmnt or we're
|
||||||
|
* still at the same vmnt. The former might've yielded a new vmnt lock,
|
||||||
|
* the latter should not have. Verify. */
|
||||||
|
if (sym_vmp != NULL) {
|
||||||
|
/* We got a vmnt lock, so the endpoints of the vnodes must
|
||||||
|
* differ.
|
||||||
|
*/
|
||||||
|
assert(sym_vp->v_fs_e != res_vp->v_fs_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym_vp != NULL && S_ISLNK(sym_vp->v_mode)) {
|
||||||
|
/* Last component is a symlink, but if we've been asked to not
|
||||||
|
* resolve it, return now.
|
||||||
|
*/
|
||||||
|
if (ret_on_symlink) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = req_rdlink(sym_vp->v_fs_e, sym_vp->v_inode_nr, NONE,
|
||||||
|
resolve->l_path, PATH_MAX - 1, 1);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
/* Failed to read link */
|
||||||
|
err_code = r;
|
||||||
|
unlock_vnode(res_vp);
|
||||||
|
unlock_vmnt(*resolve->l_vmp);
|
||||||
|
put_vnode(res_vp);
|
||||||
|
*resolve->l_vmp = NULL;
|
||||||
|
*resolve->l_vnode = NULL;
|
||||||
|
res_vp = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
resolve->l_path[r] = '\0';
|
||||||
|
|
||||||
|
if (strrchr(resolve->l_path, '/') != NULL) {
|
||||||
|
unlock_vnode(sym_vp);
|
||||||
|
unlock_vmnt(*resolve->l_vmp);
|
||||||
|
if (sym_vmp != NULL)
|
||||||
|
unlock_vmnt(sym_vmp);
|
||||||
|
*resolve->l_vmp = NULL;
|
||||||
|
put_vnode(sym_vp);
|
||||||
|
sym_vp = NULL;
|
||||||
|
|
||||||
|
symloop++;
|
||||||
|
|
||||||
|
/* Relative symlinks are relative to res_vp, not cwd */
|
||||||
|
if (resolve->l_path[0] != '/') {
|
||||||
|
loop_start = res_vp;
|
||||||
|
} else {
|
||||||
|
/* Absolute symlink, forget about res_vp */
|
||||||
|
unlock_vnode(res_vp);
|
||||||
|
put_vnode(res_vp);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} while (symloop < SYMLOOP_MAX);
|
||||||
|
|
||||||
|
if (symloop >= SYMLOOP_MAX) {
|
||||||
|
err_code = ELOOP;
|
||||||
|
res_vp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym_vp != NULL) {
|
||||||
|
unlock_vnode(sym_vp);
|
||||||
|
if (sym_vmp != NULL) {
|
||||||
|
unlock_vmnt(sym_vmp);
|
||||||
|
}
|
||||||
|
put_vnode(sym_vp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loop_start != NULL) {
|
||||||
|
unlock_vnode(loop_start);
|
||||||
|
put_vnode(loop_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the directory entry back to user_fullpath */
|
||||||
|
strncpy(resolve->l_path, dir_entry, NAME_MAX + 1);
|
||||||
|
return(res_vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
|
|
Loading…
Reference in a new issue