1
0
Files
irix-657m-src/eoe/cmd/efs/fsr/lib.c
2022-09-29 17:59:04 +03:00

1307 lines
28 KiB
C

#ident "$Revision: 1.23 $"
/*
* library-ish stuff
*/
#include "fsr.h"
#include <signal.h>
#include <syslog.h>
#include <diskinvent.h>
int gflag;
extern int vflag;
extern int dflag;
static int (*fsc_errpr)(const char *, ...);
static vecballoc(dev_t dev, int vecne, extent *vecex);
static void vecfree(dev_t dev, int vecne, extent *vecex);
static void offfree(struct fscarg *fap, off_t offset, int len);
static veccopy(int fd, extent *toex, extent *fromex, int nblocks);
static veccomputex(struct efs_mount *mp, struct fscarg *fap,
int vecne, extent *vecex, off_t offset, int nblocks);
static indir(struct efs_mount *mp, dev_t dev, efs_ino_t ino, int ne,
extent **ix, int *nie);
static inex(efs_daddr_t bn, int len, int ne, extent *ex);
static onex(efs_daddr_t bn, int len, int ne, extent *ex);
static void squirrelex(struct fscarg *from, struct fscarg *to);
#define DEBUG
#ifdef DEBUG /* { */
/*
* If dflag then save and verify stuff in these variables
* failing a test under dflag will cause a dbg_abort()
* This is mostly to catch bugs within fsr, but corrupt file systems are
* caught in this net as well. For this latter reason we ship the production
* fsr to be highly vigilant (with DEBUG _on_) to not make things worse
* "in the unlikely event of" a corrupt file system.
*/
static struct fscarg dbg_fa, dbg_nfa;
static struct efs dbg_fs;
static extent *dbg_bex; /* record BALLOC's */
static dbg_bne;
static dbg_flags;
#define D_GOTEX 0x0004
#define D_LOCKED 0x0001
#define D_COMMITTED 0x0002
#define TEMPLATE "/tmp/.fsrXXXXXX"
int lbsize = 64;
int max_ext_size;
void
dbg_abort(char *msg, int info)
{
char *tfile;
FILE *fp;
if ((tfile = mktemp(TEMPLATE)) == NULL) {
nofile:
fsc_errpr("internal error dev=0x%x ino=%d %s %d, exiting\n",
dbg_fa.fa_dev, dbg_fa.fa_ino, msg, info);
exit(1);
}
if ((fp = fopen(tfile, "w")) == NULL)
goto nofile;
fprintf(fp, "flags=0x%x dev=0x%x inode=%d\n",
dbg_flags, dbg_fa.fa_dev, (efs_ino_t)dbg_fa.fa_ino);
fprintf(fp, "dbg_fs:\n");
fprintf(fp, "size=%d firstcg=%d cgfsize=%d cgisize=%d\n",
dbg_fs.fs_size, dbg_fs.fs_firstcg, dbg_fs.fs_cgfsize,
dbg_fs.fs_cgisize);
fprintf(fp, "ncg=%d bmblock=%d bmsize=%d\n",
dbg_fs.fs_ncg, dbg_fs.fs_bmblock, dbg_fs.fs_bmsize);
fprintf(fp, "dbg_fa:\n");
fprintf(fp, "dev=0x%x ino=%d ne=%d nie=%d ex=0x%x ix=0x%x\n",
dbg_fa.fa_dev, (efs_ino_t)dbg_fa.fa_ino, dbg_fa.fa_ne,
dbg_fa.fa_nie, dbg_fa.fa_ex, dbg_fa.fa_ix);
fprexf(fp, &dbg_fa);
fprintf(fp, "dbg_nfa:\n");
fprintf(fp, "dev=0x%x ino=%d ne=%d nie=%d ex=0x%x ix=0x%x\n",
dbg_nfa.fa_dev, (efs_ino_t)dbg_nfa.fa_ino, dbg_nfa.fa_ne,
dbg_nfa.fa_nie, dbg_nfa.fa_ex, dbg_nfa.fa_ix);
fprexf(fp, &dbg_nfa);
fprintf(fp, "dbg_bex:\n");
fprex(fp, dbg_bne, dbg_bex, 0, 0);
fprintf(fp, "caller=%s info=%d\n", msg, info);
fclose(fp);
fsc_errpr("internal error, see %s\n", tfile);
exit(1);
}
void
dbg_copycheck(efs_daddr_t frombn, efs_daddr_t tobn, int nb)
{
struct fscarg *fap;
if (!(dbg_flags & D_LOCKED))
dbg_abort("cpcheck0", 0);
/*
* frombn+nb must be in current extents
* tobn+nb must be in new extents
*/
if (!dbg_bex || !dbg_bne) /* had to have BALLOC'ed first */
dbg_abort("cpcheck1", 0);
if (dbg_flags & D_COMMITTED)
fap = &dbg_nfa;
else
fap = &dbg_fa;
if (!inex(frombn, nb, fap->fa_ne, fap->fa_ex))
dbg_abort("cpcheck2", 0);
if (!inex(tobn, nb, dbg_bne, dbg_bex))
dbg_abort("cpcheck3", 0);
}
#endif /* DEBUG } */
/*
* Kernel primitives on the pseudo-driver
*/
/* ------------------------------------------------------------------------- */
static int fsc_fd;
fsc_init(int (*func)(const char *, ...), int d, int v)
{
int sz;
fsc_errpr = func;
dflag = d;
vflag = v;
for (sz = lbsize; sz + lbsize < EFS_MAXEXTENTLEN; sz += lbsize)
;
max_ext_size = sz;
if ((fsc_fd = open(DEVFSCTL, O_RDWR)) == -1)
return -1;
return 0;
}
void
fsc_close(void)
{
close(fsc_fd);
}
int
fsc_ilock(struct fscarg *fap)
{
#ifdef DEBUG
if (dflag && (dbg_flags & D_LOCKED))
dbg_abort("ilock", 0);
#endif
if (ioctl(fsc_fd, ILOCK, fap) == -1)
return -1;
#ifdef DEBUG
if (dflag) {
dbg_fa.fa_ino = fap->fa_ino;
dbg_fa.fa_dev = fap->fa_dev;
dbg_flags |= D_LOCKED;
}
#endif
return 0;
}
void
fsc_iunlock(struct fscarg *fap)
{
#ifdef DEBUG
if (dflag) {
int info;
if (fap->fa_dev != dbg_fa.fa_dev ||
fap->fa_ino != dbg_fa.fa_ino ||
!(dbg_flags & D_LOCKED))
dbg_abort("iunlock0", 0);
if ((dbg_flags & D_COMMITTED)
&& (info = exverify(&dbg_fs, &dbg_nfa)))
dbg_abort("iunlock1", info); /* ouch... bad news! */
if (dbg_bex) {
free(dbg_bex);
dbg_bex = 0;
dbg_bne = 0;
}
if (dbg_flags & D_GOTEX) {
freeex(&dbg_fa);
dbg_flags &= ~D_GOTEX;
}
if (dbg_flags & D_COMMITTED) {
freeex(&dbg_nfa);
bzero(&dbg_nfa, sizeof(dbg_nfa));
dbg_flags &= ~D_COMMITTED;
}
bzero(&dbg_fa, sizeof(dbg_fa));
dbg_flags &= ~D_LOCKED;
}
#endif
(void)ioctl(fsc_fd, IUNLOCK, fap);
}
int
fsc_icommit(struct fscarg *fap)
{
#ifdef DEBUG
if (dflag) {
int info;
if (!(dbg_flags & D_LOCKED) ||
fap->fa_dev != dbg_fa.fa_dev ||
fap->fa_ino != dbg_fa.fa_ino ||
(info = exverify(&dbg_fs, fap)))
dbg_abort("icommit0", info);
if ((dbg_flags & D_COMMITTED)
&& (info = exverify(&dbg_fs, &dbg_nfa)))
dbg_abort("icommit1", info);
}
#endif
if (ioctl(fsc_fd, ICOMMIT, fap) == -1) {
int error = errno;
if (vflag)
fsc_errpr("ioctl(ICOMMIT 0x%x i%d ne=%d ni=%d): %s\n",
fap->fa_dev, fap->fa_ino, fap->fa_ne,
fap->fa_nie, strerror(errno));
return error;
}
#ifdef DEBUG
if (dflag) {
if (dbg_flags & D_COMMITTED) {
freeex(&dbg_fa);
bcopy(&dbg_nfa, &dbg_fa, sizeof(dbg_fa));
bzero(&dbg_nfa, sizeof(dbg_nfa));
}
dbg_nfa.fa_ino = fap->fa_ino;
dbg_nfa.fa_dev = fap->fa_dev;
squirrelex(fap, &dbg_nfa);
dbg_flags |= D_COMMITTED;
free(dbg_bex);
dbg_bex = 0;
dbg_bne = 0;
}
#endif
return 0;
}
int
fsc_bitfunc(int wha, dev_t dev, efs_daddr_t bn, int len)
{
struct fscarg fa;
fa.fa_dev = dev;
fa.fa_bn = bn;
fa.fa_len = len;
#ifdef DEBUG
if (dflag) {
if (!(dbg_flags & D_LOCKED) || dbg_fa.fa_dev != dev)
dbg_abort("bit0", 0);
if (wha == BFREE) {
if ((dbg_flags & D_COMMITTED) && dbg_bne == 0) {
if (onex(bn, len, dbg_nfa.fa_ne,
dbg_nfa.fa_ex) ||
onex(bn, len, dbg_nfa.fa_nie,
dbg_nfa.fa_ix) ||
(!inex(bn, len, dbg_fa.fa_ne,
dbg_fa.fa_ex) &&
!inex(bn, len, dbg_fa.fa_nie,
dbg_fa.fa_ix)))
dbg_abort("bit1", 0);
} else if (dbg_bne) {
if (!inex(bn, len, dbg_bne, dbg_bex))
dbg_abort("bit2", 0);
} else {
dbg_abort("bit3", 0);
}
}
}
#endif
if (vflag > 1)
fsrprintf("bitfunc %s %d(%d)\n",
wha == BFREE ? "BFREE" : "BALLOC", bn, len);
if (ioctl(fsc_fd, wha, &fa) == -1) {
if (vflag > 1)
fsc_errpr("bitfunc %d error %d %d(%d)\n", wha,
errno, bn, len);
return -1;
}
#ifdef DEBUG
if (dflag && wha == BALLOC) {
extent *ex;
if ((dbg_bex=(extent*)realloc(dbg_bex,
(dbg_bne + 1) * sizeof(extent))) == NULL) {
fsc_errpr("malloc() failed\n");
exit(1);
}
ex = &dbg_bex[dbg_bne++];
ex->ex_bn = bn;
ex->ex_length = len;
if (fsc_tstalloc(fa.fa_dev, bn) < (long)len)
dbg_abort("allocfail", bn);
}
#endif
return 0;
}
int
fsc_tstfunc(int wha, dev_t dev, efs_daddr_t bn)
{
struct fscarg fa;
int len;
fa.fa_dev = dev;
fa.fa_bn = bn;
if ((len = ioctl(fsc_fd, wha, &fa)) == -1)
return -1;
return len;
}
/* ------------------------------------------------------------------------- */
int
getex(struct efs_mount *mp, struct efs_dinode *di,
struct fscarg *fap, int locked)
{
fap->fa_ex = efs_getextents(mp, di, fap->fa_ino);
if (fap->fa_ex == 0)
return 0;
fap->fa_ne = di->di_numextents;
fap->fa_nie = 0;
fap->fa_ix = NULL;
if (di->di_numextents > EFS_DIRECTEXTENTS) {
struct efs_dinode di_local = *di;
struct efs_dinode *di2 = di;
if (!locked && fsc_ilock(fap)) {
freeex(fap);
return 0;
}
di2 = efs_iget(mp, fap->fa_ino);
if (!locked)
fsc_iunlock(fap);
if (!di2) {
freeex(fap);
return 0;
}
if (di2 != di)
*di = *di2;
di_local.di_atime = di->di_atime;
if (bcmp(&di_local, di, sizeof(*di))) {
freeex(fap);
return 0;
}
fap->fa_nie = di->di_u.di_extents[0].ex_offset;
if ((fap->fa_ix = (extent*)malloc(fap->fa_nie * sizeof(extent)))
== NULL) {
fsc_errpr("getext(i%d) malloc failed\n", fap->fa_ino);
freeex(fap);
return 0;
}
bcopy(di->di_u.di_extents, fap->fa_ix,
fap->fa_nie * sizeof(extent));
if (exverify(mp->m_fs, fap)) {
freeex(fap);
return 0;
}
}
#ifdef DEBUG
if (dflag && dbg_fa.fa_ino == fap->fa_ino) {
if (exverify(mp->m_fs, fap))
return 0;
squirrelex(fap, &dbg_fa);
bcopy(mp->m_fs, &dbg_fs, sizeof(struct efs));
dbg_flags |= D_GOTEX;
}
#endif
return 1;
}
void
freeex(struct fscarg *fap)
{
free(fap->fa_ex);
fap->fa_ex = 0;
if (fap->fa_ix) {
free(fap->fa_ix);
fap->fa_ix = 0;
}
}
static void
squirrelex(struct fscarg *from, struct fscarg *to)
{
to->fa_ne = from->fa_ne;
assert(!to->fa_ex);
assert(!to->fa_ix);
if ((to->fa_ex = (extent*)malloc(to->fa_ne * sizeof(extent))) == NULL) {
fsc_errpr("malloc() failed\n");
exit(1);
}
bcopy(from->fa_ex, to->fa_ex, to->fa_ne * sizeof(extent));
if (from->fa_nie) {
to->fa_nie = from->fa_nie;
if ((to->fa_ix = (extent*)
malloc(to->fa_nie * sizeof(extent))) == NULL) {
fsc_errpr("malloc() failed\n");
exit(1);
}
bcopy(from->fa_ix, to->fa_ix,
to->fa_nie * sizeof(extent));
} else {
to->fa_nie = 0;
to->fa_ix = 0;
}
}
/* ------------------------------------------------------------------------- */
int
exverify(struct efs *fs, struct fscarg *fap)
{
extent *ex = fap->fa_ex;
int ne = fap->fa_ne;
off_t offset = 0;
int indirbbs = 0;
if (ne < 0 || ne > EFS_MAXEXTENTS)
return 1;
for (; ne--; ex++) {
if (invalidblocks(fs, ex->ex_bn, ex->ex_length))
return 3;
if (fsc_tstalloc(fap->fa_dev, ex->ex_bn) < (long)ex->ex_length)
return 4;
if (offset != ex->ex_offset)
return 5;
offset += ex->ex_length;
}
if (fap->fa_ne <= EFS_DIRECTEXTENTS && fap->fa_nie != 0)
return 6;
for (ex=fap->fa_ix, ne=fap->fa_nie; ne--; ex++) {
if (invalidblocks(fs, ex->ex_bn, ex->ex_length))
return 8;
if (fsc_tstalloc(fap->fa_dev, ex->ex_bn) < (long)ex->ex_length)
return 9;
indirbbs += ex->ex_length;
}
if (fap->fa_nie && BTOBB(fap->fa_ne * sizeof(extent)) != indirbbs)
return 10;
return 0; /* a-ok */
}
int
invalidblocks(struct efs *fs, efs_daddr_t bn, int len)
{
if ( /* just plain wacko */
bn < 0 || len < 1 ||
/* beyond last data block in file system */
bn >= EFS_CGIMIN(fs, fs->fs_ncg) ||
/* 'bn' is in an i-list */
bn < EFS_CGIMIN(fs, EFS_BBTOCG(fs, bn)) + fs->fs_cgisize ||
/* bn+len extents out of data block area of cg */
EFS_BBTOCG(fs, bn) != EFS_BBTOCG(fs, bn + len - 1) )
return 1;
return 0;
}
static int
inex(efs_daddr_t bn, int len, int ne, extent *ex)
{
for (; ne--; ex++)
if (bn >= (long)ex->ex_bn
&& bn + len <= (long)ex->ex_bn + (long)ex->ex_length)
return 1;
return 0;
}
static int
onex(efs_daddr_t bn, int len, int ne, extent *ex)
{
for (; ne--; ex++)
if ((bn >= (long)ex->ex_bn
&& bn < (long)ex->ex_bn + (long)ex->ex_length) ||
(bn + len > (long)ex->ex_bn &&
bn + len < (long)ex->ex_bn + (long)ex->ex_length))
return 1;
return 0;
}
/* ------------------------------------------------------------------------- */
efs_daddr_t
freefs(struct efs_mount *mp, int cg, int wantlen, /* RETURN */ int *gotlen)
{
int cg0, biggest = 0;
efs_daddr_t newbn, bigbn;
/* search in 'cg' */
newbn = freecg(mp, cg, wantlen, gotlen);
if (newbn == -1 || *gotlen >= wantlen)
return newbn;
biggest = *gotlen;
bigbn = newbn;
/* search from 'cg' outward */
for (cg0=cg+1; cg0 < mp->m_fs->fs_ncg; cg0++) {
newbn = freecg(mp, cg0, wantlen, gotlen);
if (newbn == -1 || *gotlen >= wantlen)
return newbn;
if (*gotlen > biggest) {
biggest = *gotlen;
bigbn = newbn;
}
}
/* search from 'cg' inward */
for ( cg0=cg-1; cg0 >= 0; cg0-- ) {
newbn = freecg(mp, cg0, wantlen, gotlen);
if (newbn == -1 || *gotlen >= wantlen)
return newbn;
if (*gotlen > biggest) {
biggest = *gotlen;
bigbn = newbn;
}
}
/* couldn't find 'wantlen', return the biggest we did find */
*gotlen = biggest;
return bigbn;
}
efs_daddr_t
freecg(struct efs_mount *mp, int cg, int wantlen, int *gotlen)
{
return freebetween(mp->m_dev,
EFS_CGIMIN(mp->m_fs, cg) + mp->m_fs->fs_cgisize,
EFS_CGIMIN(mp->m_fs, cg+1), wantlen, 0, gotlen);
}
/*
* Look for 'wantlen' blocks ['startbn','endbn').
* Returns bn of first extent of blocks greater than 'wantlen' bbs
* or the largest range found. If 'prebn' is non-NULL it will be
* used to record the 'bn' of the first hole preceding 'startbn'.
* If 'prebn' is non-NULL and 'beforebn-1' is not free: *prebn = 0.
*
* assumes 'startbn' and 'beforebn-1' are in the same cg
*/
efs_daddr_t
freebetween(dev_t dev, efs_daddr_t startbn, efs_daddr_t beforebn, int wantlen,
/* RETURN */ efs_daddr_t *prebn, int *foundlen)
{
int len;
efs_daddr_t bn = 0;
*foundlen = 0;
if (prebn)
*prebn = 0;
while (startbn < beforebn) {
if ((len = fsc_tstfree(dev, startbn)) == -1)
return -1;
if (len == 0) {
if ((len = fsc_tstalloc(dev, startbn)) == -1)
return -1;
startbn += len;
continue;
}
if (len >= wantlen) {
*foundlen = len;
return startbn;
}
if (len > *foundlen) { /* track the largest */
*foundlen = len;
bn = startbn;
}
if (prebn && startbn + len == beforebn)
*prebn = startbn;
startbn += len;
}
return bn;
}
void
print_free(dev_t dev, efs_daddr_t startbn, efs_daddr_t beforebn)
{
int len;
int cnt = 0;
int free = 1;
fsrprintf("free %d->%d", startbn, beforebn);
while (startbn < beforebn) {
if (!(cnt++ % 8))
fsrprintf("\nf");
if (free)
len = fsc_tstfree(dev, startbn);
else
len = fsc_tstalloc(dev, startbn);
fsrprintf(" %s%d", free ? "f" : "a", len);
free = !free;
if (len < 0)
break;
startbn += len;
}
fsrprintf("\n");
}
/*
* 'nb' never greater than EFS_MAXEXTENTLEN
* Use libdisk readb/writeb (syssgi(SGI_READB/WRITEB...)/rdwrb) to cover
* devices larger than 2GB.
*/
int
copy(int fd, efs_daddr_t frombn, efs_daddr_t tobn, int nb)
{
char buf[BBSIZE*EFS_MAXEXTENTLEN];
int bbcount;
if (vflag>1)
fsc_errpr("copy() %d+%d -> %d\n", frombn, nb, tobn);
#ifdef DEBUG
if (dflag)
dbg_copycheck(frombn, tobn, nb);
#endif
assert(nb > 0 && nb <= EFS_MAXEXTENTLEN);
bbcount = efs_readb(fd, buf, frombn, nb);
if (bbcount == -1) {
if (vflag)
fsc_errpr("efs_readb(%d blocks) failed: %s\n",
nb, strerror(errno));
return -1;
}
if (bbcount != nb) {
if (vflag)
fsc_errpr("efs_readb(%d blocks) got %d\n", nb, bbcount);
return -1;
}
bbcount = efs_writeb(fd, buf, tobn, nb);
if (bbcount == -1) {
if (vflag)
fsc_errpr("efs_writeb(%d blocks) failed: %s\n",
nb, strerror(errno));
return -1;
}
if (bbcount != nb) {
if (vflag)
fsc_errpr("write(%d bytes) got %d\n", nb, bbcount);
return -1;
}
return 0;
}
extent *
offtoex(extent *ex, int ne, off_t offset)
{
for (; ne--; ex++)
if (ex->ex_offset == offset)
return ex;
#ifdef DEBUG
if (dflag)
dbg_abort("offtoex", offset);
#endif
return 0;
}
char *
devnm(dev_t dev)
{
char name[MAXPATHLEN];
int namelen;
namelen = sizeof(name);
if (dev_to_rawpath(dev, name, &namelen, NULL))
return strdup(name);
fsc_errpr("could not find device file for dev number 0x%x\n", dev);
return NULL;
}
/* ------------------------------------------------------------------------- */
void
fspieces(struct efs_mount *mp, struct efsstats *es)
{
struct efs_dinode *di, *enddi, *inos;
extent *ex;
int ne, inobytes, cg;
efs_ino_t inum;
es->tpieces = 0;
es->tbb = 0;
es->tholes = 0;
es->tfreebb = 0;
fsync(mp->m_fd);
inobytes = EFS_INOPBB * mp->m_fs->fs_cgisize*sizeof(struct efs_dinode);
inos = (struct efs_dinode *) malloc(inobytes);
if (inos == NULL) {
fsc_errpr("malloc(%s) failed\n", inobytes);
exit(1);
}
/*
* Compute average file fragmentation:
*
* foreach cg
* snarf out the whole i-list
* foreach reg file
* filepieces()
*/
for (cg = 0; cg < mp->m_fs->fs_ncg; cg++) {
inos = efs_igetcg(mp, cg);
if (inos == 0)
exit(1);
enddi = &inos[EFS_INOPBB * mp->m_fs->fs_cgisize];
if (cg == 0) {
di = inos + 4;
inum = 4; /* we know 2 and 3 are dirs */
} else {
di = inos;
}
for (; di < enddi; di++, inum++)
if (S_ISREG(di->di_mode) && di->di_numextents > 0) {
ex = efs_getextents(mp, di, inum);
if (ex == 0)
exit(1);
ne = di->di_numextents;
es->tpieces += filepieces(ex, ne);
es->tbb += ex[ne-1].ex_offset +
ex[ne-1].ex_length;
free(ex);
}
free(inos);
}
/*
* Compute file system free space fragmentation
*
* read in the bitmap
* foreach cg
* count # free "holes"
* count # free blocks
*/
if (efs_bget(mp))
exit(1);
for (cg = 0; cg < mp->m_fs->fs_ncg; cg++) {
int bn = EFS_CGIMIN(mp->m_fs, cg) + mp->m_fs->fs_cgisize;
int endbn = EFS_CGIMIN(mp->m_fs, cg+1);
while (bn < endbn) {
if (btst(mp->m_bitmap, bn)) {
while (bn < endbn && btst(mp->m_bitmap, bn)) {
bn++;
es->tfreebb++;
}
es->tholes++;
} else {
while (bn < endbn && btst(mp->m_bitmap, bn) ==0)
bn++;
}
}
}
}
/*
* returns count of 'pieces' -- promotes abutting extents to a single piece
*/
int
filepieces(extent *ex, int ne)
{
int npieces = 1;
if (ne == 0)
return 0;
for (; --ne > 0; ex++)
if (ex[0].ex_bn + ex[0].ex_length != ex[1].ex_bn)
npieces++;
return npieces;
}
/* ------------------------------------------------------------------------- */
/*
* overlapped movex
*
* < slidesize >< length >
* YYYY.............XXXXXXXXXX......
* ^newbn ^offset
*
*/
int
slidex(struct efs_mount *mp, struct fscarg *fap,
off_t offset, efs_daddr_t newbn, int length, int slidesize)
{
int mvlen;
int sparebn;
int nspare;
int error;
if (vflag)
fsc_errpr("slidex() i%d %d+%d -> %d (%d)\n",
fap->fa_ino, offset, length, newbn, slidesize);
if (slidesize * 2 >= max_ext_size)
nspare = 0;
else
sparebn = freefs(mp, 0, MIN(length, max_ext_size), &nspare);
if (nspare > 2 * slidesize) {
while (length > 0) {
mvlen = MIN(length, nspare);
if (error = movex(mp, fap, offset, sparebn, mvlen))
return error;
if (error = movex(mp, fap, offset, newbn, mvlen))
return error;
newbn += mvlen;
offset += mvlen;
length -= mvlen;
}
return 0;
}
while (length > 0) {
mvlen = MIN(length, slidesize);
if (error = movex(mp, fap, offset, newbn, mvlen))
return error;
newbn += mvlen;
offset += mvlen;
length -= mvlen;
}
return 0;
}
/*
* move 'offset'+'mvlen' to newbn. newbn+mvlen assumed contiguous.
* carves mvlen into maximal extents.
*/
int
movex(struct efs_mount *mp, struct fscarg *fap,
off_t offset, efs_daddr_t newbn, int mvlen)
{
extent *ex, *ex0;
int ne, ne0, len, len2;
int error;
if (vflag)
fsc_errpr("movex() i%d %d+%d -> %d\n",
fap->fa_ino, offset, mvlen, newbn);
assert(mvlen > 0);
/* fit as many logical blocks as possible within an extent */
ne = mvlen / max_ext_size;
if (mvlen % max_ext_size)
ne++;
ex0 = ex = (extent*)malloc(ne * sizeof(extent));
if (ex == NULL) {
fsc_errpr("movex() malloc(ne=%d) failed\n", ne);
exit(1);
}
len2 = mvlen;
for (ne0 = ne; ne0--; ex0++) {
len = MIN(len2, max_ext_size);
ex0->ex_bn = newbn;
ex0->ex_length = len;
newbn += len;
len2 -= len;
}
error = vecmovex(mp, fap, ne, ex, offset, mvlen);
free(ex);
return error;
}
#define SIGHOLD() sighold(SIGINT); sighold(SIGHUP); sighold(SIGTERM);
#define SIGRELSE() sigrelse(SIGINT); sigrelse(SIGHUP); sigrelse(SIGTERM);
int
vecmovex(struct efs_mount *mp, struct fscarg *fap,
int vecne, extent *vecex, off_t offset, int nblocks)
{
struct fscarg savefap;
int ret;
/* keep some common signals from keeping us from leaving
* free blocks allocated */
SIGHOLD();
savefap = *fap;
if (vecballoc(fap->fa_dev, vecne, vecex) == -1) {
SIGRELSE();
return -1;
}
if (veccopy(mp->m_fd, vecex,
offtoex(fap->fa_ex, fap->fa_ne, offset), nblocks) == -1){
vecfree(fap->fa_dev, vecne, vecex);
SIGRELSE();
return -1;
}
if (veccomputex(mp, fap, vecne, vecex, offset, nblocks) == -1) {
vecfree(fap->fa_dev, vecne, vecex);
SIGRELSE();
return -1;
}
if (ret = fsc_icommit(fap)) {
/* BFREE the vecballoc'ed blocks and new indirs */
vecfree(fap->fa_dev, vecne, vecex);
vecfree(fap->fa_dev, fap->fa_nie, fap->fa_ix);
free(fap->fa_ex);
if (fap->fa_ix)
free(fap->fa_ix);
*fap = savefap;
SIGRELSE();
return ret;
}
/*
* free old data and indirect blocks
*/
offfree(&savefap, offset, nblocks);
vecfree(fap->fa_dev, savefap.fa_nie, savefap.fa_ix);
free(savefap.fa_ex);
if (savefap.fa_ix)
free(savefap.fa_ix);
SIGRELSE();
return 0;
}
static int
vecballoc(dev_t dev, int vecne, extent *vecex)
{
int ne;
extent *ex;
for (ne = 0, ex = vecex; ne < vecne; ne++, ex++)
if (fsc_balloc(dev, ex->ex_bn, ex->ex_length) == -1) {
vecfree(dev, ne, vecex);
return -1;
}
return 0;
}
/*
* free the blocks described by the extents
*/
static void
vecfree(dev_t dev, int vecne, extent *vecex)
{
for (; vecne--; vecex++)
fsc_bfree(dev, vecex->ex_bn, vecex->ex_length);
}
/*
* free the blocks corresponding to 'offset'+'len'
*/
static void
offfree(struct fscarg *fap, off_t offset, int len)
{
int nb;
off_t lastoff = offset + len;
extent *ex = offtoex(fap->fa_ex, fap->fa_ne, offset);
while (offset < lastoff) {
if ((long)ex->ex_offset + (long)ex->ex_length > lastoff)
nb = lastoff - ex->ex_offset;
else
nb = ex->ex_length;
fsc_bfree(fap->fa_dev, ex->ex_bn, nb);
offset += nb;
ex++;
}
}
/*
* copy the contents of the data blocks from fap's 'offset'+'nblocks'
* to the blocks described by 'vecex'
*/
static int
veccopy(int fd, extent *toex, extent *fromex, int nblocks)
{
int frombn, fromlen;
int tobn, tolen;
int copylen;
tobn = toex->ex_bn;
tolen = toex->ex_length;
frombn = fromex->ex_bn;
fromlen = fromex->ex_length;
for (;;) {
copylen = MIN(fromlen, tolen);
if (copy(fd, frombn, tobn, copylen) == -1)
return -1;
if ((nblocks -= copylen) == 0) /* end loop */
return 0;
tolen -= copylen;
if (tolen > 0)
tobn += copylen;
else {
toex++;
tobn = toex->ex_bn;
tolen = toex->ex_length;
}
fromlen -= copylen;
if (fromlen > 0)
frombn += copylen;
else {
fromex++;
frombn = fromex->ex_bn;
fromlen = fromex->ex_length;
}
}
/* NOTREACHED */
}
/*
* compute new extents, BALLOC blocks and compute new indir extents
* returns -1 and fap unchanged on error
*/
static int
veccomputex(struct efs_mount *mp, struct fscarg *fap, int vecne, extent *vecex,
off_t offset, int nblocks)
{
extent *ex, *ex1, *newex, *newix, splitex;
int n0, o0, newne, headne, tailne;
off_t tailoff;
int newnie;
int blendlen = 0;
splitex.ex_bn = 0;
/*
* compute number of new extents and malloc space
* new extent list derived from 4 possible pieces:
* -head any extents in fap->fa_ex before offset
* -vec vecex
* -split the extent created if offset+nblocks
* not on an extent boundary
* -tail any extents in fap->fa_ex whose ex_offset
* is > offset + nblocks
*/
ex = offtoex(fap->fa_ex, fap->fa_ne, offset);
assert(ex != NULL);
headne = (ex - fap->fa_ex);
tailoff = offset + nblocks;
newne = headne + vecne;
/* fully blend 1st vecex into last head if possible */
if (headne > 0) {
extent *ex0 = &fap->fa_ex[headne - 1];
if (ex0->ex_bn + ex0->ex_length == vecex->ex_bn
&& (long)(ex0->ex_length + vecex->ex_length)
<= max_ext_size)
{
blendlen = ex0->ex_length + vecex->ex_length;
vecex++; /* careful, may no longer be valid! */
vecne--; /* when 0, vecex is invalid */
newne--;
}
}
if (tailoff < (long)getnblocks(fap->fa_ex, fap->fa_ne)) {
/* file is longer than 'offset' */
/* find the first extent after offset+nblocks */
for (ex1 = ex;
tailoff > (long)(ex1->ex_offset+ex1->ex_length);
ex1++)
;
/* ex1 at possibly split extent */
if (ex1->ex_offset + ex1->ex_length != tailoff) {
++newne;
splitex.ex_bn = ex1->ex_bn + (tailoff - ex1->ex_offset);
splitex.ex_length = ex1->ex_length -
(tailoff - ex1->ex_offset);
}
ex1++;
tailne = &fap->fa_ex[fap->fa_ne] - ex1;
} else /* offset+nblocks goes to the end of the file */
tailne = 0;
newne += tailne;
if (newne > EFS_MAXEXTENTS)
return -1;
if ((newex = (extent*)malloc(newne * sizeof(extent))) == NULL) {
fsc_errpr("veccomputex() malloc(ne=%d) failed\n", newne);
exit(1);
}
/*
* copy head extents from fap, mid extents from vec, tail from fap
*/
if (headne)
bcopy(fap->fa_ex, newex, headne * sizeof(extent));
if (blendlen)
newex[headne-1].ex_length = blendlen;
if (vecne)
bcopy(vecex, &newex[headne], vecne * sizeof(extent));
if (splitex.ex_bn)
bcopy(&splitex, &newex[headne + vecne], sizeof(extent));
if (tailne)
bcopy(ex1, &newex[newne - tailne], tailne * sizeof(extent));
/*
* compute the proper ex_offset for each extent
*/
for (o0 = 0, ex = newex, n0 = newne; n0--; ex++) {
ex->ex_offset = o0;
ex->ex_magic = 0; /* (AFS data written under efs_iupdat) */
o0 += ex->ex_length;
}
/*
* compute number of indir extents and BALLOC a whole new set
* of indirect extent blocks
*/
if (indir(mp, fap->fa_dev, fap->fa_ino, newne, &newix, &newnie) == -1) {
free(newex);
return -1;
}
fap->fa_ex = newex;
fap->fa_ne = newne;
fap->fa_ix = newix;
fap->fa_nie = newnie;
return 0;
}
static int
indir(struct efs_mount *mp, dev_t dev, efs_ino_t ino, int ne, extent **ix,
int *nie)
{
int n0, indirbbs;
extent *ex, *ex1;
efs_daddr_t newbn;
int nfree;
struct efs *fs = mp->m_fs;
if (ne <= EFS_DIRECTEXTENTS) {
*nie = 0;
*ix = NULL;
return 0;
}
*ix = (extent*)malloc(EFS_DIRECTEXTENTS * sizeof(extent));
if (*ix == NULL) {
fsc_errpr("indir() malloc(newix) failed\n");
exit(1);
}
indirbbs = (int)BTOBB(ne * sizeof(extent));
*nie = 0;
for (ex = *ix; indirbbs > 0; ex++) {
n0 = MIN(EFS_MAXINDIRBBS, indirbbs);
newbn = freefs(mp, EFS_ITOCG(fs, ino), n0, &nfree);
n0 = MIN(n0, nfree);
indirbbs -= n0;
if (newbn == -1 ||
fsc_balloc(dev, newbn, n0) == -1 ||
++(*nie) > EFS_DIRECTEXTENTS) {
for (ex1 = *ix; ex1 < ex; ex1++)
fsc_bfree(dev, ex1->ex_bn, ex1->ex_length);
/* free(newex); */
free(*ix);
return -1;
}
ex->ex_bn = newbn;
ex->ex_length = n0;
}
return 0;
}
int
moveix(struct efs_mount *mp, struct fscarg *fap, efs_daddr_t newbn, int ni)
{
int errsave;
efs_daddr_t freebn;
if (fsc_balloc(mp->m_dev, newbn, fap->fa_ix[ni].ex_length) == -1)
return errno;
freebn = fap->fa_ix[ni].ex_bn;
fap->fa_ix[ni].ex_bn = newbn;
if (errsave = fsc_icommit(fap)) {
fsc_bfree(mp->m_dev, newbn, fap->fa_ix[ni].ex_length);
fap->fa_ix[ni].ex_bn = freebn;
return errsave;
}
fsc_bfree(mp->m_dev, freebn, fap->fa_ix[ni].ex_length);
return 0;
}
void
fprexf(FILE *fp, struct fscarg *fap)
{
fprex(fp, fap->fa_ne, fap->fa_ex, fap->fa_nie, fap->fa_ix);
}
void
fprex(FILE *fp, int ne, extent *ex, int nie, extent *ix)
{
if (ne < 1 || ne > EFS_MAXEXTENTS)
return;
for(; ne--; ex++)
fprintf(fp,"%d+%d(%d)\n",ex->ex_bn, ex->ex_length, ex->ex_offset);
for(; nie--; ix++)
fprintf(fp,"i%d+%d(%d)\n",ix->ex_bn, ix->ex_length, ix->ex_offset);
}
#ifdef notdef
void
dump_free_list(struct efs_mount *mp)
{
dev_t dev = mp->m_dev;
struct efs *fs = mp->m_fs;
int ncyl = fs->fs_ncg;
int cyl;
for (cyl = 0; cyl < ncyl; cyl++) {
efs_daddr_t startbn;
efs_daddr_t endbn;
efs_daddr_t len;
int pcnt;
fsrprintf("Cyl %d", cyl);
startbn = EFS_CGIMIN(fs, cyl) + fs->fs_cgisize;
endbn = EFS_CGIMIN(fs, cyl+1);
pcnt = 0;
while (startbn < endbn) {
len = fsc_tstalloc(dev, startbn);
if (len != 0) {
if (len < 0)
break;
startbn += len;
continue;
}
len = fsc_tstfree(dev, startbn);
if (len <= 0)
break;
if (!((pcnt++) % 4))
fsrprintf("\nf");
fsrprintf(" %d(%d)", startbn, len);
startbn += len;
}
fsrprintf("\n");
}
}
#endif
int
fsrprintf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (gflag) {
static int didopenlog;
if (!didopenlog) {
openlog("fsr", LOG_PID, LOG_USER);
didopenlog = 1;
}
vsyslog(LOG_INFO, fmt, ap);
} else
vprintf(fmt, ap);
va_end(ap);
return 0;
}