/* * EFS-aware file system copy * * efscopy fromdev|fromfile todev [todev ...] * efscopy fromdev tofile * * Each 'todev' must be the same size, but can be different than 'from*' if * 'todev' is big enough to hold the inodes and data. * * We can deal with writing to a file system whose geometry differs from the * one we're reading, but to make life easier we don't change anyone's inode * number (directory blocks are copied untouched, etc). We also assume that * the destination file system is empty (freshly mkfsed). * * In general this exits on anything suspicious. * * TODO: * support different sized to's * do the "mkfs" * continue in the face of errors */ static char ident[] = "@(#) efscopy.c $Revision: 1.2 $"; #include #include #include #include #include #include #include #include #include /*#define NDEBUG /* turns off assert()'s */ #include extern int errno; #include "libefs.h" #define PROGNAME "efscopy" /* for printfs */ #define DISK_ACTIVE 0 #define DISK_NOT_ACTIVE 1 #define GUI 1 #define GUI_filename "/tmp/efsdks_file" /* * The number of blocks written (per target). Used in the progress "bar" * and to compute target super block free count. */ int flushblks; /* * When copying the file system we may end up allocating a different number * of blocks for indirect extents -- different layout causing files to * go from 12 to 13 extents (or vice-versa), etc. This counter records * the _decrease_ in indirect blocks and is printed if -v. * This is to reconcile the difference in used block count between the * source and destination as reported by fsck and other tools. */ int indirfewer; int debug; /* might mean anything */ /* * Print stuff as we run. If == 0 print nothing */ int vflag; /* * This 16 is the difference between getting 1.5mb/sec aggregate * SCSI bus throughput (4 threads) and ~6mb/sec. */ int aiothreads = 16; /* (aio-internal default is 4) */ int bufblks = 8*EFS_MAXEXTENTLEN; /* ~1mb seems to work best */ #define TOMAX 128 /* or OPEN_MAX ... */ EFS_MOUNT *tomp[TOMAX]; int ntos; struct aiocb asuper[TOMAX]; struct aiocb abit[TOMAX]; void addextents(extent *, int , efs_daddr_t , int, int); void indiroffsets(extent *, int , char *); int writefs(EFS_MOUNT *frommp, efs_ino_t maxinum, int blkcount, FILE *p); int writeinode(EFS_MOUNT *mp, struct efs_dinode *dp, efs_ino_t inum); extent *mkboth(EFS_MOUNT *mp, struct efs_dinode *fromdp, struct efs_dinode *todp, int nblocks, efs_ino_t inum); void excopy(EFS_MOUNT *frommp, extent *fromex, extent *toex, int nblocks); void exdatacopy(char *data, int tofd, extent *toex, int nblocks); void datacopy(int tofd, char *data, efs_daddr_t tobn, int nb); void copy(int fromfd, efs_daddr_t frombn, int tofd, efs_daddr_t tobn, int nb); void alloccgs(EFS_MOUNT *mp, efs_ino_t maxinum); void aio_writecgs(EFS_MOUNT *mp, efs_ino_t maxinum); efs_daddr_t dalloc(EFS_MOUNT *mp, int nblocks, int *nfound); void initbuffers(int blks); void flushcopy(int whichbuf, efs_daddr_t bn, int nbytes); int ifmountedfail(char *name); main(int argc, char **argv) { char *fromfile, *tofile; EFS_MOUNT *frommp; int blkcount = 0; int inodecount; efs_ino_t maxinum; char c, stderr_msg[100]; extern int optind; int i; struct efs *fs; int toafile = 0; /* printf ("aaa [%d] %s %s %s %s]\n", argc, argv[0], argv[1], argv[2], argv[3]); */ while ((c = getopt(argc, argv, "sva:d")) != (char)-1) { switch (c) { case 'v': vflag = 1; break; case 'd': debug = 1; break; } } if (argc - optind < 2) { fprintf(stderr, "usage: efscopy [-v] fromdev|fromfile todev [todev ...]\n"); fprintf(stderr, " or: efscopy [-v] fromdev|fromfile tofile\n"); exit(1); } fromfile = argv[optind]; ifmountedfail(fromfile); if ((frommp = efs_mount(fromfile, O_RDONLY)) == 0) { fprintf(stderr,"%s: efs_mount(%s, O_RDONLY) failed\n", PROGNAME, fromfile); exit(1); } /* * efscopy fromdev tofile */ if (argc - optind == 2) { struct stat sb; tofile = argv[optind + 1]; if (stat(tofile, &sb) != -1) { /* * "tofile" exists, exit unless it's a chr device. */ if (S_ISREG(sb.st_mode)) { fprintf(stderr, "%s: %s already exists\n", PROGNAME, tofile); exit(1); } else if (!S_ISCHR(sb.st_mode)) { fprintf(stderr, "%s: %s must be chr dev or non-existent file\n", PROGNAME, tofile); exit(1); } /* else it's CHR and that's okay by me */ } else if (errno == ENOENT) { /* * File doesn't exist, create it... */ int fd; struct efs fs; if ((fd = open(tofile, O_WRONLY|O_CREAT, 0644)) == -1) { #ifdef GUI fprintf(stderr, "%s: %s=FAIL, open(%s) failed errno=%d\n", PROGNAME, tofile, errno); sprintf(stderr_msg, "%s: %s=FAIL, open(%s) failed errno=%d\n", PROGNAME, tofile, errno); process_diskunit_error(-1, stderr_msg); #else fprintf(stderr,"%s: open(%s) failed errno=%d\n", PROGNAME, tofile, errno); #endif exit(1); } /* * ...and, give it a superblock which will pass * efs_mount and the capacity tests below. */ bcopy(frommp->m_fs, &fs, sizeof(struct efs)); fs.fs_tfree = CAPACITYBLKS(&fs); fs.fs_tinode = CAPACITYINODES(&fs); if (efs_writeb(fd, (char *)&fs, BTOBB(EFS_SUPERBOFF), 1) != 1) { #ifdef GUI fprintf(stderr, "%s: %s=FAIL, efs_writeb(%s) failed\n", PROGNAME, tofile); sprintf(stderr_msg, "%s: %s=FAIL, efs_writeb(%s) failed\n", PROGNAME, tofile); process_diskunit_error(-1, stderr_msg); #else fprintf(stderr,"%s: efs_writeb(%s) failed\n", PROGNAME, tofile); #endif exit(1); } close(fd); /* * Now fall through to efs_mount as normal */ toafile = 1; } else { fprintf(stderr,"%s: stat(%s) failed errno=%d\n", PROGNAME, tofile, errno); exit(1); } } /* * efscopy fromdev|fromfile todev ... */ for (optind++, i = 0; optind < argc; optind++, i++) { struct efs *fs; tofile = argv[optind]; ifmountedfail(tofile); if ((tomp[i] = efs_mount(tofile, O_RDWR)) == 0) { fprintf(stderr,"%s: efs_mount(%s, O_RDWR) failed\n", PROGNAME, tofile); exit(1); } #ifdef GUI tomp[i]->active = DISK_ACTIVE; if (strlen(tofile) > 39) printf("Too Long-(%s)\n", tofile); strcpy(tomp[i]->tofile, tofile); #endif fs = tomp[i]->m_fs; /* * all targets must be same size */ if (CAPACITYBLKS(fs) != CAPACITYBLKS(tomp[0]->m_fs)) { #ifdef GUI fprintf(stderr, "%s: %s=FAIL, not same size\n", PROGNAME, tofile); sprintf(stderr_msg, "%s: %s=FAIL, not same size\n", PROGNAME, tofile); process_diskunit_error(i, stderr_msg); #else fprintf(stderr, "%s: %s not same size\n", PROGNAME, tofile); exit(1); #endif } /* * assumed to be freshly mkfs'ed. if capacity - currently * free > 22 (dirblks for . and lost+found) */ if (CAPACITYBLKS(fs) - fs->fs_tfree > 22 || CAPACITYINODES(fs) - fs->fs_tinode > 2) { #ifdef GUI fprintf(stderr, "%s=FAIL, not empty, mkfs before using %s\n", tofile, PROGNAME); sprintf(stderr_msg, "%s=FAIL, not empty, mkfs before using %s\n", tofile, PROGNAME); process_diskunit_error(i, stderr_msg); #else fprintf(stderr, "%s not empty, mkfs before using %s\n", tofile, PROGNAME); exit(1); #endif } } ntos = i; if (toafile) { tomp[0]->m_bitmap = (char *)calloc(1, BBTOB(BTOBB(tomp[0]->m_fs->fs_bmsize))); } else { if (efs_bget(tomp[0]) == -1) { fprintf(stderr,"%s: efs_bget(%s) failed\n", PROGNAME, tomp[0]->m_devname); exit(1); } for (i = 1; i < ntos; i++) { #ifdef GUI if (tomp[i]->active == DISK_NOT_ACTIVE) { continue; } #endif tomp[i]->m_bitmap = tomp[0]->m_bitmap; } } maxinum = efs_readinodes(frommp, vflag ? stdout : 0, &blkcount, debug); if (maxinum == 0) { fprintf(stderr, "%s: efs_readinodes returned 0 (%s mounted?)\n", PROGNAME, fromfile); exit(1); } /* is "tofile" big enough? */ if (CAPACITYBLKS(tomp[0]->m_fs) < blkcount || CAPACITYINODES(tomp[0]->m_fs) < maxinum) { fprintf(stderr, "%s: %s too small (%d blks) to hold data in %s (%d blks)\n", PROGNAME, tofile, CAPACITYBLKS(tomp[0]->m_fs), fromfile, blkcount); exit(1); } /* allocate enough cg's of efs_dinodes (and "mkfs" them) */ alloccgs(tomp[0], maxinum); for (i = 1; i < ntos; i++) { #ifdef GUI if (tomp[i]->active == DISK_NOT_ACTIVE) { continue; } #endif tomp[i]->m_ilist = tomp[0]->m_ilist; } /* do the real work */ _aio_init(aiothreads); initbuffers(bufblks); inodecount = writefs(frommp, maxinum, blkcount, vflag ? stdout : 0); flushcurrent(); /* recompute superblock free counts and write it out */ fs = tomp[0]->m_fs; fs->fs_tfree = CAPACITYBLKS(fs) - flushblks; fs->fs_tinode = CAPACITYINODES(fs) - inodecount; fs->fs_checksum = efs_checksum(tomp[0]->m_fs); /* * Tell the new fsck about the last (highest number) inode. * WARNING! Not doing this means fsck will make complaints * like "ENTRY INUM OUTRANGE IN DIR BLK" which will cause "fsck -y" * to nuke the file from the file system. */ fs->fs_lastialloc = maxinum; efs_aio_writeb(asuper, (char *)fs, BTOBB(EFS_SUPERBOFF), 1); /* write out the bitmap (computed in dalloc()) */ efs_aio_writeb(abit, tomp[0]->m_bitmap, tomp[0]->m_fs->fs_bmblock ? tomp[0]->m_fs->fs_bmblock : EFS_BITMAPBB, BTOBB(tomp[0]->m_fs->fs_bmsize)); /* write out cg's */ aio_writecgs(tomp[0], maxinum); efs_aio_wait(asuper); efs_aio_wait(abit); if (vflag) { printf("block delta %d\n", indirfewer); } exit(0); } /* * (blkcount is for the progress bar and might differ from the count -- * fewer indirect blocks may get allocated in the dest file system) */ int writefs(EFS_MOUNT *frommp, efs_ino_t maxinum, int blkcount, FILE *progress) { efs_ino_t inum, i; struct efs_dinode *dp; int inodecount = 0; int progressblks = 0, nextval = 10; if (progress) { fprintf(progress, "writing %d blocks..", blkcount); fflush(progress); } for (inum = 2, i = 0; inum <= maxinum; inum++, i++) { dp = INUMTODI(frommp, inum); if (dp->di_mode == 0) continue; inodecount++; progressblks += writeinode(frommp, dp, inum); /* XXX files larger than 10% will mess this up, oh well */ if (progress && progressblks && blkcount / progressblks < 10) { fprintf(progress, "%d%%..", nextval); fflush(progress); progressblks = 0; nextval += 10; } } if (progress) { fprintf(progress, "done\n"); fflush(progress); } return inodecount; } /* * In order to save recomputing dirblks and allocating inodes we do something * slightly cheesy... (we use the same inode numbers and copy the dirblks * right over). After not looking at this code for a few months I've decided * that preserving inode numbers is not horribly cheesy after all... * it's a _really_ nice debug feature! */ int writeinode(EFS_MOUNT *mp, struct efs_dinode *dp, efs_ino_t inum) { struct efs_dinode *todp = INUMTODI(tomp[0], inum); extent *fromex, *toex; int nblocks; if (todp->di_mode != 0) { fprintf(stderr, "%s: writeinode todp->di_mode non-zero inum=%d\n", PROGNAME, inum); exit(1); } bcopy(dp, todp, sizeof(struct efs_dinode)); switch (dp->di_mode & IFMT) { case IFREG: if (dp->di_size == 0) break; if (dp->di_numextents > EFS_DIRECTEXTENTS) fromex = DIDATA(dp, didata *)->ex; else fromex = dp->di_u.di_extents; nblocks = DATABLKS(fromex, dp->di_numextents); toex = mkboth(mp, dp, todp, nblocks, inum); /* read/write file data */ excopy(mp, fromex, toex, nblocks); break; case IFDIR: nblocks = DIDATA(dp, didata *)->dirblocks; toex = mkboth(mp, dp, todp, nblocks, inum); exdatacopy(DIDATA(dp, didata *)->dirblkp, tomp[0]->m_fd, toex, nblocks); break; case IFLNK: /* (how long can links get anyways?) */ fromex = dp->di_u.di_extents; nblocks = DATABLKS(fromex, dp->di_numextents); toex = mkboth(mp, dp, todp, nblocks, inum); /* read/write link data */ excopy(mp, fromex, toex, nblocks); break; case IFIFO: case IFCHR: case IFBLK: case IFSOCK: nblocks = 0; break; default: fprintf(stderr,"%s: writeinode unknown mode?!? 0x%x inum=%d\n", PROGNAME, dp->di_mode & IFMT, inum); exit(1); } /* if extents indirect then direct extents were malloc'ed in mkboth */ if (todp->di_numextents > EFS_DIRECTEXTENTS) free(toex); /* (else extents are direct an in the efs_dinode) */ return nblocks; } /* * make both: 1) the efs_dinode (in todp) and 2) the extents */ extent * mkboth(EFS_MOUNT *mp, struct efs_dinode *fromdp, struct efs_dinode *todp, int nblocks, efs_ino_t inum) { int nb, nfound, offset; efs_daddr_t bn; extent *toex, *lastex; int indirblks; nb = nblocks; /* * Create extents while allocating data blocks. * Start by assuming the file won't go indirect. */ offset = 0; toex = todp->di_u.di_extents; lastex = &todp->di_u.di_extents[EFS_DIRECTEXTENTS]; todp->di_numextents = 0; /* (todp a copy of fromdp) */ while (nblocks) { int newnumex, oldnumex = todp->di_numextents; /* allocate data blocks */ bn = dalloc(tomp[0], nblocks, &nfound); newnumex = nfound/EFS_MAXEXTENTLEN + (nfound % EFS_MAXEXTENTLEN ? 1 : 0) + oldnumex; if (toex + newnumex > lastex) { /* * Allocate space for indir extents by disk blocks. */ #define EXBYTES(n) ((n) * sizeof(extent)) #define EXBLKS(n) (EXBYTES(n)/BBSIZE + (EXBYTES(n) % BBSIZE ? 1 : 0)) if (oldnumex <= EFS_DIRECTEXTENTS) { /* just now going indir */ extent *tmpex; indirblks = EXBLKS(newnumex); tmpex = (extent *)calloc(indirblks, BBSIZE); if (tmpex == 0) { fprintf(stderr, "%s: out of mem!\n", PROGNAME); exit(1); } bcopy(toex, tmpex, oldnumex * sizeof(extent)); toex = tmpex; } else { int newindirblks = EXBLKS(newnumex); toex = (extent *) realloc(toex, BBSIZE*newindirblks); if (toex == 0) { fprintf(stderr, "%s: out of mem!\n", PROGNAME); exit(1); } /* zero the new blks */ bzero((char *)toex + BBSIZE * indirblks, BBSIZE*(newindirblks - indirblks)); indirblks = newindirblks; } lastex = (extent *) ((char *)toex + BBSIZE * indirblks - 1); } /* * Add the extents to describe bn+nfound at offset. */ addextents(toex + oldnumex, offset, bn, nfound, EFS_MAXEXTENTLEN); todp->di_numextents = newnumex; offset += nfound; nblocks -= nfound; } /* (the indirect extents show up after the file... oh well) */ if (todp->di_numextents > EFS_DIRECTEXTENTS) { int numindirex = 0, offset = 0; /* save a pointer to the extents */ extent *ex = toex, *iex = todp->di_u.di_extents; /* figure number of indir extent blocks */ int nb, nblocks; nb = nblocks = indirblks; /* allocate and write out blocks for indirect extents */ while (nblocks) { int bn = dalloc(tomp[0], nblocks, &nfound); int newex = nfound/EFS_MAXINDIRBBS + (nfound % EFS_MAXINDIRBBS ? 1 : 0); /* we do the offsets below */ addextents(iex + numindirex, 0, bn, nfound, EFS_MAXINDIRBBS); numindirex += newex; nblocks -= nfound; } /* indirect extent ex_offsets are different.. */ indiroffsets(iex, numindirex, (char *)ex); /* write the extents into the indirect blocks */ exdatacopy((char *)ex, tomp[0]->m_fd, iex, nb); } /* * Record the decrease in blocks used for indirect extents */ #define _INDIRBLKS(n) (n <= EFS_DIRECTEXTENTS ? 0 : EXBLKS(n)) indirfewer += _INDIRBLKS(fromdp->di_numextents) - _INDIRBLKS(todp->di_numextents); return toex; } /* * Decompose bn+len into maximally sized extents at offset. * (assumes ex points to enough space) */ void addextents(extent *ex, int offset, efs_daddr_t bn, int len, int maxexlen) { #ifdef NDEBUG if (offset != 0) { /* there are previous extents */ extent *exprev = ex - 1; assert(exprev->ex_offset + exprev->ex_length == offset); } #endif while (len) { int thislen = MIN(len, maxexlen); ex->ex_offset = offset; ex->ex_bn = bn; ex->ex_length = thislen; offset += thislen; bn += thislen; len -= thislen; ex++; } } /* * In the indirect extents the 0th ex_offset is the number of indirect * extents. Each following ex_offset is file data offset (not direct * extent offset) -- same as the ex_offset of the first extent of that * set of direct extents. */ void indiroffsets(extent *iex, int ni, char *ex) { extent *end = iex + ni; int nblocks; iex->ex_offset = ni; nblocks = iex->ex_length; iex++; for (; iex < end; iex++) { iex->ex_offset = ((extent *)(ex + nblocks * BBSIZE))->ex_offset; nblocks = iex->ex_length; } } /* (nblocks > 0) */ void excopy(EFS_MOUNT *frommp, extent *fromex, extent *toex, int nblocks) { efs_daddr_t frombn, tobn; int fromlen, tolen, copylen; tobn = toex->ex_bn; tolen = toex->ex_length; frombn = fromex->ex_bn; fromlen = fromex->ex_length; for (;;) { copylen = MIN(fromlen, tolen); copy(frommp->m_fd, frombn, tomp[0]->m_fd, tobn, copylen); if ((nblocks -= copylen) == 0) { /* end loop */ return; } 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 */ } /* * write-buffered copy * * precopy(), postcopy(), flushcopy() know about the buffering "internals" * copy(), exdatacopy(), datacopy() don't */ #define NBUFS 2 static char *buf[NBUFS]; static char *bufp, *readp; static efs_daddr_t wbn; static int readbuf; #define POSTCOPY(nblocks) (readp += (nblocks) * BBSIZE) struct aiocb ap[NBUFS][TOMAX]; struct sigaction sa; void initbuffers(int blks) { buf[0] = malloc(blks * BBSIZE); buf[1] = malloc(blks * BBSIZE); if (buf[0] == 0 || buf[1] == 0) { fprintf(stderr, "%s: failed to malloc %d blks of buffering", PROGNAME, blks); exit(1); } } char * precopy(efs_daddr_t tobn, int nb) { if (readp == 0) { /* instead of some external init routine */ bufp = buf[0]; readp = bufp; wbn = tobn; } assert(tobn != 0); assert(nb != 0); #define BLKSINBUF ((readp - bufp)/BBSIZE) if (wbn + BLKSINBUF != tobn || BLKSINBUF + nb > bufblks) { /* buffer's full... flush it and switch to new */ flushcurrent(); wbn = tobn; /* ...wait for writes to finish in the new buf... */ waitbuf(readbuf); /* ...and return pointing the reader here */ } return readp; } /* "external" interface to flush the last read(s) */ flushcurrent() { flushcopy(readbuf, wbn, readp - bufp); readbuf = readbuf == 0 ? 1 : 0; readp = bufp = buf[readbuf]; } void onwritedone() { /* * do all the work in the main line since we don't know which * write just completed */ } /* * Write nbytes of buf[whichbuf] to bn */ void flushcopy(int whichbuf, efs_daddr_t bn, int nbytes) { int i, done = 0; char stderr_msg[100]; if (ap[whichbuf][0].aio_fildes == 0) { /* first time init of aiocb's */ for (i = 0; i < ntos; i++) { #ifdef GUI if (tomp[i]->active == DISK_NOT_ACTIVE) { continue; } #endif ap[whichbuf][i].aio_reqprio = 0; ap[whichbuf][i].aio_buf = buf[whichbuf]; ap[whichbuf][i].aio_fildes = tomp[i]->m_fd; ap[whichbuf][i].aio_sigevent.sigev_signo = SIGUSR1; } sa.sa_handler = onwritedone; sa.sa_flags = SA_RESTART; if (sigaction(SIGUSR1, &sa, 0) == -1) { fprintf(stderr,"%s: sigaction failed errno=%d\n", PROGNAME, errno); exit(1); } } for (i = 0; i < ntos; i++) { #ifdef GUI if (tomp[i]->active == DISK_NOT_ACTIVE) continue; #endif ap[whichbuf][i].aio_nbytes = nbytes; ap[whichbuf][i].aio_offset = bn * BBSIZE; if (aio_write(&ap[whichbuf][i]) == -1) { #ifdef GUI sprintf(stderr_msg,"%s: aio_write failed errno=%d\n", PROGNAME, errno); process_diskunit_error(i, stderr_msg); #else fprintf(stderr,"%s: aio_write failed errno=%d\n", PROGNAME, errno); exit(1); #endif } } flushblks += nbytes/BBSIZE;/* gather stats to compute super block */ } /* * return when all aio_write's for whichbuf have completed */ waitbuf(int whichbuf) { int i; char stderr_msg[100]; int r, e; for (;;) { int done = 0; for (i = 0; i < ntos; i++) { #ifdef GUI if (tomp[i]->active == DISK_NOT_ACTIVE) continue; #endif e = aio_error(&ap[whichbuf][i]); if (e == EINPROGRESS) { done++; } else if (e != 0 || ap[whichbuf][i].aio_nbytes != (r = aio_return(&ap[whichbuf][i]))) { #ifdef GUI sprintf(stderr_msg, "%s: write failed on %s\n", PROGNAME, tomp[i]->m_devname); process_diskunit_error(i, stderr_msg); #else fprintf(stderr, "%s: write failed on %s\n", PROGNAME, tomp[i]->m_devname); exit(1); #endif } } if (done == 0) { return; } sginap(0); /* wait for a signal from async write */ } /* NOTREACHED */ } void copy(int fromfd, efs_daddr_t frombn, int tofd, efs_daddr_t tobn, int nb) { char *bp = precopy(tobn, nb); if (efs_readb(fromfd, bp, frombn, nb) != nb) { fprintf(stderr,"%s: copy() efs_readb failed!\n", PROGNAME); exit(1); } POSTCOPY(nb); } /* * yeah, yeah... this copies from "data" to the write buffer (as opposed to * somehow telling "flushcopy()" where to write from and not copying first), * but we assume this is used for dirblks, links and indirect extents which * is minimal data compared to file data (copyed by excopy/copy) */ void exdatacopy(char *data, int tofd, extent *toex, int nblocks) { efs_daddr_t bn; int length; while (nblocks) { bn = toex->ex_bn; length = toex->ex_length; datacopy(tofd, data, bn, length); data += (length * BBSIZE); toex++; nblocks -= length; } } void datacopy(int tofd, char *data, efs_daddr_t tobn, int nb) { char *bp = precopy(tobn, nb); bcopy(data, bp, nb * BBSIZE); POSTCOPY(nb); } /* * Allocate as many i-lists as needed to hold "maxinum". * "mkfs" each efs_dinode (all 0's except di_gen). */ void alloccgs(EFS_MOUNT *mp, efs_ino_t maxinum) { int i, ncg = EFS_ITOCG(mp->m_fs, maxinum) + 1; struct efs_dinode *di, *enddi; long igen; time(&igen); mp->m_ilist = (struct efs_dinode **) malloc(sizeof (struct efs_dinode *) * ncg); for (i = 0; i < ncg; i++) { mp->m_ilist[i] = (struct efs_dinode *) calloc(mp->m_fs->fs_cgisize, BBSIZE); enddi = (struct efs_dinode *) \ ((char*)mp->m_ilist[i] + mp->m_fs->fs_cgisize * BBSIZE); for (di = mp->m_ilist[i]; di < enddi; di++) { di->di_gen = igen; } } } struct aiocb acg[2][TOMAX]; void aio_writecgs(EFS_MOUNT *mp, efs_ino_t maxinum) { int i, ncg = EFS_ITOCG(mp->m_fs, maxinum) + 1; for (i = 0; i < ncg; i++) { efs_aio_writeb(acg[i & 0x1], (char *)mp->m_ilist[i], EFS_CGIMIN(mp->m_fs, i), mp->m_fs->fs_cgisize); if (i != 0) { efs_aio_wait(acg[(i - 1) & 0x1]); } } efs_aio_wait(acg[(ncg - 1) & 0x1]); } /* * dirt simple allocator -- a cg-aware counter */ efs_daddr_t dalloc(EFS_MOUNT *mp, int nblocks, int *nfound) { static int freebn, cgfree, cgno; int foundbn; if (freebn == 0) { /* happens once first time */ freebn = EFS_CGIMIN(mp->m_fs, 0) + mp->m_fs->fs_cgisize; cgfree = mp->m_fs->fs_cgfsize - mp->m_fs->fs_cgisize; } foundbn = freebn; if (nblocks > cgfree) { nblocks = cgfree; } *nfound = nblocks; cgfree -= nblocks; if (cgfree == 0) { cgno++; freebn = EFS_CGIMIN(mp->m_fs, cgno) + mp->m_fs->fs_cgisize; cgfree = mp->m_fs->fs_cgfsize - mp->m_fs->fs_cgisize; } else { freebn += nblocks; } markused(mp->m_bitmap, foundbn, nblocks); return foundbn; } markused(char *bm, efs_daddr_t bn, int nb) { for (;nb--;bn++) { bclr(bm, bn); } } /* * kick off an aio_write of buf to blk+nblks for each of nfd aiocb's */ efs_aio_writeb(struct aiocb *ap, char *buf, efs_daddr_t blk, int nblks) { char stderr_msg[100]; int i; ap, buf, blk, nblks; if (!sa.sa_handler) { sa.sa_handler = onwritedone; sa.sa_flags = SA_RESTART; if (sigaction(SIGUSR1, &sa, 0) == -1) { fprintf(stderr,"%s: sigaction failed errno=%d\n", PROGNAME, errno); exit(1); } } for (i = 0; i < ntos; i++) { #ifdef GUI if (tomp[i]->active == DISK_NOT_ACTIVE) continue; #endif ap[i].aio_reqprio = 0; ap[i].aio_buf = buf; ap[i].aio_fildes = tomp[i]->m_fd; ap[i].aio_sigevent.sigev_signo = SIGUSR1; ap[i].aio_nbytes = nblks * BBSIZE; ap[i].aio_offset = blk * BBSIZE; if (aio_write(&ap[i]) == -1) { #ifdef GUI sprintf(stderr_msg,"%s: aio_write failed errno=%d dev=%s\n", PROGNAME, errno, tomp[i]->m_devname); process_diskunit_error(i, stderr_msg); #else fprintf(stderr,"%s: aio_write failed errno=%d dev=%s\n", PROGNAME, errno, tomp[i]->m_devname); exit(1); #endif } } } /* * wait for all aio's to finish. exits on any error */ efs_aio_wait(struct aiocb *ap) { int i, e; char stderr_msg[100]; loop: for (i = 0; i < ntos; i++) { #ifdef GUI if (tomp[i]->active == DISK_NOT_ACTIVE) continue; #endif e = aio_error(&ap[i]); if (e == EINPROGRESS) { sginap(0); /* wait for a signal from async write */ goto loop; } else if (e != 0 || ap[i].aio_nbytes != aio_return(&ap[i])) { #ifdef GUI sprintf(stderr_msg, "%s: write failed on %s\n", PROGNAME, tomp[i]->m_devname); process_diskunit_error(i, stderr_msg); #else fprintf(stderr, "%s: write failed on %s\n", PROGNAME, tomp[i]->m_devname); exit(1); #endif } } /* everyone's done and successful, return */ } ifmountedfail(char *name) { struct ustat ustatarea; struct stat sb; if (stat(name, &sb) < 0) return (0); if (((sb.st_mode & S_IFMT) != S_IFCHR) && ((sb.st_mode & S_IFMT) != S_IFBLK)) return (0); if (ustat(sb.st_rdev,&ustatarea) >= 0) { fprintf(stderr, "%s: unmount %s before using %s\n", PROGNAME, name, PROGNAME); exit(1); } return (0); } /* * The disk units that have failed are set to NOT_ACTIVE. */ process_diskunit_error(i, msg) int i; char *msg; { char fail_msg[200], file_name[20], name[20], *d_ptr, *e_ptr; #ifdef GUI tomp[i]->active = DISK_NOT_ACTIVE; if (i != -1) { d_ptr = strstr(tomp[i]->tofile, "dks"); e_ptr = strstr(d_ptr, "s0"); *e_ptr = '\0'; } strcpy (name, d_ptr); sprintf (file_name, "/tmp/%s.err", name); append_tmp_file(file_name, msg); #endif } #ifdef GUI append_tmp_file(filename, write_line) char *filename; char *write_line; { FILE *fp; struct stat file_info; int stat; long bytes_wrote; int line_size; fp = fopen(filename, "w"); if (fp == NULL){ printf ("append file (%s) not found\n", filename); return; } line_size = strlen(write_line); bytes_wrote = fwrite(write_line, 1, line_size, fp); fclose(fp); } #endif