263 lines
5.8 KiB
C
263 lines
5.8 KiB
C
#ident "$Revision: 1.7 $"
|
|
|
|
/*
|
|
* Extend the given file by allocating data blocks.
|
|
*/
|
|
#include "efs.h"
|
|
|
|
static void blockclr(efs_daddr_t);
|
|
static void copy_block(efs_daddr_t, efs_daddr_t);
|
|
static efs_daddr_t findspace(efs_daddr_t, int);
|
|
static efs_daddr_t nextbn(efs_daddr_t);
|
|
|
|
/*
|
|
* Return next usable disk block. If block is outside range of cylinder
|
|
* group, move it to next cylinder group.
|
|
*/
|
|
static efs_daddr_t
|
|
nextbn(efs_daddr_t bn)
|
|
{
|
|
int cg;
|
|
int maxdblock;
|
|
|
|
maxdblock = fs->fs_firstcg + (fs->fs_ncg * fs->fs_cgfsize);
|
|
for (;;) {
|
|
cg = EFS_BBTOCG(fs, bn);
|
|
if (bn >= maxdblock) {
|
|
fprintf(stderr,
|
|
"%s: filesystem is out of space\n", progname);
|
|
exit(1);
|
|
} else
|
|
if (bn < fs->fs_firstcg)
|
|
bn = fs->fs_firstcg + fs->fs_cgisize;
|
|
else
|
|
if (bn >= fs->fs_firstcg + fs->fs_cgfsize * (cg + 1))
|
|
bn = fs->fs_firstcg + fs->fs_cgfsize * (cg + 1) +
|
|
fs->fs_cgisize;
|
|
else
|
|
return (bn);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clear out the newly allocated disk block
|
|
*/
|
|
static void
|
|
blockclr(efs_daddr_t bn)
|
|
{
|
|
char buf[BBSIZE];
|
|
long *lp;
|
|
|
|
/* zero out buffer */
|
|
for (lp = (long *) buf; lp < (long *) &buf[BBSIZE]; )
|
|
*lp++ = 0;
|
|
lseek(fs_fd, BBTOB(bn), SEEK_SET);
|
|
if (write(fs_fd, buf, BBSIZE) != BBSIZE)
|
|
error();
|
|
}
|
|
|
|
void
|
|
efs_extend(struct efs_dinode *di, int incr)
|
|
{
|
|
efs_daddr_t bn, newbn;
|
|
long newbbs, newextentlen;
|
|
extent *ex;
|
|
static efs_daddr_t nextfree;
|
|
|
|
/*
|
|
* See if incr is already in current allocated size.
|
|
*/
|
|
newbbs = (long)(BTOBB(di->di_size + incr) - BTOBB(di->di_size));
|
|
if (newbbs == 0)
|
|
goto out;
|
|
|
|
/* read in bitmap, if isn't already here */
|
|
if (bitmap == (char *)0)
|
|
efs_bget();
|
|
|
|
/*
|
|
* Extend last extent if possible.
|
|
*/
|
|
top:
|
|
if (di->di_numextents) {
|
|
ex = &di->di_u.di_extents[di->di_numextents-1];
|
|
bn = ex->ex_bn + ex->ex_length;
|
|
while ((ex->ex_length < EFS_MAXEXTENTLEN) && newbbs) {
|
|
newbn = nextbn(bn);
|
|
if (newbn != bn)
|
|
break;
|
|
if (btst(bitmap, newbn) == 0)
|
|
break;
|
|
bclr(bitmap, newbn);
|
|
fs->fs_tfree--;
|
|
blockclr(newbn);
|
|
nextfree = newbn + 1;
|
|
ex->ex_length++;
|
|
newbbs--;
|
|
bn++;
|
|
}
|
|
if (newbbs == 0)
|
|
goto out;
|
|
}
|
|
|
|
/* Didn't satisfy the request. If the last extent exists and is
|
|
* smaller than EFS_MAXEXTENTLEN, as will happen with growing a
|
|
* directory after inserting files in it, we are going to chuck that
|
|
* extent and replace it with a completely fresh one, copying data as
|
|
* required. This avoids the problem of directories ending up
|
|
* with one-block extents and running out of space. We will set the
|
|
* "next block" rotor to the recyled blocks to avoid ending up with
|
|
* a lot of space in 1, 2, 3, 4, 5 etc block chunks as a directory
|
|
* grows. Note that regular files (working from a mkfs proto)
|
|
* do NOT now go through this route, they are created by a separate
|
|
* function in mkfs with its own block rotor, so the freed blocks
|
|
* here do not result in regular file fragmentation.
|
|
*/
|
|
|
|
if (di->di_numextents && (ex->ex_length < EFS_MAXEXTENTLEN))
|
|
{
|
|
newextentlen = (long)(ex->ex_length + newbbs);
|
|
if (newextentlen > EFS_MAXEXTENTLEN)
|
|
newextentlen = EFS_MAXEXTENTLEN;
|
|
newbbs -= (newextentlen - ex->ex_length);
|
|
|
|
if ((newbn = findspace(bn, newextentlen)) == 0)
|
|
goto nospace;
|
|
|
|
bn = newbn;
|
|
if (ex->ex_bn < nextfree)
|
|
nextfree = ex->ex_bn;
|
|
while (ex->ex_length--)
|
|
{
|
|
copy_block(ex->ex_bn, newbn++);
|
|
fs->fs_tfree++;
|
|
bset(bitmap, ex->ex_bn);
|
|
ex->ex_bn++; /* can't do in bset: macro side effects*/
|
|
}
|
|
ex->ex_length = newextentlen;
|
|
ex->ex_bn = bn;
|
|
if (newbbs == 0)
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Allocate a new extent if (a) there are no extents yet,
|
|
* or (b) growing the last extent failed to satisfy the request.
|
|
* We don't support indirect extents here.
|
|
* If we're starting a new inode, reset the block rotor to zero to
|
|
* make sure any holes caused by recyled grown-by-moving extents get
|
|
* used up.
|
|
*/
|
|
if (di->di_numextents == EFS_DIRECTEXTENTS) {
|
|
fprintf(stderr, "%s: can't extend file, too many extents\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
ex = &di->di_u.di_extents[di->di_numextents];
|
|
if (di->di_numextents > 0) {
|
|
extent *prev;
|
|
|
|
prev = &di->di_u.di_extents[di->di_numextents-1];
|
|
ex->ex_offset = prev->ex_offset + prev->ex_length;
|
|
}
|
|
else
|
|
nextfree = 0;
|
|
|
|
di->di_numextents++;
|
|
|
|
/*
|
|
* Now find some free disk space to allocate out of.
|
|
*/
|
|
bn = nextfree;
|
|
while (bn < fs->fs_size) {
|
|
newbn = nextbn(bn);
|
|
if (btst(bitmap, bn)) {
|
|
/*
|
|
* Found a free block. Start over at the top,
|
|
* trying to extend the file.
|
|
*/
|
|
bclr(bitmap, bn);
|
|
fs->fs_tfree--;
|
|
blockclr(newbn);
|
|
ex->ex_bn = bn;
|
|
ex->ex_length = 1;
|
|
nextfree = bn + 1;
|
|
if (--newbbs == 0)
|
|
goto out;
|
|
goto top;
|
|
}
|
|
bn++;
|
|
}
|
|
|
|
nospace:
|
|
fprintf(stderr, "%s: filesystem is full - can't extend file\n",
|
|
progname);
|
|
exit(1);
|
|
|
|
out:
|
|
di->di_size += incr;
|
|
}
|
|
|
|
/* Findspace(): looks for 'size' contiguous blocks, starting the search at
|
|
* bn. If found, allocates & clears the found blocks, and returns the block
|
|
* number where they start. If none available, returns 0.
|
|
*/
|
|
|
|
static efs_daddr_t
|
|
findspace(efs_daddr_t bn, int size)
|
|
{
|
|
efs_daddr_t testbn, runstart;
|
|
int runlength, i;
|
|
|
|
runlength = 0;
|
|
runstart = 0;
|
|
for (;;)
|
|
{
|
|
testbn = nextbn(bn);
|
|
if (testbn != bn) /* jumped to next cg */
|
|
{
|
|
runlength = 0;
|
|
runstart = 0;
|
|
}
|
|
if (testbn >= fs->fs_size)
|
|
return 0;
|
|
if (btst(bitmap, testbn))
|
|
{
|
|
runlength++;
|
|
if (!runstart)
|
|
runstart = testbn;
|
|
}
|
|
else
|
|
{
|
|
runlength = 0;
|
|
runstart = 0;
|
|
}
|
|
if (runlength == size)
|
|
goto found;
|
|
bn = testbn + 1;
|
|
}
|
|
|
|
found:
|
|
|
|
for (i = 0, testbn = runstart; i < size; i++, testbn++)
|
|
{
|
|
bclr(bitmap, testbn);
|
|
fs->fs_tfree--;
|
|
blockclr(testbn);
|
|
}
|
|
return runstart;
|
|
}
|
|
|
|
static void
|
|
copy_block(efs_daddr_t from, efs_daddr_t to)
|
|
{
|
|
char buf[BBSIZE];
|
|
|
|
lseek(fs_fd, BBTOB(from), SEEK_SET);
|
|
if (read(fs_fd, buf, BBSIZE) != BBSIZE)
|
|
error();
|
|
lseek(fs_fd, BBTOB(to), SEEK_SET);
|
|
if (write(fs_fd, buf, BBSIZE) != BBSIZE)
|
|
error();
|
|
}
|