- 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:
Thomas Veerman 2010-01-21 09:32:15 +00:00
parent a5a2073680
commit ca9280e097
8 changed files with 108 additions and 27 deletions

View file

@ -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). */

View file

@ -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),

View file

@ -584,7 +584,7 @@ PUBLIC int fs_getdents(void)
else else
dp = &bp->b_dir[0]; dp = &bp->b_dir[0];
for (; dp < &bp->b_dir[NR_DIR_ENTRIES(block_size)]; dp++) { for (; dp < &bp->b_dir[NR_DIR_ENTRIES(block_size)]; dp++) {
if (dp->d_ino == 0) if (dp->d_ino == 0)
continue; /* Entry is not in use */ continue; /* Entry is not in use */
/* Compute the length of the name */ /* Compute the length of the name */
@ -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).
*/ */

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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] = '/';
} }

View file

@ -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");