/* cpio COMPILE: cc -O cpio.c -s -i -o cpio cpio -- copy file collections */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #ifdef RT #define S_IFEXT 0120000 /* allocated by extents */ #define S_IF1EXT 0130000 /* one extent */ #endif #define EQ(x,y) (strcmp(x,y)==0) /* for VAX, Interdata, ... */ #define MKSHORT(v,lv) {U.l=1L;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,v[0]=U.s[0],v[1]=U.s[1];} #define MAGIC 070707 #define IN 1 #define OUT 2 #define PASS 3 #define HDRSIZE ((sizeof Hdr)-256) #define LINKS 1000 #define MERT 0 #define CHARS 76 #ifdef RT #define MERT 1 /* yes = 1 ; no = 0 */ extern long filespace; #endif struct stat Statb, Xstatb; struct header { short h_magic, h_dev; ushort h_ino, h_mode, h_uid, h_gid; short h_nlink, h_rdev, h_mtime[2], h_namesize, h_filesize[2]; char h_name[256]; } Hdr; int Bufsize = 512; short Buf[256], *Dbuf; char BBuf[512]; char *Cbuf; int Wct,Wc; short *Wp; char *Cp; #ifdef RT short Actual_size[2]; /* MERT variable */ struct{ long long_size; }; #endif short Option, Dir, Uncond, Link, Rename, Toc, Verbose, Select, Mod_time, Acc_time, Cflag, Swap; int Ifile, Ofile, Input = 0, Output = 1; long Blocks, Longfile, Longtime; char Fullname[256], Name[256]; int Pathend; FILE *Rtty, *Wtty; char *Pattern[100]; char Strhdr[500]; char *Chdr = Strhdr; short Dev, Uid, Gid, A_directory, A_special, #ifdef RT One_extent, Multi_extent, #endif Filetype = S_IFMT; #ifdef RT short Remove_mode = 0007777; short New_mode; #endif extern errno; char *malloc(); char *cd(); char *Cd_name; FILE *popen(); union { long l; short s[2]; char c[4]; } U; /* for VAX, Interdata, ... */ long mklong(v) short v[]; { U.l = 1; if(U.c[0]) U.s[0] = v[1], U.s[1] = v[0]; else U.s[0] = v[0], U.s[1] = v[1]; return U.l; } main(argc, argv) char **argv; { register ct; long filesz; long lng; register char *fullp; register i; signal(SIGSYS, 1); if(*argv[1] != '-') usage(); Uid = getuid(); umask(0); Gid = getgid(); Pattern[0] = "*"; while(*++argv[1]) { switch(*argv[1]) { case 'a': Acc_time++; break; case 'B': Bufsize = 5120; break; case 'i': Option = IN; if(argc > 2 ) { for(i = 0; (i+2) < argc; ++i) Pattern[i] = argv[i+2]; } break; case 'o': if(argc != 2) usage(); Option = OUT; break; case 'p': if(argc != 3) usage(); if(access(argv[2], 2) == -1) { accerr: err("cannot write in <%s>\n", argv[2]); exit(2); } strcpy(Fullname, argv[2]); strcat(Fullname, "/"); stat(Fullname, &Xstatb); if((Xstatb.st_mode&S_IFMT) != S_IFDIR) goto accerr; Option = PASS; Dev = Xstatb.st_dev; break; case 'c': Cflag++; break; case 'd': Dir++; break; case 'l': Link++; break; case 'm': Mod_time++; break; case 'r': Rename++; Rtty = fopen("/dev/tty", "r"); Wtty = fopen("/dev/tty", "w"); if(Rtty==NULL || Wtty==NULL) { err( "Cannot rename (/dev/tty missing)\n"); exit(2); } break; case 's': Swap++; break; case 't': Toc++; break; case 'u': Uncond++; break; case 'v': Verbose++; break; case '6': Filetype = 060000; break; default: usage(); } } if(!Option) { err("Options must include o|i|p\n"); exit(2); } if (Cflag && Swap) { err("Swap flag is ignored with Cflag\n"); Swap = 0; } if(Option != PASS) { Wp = Dbuf = (short *)malloc(Bufsize); Cp = Cbuf = (char *)malloc(Bufsize); } Wct = Bufsize >> 1; Wc = Bufsize; if(Option == PASS && Rename) { err("Pass and Rename cannot be used together"); exit(2); } switch(Option) { case OUT: while(getname()) { if( mklong(Hdr.h_filesize) == 0L) { if ( Cflag ) writehdr(Chdr,CHARS+Hdr.h_namesize); else bwrite(&Hdr, HDRSIZE+Hdr.h_namesize); #ifdef RT if( (MERT) && (((Hdr.h_mode & Filetype) == S_IF1EXT) || ((Hdr.h_mode & Filetype) == S_IFEXT))) { actsize(); bwrite(&Hdr, HDRSIZE+Hdr.h_namesize); } #endif continue; } if((Ifile = open(Hdr.h_name, 0)) < 0) { err("<%s> ?\n", Hdr.h_name); continue; } if ( Cflag ) writehdr(Chdr,CHARS+Hdr.h_namesize); else bwrite(&Hdr, HDRSIZE+Hdr.h_namesize); #ifdef RT if( (MERT) && (((Hdr.h_mode & Filetype) == S_IF1EXT) || ((Hdr.h_mode & Filetype) == S_IFEXT))) { actsize(); bwrite(&Hdr, HDRSIZE+Hdr.h_namesize); } #endif for(filesz=mklong(Hdr.h_filesize); filesz>0; filesz-= 512){ ct = filesz>512? 512: filesz; if(read(Ifile, Cflag? BBuf: (char *)Buf, ct) < 0) { err("Cannot read %s\n", Hdr.h_name); continue; } Cflag? writehdr(BBuf,ct): bwrite(Buf,ct); } close(Ifile); if(Acc_time) utime(Hdr.h_name, &Statb.st_atime); if(Verbose) err("%s\n", Hdr.h_name); } strcpy(Hdr.h_name, "TRAILER!!!"); Hdr.h_magic = MAGIC; MKSHORT(Hdr.h_filesize, 0L); Hdr.h_namesize = strlen("TRAILER!!!") + 1; if ( Cflag ) { lng = 0; bintochar(lng); writehdr(Chdr,CHARS+Hdr.h_namesize); } else bwrite(&Hdr, HDRSIZE+Hdr.h_namesize); Cflag? writehdr(Cbuf, Bufsize): bwrite(Dbuf, Bufsize); break; case IN: pwd(); while(gethdr()) { Ofile = ckname(Hdr.h_name)? openout(Hdr.h_name): 0; for(filesz=mklong(Hdr.h_filesize); filesz>0; filesz-= 512){ ct = filesz>512? 512: filesz; Cflag? readhdr(BBuf, ct): bread(Buf, ct); if(Ofile) { if(Swap) swap(Buf, ct); if(write(Ofile, Cflag? BBuf: (char *)Buf, ct) < 0) { err("Cannot write %s\n", Hdr.h_name); continue; } } } if(Ofile) { close(Ofile); set_time(Cd_name, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime)); } if(!Select) continue; if(Verbose) if(Toc) pentry(Hdr.h_name); else puts(Hdr.h_name); else if(Toc) puts(Hdr.h_name); } break; case PASS: fullp = Fullname + strlen(Fullname); while(getname()) { if(!ckname(Hdr.h_name)) continue; strcpy(fullp, Hdr.h_name); if(Link && !A_directory && Dev == Statb.st_dev) { /* ??? && (Uid == Statb.st_uid || !Uid)) {*/ if(link(Hdr.h_name, Fullname) < 0) { /* missing dir.? */ unlink(Fullname); missdir(Fullname); if(link(Hdr.h_name, Fullname) < 0) { err( "Cannot link <%s> & <%s>\n", Hdr.h_name, Fullname); continue; } } set_time(Hdr.h_name, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime)); goto ckverbose; } if(!(Ofile = openout(Fullname))) continue; if((Ifile = open(Hdr.h_name, 0)) < 0) { err("<%s> ?\n", Hdr.h_name); close(Ofile); continue; } filesz = Statb.st_size; for(; filesz > 0; filesz -= 512) { ct = filesz>512? 512: filesz; if(read(Ifile, Buf, ct) < 0) { err("Cannot read %s\n", Hdr.h_name); break; } if(Ofile) if(write(Ofile, Buf, ct) < 0) { err("Cannot write %s\n", Hdr.h_name); break; } ++Blocks; } close(Ifile); if(Acc_time) utime(Hdr.h_name, &Statb.st_atime); if(Ofile) { close(Ofile); set_time(Fullname, Statb.st_atime, mklong(Hdr.h_mtime)); ckverbose: if(Verbose) puts(Fullname); } } } err("%ld blocks\n", Blocks * (Bufsize>>9)); exit(0); } usage() { err("Usage: cpio -o[acvB] <name-list >collection\n%s\n%s\n", " cpio -i[cdmrstuvB6] [pattern ...] <collection", " cpio -p[adlmruv] directory <name-list"); exit(2); } getname() { register char *namep = Name; long tlong; for(;;) { if(gets(namep) == NULL) return 0; if(*namep == '.' && namep[1] == '/') namep += 2; strcpy(Hdr.h_name, namep); if(stat(namep, &Statb) < 0) { err("< %s > ?\n", Hdr.h_name); continue; } A_directory = (Statb.st_mode & Filetype) == S_IFDIR; A_special = ((Statb.st_mode & Filetype) == S_IFBLK) || ((Statb.st_mode & Filetype) == S_IFCHR) || ((Statb.st_mode & Filetype) == S_IFIFO); #ifdef RT if(MERT) { One_extent = (Statb.st_mode & Filetype) == S_IF1EXT; Multi_extent = (Statb.st_mode & Filetype) == S_IFEXT; } #endif Hdr.h_magic = MAGIC; Hdr.h_namesize = strlen(Hdr.h_name) + 1; Hdr.h_uid = Statb.st_uid; Hdr.h_gid = Statb.st_gid; Hdr.h_dev = Statb.st_dev; Hdr.h_ino = Statb.st_ino; Hdr.h_mode = Statb.st_mode; MKSHORT(Hdr.h_mtime, Statb.st_mtime); Hdr.h_nlink = Statb.st_nlink; tlong = Hdr.h_mode & S_IFREG? Statb.st_size: 0L; MKSHORT(Hdr.h_filesize, tlong); Hdr.h_rdev = Statb.st_rdev; if(Cflag) bintochar(tlong); return 1; } } bintochar(t) long t; { sprintf(Chdr,"%.6o%.6ho%.6ho%.6ho%.6ho%.6ho%.6ho%.6ho%.11lo%.6ho%.11lo%s", MAGIC,Statb.st_dev,Statb.st_ino,Statb.st_mode,Statb.st_uid, Statb.st_gid,Statb.st_nlink,Statb.st_rdev & 00000177777, Statb.st_mtime,(short)strlen(Hdr.h_name)+1,t,Hdr.h_name); } chartobin() { sscanf(Chdr,"%6o%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6ho%11lo", &Hdr.h_magic,&Hdr.h_dev,&Hdr.h_ino,&Hdr.h_mode,&Hdr.h_uid, &Hdr.h_gid,&Hdr.h_nlink,&Hdr.h_rdev,&Longtime,&Hdr.h_namesize, &Longfile); MKSHORT(Hdr.h_filesize, Longfile); MKSHORT(Hdr.h_mtime, Longtime); } gethdr() { if ( Cflag ) { readhdr(Chdr,CHARS); chartobin(); } else bread(&Hdr, HDRSIZE); if(Hdr.h_magic != MAGIC) { err("Out of phase--get help"); exit(2); } if(!Cflag) bread(Hdr.h_name, Hdr.h_namesize); else readhdr(Hdr.h_name, Hdr.h_namesize); if(Swap) swap(Hdr.h_name, Hdr.h_namesize); if(EQ(Hdr.h_name, "TRAILER!!!")) return 0; A_directory = (Hdr.h_mode & Filetype) == S_IFDIR; A_special =((Hdr.h_mode & Filetype) == S_IFBLK) || ((Hdr.h_mode & Filetype) == S_IFCHR) || ((Hdr.h_mode & Filetype) == S_IFIFO); #ifdef RT if( (MERT) && (((Hdr.h_mode & Filetype) == S_IF1EXT) || ((Hdr.h_mode & Filetype) == S_IFEXT))) { One_extent = (Hdr.h_mode & Filetype) == S_IF1EXT; Multi_extent = (Hdr.h_mode & Filetype) == S_IFEXT; Actual_size[0] = Hdr.h_filesize[0]; Actual_size[1] = Hdr.h_filesize[1]; bread(&Hdr, HDRSIZE); if(Hdr.h_magic != MAGIC) { err("Out of phase--get MERT help"); exit(2); } bread(Hdr.h_name, Hdr.h_namesize); } #endif return 1; } ckname(namep) register char *namep; { ++Select; if(!nmatch(namep, Pattern)) { Select = 0; return 0; } if(Rename && !A_directory) { fprintf(Wtty, "Rename <%s>\n", namep); fflush(Wtty); fgets(namep, 128, Rtty); if(feof(Rtty)) exit(2); namep[strlen(namep) - 1] = '\0'; if(EQ(namep, "")) { printf("Skipped\n"); return 0; } } return !Toc; } openout(namep) register char *namep; { register f; register char *np; if(!strncmp(namep, "./", 2)) namep += 2; np = namep; if(Option == IN) Cd_name = namep = cd(namep); if(A_directory) { if(!Dir || Rename || EQ(namep, ".") || EQ(namep, "..") || stat(namep, &Xstatb) == 0) return 0; if(!makdir(namep)) { missdir(namep); } ret: chmod(namep, Hdr.h_mode); if(Uid == 0) chown(namep, Hdr.h_uid, Hdr.h_gid); set_time(namep, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime)); return 0; } if(Hdr.h_nlink > 1) if(!postml(namep, np)) return 0; if(A_special) { s_again: if((Hdr.h_mode & Filetype) == S_IFIFO) Hdr.h_rdev = 0; if(mknod(namep, Hdr.h_mode, Hdr.h_rdev) < 0) { if(missdir(namep)) goto s_again; err("Cannot mknod <%s>\n", namep); return 0; } goto ret; } if(stat(namep, &Xstatb) == 0) { if(Uncond && !(Xstatb.st_mode & S_IWRITE)) unlink(namep); if(!Uncond && (mklong(Hdr.h_mtime) < Xstatb.st_mtime)) { err("current <%s> newer\n", namep); return 0; } } if(Option == PASS && Hdr.h_ino == Xstatb.st_ino && Hdr.h_dev == Xstatb.st_dev) { err("Attempt to pass file to self!\n"); exit(2); } #ifdef RT one_again: if(One_extent || Multi_extent) { if((f = falloc(namep, Hdr.h_mode, Hdr.h_filesize[0].long_size)) < 0) { if(missdir(namep)) goto one_again; err("Cannot create <%s> (errno:%d)\n", namep, errno); return 0; } if(filespace < Hdr.h_filesize[0].long_size){ err("Cannot create contiguous file <%s> proper size\n", namep); err(" <%s> will be created as a regular file\n", namep); if(unlink(Fullname) != 0) err("<%s> not removed\n", namep); New_mode = Hdr.h_mode & Remove_mode; New_mode = New_mode | S_IFREG; once_again: if((f = creat(namep, New_mode)) < 0){ if(missdir(namep)) goto once_again; err("Cannot create <%s> (errno:%d)\n", namep, errno); return (0); } } } #endif #ifdef RT if(MERT && (One_extent || Multi_extent)) goto skip_c; #endif c_again: if((f = creat(namep, Hdr.h_mode)) < 0) { if(missdir(namep)) goto c_again; err("Cannot create <%s> (errno:%d)\n", namep, errno); return 0; } #ifdef RT skip_c: #endif if(Uid == 0) chown(namep, Hdr.h_uid, Hdr.h_gid); return f; } bread(b, c) register c; register short *b; { static nleft = 0; static short *ip; register short *p = ip; c = (c+1)>>1; while(c--) { if(!nleft) { again: if(read(Input, Dbuf, Bufsize)!=Bufsize) { Input = chgreel(0, Input); goto again; } nleft = Bufsize >> 1; p = Dbuf; ++Blocks; } *b++ = *p++; --nleft; } ip = p; } readhdr(b, c) register c; register char *b; { static nleft = 0; static char *ip; register char *p = ip; while(c--) { if(!nleft) { again: if(read(Input, Cbuf, Bufsize) != Bufsize) { Input = chgreel(0, Input); goto again; } nleft = Bufsize; p = Cbuf; ++Blocks; } *b++ = *p++; --nleft; } ip = p; } bwrite(rp, c) register short *rp; register c; { register short *wp = Wp; c = (c+1) >> 1; while(c--) { if(!Wct) { again: if(write(Output, Dbuf, Bufsize)<0) { Output = chgreel(1, Output); goto again; } Wct = Bufsize >> 1; wp = Dbuf; ++Blocks; } *wp++ = *rp++; --Wct; } Wp = wp; } writehdr(rp,c) register char *rp; register c; { register char *cp = Cp; while(c--) { if(!Wc) { again: if(write(Output,Cbuf,Bufsize)<0) { Output = chgreel(1, Output); goto again; } Wc = Bufsize; cp = Cbuf; ++Blocks; } *cp++ = *rp++; --Wc; } Cp = cp; } postml(namep, np) register char *namep, *np; { register i; static struct ml { short m_dev, m_ino; char m_name[2]; } *ml[LINKS]; static mlinks = 0; char *mlp; for(i = 0; i < mlinks; ++i) { if(mlinks == LINKS) break; if(ml[i]->m_ino==Hdr.h_ino && ml[i]->m_dev==Hdr.h_dev) { if(Verbose) printf("%s linked to %s\n", ml[i]->m_name, np); unlink(namep); if(Option == IN) { Fullname[Pathend] = '\0'; strcat(Fullname, ml[i]->m_name); mlp = Fullname; } else mlp = ml[i]->m_name; l_again: if(link(mlp, namep) < 0) { if(missdir(np)) goto l_again; err("Cannot link <%s>&<%s>.\n", ml[i]->m_name, np); } set_time(namep, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime)); return 0; } } if(mlinks == LINKS || !(ml[mlinks] = (struct ml *)malloc(strlen(np) + 2 + sizeof(struct ml)))) { static int first=1; if(first) if(mlinks == LINKS) err("Too many links\n"); else err("No memory for links\n"); mlinks = LINKS; first = 0; return 1; } ml[mlinks]->m_dev = Hdr.h_dev; ml[mlinks]->m_ino = Hdr.h_ino; strcpy(ml[mlinks]->m_name, np); ++mlinks; return 1; } pentry(namep) register char *namep; { register i; static short lastid = -1; #include <pwd.h> static struct passwd *pw; struct passwd *getpwuid(); static char tbuf[32]; printf("%-7o", Hdr.h_mode & 0177777); if(lastid == Hdr.h_uid) printf("%-6s", pw->pw_name); else { setpwent(); if(pw = getpwuid(Hdr.h_uid)) { printf("%-6s", pw->pw_name); lastid = Hdr.h_uid; } else { printf("%-6d", Hdr.h_uid); lastid = -1; } } printf("%7ld ", mklong(Hdr.h_filesize)); U.l = mklong(Hdr.h_mtime); strcpy(tbuf, ctime(&U.l)); tbuf[24] = '\0'; printf(" %s %s\n", &tbuf[4], namep); } nmatch(s, pat) char *s, **pat; { if(EQ(*pat, "*")) return 1; while(*pat) { if((**pat == '!' && !gmatch(s, *pat+1)) || gmatch(s, *pat)) return 1; ++pat; } return 0; } gmatch(s, p) register char *s, *p; { register int c; register cc, ok, lc, scc; scc = *s; lc = 077777; switch (c = *p) { case '[': ok = 0; while (cc = *++p) { switch (cc) { case ']': if (ok) return(gmatch(++s, ++p)); else return(0); case '-': ok |= (lc <= scc & scc <= (cc=p[1])); } if (scc==(lc=cc)) ok++; } return(0); case '?': caseq: if(scc) return(gmatch(++s, ++p)); return(0); case '*': return(umatch(s, ++p)); case 0: return(!scc); } if (c==scc) goto caseq; return(0); } umatch(s, p) register char *s, *p; { if(*p==0) return(1); while(*s) if (gmatch(s++,p)) return(1); return(0); } makdir(namep) register char *namep; { static status; register pid; if(pid = fork()) while(wait(&status) != pid); else { close(2); execl("/bin/mkdir", "mkdir", namep, 0); exit(2); } return ((status>>8) & 0377)? 0: 1; } swap(buf, ct) register ct; register union swp { short shortw; char charv[2]; } *buf; { register char c; ct = (ct + 1) >> 1; while(ct--) { c = buf->charv[0]; buf->charv[0] = buf->charv[1]; buf->charv[1] = c; ++buf; } } set_time(namep, atime, mtime) register *namep; long atime, mtime; { static long timevec[2]; if(!Mod_time) return; timevec[0] = atime; timevec[1] = mtime; utime(namep, timevec); } chgreel(x, fl) { register f; char str[22]; FILE *devtty; struct stat statb; err("errno: %d, ", errno); err("Can't %s\n", x? "write output": "read input"); fstat(fl, &statb); #ifdef RT if(!MERT){ if((statb.st_mode&S_IFMT) != S_IFCHR) exit(2); } else if((statb.st_mode & (S_IFBLK|S_IFREC))==0) exit(2); #endif #ifndef RT if((statb.st_mode&S_IFMT) != S_IFCHR) exit(2); #endif again: err("If you want to go on, type device/file name when ready\n"); devtty = fopen("/dev/tty", "r"); fgets(str, 20, devtty); str[strlen(str) - 1] = '\0'; if(!*str) exit(2); close(fl); if((f = open(str, x? 1: 0)) < 0) { err("That didn't work"); fclose(devtty); goto again; } return f; } missdir(namep) register char *namep; { register char *np; register ct = 0; if(!Dir) return 0; for(np = namep; *np; ++np) if(*np == '/') { *np = '\0'; if(stat(namep, &Xstatb) == -1) makdir(namep), ++ct; *np = '/'; } return ct; } err(a, b, c) { fprintf(stderr, a, b, c); } pwd() { FILE *dir; dir = popen("pwd", "r"); fgets(Fullname, 256, dir); if(pclose(dir)) exit(2); Pathend = strlen(Fullname); Fullname[Pathend - 1] = '/'; } char * cd(n) register char *n; { char *p_save = Name, *n_save = n, *p_end = 0; register char *p = Name; static char dotdot[]="../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../"; int slashes; if(*n == '/') /* don't try to chdir on full pathnames */ return n; for(; *p && *n == *p; ++p, ++n) { /* whatever part of strings == */ if(*p == '/') p_save = p+1, n_save = n+1; } p = p_save; *p++ = '\0'; for(slashes = 0; *p; ++p) { /* if prev is longer, chdir("..") */ if(*p == '/') ++slashes; } p = p_save; if(slashes) { slashes = slashes * 3 - 1; dotdot[slashes] = '\0'; chdir(dotdot); dotdot[slashes] = '/'; } n = n_save; for(; *n; ++n, ++p) { *p = *n; if(*n == '/') p_end = p+1, n_save = n+1; } *p = '\0'; if(p_end) { *p_end = '\0'; if(chdir(p_save) == -1) { if(!missdir(p_save)) { cd_err: err("Cannot chdir (no `d' option)\n"); exit(2); } else if(chdir(p_save) == -1) goto cd_err; } } else *p_save = '\0'; return n_save; } #ifdef RT actsize() { } #endif