/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ static char Sccsid[] = "@(#)dcopy.c 3.1 8/17/87"; /* * dcopy -- copy file systems for optimal access times. * * Removed 3b processor and RT dependencies. * Added -n flag; do not ask before copying, default is to ask */ /* Based on: (SYSTEM V) dcopy.c 1.3 */ #include <stdio.h> #include <sys/param.h> #include <signal.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/filsys.h> #include <sys/devmaj.h> #include <sys/mount.h> #include <a.out.h> #include <sys/dir.h> #include <sys/ino.h> #include <sys/inode.h> #include <sys/fblk.h> /* these 3 are from <fcntl.h> */ #define O_RDONLY 0 #define O_RDWR 2 #define O_CREAT 00400 #define MAXCYL 714 /* Maximum # of blocks on a cylinder */ /* RP06 is this big */ #define STEPSIZE 9 /* Default interleaving number */ #define CYLSIZE 500 /* Default # of blocks on a cylinder */ #define OLD 7 /* Default for -a */ #define VERYOLD 127 /* Too old to care to sort in directory */ #define NAMSIZ 128 /* length of device names */ #define MAXDIR 10 /* Biggest direct, #blocks */ #define NIBLOCKS 30 /* Max. inode blocks read at once */ #define MIBLOCKS 3 /* Min. inode blocks to be read */ #define SECPDAY 1440 /* seconds per earth day */ #define DIRPBLK (BSIZE/sizeof (struct direct)) /* Dir entries/block */ #define NBUFIN 3 /* Number in indirect block buffers */ #define GOOD 2 /* Not NULL or EOF */ #define B_WRITE 1 /* For alloc */ #define B_READ 0 /* ditto */ #define FAIL -1 #define howmany(a,b) (((a)+((b)-1))/(b)) #define roundup(a,b) (howmany(a,b)*(b)) #define min(a,b) ((a) < (b) ? (a) : (b)) #define super0 _super0.b_sup #define super1 _super1.b_sup #define oldfs1 _oldfs1.b_sup /* * Smalli is the information contained in the inodes * that needs to be remembered between the time the * files are copies and the directory entries are updated. */ struct smalli { ino_t index; }; /* * Dblock is a union for several of the forms a disk block may take. */ union dblock { struct direct b_dir[DIRPBLK]; char b_file[BSIZE]; struct fblk b_free; daddr_t b_indir[NINDIR]; struct filsys b_sup; }; /* * Inbuf is the internal in-core copies of new indirect blocks * along with some other information to make the buffering work. */ struct inbuf { daddr_t i_bn; int i_indlev; daddr_t i_indir[NINDIR]; }; /* * All is the information needed to allocate disk blocks (in alloc()) * All[0] contains the information for the young files (or all files * if no -a is in effect) and all[1] has the info for old files. * Blocks for young files are allocated from the begining of the file * system and old files start on the last cylinder and work their way in. */ struct all { daddr_t a_baseblock; /* First block of current cylinder */ int a_pblk; /* Position in multiblock block (-c) */ int a_nalloc; /* Counts blocks allocated in this cylinder */ char a_flg[MAXCYL]; /* Remembers which blocks we have given away */ int a_lastb; }; struct stat statb; #define NMNT 0 #define MNT 1 struct nlist nl[] = { { "_nmount" }, { "_mount" }, { "" }, }; int nmount; struct mount *mp; struct mount *mpp; char *coref = "/dev/mem"; FILE *Devtty; char xflg = 0; /* -x makes executable files contiguous; Not supported*/ char dflg = 1; /* -d sorts directory entries */ char fflg = 0; /* -f lets you specify file system and ilist size */ char nflg = 0; /* -n don't ask for confirmation prior to copying */ char aflg = 1; /* -a separates old files from new ones */ char zflg = 0; /* -z for debugging output */ char vflg = 0; /* -v is verbose */ char sflg = 0; /* cylsize and stepsize were specified on cmd line */ short bsize = 1; /* Number of physical blocks/logical block (for -c) */ int infs; /* Source file system file descriptor */ int outfs; /* Destination file system file descriptor */ int tempfd; /* temporary file for internal buffers */ int pass; /* What are we doing these days */ union dblock _super0; /* Old superblock */ union dblock _super1; /* New superblock */ union dblock _oldfs1; /* old output fs superblock */ short old; /* Files not accessed since the old days are put far away */ long oldtime; /* The actual date for above */ int cylsize; /* User specified cylinder size */ int stepsize; /* User specified interleaving size */ short pblock; /* Current physical block in log. block (0-bsize-1) */ ino_t inext = 1; /* Counter for new i-list */ ino_t incnt; /* Counter for old i-list */ ino_t icount; /* Total count of inodes */ daddr_t fmin; /* block number for first data block */ daddr_t iblkno; /* Block number of inodes just read */ daddr_t oblk = (daddr_t) 2; /* Block number of inodes to be written */ int niblks; /* nbufi/INOPB */ daddr_t rblock; /* Current logical block from file or directory */ daddr_t wblock; /* Logical block to be written to file or direc. */ ino_t inum; /* Index into block of inodes just read in */ ino_t onum; /* Index into block of inodes to be written out */ ino_t nbufi; /* Number of inodes read in at once */ char *dspc; /* Remembered ages (in days) */ daddr_t dspc_off; /* file offset pointer for time, only used with '-d' */ struct smalli *ispace;/* Inode information, space gotten from malloc() */ daddr_t ispace_off; /* offset into temporary file to ispace structure */ struct dinode node0; /* Source inode of current file */ struct dinode node1; /* Destination inode */ struct dinode *ibuf; /* Current bunch of inodes just read */ struct dinode out_node[INOPB];/* Current block of inodes to be written */ struct all all[2]; /* Alloc() information */ union dblock in; /* Block for file and directory data in */ union dblock out; /* Block for file and directory data out */ daddr_t addr0[NADDR]; /* Usable disk addresses from node0 */ daddr_t addr1[NADDR]; /* New disk addresses to be put on node1 */ struct inbuf inbuf[2][NBUFIN]; /* Indirect address buffers */ char *tempnm = "/tmp/dpyXXXXXX"; /* temporary file name */ int blk_size = BSIZE; /* blocksize of temp file */ daddr_t blk_alloc = 0; /* number of temp blocks allocated*/ daddr_t bmap(); daddr_t alloc(); daddr_t allspace(); daddr_t countfree(); char *getpwoff(); char *malloc(); daddr_t *getindir(); char *sbrk(); long ulimit(); long lseek(); int older(); int intr(); int intrq(); #ifdef DEBUG /* * For Debugging alloc. */ #define PLACE(b) map[((short) b)>>3] |= (1<<(((short) b) & 7)) #define ISTHERE(b) (map[((short) b)>>3] & (1 <<(((short) b) & 7))) char map[1000]; #endif main(argc, argv) char **argv; { long time(), atol(); long t; int iage, j, mem; daddr_t d; register i; register struct smalli *sip; daddr_t sip_off; char *bp; char **args; char name1[NAMSIZ]; char name2[NAMSIZ]; sync(); super1.s_fsize = super1.s_isize = 0; args = argv + 1; while(--argc > 0 && **++argv == '-') { switch(*++*argv) { case 's': /* supply device information */ sflg++; stype(++*argv); break; case 'a': /* alter file postion based on access times */ aflg = 1; old = *++*argv ? atoi(*argv) : 0; if(old == 0) aflg = 0; /* no movement */ break; case 'd': /* leave directory order as is */ dflg = 0; break; case 'n': /* no confirmation prompt before copying */ nflg++; break; case 'f': /* supply filesystem and inode size (blocks) */ fflg++; super1.s_fsize = atol(++*argv); while(**argv != '\0' && *(*argv)++ != ':') ; super1.s_isize = atoi(*argv) + 2; break; case 'z': /* turn on DEBUG output */ zflg++; /* can be multiply-specified for heavy duty */ break; case 'v': /* verbose mode */ vflg++; break; default: err("Unknown arg (-%c)", **argv); } } if(argc != 2) err("Usage: dcopy [ options ] source-fs dest-fs"); t = time(0); strcpy(name1,argv[0]); strcpy(name2,argv[1]); if(aflg) oldtime = t - old * SECPDAY; if((infs = open(argv[0], O_RDONLY)) < 0) err("Can't open input fs %s", argv[0]); if((outfs = open(argv[1], O_RDWR)) < 0) err("Can't open output fs %s", argv[1]); if(stat(argv[0], &statb) < 0) err("cannot stat %s", argv[0]); i = (statb.st_rdev >> 8) & 0377; if(i < RK_RMAJ) /* input should be raw */ err("input fs (%s) not RAW device!", argv[0]); if(stat(argv[1], &statb) < 0) err("cannot stat %s", argv[1]); i = (statb.st_rdev >> 8) & 0377; if(!(i < RK_RMAJ)) /* output should be block */ err("output fs (%s) not BLOCK device!", argv[1]); nlist("/unix", nl); if((nl[NMNT].n_value==0) || (nl[MNT].n_value==0)) /* output mounted? */ err("cannot access /unix namelist"); mem = open(coref, 0); if(mem < 0 ) err("cannot open %s", coref); lseek(mem, (long)nl[NMNT].n_value, 0); read(mem, (char *)&nmount, sizeof(nmount)); mp = malloc(sizeof(struct mount) * nmount); lseek(mem, (long)nl[MNT].n_value, 0); read(mem, (char *)mp, sizeof(struct mount) * nmount); mpp = mp; for(i=0; i<nmount; i++, mpp++) { j = (statb.st_rdev >> 8) & 0377; j <<= 8; j |= (statb.st_rdev & 0377); if((mpp->m_bufp == 0) || (mpp->m_dev != j)) continue; err("SORRY - output file system is mounted!"); } args[1] = 0; /* * Copy bootstrap (block 0) and read input fs super block. */ getblk(&super0, (daddr_t) 0); putblk(&super0, (daddr_t) 0); getblk(&super0, (daddr_t) 1); getoblk(&oldfs1, (daddr_t) 1); /* get output fs super block */ if (zflg > 1) { fprintf(stderr,"\n\tBlocks on input: %D\t",super0.s_fsize); fprintf(stderr,"Blocks on output: %D\n",oldfs1.s_fsize); fprintf(stderr,"\tInput inodes: %u\t",super0.s_isize); fprintf(stderr,"Output inodes: %u\n",oldfs1.s_isize); fprintf(stderr,"\tFree blocks in: %D\t",super0.s_tfree); fprintf(stderr,"Free blocks out: %D\n",oldfs1.s_tfree); fprintf(stderr,"\tFree inodes in: %d\t",super0.s_tinode); fprintf(stderr,"Free inodes out: %d\n",oldfs1.s_tinode); fprintf(stderr,"\tIn: m/n %d/%d\t",super0.s_m, super0.s_n); fprintf(stderr,"Out: m/n %d/%d\n",oldfs1.s_m, oldfs1.s_n); fprintf(stderr,"\tIn [name/vol]: [%.6s %.6s]\n",super0.s_fname, super0.s_fpack); fprintf(stderr,"\tOut [name/vol]: [%.6s %.6s]\n",oldfs1.s_fname, oldfs1.s_fpack); } /* * Assign and check new file system sizes. * The -f option will override the current sizes on * the output fs, unless those values given are unreasonable, * in which case it just uses the output fs values. */ if (fflg && ((super1.s_fsize <= 0) || (super1.s_isize <= 2))) { printf("Bad file system sizes (-f)\n"); printf("Using output file system sizes...\n"); printf("output isize: %u\n",oldfs1.s_isize); printf("output fsize: %D\n",oldfs1.s_fsize); super1.s_isize = oldfs1.s_isize; super1.s_fsize = oldfs1.s_fsize; fflg=0; } if (super1.s_fsize <= 0) { if (oldfs1.s_fsize <= 0) { fprintf(stderr,"output fs size is zero!\n"); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } else /* use size that's on output fs */ super1.s_fsize = oldfs1.s_fsize; } if (super1.s_isize <= 2) { if (oldfs1.s_isize <= 2) { fprintf(stderr,"Bad inode size (%u) on output fs!\n",oldfs1.s_isize); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } else /* use isize that's on output fs */ super1.s_isize = oldfs1.s_isize; } /* check if stuff will fit... */ if((super1.s_isize < counti() + 2 || super0.s_fsize - (super0.s_isize + countfree()) > super1.s_fsize - super1.s_isize)) { if (fflg) { printf("Bad file system sizes (-f)\n"); super1.s_isize = oldfs1.s_isize; super1.s_fsize = oldfs1.s_fsize; printf("Using output file system sizes...\n"); printf("output isize: %u\n",super1.s_isize); printf("output fsize: %D\n",super1.s_fsize); fflg=0; } if(!fflg && ((super1.s_isize < counti() + 2 || super0.s_fsize - (super0.s_isize + countfree()) > super1.s_fsize - super1.s_isize))) { fprintf(stderr,"Bad output file system sizes.\n"); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } } /* * Check if output fs is large enough to hold the -f * sizes given. Note: if we didn't specify -f, we are * already using output sizes, so no warning gets done. */ if(super1.s_isize > oldfs1.s_isize) { printf("Warning: requested isize: %u\n",super1.s_isize); printf(" current isize: %u\n",oldfs1.s_isize); printf("There may not be enough space on the output fs!\n"); } if(super1.s_fsize > oldfs1.s_fsize) { printf("Warning: requested fsize: %D\n",super1.s_fsize); printf(" current fsize: %D\n",oldfs1.s_fsize); printf("There may not be enough space on the output fs!\n"); } /* * Compute interleaving and remember it in new superblock. */ if (cylsize <= 0) { /* bad -s arg */ if (oldfs1.s_n <= 0) { fprintf(stderr,"output cylinder size is zero!\n"); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } else { if (sflg) fprintf(stderr,"output cylsize: %d\n",oldfs1.s_n); cylsize = oldfs1.s_n; } } if (stepsize <= 0) { /* possible bad -s arg */ if (oldfs1.s_m <= 0) { fprintf(stderr,"output stepsize is zero!\n"); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } else { if (sflg) fprintf(stderr,"output stepsize: %d\n",oldfs1.s_m); stepsize = oldfs1.s_m; } } if (stepsize > cylsize) { fprintf(stderr, "Error: output stepsize (%d) is greater than cylinder size (%d)!\n",stepsize, cylsize); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } if (stepsize <= 0) { fprintf(stderr,"Error: output stepsize is zero!\n"); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } if (cylsize <= 0) { fprintf(stderr,"Error: output cylinder size is zero!\n"); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } if (cylsize > MAXCYL) { fprintf(stderr, "Error: cylinder size (%d) is greater than allowed maximum (%d)!\n",cylsize, MAXCYL); fprintf(stderr,"See mkfs(1) for help with contructing a new output filesystem.\n"); err("Exit."); } if (vflg) { printf("\nold filesize = %D, old inode size = %u\n", super0.s_fsize,super0.s_isize); printf("old stepsize = %d, old cylinder size = %d\n", super0.s_m,super0.s_n); } super1.s_m = stepsize; super1.s_n = cylsize; /* * Set up block allocation and get mem for inode information. */ fmin = (daddr_t)super1.s_isize; all[0].a_baseblock = roundup(fmin, cylsize) - cylsize; all[0].a_nalloc = fmin % cylsize; all[0].a_pblk = all[1].a_pblk = 1; d = roundup(super1.s_fsize, cylsize); all[1].a_baseblock = d - cylsize; all[1].a_nalloc = d - super1.s_fsize; if(vflg) { printf("new filesize = %D, new inode size = %u\n", super1.s_fsize,super1.s_isize); printf("new stepsize = %d, new cylinder size = %d\n", super1.s_m, super1.s_n); } printf("\nFrom: %s To: %s\n",name1,name2); if (!nflg) { if((Devtty = fopen("/dev/tty", "r")) < 0) { fprintf(stderr,"Cannot open terminal!\n"); exit(1); } signal(SIGINT, intrq); /* if ^C, want to remove tmp files */ do { printf("Ready to copy file systems? <y or n>: "); } while( ask() > 0); } d = (daddr_t)(super0.s_isize - 2) * INOPB * sizeof(struct smalli); ispace_off = sip_off = allspace(d); if(dflg) { dspc_off = allspace(d/2); } /* * Get mem for inodes */ getimem(); inum = nbufi; iblkno = 2 - niblks; /* * Copy files */ icount = (super0.s_isize - 2) * INOPB; pass = 1; signal(SIGQUIT, intr); signal(SIGINT, intr); printf("Pass 1: Reorganizing file system\n"); for(incnt = 1; incnt <= icount; incnt++) { if((sip = (struct smalli *)getpwoff(sip_off)) == NULL) { err("couldn't get sip pointer\n"); } geti(); if(node0.di_mode & IFMT) { copyi(); sprintf(*args, "On i-num %d of %u", incnt, icount); sip->index = inext++; } else sip->index = 0; if(dflg) { iage = (t - node0.di_atime) / SECPDAY; if((bp = getpwoff(dspc_off + incnt)) == NULL) { err("couldn't get directory time pointer"); } if( (node0.di_mode & IFMT) == IFDIR ) *bp = 0; else *bp = min(iage, VERYOLD); } sip_off += sizeof(struct smalli); } flushi(); /* * Now make the source filesystem the destination filesystem * so getblk() will get blocks from there, which will have * already compressed the directories. */ infs = outfs; /* * Reinitialize inode pointers. */ inum = nbufi; iblkno = 2 - niblks; onum = (ino_t) 0; oblk = (daddr_t) 2; /* * Fix i-numbers in directory entries, with deletion of * zero entries and sorting, if desired. */ pass = 2; printf("Pass 2: Fixing inums in directories\n"); for(incnt = 1; incnt < inext; incnt++) { geti(); if((node0.di_mode & IFMT) == IFDIR) reldir(); } /* * Make a new i-list and freelist */ pass = 3; printf("Pass 3: Remake freelist\n"); icount = (super1.s_isize - 2) * INOPB; i = inext; while(i <= icount && super1.s_ninode < NICINOD) super1.s_inode[super1.s_ninode++] = i++; freelist(); /* * Copy superblock and sync to make sure it all gets done. */ putblk(&super1, (daddr_t) 1); sync(); if(vflg) { printf("Files: %d\n", --inext); printf("Free blocks in: %D\nFree blocks out: %D\n", super0.s_tfree, super1.s_tfree); } printf("Complete\n"); #ifdef DEBUG if(zflg > 2) for(d=0; d<super1.s_fsize;d++) if(!ISTHERE(d)) fprintf(stderr, "%lu MISSING\n", d); #endif close(tempnm); unlink(tempnm); exit(0); } /* * Stype processes the user's -s argument. * (from fsck.c) */ stype(p) register char *p; { if(*p == 0) return; if (*(p+1) == 0) { if (*p == '3') { cylsize = 200; stepsize = 5; return; } if (*p == '4') { cylsize = 418; stepsize = 9; return; } } cylsize = atoi(p); while(*p && *p != ':') p++; if(*p) p++; stepsize = atoi(p); if(stepsize <= 0 || stepsize > cylsize || cylsize <= 0 || cylsize > MAXCYL) { fprintf(stderr, "Invalid -s argument;\n"); fprintf(stderr, "Using output fs cylsize and stepsize...\n"); cylsize = stepsize = 0; } } /* * Count inodes */ counti() { register i, j, n; struct dinode in_node[INOPB]; n = 0; for(i = 2; i < super0.s_isize; i++) { getblk(in_node, (daddr_t) i); for(j = 0; j < INOPB; j++) if(in_node[j].di_mode & IFMT) n++; } return((n + INOPB - 1) / INOPB); } /* * Countfree counts the free blocks (obviously). */ daddr_t countfree() { daddr_t n, m; n = super0.s_nfree; if(n) { m = super0.s_free[0]; while(m) { getblk(&in.b_free, (daddr_t) m); n += in.b_free.df_nfree; m = in.b_free.df_free[0]; } } return(n); } /* * Geti sets the external structure node0 with the disk inode information * for the inum'th inode in chunk iblkno. The i-list is read in a block * at a time and inum is incremented with each call. */ geti() { if(inum >= nbufi) { lseek(infs, (long) BSIZE * (iblkno += niblks), 0); if(zflg) fprintf(stderr, "Reading block %ld of inodes\n", iblkno); if(read(infs, ibuf, niblks * BSIZE) != niblks * BSIZE) err("Inode read error: block: %ld, size %d\n", iblkno, niblks * BSIZE); inum = 0; } node0 = ibuf[inum++]; } /* * Puti saves the current inode (node1) in the current block of * inodes to be written out. If the block is full, it writes it * out. */ puti() { if(onum >= INOPB) { putblk(out_node, oblk++); onum = 0; } out_node[onum++] = node1; } /* * Flushi writes out the last block of inodes, and zeros the rest of the * ilist. */ flushi() { register i; i = onum * sizeof(struct dinode); clear(out_node + onum, BSIZE - i); putblk(out_node, oblk); clear(out_node, BSIZE); while(++oblk < super1.s_isize) putblk(out_node, oblk); } /* * Copyi copies the inode (with associated blocks, if it is a file) * which is stored in node0. */ copyi() { int type; node1 = node0; switch(type = (node1.di_mode & IFMT)) { case IFDIR: case IFREG: fixaddr(); type == IFREG ? copyfile() : copydir(); flushaddr(); puti(); return; case IFLNK: fixaddr(); copyfile(); flushaddr(); puti(); return; default: fprintf(stderr, "I=%d Unexpected mode (%o)\n", inext, node1.di_mode); /* fall into ... */ case IFCHR: case IFBLK: case IFIFO: puti(); return; } } /* * Copy and compress directory files */ copydir() { register struct direct *odp, *idp; register int nent; int dirent, r; dirent = 0; odp = out.b_dir; nent = node0.di_size/sizeof(struct direct); while((r = r_block(in.b_dir)) != EOF) { if(r == NULL) continue; for( idp = in.b_dir; (idp < &in.b_dir[DIRPBLK]) && (nent-->0); idp++ ) if(idp->d_ino) { dirent++; *odp++ = *idp; if(odp >= &out.b_dir[DIRPBLK]) { w_block(out.b_dir); odp = out.b_dir; } } } if(odp != out.b_dir) { clear(odp, ((char *) &out.b_dir[DIRPBLK]) - ((char *) odp)); w_block(out.b_dir); } node1.di_size = dirent * sizeof(struct direct); } /* * Copyfile reads in the current file and writes it back out, * taking care not to write null blocks out. */ copyfile() { register r; while((r = r_block(in.b_file)) != EOF) if(r == NULL) wblock++; /* Bmap does the dirty work */ else w_block(in.b_file); } /* * Alloc allocates disk blocks according to the user specified * options. The file mode (for -x) is in node0.di_mode. The * access time (for -a) is in node0.di_atime. Depending on * whether -a is in effect and the file is old, we choose all[0] * or all[1] to provide the information about the current cylinder. * The array a_flg[] keeps track of the disk blocks we have already * allocated in this cylinder. A_pblock keeps track of which * physical block we are in in the current logical block and * has a range from zero to bsize-1. A_nalloc counts how many blocks * already given away in this cylinder. */ daddr_t alloc() { register i; register struct all *ap; static overlap, ovw; int which; daddr_t blk; which = overlap ? ovw : (aflg && (node0.di_atime <= oldtime) && ((node0.di_mode &IFMT) != IFDIR) ); ap = all + which; if(ap->a_nalloc++ >= cylsize) /* New cylinder needed */ do { /* * If we already overlapped, we are now out of space. */ if(overlap) return((daddr_t) 0); clear(ap->a_flg, sizeof(ap->a_flg)); i = 0; ap->a_pblk = 1; ap->a_nalloc = 0; ap->a_baseblock += which ? -cylsize : cylsize; /* * When freelist() gets into the cylinder of the old files, * or the young and old files have overlapped on the same * cylinder, we have to make sure we only free the blocks * not already allocated to a file. We do this by making * sure to use the all[] structure which came first. */ if(all[0].a_baseblock == all[1].a_baseblock) { overlap++; ovw = 1 - which; ap = &all[ovw]; } } while(ap->a_nalloc++ >= cylsize); else { if(ap->a_pblk++ < bsize || (xflg && (node0.di_mode & IEXEC))) i = ap->a_lastb + 1; else { ap->a_pblk = 1; i = ap->a_lastb + stepsize; } } i %= cylsize; do { while(ap->a_flg[i]) i = (i + 1) % cylsize; ap->a_flg[i]++; blk = ap->a_baseblock + i; ap->a_lastb = i; } while(blk < fmin || blk >= super1.s_fsize); if(zflg > 2 && blk%cylsize == 0) { fprintf(stderr, "blk. no. %lu\n", blk); for(i=0; i<2; i++) fprintf(stderr, "[%d]: b:%lu p:%u n:%u l:%u\n", i, all[i].a_baseblock, all[i].a_pblk, all[i].a_nalloc, all[i].a_lastb); } #ifdef DEBUG if(zflg > 2) { if(ISTHERE(blk)) fprintf(stderr, "DUP %lu\n", blk); PLACE(blk); printf("%lu ", blk); } #endif return(blk); } err(fmt, x1, x2, x3, x4) char *fmt; { fprintf(stderr, "dcopy: "); fprintf(stderr, fmt, x1, x2, x3, x4); fprintf(stderr, "\n"); exit(1); } clear(p, b) register char *p; register b; { while(b--) *p++ = 0; } /* * R_block reads in a block from the current file. */ r_block(b) char *b; { daddr_t bn; if((bn = bmap(rblock++, B_READ)) == (daddr_t) (-1)) return(rblock > howmany(node0.di_size, BSIZE) ? EOF : NULL); getblk(b, bn); return(GOOD); } /* * W_block writes out a block to the current file. */ w_block(b) char *b; { daddr_t bn; if((bn = bmap(wblock++, B_WRITE)) == (daddr_t) (-1)) return(EOF); putblk(b, bn); return(!EOF); } /* * Bmap defines the structure of file system storage * by returning the physical block number on a device given the * inode and the logical block number in a file. */ daddr_t bmap(bn, rwflg) daddr_t bn; { register daddr_t *dp; register i; register daddr_t *inp; int j, sh; daddr_t nb; dp = (rwflg == B_READ) ? addr0 : addr1; /* * blocks 0..NADDR-4 are direct blocks */ if(bn < NADDR-3) { i = bn; nb = dp[i]; if(nb == 0) { if(rwflg==B_READ || (nb = alloc())==NULL) return((daddr_t)-1); dp[i] = nb; } /* We should always be doing block writes at EOF in pass1 */ else if(rwflg != B_READ && pass == 1) err("Write not at EOF"); return(nb); } /* * addresses NADDR-3, NADDR-2, and NADDR-1 * have single, double, triple indirect blocks. * the first step is to determine * how many levels of indirection. */ sh = 0; nb = 1; bn -= NADDR-3; for(j=3; j>0; j--) { sh += NSHIFT; nb <<= NSHIFT; if(bn < nb) break; bn -= nb; } /* * Check for HUGE file. */ if(j == 0) return((daddr_t)-1); /* * fetch the address from the inode */ nb = dp[NADDR-j]; if(nb == 0) { if(rwflg==B_READ || (nb = alloc())==NULL) return((daddr_t)-1); dp[NADDR-j] = nb; } /* * fetch through the indirect blocks */ for(; j<=3; j++) { inp = getindir(nb, 4 - j, rwflg); sh -= NSHIFT; i = (bn>>sh) & NMASK; nb = inp[i]; if(nb == 0) { if(rwflg==B_READ || (nb = alloc())==NULL) return((daddr_t)-1); inp[i] = nb; } else if(j == 3 && rwflg != B_READ && pass == 1) err("Write not at EOF"); } return(nb); } /* * Getindir returns a pointer to a block of indirect addresses. * The first time getindir is called with a new bno, the list is * searched for free buffers, and if none are found, a single or double * indirect block is used (after writing it out). The found block is cleared * and a pointer to it is returned. Once an indirect block is written * out it will not be read in again from the destination file system. * Thus, NBUFIN has to be large enough to accomadate all indirect blocks * needed to access any block in the file (NBUFIN = 3 for triple indir- * ection) and since the blocks in the file are always accessed in order * and single indirect blocks are reused before double indirect blocks, * no more than 3 indirect blocks are ever needed. Thus making NBUFIN * bigger than 3 doesn't gain anything. * Lev is the level of indirection passed from bmap. */ daddr_t * getindir(bno, lev, rwflg) daddr_t bno; { register struct inbuf *ip; register levcnt; struct inbuf *instart, *inend; instart = inbuf[rwflg]; inend = instart + NBUFIN; for(levcnt = -1; levcnt <= 2; levcnt++) { for(ip = instart; ip < inend; ip++) if(levcnt < 0 && ip->i_bn == bno) /* A hit */ return(ip->i_indir); else if(levcnt == ip->i_indlev) { if(levcnt && rwflg == B_WRITE) putblk(ip->i_indir, ip->i_bn); ip->i_indlev = lev; ip->i_bn = bno; if(pass != 1 || rwflg == B_READ) getblk(ip->i_indir, bno); else clear(ip->i_indir, BSIZE); return(ip->i_indir); } } err("getindir: no freeable blocks"); /* cannot happen */ } /* * Getblock() reads block bno of the source f.s. * It could be made smarter */ getblk(p, bno) char *p; daddr_t bno; { if(zflg > 2) fprintf(stderr, "Reading block %lu\n", bno); if(lseek(infs, (long) bno * BSIZE, 0) == (long) -1) err("Can't seek to b.n. %lu on source", bno); if(read(infs, p, BSIZE) != BSIZE) err("Read error: block no %lu", bno); } /* * get super block from existing output fs */ getoblk(p, bno) char *p; daddr_t bno; { if(zflg > 2) fprintf(stderr, "Reading output fs super block (%lu)\n", bno); if(lseek(outfs, (long) bno * BSIZE, 0) == (long) -1) err("Can't seek to output fs super block (%lu)", bno); if(read(outfs, p, BSIZE) != BSIZE) err("Read error: output fs super block (%lu)", bno); } /* * Putblk() writes block bno of the source f.s. * it could buffer, but it dont. */ putblk(p, bno) char *p; daddr_t bno; { if(zflg > 2) fprintf(stderr, "Writing to %lu\n", bno); if(lseek(outfs, (long) bno * BSIZE, 0) == (long) -1) err("Can't seek to b.n. %lu on destination", bno); if(write(outfs, p, BSIZE) != BSIZE) err("Write error: block no. %lu", bno); } /* * Reldir() fixes up the i-numbers in directory files * and sorts entries, if -d option in effect. */ reldir() { register struct direct *dsp, *dend; char *mspace; register unsigned i; fixaddr(); i = roundup(node0.di_size, BSIZE); if((mspace = malloc(i)) == NULL) err("Can't get mem for directory"); dsp = (struct direct *) mspace; dend = dsp + node0.di_size/sizeof(struct direct); for(; dsp < dend; dsp += DIRPBLK) r_block(dsp); dsp = (struct direct *) mspace; if(dend - dsp < 2) fprintf(stderr, "Truncated directory I=%d\n", incnt); else if(dflg) qsort(dsp + 2, dend - dsp - 2, sizeof(struct direct), older); for(; dsp < dend; dsp++) { if((ispace = (struct smalli *)getpwoff(ispace_off + ((long)(dsp->d_ino -1) * sizeof(struct smalli)) )) == NULL) { err("Couldn't get ispace pointer\n"); } dsp->d_ino = ispace->index; } for(dsp = (struct direct *) mspace; dsp < dend; dsp += DIRPBLK) w_block(dsp); free(mspace); } /* * Older - called from qsort, returns + if the directory entry pointed * to by d1 is older than the directory entry pointed to by d2. * The ages were found out in the first pass and gotten by indexing * ispace, the smalli structure; */ older(d1, d2) struct direct *d1, *d2; { char *b1,*b2; int i; if((b1 = getpwoff(dspc_off + (d1->d_ino))) == NULL){ err("cannot get offset to time\n"); } i = *b1; if((b2 = getpwoff(dspc_off + (d2->d_ino))) == NULL){ err("cannot get offset to time\n"); } return(i - *b2); } /* * Fixaddr() sets up variables such that sucessive read and write calls * start from the beginning of the data for the current inode. */ fixaddr() { register struct inbuf *ip; l3tol(addr0, node0.di_addr, NADDR); rblock = wblock = (daddr_t) 0; pblock = 0; if(pass == 1) clear(addr1, sizeof(addr1)); else l3tol(addr1, node0.di_addr, NADDR); for(ip = inbuf[B_READ]; ip < &inbuf[B_READ][NBUFIN]; ip++) ip->i_indlev = 0; for(ip = inbuf[B_WRITE]; ip < &inbuf[B_WRITE][NBUFIN]; ip++) ip->i_indlev = 0; } /* * Flushaddr() is called to fix up the current inode, and write out any * buffered indirect blocks. */ flushaddr() { register struct inbuf *ip; for(ip = inbuf[B_WRITE]; ip < &inbuf[B_WRITE][NBUFIN]; ip++) if(ip->i_indlev) putblk(ip->i_indir, ip->i_bn); ltol3(node1.di_addr, addr1, NADDR); } /* * Freelist() creates the new free list and fills in the superblock. * It adds blocks to the freelist in the same order that the system * will ask for them; */ freelist() { register daddr_t *fp; register i; daddr_t frblk; frblk = (daddr_t) 0; super1.s_nfree = 0; super1.s_ninode = 0; super1.s_flock = 0; super1.s_ilock = 0; super1.s_fmod = 0; super1.s_ronly = 0; super1.s_tfree = 0; super1.s_tinode = icount - inext + 1; super1.s_time = super0.s_time; for(i = 0; i < 6; i++) super1.s_fname[i] = super0.s_fname[i]; for(i = 0; i < 6; i++) super1.s_fpack[i] = super0.s_fpack[i]; for(i = 0; i < 4; i++) super1.s_magic[i] = super0.s_magic[i]; node0.di_mode = IFREG; /* For alloc() */ aflg = 0; /* ditto */ i = 0; fp = &super1.s_free[NICFREE]; while(1) { if((*--fp = alloc()) != (daddr_t) 0) { i++; super1.s_tfree++; } if(i == NICFREE || *fp == (daddr_t) 0) { if(*fp == (daddr_t) 0) { register daddr_t *cfp; register k; cfp = (frblk == (daddr_t) 0) ? super1.s_free : in.b_free.df_free; for(k = i; k-- > 0;) *++cfp = *++fp; i++; } if(frblk) { in.b_free.df_nfree = i; putblk(&in.b_free, frblk); } else super1.s_nfree = i; if((frblk = *fp) == (daddr_t) 0) return; fp = &in.b_free.df_free[NICFREE]; clear(&in.b_free, BSIZE); i = 0; } } } intr(sig) { printf("Pass %d, inum %u of %u\n", pass, incnt, icount); if(sig == SIGQUIT) { printf("No longer catching interupts\n"); signal(SIGINT, intrq); /* quit next time */ return; } signal(SIGINT, intr); } intrq(sig) /* clean-up before leaving */ { close(tempnm); unlink(tempnm); exit(1); } getimem() { register unsigned size, avail; char *space; /* determine what space is available */ /* avail = (ulimit(3,0L) - (unsigned)sbrk(0))/BSIZE; if( avail > (MAXDIR+NIBLOCKS+4) ) size = MAXDIR+NIBLOCKS+4; else size = avail; */ size = MAXDIR+NIBLOCKS+4; /* try this as a default */ if (zflg > 1) fprintf(stderr,"[ Check: Initial size is: %d ]\n",size); while( (space = malloc(size*BSIZE)) == 0 ) size--; if (zflg > 1) fprintf(stderr,"[ Check: size after malloc is: %d ]\n",size); free(space); if( size < (MAXDIR+MIBLOCKS+4) ) { if (zflg > 1) fprintf(stderr, "[ Check: size(%d) MAXDIR+MIBLOCKS+4 ]\n",size); err("Can't get inode buffer space"); } size -= MAXDIR + 4; if( size > NIBLOCKS ) { if (zflg > 1) { fprintf(stderr,"[ Check: size(%d) > NIBLOCKS(%d) ]\n",size, NIBLOCKS); fprintf(stderr,"[ Check: Setting size=NIBLOCKS! ]\n"); } size = NIBLOCKS; } else if (zflg > 1) fprintf(stderr,"[ Check: size(%d) NOT > NIBLOCKS(%d), OK! ]\n",size,NIBLOCKS); if (size*BSIZE <=0) { if (zflg > 1) fprintf(stderr,"[ Check: malloc arg is <= 0 ]\n"); } if (zflg > 1) fprintf(stderr,"size before malloc is: %d\n",size); if((ibuf = (struct dinode *) malloc(size*BSIZE)) == NULL) { if (zflg > 1) fprintf(stderr,"[ Check: malloc problems ]\n"); err("Can't get inode buffer space"); } niblks = size; nbufi = niblks * INOPB; if (zflg) printf("Available mem %u, got %u for inodes (that's %u inodes)\n", avail*BSIZE, size*BSIZE, nbufi); } /* Allocate space in a file which can be accessed through * routine getpwoff. */ long allspace(size) long size; { extern long putblock(); extern int blk_size; extern daddr_t blk_alloc; extern long curr_blk[]; extern long cntr_blk; extern char *float_ptr[]; register i; daddr_t next_blk; daddr_t offset; daddr_t blk; char *bp; if( zflg > 2 ) { fprintf(stderr,"size requested = %ld\n",size); fprintf(stderr,"blocks allocated = %ld\n",blk_alloc); } if(blk_alloc == 0) { cntr_blk = FAIL; curr_blk[0] = 0; mktemp(tempnm); if((creat(tempnm, 0600)) < 0) { err("Cannot create temporary name %s",tempnm); } if((tempfd = open(tempnm,O_RDWR)) < 0) { err("Cannot open temporary name %s",tempnm); } if((float_ptr[0] = malloc(blk_size)) == NULL){ err("Cannot allocate space of paging block"); } if((float_ptr[1] = malloc(blk_size)) == NULL){ err("Cannot allocate space of paging block"); } } else { if(putblock(float_ptr[1],curr_blk[1],blk_size) != curr_blk[1]) { err("Cannot put blocks in temporary file"); } } bp = float_ptr[1]; for( i = 0; i < blk_size;i++){ *bp++ = NULL; } offset = blk_alloc * blk_size; blk = howmany(size,blk_size) + blk_alloc; for ( next_blk = blk_alloc; next_blk < blk; next_blk++) { if(putblock(float_ptr[1],next_blk,blk_size) != next_blk) { err("Cannot put blocks in temporary file"); } } curr_blk[1] = blk_alloc = blk; return(offset); } /* * This function takes a offset in the file and returns a pointer * to the offset. If successful a pointer is returned, if not * NULL is returned. */ char * getpwoff(offset) off_t offset; { extern int blk_size; extern char *getblock(); daddr_t blk; char *ptr; /* * Calculate block number */ blk = offset / blk_size; /* * Get the block */ if((ptr = getblock(blk)) == NULL) { return(NULL); } ptr += offset - (blk_size * blk); return(ptr); } /* * This routine allocates and keeps track of the queue pages. * If the page is unallocated the space for the page is allocated * and the page is read. Normally there are two pages in memory, * the control page, and the floating page. When a page is not in * memory and the block is in the floating page, the page is put * back on disk and the new page is brought into the floating * page. The control page once allocated and brought in to memory * doesn't leave memory. Once the block is allocated and brought * in a pointer to the block is passed to the calling routine. */ char *cntr_ptr = NULL; char *float_ptr[2] = { NULL,NULL }; long curr_blk[2]; long cntr_blk = 0; char * getblock(blk_no) long blk_no; { extern long putblock(); /* put a block on disk */ extern long lseek(); /* disk seek routine */ extern char *malloc(); /* memory allocation routine */ extern int blk_size; /* page size */ extern int errno; daddr_t offset; /* calculated offset in file */ char **ptr; /* pointer to buffer pointer */ long *bptr; /* pointer to block number */ long dist0; long dist1; int index; int pflag; /* put block flag */ int rflag; /* read block flag */ pflag = rflag = 0; if(blk_no == cntr_blk) { ptr = &cntr_ptr; bptr = &cntr_blk; } else { if(blk_no != curr_blk[0] && blk_no != curr_blk[1]) { pflag++; rflag++; } dist0 = (blk_no >= curr_blk[0] ? blk_no - curr_blk[0] : curr_blk[0] - blk_no); dist1 = (blk_no >= curr_blk[1] ? blk_no - curr_blk[1] : curr_blk[1] - blk_no); index = (dist0 > dist1 ? 1 : 0); ptr = &float_ptr[index]; bptr = &curr_blk[index]; } if(*ptr == NULL) { if(( *ptr = malloc((unsigned)blk_size) ) == NULL) return(NULL); pflag = 0; } if(pflag) { if(*bptr != -1) { if(putblock(*ptr,*bptr,blk_size) != *bptr) { return(NULL); } } } if(rflag) { offset = blk_size * blk_no; if( zflg > 2) { fprintf(stderr, "getblock: offset = %ld size = %d, blk_no = %ld\n", offset, blk_size, blk_no); } if(( lseek(tempfd,offset,0) == offset ) && ( read(tempfd,*ptr,blk_size) == blk_size )) { *bptr = blk_no; return(*ptr); } return(NULL); } return(*ptr); } /* * This routine puts the designated block back on the disk */ long putblock(ptr,block,size) char *ptr; long block; int size; { extern long lseek(); /* disk seek function */ daddr_t offset; /* calculated offset */ if(zflg > 2) printf("putblock(%o,%ld,%d)\n",ptr,block,size); offset = block * size; if(( lseek(tempfd,offset,0) == offset ) && ( write(tempfd,ptr,size) == size )) { return(block); } return(FAIL); } EQ(s1, s2, ct) char *s1, *s2; int ct; { register i; for(i=0; i<ct; ++i) { if(*s1 == *s2) {; if(*s1 == '\0') return(1); s1++; s2++; continue; } else return(0); } return(1); } ask() { char ans[12]; fgets(ans, 10, Devtty); if(EQ(ans,"y", 1)) return(0); if(EQ(ans,"n", 1)) exit(2); else return(1); }