/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ /* * SCCSID: @(#)rdwri.c 3.0 4/21/86 */ #include <sys/param.h> #include <sys/systm.h> #include <sys/inode.h> #include <sys/file.h> #include <sys/dir.h> #include <sys/user.h> #include <sys/buf.h> #include <sys/conf.h> /* * Read the file corresponding to * the inode pointed at by the argument. * The actual read arguments are found * in the variables: * u_base core address for destination * u_offset byte offset in file * u_count number of bytes to read * u_segflg read to kernel/user/user I */ readi(ip) register struct inode *ip; { struct buf *bp; dev_t dev; daddr_t lbn, bn; off_t diff; register on, n; register type; if(u.u_count == 0) return; if(u.u_offset < 0) { u.u_error = EINVAL; return; } dev = (dev_t)ip->i_rdev; type = ip->i_mode&IFMT; switch(type) { case IFCHR: ip->i_flag |= IACC; (*cdevsw[major(dev)].d_read)(dev); break; case IFIFO: while (ip->i_size == 0) { if (ip->i_fwcnt == 0) return; if (u.u_fmode&FNDELAY) return; ip->i_fflag |= IFIR; prele(ip); sleep((caddr_t)&ip->i_frcnt, PPIPE); plock(ip); } u.u_offset = ip->i_frptr; case IFBLK: case IFREG: case IFDIR: case IFLNK: do { lbn = bn = u.u_offset >> BSHIFT; on = u.u_offset & BMASK; n = MIN((unsigned)(BSIZE-on), u.u_count); if (type != IFBLK) { if (type == IFIFO) diff = ip->i_size; else diff = ip->i_size - u.u_offset; if (diff <= 0) break; if (diff < n) n = diff; bn = bmap(ip, bn, B_READ); if (u.u_error) break; dev = ip->i_dev; } else rablock = bn+1; #ifndef SELECT if ((long)bn<0) { if (type != IFREG) break; bp = geteblk(); clrbuf(bp); bp->b_resid = 0; } else if (ip->i_lastr+1 == lbn && (type != IFIFO) && (on+n) == BSIZE) bp = breada(dev, bn, rablock); else bp = bread(dev, bn); if ((on+n) == BSIZE) ip->i_lastr = lbn; #else SELECT /* * On fifo's we use the i_lastr data area for keeping * track of people doing selects, so we can't touch it. */ if ((long)bn<0) { if (type != IFREG) break; bp = geteblk(); clrbuf(bp); bp->b_resid = 0; if ((on+n) == BSIZE) ip->i_lastr = lbn; } else if (type != IFIFO && (on+n) == BSIZE) { if (ip->i_lastr+1 == lbn) bp = breada(dev, bn, rablock); else bp = bread(dev, bn); ip->i_lastr = lbn; } else bp = bread(dev, bn); #endif SELECT if (bp->b_resid) { n = 0; } if (n!=0) { pimove(PADDR(bp)+on, n, B_READ); } if (type == IFIFO) { ip->i_size -= n; if (u.u_offset >= PIPSIZ) u.u_offset = 0; if ((on+n) == BSIZE && ip->i_size < (PIPSIZ-BSIZE)) bp->b_flags &= ~B_DELWRI; } brelse(bp); ip->i_flag |= IACC; } while (u.u_error==0 && u.u_count!=0 && n!=0); if (type == IFIFO) { if (ip->i_size) ip->i_frptr = u.u_offset; else ip->i_frptr = ip->i_fwptr = 0; if (ip->i_fflag&IFIW) { ip->i_fflag &= ~IFIW; curpri = PPIPE; wakeup((caddr_t)&ip->i_fwcnt); } #ifdef SELECT if (ip->i_frsel) { selwakeup(ip->i_frsel, ip->i_fflag&IFI_RCOLL); ip->i_fflag &= ~IFI_RCOLL; ip->i_frsel = 0; } #endif SELECT } break; default: u.u_error = ENODEV; } } /* * Write the file corresponding to * the inode pointed at by the argument. * The actual write arguments are found * in the variables: * u_base core address for source * u_offset byte offset in file * u_count number of bytes to write * u_segflg write to kernel/user/user I */ writei(ip) register struct inode *ip; { struct buf *bp; dev_t dev; daddr_t bn; register n, on; register type; unsigned usave; struct direct *dp; struct buf *tdp; if(u.u_offset < 0) { u.u_error = EINVAL; return; } dev = (dev_t)ip->i_rdev; type = ip->i_mode&IFMT; switch(type) { case IFCHR: ip->i_flag |= IUPD|ICHG; (*cdevsw[major(dev)].d_write)(dev); break; case IFIFO: floop: usave = 0; while ((u.u_count+ip->i_size) > PIPSIZ) { if (ip->i_frcnt == 0) break; if ((u.u_count > PIPSIZ) && (ip->i_size < PIPSIZ)) { usave = u.u_count; u.u_count = PIPSIZ - ip->i_size; usave -= u.u_count; break; } if (u.u_fmode&FNDELAY) return; ip->i_fflag |= IFIW; prele(ip); sleep((caddr_t)&ip->i_fwcnt, PPIPE); plock(ip); } if (ip->i_frcnt == 0) { u.u_error = EPIPE; psignal(u.u_procp, SIGPIPE); break; } u.u_offset = ip->i_fwptr; case IFBLK: case IFREG: case IFDIR: case IFLNK: while (u.u_error==0 && u.u_count!=0) { bn = u.u_offset >> BSHIFT; on = u.u_offset & BMASK; n = MIN((unsigned)(BSIZE-on), u.u_count); if (type != IFBLK) { bn = bmap(ip, bn, B_WRITE); if (u.u_error) break; dev = ip->i_dev; } if (n == BSIZE) bp = getblk(dev, bn); else if (type==IFIFO && on==0 && ip->i_size < (PIPSIZ-BSIZE)) bp = getblk(dev, bn); else bp = bread(dev, bn); pimove(PADDR(bp)+on, n, B_WRITE); if (u.u_error) { brelse(bp); } else { dp = (struct direct *)((unsigned)mapin(bp)+on); if ((ip->i_mode&IFMT) == IFDIR && (dp->d_ino == 0)) { mapout(bp); /* * Writing to clear a directory entry. * Must insure the write occurs before * the inode is freed, or may end up * pointing at a new (different) file * if inode is quickly allocated again * and system crashes. */ bwrite(bp); } else { mapout(bp); tdp = bdevsw[major(bp->b_dev)].d_tab; tdp += dp_adj(bp->b_dev); if(((u.u_offset & BMASK) == 0) && ((tdp->b_flags&B_TAPE) == 0)) { bp->b_flags |= B_AGE; bawrite(bp); } else bdwrite(bp); } } #ifdef UCB_SYMLINKS if (type == IFREG || type == IFDIR || type == IFLNK) #else UCB_SYMLINKS if (type == IFREG || type == IFDIR) #endif UCB_SYMLINKS { if (u.u_offset > ip->i_size) ip->i_size = u.u_offset; } else if (type == IFIFO) { ip->i_size += n; if (u.u_offset == PIPSIZ) u.u_offset = 0; } ip->i_flag |= IUPD|ICHG; } if (type == IFIFO) { ip->i_fwptr = u.u_offset; if (ip->i_fflag&IFIR) { ip->i_fflag &= ~IFIR; curpri = PPIPE; wakeup((caddr_t)&ip->i_frcnt); } #ifdef SELECT if (ip->i_fwsel) { selwakeup(ip->i_fwsel, ip->i_fflag&IFI_WCOLL); ip->i_fwsel = 0; ip->i_fflag &= ~IFI_WCOLL; } #endif SELECT if (u.u_error==0 && usave!=0) { u.u_count = usave; goto floop; } } break; default: u.u_error = ENODEV; } } #ifndef MAX /* * Return the logical maximum * of the 2 arguments. */ max(a, b) unsigned a, b; { if(a > b) return(a); return(b); } /* * Return the logical minimum * of the 2 arguments. */ min(a, b) unsigned a, b; { if(a < b) return(a); return(b); } #endif /* * Move n bytes at byte location * cp to/from (flag) the * user/kernel (u.u_segflg) area starting at u.u_base. * Update all the arguments by the number * of bytes moved. */ pimove(cp, n, flag) long cp; register unsigned n; { if (copyio(cp, u.u_base, n, (u.u_segflg<<1)|flag)) u.u_error = EFAULT; else { u.u_base += n; u.u_offset += n; u.u_count -= n; } }