/* * Copy files from RSX-11 (ODS-1) filesystem. Files may be copied to * disk or to standard output. Transfer modes supported are "text" * (FCS stuff is thrown away, newline is tacked on the end of each * RSX record), and "image" (straight byte-by-byte transfer). There * were once plans to support a third mode ("binary"), but this has * not yet been implemented. Defaults for the output destination and * transfer mode are set by #defines, but the destination/mode can be * specified at runtime by using various flags (see "options()"). * * The input device and UFD, if omitted, will default to that of the * previous filespec. Note that this means that the first filespec * MUST have a UFD specified, and (if DFLTDEV is not defined) also a * device as well. The filename syntax is the same as the standard * RSX naming scheme, except that a "." may be used to separate the * filetype from the version number, and some delimiters may be changed * via #defines, if desired. (This is all to avoid the possibility of * having to escape some of the characters that the shell treats as * special.) The device name is the name of the UNIX special file in * /dev, rather than what RSX thinks it would be. * * If the first character of argv[0] is "l", or if the "-l" option is * used, the program lists the contents of the UFD rather than copying * a file. At present, only one UFD may be listed per command. * * Written by Mark Bartelt. Structs (plus routine putch()) are left over * from an earlier attempt by Rob Pike. Otherwise, this is a total rewrite. */ #define err0(msg) { errmsg(msg); return(0); } #define err1(msg,arg) { errmsg(msg,arg); return(0); } #define alphnum(x) ( ( 'a'<=(x) && (x)<='z' ) || ( 'A'<=(x) && (x)<='Z' ) || ( '0'<=(x) && (x)<='9' ) ) #define octal(x) ( '0'<=(x) && (x)<='9' ) #include typedef unsigned short ushort; struct filnam { ushort fnam[3]; /* File name (radix-50) */ ushort ftyp; /* File type (radix-50) */ ushort fver; /* Version number */ }; struct uic { char u_prog; /* Programmer number */ char u_proj; /* Project number */ }; struct fileid { ushort f_num; /* File number */ ushort f_seq; /* File sequence number (worthless concept) */ ushort f_rvn; /* Relative volume number (ditto and MBZ) */ }; struct fcs { char f_rtyp; /* Record type */ char f_ratt; /* Record attributes */ ushort f_rsiz; /* Record size */ ushort f_hibk[2]; /* Highest VBN allocated */ ushort f_efbk[2]; /* End of file block */ ushort f_ffby; /* First free byte */ }; struct header { char h_idof; /* Ident area offset */ char h_mpof; /* Map area offset */ ushort h_fnum; /* File number */ ushort h_fseq; /* File sequence number (why no RVN?) */ ushort h_flev; /* File structure level */ struct uic h_fown; /* File owner UIC */ ushort h_fpro; /* File protection code */ char h_fcha[2]; /* File characteristics */ struct fcs h_fcs; /* FCS area */ char h_ufat[18]; /* User attribute area */ ushort h_data[256-23]; /* Rest of block used for maps, etc. */ }; struct ident { struct filnam i_fnam; /* File name, type, version number */ ushort i_rvno; /* Revision number */ char i_rvdat[7]; /* Revision date (ASCII) */ char i_rvti[6]; /* Revision time (ASCII) */ char i_crdt[7]; /* Creation time (ASCII) */ char i_crti[6]; /* Creation time (ASCII) */ char i_exdt[7]; /* Expiration date (ASCII) */ char i_UU; /* Unused */ }; struct rtrvp { char lbn_hi; /* High order 8 bits of LBN */ char count; /* Count field */ ushort lbn_lo; /* Low order 16 bits of LBN */ }; struct map { char m_esqn; /* Extension segment number */ char m_ervn; /* Extension relative volume number */ ushort m_efnu; /* Extension file number */ ushort m_efsq; /* Extension file sequence number */ char m_ctsz; /* Block count field size */ char m_lbsz; /* LBN Field size */ char m_use; /* Map words in use */ char m_max; /* Map words available */ struct rtrvp m_rtrv[1]; /* Retrieval pointers */ }; struct homeblock { ushort H_ibsz; /* Index file bitmap size */ ushort H_iblb[2]; /* Index file bitmap LBN */ ushort H_fmax; /* Maximum number of files */ ushort H_sbcl; /* Storage bitmap cluster factor */ ushort H_dvty; /* Disk device type */ ushort H_vlev; /* Volume structure level */ char H_vnam[12]; /* Volume name */ char H_UU1[4]; /* Unused 1 */ struct uic H_vown; /* Volume owner UIC */ ushort H_vpro; /* Volume protection code */ char H_vcha[2]; /* Volume characteristics */ ushort H_fpro; /* Default file protection */ char H_UU2[6]; /* Unused 2 */ char H_wisz; /* Default window size */ char H_fiex; /* Default file extend */ char H_lruc; /* Directory pre-access limit */ char H_UU3[11]; /* Unused 3 */ ushort H_chk1; /* First checksum */ char H_vdat[14]; /* Volume creation date */ char H_UU4[398]; /* Unused 4 */ char H_indn[12]; /* Volume name (don't you believe H_vnam?) */ char H_indo[12]; /* Volume owner (in a different format!) */ char H_indf[12]; /* Format type == "DECFILE11A " */ char H_UU5[2]; /* Unused 5 */ ushort H_chk2; /* Final checksum */ } hblock; struct directory { struct fileid d_fid; /* File id */ struct filnam d_fname; /* File name, type, version number */ }; #define DEPB 32 /* Number of directory entries per block */ #define bit(x) ((01)<<(x)) #define DEV bit(0) #define DIR bit(1) #define FIL bit(2) #define EXT bit(3) #define VER bit(4) #define DIRBEG '[' #define DIREND ']' #define NULLCHR '\0' #define NULLSTR "" #define FSMAX 100 #define DEVMAX 10 #define TEXT 0 #define IMAGE 1 #define BINARY 2 #define DISK 0 #define STDOUT 1 #define DFLTMOD TEXT #define DFLTOUT DISK char **av; /* Global argv */ char lsflag = 0; /* Nonzero ==> list directory */ int xfermode = DFLTMOD; /* Transfer mode */ int outdest = DFLTOUT; /* Output destination */ char filspec[FSMAX]; /* Full filename string being processed */ int pflags; /* Flags returned by crack() */ char *dev, *dir, *fil, *typ, *ver; /* Pointers to cracked filename fields */ char rsxdev[DEVMAX+6]; /* Special file name for RSX filesystem */ int rsx = -1; /* File descriptor for reading RSX filesystem */ FILE *of; /* Stream pointer for output file */ struct header indexh, mfdh, dirh, fileh; /* File headers for index file and MFD */ struct ident *indexi, *mfdi, *diri; /* Pointers to their index areas */ struct map *indexm, *mfdm, *dirm, *filem; /* Pointers to their map areas */ char r50c[128] = { /* ASCII to Radix-50 conversion table */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 000, -1, -1, -1, 033, -1, -1, -1, -1, -1, -1, -1, -1, -1, 034, -1, 036, 037, 040, 041, 042, 043, 044, 045, 046, 047, -1, -1, -1, -1, -1, -1, -1, 001, 002, 003, 004, 005, 006, 007, 010, 011, 012, 013, 014, 015, 016, 017, 020, 021, 022, 023, 024, 025, 026, 027, 030, 031, 032, -1, -1, -1, -1, -1, -1, 001, 002, 003, 004, 005, 006, 007, 010, 011, 012, 013, 014, 015, 016, 017, 020, 021, 022, 023, 024, 025, 026, 027, 030, 031, 032, -1, -1, -1, -1, -1 }; char r50a[] = " abcdefghijklmnopqrstuvwxyz$.?0123456789"; main(argc,argv) int argc; char **argv; { av = argv; if ( --argc == 0 ) usage(); if ( **argv == 'l' ) ++lsflag; while ( argc-- ) { if ( **++av == '-' ) options(*argv); else getrsx(); } } usage() { fprintf(stderr,"usage: %s [-t] [-i] [-b] [-d] [-f] [-s] rsxfile",*av); } /* * Process option flags */ options() { register char *p; for ( p = *av; *++p; ) { switch ( *p ) { case 'd': case 'f': outdest = DISK; break; case 's': outdest = STDOUT; break; case 't': xfermode = TEXT; break; case 'i': xfermode = IMAGE; break; case 'b': xfermode = BINARY; break; case 'l': ++lsflag; break; case 'c': lsflag = 0; break; default: fprintf(stderr,"Invalid option (%c)\n",*p); } } } /* * Get the next reqested file from the RSX filesystem */ getrsx() { if ( strlen(*av) > FSMAX ) err0("Filespec too long"); strcpy(filspec,*av); if ( lsflag ) { if ( openin() ) listdir(); } else { if ( openin() && openout() ) { copyfile(); if ( of != stdout ) fclose(of); } } } /* * Open RSX file for input */ openin() { static int filecnt = 0; int crackok; struct filnam r50fil; ushort fn; ushort search(); ++filecnt; crackok = crack(); if ( pflags&DEV && !openrsx(dev) ) return(0); #ifdef DFLTDEV if ( !(pflags&DEV) && filecnt==1 && !openrsx(DFLTDEV) ) return(0); #endif if ( rsx < 0 ) err0("No device specified"); if ( pflags&(DEV|DIR) && !finddir() ) return(0); if ( !diri ) err0("No directory specified"); if ( lsflag ) { if ( pflags & (FIL|EXT|VER) ) err0("Invalid directory specification"); return(1); } if ( !crackok ) return(0); if ( !(pflags&EXT) ) typ = NULLSTR; if ( !(pflags&VER) ) ver = NULLSTR; if ( !r50fn(fil,typ,ver,&r50fil) ) return(0); if ( !(fn=search(dirm,&r50fil)) ) err0("File does not exist"); if ( !gethdr(fn,&fileh) ) err0("Can't get file header for file"); filem = (struct map *) &fileh.h_data[fileh.h_mpof-23]; return(1); } /* * Crack the filename string -- First step in parsing it; just * locates the fields, doesn't do much real validity checking */ crack() { register char *p; register char *q; for ( p=filspec, pflags=0; *p; ) { if ( *p == DIRBEG ) { if ( pflags & (DIR|FIL|EXT|VER) ) err0("Bad filename syntax"); dir = p+1; while ( *p != DIREND ) if ( *p++ == NULLCHR ) err0("Bad filename syntax"); *p++ = NULLCHR; pflags |= DIR; continue; } for ( q=p; alphnum(*q); ++q ) ; if ( *q == ':' ) { if ( pflags&(DEV|DIR|FIL|EXT|VER) ) err0("Bad filename syntax"); dev = p; pflags |= DEV; *q = NULLCHR; p = q + 1; continue; } if ( *q == '.' || *q == NULLCHR ) { if ( !(pflags&FIL) ) { if ( p == q ) err0("Filename missing"); fil = p; pflags |= FIL; if ( *q == ';' ) { typ = NULLSTR; pflags |= EXT; } } else if ( !(pflags&EXT) ) { typ = p; pflags |= EXT; } else if ( !(pflags&VER) ) { ver = p; pflags |= VER; } else err0("Bad filename syntax"); if ( *q == NULLCHR ) { if ( !(pflags&EXT) ) typ = NULLSTR; if ( !(pflags&VER) ) ver = NULLSTR; break; } *q = NULLCHR; p = q + 1; continue; } err0("Bad filename syntax"); } return(1); } /* * Open a disk containing an RSX filesystem */ openrsx(devname) char *devname; { long ifhbn; if ( strlen(devname) > DEVMAX ) err1("Device name too long (%s)",devname); strcpy(rsxdev,"/dev/"); strcat(rsxdev,devname); if ( (rsx=open(rsxdev,0)) < 0 ) err1("Can't open %s",rsxdev); if ( !getlb(1L,&hblock) ) err1("Can't read homeblock on %s",rsxdev); ifhbn = ((long)hblock.H_iblb[0]<<16) + (long)hblock.H_iblb[1] + hblock.H_ibsz; if ( !getlb(ifhbn,&indexh) ) err1("Can't read index file header on %s\n",rsxdev); indexi = (struct ident *) &indexh.h_data[indexh.h_idof-23]; indexm = (struct map *) &indexh.h_data[indexh.h_mpof-23]; if ( !getlb(ifhbn+3,&mfdh) ) err1("Can't read mfd header on %s",rsxdev); mfdi = (struct ident *) &mfdh.h_data[mfdh.h_idof-23]; mfdm = (struct map *) &mfdh.h_data[mfdh.h_mpof-23]; return(1); } /* * Locate the directory whose name is pointed to by "dir" */ finddir() { char dirname[7]; register char *p; register char *q; register char *s; char *strchr(); int nch; struct filnam r50dir; ushort dirfnum; ushort search(); diri = (struct ident *) 0; dirm = (struct map *) 0; strcpy(dirname,"000000"); p = dir; q = dirname; if ( (s=strchr(p,',')) == (char *)0 || (nch=s-p) == 0 || nch > 3 ) err1("Invalid UFD ([%s])",dir); for ( q += 3-nch; p < s; ) { if ( !octal(*p) ) err1("Invalid UFD ([%s])",dir); *q++ = *p++; } if ( (nch=strlen(++p)) == 0 || nch > 3 ) err1("Invalid UFD ([%s])",dir); for ( q += 3-nch; *p; ) { if ( !octal(*p) ) err1("Invalid UFD ([%s])",dir); *q++ = *p++; } if ( !r50fn(dirname,"dir","1",&r50dir) ) err1("Invalid UFD ([%s])",dir); if ( !(dirfnum=search(mfdm,&r50dir)) ) err1("UFD [%s] does not exist",dir); if ( !gethdr(dirfnum,&dirh) ) err1("Can't get file header for UFD [%s]",dir); diri = (struct ident *) &dirh.h_data[dirh.h_idof-23]; dirm = (struct map * ) &dirh.h_data[dirh.h_mpof-23]; return(1); } /* * Convert file name, type, and version number to "struct filnam" format */ r50fn(fl,tp,vr,r50f) char *fl; char *tp; char *vr; struct filnam *r50f; { register int i; ushort ator50(); if ( strlen(fl) > 9 ) err0("Filename longer than 9 characters"); if ( strlen(tp) > 3 ) err0("File type longer than 3 characters"); for ( i=0; i<3; ++i ) if ( (r50f->fnam[i]=ator50(&fl)) == -1 ) return(0); if ( (r50f->ftyp=ator50(&tp)) == -1 ) return(0); r50f->fver = 0; while ( *vr ) { if ( !octal(*vr) ) err0("Non-octal character in version number"); r50f->fver <<= 3; r50f->fver += *vr++ - '0'; } return(1); } /* * Convert up to three ascii characters to a 16-bit radix-50 value; note * type of s (char **, not char *); *s is left pointing to the null byte, * if encountered */ ushort ator50(s) register char **s; { int i = 3; ushort scale = 050*050; ushort r = 0; while ( **s && i-- ) { if ( r50c[**s] == -1 ) { errmsg("Bad radix-50 character (%c)",**s); return(-1); } r += r50c[*(*s)++] * scale; scale /= 050; } return(r); } /* * Search a directory (identified by dmap) for a radix50 filename */ ushort search(dmap,file) register struct map *dmap; register struct filnam *file; { register ushort hldver; register ushort hldfnum; register long vb; struct directory dirbuf[DEPB]; struct directory *de; for ( hldver=0, hldfnum=0, vb=0; getvb(++vb,(char *)dirbuf,dmap); ) { for ( de=dirbuf; ded_fid.f_num == 0 ) continue; if ( file->fver && match(file,&de->d_fname,5) ) return(de->d_fid.f_num); if ( !file->fver && de->d_fname.fver>hldver && match(file,&de->d_fname,4) ) { hldfnum = de->d_fid.f_num; hldver = de->d_fname.fver; } } } return(hldfnum); } /* * Check whether rad50 filenames (possibly including version number) match */ match(a,b,n) register ushort *a; register ushort *b; int n; { while ( n-- ) if ( *a++ != *b++ ) return(0); return(1); } /* * Open output file */ openout() { char outfile[14]; if ( outdest == STDOUT ) { of = stdout; return(1); } strcpy(outfile,fil); strcat(outfile,"."); strcat(outfile,typ); if ( (of=fopen(outfile,"w")) == NULL ) err0("Can't open output file"); return(1); } /* * Copy input file to output destination */ copyfile() { long eofblk; register long block = 0; register long b = 0; char buf[512]; char *limit = &buf[512]; register char *p; if ( xfermode == BINARY ) err0("Binary mode not yet supported"); if ( xfermode != IMAGE ) eofblk = ( (long)fileh.h_fcs.f_efbk[0] << 16 ) + fileh.h_fcs.f_efbk[1]; while ( getvb(++block,buf,filem) ) { if ( xfermode == IMAGE ) { if ( fwrite(buf,1,512,of) != 512 ) err0("write error"); continue; } if ( ++b > eofblk ) return(1); if ( b == eofblk ) limit = &buf[fileh.h_fcs.f_ffby]; for ( p=buf; pd_fid.f_num != 0 ) prtfn(&de->d_fname); fclose(stdout); wait(0); exit(0); } /* * Redirect standard output, to pipe it through "sort" */ redirout() { int pfd[2]; int fk; if ( pipe(pfd) ) err0("Can't create pipe"); while ( (fk=fork()) == -1 ) sleep(1); if ( fk == 0 ) dup2(pfd[0],0); else dup2(pfd[1],1); close(pfd[0]); close(pfd[1]); if ( fk == 0 ) { execl("/usr/bin/sort","sort",0); execl("/bin/sort","sort",0); err0("Can't exec sort"); } } /* * Convert a radix-50 filename to ASCII, and write to standard output. */ prtfn(r50fil) struct filnam *r50fil; { register int i; for ( i=0; i<3; ++i ) if ( !r50out(r50fil->fnam[i]) ) break; putchar('.'); r50out(r50fil->ftyp); fprintf(stdout,";%o\n",r50fil->fver); } /* * Convert up to three nonblank radix-50 characters to ASCII, and write to * standard output. Returns 1 if three characters converted, 0 otherwise. */ r50out(x) ushort x; { ushort div = 050*050; char c; while ( (c=r50a[(x/div)%050]) != ' ' ) { putchar(c); if ( !(div/=050) ) return(1); } return(0); } /* * Get a file header, given the file number */ gethdr(fn,buf) ushort fn; char buf[512]; { long bn; bn = (long)fn + (long)hblock.H_ibsz + 2; return(getvb(bn,buf,indexm)); } /* * Routine to get specified virtual block from a file. Returns 0 * on EOF, 1 otherwise. Note that vbn is 1-based, not 0-based. */ getvb(vbn,buf,map) register long vbn; char buf[512]; register struct map *map; { register struct rtrvp *rp; register struct rtrvp *limit; register long block; register long lbn; rp = map->m_rtrv; block = 1; limit = (struct rtrvp *) ((ushort *)map->m_rtrv + map->m_use); while ( vbn >= ( block += (rp->count&0377)+1 ) ) if ( ++rp >= limit ) return(0); lbn = (long)rp->lbn_lo + (((long)rp->lbn_hi & 0377)<<16) + vbn - block + (rp->count&0377) + 1; return(getlb(lbn,buf)); } /* * Get block from the filesystem, given the logical block number */ getlb(lbn,buf) long lbn; char buf[512]; { if ( lbn == 0 ) err0("Bad block in file"); if ( lseek(rsx,512L*lbn,0) == -1 || read(rsx,buf,512) != 512 ) err0("Read error"); return(1); } /* * Issue an error message */ errmsg(msg,arg) char *msg; int arg; { fprintf(stderr,"%s -- ",*av); fprintf(stderr,msg,arg); fprintf(stderr,"\n"); }