+/*
+ * Reallocate a fragment to a bigger size
+ *
+ * The number and size of the old block is given, and a preference
+ * and new size is also specified. The allocator attempts to extend
+ * the original block. Failing that, the regular block allocator is
+ * invoked to get an appropriate block.
+ */
+struct buf *
+realloccg(ip, bprev, bpref, osize, nsize)
+ register struct inode *ip;
+ daddr_t bprev, bpref;
+ int osize, nsize;
+{
+ register struct fs *fs;
+ register struct buf *bp, *obp;
+ int cg, request;
+ daddr_t bno, bn;
+ int i, count;
+
+ fs = ip->i_fs;
+ if ((unsigned)osize > fs->fs_bsize || fragoff(fs, osize) != 0 ||
+ (unsigned)nsize > fs->fs_bsize || fragoff(fs, nsize) != 0) {
+ printf("dev = 0x%x, bsize = %d, osize = %d, nsize = %d, fs = %s\n",
+ ip->i_dev, fs->fs_bsize, osize, nsize, fs->fs_fsmnt);
+ panic("realloccg: bad size");
+ }
+ if (u.u_uid != 0 && freespace(fs, fs->fs_minfree) <= 0)
+ goto nospace;
+ if (bprev == 0) {
+ printf("dev = 0x%x, bsize = %d, bprev = %d, fs = %s\n",
+ ip->i_dev, fs->fs_bsize, bprev, fs->fs_fsmnt);
+ panic("realloccg: bad bprev");
+ }
+#ifdef QUOTA
+ u.u_error = chkdq(ip, (long)btodb(nsize - osize), 0);
+ if (u.u_error)
+ return (NULL);
+#endif
+ cg = dtog(fs, bprev);
+ bno = fragextend(ip, cg, (long)bprev, osize, nsize);
+ if (bno != 0) {
+ do {
+#ifdef SECSIZE
+ bp = bread(ip->i_dev, fsbtodb(fs, bno), osize,
+ fs->fs_dbsize);
+#else SECSIZE
+ bp = bread(ip->i_dev, fsbtodb(fs, bno), osize);
+#endif SECSIZE
+ if (bp->b_flags & B_ERROR) {
+ brelse(bp);
+ return (NULL);
+ }
+ } while (brealloc(bp, nsize) == 0);
+ bp->b_flags |= B_DONE;
+ bzero(bp->b_un.b_addr + osize, (unsigned)nsize - osize);
+ ip->i_blocks += btodb(nsize - osize);
+ ip->i_flag |= IUPD|ICHG;
+ return (bp);
+ }
+ if (bpref >= fs->fs_size)
+ bpref = 0;
+ switch ((int)fs->fs_optim) {
+ case FS_OPTSPACE:
+ /*
+ * Allocate an exact sized fragment. Although this makes
+ * best use of space, we will waste time relocating it if
+ * the file continues to grow. If the fragmentation is
+ * less than half of the minimum free reserve, we choose
+ * to begin optimizing for time.
+ */
+ request = nsize;
+ if (fs->fs_minfree < 5 ||
+ fs->fs_cstotal.cs_nffree >
+ fs->fs_dsize * fs->fs_minfree / (2 * 100))
+ break;
+ log(LOG_NOTICE, "%s: optimization changed from SPACE to TIME\n",
+ fs->fs_fsmnt);
+ fs->fs_optim = FS_OPTTIME;
+ break;
+ case FS_OPTTIME:
+ /*
+ * At this point we have discovered a file that is trying
+ * to grow a small fragment to a larger fragment. To save
+ * time, we allocate a full sized block, then free the
+ * unused portion. If the file continues to grow, the
+ * `fragextend' call above will be able to grow it in place
+ * without further copying. If aberrant programs cause
+ * disk fragmentation to grow within 2% of the free reserve,
+ * we choose to begin optimizing for space.
+ */
+ request = fs->fs_bsize;
+ if (fs->fs_cstotal.cs_nffree <
+ fs->fs_dsize * (fs->fs_minfree - 2) / 100)
+ break;
+ log(LOG_NOTICE, "%s: optimization changed from TIME to SPACE\n",
+ fs->fs_fsmnt);
+ fs->fs_optim = FS_OPTSPACE;
+ break;
+ default:
+ printf("dev = 0x%x, optim = %d, fs = %s\n",
+ ip->i_dev, fs->fs_optim, fs->fs_fsmnt);
+ panic("realloccg: bad optim");
+ /* NOTREACHED */
+ }
+ bno = (daddr_t)hashalloc(ip, cg, (long)bpref, request,
+ (u_long (*)())alloccg);
+ if (bno > 0) {
+#ifdef SECSIZE
+ obp = bread(ip->i_dev, fsbtodb(fs, bprev), osize,
+ fs->fs_dbsize);
+#else SECSIZE
+ obp = bread(ip->i_dev, fsbtodb(fs, bprev), osize);
+#endif SECSIZE
+ if (obp->b_flags & B_ERROR) {
+ brelse(obp);
+ return (NULL);
+ }
+ bn = fsbtodb(fs, bno);
+#ifdef SECSIZE
+ bp = getblk(ip->i_dev, bn, nsize, fs->fs_dbsize);
+#else SECSIZE
+ bp = getblk(ip->i_dev, bn, nsize);
+#endif SECSIZE
+ bcopy(obp->b_un.b_addr, bp->b_un.b_addr, (u_int)osize);
+ count = howmany(osize, CLBYTES);
+ for (i = 0; i < count; i++)
+#ifdef SECSIZE
+ munhash(ip->i_dev, bn + i * CLBYTES / fs->fs_dbsize);
+#else SECSIZE
+ munhash(ip->i_dev, bn + i * CLBYTES / DEV_BSIZE);
+#endif SECSIZE
+ bzero(bp->b_un.b_addr + osize, (unsigned)nsize - osize);
+ if (obp->b_flags & B_DELWRI) {
+ obp->b_flags &= ~B_DELWRI;
+ u.u_ru.ru_oublock--; /* delete charge */
+ }
+ brelse(obp);
+ blkfree(ip, bprev, (off_t)osize);
+ if (nsize < request)
+ blkfree(ip, bno + numfrags(fs, nsize),
+ (off_t)(request - nsize));
+ ip->i_blocks += btodb(nsize - osize);
+ ip->i_flag |= IUPD|ICHG;
+ return (bp);
+ }
+nospace:
+ /*
+ * no space available
+ */
+ fserr(fs, "file system full");
+ uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);
+ u.u_error = ENOSPC;
+ return (NULL);
+}
+
+/*
+ * Allocate an inode in the file system.
+ *
+ * A preference may be optionally specified. If a preference is given
+ * the following hierarchy is used to allocate an inode:
+ * 1) allocate the requested inode.
+ * 2) allocate an inode in the same cylinder group.
+ * 3) quadradically rehash into other cylinder groups, until an
+ * available inode is located.
+ * If no inode preference is given the following heirarchy is used
+ * to allocate an inode:
+ * 1) allocate an inode in cylinder group 0.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available inode is located.
+ */