/* RECOVERY.C A program to recover files from damaged NBACKUP archive files. File names, directories, and sizes are retained, file time/dates are not. Written and Copyright 1991 by Joe R. Doupnik, Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet, (801) 750-2982. This program may be freely copied at no cost. It may not be sold without permission of the author, above. Written for Microsoft C version 5 and above. Build by saying CL RECOVERY.C */ #include #include /* BUFLEN is local work buffer, it should be no less than 512 bytes */ #define BUFLEN 8096 #define BSLASH 0x5c FILE *fin, *fout; unsigned char seqlimit[3]; unsigned char initcwd[65]; unsigned char infile[65]; unsigned char nbpath[65]; unsigned char filepath[65]; unsigned char filename[65]; unsigned char outroot[65]; unsigned char buf[BUFLEN]; int count, buflen, skipped; int fnamelen, pathlen, filetype1, filetype2; unsigned long filepos, filecnt, nbfilesize; main() { void match(), fixpath(), makepath(); int chdir(), i; char *strcpy(), *getcwd(); getcwd(initcwd, 65); /* get our current dir */ printf("\n\tRECOVERY, a program to recover files from NBACKUP "); printf("archives.\n\n"); printf(" Version 1.0 Copyright 1991 Joe R. Doupnik, Utah State "); printf("University, 7 May 1991\n\n"); printf("This program asks for -\n o the starting filename of a "); printf("NetWare NBACKUP utility set\n"); printf(" o the sequence number digits of the last such file to "); printf("process (FFF for all)\n"); printf(" o the directory which will be the root of recovered files\n"); printf( " Only the files are recovered, no Bindery nor time/date info\n"); printf(" Use Control-C to exit at any time.\n\n"); printf("Enter filename of the starting archive set: "); if (gets(buf) == NULL) exit(1); if (strlen(buf) > 64) exit(1); strcpy(infile, buf); /* save as archive file */ printf("Enter extension of last backup set to do, 3 chars: "); if (gets(buf) == NULL) exit(1); for (i = 0; i < 3; i++) seqlimit[i] = buf[i]; printf("Enter directory which will be the root \n"); printf(" for recovered output: "); if (gets(buf) == NULL) exit(1); if ((i = strlen(buf)) > 60) exit(1); /* too long */ if (buf[i-1] != BSLASH) /* add backslash to path */ { buf[i] = BSLASH; buf[i+1] = '\0'; } strcpy(outroot, buf); /* save as output root */ if ((fin = fopen(infile, "rb")) == NULL) /* archive file */ { printf("Cannot open %s\n", infile); exit(1); } fout = NULL; match(); /* do all the work */ fclose(fin); fclose(fout); chdir(initcwd); /* go back to starting dir */ exit(0); } void match() { char *str1char(), *res, c; register int i, j; int strncmp(), writeout(), discard(); void sizereport(), makepath(), fixpath(); do { if (readin() == 0) return; /* no more information */ res = str1char('B'); /* look for B in Backup */ if (res == NULL) /* not found in this buffer */ { writeout(buflen - 2); /* save int precusor */ continue; } skipped = res - 2 - buf; /* write data before int+B */ if (skipped < 0) skipped = 0; /* copy int and 'B' to start of buffer and fill it again */ if (skipped > 0) writeout(skipped); /* try for full string */ if (strncmp(&buf[02], "Backup Session: ",16) != 0) { /* B is not Backup Session */ writeout(3); /* write int + 'B' */ continue; } switch ((int) buf[0]) /* type of "Backup Session: " */ { case 2: /* end of this NBACKUP file */ discard(BUFLEN); /* just to be sure */ printf("\nReached end of archive input file %s\n",infile); if (newinput() == -1) return; /* all done */ break; case 3: /* create directory */ if (fout != NULL) /* if active output file */ { printf("CD command seen while output file is open."); printf("\n Prematurely closing output file %s\n", filename); fclose(fout); fout = NULL; sizereport(); /* say file size written */ filecnt = 0L; nbfilesize = 0L; } discard(18); break; /* attempt at decoding dir info */ c = buf[148]; /* path terminator char */ j = strlen(&buf[162]); /* get asciiz string for (i = 0; i < j; i++) nbpath[i] = buf[162 + i]; for (i = 0; i < j; i++) if (nbpath[j-i] == c) { nbpath[j-i] = '\0'; break; } fixpath(); /* correct NBACKUP path syntax */ if (chdir(filepath) != 0) /* if NBACKUP path not found */ makepath(); /* then go make it */ printf("Change from dir %s\n", filepath); discard(230); break; case 4: discard(18); break; case 5: discard(18); /* unknown, maybe Bindery info */ break; /* for above directory */ case 6: /* open output file */ if (fout != NULL) { printf("Warning: Open File command seen while file"); printf(" %s is open. Closing it.\n", filename); fclose(fout); fout = NULL; sizereport(); nbfilesize = 0L; filecnt = 0L; } pathlen = buf[156]; /* path length */ for (i = 0; i < pathlen; i++) nbpath[i] = buf[162 + i]; nbpath[pathlen] = '\0'; fixpath(); /* correct NBACKUP path syntax */ makepath(); /* then go make needed dirs */ chdir(initcwd); /* come back home afterward */ fnamelen = buf[35] & 0x0f; strcpy(filename, filepath); j = strlen(filename); if (filename[j-1] != BSLASH) { /* one backslash, please */ strcat(filename, "\\"); j++; } i = 0; /* beginning of filename if (buf[36] == BSLASH) i++; /* strip leading back slash */ for ( ; i < fnamelen; i++) /* file's name from NBACKUP */ filename[j + i] = buf[36+i]; filename[j + fnamelen] = '\0'; if ((fout = fopen(filename, "wb")) == NULL) printf("Error -- cannot create file %s\n",filename); else printf("%s", filename); nbfilesize = *(long *)(&buf[72]); /* NBACKUP file size */ filecnt = 0L; discard(296); /* most of the header */ for (i = 0; i < 10; i++) /* really 301-302 overall */ if (buf[i] != '\0') break; /* scan off nulls */ discard(i + 2); /* discard int precusor to real file */ filecnt = 0L; break; case 7: /* close output file */ discard(22); if (fout == NULL) { printf("Close File command, no open output file.\n"); break; } fflush(fout); fclose(fout); sizereport(); filecnt = 0L; nbfilesize = 0L; filename[0] = '\0'; fout = NULL; break; case 8: /* start of new NBACKUP file */ printf("\nStarting archive input file %s\n", infile); discard(118); break; case 9: /* end of all NBACKUP files */ printf("Completed last archive file in the set\n"); return; default: printf("Observed and ignored unknown command code %u\n", (int) buf[0]); discard(18); break; } /* end case */ continue; } while (0 == 0); } /* Return the pointer to the first match of character c in the buffer, or NULL if not found. */ char * str1char(c) unsigned char c; { register int i; for (i = 0; i < buflen; i++) if (buf[i] == c) return (&buf[i]); return ((char *) NULL); } /* Write "chunk" bytes to the fout file, if it's open. Move remining buffer contents to the start of the buffer and attempt filling the remainder of the buffer with freshly read data. */ int writeout(chunk) int chunk; { register int i; int count; if (fout == NULL) return (discard(chunk)); /* for closed files */ count = chunk; if ((long)chunk > (nbfilesize - filecnt)) /* if write beyond NBACKUP*/ count = nbfilesize - filecnt; /* limit to length of file */ count = fwrite(buf, 1, count, fout); /* do NBACKUP limit */ filecnt += count; /* chunk - count == discarded */ for (i = 0; i < BUFLEN - chunk; i++) buf[i] = buf[i + chunk]; buflen -= chunk; /* includes discards */ return (readin()); /* refill buffer */ } /* Discard chunk bytes. */ int discard(chunk) int chunk; { register int i; if (chunk > buflen) chunk = buflen; for (i = 0; i < BUFLEN - chunk; i++) buf[i] = buf[i + chunk]; buflen -= chunk; return (readin()); /* refill buffer */ } /* Read from disk to fill the BUFLEN buffer. Return current buffer length in buflen. Ensure non-data after buflen bytes are null filled. */ int readin() { register int i, count = 0; if (buflen < 0) buflen = 0; if (buflen == BUFLEN) return (buflen); if (fin != NULL) { count = fread(&buf[buflen], 1, BUFLEN-buflen, fin); buflen += count; } return (buflen); } /* Open new NBACKUP source file. Return buflen chars if opened, else -1 */ int newinput() { register int i, count = 0; retry: count = strlen(infile); /* get end of archive filename */ infile[count - 1]++; /* l.sig. digit, for next file */ for (i = count-1; i > count-4; i--) /* do carry/rollover */ { if (infile[i] == ('9'+1)) infile[i] = 'A'; /* hex */ else if (infile[i] == ('F'+1)) { infile[i] = '0'; /* roll over*/ if (i != -3) infile[i-1]++; /* carry */ } } for (i = 2; i <= 0; i--) /* test for ending set */ { if (seqlimit[i] > infile[count - 4 + i]) break; if (seqlimit[i] < infile[count - 4 + i]) return (-1); /* beyond limit, say 0 to end pgm */ } if ((fin = fopen(infile, "rb")) == NULL) { printf("Cannot open archive file %s\n", infile); writeout(buflen); if (fout != NULL) { fclose(fout); sizereport(); fout = NULL; filecnt = 0L; } buflen = 0; goto retry; /* try for another file */ } count = fread(&buf[buflen], 1, BUFLEN-buflen, fin); buflen += count; return (buflen); } /* Change NBACKUP path syntax of SYS:ITEM/ITEM to DOS' \ITEM\ITEM */ void fixpath() { register int i, len; register char *p, c; p = filepath; /* output form */ len = strlen(nbpath); /* input NBACKUP form */ for (i = 0 ; i < len; i++) { c = nbpath[i]; if (c == '/') c = BSLASH; /* DOS not Unix, please */ *p++ = c; if (c == ':') /* remove SYS: -isms */ { p = filepath; /* SYS: means use root */ *p++ = BSLASH; } } *p = '\0'; /* end on a null */ } /* Create directories as needed based on path information from NBACKUP. Expects fixpath() to have been called first for Novell to DOS fixes. Returns after constructing backslash-null terminated final full path. */ void makepath() { char temp[65]; char *getcwd(); register char *p1, *p2; register int i, k, len, start; int strcmp(); p1 = filepath; /* path given by NBACKUP record after fixpath() */ len = strlen(p1); /* its length */ start = 0; while (filepath[start] == BSLASH) /* remove leading bslashes */ start++; chdir(outroot); /* start at our output root */ strcpy(temp, outroot); /* backslash+null terminated outroot*/ p2 = &temp[strlen(temp)]; /* look at null terminator */ do { /* do each \dir phrase separately */ i = start; while ((i < len) && (p1[i] != BSLASH) && (p1[i] != '\0')) *p2++ = p1[i++]; /* add new NBACKUP dir to temp */ *p2 = '\0'; start = i + 1; k = 0; if (chdir(temp) != 0) { k = mkdir(temp); /* if not exist, construct it */ if (k == 0) /* 0 means success */ { printf("Creating directory %s\n", temp); chdir(temp); } } p2 = temp + strlen(temp); /* look at null terminator */ if (*(p2 - 1) != BSLASH) /* look for existing bslash*/ *p2++ = BSLASH; /* insert backslash */ *p2 = '\0'; /* terminator */ } while ((start < len) && (k == 0)) ; /* do all tokens */ if (k != 0) printf("Error -- unable to make dir %s\n", temp); if (k == 0) strcpy(filepath, temp); } /* Report output file size */ void sizereport() { if (filecnt == nbfilesize) /* we wrote what NBACKUP said */ printf(" successfully completed (%lu bytes)\n", filecnt); else printf(" WARNING -- INCOMPLETE, wrote %lu of %lu bytes\n", filecnt, nbfilesize); }