#include "../h/local.h" #ifdef SCCS_ID static char SCCS_ID [] = "@(#)alloc.c 3.5 15:55:38 - 82/02/03 "; #endif SCCS_ID #include "../h/param.h" #include "../h/systm.h" #include "../h/mount.h" #include "../h/filsys.h" #include "../h/fblk.h" #include "../h/conf.h" #include "../h/buf.h" #include "../h/inode.h" #include "../h/ino.h" #include "../h/dir.h" #include "../h/user.h" #ifdef UCB_QUOTAS #include "../h/quota.h" #endif UCB_QUOTAS typedef struct fblk *FBLKP; #ifdef UCB_QUOTAS /* * Book keeping for quota system * interfaces to alloc() and free() * * count one block against the quota of the given inode. * if the quota has been exceeded, retrun NULL without allocating block. */ qalloc(ip, d) register struct inode *ip; dev_t d; { register struct inode *qp, *rp; register struct buf *ret; if (ip->i_quot) qprint(01)("qalloc: ip->i_number = %d\n", ip->i_number); else qprint(04)("QALLOC: ip->i_quot == NULL\n"); for (qp = ip->i_quot; qp != NULL; qp = qp->i_quot) { if (qp->i_un.i_qused >= qp->i_un.i_qmax) { /* * A quota was exceeded. Undo the counts. */ for (rp = ip->i_quot; rp != qp; rp = rp->i_quot) rp->i_un.i_qused -= QCOUNT; u.u_error = EQUOT; return(NULL); } qp->i_un.i_qused += QCOUNT; qp->i_flag |= IUPD; } if((ret = alloc(d)) != NULL) return(ret); /* * The block could not be allocated. * Undo the quota count. */ for (qp = ip->i_quot; qp != NULL; qp = qp->i_quot) if(qp->i_un.i_qused != 0) qp->i_un.i_qused -= QCOUNT; return(NULL); } #endif UCB_QUOTAS /* * alloc will obtain the next available * free disk block from the free list of * the specified device. * The super block has up to NICFREE remembered * free blocks; the last of these is read to * obtain NICFREE more . . . * * no space on dev x/y -- when * the free list is exhausted. */ struct buf * alloc(dev) dev_t dev; { daddr_t bno; register struct filsys *fp; register struct buf *bp; fp = getfs(dev); while(fp->s_flock) sleep((caddr_t)&fp->s_flock, PINOD); do { if(fp->s_nfree <= 0) goto nospace; if (fp->s_nfree > NICFREE) { prdev("Bad free count", dev); goto nospace; } bno = fp->s_free[--fp->s_nfree]; if(bno == 0) goto nospace; } while (badblock(fp, bno, dev)); if(fp->s_nfree <= 0) { fp->s_flock++; bp = bread(dev, bno); if ((bp->b_flags&B_ERROR) == 0) { fp->s_nfree = ((FBLKP)(bp->b_un.b_addr))->df_nfree; bcopy((caddr_t)((FBLKP)(bp->b_un.b_addr))->df_free, (caddr_t)fp->s_free, sizeof(fp->s_free)); } brelse(bp); #ifdef UCB_NODUPS /* prevent "dups in free" */ /* * Prevent ``dups in free'' */ bp = getblk(dev, SUPERB); fp->s_fmod = 0; fp->s_time = time; #ifdef UCB_MOUNT bcopy((caddr_t)fp, bp->b_un.b_addr, SBSIZE); #else UCB_MOUNT bcopy((caddr_t)fp, bp->b_un.b_addr, BSIZE); #endif UCB_MOUNT bwrite(bp); #endif UCB_NODUPS fp->s_flock = 0; wakeup((caddr_t)&fp->s_flock); if (fp->s_nfree <=0) goto nospace; } bp = getblk(dev, bno); clrbuf(bp); fp->s_fmod = 1; #ifdef CGL_NFREE fp->s_tfree--; #endif CGL_NFREE return(bp); nospace: fp->s_nfree = 0; #ifdef CGL_NFREE fp->s_tfree = 0; #endif CGL_NFREE #ifdef UCB_MISC /* SHOULD PROBABLY HINDER THE ABILITY OF */ /* ONE PROCESS TO CAUSE THIS... */ #endif UCB_MISC prdev("no space", dev); u.u_error = ENOSPC; return(NULL); } #ifdef UCB_QUOTAS /* * decrement the count for quotas */ qfree(ip, b) register struct inode *ip; daddr_t b; { register struct inode *qp; if (ip->i_quot) qprint(01)("qfree: ip->i_quot = d\n", ip->i_quot->i_number); for (qp = ip->i_quot; qp != NULL; qp = qp->i_quot) if (qp->i_un.i_qused) { qp->i_un.i_qused -= QCOUNT; qp->i_flag |= IUPD; } free(ip->i_dev, b); } #endif UCB_QUOTAS /* * place the specified disk block * back on the free list of the * specified device. */ free(dev, bno) dev_t dev; daddr_t bno; { register struct filsys *fp; register struct buf *bp; fp = getfs(dev); fp->s_fmod = 1; while(fp->s_flock) sleep((caddr_t)&fp->s_flock, PINOD); if (badblock(fp, bno, dev)) return; if(fp->s_nfree <= 0) { fp->s_nfree = 1; fp->s_free[0] = 0; } if(fp->s_nfree >= NICFREE) { fp->s_flock++; bp = getblk(dev, bno); ((FBLKP)(bp->b_un.b_addr))->df_nfree = fp->s_nfree; bcopy((caddr_t)fp->s_free, (caddr_t)((FBLKP)(bp->b_un.b_addr))->df_free, sizeof(fp->s_free)); fp->s_nfree = 0; bwrite(bp); fp->s_flock = 0; wakeup((caddr_t)&fp->s_flock); } fp->s_free[fp->s_nfree++] = bno; #ifdef CGL_NFREE fp->s_tfree++; #endif CGL_NFREE fp->s_fmod = 1; } /* * Check that a block number is in the * range between the I list and the size * of the device. * This is used mainly to check that a * garbage file system has not been mounted. * * bad block on dev x/y -- not in range */ badblock(fp, bn, dev) register struct filsys *fp; daddr_t bn; dev_t dev; { if (bn < fp->s_isize || bn >= fp->s_fsize) { prdev("bad block", dev); return(1); } return(0); } /* * Allocate an unused I node * on the specified device. * Used with file creation. * 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. */ struct inode * ialloc(dev) dev_t dev; { register struct filsys *fp; register struct buf *bp; register struct inode *ip; int i; struct dinode *dp; ino_t ino; daddr_t adr; #ifdef CGL_ISRCH ino_t inobas; int first; #endif CGL_ISRCH fp = getfs(dev); while(fp->s_ilock) sleep((caddr_t)&fp->s_ilock, PINOD); loop: if(fp->s_ninode > 0) { ino = fp->s_inode[--fp->s_ninode]; if (ino < ROOTINO) goto loop; ip = iget(dev, ino); if(ip == NULL) return(NULL); if(ip->i_mode == 0) { for (i=0; ii_un.i_addr[i] = 0; fp->s_fmod = 1; #ifdef CGL_NFREE fp->s_tinode--; #endif CGL_NFREE return(ip); } /* * Inode was allocated after all. * Look some more. */ iput(ip); goto loop; } fp->s_ilock++; #ifdef CGL_ISRCH #ifdef TRACEIA printf("s_lasti %d, s_nbehind %d\n", fp->s_lasti, fp->s_nbehind); #endif TRACEIA if (fp->s_nbehind < 4 * NICINOD) { first = 1; ino = fp->s_lasti; if (itoo(ino)) panic("ialloc"); adr = itod(ino); } else { fromtop: first = 0; ino = 1; adr = SUPERB+1; fp->s_nbehind = 0; } #ifdef TRACEIA printf("searching %x/%x from %d\n", major(dev), minor(dev), ino); #endif TRACEIA for(; adr < fp->s_isize; adr++) { inobas = ino; #else CGL_ISRCH ino = 1; for(adr = SUPERB+1; adr < fp->s_isize; adr++) { #endif CGL_ISRCH bp = bread(dev, adr); #ifdef INBLK if ((bp->b_flags&B_CACHE) == 0) u.u_vm.vm_inblk--; /* no charge! */ #endif INBLK if (bp->b_flags & B_ERROR) { brelse(bp); ino += INOPB; continue; } dp = bp->b_un.b_dino; for(i=0; idi_mode != 0) goto cont; #ifdef UCB_IHASH if(ifind(dev, ino)) #else UCB_IHASH for(ip = &inode[0]; ip < inodeNINODE; ip++) if(dev==ip->i_dev && ino==ip->i_number) #endif UCB_IHASH goto cont; fp->s_inode[fp->s_ninode++] = ino; if(fp->s_ninode >= NICINOD) break; cont: ino++; dp++; } brelse(bp); if(fp->s_ninode >= NICINOD) break; } #ifdef CGL_ISRCH if (fp->s_ninode < NICINOD && first) { #ifdef TRACEIA printf("found only %d inodes on dev %x/%x, looking from top\n", fp->s_ninode, major(dev), minor(dev)); #endif TRACEIA goto fromtop; } #ifdef TRACEIA printf("search of %x/%x ended at %d\n", major(dev), minor(dev), ino); #endif TRACEIA fp->s_lasti = inobas; #endif CGL_ISRCH fp->s_ilock = 0; wakeup((caddr_t)&fp->s_ilock); if(fp->s_ninode > 0) goto loop; prdev("Out of inodes", dev); u.u_error = ENOSPC; return(NULL); } /* * Free the specified I node * on the specified device. * The algorithm stores up * to NICINOD I nodes in the super * block and throws away any more. */ ifree(dev, ino) dev_t dev; ino_t ino; { register struct filsys *fp; fp = getfs(dev); #ifdef CGL_NFREE fp->s_tinode++; #endif CGL_NFREE if(fp->s_ilock) return; #ifdef CGL_ISRCH if(fp->s_ninode >= NICINOD) { #ifdef USTEST if (USTOI(fp->s_lasti) > USTOI(ino)) #else USTEST if (fp->s_lasti > ino) #endif USTEST fp->s_nbehind++; return; } #else CGL_ISRCH if(fp->s_ninode >= NICINOD) return; #endif CGL_ISRCH fp->s_inode[fp->s_ninode++] = ino; fp->s_fmod = 1; } /* * getfs maps a device number into * a pointer to the incore super * block. * The algorithm is a linear * search through the mount table. * A consistency check of the * in core free-block and i-node * counts. * * bad count on dev x/y -- the count * check failed. At this point, all * the counts are zeroed which will * almost certainly lead to "no space" * diagnostic * panic: no fs -- the device is not mounted. * this "cannot happen" */ struct filsys * getfs(dev) dev_t dev; { register struct mount *mp; register struct filsys *fp; for(mp = &mount[0]; mp < mountNMOUNT; mp++) if(mp->m_bufp != NULL && mp->m_dev == dev) { #ifdef UCB_MOUNT fp = mp->m_caddr; #else UCB_MOUNT fp = mp->m_bufp->b_un.b_filsys; #endif UCB_MOUNT if(fp->s_nfree > NICFREE || fp->s_ninode > NICINOD) { #ifdef UCB_MOUNT printf("nfree = %d, ninode = %d, ", fp->s_nfree, fp->s_ninode); #endif UCB_MOUNT prdev("bad count", dev); fp->s_nfree = 0; fp->s_ninode = 0; } return(fp); } panic("no fs"); return(NULL); } /* * update is the internal name of * 'sync'. It goes through the disk * queues to initiate sandbagged IO; * goes through the I nodes to write * modified nodes; and it goes through * the mount table to initiate modified * super blocks. */ update() { register struct inode *ip; register struct mount *mp; register struct buf *bp; struct filsys *fp; if(updlock) return; updlock++; for(mp = &mount[0]; mp < mountNMOUNT; mp++) if(mp->m_bufp != NULL) { #ifdef UCB_MOUNT fp = mp->m_caddr; #else UCB_MOUNT fp = mp->m_bufp->b_un.b_filsys; #endif UCB_MOUNT if(fp->s_fmod==0 || fp->s_ilock!=0 || fp->s_flock!=0 || fp->s_ronly!=0) continue; bp = getblk(mp->m_dev, SUPERB); if (bp->b_flags & B_ERROR) continue; fp->s_fmod = 0; fp->s_time = time; #ifdef UCB_MOUNT bcopy((caddr_t)fp, bp->b_un.b_addr, SBSIZE); #else UCB_MOUNT bcopy((caddr_t)fp, bp->b_un.b_addr, BSIZE); #endif UCB_MOUNT bwrite(bp); } for(ip = &inode[0]; ip < inodeNINODE; ip++) if((ip->i_flag&ILOCK)==0 && ip->i_count) { ip->i_flag |= ILOCK; ip->i_count++; #ifdef UCB_NODUPS iupdat(ip, &time, &time, 0); #else UCB_NODUPS iupdat(ip, &time, &time); #endif UCB_NODUPS iput(ip); } updlock = 0; bflush(NODEV); }