diff --git a/servers/fs/open.c b/servers/fs/open.c index 9052046a4..87ccd8c0c 100644 --- a/servers/fs/open.c +++ b/servers/fs/open.c @@ -2,16 +2,18 @@ * seeking on files. * * The entry points into this file are - * do_creat: perform the CREAT system call - * do_open: perform the OPEN system call - * do_mknod: perform the MKNOD system call - * do_mkdir: perform the MKDIR system call - * do_close: perform the CLOSE system call - * do_lseek: perform the LSEEK system call + * do_creat: perform the CREAT system call + * do_open: perform the OPEN system call + * do_mknod: perform the MKNOD system call + * do_mkdir: perform the MKDIR system call + * do_close: perform the CLOSE system call + * do_lseek: perform the LSEEK system call + * do_symlink: perform the LSEEK system call */ #include "fs.h" #include +#include #include #include #include @@ -205,14 +207,37 @@ PRIVATE struct inode *new_node(char *path, mode_t bits, zone_t z0) */ register struct inode *rlast_dir_ptr, *rip; + struct inode *old_workdir_ip; register int r; + int slink_found, loops = 0; char string[NAME_MAX]; - /* See if the path can be opened down to the last directory. */ - if ((rlast_dir_ptr = last_dir(path, string)) == NIL_INODE) return(NIL_INODE); + old_workdir_ip = fp->fp_workdir; /* Save the current working directory */ + + do { + slink_found = FALSE; + /* See if the path can be opened down to the last directory. */ + if ((rlast_dir_ptr = last_dir(path, string)) == NIL_INODE) { + fp->fp_workdir = old_workdir_ip; /* Restore cwd */ + return(NIL_INODE); + } + + /* The final directory is accessible. Get final component of the path. */ + rip = advance(rlast_dir_ptr, string); + if (rip != NIL_INODE && (rip->i_mode & I_TYPE) == I_SYMBOLIC_LINK) { + if (++loops > MAX_SYM_LOOPS) { + fp->fp_workdir = old_workdir_ip; + put_inode(rlast_dir_ptr); + err_code = ELOOP; + return(NIL_INODE); + } + rip = slink_traverse(rip, path, string, rlast_dir_ptr); + slink_found = TRUE; + put_inode(rlast_dir_ptr); + fp->fp_workdir = rip; /* cd to symlink target dir */ + } + } while (slink_found); - /* The final directory is accessible. Get final component of the path. */ - rip = advance(rlast_dir_ptr, string); if ( rip == NIL_INODE && err_code == ENOENT) { /* Last path component does not exist. Make new directory entry. */ if ( (rip = alloc_inode(rlast_dir_ptr->i_dev, bits)) == NIL_INODE) { @@ -471,3 +496,67 @@ PUBLIC int do_lseek() m_out.reply_l1 = pos; /* insert the long into the output message */ return(OK); } + + +/*===========================================================================* + * do_symlink * + *===========================================================================*/ +PUBLIC int do_symlink() +{ +/* perform the symlink(name1, name2) system call */ + + register struct inode *ip; + register int r; + char string[NAME_MAX]; + register struct inode *new_ip; + struct buf *bp; + + if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) + return(err_code); + + /* Does the final directory of 'name2' exist? */ + if ( (ip = last_dir(user_path, string)) == NIL_INODE) + return(err_code); + + r = OK; + /* if name2 exists in full set 'r' to error */ + if ( (new_ip = advance(ip, string)) == NIL_INODE) { + r = err_code; + if (r == ENOENT) r = OK; + } else { + put_inode(new_ip); + r = EEXIST; + } + put_inode(ip); + if (r != OK) return(r); + + if ( (new_ip = new_node(user_path, (mode_t)(I_SYMBOLIC_LINK | 0777), + (off_t)0)) == NIL_INODE) { + put_inode(new_ip); + return(err_code); + } + + truncate(new_ip); + wipe_inode(new_ip); + new_ip->i_size = m_in.name1_length; + + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) { + put_inode(new_ip); + return(err_code); + } + /* allocate disk block for name1 */ + if ( (bp = new_block(new_ip, 0)) == NIL_BUF) { + put_inode(new_ip); + return(err_code); + } + /* stuff pathname into diskblock and set immediate writing */ + memcpy(bp->b_data, user_path, m_in.name1_length); + bp->b_dirt = DIRTY; + put_block(bp, INODE_BLOCK); + new_ip->i_dirt = DIRTY; + rw_inode(new_ip, WRITING); + put_inode(new_ip); + return(err_code); +} + +/** open.c **/ diff --git a/servers/fs/path.c b/servers/fs/path.c index c954a64ad..03f8e092d 100644 --- a/servers/fs/path.c +++ b/servers/fs/path.c @@ -32,20 +32,44 @@ char *path; /* the path name to be parsed */ * return NIL_INODE as function value and an error code in 'err_code'. */ - register struct inode *ldip, *rip; - char string[NAME_MAX]; /* hold 1 path component name here */ + register struct inode *ldip, *rip, *old_workdir_ip; + char string[NAME_MAX]; /* hold 1 path component name here */ + int slink_found; + int loops = 0; /* count symlink traversals */ - /* First open the path down to the final directory. */ - if ( (ldip = last_dir(path, string)) == NIL_INODE) { - return(NIL_INODE); /* we couldn't open final directory */ - } + old_workdir_ip = fp->fp_workdir; /* save the current working directory */ - /* The path consisting only of "/" is a special case, check for it. */ - if (string[0] == '\0') return(ldip); + do { + slink_found = FALSE; - /* Get final component of the path. */ - rip = advance(ldip, string); - put_inode(ldip); + /* First open the path down to the final directory. */ + if ( (ldip = last_dir(path, string)) == NIL_INODE) { + fp->fp_workdir = old_workdir_ip; + return(NIL_INODE); /* we couldn't open final directory */ + } + + /* The path consisting only of "/" is a special case, check for it. */ + if (string[0] == '\0') return(ldip); + + /* Get final component of the path. */ + rip = advance(ldip, string); + + if (rip != NIL_INODE && (rip->i_mode & I_TYPE) == I_SYMBOLIC_LINK) { + if (++loops > MAX_SYM_LOOPS) { + put_inode(rip); + put_inode(ldip); + fp->fp_workdir = old_workdir_ip; + err_code = ELOOP; + return(NIL_INODE); + } + rip = slink_traverse(rip, path, string, ldip); + slink_found = TRUE; + fp->fp_workdir = rip; /* cd to link's starting dir */ + put_inode(rip); + } + put_inode(ldip); + } while (slink_found); + fp->fp_workdir = old_workdir_ip; return(rip); } @@ -67,6 +91,7 @@ char string[NAME_MAX]; /* the final component is returned here */ register struct inode *rip; register char *new_name; register struct inode *new_ip; + int loops = 0; /* count symlink traversals */ /* Is the path absolute or relative? Initialize 'rip' accordingly. */ rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir); @@ -99,11 +124,24 @@ char string[NAME_MAX]; /* the final component is returned here */ /* There is more path. Keep parsing. */ new_ip = advance(rip, string); - put_inode(rip); /* rip either obsolete or irrelevant */ - if (new_ip == NIL_INODE) return(NIL_INODE); - + if (new_ip == NIL_INODE) { + put_inode(rip); + return(NIL_INODE); + } + /* The call to advance() succeeded. Fetch next component. */ - path = new_name; + if ((new_ip->i_mode & I_TYPE) == I_SYMBOLIC_LINK) { + if (++loops > MAX_SYM_LOOPS) { + err_code = ELOOP; + put_inode(rip); + put_inode(new_ip); + return(NIL_INODE); + } + new_ip = slink_traverse(new_ip, path, string, rip); + } else + path = new_name; + + put_inode(rip); /* rip either obsolete or irrelevant */ rip = new_ip; } } @@ -376,3 +414,43 @@ int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */ } return(OK); } + + +/*===========================================================================* + * slink_traverse * + *===========================================================================*/ +PUBLIC struct inode *slink_traverse(rip, path, string, ldip) +register struct inode *rip; +char *path; +char *string; +register struct inode *ldip; +{ + + /* copy path out of symlink's disk block -- return inode pointer to + * the directory that the path infers */ + + register char *p, *q; + char temp[PATH_MAX]; + struct buf *bp; + block_t b; + + b = read_map(rip, 0); + bp = get_block(rip->i_dev, b, NORMAL); /* get the pathname block */ + memcpy(temp, bp->b_data, rip->i_size); + temp[rip->i_size] = '\0'; + put_block(bp, NORMAL); + + q = strstr(path, string); + if ((p = strchr(q, '/')) != NULL && (q + strlen(string)) == p) { + strcat(temp, p); + } + strcpy(path, temp); + + put_inode(rip); + rip = (*path == '/') ? fp->fp_rootdir : ldip; + + dup_inode(rip); + return (rip); +} + +/** path.c **/ diff --git a/servers/fs/proto.h b/servers/fs/proto.h index 705f4692c..0971df558 100644 --- a/servers/fs/proto.h +++ b/servers/fs/proto.h @@ -110,6 +110,7 @@ _PROTOTYPE( int do_lseek, (void) ); _PROTOTYPE( int do_mknod, (void) ); _PROTOTYPE( int do_mkdir, (void) ); _PROTOTYPE( int do_open, (void) ); +_PROTOTYPE( int do_symlink, (void) ); /* path.c */ _PROTOTYPE( struct inode *advance,(struct inode *dirp, char string[NAME_MAX])); @@ -117,6 +118,8 @@ _PROTOTYPE( int search_dir, (struct inode *ldir_ptr, char string [NAME_MAX], ino_t *numb, int flag) ); _PROTOTYPE( struct inode *eat_path, (char *path) ); _PROTOTYPE( struct inode *last_dir, (char *path, char string [NAME_MAX])); +_PROTOTYPE( struct inode *slink_traverse, (struct inode *rip, char *path, + char string[NAME_MAX], struct inode *ldip) ); /* pipe.c */ _PROTOTYPE( int do_pipe, (void) ); @@ -153,6 +156,8 @@ _PROTOTYPE( int do_chdir, (void) ); _PROTOTYPE( int do_fchdir, (void) ); _PROTOTYPE( int do_chroot, (void) ); _PROTOTYPE( int do_fstat, (void) ); +_PROTOTYPE( int do_lstat, (void) ); +_PROTOTYPE( int do_rdlink, (void) ); _PROTOTYPE( int do_stat, (void) ); _PROTOTYPE( int do_fstatfs, (void) ); diff --git a/servers/fs/stadir.c b/servers/fs/stadir.c index 4f8029c38..003f87016 100644 --- a/servers/fs/stadir.c +++ b/servers/fs/stadir.c @@ -6,10 +6,13 @@ * do_chroot: perform the CHROOT system call * do_stat: perform the STAT system call * do_fstat: perform the FSTAT system call + * do_rdlink: perform the RDLINK system call + * do_lstat: perform the LSTAT system call * do_fstatfs: perform the FSTATFS system call */ #include "fs.h" +#include "buf.h" #include #include #include @@ -216,6 +219,75 @@ char *user_addr; /* user space address where stat buf goes */ return(r); } +/*===========================================================================* + * do_rdlink * + *===========================================================================*/ +PUBLIC int do_rdlink() +{ +/* Perform the readlink(name, buf, bufsiz) system call. */ + + register struct inode *ldip, *rip; + register int r; + char string[NAME_MAX]; + register char *p; + vir_bytes v; + block_t b; + struct buf *bp; + + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + if ( (ldip = last_dir(user_path, string)) == NIL_INODE) return(err_code); + + /* Get final component of the path. */ + rip = advance(ldip, string); + put_inode(ldip); + + if (rip == NIL_INODE) return(err_code); + + if ((rip->i_mode & I_TYPE) == I_SYMBOLIC_LINK) { +#define bufsiz m1_i2 + int len = MIN(rip->i_size, m_in.bufsiz); + b = read_map(rip, 0); + bp = get_block(rip->i_dev, b, NORMAL); + /* Copy the name to user space. */ + r = sys_datacopy(FS_PROC_NR, (phys_bytes) bp->b_data, + who, (phys_bytes) m_in.name2, (phys_bytes) len); + put_block(bp, NORMAL); + r = rip->i_size; + } else + r = EINVAL; + put_inode(rip); /* release the inode */ + return(r); +} + +/*===========================================================================* + * do_lstat * + *===========================================================================*/ +PUBLIC int do_lstat() +{ +/* Perform the lstat(name, buf) system call. */ + + register struct inode *ldip, *rip; + register int r; + char string[NAME_MAX]; + register char *p; + + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + + /* Can't use eat_path() since it will traverse the link */ + if ( (ldip = last_dir(user_path, string)) == NIL_INODE) return(err_code); + + /* Get final component of the path. */ + rip = advance(ldip, string); + put_inode(ldip); + + if (rip == NIL_INODE) return(err_code); + + r = stat_inode(rip, NIL_FILP, m_in.name2); /* Work just like stat */ + + put_inode(rip); /* Release the inode */ + return(r); +} + /*===========================================================================* * do_fstatfs * *===========================================================================*/ @@ -237,3 +309,4 @@ PUBLIC int do_fstatfs() return(r); } +/** stadir.c **/ diff --git a/servers/fs/table.c b/servers/fs/table.c index 45ebd1c70..41dda3c7a 100644 --- a/servers/fs/table.c +++ b/servers/fs/table.c @@ -60,12 +60,12 @@ PUBLIC _PROTOTYPE (int (*call_vec[]), (void) ) = { do_pipe, /* 42 = pipe */ no_sys, /* 43 = times */ no_sys, /* 44 = (prof) */ - no_sys, /* 45 = unused */ + do_symlink, /* 45 = symlink */ do_set, /* 46 = setgid */ no_sys, /* 47 = getgid */ no_sys, /* 48 = (signal)*/ - no_sys, /* 49 = unused */ - no_sys, /* 50 = unused */ + do_rdlink, /* 49 = readlink*/ + do_lstat, /* 50 = lstat */ no_sys, /* 51 = (acct) */ no_sys, /* 52 = (phys) */ no_sys, /* 53 = (lock) */