- Fix dangling symlink regression
- Make open(2) more POSIX compliant - Add a test case for dangling symlinks and open() syscall with O_CREAT and O_EXCL on a symlink. - Update open(2) man page to reflect change.
This commit is contained in:
parent
a5a2073680
commit
ca9280e097
8 changed files with 108 additions and 27 deletions
|
@ -58,11 +58,11 @@
|
||||||
#define REQ_RDONLY 001
|
#define REQ_RDONLY 001
|
||||||
#define REQ_ISROOT 002
|
#define REQ_ISROOT 002
|
||||||
#define PATH_NOFLAGS 000
|
#define PATH_NOFLAGS 000
|
||||||
#define PATH_RET_SYMLINK 001 /* Return a symlink object (i.e.
|
#define PATH_RET_SYMLINK 010 /* Return a symlink object (i.e.
|
||||||
* do not continue with the contents
|
* do not continue with the contents
|
||||||
* of the symlink if it is the last
|
* of the symlink if it is the last
|
||||||
* component in a path). */
|
* component in a path). */
|
||||||
#define PATH_GET_UCRED 002 /* Request provides a grant ID in m9_l1
|
#define PATH_GET_UCRED 020 /* Request provides a grant ID in m9_l1
|
||||||
* and struct ucred size in m9_s4 (as
|
* and struct ucred size in m9_s4 (as
|
||||||
* opposed to a REQ_UID). */
|
* opposed to a REQ_UID). */
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,10 @@ allocating the inode for O_CREAT.
|
||||||
points outside the process's allocated address space.
|
points outside the process's allocated address space.
|
||||||
.TP 15
|
.TP 15
|
||||||
[EEXIST]
|
[EEXIST]
|
||||||
O_CREAT and O_EXCL were specified and the file exists.
|
O_CREAT and O_EXCL were specified and the file exists or
|
||||||
|
.I
|
||||||
|
path
|
||||||
|
names a symbolic link (regardless of contents of the link).
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
.BR chmod (2),
|
.BR chmod (2),
|
||||||
.BR close (2),
|
.BR close (2),
|
||||||
|
|
|
@ -600,7 +600,7 @@ PUBLIC int fs_getdents(void)
|
||||||
if (o != 0)
|
if (o != 0)
|
||||||
reclen += sizeof(long) - o;
|
reclen += sizeof(long) - o;
|
||||||
|
|
||||||
/* Need the postition of this entry in the directory */
|
/* Need the position of this entry in the directory */
|
||||||
ent_pos = block_pos + ((char *)dp - bp->b_data);
|
ent_pos = block_pos + ((char *)dp - bp->b_data);
|
||||||
|
|
||||||
if(tmpbuf_off + reclen > GETDENTS_BUFSIZ) {
|
if(tmpbuf_off + reclen > GETDENTS_BUFSIZ) {
|
||||||
|
@ -620,7 +620,7 @@ PUBLIC int fs_getdents(void)
|
||||||
/* The user has no space for one more record */
|
/* The user has no space for one more record */
|
||||||
done = TRUE;
|
done = TRUE;
|
||||||
|
|
||||||
/* Record the postion of this entry, it is the
|
/* Record the position of this entry, it is the
|
||||||
* starting point of the next request (unless the
|
* starting point of the next request (unless the
|
||||||
* postion is modified with lseek).
|
* postion is modified with lseek).
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
PRIVATE char mode_map[] = {R_BIT, W_BIT, R_BIT|W_BIT, 0};
|
PRIVATE char mode_map[] = {R_BIT, W_BIT, R_BIT|W_BIT, 0};
|
||||||
|
|
||||||
FORWARD _PROTOTYPE( int common_open, (int oflags, mode_t omode) );
|
FORWARD _PROTOTYPE( int common_open, (int oflags, mode_t omode) );
|
||||||
FORWARD _PROTOTYPE( struct vnode *new_node, (mode_t bits) );
|
FORWARD _PROTOTYPE( struct vnode *new_node, (int oflags, mode_t bits) );
|
||||||
FORWARD _PROTOTYPE( int pipe_open, (struct vnode *vp,mode_t bits,int oflags));
|
FORWARD _PROTOTYPE( int pipe_open, (struct vnode *vp,mode_t bits,int oflags));
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ PRIVATE int common_open(register int oflags, mode_t omode)
|
||||||
/* If O_CREATE is set, try to make the file. */
|
/* If O_CREATE is set, try to make the file. */
|
||||||
if (oflags & O_CREAT) {
|
if (oflags & O_CREAT) {
|
||||||
omode = I_REGULAR | (omode & ALL_MODES & fp->fp_umask);
|
omode = I_REGULAR | (omode & ALL_MODES & fp->fp_umask);
|
||||||
vp = new_node(omode);
|
vp = new_node(oflags, omode);
|
||||||
r = err_code;
|
r = err_code;
|
||||||
if (r == OK) exist = FALSE; /* We just created the file */
|
if (r == OK) exist = FALSE; /* We just created the file */
|
||||||
else if (r != EEXIST) return(r); /* other error */
|
else if (r != EEXIST) return(r); /* other error */
|
||||||
|
@ -114,7 +114,7 @@ PRIVATE int common_open(register int oflags, mode_t omode)
|
||||||
fil_ptr->filp_vno = vp;
|
fil_ptr->filp_vno = vp;
|
||||||
fil_ptr->filp_flags = oflags;
|
fil_ptr->filp_flags = oflags;
|
||||||
|
|
||||||
/* Only do the normal open code if didn't just create the file. */
|
/* Only do the normal open code if we didn't just create the file. */
|
||||||
if(exist) {
|
if(exist) {
|
||||||
/* Check protections. */
|
/* Check protections. */
|
||||||
if ((r = forbidden(vp, bits)) == OK) {
|
if ((r = forbidden(vp, bits)) == OK) {
|
||||||
|
@ -236,18 +236,33 @@ PRIVATE int common_open(register int oflags, mode_t omode)
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
* new_node *
|
* new_node *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
PRIVATE struct vnode *new_node(mode_t bits)
|
PRIVATE struct vnode *new_node(int oflags, mode_t bits)
|
||||||
{
|
{
|
||||||
|
/* Try to create a new inode and return a pointer to it. If the inode already
|
||||||
|
exists, return a pointer to it as well, but set err_code accordingly.
|
||||||
|
NIL_VNODE is returned if the path cannot be resolved up to the last
|
||||||
|
directory, or when the inode cannot be created due to permissions or
|
||||||
|
otherwise. */
|
||||||
struct vnode *dirp, *vp;
|
struct vnode *dirp, *vp;
|
||||||
int r;
|
int r, flags;
|
||||||
struct node_details res;
|
struct node_details res;
|
||||||
struct vnode *rest;
|
struct vnode *rest;
|
||||||
|
|
||||||
|
/* When O_CREAT and O_EXCL flags are set, the path may not be named by a
|
||||||
|
* symbolic link. */
|
||||||
|
flags = PATH_NOFLAGS;
|
||||||
|
if (oflags & O_EXCL) flags |= PATH_RET_SYMLINK;
|
||||||
|
|
||||||
/* See if the path can be opened down to the last directory. */
|
/* See if the path can be opened down to the last directory. */
|
||||||
if ((dirp = last_dir()) == NIL_VNODE) return(NIL_VNODE);
|
if ((dirp = last_dir()) == NIL_VNODE) return(NIL_VNODE);
|
||||||
|
|
||||||
/* The final directory is accessible. Get final component of the path. */
|
/* The final directory is accessible. Get final component of the path. */
|
||||||
vp = advance(dirp, 0);
|
vp = advance(dirp, flags);
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
if (user_fullpath[0] == '/') return new_node(oflags, bits);
|
||||||
|
|
||||||
if (vp == NIL_VNODE && err_code == ENOENT) {
|
if (vp == NIL_VNODE && err_code == ENOENT) {
|
||||||
/* Last path component does not exist. Make a new directory entry. */
|
/* Last path component does not exist. Make a new directory entry. */
|
||||||
if ((vp = get_free_vnode()) == NIL_VNODE) {
|
if ((vp = get_free_vnode()) == NIL_VNODE) {
|
||||||
|
@ -258,10 +273,61 @@ PRIVATE struct vnode *new_node(mode_t bits)
|
||||||
if ((r = forbidden(dirp, W_BIT|X_BIT)) != OK ||
|
if ((r = forbidden(dirp, W_BIT|X_BIT)) != OK ||
|
||||||
(r = req_create(dirp->v_fs_e, dirp->v_inode_nr,bits, fp->fp_effuid,
|
(r = req_create(dirp->v_fs_e, dirp->v_inode_nr,bits, fp->fp_effuid,
|
||||||
fp->fp_effgid, user_fullpath, &res)) != OK ) {
|
fp->fp_effgid, user_fullpath, &res)) != OK ) {
|
||||||
/* Can't create new directory entry: either no permission or
|
/* Can't create inode either due to permissions or some other
|
||||||
something else is wrong. */
|
* problem. In case r is EEXIST, we might be dealing with a
|
||||||
|
* dangling symlink.*/
|
||||||
|
if (r == EEXIST) {
|
||||||
|
struct vnode *slp, *old_wd;
|
||||||
|
|
||||||
|
/* Resolve path up to symlink */
|
||||||
|
slp = advance(dirp, PATH_RET_SYMLINK);
|
||||||
|
if (slp != NIL_VNODE) {
|
||||||
|
if (S_ISLNK(slp->v_mode)) {
|
||||||
|
/* Get contents of link */
|
||||||
|
|
||||||
|
int max_linklen;
|
||||||
|
max_linklen = sizeof(user_fullpath)-1;
|
||||||
|
r = req_rdlink(slp->v_fs_e,
|
||||||
|
slp->v_inode_nr,
|
||||||
|
VFS_PROC_NR,
|
||||||
|
user_fullpath,
|
||||||
|
max_linklen);
|
||||||
|
if (r < 0) {
|
||||||
|
/* Failed to read link */
|
||||||
|
put_vnode(slp);
|
||||||
|
put_vnode(dirp);
|
||||||
|
err_code = r;
|
||||||
|
return(NIL_VNODE);
|
||||||
|
}
|
||||||
|
user_fullpath[r] = '\0';/* Term. path*/
|
||||||
|
}
|
||||||
|
put_vnode(slp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to create the inode the dangling symlink was
|
||||||
|
* pointing to. We have to use dirp as starting point
|
||||||
|
* as there might be multiple successive symlinks
|
||||||
|
* crossing multiple mountpoints. */
|
||||||
|
old_wd = fp->fp_wd; /* Save orig. working dirp */
|
||||||
|
fp->fp_wd = dirp;
|
||||||
|
vp = new_node(oflags, bits);
|
||||||
|
fp->fp_wd = old_wd; /* Restore */
|
||||||
|
|
||||||
|
if (vp != NIL_VNODE) {
|
||||||
|
put_vnode(dirp);
|
||||||
|
return(vp);
|
||||||
|
}
|
||||||
|
r = err_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r == EEXIST)
|
||||||
|
err_code = EIO; /* Impossible, we have verified that
|
||||||
|
* the last component doesn't exist and
|
||||||
|
* is not a dangling symlink. */
|
||||||
|
else
|
||||||
|
err_code = r;
|
||||||
|
|
||||||
put_vnode(dirp);
|
put_vnode(dirp);
|
||||||
err_code = r;
|
|
||||||
return(NIL_VNODE);
|
return(NIL_VNODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,9 +346,10 @@ PRIVATE struct vnode *new_node(mode_t bits)
|
||||||
} else {
|
} else {
|
||||||
/* Either last component exists, or there is some other problem. */
|
/* Either last component exists, or there is some other problem. */
|
||||||
if (vp != NIL_VNODE)
|
if (vp != NIL_VNODE)
|
||||||
r = EEXIST;
|
r = EEXIST; /* File exists or a symlink names a file while
|
||||||
|
* O_EXCL is set. */
|
||||||
else
|
else
|
||||||
r = err_code;
|
r = err_code; /* Other problem. */
|
||||||
}
|
}
|
||||||
|
|
||||||
err_code = r;
|
err_code = r;
|
||||||
|
|
|
@ -149,10 +149,13 @@ node_details_t *res;
|
||||||
size_t len;
|
size_t len;
|
||||||
message m;
|
message m;
|
||||||
|
|
||||||
|
if (path[0] == '/')
|
||||||
|
panic(__FILE__, "req_create: filename starts with '/'", NO_NUM);
|
||||||
|
|
||||||
len = strlen(path) + 1;
|
len = strlen(path) + 1;
|
||||||
grant_id = cpf_grant_direct(fs_e, (vir_bytes) path, len, CPF_READ);
|
grant_id = cpf_grant_direct(fs_e, (vir_bytes) path, len, CPF_READ);
|
||||||
if (grant_id == -1)
|
if (grant_id == -1)
|
||||||
panic(__FILE__, "req_create: cpf_grant_direct failed", NO_NUM);
|
panic(__FILE__, "req_create: cpf_grant_direct failed", NO_NUM);
|
||||||
|
|
||||||
/* Fill in request message */
|
/* Fill in request message */
|
||||||
m.m_type = REQ_CREATE;
|
m.m_type = REQ_CREATE;
|
||||||
|
|
|
@ -15,9 +15,6 @@ typedef struct node_details {
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
|
|
||||||
/* For faster access */
|
|
||||||
unsigned short inode_index;
|
|
||||||
|
|
||||||
/* For char/block special files */
|
/* For char/block special files */
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
} node_details_t;
|
} node_details_t;
|
||||||
|
|
|
@ -665,14 +665,25 @@ void test25e()
|
||||||
if (fd != -1) e(40);
|
if (fd != -1) e(40);
|
||||||
if (errno != EEXIST) e(41);
|
if (errno != EEXIST) e(41);
|
||||||
|
|
||||||
|
/* open should fail when O_CREAT|O_EXCL are set and a symbolic link names
|
||||||
|
a file with EEXIST (regardless of link actually works or not) */
|
||||||
|
if (symlink("exists", "slinktoexists") == -1) e(42);
|
||||||
|
if (open("slinktoexists", O_RDWR | O_CREAT | O_EXCL, 0777) != -1) e(43);
|
||||||
|
if (unlink("exists") == -1) e(44);
|
||||||
|
/* "slinktoexists has become a dangling symlink. open(2) should still fail
|
||||||
|
with EEXIST */
|
||||||
|
if (open("slinktoexists", O_RDWR | O_CREAT | O_EXCL, 0777) != -1) e(45);
|
||||||
|
if (errno != EEXIST) e(46);
|
||||||
|
|
||||||
|
|
||||||
/* Test ToLongName and ToLongPath */
|
/* Test ToLongName and ToLongPath */
|
||||||
if ((fd = open(ToLongName, O_RDWR | O_CREAT, 0777)) != 3) e(45);
|
if ((fd = open(ToLongName, O_RDWR | O_CREAT, 0777)) != 3) e(47);
|
||||||
if (close(fd) != 0) e(46);
|
if (close(fd) != 0) e(48);
|
||||||
ToLongPath[PATH_MAX - 2] = '/';
|
ToLongPath[PATH_MAX - 2] = '/';
|
||||||
ToLongPath[PATH_MAX - 1] = 'a';
|
ToLongPath[PATH_MAX - 1] = 'a';
|
||||||
if ((fd = open(ToLongPath, O_RDWR | O_CREAT, 0777)) != -1) e(47);
|
if ((fd = open(ToLongPath, O_RDWR | O_CREAT, 0777)) != -1) e(49);
|
||||||
if (errno != ENAMETOOLONG) e(48);
|
if (errno != ENAMETOOLONG) e(50);
|
||||||
if (close(fd) != -1) e(49);
|
if (close(fd) != -1) e(51);
|
||||||
ToLongPath[PATH_MAX - 1] = '/';
|
ToLongPath[PATH_MAX - 1] = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -537,7 +537,7 @@ static int can_use_network(void)
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
/* try to ping minix3.org */
|
/* try to ping minix3.org */
|
||||||
status = system("ping www.minix3.org > /dev/nul 2>&1");
|
status = system("ping www.minix3.org > /dev/null 2>&1");
|
||||||
if (status == 127)
|
if (status == 127)
|
||||||
{
|
{
|
||||||
printf("cannot execute ping\n");
|
printf("cannot execute ping\n");
|
||||||
|
|
Loading…
Reference in a new issue