/* * traditional disk filesystem */ #include "sys/param.h" #include "sys/systm.h" #include "sys/ino.h" #include "sys/buf.h" #include "sys/filsys.h" #include "sys/mount.h" #include "sys/dir.h" #include "sys/user.h" #include "sys/inode.h" #include "sys/file.h" #include "sys/conf.h" #include "sys/vlimit.h" #include "sys/stat.h" #include "sys/filio.h" #include "sys/cmap.h" #include "sys/stream.h" int fsput(), fsupdat(), fsread(), fswrite(), fstrunc(), fsstat(); int fsnami(), fsmount(), fsioctl(), fsdirread(); struct inode *fsopen(); struct fstypsw fsfs = fsinit(fsput, fsupdat, fsread, fswrite, fstrunc, fsstat, fsnami, fsmount, fsioctl, fsopen, fsdirread); extern struct mount fsmtab[]; extern int fscnt; /* * silly mount/unmount routine for fs type 0 */ fsmount(sip, ip, flag, mnt, fstyp) register struct inode *ip, *sip; int flag, mnt, fstyp; { if (!suser()) return; if (mnt) fson(sip, ip, flag, fstyp); else fsoff(ip, fstyp); } /* * mount a type 0 filesystem (sip on ip) * maintain the mount table for vm code's sake * * magic to help booting: * if rootdir == NULL, we are being called from iinit, so * - allow s_valid == 0 on bitmapped filesystems so root may be mounted * - call clkinit to set the clock from the super-block time */ fson(sip, ip, ronly, fstyp) register struct inode *ip, *sip; int ronly, fstyp; { dev_t dev; register struct mount *mp; struct mount *emp; struct inode *rip; register struct filsys *fp; struct buf *bp; struct inode pi; /* primer */ if ((sip->i_mode&IFMT) != IFBLK) { u.u_error = EINVAL; return; } if (ip->i_fstyp == fstyp && ip->i_dev == sip->i_un.i_rdev) { u.u_error = EBUSY; return; } emp = NULL; for (mp = &fsmtab[fscnt-1]; mp >= fsmtab; mp--) { if (mp->m_dev == NULL) emp = mp; else if (mp->m_dev == sip) goto hopeless; } if (emp == NULL) goto hopeless; emp->m_dev = sip; dev = (dev_t)sip->i_un.i_rdev; bp = bread(dev, SUPERB); if(u.u_error) { emp->m_dev = NULL; brelse(bp); return; } bp->b_flags |= B_LOCKED; brelse(bp); fp = bp->b_un.b_filsys; fp->s_ilock = 0; fp->s_flock = 0; fp->s_ronly = ronly & 1; fp->s_nbehind = 0; fp->s_lasti = 1; if(fp->s_cylsize == 0) fp->s_cylsize = 40; /* transition hack */ if(fp->s_aspace == 0) fp->s_aspace = 4; /* likewise */ if(BITFS(dev) && !fp->s_valid && !fp->s_ronly && rootdir != NULL) goto failed; /* NOT IMPLEMENTED */ if(BITFS(dev) && fp->U.N.S_flag) { if(fsbiton(dev, fp)) goto failed; /* hack until pjw fixes chuck */ } else if (BITFS(dev) && fp->s_isize + BITMAP*BITCELL < fp->s_fsize) if(fsbiton(dev, fp)) goto failed; pi.i_dev = dev; pi.i_fstyp = fstyp; pi.i_un.i_bufp = bp; if ((rip = iget(&pi, dev, ROOTINO)) == NULL) goto failed; if (rip->i_count == 1 && fsiread(&pi, rip) < 0) goto failed; if (rip->i_count != 1) { /* already mounted */ iput(rip); goto failed; } emp->m_mroot = rip; ip->i_mroot = rip; rip->i_mpoint = ip; ip->i_count++; sip->i_count++; prele(rip); if (rootdir == NULL) clkinit(fp->s_time); return; failed: bp->b_flags &= ~B_LOCKED; emp->m_dev = NULL; hopeless: if (u.u_error == 0) u.u_error = EBUSY; } fsoff(mip, fstyp) register struct inode *mip; { dev_t dev; register struct mount *mp; register struct inode *rip; struct buf *bp; struct filsys *fp; rip = mip->i_mroot; for (mp = &fsmtab[fscnt-1]; mp >= fsmtab; mp--) if (mp->m_mroot == rip) break; if (mp < fsmtab) panic("umount mp"); plock(rip); xumount(rip); update(); /* silly */ if (rip->i_count > 1 || ifsbusy(rip)) { u.u_error = EBUSY; prele(rip); return; } plock(mip); mip->i_mroot = NULL; iput(mip); dev = rip->i_dev; if ((bp = getblk(dev, SUPERB)) != rip->i_un.i_bufp) panic("umount"); iput(rip); bp->b_flags &= ~(B_LOCKED|B_ASYNC); /* ~async needed? */ if (bp->b_un.b_filsys->s_ronly) brelse(bp); else { if (BITFS(dev)) { fp = bp->b_un.b_filsys; fp->s_valid = 1; if(fp->U.N.S_flag) fsbitoff(dev, fp, 1); } bwrite(bp); } mpurge(mp - fsmtab); idec(mp->m_dev); mp->m_dev = NULL; } fsbiton(dev, fp) dev_t dev; register struct filsys *fp; { register int i, nf; register struct buf **bpp; register struct buf *bp; /* two chuck should do */ fp->U.N.S_flag = 1; /* bit map not in superblock */ nf = fp->U.N.S_bsize = BSIZE(dev) * NBBY; if (fp->s_ronly) return (0); nf = (fp->s_fsize + nf - 1) / nf; bpp = fp->U.N.S_blk; for(i = 0; i < nf; i++) bpp[i] = 0; for(i = 0; i < nf; i++) { bp = bread(dev, fp->s_fsize-nf+i); if(u.u_error) { brelse(bp); goto failed; } bp->b_flags |= B_LOCKED; brelse(bp); bpp[i] = bp; } return(0); failed: fsbitoff(dev, fp, 0); return(1); } fsbitoff(dev, fp, wrtflg) dev_t dev; struct filsys *fp; { register struct buf **bpp; register struct buf *bp; register int i, nf; if (fp->s_ronly) return; nf = fp->U.N.S_bsize; nf = (fp->s_fsize + nf-1)/nf; bpp = fp->U.N.S_blk; for(i = 0; i < nf; i++) if(bpp[i]) { if ((bp = getblk(dev, fp->s_fsize - nf + i)) != bpp[i]) panic("fsbitoff"); bp->b_flags &= ~B_LOCKED; if(wrtflg) bwrite(bp); bpp[i] = 0; /* safety first */ } } /* * fill in inode ip from the disk copy * in filesystem fip * ip is an inode fresh from iget */ fsiread(fip, ip) register struct inode *fip, *ip; { register struct buf *bp; daddr_t bno; if (ip->i_number <= 0) { u.u_error = EINVAL; iput(ip); return (-1); } bno = itod(fip->i_dev, (ino_t)ip->i_number); if (bno >= getfs(fip)->s_isize) { u.u_error = EINVAL; iput(ip); return (-1); } bp = bread(fip->i_dev, bno); if((bp->b_flags&B_ERROR) != 0) { brelse(bp); iput(ip); return(-1); } iexpand(ip, bp->b_un.b_dino+itoo(fip->i_dev, (ino_t)ip->i_number)); brelse(bp); ip->i_un.i_bufp = fip->i_un.i_bufp; ip->i_un.i_lastr = 0; return (0); } iexpand(ip, dp) register struct inode *ip; register struct dinode *dp; { register char *p1, *p2; register int i; ip->i_mode = dp->di_mode; ip->i_nlink = dp->di_nlink; ip->i_uid = dp->di_uid; ip->i_gid = dp->di_gid; ip->i_size = dp->di_size; p1 = (char *)ip->i_un.i_addr; p2 = (char *)dp->di_addr; for(i=0; i<NADDR; i++) { *p1++ = *p2++; *p1++ = *p2++; *p1++ = *p2++; *p1++ = 0; } } fsput(ip) struct inode *ip; { if (ip->i_nlink == 0) { fstrunc(ip); ifree(ip); ip->i_mode = 0; ip->i_flag |= IUPD|ICHG; } if ((ip->i_flag&(IUPD|IACC|ICHG)) != 0) fsiupdat(ip, &time, &time, 0); } fsupdat(ip, ta, tm, waitfor) register struct inode *ip; time_t *ta, *tm; { fsiupdat(ip, ta, tm, waitfor); if (ip->i_un.i_bufp->b_un.b_filsys->s_fmod) fsfupdat(ip); } /* * update the inode on disk * if the super-block is dirty, write it too */ fsiupdat(ip, ta, tm, waitfor) register struct inode *ip; time_t *ta, *tm; { register struct buf *bp; struct dinode *dp; register char *p1, *p2; register int i; if (ip->i_un.i_bufp->b_un.b_filsys->s_ronly) return; bp = bread(ip->i_dev, itod(ip->i_dev, ip->i_number)); if (bp->b_flags & B_ERROR) { brelse(bp); return; } dp = bp->b_un.b_dino; dp += itoo(ip->i_dev, ip->i_number); dp->di_mode = ip->i_mode; dp->di_nlink = ip->i_nlink; dp->di_uid = ip->i_uid; dp->di_gid = ip->i_gid; dp->di_size = ip->i_size; p1 = (char *)dp->di_addr; p2 = (char *)ip->i_un.i_addr; for(i=0; i<NADDR; i++) { *p1++ = *p2++; *p1++ = *p2++; *p1++ = *p2++; if(*p2++) printf("ino %d dev #%x addr #%x\n", ip->i_number, ip->i_dev, ip->i_un.i_addr[i]); } if(ip->i_flag&IACC) dp->di_atime = *ta; if(ip->i_flag&IUPD) dp->di_mtime = *tm; if(ip->i_flag&ICHG) dp->di_ctime = time; ip->i_flag &= ~(IUPD|IACC|ICHG); if (waitfor) bwrite(bp); else bdwrite(bp); } /* * write the super-block */ fsfupdat(ip) struct inode *ip; { register struct filsys *fp; struct buf *bp; fp = ip->i_un.i_bufp->b_un.b_filsys; if (fp->s_fmod == 0 || fp->s_ilock || fp->s_flock || fp->s_ronly) return; bp = getblk(ip->i_dev, SUPERB); if (bp->b_un.b_filsys != fp) panic("fsfupdat"); fp->s_fmod = 0; fp->s_time = time; bwrite(bp); } fsstat(ip, ub) register struct inode *ip; struct stat *ub; { register struct dinode *dp; register struct buf *bp; struct stat ds; /* * first copy from inode table */ ds.st_dev = ip->i_dev; ds.st_ino = ip->i_number; ds.st_mode = ip->i_mode; ds.st_nlink = ip->i_nlink; ds.st_uid = ip->i_uid; ds.st_gid = ip->i_gid; ds.st_rdev = (dev_t)ip->i_un.i_rdev; ds.st_size = ip->i_size; /* * next the dates in the disk */ bp = bread(ip->i_dev, itod(ip->i_dev, ip->i_number)); dp = bp->b_un.b_dino; dp += itoo(ip->i_dev, ip->i_number); ds.st_atime = dp->di_atime; ds.st_mtime = dp->di_mtime; ds.st_ctime = dp->di_ctime; brelse(bp); if (copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds)) < 0) u.u_error = EFAULT; } fstrunc(ip) register struct inode *ip; { register i; daddr_t bn; struct inode itmp; i = ip->i_mode & IFMT; if (i!=IFREG && i!=IFDIR && i!=IFLNK) return; /* * Clean inode on disk before freeing blocks * to insure no duplicates if system crashes. */ itmp = *ip; itmp.i_size = 0; for (i = 0; i < NADDR; i++) itmp.i_un.i_addr[i] = 0; itmp.i_flag |= ICHG|IUPD; fsiupdat(&itmp, &time, &time, 1); ip->i_flag &= ~(IUPD|IACC|ICHG); /* * Now return blocks to free list... if machine * crashes, they will be harmless MISSING blocks. */ for(i=NADDR-1; i>=0; i--) { bn = ip->i_un.i_addr[i]; if(bn == (daddr_t)0) continue; ip->i_un.i_addr[i] = (daddr_t)0; switch(i) { default: free(ip, bn); break; case NADDR-3: tloop(ip, bn, 0, 0); break; case NADDR-2: tloop(ip, bn, 1, 0); break; case NADDR-1: tloop(ip, bn, 1, 1); } } ip->i_size = 0; /* * Inode was written and flags updated above. * No need to modify flags here. */ } tloop(fip, bn, f1, f2) register struct inode *fip; daddr_t bn; { register i; register struct buf *bp; register daddr_t *bap; daddr_t nb; bp = NULL; for(i=NINDIR(fip->i_dev)-1; i>=0; i--) { if(bp == NULL) { bp = bread(fip->i_dev, bn); if (bp->b_flags & B_ERROR) { brelse(bp); return; } bap = bp->b_un.b_daddr; } nb = bap[i]; if(nb == (daddr_t)0) continue; if(f1) { brelse(bp); bp = NULL; tloop(fip, nb, f2, 0); } else free(fip, nb); } if(bp != NULL) brelse(bp); free(fip, bn); } fsioctl(ip, cmd, cmarg, flag) register struct inode *ip; caddr_t cmarg; { int fmt; register dev_t dev; fmt = ip->i_mode & IFMT; if (fmt != IFCHR) { if (cmd==FIONREAD && (fmt == IFREG || fmt == IFDIR)) { off_t nread = ip->i_size; /* - fp->f_offset */ if (copyout((caddr_t)&nread, cmarg, sizeof(off_t))) u.u_error = EFAULT; } else u.u_error = ENOTTY; return; } dev = ip->i_un.i_rdev; u.u_r.r_val1 = 0; (*cdevsw[major(dev)]->d_ioctl)(dev, cmd, cmarg, flag); } struct inode * fsopen(ip, rw) register struct inode *ip; { dev_t dev; register unsigned int maj; dev = (dev_t)ip->i_un.i_rdev; maj = major(dev); switch(ip->i_mode&IFMT) { case IFCHR: if(maj >= nchrdev || cdevsw[maj] == NULL) goto bad; if (cdevsw[maj]->qinfo) /* stream device */ return(stopen(cdevsw[major(dev)]->qinfo, dev, rw, ip)); (*cdevsw[maj]->d_open)(dev, rw); break; case IFBLK: if(maj >= nblkdev || bdevsw[maj] == NULL) goto bad; (*bdevsw[maj]->d_open)(dev, rw); } if (u.u_error) { iput(ip); return (NULL); } return (ip); bad: u.u_error = ENXIO; iput(ip); return(NULL); } fsread(ip) register struct inode *ip; { struct buf *bp; dev_t dev; daddr_t lbn, bn; off_t diff; register int on, type; register unsigned n; dev = (dev_t)ip->i_un.i_rdev; type = ip->i_mode&IFMT; if (type==IFCHR) { (*cdevsw[major(dev)]->d_read)(dev); return; } if (Lsign(u.u_offset) < 0) { /* and not IFCHR */ u.u_error = EINVAL; return; } if (type != IFBLK) { /* if offset >= size, avoid overflow */ if (Lsign(Lladd(u.u_offset, -ip->i_size))>=0) return; dev = ip->i_dev; } do { lbn = bn = Lshift(u.u_offset, BSHIFT(dev)); on = Ltol(u.u_offset) & BMASK(dev); n = MIN((unsigned)(BSIZE(dev)-on), u.u_count); if (type!=IFBLK) { diff = ip->i_size - Ltol(u.u_offset); if (diff <= 0) return; if (diff < n) n = diff; bn = bmap(ip, bn, B_READ); if (u.u_error) return; } else rablock = bn+1; if ((long)bn<0) { bp = geteblk(); clrbuf(bp); } else if (ip->i_un.i_lastr+1==lbn) bp = breada(dev, bn, rablock); else bp = bread(dev, bn); ip->i_un.i_lastr = lbn; n = MIN(n, BSIZE(dev)-bp->b_resid); if (n!=0) iomove(bp->b_un.b_addr+on, n, B_READ); brelse(bp); } while(u.u_error==0 && u.u_count!=0 && n!=0); } fsdirread(ip, len) struct inode *ip; { struct buf *bp = 0, *outb; dev_t dev; daddr_t bn, lbn; int on, type, j, m, new = 0; char *p, *lp, *lastp; struct direct *dp; char ncvt[24]; if(Lsign(u.u_offset) < 0) { u.u_error = EINVAL; return; } type = ip->i_mode & IFMT; if(type != IFDIR) { u.u_error = ENOTDIR; return; } dev = ip->i_dev; outb = geteblk(); /* to hold the stuff for the user */ if(len > BSIZE(dev)) len = BSIZE(dev); p = lp = outb->b_un.b_addr; lastp = p + len; loop: if(new <= 0) { /* get some goo */ lbn = bn = Lshift(u.u_offset, BSHIFT(dev)); on = Ltol(u.u_offset) & BMASK(dev); if(ip->i_size <= Ltol(u.u_offset)) goto done; bn = bmap(ip, bn, B_READ); if(u.u_error) return; if((long)bn < 0) { /* holes in dirs? */ u.u_offset = Lladd(u.u_offset, BSIZE(dev)-on); goto loop; /* try the next block */ } if(bp) brelse(bp); if(ip->i_un.i_lastr + 1 == lbn) bp = breada(dev, bn, rablock); else bp = bread(dev, bn); ip->i_un.i_lastr = lbn; new = BSIZE(dev) - on; if(new > BSIZE(dev) - bp->b_resid) /* io error? */ new = BSIZE(dev) - bp->b_resid; /* or paranoia */ if(new > ip->i_size - Ltol(u.u_offset)) new = ip->i_size - Ltol(u.u_offset); dp = (struct direct *) (bp->b_un.b_addr + on); } if(dp->d_ino == 0) goto incr; for(m = dp->d_ino, j = sizeof(ncvt)-1; m; j--) { ncvt[j] = m%10 + '0'; m /= 10; } for(++j; j < sizeof(ncvt) && p < lastp; ) *p++ = ncvt[j++]; if(j != sizeof(ncvt) || p >= lastp) goto early; *p++ = '\t'; for(j = 0; j < DIRSIZ && dp->d_name[j] && p < lastp; j++) *p++ = dp->d_name[j]; if(p >= lastp) goto early; *p++ = 0; incr: lp = p; new -= sizeof(*dp); u.u_offset = Lladd(u.u_offset, sizeof(*dp)); dp++; if(p < lastp) goto loop; done: j = lp - outb->b_un.b_addr; if(j > 0) if(copyout((caddr_t)outb->b_un.b_addr, u.u_base, j)) u.u_error = EFAULT; if(bp) brelse(bp); brelse(outb); u.u_r.r_val1 = j; return; early: *lp = 0; if(lp > outb->b_un.b_addr) goto done; u.u_error = ENOSPC; if(bp) brelse(bp); brelse(outb); } fswrite(ip) register struct inode *ip; { struct buf *bp; dev_t dev; daddr_t bn; register int on, type; register unsigned n; register int i; dev = (dev_t)ip->i_un.i_rdev; type = ip->i_mode&IFMT; if (type==IFCHR) { ip->i_flag |= IUPD|ICHG; (*cdevsw[major(dev)]->d_write)(dev); return; } if (Lsign(u.u_offset) < 0) { /* and not IFCHR */ u.u_error = EINVAL; return; } if (u.u_count == 0) return; if ((ip->i_mode&IFMT)==IFREG && (Lsign(Luadd(u.u_offset, u.u_count))!=0 || Ltol(u.u_offset) + u.u_count > u.u_limit[LIM_FSIZE])) { u.u_error = EMFILE; return; } if (type != IFBLK) dev = ip->i_dev; do { bn = Lshift(u.u_offset, BSHIFT(dev)); on = Ltol(u.u_offset) & BMASK(dev); n = MIN((unsigned)(BSIZE(dev)-on), u.u_count); if (type!=IFBLK) { bn = bmap(ip, bn, B_WRITE); if((long)bn<0) return; } i = getfsx(dev); if (bn && mfind(i, bn)) munhash(i, bn); if(n == BSIZE(dev)) bp = getblk(dev, bn); else bp = bread(dev, bn); iomove(bp->b_un.b_addr+on, n, B_WRITE); if(u.u_error != 0) brelse(bp); else { if ((ip->i_mode&IFMT) == IFDIR && ((struct direct *)(bp->b_un.b_addr+on))->d_ino == 0) bwrite(bp); /* consistency */ else if ((n+on) == BSIZE(dev)) bawrite(bp); else bdwrite(bp); } if(Ltol(u.u_offset) > ip->i_size && (type==IFDIR || type==IFREG || type==IFLNK)) ip->i_size = Ltol(u.u_offset); ip->i_flag |= IUPD|ICHG; } while(u.u_error==0 && u.u_count!=0); } /* * Getfsx returns the index in the file system * table of the specified device. The swap device * is also assigned a pseudo-index. The index may * be used as a compressed indication of the location * of a block, recording * <getfsx(dev),blkno> * rather than * <dev, blkno> * provided the information need remain valid only * as long as the file system is mounted. * * only the vm code calls this. */ getfsx(dev) dev_t dev; { register struct mount *mp; if (dev == swapdev) return (MSWAPX); for (mp = &fsmtab[fscnt-1]; mp >= fsmtab; mp--) { if (mp->m_dev == NULL) continue; if (mp->m_dev->i_un.i_rdev == dev) return (mp - fsmtab); } return (-1); }