/***************************************************************** * * * Copyright (c) 1984 * * Massachusetts Institute of Technology * * * * This material is a component of the TRIX system, developed by * * D. Goddeau, J. Sieber, and S. Ward of the * * * * RTS group * * Laboratory for Computer Science * * Massachusetts Institute of Technology * * Cambridge, Massachusetts 02139 * * * * Permission to copy this software, to redistribute it, and to * * use it for any purpose is granted, subject to the conditions * * that (1) This copyright notice remain, unchanged and in full, * * on any copy or derivative of the software; and (2) that * * references to or documentation of any copy or derivative of * * this software acknowledge its source in accordance with the * * usual standards in academic research. * * * * MIT has made no warrantee or representation that the * * operation of this software will be error-free, and MIT is * * under no obligation to provide any services, by way of * * maintenance, update, or otherwise. * * * * In conjunction with products arising from the use of this * * material, there shall be no use of the name of the * * Massachusetts Institute of Technology nor of any adaptation * * thereof in any advertising, promotional, or sales literature * * without prior written consent from MIT in each case. * * * *****************************************************************/ #include "../h/param.h" #include "../h/args.h" #include "../h/calls.h" #include "../h/assert.h" #include "../h/link.h" #include "../h/status.h" #include "../h/protocol.h" #include "../h/unixtypes.h" #include "param.h" #include "fsys.h" #include "inode.h" static int bloop(); struct inode inodes[N_INODE]; int icachehits; int icachemiss; #define min(x,y) ((x) < (y) ? (x) : (y)) /* * return a pointer to the incore copy of the designated inode * (reading it in if necessary) * the inode's usage is incremented and it is locked */ struct inode * icache(fp, ino) register struct fsys *fp; register ino_t ino; { register struct inode *ip; struct inode *fip = NULLINODE; struct dinode dino; register struct dinode *dp; static struct inode *isearch = &inodes[0]; search: for(ip = isearch ; ip < &inodes[N_INODE] ; ip++) { if(ip->i_fsys == fp && ino == ip->i_number) { /* we found it in core */ if((ip->i_flag&ILOCK) != 0) { /* its locked */ DEBUGOUT(("sleeping on locked %d in ichache\n", ip->i_number)); ip->i_flag |= IWANT; t_SLEEP(fsys_sync, ip); /* better look again in case its gone now */ goto search; } if(!(ip->i_flag & IINUSE)) { /* a real hit -- the file is not currently open */ icachehits++; ++fp->fs_opncnt; } /* its ours for now */ ip->i_count++; ip->i_flag |= ILOCK|IINUSE; return(ip); } /* remember a free inode */ if(fip == NULLINODE && !(ip->i_flag & IINUSE)) fip = ip; } if(isearch != &inodes[0]) { /* no match from the search point -- try again from the beginning */ isearch = &inodes[0]; goto search; } if(fip == NULLINODE) { /* searched from the beginning and there were no free slots */ panic("no free inode slots"); /* should actually sleep and retry */ } /* continue search from here next time */ isearch = &fip[1]; /* inode is not in core -- allocate one and read it in */ icachemiss++; ip = fip; ++fp->fs_opncnt; ip->i_fsys = fp; ip->i_number = ino; ip->i_flag = ILOCK|IINUSE; ip->i_count = 1; ip->i_blocks = -BLOCK; /* don't match on the cache */ if(t_READ(fp->fs_dev, (off_t)itoo(ino), (addr_t)(&dino), (off_t)sizeof(dino)) != sizeof(dino)) { /* read got error */ panic("read error in icache"); return(NULLINODE); } /* convert from the on disk format to incore format */ dp = &dino; 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; ip->i_atime = dp->di_atime; ip->i_ctime = dp->di_ctime; ip->i_mtime = dp->di_mtime; l3tol((char *)ip->i_addr, (char *)dp->di_addr, NADDR); DEBUGOUT((" inum:%d mode:%o nlnk:%d uid:%d gid:%d size:%d\n", ip->i_number,ip->i_mode, ip->i_nlink, ip->i_uid, ip->i_gid, ip->i_size)); return(ip); } /* * allocate an unused inode on the given filesystem and make it the * given mode (for file creation) * the inode is returned in the state left by icache (usage=1 and locked) * and it has no links * the algorithm keeps up to NICINOD spare I nodes in the super block * when this runs out, a linear search through the I list is instituted * to pick up NICINOD more * the inodes in the list are not assumed to actually be free */ struct inode * ialloc(fp, mode) register struct fsys *fp; { register struct inode *ip; struct dinode dino[BSIZE/sizeof(struct dinode)]; int i; ino_t ino; /* wait for filesystem to be idle */ while(fp->fs_superb.s_ilock) { printf("sleeping on ilock in ialloc\n"); t_SLEEP(fsys_sync, fp); } loop: if(fp->fs_superb.s_ninode > 0) { /* get an inode from the free inode table in sblock */ ino = fp->fs_superb.s_inode[--fp->fs_superb.s_ninode]; ip = icache(fp, ino); if(ip == NULLINODE) return(NULLINODE); if(ip->i_mode == 0) { /* make sure its really free */ ip->i_size = 0; for (i=0; ii_addr[i] = 0; ip->i_blocks = -BLOCK; /* don't match on the cache */ ip->i_nlink = 0; ip->i_flag |= IACC|IUPD|ICHG; ip->i_uid = TRIXUID; ip->i_gid = TRIXGID; /* asking for unallocated type -> regular file */ if((mode & IFMT) == 0) mode |= IFREG; ip->i_mode = mode; fp->fs_superb.s_tinode--; fp->fs_superb.s_fmod = 1; return(ip); } /* it was allocated after all, look some more */ ifree(ip); goto loop; } /* get a "bunch" of free inodes */ fp->fs_superb.s_ilock++; /* this should really search from where it last stopped ... */ for(ino = 1 ; itoo(ino)>>BSHIFT < fp->fs_superb.s_isize ; ) { if(t_READ(fp->fs_dev, (off_t)itoo(ino), (addr_t)(dino), (off_t)sizeof(dino)) != sizeof(dino)) { /* read got error */ panic("read error in icache"); return(NULLINODE); } for(i = 0 ; i < sizeof(dino)/sizeof(dino[0]) ; i++, ino++) { if(dino[i].di_mode == 0) fp->fs_superb.s_inode[fp->fs_superb.s_ninode++] = ino; if(fp->fs_superb.s_ninode >= NICINOD) goto done; } } done: fp->fs_superb.s_ilock = 0; t_WAKEUP(fsys_sync, fp); if(fp->fs_superb.s_ninode > 0) goto loop; fsyserr("Out of inodes", fp); return(NULLINODE); } /* * free up a use of the incore copy of the designated inode * (writing it out if it is no longer needed) * if it is no longer active (usage=0) and there are no links to it free it * on disk * the inode is left unlocked with its usage decremented */ ifree(ip) register struct inode *ip; { if(ip->i_count == 1) { register struct fsys *fp; /* this is the last active use */ fp = ip->i_fsys; ip->i_flag |= ILOCK; if(ip->i_nlink <= 0) { /* the inode is really free (no links to it) */ ip->i_nlink = 0; itrunc(ip); ip->i_mode = 0; ip->i_flag |= IUPD|ICHG; if(!fp->fs_superb.s_ilock && fp->fs_superb.s_ninode < NICINOD) { /* remember the free inode */ fp->fs_superb.s_inode[fp->fs_superb.s_ninode++] = ip->i_number; } fp->fs_superb.s_tinode++; fp->fs_superb.s_fmod = 1; } /* update the disk copy before freeing the cached version */ iupdat(ip); /* mark the slot as free */ ip->i_flag &= ~IINUSE; ip->i_count--; iunlock(ip); /* if this is the last open file on the filesystem, unmount it */ if(--fp->fs_opncnt <= 0) { printf("FREEING FILESYSTEM (inum=%d)\n", ip->i_number); fsfree(fp); } } else { /* its still actively used */ ip->i_count--; iunlock(ip); } } /* * free all the disk blocks associated with the specified inode structure * the blocks of the file are removed in reverse order * this FILO algorithm will tend to maintain a contiguous free list * longer than FIFO. */ itrunc(ip) register struct inode *ip; { register i; register daddr_t bn; i = itype(ip); if(i != IFREG && i != IFDIR) return; /* free all the blocks associated with a file or directory */ for(i = NADDR-1 ; i >= 0 ; i--) { bn = ip->i_addr[i]; if(bn == (daddr_t)0) continue; ip->i_addr[i] = (daddr_t)0; switch(i) { default: /* direct blocks */ bfree(ip->i_fsys, bn); break; case NADDR-3: /* indirect blocks */ bloop(ip, bn, 0); break; case NADDR-2: /* double indirect blocks */ bloop(ip, bn, 1); break; case NADDR-1: /* triple indirect blocks */ bloop(ip, bn, 2); break; } } ip->i_size = 0; ip->i_flag |= ICHG|IUPD; ip->i_blocks = -BLOCK; /* don't match on the cache */ } static bloop(ip, bn, lev) register struct inode *ip; daddr_t bn; { register struct fsys *fp = ip->i_fsys; register int i; /* loop over an indirect block and free the tree of blocks */ if(lev) { daddr_t nb; for(i = 0 ; i < NINDIR ; i++) { /* read in a single block number to avoid needing a buffer */ t_READ(fp->fs_dev, (long)((bn<i_fsys->fs_dev, (long)((bn<i_block, sizeof(ip->i_block)) != sizeof(ip->i_block)){ printf("can't read into icache (%d) in bloop\n", i); continue; } for(j = 0 ; j < BLOCK ; j++) { if(ip->i_block[j] != (daddr_t)0) bfree(fp, ip->i_block[j]); } } } /* free the indirect block */ bfree(fp, bn); } /* * update the disk copy of the inode with the data from the incore copy * check accessed and update flags on an inode structure * if any are on, update the inode with the current time */ iupdat(ip) register struct inode *ip; { time_t time; struct dinode dino; register struct dinode *dp = &dino; if((ip->i_flag&(IUPD|IACC|ICHG)) != 0) { /* its been changed */ if(ip->i_fsys->fs_superb.s_ronly) /* the filesystem is not mounted for writing */ return; /* read the disk copy in to get unchanged information */ if(t_READ(ip->i_fsys->fs_dev, (off_t)itoo(ip->i_number), (addr_t)dp, (off_t)sizeof(dino)) != sizeof(dino)) panic("write error in iupdat"); /* convert incore format to disk format */ 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; { extern hndl_t clkhndl; t_READ(clkhndl, 0, &time, sizeof(time)); if(ip->i_flag&IACC) ip->i_atime = time; if(ip->i_flag&ICHG) ip->i_ctime = time; if(ip->i_flag&IUPD) ip->i_mtime = time; } dp->di_atime = ip->i_atime; dp->di_ctime = ip->i_ctime; dp->di_mtime = ip->i_mtime; if(ltol3((char *)dp->di_addr, (char *)ip->i_addr, NADDR) != NADDR) DEBUGOUT(("iaddress > 2^24 (ino=%d)\n", ip->i_number)); ip->i_flag &= ~(IUPD|IACC|ICHG); if(t_WRITE(ip->i_fsys->fs_dev, (off_t)itoo(ip->i_number), (addr_t)dp, (off_t)sizeof(dino)) != sizeof(dino)) panic("write error in iupdat"); } } /* * returns a port to a handler for the given inode * the inode is unmodified */ hndl_t iopen(ip) register struct inode *ip; { extern void stat_handler(); extern void drct_handler(); extern void file_handler(); switch(itype(ip)) { case IFREG: return(t_MAKEPORT(file_handler, ip, 0)); case IFDIR: return(t_MAKEPORT(drct_handler, ip, 0)); default: printf("Bad inode (%d) type (%o)", ip->i_number, itype(ip)); return(t_MAKEPORT(stat_handler, ip, 0)); } } /* * read count bytes of data from the file designated by inode * into data (which can either be local or the passed data window) * it uses t_READA so it can designate a read ahead block */ off_t iread(ip, offset, count, data) struct inode *ip; off_t offset; off_t count; addr_t data; { off_t rcnt, cnt; daddr_t bn, rabn; off_t on, n; DEBUGOUT(("iread(%d,%d,%d,%x)\n", ip->i_number, offset, count, data)); if(offset < 0) { /* ERROR(EOFFSET);*/ return(0); } rcnt = 0; while(count > 0) { /* don't read past end of file */ if((n = ip->i_size - offset) <= 0) break; /* find block on device to read */ on = offset & BMASK; n = min(n, min((unsigned)(BSIZE-on), count)); /* compute actual block to read */ bn = imap(ip, offset>>BSHIFT, READ); if(bn == 0 || bn == -1) { printf("filling read hole on block %d inode %d\n", offset>>BSHIFT, ip->i_number); bn = imap(ip, offset>>BSHIFT, WRITE); } if(bn == 0 || bn == -1) { printf("m=%o s=%d a=%d,%d,%d\nb=%d,%d,%d\n", ip->i_mode, ip->i_size, ip->i_addr[0], ip->i_addr[1], ip->i_addr[2], ip->i_block[0], ip->i_block[1], ip->i_block[2]); panic("imap(%d,%d) -> %d\n", ip->i_number, offset>>BSHIFT, bn); } /* compute read ahead block number */ rabn = imap(ip, (offset+BSIZE)>>BSHIFT, READ); if(rabn != 0 && rabn != -1) { /* valid read ahead - reada the data in */ cnt = t_READA(ip->i_fsys->fs_dev, (off_t)((bn<i_fsys->fs_dev, (off_t)((bn<i_flag |= IACC; } #ifdef SOFTENTERS if(count > 0 && (ip->i_mode&IFMT) == IFDIR) { struct mount *mp; /* special handling of directories to catch soft enters */ offset -= ip->i_size; for(mp = ip->i_fsys->fs_mntd ; mp != NULL ; mp = mp->mn_nxt) { struct direct d; if(mp->mn_ino != ip->i_number) continue; if(offset >= DSIZE) { offset -= DSIZE; continue; } d.d_ino = SOFTINO; strncpy(d.d_name, mp->mn_name, NAMESIZE); on = offset & DMASK; n = min(DSIZE-on, count); cnt = Store((char *)(data.pntr+rcnt), &d, n); if(cnt <= 0) break; rcnt += cnt; count -= cnt; offset = (offset+cnt) & DMASK; ip->i_flag |= IACC; } } #endif return(rcnt); } /* * write count bytes of data to the file designated by inode * into data (which can either be local or the passed data window) */ off_t iwrite(ip, offset, count, data) struct inode *ip; off_t offset; off_t count; addr_t data; { off_t rcnt, cnt; daddr_t bn; off_t on, n; DEBUGOUT(("iwrite(%d,%d,%d,%x)\n", ip->i_number, offset, count, data)); if(offset == APPEND_OFF) offset = ip->i_size; if(offset < 0) { /* ERROR(EOFFSET);*/ return(0); } rcnt = 0; while(count > 0) { /* find block on device to read */ on = offset & BMASK; n = min((unsigned)(BSIZE-on), count); bn = imap(ip, offset>>BSHIFT, WRITE); /* write the data out */ cnt = t_WRITE(ip->i_fsys->fs_dev, (off_t)((bn< ip->i_size) { DEBUGOUT(("extending file\n")); ip->i_size = offset; } ip->i_flag |= IUPD|ICHG; } return(rcnt); } /* GETSTAT on inodes */ long igstat(ip, type, dflt) register struct inode *ip; int type; long dflt; { switch(type) { case STATUS_ATIME: iupdat(ip); return(ip->i_atime); case STATUS_CTIME: iupdat(ip); return(ip->i_ctime); case STATUS_MTIME: iupdat(ip); return(ip->i_mtime); case STATUS_IDENT: return(ip->i_number | ((int)ip->i_fsys << 16)); case STATUS_PROT: switch(itype(ip)) { case IFREG: return(PROT_FILE); case IFDIR: return(PROT_DIRECT); } return(PROT_UNKNOWN); case STATUS_ACCESS: return(ip->i_mode & 0777); case STATUS_OWNER: return((ip->i_uid) | (ip->i_gid << 16)); case STATUS_SUSES: return(ip->i_nlink); case STATUS_ISIZE: /* should include size of softenters */ return(ip->i_size); } return(dflt); } /* PUTSTAT on inode */ long ipstat(ip, type, val) register struct inode *ip; int type; long val; { switch(type) { case STATUS_ACCESS: val &= 07777; ip->i_mode &= ~07777; ip->i_mode |= val; break; case STATUS_OWNER: ip->i_uid = val & 0xFFFF; ip->i_gid = (val >> 16) & 0xFFFF; break; case STATUS_ATIME: ip->i_atime = val; break; case STATUS_MTIME: ip->i_mtime = val; default: return(-1); } ip->i_flag |= ICHG; return(val); } /* return the type of the inode */ itype(ip) struct inode *ip; { return(ip->i_mode & IFMT); } /* wait for an inode to be unlocked and lock it */ ilock(ip) struct inode *ip; { /* you must have a valid (counted) pointer to the inode */ while(ip->i_flag & ILOCK) { DEBUGOUT(("sleeping in ilock on %d\n", ip->i_number)); /* mark the inode as wanted so unlock will wake us up */ ip->i_flag |= IWANT; t_SLEEP(fsys_sync, ip); } ip->i_flag |= ILOCK; } /* increment the inodes use count (the inode is assued to be locked by the caller) */ iuse(ip) register struct inode *ip; { ASSERT(ip->i_flag & ILOCK); ip->i_count++; } /* unlock an inode and wakeup those waiting for it */ iunlock(ip) register struct inode *ip; { ip->i_flag &= ~ILOCK; if(ip->i_flag & IWANT) { /* only do wakeup if someone is waiting */ ip->i_flag &= ~IWANT; t_WAKEUP(fsys_sync, ip); } } /* map virtual blocks in a file to physical disk blocks */ daddr_t imap(ip, bn, rwflg) register struct inode *ip; register daddr_t bn; { register i; int j, sh; daddr_t nb, nnb; daddr_t rbn; if(bn < 0) { /* ERROR(EFSIZE);*/ return((daddr_t)0); } /* * blocks 0..NADDR-4 are direct blocks */ if(bn < NADDR-3) { i = bn; nb = ip->i_addr[i]; if(nb == 0) { if(rwflg==READ || (nb = balloc(ip->i_fsys)) == 0) return((daddr_t)-1); ip->i_addr[i] = nb; ip->i_flag |= IUPD|ICHG; } return(nb); } /* * check to see if the block is in the blocks cache * if it is we dont have to go to the disk * this should make a BIG difference in large files */ if(bn >= ip->i_blocks && bn < ip->i_blocks+BLOCK && ip->i_block[bn - ip->i_blocks] != 0) return(ip->i_block[bn - ip->i_blocks]); /* save the real block number */ rbn = bn; /* * addresses NADDR-3, NADDR-2, and NADDR-1 * have single, double, triple indirect blocks. * the first step is to determine * how many levels of indirection. */ sh = 0; nb = 1; bn -= NADDR-3; for(j=3; j>0; j--) { sh += NSHIFT; nb <<= NSHIFT; if(bn < nb) break; bn -= nb; } if(j == 0) { ERROR(E_MAXSIZE); return((daddr_t)0); } /* * fetch the address from the ip */ nb = ip->i_addr[NADDR-j]; if(nb == 0) { if(rwflg==READ || (nb = balloc(ip->i_fsys)) == 0) return((daddr_t)-1); ip->i_addr[NADDR-j] = nb; ip->i_flag |= IUPD|ICHG; } /* * fetch through the indirect blocks */ for(; j<=3; j++) { sh -= NSHIFT; i = (bn>>sh) & NMASK; if(j == 3) { /* * last level of indirection * read info into the blocks cache */ ip->i_blocks = rbn - (i & (BLOCK-1)); if(t_READ(ip->i_fsys->fs_dev, (long)((nb<i_block, sizeof(ip->i_block))!=sizeof(ip->i_block)){ /* invalidate the cache */ ip->i_blocks = -BLOCK; return((daddr_t)0); } nnb = ip->i_block[rbn - ip->i_blocks]; } else { /* get the single indirect block entry */ if(t_READ(ip->i_fsys->fs_dev, (long)((nb<i_fsys)) == 0) return((daddr_t)-1); t_WRITE(ip->i_fsys->fs_dev, (long)((nb<i_blocks = -BLOCK; } nb = nnb; } return(nb); }