/* * Copyright (c) 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)ufs_alloc.c 1.3 (2.11BSD GTE) 1996/9/19 */ #include "param.h" #include "../machine/seg.h" #include "fs.h" #include "dir.h" #include "inode.h" #include "buf.h" #include "user.h" #include "kernel.h" #include "mount.h" #ifdef QUOTA #include "quota.h" #endif typedef struct fblk *FBLKP; /* * Allocate a block in the file system. * * 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... */ struct buf * balloc(ip, flags) struct inode *ip; int flags; { register struct fs *fs; register struct buf *bp; int async; daddr_t bno; fs = ip->i_fs; async = fs->fs_flags & MNT_ASYNC; while (fs->fs_flock) sleep((caddr_t)&fs->fs_flock, PINOD); do { if (fs->fs_nfree <= 0) goto nospace; if (fs->fs_nfree > NICFREE) { fserr(fs, "bad free count"); goto nospace; } bno = fs->fs_free[--fs->fs_nfree]; if (bno == 0) goto nospace; } while (badblock(fs, bno)); if (fs->fs_nfree <= 0) { fs->fs_flock++; bp = bread(ip->i_dev, bno); if (((bp->b_flags&B_ERROR) == 0) && (bp->b_resid==0)) { register struct fblk *fbp; fbp = (FBLKP) mapin(bp); *((FBLKP)&fs->fs_nfree) = *fbp; mapout(bp); } brelse(bp); /* * Write the superblock back, synchronously if requested, * so that the free list pointer won't point at garbage. * We can still end up with dups in free if we then * use some of the blocks in this freeblock, then crash * without a sync. */ bp = getblk(ip->i_dev, SUPERB); fs->fs_fmod = 0; fs->fs_time = time.tv_sec; { register struct fs *fps; fps = (struct fs *)mapin(bp); *fps = *fs; } mapout(bp); if (!async) bwrite(bp); else bdwrite(bp); fs->fs_flock = 0; wakeup((caddr_t)&fs->fs_flock); if (fs->fs_nfree <=0) goto nospace; } bp = getblk(ip->i_dev, bno); bp->b_resid = 0; if (flags & B_CLRBUF) clrbuf(bp); fs->fs_fmod = 1; fs->fs_tfree--; return(bp); nospace: fs->fs_nfree = 0; fs->fs_tfree = 0; fserr(fs, "file system full"); /* * THIS IS A KLUDGE... * SHOULD RATHER SEND A SIGNAL AND SUSPEND THE PROCESS IN A * STATE FROM WHICH THE SYSTEM CALL WILL RESTART */ uprintf("\n%s: write failed, file system full\n", fs->fs_fsmnt); { register int i; for (i = 0; i < 5; i++) sleep((caddr_t)&lbolt, PRIBIO); } u.u_error = ENOSPC; return(NULL); } /* * Allocate an inode in the file system. * * 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(pip) struct inode *pip; { register struct fs *fs; register struct buf *bp; register struct inode *ip; int i; struct dinode *dp; ino_t ino; daddr_t adr; ino_t inobas; int first; struct inode *ifind(); char *emsg = "no inodes free"; fs = pip->i_fs; while (fs->fs_ilock) sleep((caddr_t)&fs->fs_ilock, PINOD); #ifdef QUOTA QUOTAMAP(); u.u_error = chkiq(pip->i_dev, NULL, u.u_uid, 0); QUOTAUNMAP(); if (u.u_error) return(NULL); #endif loop: if (fs->fs_ninode > 0) { ino = fs->fs_inode[--fs->fs_ninode]; if (ino <= ROOTINO) goto loop; ip = iget(pip->i_dev, fs, ino); if (ip == NULL) return(NULL); if (ip->i_mode == 0) { bzero((caddr_t)ip->i_addr,sizeof(ip->i_addr)); ip->i_flags = 0; fs->fs_fmod = 1; fs->fs_tinode--; return(ip); } /* * Inode was allocated after all. * Look some more. */ iput(ip); goto loop; } fs->fs_ilock++; if (fs->fs_nbehind < 4 * NICINOD) { first = 1; ino = fs->fs_lasti; #ifdef DIAGNOSTIC if (itoo(ino)) panic("ialloc"); #endif DIAGNOSTIC adr = itod(ino); } else { fromtop: first = 0; ino = 1; adr = SUPERB+1; fs->fs_nbehind = 0; } for (;adr < fs->fs_isize;adr++) { inobas = ino; bp = bread(pip->i_dev, adr); if ((bp->b_flags & B_ERROR) || bp->b_resid) { brelse(bp); ino += INOPB; continue; } dp = (struct dinode *)mapin(bp); for (i = 0;i < INOPB;i++) { if (dp->di_mode != 0) goto cont; if (ifind(pip->i_dev, ino)) goto cont; fs->fs_inode[fs->fs_ninode++] = ino; if (fs->fs_ninode >= NICINOD) break; cont: ino++; dp++; } mapout(bp); brelse(bp); if (fs->fs_ninode >= NICINOD) break; } if (fs->fs_ninode < NICINOD && first) goto fromtop; fs->fs_lasti = inobas; fs->fs_ilock = 0; wakeup((caddr_t)&fs->fs_ilock); if (fs->fs_ninode > 0) goto loop; fserr(fs, emsg); uprintf("\n%s: %s\n", fs->fs_fsmnt, emsg); u.u_error = ENOSPC; return(NULL); } /* * Free a block or fragment. * * Place the specified disk block back on the free list of the * specified device. */ free(ip, bno) struct inode *ip; daddr_t bno; { register struct fs *fs; register struct buf *bp; struct fblk *fbp; fs = ip->i_fs; if (badblock(fs, bno)) { printf("bad block %D, ino %d\n", bno, ip->i_number); return; } while (fs->fs_flock) sleep((caddr_t)&fs->fs_flock, PINOD); if (fs->fs_nfree <= 0) { fs->fs_nfree = 1; fs->fs_free[0] = 0; } if (fs->fs_nfree >= NICFREE) { fs->fs_flock++; bp = getblk(ip->i_dev, bno); fbp = (FBLKP)mapin(bp); *fbp = *((FBLKP)&fs->fs_nfree); mapout(bp); fs->fs_nfree = 0; if (fs->fs_flags & MNT_ASYNC) bdwrite(bp); else bwrite(bp); fs->fs_flock = 0; wakeup((caddr_t)&fs->fs_flock); } fs->fs_free[fs->fs_nfree++] = bno; fs->fs_tfree++; fs->fs_fmod = 1; } /* * Free an inode. * * 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(ip, ino) struct inode *ip; ino_t ino; { register struct fs *fs; fs = ip->i_fs; fs->fs_tinode++; if (fs->fs_ilock) return; if (fs->fs_ninode >= NICINOD) { if (fs->fs_lasti > ino) fs->fs_nbehind++; return; } fs->fs_inode[fs->fs_ninode++] = ino; fs->fs_fmod = 1; } /* * Fserr prints the name of a file system with an error diagnostic. * * The form of the error message is: * fs: error message */ fserr(fp, cp) struct fs *fp; char *cp; { printf("%s: %s\n", fp->fs_fsmnt, cp); }