ast@cs.vu.nl (Andy Tanenbaum) (07/01/87)
: This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin echo Extracting \l\i\n\k\.\c sed 's/^X//' > \l\i\n\k\.\c << '+ END-OF-FILE '\l\i\n\k\.\c X/* This file handles the LINK and UNLINK system calls. It also deals with X * deallocating the storage used by a file when the last UNLINK is done to a X * file and the blocks must be returned to the free block pool. X * X * The entry points into this file are X * do_link: perform the LINK system call X * do_unlink: perform the UNLINK system call X * truncate: release all the blocks associated with an inode X */ X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/error.h" X#include "const.h" X#include "type.h" X#include "buf.h" X#include "file.h" X#include "fproc.h" X#include "glo.h" X#include "inode.h" X#include "param.h" X X/*===========================================================================* X * do_link * X *===========================================================================*/ XPUBLIC int do_link() X{ X/* Perform the link(name, name2) system call. */ X X register struct inode *ip, *rip; X register int r; X char string[NAME_SIZE]; X struct inode *new_ip; X extern struct inode *advance(), *last_dir(), *eat_path(); X X /* See if 'name' (file to be linked) exists. */ X if (fetch_name(name1, name1_length, M1) != OK) return(err_code); X if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); X X /* Check to see if the file has maximum number of links already. */ X r = OK; X if ( (rip->i_nlinks & BYTE) == MAX_LINKS) r = EMLINK; X X /* Only super_user may link to directories. */ X if (r == OK) X if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM; X X /* If error with 'name', return the inode. */ X if (r != OK) { X put_inode(rip); X return(r); X } X X /* Does the final directory of 'name2' exist? */ X if (fetch_name(name2, name2_length, M1) != OK) { X put_inode(rip); X return(err_code); X } X if ( (ip = last_dir(user_path, string)) == NIL_INODE) r = err_code; X X /* If 'name2' exists in full (even if no space) set 'r' to error. */ X if (r == OK) { X if ( (new_ip = advance(ip, string)) == NIL_INODE) { X r = err_code; X if (r == ENOENT) r = OK; X } else { X put_inode(new_ip); X r = EEXIST; X } X } X X /* Check for links across devices. */ X if (r == OK) X if (rip->i_dev != ip->i_dev) r = EXDEV; X X /* Try to link. */ X if (r == OK) X r = search_dir(ip, string, &rip->i_num, ENTER); X X /* If success, register the linking. */ X if (r == OK) { X rip->i_nlinks++; X rip->i_dirt = DIRTY; X } X X /* Done. Release both inodes. */ X put_inode(rip); X put_inode(ip); X return(r); X} X X X/*===========================================================================* X * do_unlink * X *===========================================================================*/ XPUBLIC int do_unlink() X{ X/* Perform the unlink(name) system call. */ X X register struct inode *rip, *rlast_dir_ptr; X register int r; X inode_nr numb; X char string[NAME_SIZE]; X extern struct inode *advance(), *last_dir(); X X /* Get the last directory in the path. */ X if (fetch_name(name, name_length, M3) != OK) return(err_code); X if ( (rlast_dir_ptr = last_dir(user_path, string)) == NIL_INODE) X return(err_code); X X /* The last directory exists. Does the file also exist? */ X r = OK; X if ( (rip = advance(rlast_dir_ptr, string)) == NIL_INODE) r = err_code; X X /* If error, return inode. */ X if (r != OK) { X put_inode(rlast_dir_ptr); X return(r); X } X X /* See if the file is a directory. */ X if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) X r = EPERM; /* only super_user can unlink directory */ X if (r == OK) X r = search_dir(rlast_dir_ptr, string, &numb, DELETE); X X if (r == OK) { X rip->i_nlinks--; X rip->i_dirt = DIRTY; X } X X /* If unlink was possible, it has been done, otherwise it has not. */ X put_inode(rip); X put_inode(rlast_dir_ptr); X return(r); X} X X X/*===========================================================================* X * truncate * X *===========================================================================*/ XPUBLIC truncate(rip) Xregister struct inode *rip; /* pointer to inode to be truncated */ X{ X/* Remove all the zones from the inode 'rip' and mark it dirty. */ X X register file_pos position; X register zone_type zone_size; X register block_nr b; X register zone_nr z, *iz; X register int scale; X register struct buf *bp; X register dev_nr dev; X extern struct buf *get_block(); X extern block_nr read_map(); X X dev = rip->i_dev; /* device on which inode resides */ X scale = scale_factor(rip); X zone_size = (zone_type) BLOCK_SIZE << scale; X if (rip->i_pipe == I_PIPE) rip->i_size = PIPE_SIZE; /* pipes can shrink */ X X /* Step through the file a zone at a time, finding and freeing the zones. */ X for (position = 0; position < rip->i_size; position += zone_size) { X if ( (b = read_map(rip, position)) != NO_BLOCK) { X z = (zone_nr) b >> scale; X free_zone(dev, z); X } X } X X /* All the data zones have been freed. Now free the indirect zones. */ X free_zone(dev, rip->i_zone[NR_DZONE_NUM]); /* single indirect zone */ X if ( (z = rip->i_zone[NR_DZONE_NUM+1]) != NO_ZONE) { X b = (block_nr) z << scale; X bp = get_block(dev, b, NORMAL); /* get double indirect zone */ X for (iz = &bp->b_ind[0]; iz < &bp->b_ind[NR_INDIRECTS]; iz++) { X free_zone(dev, *iz); X } X X /* Now free the double indirect zone itself. */ X put_block(bp, INDIRECT_BLOCK); X free_zone(dev, z); X } X X /* The inode being truncated might currently be open, so certain fields must X * be cleared immediately, even though these fields are also cleared by X * alloc_inode(). The function wipe_inode() does the dirty work in both cases. X */ X wipe_inode(rip); X} + END-OF-FILE link.c chmod 'u=rw,g=r,o=r' \l\i\n\k\.\c set `sum \l\i\n\k\.\c` sum=$1 case $sum in 36084) :;; *) echo 'Bad sum in '\l\i\n\k\.\c >&2 esac echo Extracting \m\i\s\c\.\c sed 's/^X//' > \m\i\s\c\.\c << '+ END-OF-FILE '\m\i\s\c\.\c X/* This file contains a collection of miscellaneous procedures. Some of them X * perform simple system calls. Some others do a little part of system calls X * that are mostly performed by the Memory Manager. X * X * The entry points into this file are X * do_dup: perform the DUP system call X * do_sync: perform the SYNC system call X * do_fork: adjust the tables after MM has performed a FORK system call X * do_exit: a process has exited; note that in the tables X * do_set: set uid or gid for some process X * do_revive: revive a process that was waiting for something (e.g. TTY) X */ X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/callnr.h" X#include "../h/com.h" X#include "../h/error.h" X#include "const.h" X#include "type.h" X#include "buf.h" X#include "dev.h" X#include "file.h" X#include "fproc.h" X#include "glo.h" X#include "inode.h" X#include "param.h" X#include "super.h" X X/*===========================================================================* X * do_dup * X *===========================================================================*/ XPUBLIC int do_dup() X{ X/* Perform the dup(fd) or dup(fd,fd2) system call. */ X X register int rfd; X register struct fproc *rfp; X struct filp *dummy; X int r; X extern struct filp *get_filp(); X X /* Is the file descriptor valid? */ X rfd = fd & ~DUP_MASK; /* kill off dup2 bit, if on */ X rfp = fp; X if (get_filp(rfd) == NIL_FILP) return(err_code); X X /* Distinguish between dup and dup2. */ X if (fd == rfd) { /* bit not on */ X /* dup(fd) */ X if ( (r = get_fd(0, &fd2, &dummy)) != OK) return(r); X } else { X /* dup2(fd, fd2) */ X if (fd2 < 0 || fd2 >= NR_FDS) return(EBADF); X if (rfd == fd2) return(fd2); /* ignore the call: dup2(x, x) */ X fd = fd2; /* prepare to close fd2 */ X do_close(); /* cannot fail */ X } X X /* Success. Set up new file descriptors. */ X rfp->fp_filp[fd2] = rfp->fp_filp[rfd]; X rfp->fp_filp[fd2]->filp_count++; X return(fd2); X} X X X/*===========================================================================* X * do_sync * X *===========================================================================*/ XPUBLIC int do_sync() X{ X/* Perform the sync() system call. Flush all the tables. */ X X register struct inode *rip; X register struct buf *bp; X register struct super_block *sp; X dev_nr d; X extern real_time clock_time(); X extern struct super_block *get_super(); X X /* The order in which the various tables are flushed is critical. The X * blocks must be flushed last, since rw_inode() and rw_super() leave their X * results in the block cache. X */ X X /* Update the time in the root super_block. */ X sp = get_super(ROOT_DEV); X sp->s_time = clock_time(); X if (sp->s_rd_only == FALSE) sp->s_dirt = DIRTY; X X /* Write all the dirty inodes to the disk. */ X for (rip = &inode[0]; rip < &inode[NR_INODES]; rip++) X if (rip->i_count > 0 && rip->i_dirt == DIRTY) rw_inode(rip, WRITING); X X /* Write all the dirty super_blocks to the disk. */ X for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) X if (sp->s_dev != NO_DEV && sp->s_dirt == DIRTY) rw_super(sp, WRITING); X X /* Write all the dirty blocks to the disk. First do drive 0, then the rest. X * This avoids starting drive 0, then starting drive 1, etc. X */ X for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) { X d = bp->b_dev; X if (d != NO_DEV && bp->b_dirt == DIRTY && ((d>>MINOR) & BYTE) == 0) X rw_block(bp, WRITING); X } X X for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) { X d = bp->b_dev; X if (d != NO_DEV && bp->b_dirt == DIRTY && ((d>>MINOR) & BYTE) != 0) X rw_block(bp, WRITING); X } X X return(OK); /* sync() can't fail */ X} X X X/*===========================================================================* X * do_fork * X *===========================================================================*/ XPUBLIC int do_fork() X{ X/* Perform those aspects of the fork() system call that relate to files. X * In particular, let the child inherit its parents file descriptors. X * The parent and child parameters tell who forked off whom. The file X * system uses the same slot numbers as the kernel. Only MM makes this call. X */ X X register struct fproc *cp; X register char *sptr, *dptr; X int i; X X /* Only MM may make this call directly. */ X if (who != MM_PROC_NR) return(ERROR); X X /* Copy the parent's fproc struct to the child. */ X sptr = (char *) &fproc[parent]; /* pointer to parent's 'fproc' struct */ X dptr = (char *) &fproc[child]; /* pointer to child's 'fproc' struct */ X i = sizeof(struct fproc); /* how many bytes to copy */ X while (i--) *dptr++ = *sptr++; /* fproc[child] = fproc[parent] */ X X /* Increase the counters in the 'filp' table. */ X cp = &fproc[child]; X for (i = 0; i < NR_FDS; i++) X if (cp->fp_filp[i] != NIL_FILP) cp->fp_filp[i]->filp_count++; X X /* Record the fact that both root and working dir have another user. */ X dup_inode(cp->fp_rootdir); X dup_inode(cp->fp_workdir); X return(OK); X} X X X/*===========================================================================* X * do_exit * X *===========================================================================*/ XPUBLIC int do_exit() X{ X/* Perform the file system portion of the exit(status) system call. */ X X register int i, exitee; X X /* Only MM may do the EXIT call directly. */ X if (who != MM_PROC_NR) return(ERROR); X X /* Nevertheless, pretend that the call came from the user. */ X fp = &fproc[slot1]; /* get_filp() needs 'fp' */ X exitee = slot1; X X if (fp->fp_suspended == SUSPENDED) { X if (fp->fp_task == XPIPE) susp_count--; X pro = exitee; X do_unpause(); X fp->fp_suspended = NOT_SUSPENDED; X } X X /* Loop on file descriptors, closing any that are open. */ X for (i=0; i < NR_FDS; i++) { X fd = i; X do_close(); X } X X /* Release root and working directories. */ X put_inode(fp->fp_rootdir); X put_inode(fp->fp_workdir); X X return(OK); X} X X X/*===========================================================================* X * do_set * X *===========================================================================*/ XPUBLIC int do_set() X{ X/* Set uid or gid field. */ X X register struct fproc *tfp; X X /* Only MM may make this call directly. */ X if (who != MM_PROC_NR) return(ERROR); X X tfp = &fproc[slot1]; X if (fs_call == SETUID) { X tfp->fp_realuid = (uid) real_user_id; X tfp->fp_effuid = (uid) eff_user_id; X } X if (fs_call == SETGID) { X tfp->fp_effgid = (gid) eff_grp_id; X tfp->fp_realgid = (gid) real_grp_id; X } X return(OK); X} X X X/*===========================================================================* X * do_revive * X *===========================================================================*/ XPUBLIC int do_revive() X{ X/* A task, typically TTY, has now gotten the characters that were needed for a X * previous read. The process did not get a reply when it made the call. X * Instead it was suspended. Now we can send the reply to wake it up. This X * business has to be done carefully, since the incoming message is from X * a task (to which no reply can be sent), and the reply must go to a process X * that blocked earlier. The reply to the caller is inhibited by setting the X * 'dont_reply' flag, and the reply to the blocked process is done explicitly X * in revive(). X */ X X if (who > 0) return(EPERM); X revive(m.REP_PROC_NR, m.REP_STATUS); X dont_reply = TRUE; /* don't reply to the TTY task */ X return(OK); X} + END-OF-FILE misc.c chmod 'u=rw,g=r,o=r' \m\i\s\c\.\c set `sum \m\i\s\c\.\c` sum=$1 case $sum in 04234) :;; *) echo 'Bad sum in '\m\i\s\c\.\c >&2 esac echo Extracting \p\i\p\e\.\c sed 's/^X//' > \p\i\p\e\.\c << '+ END-OF-FILE '\p\i\p\e\.\c X/* This file deals with the suspension and revival of processes. A process can X * be suspended because it wants to read or write from a pipe and can't, or X * because it wants to read or write from a special file and can't. When a X * process can't continue it is suspended, and revived later when it is able X * to continue. X * X * The entry points into this file are X * do_pipe: perform the PIPE system call X * pipe_check: check to see that a read or write on a pipe is feasible now X * suspend: suspend a process that cannot do a requested read or write X * release: check to see if a suspended process can be released and do it X * revive: mark a suspended process as able to run again X * do_unpause: a signal has been sent to a process; see if it suspended X */ X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/callnr.h" X#include "../h/com.h" X#include "../h/error.h" X#include "../h/signal.h" X#include "const.h" X#include "type.h" X#include "file.h" X#include "fproc.h" X#include "glo.h" X#include "inode.h" X#include "param.h" X XPRIVATE message mess; X X/*===========================================================================* X * do_pipe * X *===========================================================================*/ XPUBLIC int do_pipe() X{ X/* Perform the pipe(fil_des) system call. */ X X register struct fproc *rfp; X register struct inode *rip; X int r; X dev_nr device; X struct filp *fil_ptr0, *fil_ptr1; X int fil_des[2]; /* reply goes here */ X extern struct inode *alloc_inode(); X X /* Acquire two file descriptors. */ X rfp = fp; X if ( (r = get_fd(R_BIT, &fil_des[0], &fil_ptr0)) != OK) return(r); X rfp->fp_filp[fil_des[0]] = fil_ptr0; X fil_ptr0->filp_count = 1; X if ( (r = get_fd(W_BIT, &fil_des[1], &fil_ptr1)) != OK) { X rfp->fp_filp[fil_des[0]] = NIL_FILP; X fil_ptr0->filp_count = 0; X return(r); X } X rfp->fp_filp[fil_des[1]] = fil_ptr1; X fil_ptr1->filp_count = 1; X X /* Make the inode in the current working directory. */ X device = rfp->fp_workdir->i_dev; /* inode dev is same as working dir */ X if ( (rip = alloc_inode(device, I_REGULAR)) == NIL_INODE) { X rfp->fp_filp[fil_des[0]] = NIL_FILP; X fil_ptr0->filp_count = 0; X rfp->fp_filp[fil_des[1]] = NIL_FILP; X fil_ptr1->filp_count = 0; X return(err_code); X } X X rip->i_pipe = I_PIPE; X fil_ptr0->filp_ino = rip; X dup_inode(rip); /* for double usage */ X fil_ptr1->filp_ino = rip; X rw_inode(rip, WRITING); /* mark inode as allocated */ X reply_i1 = fil_des[0]; X reply_i2 = fil_des[1]; X return(OK); X} X X X/*===========================================================================* X * pipe_check * X *===========================================================================*/ XPUBLIC int pipe_check(rip, rw_flag, virgin, bytes, position) Xregister struct inode *rip; /* the inode of the pipe */ Xint rw_flag; /* READING or WRITING */ Xint virgin; /* 1 if no data transferred yet, else 0 */ Xregister int bytes; /* bytes to be read or written (all chunks) */ Xregister file_pos *position; /* pointer to current file position */ X{ X/* Pipes are a little different. If a process reads from an empty pipe for X * which a writer still exists, suspend the reader. If the pipe is empty X * and there is no writer, return 0 bytes. If a process is writing to a X * pipe and no one is reading from it, give a broken pipe error. X */ X X extern struct filp *find_filp(); X X /* If reading, check for empty pipe. */ X if (rw_flag == READING) { X if (*position >= rip->i_size) { X /* Process is reading from an empty pipe. */ X if (find_filp(rip, W_BIT) != NIL_FILP) { X /* Writer exists; suspend rdr if no data already read.*/ X if (virgin) suspend(XPIPE); /* block reader */ X X /* If need be, activate sleeping writer. */ X if (susp_count > 0) release(rip, WRITE, 1); X } X return(0); X } X } else { X /* Process is writing to a pipe. */ X if (bytes > PIPE_SIZE) return(EFBIG); X if (find_filp(rip, R_BIT) == NIL_FILP) { X /* Tell MM to generate a SIGPIPE signal. */ X mess.m_type = KSIG; X mess.PROC1 = fp - fproc; X mess.SIG_MAP = 1 << (SIGPIPE - 1); X send(MM_PROC_NR, &mess); X return(EPIPE); X } X X if (*position + bytes > PIPE_SIZE) { X suspend(XPIPE); /* stop writer -- pipe full */ X return(0); X } X X /* Writing to an empty pipe. Search for suspended reader. */ X if (*position == 0) release(rip, READ, 1); X } X X return(1); X} X X X/*===========================================================================* X * suspend * X *===========================================================================*/ XPUBLIC suspend(task) Xint task; /* who is proc waiting for? (PIPE = pipe) */ X{ X/* Take measures to suspend the processing of the present system call. X * Store the parameters to be used upon resuming in the process table. X * (Actually they are not used when a process is waiting for an I/O device, X * but they are needed for pipes, and it is not worth making the distinction.) X */ X X if (task == XPIPE) susp_count++; /* count procs suspended on pipe */ X fp->fp_suspended = SUSPENDED; X fp->fp_fd = fd << 8 | fs_call; X fp->fp_buffer = buffer; X fp->fp_nbytes = nbytes; X fp->fp_task = -task; X dont_reply = TRUE; /* do not send caller a reply message now */ X} X X X/*===========================================================================* X * release * X *===========================================================================*/ XPUBLIC release(ip, call_nr, count) Xregister struct inode *ip; /* inode of pipe */ Xint call_nr; /* READ or WRITE */ Xint count; /* max number of processes to release */ X{ X/* Check to see if any process is hanging on the pipe whose inode is in 'ip'. X * If one is, and it was trying to perform the call indicated by 'call_nr' X * (READ or WRITE), release it. X */ X X register struct fproc *rp; X X /* Search the proc table. */ X for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) { X if (rp->fp_suspended == SUSPENDED && (rp->fp_fd & BYTE) == call_nr && X rp->fp_filp[rp->fp_fd>>8]->filp_ino == ip) { X revive(rp - fproc, 0); X susp_count--; /* keep track of who is suspended */ X if (--count == 0) return; X } X } X} X X X/*===========================================================================* X * revive * X *===========================================================================*/ XPUBLIC revive(proc_nr, bytes) Xint proc_nr; /* process to revive */ Xint bytes; /* if hanging on task, how many bytes read */ X{ X/* Revive a previously blocked process. When a process hangs on tty, this X * is the way it is eventually released. X */ X X register struct fproc *rfp; X X if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("revive err", proc_nr); X rfp = &fproc[proc_nr]; X if (rfp->fp_suspended == NOT_SUSPENDED || rfp->fp_revived == REVIVING)return; X X /* The 'reviving' flag only applies to pipes. Processes waiting for TTY get X * a message right away. The revival process is different for TTY and pipes. X * For TTY revival, the work is already done, for pipes it is not: the proc X * must be restarted so it can try again. X */ X if (rfp->fp_task == XPIPE) { X /* Revive a process suspended on a pipe. */ X rfp->fp_revived = REVIVING; X reviving++; /* process was waiting on pipe */ X } else { X /* Revive a process suspended on TTY or other device. */ X rfp->fp_suspended = NOT_SUSPENDED; X rfp->fp_nbytes = bytes; /* pretend it only wants what there is */ X reply(proc_nr, bytes); /* unblock the process */ X } X} X X X/*===========================================================================* X * do_unpause * X *===========================================================================*/ XPUBLIC int do_unpause() X{ X/* A signal has been sent to a user who is paused on the file system. X * Abort the system call with the EINTR error message. X */ X X register struct fproc *rfp; X int proc_nr, task, fild; X struct filp *f; X dev_nr dev; X extern struct filp *get_filp(); X X if (who > MM_PROC_NR) return(EPERM); X proc_nr = pro; X if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("unpause err 1", proc_nr); X rfp = &fproc[proc_nr]; X if (rfp->fp_suspended == NOT_SUSPENDED) return(OK); X task = -rfp->fp_task; X X if (task != XPIPE) { X fild = rfp->fp_fd >> 8; /* extract file descriptor */ X if (fild < 0 || fild >= NR_FDS) return(EBADF); X f = rfp->fp_filp[fild]; X dev = f->filp_ino->i_zone[0]; /* device on which proc is hanging */ X mess.TTY_LINE = (dev >> MINOR) & BYTE; X mess.PROC_NR = proc_nr; X mess.m_type = CANCEL; X if (sendrec(task, &mess) != OK) panic("unpause err 3", NO_NUM); X while (mess.REP_PROC_NR != proc_nr) { X revive(mess.REP_PROC_NR, mess.REP_STATUS); X if (receive(task, &m) != OK) panic("unpause err 3", NO_NUM); X } X revive(proc_nr, EINTR); /* signal interrupted call */ X } X X return(OK); X} + END-OF-FILE pipe.c chmod 'u=rw,g=r,o=r' \p\i\p\e\.\c set `sum \p\i\p\e\.\c` sum=$1 case $sum in 23565) :;; *) echo 'Bad sum in '\p\i\p\e\.\c >&2 esac exit 0