From: Ben Gras Date: Tue, 20 Dec 2005 14:23:44 +0000 (+0000) Subject: Merge of minix-vmd symlink code. X-Git-Tag: v3.1.2a~468 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/static/style.css?a=commitdiff_plain;h=6a911b5284d7d9e19c26fcb009e25c8bc071cc81;p=minix.git Merge of minix-vmd symlink code. . new_node() now returns inode of parent dir as argument that has to be put_node()d again by the caller of new_node(). it can also return the name of the last component as last_dir() did. . advance() takes a pointer to a pointer of an inode as the parent now. This parent can change, in which case the old one is put_node()d and a new one is put there. . eat_path() replaced by more flexible parse_path() . last_dir() replaced by call to parse_path(). . do_slink(), do_readlink(), do_lstat() and slink_traverse() added. Also added some truncate()/ftruncate()-introduction related changes. (e.g. renamed truncate() to truncate_inode().) --- diff --git a/servers/fs/const.h b/servers/fs/const.h index 245360591..71d8bad45 100644 --- a/servers/fs/const.h +++ b/servers/fs/const.h @@ -48,6 +48,18 @@ #define DELETE 2 /* tells search_dir to delete entry */ #define IS_EMPTY 3 /* tells search_dir to ret. OK or ENOTEMPTY */ +#define PATH_TRANSPARENT 000 /* parse_path stops at final object */ +#define PATH_PENULTIMATE 001 /* parse_path stops at last but one name */ +#define PATH_OPAQUE 002 /* parse_path stops at final name */ +#define PATH_NONSYMBOLIC 004 /* parse_path scans final name if symbolic */ +#define PATH_STRIPDOT 010 /* parse_path strips /. from path */ +#define EAT_PATH PATH_TRANSPARENT +#define EAT_PATH_OPAQUE PATH_OPAQUE +#define LAST_DIR PATH_PENULTIMATE +#define LAST_DIR_NOTDOT PATH_PENULTIMATE | PATH_STRIPDOT +#define LAST_DIR_EATSYM PATH_NONSYMBOLIC +#define SYMLOOP 16 + #define CLEAN 0 /* disk and memory copies identical */ #define DIRTY 1 /* disk and memory copies differ */ #define ATIME 002 /* set if atime field needs updating */ diff --git a/servers/fs/inode.c b/servers/fs/inode.c index a79005fca..a97779666 100644 --- a/servers/fs/inode.c +++ b/servers/fs/inode.c @@ -86,12 +86,12 @@ register struct inode *rip; /* pointer to inode to be released */ if (--rip->i_count == 0) { /* i_count == 0 means no one is using it now */ if (rip->i_nlinks == 0) { /* i_nlinks == 0 means free the inode. */ - truncate(rip); /* return all the disk blocks */ + truncate_inode(rip, 0, 0); /* return all the disk blocks */ rip->i_mode = I_NOT_ALLOC; /* clear I_TYPE field */ rip->i_dirt = DIRTY; free_inode(rip->i_dev, rip->i_num); } else { - if (rip->i_pipe == I_PIPE) truncate(rip); + if (rip->i_pipe == I_PIPE) truncate_inode(rip, 0, 0); } rip->i_pipe = NO_PIPE; /* should always be cleared */ if (rip->i_dirt == DIRTY) rw_inode(rip, WRITING); diff --git a/servers/fs/link.c b/servers/fs/link.c index 498a8095d..bf18021c0 100644 --- a/servers/fs/link.c +++ b/servers/fs/link.c @@ -36,7 +36,7 @@ PUBLIC int do_link() { /* Perform the link(name1, name2) system call. */ - register struct inode *ip, *rip; + struct inode *ip, *rip; register int r; char string[NAME_MAX]; struct inode *new_ip; @@ -69,7 +69,7 @@ PUBLIC int do_link() /* If 'name2' exists in full (even if no space) set 'r' to error. */ if (r == OK) { - if ( (new_ip = advance(ip, string)) == NIL_INODE) { + if ( (new_ip = advance(&ip, string)) == NIL_INODE) { r = err_code; if (r == ENOENT) r = OK; } else { @@ -121,7 +121,7 @@ PUBLIC int do_unlink() /* The last directory exists. Does the file also exist? */ r = OK; - if ( (rip = advance(rldirp, string)) == NIL_INODE) r = err_code; + if ( (rip = advance(&rldirp, string)) == NIL_INODE) r = err_code; /* If error, return inode. */ if (r != OK) { @@ -178,12 +178,12 @@ PUBLIC int do_rename() if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); if ( (old_dirp = last_dir(user_path, old_name))==NIL_INODE) return(err_code); - if ( (old_ip = advance(old_dirp, old_name)) == NIL_INODE) r = err_code; + if ( (old_ip = advance(&old_dirp, old_name)) == NIL_INODE) r = err_code; /* See if 'name2' (new name) exists. Get dir and file inodes. */ if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) r = err_code; if ( (new_dirp = last_dir(user_path, new_name)) == NIL_INODE) r = err_code; - new_ip = advance(new_dirp, new_name); /* not required to exist */ + new_ip = advance(&new_dirp, new_name); /* not required to exist */ if (old_ip != NIL_INODE) odir = ((old_ip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */ @@ -200,7 +200,7 @@ PUBLIC int do_rename() r = EINVAL; break; } - next_new_superdirp = advance(new_superdirp, dot2); + next_new_superdirp = advance(&new_superdirp, dot2); put_inode(new_superdirp); if (next_new_superdirp == new_superdirp) break; /* back at system root directory */ @@ -312,10 +312,12 @@ PUBLIC int do_rename() } /*===========================================================================* - * truncate * + * truncate_inode * *===========================================================================*/ -PUBLIC void truncate(rip) +PUBLIC void truncate_inode(rip, newsize, resetzones) register struct inode *rip; /* pointer to inode to be truncated */ +off_t newsize; /* inode must become this size (ignored) */ +int resetzones; /* zone references cleared on disk (ignored) */ { /* Remove all the zones from the inode 'rip' and mark it dirty. */ diff --git a/servers/fs/open.c b/servers/fs/open.c index 9052046a4..0f64d3705 100644 --- a/servers/fs/open.c +++ b/servers/fs/open.c @@ -13,6 +13,7 @@ #include "fs.h" #include #include +#include #include #include #include "buf.h" @@ -29,8 +30,8 @@ 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 pipe_open, (struct inode *rip,mode_t bits,int oflags)); -FORWARD _PROTOTYPE( struct inode *new_node, (char *path, mode_t bits, - zone_t z0) ); +FORWARD _PROTOTYPE( struct inode *new_node, (struct inode **ldirp, + char *path, mode_t bits, zone_t z0, int opaque, char *string)); /*===========================================================================* * do_creat * @@ -75,7 +76,7 @@ PRIVATE int common_open(register int oflags, mode_t omode) { /* Common code from do_creat and do_open. */ - register struct inode *rip; + struct inode *rip, *ldirp; int r, b, exist = TRUE; dev_t dev; mode_t bits; @@ -92,8 +93,9 @@ PRIVATE int common_open(register int oflags, mode_t omode) if (oflags & O_CREAT) { /* Create a new inode by calling new_node(). */ omode = I_REGULAR | (omode & ALL_MODES & fp->fp_umask); - rip = new_node(user_path, omode, NO_ZONE); + rip = new_node(&ldirp, user_path, omode, NO_ZONE, oflags&O_EXCL, NULL); r = err_code; + put_inode(ldirp); if (r == OK) exist = FALSE; /* we just created the file */ else if (r != EEXIST) return(r); /* other error */ else exist = !(oflags & O_EXCL); /* file exists, if the O_EXCL @@ -119,7 +121,7 @@ PRIVATE int common_open(register int oflags, mode_t omode) /* Truncate regular file if O_TRUNC. */ if (oflags & O_TRUNC) { if ((r = forbidden(rip, W_BIT)) !=OK) break; - truncate(rip); + truncate_inode(rip, 0, 0); wipe_inode(rip); /* Send the inode from the inode cache to the * block cache, so it gets written on the next @@ -195,29 +197,43 @@ PRIVATE int common_open(register int oflags, mode_t omode) /*===========================================================================* * new_node * *===========================================================================*/ -PRIVATE struct inode *new_node(char *path, mode_t bits, zone_t z0) +PRIVATE struct inode *new_node(struct inode **ldirp, + char *path, mode_t bits, zone_t z0, int opaque, char *parsed) { /* New_node() is called by common_open(), do_mknod(), and do_mkdir(). * In all cases it allocates a new inode, makes a directory entry for it on * the path 'path', and initializes it. It returns a pointer to the inode if * it can do this; otherwise it returns NIL_INODE. It always sets 'err_code' * to an appropriate value (OK or an error code). + * + * The parsed path rest is returned in 'parsed' if parsed is nonzero. It + * has to hold at least NAME_MAX bytes. */ - register struct inode *rlast_dir_ptr, *rip; + register struct inode *rip; register int r; 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); + *ldirp = parse_path(path, string, opaque ? LAST_DIR : LAST_DIR_EATSYM) +; + if (*ldirp == NIL_INODE) return(NIL_INODE); /* The final directory is accessible. Get final component of the path. */ - rip = advance(rlast_dir_ptr, string); + rip = advance(ldirp, string); + + if (S_ISDIR(bits) && + (*ldirp)->i_nlinks >= ((*ldirp)->i_sp->s_version == V1 ? + CHAR_MAX : SHRT_MAX)) { + /* New entry is a directory, alas we can't give it a ".." */ + put_inode(rip); + err_code = EMLINK; + return(NIL_INODE); + } + 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) { + if ( (rip = alloc_inode((*ldirp)->i_dev, bits)) == NIL_INODE) { /* Can't creat new inode: out of inodes. */ - put_inode(rlast_dir_ptr); return(NIL_INODE); } @@ -230,8 +246,7 @@ PRIVATE struct inode *new_node(char *path, mode_t bits, zone_t z0) rw_inode(rip, WRITING); /* force inode to disk now */ /* New inode acquired. Try to make directory entry. */ - if ((r = search_dir(rlast_dir_ptr, string, &rip->i_num,ENTER)) != OK) { - put_inode(rlast_dir_ptr); + if ((r = search_dir(*ldirp, string, &rip->i_num,ENTER)) != OK) { rip->i_nlinks--; /* pity, have to free disk inode */ rip->i_dirt = DIRTY; /* dirty inodes are written out */ put_inode(rip); /* this call frees the inode */ @@ -247,8 +262,12 @@ PRIVATE struct inode *new_node(char *path, mode_t bits, zone_t z0) r = err_code; } - /* Return the directory inode and exit. */ - put_inode(rlast_dir_ptr); + if(parsed) { /* Give the caller the parsed string if requested. */ + strncpy(parsed, string, NAME_MAX-1); + parsed[NAME_MAX-1] = '\0'; + } + + /* The caller has to return the directory inode (*ldirp). */ err_code = r; return(rip); } @@ -288,15 +307,16 @@ PUBLIC int do_mknod() /* Perform the mknod(name, mode, addr) system call. */ register mode_t bits, mode_bits; - struct inode *ip; + struct inode *ip, *ldirp; /* Only the super_user may make nodes other than fifos. */ mode_bits = (mode_t) m_in.mk_mode; /* mode of the inode */ if (!super_user && ((mode_bits & I_TYPE) != I_NAMED_PIPE)) return(EPERM); if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); bits = (mode_bits & I_TYPE) | (mode_bits & ALL_MODES & fp->fp_umask); - ip = new_node(user_path, bits, (zone_t) m_in.mk_z0); + ip = new_node(&ldirp, user_path, bits, (zone_t) m_in.mk_z0, TRUE, NULL); put_inode(ip); + put_inode(ldirp); return(err_code); } @@ -311,24 +331,16 @@ PUBLIC int do_mkdir() ino_t dot, dotdot; /* inode numbers for . and .. */ mode_t bits; /* mode bits for the new inode */ char string[NAME_MAX]; /* last component of the new dir's path name */ - register struct inode *rip, *ldirp; + struct inode *rip, *ldirp; - /* Check to see if it is possible to make another link in the parent dir. */ if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); - ldirp = last_dir(user_path, string); /* pointer to new dir's parent */ - if (ldirp == NIL_INODE) return(err_code); - if (ldirp->i_nlinks >= (ldirp->i_sp->s_version == V1 ? - CHAR_MAX : SHRT_MAX)) { - put_inode(ldirp); /* return parent */ - return(EMLINK); - } /* Next make the inode. If that fails, return error code. */ bits = I_DIRECTORY | (m_in.mode & RWX_MODES & fp->fp_umask); - rip = new_node(user_path, bits, (zone_t) 0); + rip = new_node(&ldirp, user_path, bits, (zone_t) 0, TRUE, string); if (rip == NIL_INODE || err_code == EEXIST) { put_inode(rip); /* can't make dir: it already exists */ - put_inode(ldirp); /* return parent too */ + put_inode(ldirp); return(err_code); } @@ -349,8 +361,11 @@ PUBLIC int do_mkdir() ldirp->i_nlinks++; /* this accounts for .. */ ldirp->i_dirt = DIRTY; /* mark parent's inode as dirty */ } else { - /* It was not possible to enter . or .. probably disk was full. */ - (void) search_dir(ldirp, string, (ino_t *) 0, DELETE); + /* It was not possible to enter . or .. probably disk was full - + * links counts haven't been touched. + */ + if(search_dir(ldirp, string, (ino_t *) 0, DELETE) != OK) + panic(__FILE__, "Dir disappeared ", rip->i_num); rip->i_nlinks--; /* undo the increment done in new_node() */ } rip->i_dirt = DIRTY; /* either way, i_nlinks has changed */ @@ -471,3 +486,54 @@ PUBLIC int do_lseek() m_out.reply_l1 = pos; /* insert the long into the output message */ return(OK); } + +/*===========================================================================* + * do_slink * + *===========================================================================*/ +PUBLIC int do_slink() +{ +/* Perform the symlink(name1, name2) system call. */ + + register int r; /* error code */ + char string[NAME_MAX]; /* last component of the new dir's path name */ + struct inode *sip; /* inode containing symbolic link */ + struct buf *bp; /* disk buffer for link */ + struct inode *ldirp; /* directory containing link */ + + if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) + return(err_code); + + if (m_in.name1_length <= 1 || m_in.name1_length > _MIN_BLOCK_SIZE+1) + return(ENAMETOOLONG); + + /* Create the inode for the symlink. */ + sip = new_node(&ldirp, user_path, (mode_t) (I_SYMBOLIC_LINK | RWX_MODES), + (zone_t) 0, TRUE, string); + + /* Allocate a disk block for the contents of the symlink. + * Copy contents of symlink (the name pointed to) into first disk block. + */ + if ((r = err_code) == OK) { + r = (bp = new_block(sip, (off_t) 0)) == NIL_BUF + ? err_code + : ( sip->i_size = m_in.name1_length-1, + sys_vircopy(who, D, (vir_bytes) m_in.name1, + SELF, D, (vir_bytes) bp->b_data, + (vir_bytes) m_in.name1_length-1)); + + put_block(bp, DIRECTORY_BLOCK); /* put_block() accepts NIL_BUF. */ + + if (r != OK) { + sip->i_nlinks = 0; + if (search_dir(ldirp, string, (ino_t *) 0, DELETE) != OK) + panic(__FILE__, "Symbolic link vanished", NO_NUM); + } + } + + /* put_inode() accepts NIL_INODE as a noop, so the below are safe. */ + put_inode(sip); + put_inode(ldirp); + + return(r); +} + diff --git a/servers/fs/path.c b/servers/fs/path.c index c954a64ad..514f627f2 100644 --- a/servers/fs/path.c +++ b/servers/fs/path.c @@ -6,11 +6,13 @@ * last_dir: find the final directory on a given path * advance: parse one component of a path name * search_dir: search a directory for a string and return its inode number + * */ #include "fs.h" #include #include +#include #include "buf.h" #include "file.h" #include "fproc.h" @@ -22,51 +24,30 @@ PUBLIC char dot2[3] = ".."; /* permissions for . and .. */ FORWARD _PROTOTYPE( char *get_name, (char *old_name, char string [NAME_MAX]) ); -/*===========================================================================* - * eat_path * - *===========================================================================*/ -PUBLIC struct inode *eat_path(path) -char *path; /* the path name to be parsed */ -{ -/* Parse the path 'path' and put its inode in the inode table. If not possible, - * 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 */ - - /* 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 */ - } - - /* 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); - put_inode(ldip); - return(rip); -} +FORWARD _PROTOTYPE( struct inode *ltraverse, (struct inode *rip, + char *path, char *suffix, struct inode *ldip) ); /*===========================================================================* - * last_dir * + * parse_path * *===========================================================================*/ -PUBLIC struct inode *last_dir(path, string) -char *path; /* the path name to be parsed */ -char string[NAME_MAX]; /* the final component is returned here */ +PUBLIC struct inode *parse_path(path, string, action) +char *path; /* the path name to be parsed */ +char string[NAME_MAX]; /* the final component is returned here */ +int action; /* action on last part of path */ { -/* Given a path, 'path', located in the fs address space, parse it as - * far as the last directory, fetch the inode for the last directory into - * the inode table, and return a pointer to the inode. In - * addition, return the final component of the path in 'string'. - * If the last directory can't be opened, return NIL_INODE and - * the reason for failure in 'err_code'. +/* This is the actual code for last_dir and eat_path. Return the inode of + * the last directory and the name of object within that directory, or the + * inode of the last object (an empty name will be returned). Names are + * returned in string. If string is null the name is discarded. The action + * code determines how "last" is defined. If an error occurs, NIL_INODE + * will be returned with an error code in err_code. */ - register struct inode *rip; - register char *new_name; - register struct inode *new_ip; + struct inode *rip, *dir_ip; + char *new_name; + struct inode *new_ip; + int symloop; + char lstring[NAME_MAX]; /* Is the path absolute or relative? Initialize 'rip' accordingly. */ rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir); @@ -79,6 +60,9 @@ char string[NAME_MAX]; /* the final component is returned here */ dup_inode(rip); /* inode will be returned with put_inode */ + symloop = 0; /* symbolic link traversal count */ + if (string == (char *) 0) string = lstring; + /* Scan the path component by component. */ while (TRUE) { /* Extract one component. */ @@ -86,7 +70,7 @@ char string[NAME_MAX]; /* the final component is returned here */ put_inode(rip); /* bad path in user space */ return(NIL_INODE); } - if (*new_name == '\0') { + if (*new_name == '\0' && (action & PATH_PENULTIMATE)) { if ( (rip->i_mode & I_TYPE) == I_DIRECTORY) { return(rip); /* normal exit */ } else { @@ -98,14 +82,128 @@ 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); + dir_ip = rip; + rip = advance(&dir_ip, string); + + if (rip == NIL_INODE) { + if (*new_name == '\0' && (action & PATH_NONSYMBOLIC) != 0) + return(dir_ip); + else { + put_inode(dir_ip); + return(NIL_INODE); + } + } + + /* The call to advance() succeeded. Fetch next component. */ + if (S_ISLNK(rip->i_mode)) { + if (*new_name != '\0' || (action & PATH_OPAQUE) == 0) { + if (*new_name != '\0') new_name--; + rip = ltraverse(rip, path, new_name, dir_ip); + put_inode(dir_ip); + if (++symloop > SYMLOOP) { + err_code = ELOOP; + put_inode(rip); + rip = NIL_INODE; + } + if (rip == NIL_INODE) return(NIL_INODE); + continue; + } + } else if (*new_name != '\0') { + put_inode(dir_ip); + path = new_name; + continue; + } + + /* Either last name reached or symbolic link is opaque */ + if ((action & PATH_NONSYMBOLIC) != 0) { + put_inode(rip); + return(dir_ip); + } else { + put_inode(dir_ip); + return(rip); + } + } +} + +/*===========================================================================* + * eat_path * + *===========================================================================*/ +PUBLIC struct inode *eat_path(path) +char *path; /* the path name to be parsed */ +{ + /* Parse the path 'path' and put its inode in the inode table. If not possible, + * return NIL_INODE as function value and an error code in 'err_code'. + */ + + return parse_path(path, (char *) 0, EAT_PATH); +} - /* The call to advance() succeeded. Fetch next component. */ - path = new_name; - rip = new_ip; +/*===========================================================================* + * last_dir * + *===========================================================================*/ +PUBLIC struct inode *last_dir(path, string) +char *path; /* the path name to be parsed */ +char string[NAME_MAX]; /* the final component is returned here */ +{ +/* Given a path, 'path', located in the fs address space, parse it as + * far as the last directory, fetch the inode for the last directory into + * the inode table, and return a pointer to the inode. In + * addition, return the final component of the path in 'string'. + * If the last directory can't be opened, return NIL_INODE and + * the reason for failure in 'err_code'. + */ + + return parse_path(path, string, LAST_DIR); +} + +/*===========================================================================* + * ltraverse * + *===========================================================================*/ +PRIVATE struct inode *ltraverse(rip, path, suffix, ldip) +register struct inode *rip; /* symbolic link */ +char *path; /* path containing link */ +char *suffix; /* suffix following link within path */ +register struct inode *ldip; /* directory containing link */ +{ +/* Traverse a symbolic link. Copy the link text from the inode and insert + * the text into the path. Return the inode of base directory and the + * ammended path. The symbolic link inode is always freed. The inode + * returned is already duplicated. NIL_INODE is returned on error. + */ + + block_t b; /* block containing link text */ + struct inode *bip; /* inode of base directory */ + struct buf *bp; /* buffer containing link text */ + size_t sl; /* length of link */ + size_t tl; /* length of suffix */ + char *sp; /* start of link text */ + char *ep; /* end of conditional segment */ + + bip = NIL_INODE; + bp = NIL_BUF; + + if ((b = read_map(rip, (off_t) 0)) != NO_BLOCK) { + bp = get_block(rip->i_dev, b, NORMAL); + sl = rip->i_size; + sp = bp->b_data; + + /* Insert symbolic text into path name. */ + tl = strlen(suffix); + if (sl > 0 && sl + tl <= PATH_MAX-1) { + memmove(path+sl, suffix, tl); + memmove(path, sp, sl); + path[sl+tl] = 0; + dup_inode(bip = path[0] == '/' ? fp->fp_rootdir : ldip); + } + } + + put_block(bp, DIRECTORY_BLOCK); + put_inode(rip); + if (bip == NIL_INODE) + { + err_code = ENOENT; } + return (bip); } /*===========================================================================* @@ -152,8 +250,8 @@ char string[NAME_MAX]; /* component extracted from 'old_name' */ /*===========================================================================* * advance * *===========================================================================*/ -PUBLIC struct inode *advance(dirp, string) -struct inode *dirp; /* inode for directory to be searched */ +PUBLIC struct inode *advance(pdirp, string) +struct inode **pdirp; /* inode for directory to be searched */ char string[NAME_MAX]; /* component name to look for */ { /* Given a directory and a component of a path, look up the component in @@ -161,13 +259,14 @@ char string[NAME_MAX]; /* component name to look for */ * slot. If it can't be done, return NIL_INODE. */ - register struct inode *rip; - struct inode *rip2; + register struct inode *rip, *dirp; register struct super_block *sp; int r, inumb; dev_t mnt_dev; ino_t numb; + dirp = *pdirp; + /* If 'string' is empty, yield same inode straight away. */ if (string[0] == '\0') { return(get_inode(dirp->i_dev, (int) dirp->i_num)); } @@ -189,20 +288,30 @@ char string[NAME_MAX]; /* component name to look for */ return(NIL_INODE); } + /* The following test is for "mountpoint/.." where mountpoint is a + * mountpoint. ".." will refer to the root of the mounted filesystem, + * but has to become a reference to the parent of the 'mountpoint' + * directory. + * + * This case is recognized by the looked up name pointing to a + * root inode, and the directory in which it is held being a + * root inode, _and_ the name[1] being '.'. (This is a test for '..' + * and excludes '.'.) + */ if (rip->i_num == ROOT_INODE) if (dirp->i_num == ROOT_INODE) { if (string[1] == '.') { for (sp = &super_block[1]; sp < &super_block[NR_SUPERS]; sp++){ if (sp->s_dev == rip->i_dev) { /* Release the root inode. Replace by the - * inode mounted on. + * inode mounted on. Update parent. */ put_inode(rip); + put_inode(dirp); mnt_dev = sp->s_imount->i_dev; inumb = (int) sp->s_imount->i_num; - rip2 = get_inode(mnt_dev, inumb); - rip = advance(rip2, string); - put_inode(rip2); + dirp = *pdirp = get_inode(mnt_dev, inumb); + rip = advance(pdirp, string); break; } } @@ -259,7 +368,9 @@ int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */ int extended = 0; /* If 'ldir_ptr' is not a pointer to a dir inode, error. */ - if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) return(ENOTDIR); + if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) { + return(ENOTDIR); + } r = OK; diff --git a/servers/fs/proto.h b/servers/fs/proto.h index 705f4692c..e3561fc09 100644 --- a/servers/fs/proto.h +++ b/servers/fs/proto.h @@ -74,7 +74,7 @@ _PROTOTYPE( void wipe_inode, (struct inode *rip) ); _PROTOTYPE( int do_link, (void) ); _PROTOTYPE( int do_unlink, (void) ); _PROTOTYPE( int do_rename, (void) ); -_PROTOTYPE( void truncate, (struct inode *rip) ); +_PROTOTYPE( void truncate_inode, (struct inode *rip, off_t len, int cz) ); /* lock.c */ _PROTOTYPE( int lock_op, (struct filp *f, int req) ); @@ -110,13 +110,16 @@ _PROTOTYPE( int do_lseek, (void) ); _PROTOTYPE( int do_mknod, (void) ); _PROTOTYPE( int do_mkdir, (void) ); _PROTOTYPE( int do_open, (void) ); +_PROTOTYPE( int do_slink, (void) ); /* path.c */ -_PROTOTYPE( struct inode *advance,(struct inode *dirp, char string[NAME_MAX])); +_PROTOTYPE( struct inode *advance,(struct inode **dirp, char string[NAME_MAX])); _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 *parse_path, (char *path, char string[NAME_MAX], + int action) ); /* pipe.c */ _PROTOTYPE( int do_pipe, (void) ); @@ -155,6 +158,8 @@ _PROTOTYPE( int do_chroot, (void) ); _PROTOTYPE( int do_fstat, (void) ); _PROTOTYPE( int do_stat, (void) ); _PROTOTYPE( int do_fstatfs, (void) ); +_PROTOTYPE( int do_rdlink, (void) ); +_PROTOTYPE( int do_lstat, (void) ); /* super.c */ _PROTOTYPE( bit_t alloc_bit, (struct super_block *sp, int map, bit_t origin)); diff --git a/servers/fs/stadir.c b/servers/fs/stadir.c index 4f8029c38..1e23f837f 100644 --- a/servers/fs/stadir.c +++ b/servers/fs/stadir.c @@ -7,12 +7,16 @@ * do_stat: perform the STAT system call * do_fstat: perform the FSTAT system call * do_fstatfs: perform the FSTATFS system call + * do_lstat: perform the LSTAT system call + * do_rdlink: perform the RDLNK system call */ #include "fs.h" #include #include #include +#include +#include "buf.h" #include "file.h" #include "fproc.h" #include "inode.h" @@ -237,3 +241,55 @@ PUBLIC int do_fstatfs() return(r); } +/*===========================================================================* + * do_lstat * + *===========================================================================*/ +PUBLIC int do_lstat() +{ +/* Perform the lstat(name, buf) system call. */ + + register int r; /* return value */ + register struct inode *rip; /* target inode */ + + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + if ((rip = parse_path(user_path, (char *) 0, EAT_PATH_OPAQUE)) == NIL_INODE) + return(err_code); + r = stat_inode(rip, NIL_FILP, m_in.name2); + put_inode(rip); + return(r); +} + +/*===========================================================================* + * do_rdlink * + *===========================================================================*/ +PUBLIC int do_rdlink() +{ +/* Perform the readlink(name, buf) system call. */ + + register int r; /* return value */ + block_t b; /* block containing link text */ + struct buf *bp; /* buffer containing link text */ + register struct inode *rip; /* target inode */ + + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + if ((rip = parse_path(user_path, (char *) 0, EAT_PATH_OPAQUE)) == NIL_INODE) + return(err_code); + + r = EACCES; + if (S_ISLNK(rip->i_mode) && (b = read_map(rip, (off_t) 0)) != NO_BLOCK) { + if (m_in.name2_length <= 0) r = EINVAL; + else if (m_in.name2_length < rip->i_size) r = ERANGE; + else { + bp = get_block(rip->i_dev, b, NORMAL); + r = sys_vircopy(SELF, D, (vir_bytes) bp->b_data, + who, D, (vir_bytes) m_in.name2, (vir_bytes) rip->i_size); + + if (r == OK) r = rip->i_size; + put_block(bp, DIRECTORY_BLOCK); + } + } + + put_inode(rip); + return(r); +} + diff --git a/servers/fs/super.c b/servers/fs/super.c index 0d2955802..4f1279fc2 100644 --- a/servers/fs/super.c +++ b/servers/fs/super.c @@ -130,7 +130,7 @@ bit_t bit_returned; /* number of bit to insert into the map */ k = conv2(sp->s_native, (int) bp->b_bitmap[word]); if (!(k & mask)) { panic(__FILE__,map == IMAP ? "tried to free unused inode" : - "tried to free unused block", NO_NUM); + "tried to free unused block", bit_returned); } k &= ~mask; diff --git a/servers/fs/table.c b/servers/fs/table.c index 052b7e34f..59f0c1de6 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 = symlink */ + do_slink, /* 45 = symlink */ do_set, /* 46 = setgid */ no_sys, /* 47 = getgid */ no_sys, /* 48 = (signal)*/ - no_sys, /* 49 = readlink*/ - no_sys, /* 50 = lstat */ + do_rdlink, /* 49 = readlink*/ + do_lstat, /* 50 = lstat */ no_sys, /* 51 = (acct) */ no_sys, /* 52 = (phys) */ no_sys, /* 53 = (lock) */ diff --git a/servers/pm/table.c b/servers/pm/table.c index 53e9630eb..954b45bc2 100644 --- a/servers/pm/table.c +++ b/servers/pm/table.c @@ -109,6 +109,10 @@ _PROTOTYPE (int (*call_vec[NCALLS]), (void) ) = { do_time, /* 90 = gettimeofday */ do_getset, /* 91 = seteuid */ do_getset /* 92 = setegid */ +#if 0 + no_sys, /* 93 = truncate */ + no_sys, /* 94 = ftruncate */ +#endif }; /* This should not fail with "array size is negative": */ extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1];