[comp.os.minix] MINIX 1.2 fs changes

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