+
+
+
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Minus.gif" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Minus.gif"
new file mode 100644
index 0000000..17751cb
Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Minus.gif" differ
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Plus.gif" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Plus.gif"
new file mode 100644
index 0000000..f6009ca
Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Plus.gif" differ
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/MTF_100a.pdf" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/MTF_100a.pdf"
new file mode 100644
index 0000000..9f1debd
Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/MTF_100a.pdf" differ
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/CONTACT.HTM" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/CONTACT.HTM"
new file mode 100644
index 0000000..19fc4ec
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/CONTACT.HTM"
@@ -0,0 +1,41 @@
+
+
+
Contact Info
+You are welcome (encouraged) to send me personal email regarding topics discussed on
+my web pages. If you don't mind please include at least you country and possibly
+you state/country/territory. With some of my more obscure software I get mail from
+all over and its interesting tracking the source. Please send email
+to the following address:
+will_kranz at SoftHome.net
+Sorry, you've got to cut and paste the address above. I've
+been getting way too much junk email and have been told to
+remove automatic HTML addresses such as
+someone because
+automated systems scan web pages looking for these. My user name and
+server above are separated by the string " at " which must be replace by '@'
+to create a valid address. Sorry to
+make you do a little extra work, but maybe this will cut down
+on the amount of spam I recieve.
+If you are emailing me some solicitation I have not requested, please
+don't. I've registered with Junkbusters and will attempt to
+charge people who do send me unsolicited solicitations. See the
+registered terms and conditions
+under which I will accept unsolicited email of this nature.
+You won't find my phone number or postal address on these pages,
+but I feel the same way about
+telephone and mail solicitations.
+
+
+
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/GET_FOFF.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/GET_FOFF.C"
new file mode 100644
index 0000000..b2b97d3
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/GET_FOFF.C"
@@ -0,0 +1,87 @@
+/* get_foff.c 3/30/05 developed in ffndstr.c then moved here.
+ see tst_foff.c for a driver routine, plan use as generic
+ module for HAS_INT64 code in dumpit and ntbkup.c
+ Always display in hex wwith the leading 0x
+ Always look for leading 'h' in string for hex.
+ if there do as hex and check for HAS_INT64, otherwise only
+ treat as a long.
+
+ The MSVC runtime library docs say printf (and presumably scanf)
+ accepts I64 for int64 as well as l for long in format spec
+
+ gcc's libc C seems to have no such built in support so just
+ truncate quadword decimal input for these.
+*/
+disp_foffset(FOFFSET pos)
+{
+ unsigned long *dw = (unsigned long *)&pos;
+ printf("0x");
+#ifdef HAS_INT64
+ if(*(dw+1) > 0)
+ printf("%lx",*(dw+1)); // print high order portion
+#endif
+ printf("%lx",*dw);
+
+}
+
+FOFFSET get_foffset(char *str,FOFFSET *pos)
+{
+ int suc=0,cnt=0,i,nul;
+ char *num=str, ch = toupper(*str);
+ *pos = 0; // clear it, may only set low bits below
+ if(ch == 'H')
+ {
+ str++; // skip initial q
+ while(*(str+cnt) != 0)
+ {
+ ch = toupper(*(str+cnt));
+ if((ch >= '0' && ch <= '9') ||
+ (ch >= 'A' && ch <= 'F') )
+ cnt++;
+ else
+ break;
+ }
+// if(cnt > 16 just truncate like sscanf(%lx) does
+ if(cnt <= 8)
+ i = 0;
+ else
+ i = cnt - 8;
+ suc = sscanf(str+i,"%lx",(unsigned long *)pos); // set low order DWORD
+
+ cnt = i; // chars remaining
+ if(cnt && suc) // more than 8 bytes in input string
+ {
+#ifndef HAS_INT64
+ printf("\nWarning, input truncated to 32 bits\n");
+#else
+ nul = i;
+ ch = *(str+nul);
+ *(str+nul) = 0; // temporarily terminate
+ if(cnt <= 8)
+ i = 0;
+ else
+ i = cnt -8;
+ suc = sscanf(str+i,"%lx",(unsigned long *)pos+1); // set high order DWORD
+ *(str+nul) = ch; // restore str
+ if(i & suc)
+ printf("\nWarning, input truncated to 64 bits\n");
+#endif
+ }
+ }
+ else
+ {
+#if defined(HAS_INT64) && defined(_WIN32)
+ suc = sscanf(str,"%I64u",pos); // unsigned long long
+#else // truncate to 32 bits
+ suc = sscanf(str,"%lu",(unsigned long *)pos);// unsigned long
+#endif
+ }
+ if(suc == 0)
+ {
+ printf("\nfailed to parse string: %s",num);
+ exit(-1);
+ }
+ else
+ return(*pos);
+}
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.CL" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.CL"
new file mode 100644
index 0000000..a87fe8e
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.CL"
@@ -0,0 +1,11 @@
+#macro defines for microsofts MSC 6.0
+#same as VC 5.0 execpt for the DELIM
+EXE = exe
+OBJ = obj
+DEL = del
+DELIM = ;
+CC = cl /c /AL
+LNK = link
+
+#for debugging /Zi recommended in CC, and /CO required in LNK
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.QCL" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.QCL"
new file mode 100644
index 0000000..2ee517e
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.QCL"
@@ -0,0 +1,8 @@
+#macro defines for Microsofts QC25 environment
+EXE = exe
+OBJ = obj
+DEL = del
+DELIM = ;
+CC = qcl /c /AL
+LNK = qlink
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.VC" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.VC"
new file mode 100644
index 0000000..8428c06
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.VC"
@@ -0,0 +1,8 @@
+#macro defines for Microsofts VC 5.0 command line compiler
+EXE = exe
+OBJ = obj
+DEL = del
+CC = cl /c
+LNK = link
+
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBACKUP.HTM" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBACKUP.HTM"
new file mode 100644
index 0000000..f9eb152
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBACKUP.HTM"
@@ -0,0 +1,1531 @@
+
+
+
+
+
+
+
+
+
Win9x & XP & NT MSBackUp File Format and Data Recovery
+
+Beginning in 2004 the amount of email I recieve on this subject has decreased.
+I suspect most people who might get burned by these 'great' MS applications have
+resolved their problems. I am happy to give anyone a couple of free support
+emails. I have also charged a few people for extracting files from, or
+'fixing' corrupted backup files. In general this has worked well, and I include
+some acknowledgements, however I've also been stiffed for several
+days work so if you need something like this I now have a money in front policy.
+
+I also spend half my time at the end of a dirt road with a slow telephone link.
+Please to not send large attachements without discussing it in advance. Recently
+a well intentioned user sent me some *.jpg screen images of his output, this would
+have been painful if I had been on the dirt road when I recieved them.
+End of Rant
+
+
+I took a look at Win32 Backup Programs after coming across a FreeDos
+project.
+The project page above says there were several incompatible
+MSDOS versions of BACKUP. Apparently none of these
+are compatible with the Win32 programs described below, and
+worse I currently know of three mutually incompatible Win32
+backup programs: NT, XP, and Win 2000 use NTBackup while Win95 and Win98
+used different versions of MSBackup. By default NTBackup produces a *.BKF
+file and both versions of MSBackup produce *.QIC files,
+but the *.QIC internals are sufficiently different that these files
+can only be used with the program with which
+they were created. Thanks again guys, that's real helpful in a
+backup program!
+
+I couldn't resist looking at the more recent NTBackup
+issues and also created a program that will restore files from these archives.
+It turns out both my programs are useful for data recovery. Apparently passing
+one of these backup archives around a network can cause minnor corruption such
+that even the originating program no longer recognizes the file, yet my
+more simplistic approaches are perfectly happy recovering the data.
+
+Additional information is available from
+Microsoft
+which confirms the NTBackUp incompatibility with Win32 *.QIC formats.
+It seems odd, but this Microsoft Knowledge Base article seems
+to say the NT and XP versions of NTBackUp are different. Apparently none
+of the new NTBackUp programs support QIC tapes
+as the Win9x versions did. The NTBackUp version that comes with XP (but not NT)
+can read an uncompressed *.QIC file image. The Microsoft article says
+NTBackUp uses a different file compression algorithm. So if you compressed
+your archive or are running NT, Microsoft can't help you.
+Another third party article about
+XP Home
+edition suggests it is not so easy to find or install NTBackUp if you
+don't have the Professional edition.(Search for NTBackUp on page above to
+find the relevant section)
+
+The general solution for recovering data from a *.QIC archive seems to be
+you should take the file to a Win9x machine running the OS the archive was
+created with and use its standard MSBackUp program if you despreately need
+to recover the older archive. This is not bad advise, just not very convenient.
+
+I have not been trapped by this problem. I either backup the original files to CDROM, or
+use a Unix compatible TAR program whose format hasn't change significantly
+in something like 20 years. However I was curious about the data
+file format and I did the exploration described below. At this time
+I understand the general layout of both *.QIC and *.BKF files. I only
+know how to decompress *.QIC files. As of 2004 I'm making both executable
+versions and source code available under the GNU Public License.
+Binary distributions for
+MSDOS, WIN32, and Linux are now freely available. However I still
+ask that you give me feedback so I can improve these programs
+(ie fix bugs!).
+
+
+I don't imagine there will be a lot of interest in this, but if
+you have gotten this far and are interested please send me
+a note.
+I'd be interested in looking at your sample archive data if these programs
+do not work properly. In particular I do not have either WinXP nor NT
+so I can not create my own *.BKF archives.
+People have sent me several small uncompressed *.BKF archives as samples
+that I've used to verify this work. Please DO NOT send large files
+without checking, I don't always have a high speed connection nor
+a lot of disk space. Its apparently not possible to activate
+the compression option when backing up to a file, and I only deal with *.BKF
+files.
+
+
+
WARNING: all information presented below are GUESSES done by inspection.
+Use this information and the associated programs at your own risk.
+I do NOT claim they are correct nor accept liability for your use of them.
+
+Microsoft Backup
+Version 4.10.1397
+Distributed by: Seagate Software, Inc.
+Copyright 1998 Seagate Software, Inc. All rights reserved
+
+
+
+Note after about 10 days looking at this WinME format, I find differences
+between it and my Win95 BackUp programs *.qic files.
+The major and minor version numbers from the VTBL header from the WinME program
+in the *.qic files discussed below are 0x5341 and 0x49.
+
+
+
+The Win9x version of MSBackUp clearly has some relationship to the QIC format
+specifications which
+are available on-line. I'd done some work on this previously as I
+have some QIC80 tape drives. However I quickly found the MSBackUp
+*.qic file format is significantly different. I am using the
+structure definitions below to attempt to describe what I have
+learned regarding the *.qic file format. See the msqic.h file in the
+source code archive for the most recent information.
+
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+
+
+// from pp9 of QIC113G,
+struct qic_vtbl {
+BYTE tag[4]; // should be 'VTBL'
+DWORD nseg; // # of logical segments
+char desc[44];
+DWORD date; // date and time created
+BYTE flag; // bitmap
+BYTE seq; // multi catridge sequence #
+WORD rev_major,rev_minor; // revision numberrs
+BYTE vres[14]; // reserved for vendor extensions
+DWORD start,end; // physical QFA block numbers, in WIN98 and WINME
+ // these point to start volume data and dir segments
+BYTE passwd[8]; // if not used, start with a 0 byte
+DWORD dirSz, // size of file set directory region in bytes
+ dataSz[2]; // total size of data region in bytes
+BYTE OSver[2]; // major and minor #
+BYTE sdrv[16]; // source drive volume lable
+BYTE ldev, // logical dev file set originated from
+ res, // should be 0
+ comp, // compression bitmap, 0 if not used
+ OStype,
+ res2[2]; // more reserved stuff
+};
+
+/* If its a compressed volume there will be cseg_head
+ records ahead of each segment (in both catalog and data segments).
+ The first immediately follows the Volume Table area
+ For the sake of argument, lets assume per QIC133 segments are
+ supposed to be < 32K, ie seg_sz high order bit isn't required.
+ Its used as a flag bit, set to indicate raw data. IE do not
+ decompress this segment. Use seg_sz to jump to the
+ next segment header.
+*/
+#define SEG_SZ 0x7400 // Segment Size = blocking factor for *.QIC file
+#define RAW_SEG 0x8000 // flag for a raw data segment
+
+struct cseg_head {
+DWORD cum_sz, // cumlative uncompressed bytes preceeding this segment
+ cum_sz_hi;// normally zero. High order DWORD of above for > 4Gb
+WORD seg_sz; // physical bytes in this segment, offset to next header
+ // typically 40% -50% of bytes which will be decompressed
+};
+
+
+// see section 7.1.3 of QIC 113 Spec for directory info, does not match below
+
+// DATA_SIG only if in data region
+struct ms_dir_fixed {
+WORD rec_len; // only valid in dir set
+DWORD ndx[2]; // thought this was quad word pointer to data? apparently not
+ // ndx[0] varies, ndx[1] = 0, was unknow[8]
+ // in data section always seems to be 0xffffffff
+WORD path_len, // @ 0xA # path chars, exits in catalog and data section
+ // however path chars only present in data section
+ unknww1; // 0xA always?
+BYTE flag; // flag bytes
+WORD unknww2; // 0x7 always?
+DWORD file_len; // @ 0x11 # bytes in original file
+BYTE unknwb1[20], // was flags[0x18] but attrib at flags[20]
+ attrib,
+ unknwb2[3];
+DWORD c_datetime, // created
+ unknwl1, // always 0xFFFFFFFF?
+ a_datetime, // accessed
+ unknwl2, // always 0xFFFFFFFF?
+ m_datetime, // modified, as shown in DOS
+ unknwl3; // so can be expanded? always 0xFFFFFFFF?
+WORD nm_len; // length of the long variable length name
+};
+// var length name, case sensitive, unicode
+
+struct ms_dir_fixed2 {
+BYTE unkwn1[13]; // was [0x15]; this region fairly constant
+DWORD var1; // these vars change file to file
+DWORD var2;
+WORD nm_len; // length of 2nd, short ie DOS, variable length name
+};
+
+// var length name, always upper case => DOS, unicode
+// if in data region path follows, not in directory set
+// var length path per ms_dir_fixed.path_len, unicode
+
+// BOTH ms_dir_fix* structures must be packed!
+
+/* Bitmap defines for flags
+ below seem to work with my current ms_dir_fixed.flag
+ don't seem to match QIC113G
+
+ Note there are a LOT of undefined bits below. Wonder what they might be?
+*/
+#define SUBDIR 0x1 // this is a directory entry, not a file
+#define EMPTYDIR 0x2 // this marks an empty sub-directory
+#define DIRLAST 0x8 // last entry in this directory
+#define DIREND 0x30 // last entry in entire volume directory
+#define DAT_SIG 0x33CC33CCL // signature at start of Data Segment
+#define EDAT_SIG 0x66996699L // just before start of data file
+/* EDAT_SIG is immediately followed by a WORD before the
+ actual data stream starts. No idea what this is, in my
+ sample files its been 0x7. I ignore it
+*/
+
+#define BASEYR 1970 // uses unix base year and elapsed seconds in time
+
+
+Starting from the top, the file begins with a standard QIC113
+volume table per struct qic_vtbl. There is at least one VTBL tag entry
+followed by a second Microsoft specific MDID tag and data block
+to terminate the volume table. Most of the fields conform to
+the QIC specification, however bit 5 of the flag byte is not set
+although the directory set does seem to follow the data region.
+I'm not clear if the size fields conform or not (can't tell
+from my reading of the spec). dataSz looks like the number
+of uncompressed bytes used for the data region. dirSz is
+the number of bytes from the start of the directory to the
+end of the file. The volume table header area normally
+contains 256 bytes, one VTBL region and one MDID region. However if
+multiple drives are contained in the archive there is one VTBL for
+each drive. In a compressed volume these records are immediately followed by
+10 bytes for the first struct cseg_head.
+
+Note to find the beginning of the data or directory (catalog) segements
+use the qic_vtbl start and end fields. Subtracting 3 from each of
+these produces the number of SEG_SZ segments before the start of
+the data. Ie a value of 3 implies data starts immediately after the
+MDID region. See also the discussion of how this is done for
+WIN95 archives. The WIN95 logic works for
+single volume WIN98 and WINME archives.
+
+Following this header region where entries have a 128 byte block size,
+the remainder of the file is broken up into segments of 0x7400 bytes (SEG_SZ).
+All Win98/ME archives I've seen do not compress the 1st segment,
+nor the catalog segment(s), thus these files will always be at least
+59648 bytes long. Data compression is discussed
+in detail later, but is done on a segment by segment basis.
+
+The first data region segment immediately follows the VTBL header region.
+In a compressed volume the sum of the bytes in the VTBL header region
++ dataSz generally takes one well past EOF, ie dataSz always
+represents the uncompressed data length. Without compression, for my sample
+files, the VTBL header region size + dataSz falls significantly
+short of the beginning of the directory set because the last segment
+is rarely full. In Win98/ME the dirSz is the physical size of the segment(s),
+but in Win95 it is the amount of space used in the segment(s).
+
+The time stamps MSQIC displays for the individual files
+in the archive look correct. However the time stamp for the VTBL
+creation time, data, was off by two years into the future. I added
+a corrective fudge factor, but its odd. In the process of
+trying to figure out this time stamp issue and address why
+MSBackUp won't recognize my output files, I looked at the
+second volume table region with tag = 'MDID'. A sample dump
+follows:
+
+It appears to be a series of id tags followed by ascii strings,
+except that the string terminator is 0xB0. My best guess at
+the id tags is as follows:
+
+Tag used as
+MDID - vtbl tag
+MediumID - unique 19 decimal digits for identification
+VR - version? always 0100
+CS - ? followed by 4 hex bytes
+FM - ? always followed by '2'? format?
+UL - user label, ascii input string
+DT - datetime of archive creation as 8 hex bytes
+
+The DT string seems to be in the same format as the file time stamps.
+It matches the time stamp of the archive file within +/- 10 hours.
+The difference is still puzzling, but much closer than the
+VTBL.datetime. Possibly just a timezone issue? The CS tag
+is a puzzle, it varies without any logic I can determine.
+Nor can I figure out how the Unique MediumID value is generated.
+Either of these could be the problem in getting MSBackUp to
+recognize my files, but I just don't get it.
+
+
+
+Each directory entry contains two or more variable length strings. The general format
+is similar to the QIC113 specification but the internal structure
+is significantly different as indicated by my ms_dir_fix* structures
+above. Every field with a name starting with 'unknw' has an unknown
+function, ie I have a significant amount to learn! But the ones
+I do understand should be enough to reconstruct a file at its original
+location. The directory contains repeats of the following:
+
+ { struct ms_dir_fixed,
+ variable length long file name,
+ struct ms_dir_fixed2,
+ variable length short (MSDOS) file name,
+ a path string (may be empty)
+ }
+
+The discussion below relates to how this information is arranged in
+the directory (catalog) region of the file. As mentioned its slightly
+different in the data region of the file where it is duplicated.
+The first field, rec_len, in ms_dir_fixed, is the length of
+the entire variable length block so one can read the entire
+thing or break the read up into segments. ms_dir_fixed contains most
+of the key file information and is followed by the long filename.
+This is in turn followed by ms_dir_fixed2 and the MSDOS 8.3 format
+sort file name. Both structures contain a nm_len field
+which is the number of data bytes in the variable length string which immediately follows
+the structure. This length field may be zero as it seems to be for the
+root directory. The names appear to be in unicode, in my samples
+every second byte in the name is zero. They are not NUL terminated.
+As indicated in the structure definition above, the path string at the
+end only exists in the data region, not the catalog region. path_len
+may also be zero representing an empty string.
+
+The key data is in the ms_dir_fixed structure. Its flag field is
+a bitmap which uses the defines {SUBDIR,LASTDIR,ENDDIR} above.
+The meaning is consistent with the QIC113 specification, but the
+bit values are different. As indicated in the structure definition
+the file length, time stamps {creation, modification, last access},
+and file attributes have been identified. The attribute byte
+matches the standard MSDOS attributes. The names in the directory
+listing appear to be in alphbetic sorted order (case sensitive) based on
+the long file name for each subdirectoy containing a file.
+I have not yet identified the link from the
+directory to the individual files in the data section. However the
+order in the directory set seems to match the order in the data region.
+Ie one can determine the files location in the data region by an appropriate
+summation of the file and header bytes. Also note that a compressed archive
+file has struct cseg_head records embedded in its directory region even though
+the region is not compressed (the RAW_SEG bit is set in the seg_sz field).
+
+The structure of the data region is similar, but each entry additionally
+contains the DAT_SIG and EDAT_SIG signature fields and if the entry
+represents a file rather than a directory is followed by the file data.
+Per the comment following EDAT_SIG above, there
+is also a WORD value between the EDAT_SIG and file data that I ignore.
+Note there is more information in the directory set fields than the data set
+fields for equivant structures. My guess is that additional information is added as
+the file is opened and read. Then MSBackUp updates the structures
+and writes them to the directory set (the catalog in MSBackUp terms).
+In particluar for Win98/ME the first 10
+bytes of the ms_fixed_dir are always 0xFF. Therefore if one were
+to attempt to directly parse the data set regions (ie for emergency
+recovery per msqic -x) the rec_len and ndx[] fields are not valid. Also as mentioned
+above, the data region is the only place the data path string occurs,
+when looking at the directory set region one must generate the path from
+preceeding subdir entries.
+
+I had someone point out that MSBACKUP *.qic files can span multiple volumes
+of media. The person I talked to had 3 Zip disks in a single archive. I
+have duplicated this behavior with floppy disks. I only tried it once, and
+created an archive that filled the 1st floppy and spilled over onto a 2nd.
+There as a *.qic file on each that was consistent, ie MSQIC recognized them.
+The first had the flags set indicating "Volume spans multiple cartidges"
+and the second did not. The catalog for the first only included the files
+that were in the archive on that disk. The catalog for the second included
+all the files in both archive files. It is not apparent to me how one would
+know which of the files in this catalog were on the prior disk. Again one
+expects this information to be in some of the fields I don't understand,
+but I have no idea where!
+
+
+For that matter, I expect a linkage from the catalog to the data region,
+but just don't see it. There are a number of fields in the struct ms_dir_fixed
+that I do not understand, but nothing jumps out at me regarding this linkage.
+
+
+
+
+The owner of this file says he believes it was created
+"as the output of a disaster recovery as oppopsed to a straight files only backup".
+The system on which it was created is long gone.
+I don't find this option in my version of MSBackUp, but haven't explored it in detail.
+I'm putting this note here in case someone else has this experience. More information
+about how to such a file is created would be interesting.
+
+For the record I can't reproduce the example in QIC122B, I fear
+there may be some typos. I have now decompressed a couple sample
+archives and have feedback from others who have used my alorithm
+successfully, so I'm pretty sure its correct. Interestingly enough the first
+segment( roughtly 30Kb) in Win98 is not compressed, but the other segments are.
+Conversely Win95 MSBackup seems to compress all the segments in a volume.
+
+A compressed archive is broken up into a series of segments.
+I'm not sure why it was done this way as files often cross
+the segment boundries. This allows one to decompress subsections
+on a segment by segment basis in large archives.
+Each segment is preceeded by the 10
+byte cseg_head record shown above. These only occur in
+compressed files. The first cseg_head record immediately follows the
+Volume table normally at file byte offset 256 for Win98 backups (assuming
+there is only one volume) and byte offset
+128 for Win96 backups. The cseg_head records form a linked
+list of the segments as there are often some unused bytes
+at the end of a segment which must be skipped.
+
+In a compressed archive there will be a cseg_head record at the start
+of each segment, at increments of SEG_SZ (0x7400) following the end
+of the volume table. As mentioned above, the RAW_SEG flag in the
+cseg_head.seg_sz field indicates if the data has been compressed.
+The first segment of a data region and the catalog segments are not compressed.
+One obtains the physical size of the segment data by masking the high
+order RAW_SEG bit in the seg_sz field. In the data region, the size will be
+0x73F6 if the entire segment is used (10 bytes are used by the cseg_head).
+There is always a terminating cseg_head in the data region with seg_sz = 0.
+The preceeding cseg_head.seg_sz will be @#60 0x73ED as the terminating header
+is always inserted inside a segment and does not occur at the 0x7400 block
+boundry. In small archive the first data region cseg_head may point to this
+terminating header. If and apparently only if ther terminator occurs in
+the first uncompressed segment the first word of the cseg_head.cum_sz field
+contains the byte offset from the end of this word to the next cseg_head.
+One only needs to know this because normally the terminating cseg_head.cum_sz
+is zero, but not when it occurs in the first data segment.
+When you find the terminating header, you are done with the data section of the file.
+If you were decompressing the segments as they were traversed, you should have
+decompressed the number of bytes indicated in the VTBL.dataSz
+field.
+
+Note in a compressed file the catalog segments also have cseg_head records at
+the start of each segment, however there is not a terminating record for the
+catalog section. All catalog records seem to have a seg_sz = 0xF7F6.
+The actual data length is determined by the flags in the catalog data.
+
+I am able to decode each segment independantly
+using a slightly modified QIC122B algorithm. With my
+sample text files its about a 2:1 compression factor, not
+bad for a fairly simple algorithm. I've found one major difference
+between the publish specification and practical application.
+When copying a string of bytes
+from the history buffer, the example in QIC122B uses an
+offset to the start of the region to copy which is an absolute
+index into the history buffer. MSBackUp apparently uses
+a relative offset from the current position in the history buffer
+to the start of the data to copy. Care must be taken to
+wrap this relative offset back to the end of the history
+buffer when required (basically a modulo operation to prevent
+a negative index). With this system an offset
+of zero is still the termination marker for the algorithm.
+As with many compression algorithms, this depends on the
+nearby data so you have to decode segments as a unified block,
+you can't jump in and start in the middle of a segment. One can
+get a handle on which segments contain which files by comparing
+the file set directory with the cseg_head records as records in
+the directory and data regions occur in the same order. The point
+is that if one has a large archive and were desperate you could
+unpack portions of it rather than the entire archive. However I
+suggest you just get a large spare drive and decompress the hole
+thing if you need to play around with the data.
+
+
+When one ventures into NTBackUp *.BKF files one quickly runs into the 4GB boundry.
+My MSDOS binary for NTBKUP is limited to 4GB files, but the WIN32 and Linux versions
+use long longs for 64 bit file offsets. The part that becomes a little ugly
+is displaying such an offset. Although GNU's gcc supports long long format
+specifications for printf() its not portable (at least not to MSVC 5.0).
+For simplicity long longs are only displayed in hex in these programs.
+
+I'm working on a FAT32 system so I can't test these large file options.
+If you have a *.BKF file > 4GB and are interested in this project, please test
+it with my code. This code is known to work with smaller archives
+on FAT32 systems. Please do NOT send me a sample archive, I have no place to put it!
+
+Once I knew the appropriate name I also found a brief summary
+
+article by Microsoft that confirms NTBackUp files are MTF compatible.
+However no links are provided to the supporting MTF documentation.
+More recently, 1/27/2004, a
+JAVA MTF Reader was released by Michael Bauer. I have not looked at this
+yet, but it seems like a nice cross platform solution to the problem.
+
+My structures and trial program only dealt with a semi-functional subset of this
+MTF specification. The portions of the MTF document I've reviewed so far
+appear to be VERY similar to the *.bkf archive samples I've seen.
+Its not exactly light reading but seems to cover everything.
+A few high points below:
+
+MTF_DB_HDR - section 5.1 describes a common header for the main file
+ blocks. This maps to my tag_head, although I didn't know
+ what a lot of it was about. See the block ID table, for
+ blocks that conform to the common header block checksum rule.
+
+MTF_STREAM_HDR - section 6.1 and
+Type 1 Media Based Catalog - section 7.3 describe the TFDD catalog region.
+ Note I treat this like the other common blocks, but apparently
+ it is technically a data stream. In the *.bkf files I've seen
+ its position is padded to an 0x400 byte boundry, but there
+ seems to be nothing in the specification to require this.
+
+Format Logical Block - section 3.4. The logical block has been 0x400 for
+ the *.bkf I've seen. The specification says 0x200 is also a valid
+ size and it is defined in the tape header, MTF_TAPE.format.
+
+MTF_TAPE.major version = 1 in *.bkf files I've looked at.
+MTF_SSET.minnor version = 0 These match the version numbers for this document.
+
+MTF_DIRB - section 5.2.4 There is one of these for each directory
+ backed up on the media. All MTF_FILE blocks following
+ an MTF_DIRB are located in this directory. They are often LARGE!
+
+MTF_FILE - section 5.2.5, maps to my xp_file
+
+MTF_TAPE_ADDRESS - section 4.2 clarifys how to locate the variable
+ length data sections. I had identified the length field,
+ but not the offset as its the same in all my *.bkf examples.
+
+OS specific data is covered in Appendix A. Most MTF_DB_HDRs contain a
+pointer to some sort of OS specific data. This spec talks about NT
+specific data for OS ID = 14 and OS Versions 0 and 1. The *.bkf files
+I've seen are OS ID 14 with OS Version 2 which is not covered. However
+the attribute and short name fields seem to be in the same locations
+(I have not tried to figure out what is different in Version 2).
+
+
+
+
+After releasing this, the author of JMTF and I have both discovered that
+some regions beginning with the FILE tag do not contain the STAN stream
+record. In July of 2004 Geoff Nordli emailed me a sample *.bkf file that
+explains this behavior. It appears that empty files are stored without a
+STAN stream, so as of version 1.06, if no STAN stream is detected the file
+is created with no data which matches the normal behavior of NTBackUp.
+
+Given the MTF information I now see how the entire file can be represented
+as a linked list of data elements. Each main block common header has a 'next event'
+offset. This either points to the next main block header, or the start of a
+chain of stream headers which are linked together in a similar mannor.
+The last stream header in a chain points to the next main block header.
+I added some of this to my proof of concept program which enhanced its
+ability to traverse *.bkf files. In particular finding the start of
+each individual file's data is now totally generic and I see there is normally
+a checksum after the file data which can be used for validation.
+
+The original section titled "Obsolete Structure Analysis" describing
+my reverse engineering has been deleted as the document above is much better.
+The only point worth making is that the *.QIC concept of 30Kb segments seems
+to have been dropped making the files a little more compact.
+
+
+I believe it decompresses my Win9x MSBackup compressed
+archives correctly, ie the data recovered looks like
+the original. My original goal was do this decompression and then let
+the NTBackUp that comes with WinXP to manipulate entire archives
+decompressed with this program. However my WinME MSBackUp doesn't
+recognize the decompressed file MSQIC produces so I doubt NTBackup will either.
+It must be a very subtle difference as I have done byte comparisons
+between MSQIC's output and what MSBackUp produces without compression
+and do not see the difference! Its close BUT being off by 1%
+is the same as being off by a mile. Sigh.
+
+However MSQIC stands alone and can extract individual files or groups
+of files from a sub-directory from either
+compressed or uncompressed archives. It can also decompress
+the entire archive, and will recognize it afterwards.
+Its useful for recovering files you desperately need, or as a testing tool for
+examining an archives internals. Or if you are a brave soul, try the -p or @
+options to restore large blocks of the directory tree stored in the archive.
+The command line options are shown below:
+
+MSQIC Ver 1.11 compiled for OS_STR
+Copyright 2003 William T. Kranz
+...
+
+msqic <file> [@] -p [-x] [-t] [-v] [-s{c|d}#] [-f{d|e|s}] [-d] [-r]
+ @<cmd> to extract directories based on command file
+-p<path> extract all FILES from ONE path in directory tree
+-x to extract file, nm, using paths in tree structure
+-t[ds] to display catalog as tree, d => directory only, s => with segment info
+-v just display VTBL and exit
+-fd find file id 0x33cc33cc in data
+-fe find file id 0x66996699 in data
+-fs find & display compressed file segments
+-sc# force start catalog (directory set) at hex offset
+-sd# force start data region at hex offset
+-D to decompress archive and write output to dcomp.out
+-d##[:#] to decompress a segment(s) starting at hex offset ## in file
+ use optional hex :cnt to decompress cnt contiguous segments
+-r[filter] attempt raw file data recovery, use -sd to set data region start
+ use optional filter string, ie *.txt, to limit hits
+
+
+An archive file name must be supplied or you get the display above.
+Under MSDOS it must be a 8.3 style short filename.
+MSDOS systems also only display 8.3 style paths while Linux and Win32
+systems can handle long file names. The OS_STR above indicates
+the Operating System the program was compiled for: MSDOS, WIN32, Unix, or CYGWIN.
+
+
+By default when run with just a
+file name argument the catalog is displayed with each of the
+files attributes and the file names truncated to 18 characters so they fit on one line.
+Adding -v or -t changes the display as indicated above.
+
+The first options listed, {@, -p, -x, -t}, all depend on a valid catalog
+in the *.QIC archive and will fail if it doesn't exist. They parse the
+catalog dynamically allocating the directory tree in memory. Large archives
+and systems with limited memory could have problems with these. Alternatively
+try the -r option that does not depend on the catalog nor directory tree.
+
+The -t option attempts to display the full file name with
+indentation below the associated sub-directories to indicate the tree structure
+on the disk when the backup was created. There are two additional options which
+may follow -t. A 'd' only displays subdirectores (see @ option below) without
+the files. An 's' appends numbers after the file name which are the
+segment:offset to help you locate a specific file in a compressed segment.
+
+The -x option allows extracting a single file from the archive to the current
+directory. It depends on the paths shown via -t, and on all but MSDOS systems
+the path and file name search is case sensitive. File time stamps and the
+read/write permission attribute are preserved as of version 1.08.
+
+
+The -s options allow forcing the file position used for the data and directory regions.
+This is required if your file has a corrupted VTBL region (which occurs more often
+than you might think) but other parts of the file are in tact. Typically you use
+the -f option below to find appropriate values for the -s options.
+
+The -f options search the archive file, display hits, and then exit.
+A compressed file is a series of compressed segments, each preceeded by
+a struct cseg_head. These segment locations are listed via
+the -fs option. -fs accepts an optional start address for the search,
+and its only a best guess which doesn't work well unless there are several
+segments in the chain. Look at the output to be sure it makes sense.
+After finding the beginning of one or more
+compressed segments, they can be individually decompressed with
+the -d### option (note use a hex offset as displayed by -fs, prior to version
+1.09 this was decimal and you had to add 10 bytes to skip the cseg_head).
+
+The -D option attempts to decompress an entire compressed file, or for
+Win98 and WinME multi-volume archives, one of the volumes. Using the
+-s option in conjunction can help when the VTBL is corrupted.
+
+The -x option will do a case sensitive search
+for a path specification and extract the file if found.
+
+The bulk of the code was written before I discovered that WinME (but not Win95)
+produces a separate VTBL entry for each drive. If decompressing an
+archive created by Win98/ME which included multiple drives with MSQIC, you
+can currently only access one at a time. At startup you will be
+prompted to select the drive of interest which will the be labled 'ROOT'
+in the tree display. Otherwise the operations are the same. If doing
+data recovery on a file with Multiple VTBL entries for the different drives,
+you will find its broken up into separate
+sections as if the data and directory regions from separate archives
+were concatonated together. MSQIC lets you work with one section
+at a time, you can use the -v option to see where each of the
+sections in the file is located.
+
+Since version 1.10 some interactive prompts have been added at startup
+when a valid VTBL region can't be found. These ask you to use the
+-s options to set the regions of interest and confirm the archive
+type as there are differences between Win95 and the latter Win98 and WinME
+format.
+
+
+The @ and -p options were introduced with version 1.07. The -p option
+is a special case of the more general @ options. A command file path
+specification must immediately follow the '@' symbol. This file controls
+extraction of files at the directory level and has the following format:
+One line for each source directory to be extracted.
+The line must contain a source directory specification for the archive followed
+by a redirection path to the destination disk
+separated from the source by white space. With the -p option only one
+source path can be specified, and the destination is always the current directory.
+
+Use the -td option to get a list of directories in the archive. I recommend
+redirecting this output to a file and editing it to be sure you don't have
+an upper/lower case error. Use it to create the desired command file or source path.
+The current implimentation forces the use of the OS specific path
+delimiter, DOS and Windows use '\' and Unix uses '/'.
+If the source directory path ends with a delimiter ('\' or '/')
+only the files in this directory will be extracted. If the path ends
+with '*' all sub-directory contents below this directory will also
+be extracted to corresponding
+directories below the destination directory. If the source path ends with
+'+' the program will attempt to create subdirectories before doing
+the extraction. To be parsed the source path must end with the appropriate OS
+specific delimitor, '*' or '+'.
+With the @ option, a redirection path must also be added on the same line.
+Be sure to add some spaces to separate it from the source specification and to
+add quotes around any paths containing white space. The redirection
+path will be subsituted for the source path when the file(s) and optional
+sub-directories are extracted.
+If you have spaces in a path specification you must enclose the complete
+path in quotes. Note I do special processing for the OS specific destination
+path ".\" and "./", these map to the system's current drive and directory.
+Further more there are some odd side effects with the -p option
+when processing a quoted path that ends in '\' as required by MSDOS and WIN32.
+See the examples and discussion page.
+
+The following sample Windows file would extract all files from \temp in the archive
+to the same directory on the current source drive. The second line says extract
+all files and sub-directories in or below \dos in the archive to "d:\old dos".
+The last line extracts all files from \test in the archive to the current directory.
+
+In the example above I've assumed these files were generated on Win98 systems
+and that the path separators are '\'. When used on a linux system you should use
+the redirect path with appropriate '/' separators.
+The default is to
+write to the current drive, but the redirect path is free format
+and should support MSDOS style drive specifiers as well as mounted linux drives.
+File time stamps and ownership are not preserved on extraction.
+Destination directories WILL NOT be created, they must exist for the
+extraction to work.
+
+Possibly the most confusing
+thing is the Win95 versus Win98 format issue. In Win95 the root node has
+a name preceeding the separator, while in Win98 its embedded in the MDID
+and does not occur in the archive tree display.
+I force the top level name 'ROOT' for for the Win98 systems.
+Again the -t option will
+show this and you should use the same format when generating your command file.
+
+
+I've also added a command line option, -p to perform
+directory based extraction similar to one line in the @cmd_file.
+Supply a source path in the archive terminated by '\','/' or '*'. The
+system will supply a redirect path to the current directory where the
+files from the source directory are extracted. If a recursive
+copy is requested with the '*' terminator the files in all the
+source subdirectories will be copied as long as the required subdirectory
+exists under the current directory. Note that entire an entire drive
+in a Win98 style archive could be restored with the following command
+if all the sub-directories existed and the root directory, \, was the
+current directory:
+-pROOT*
+
+A word of caution about using the @ option with compressed files.
+Its not a very efficient algorithm. If you are doing a lot of small
+files you would be well advised to decompress the entire archive
+and then do the extraction. When extracting from a compressed archive
+the segment(s) containing EACH file are decompressed to extract the
+file. If there are 10 files in a given segment, it will be decompressed
+10 times during the extraction process! One could do this more
+efficiently, but at this point that isn't the way its being done.
+
+I only own and have looked at two OS specific versions of *.qic archives and
+both were slightly different, just enough so the data had to be handled
+differently during extraction.
+Several other people have used this code successfully, but that's not to say
+there isn't a 3rd or 4th variation out there somewhere.
+Another limitation of the original proof of concept code was that
+it used 32 bit signed longs for file lengths and seeks
+within the backup archive file. The data structures appear
+to be designed for 64 bit values, but for simplicity I have
+ignored the four high order bytes. See the large file issues
+discussion above.
+In Version 1.03, I fixed the logic for archives where the dirSz is greater than 29696
+(aproximately 250 files).
+
+
+The time stamps my program displays for the individual files
+in the archive look correct. However for some reason the time stamp
+for the VTBL creation time was off by two years into the future.
+A corrective fudge factor has been added, but don't put complete faith
+in this value.
+
+In discussions with people who have tried this program for data recovery
+I came across some whose VTBL section were corrupted (all zeros). The
+command line options -sd# and -sc# were added to override the VTBL and
+work in this case. For Win95 use -sd128 and for Win98 use -sd256 for
+the start of the data region. For both, the catalog normally starts at the
+beginning of the last segment and is not compressed, see the
+details above.
+The changes in MSQIC 1.11 were prompted by Darryl Hunt from Austrialia wrote
+regarding a data recovery problem. His archive was mostly in tact, but since
+VTBL region was corrupt MSQIC did not know it was compressed. I
+added some interactive prompts allow the user to set some of the VTBL fields to
+correct the problem. See the end of the MSQIC sample output
+for more detail on this. If your archive is not corrupted in this mannor the changes
+should be transparent.
+
+
+
+
+The -r option was added to recover data from damaged files. If your archive
+is not damaged the -x or @ options maybe the method of choice, but you are
+welcome to try -r. It only works with a decompressed file, or sections
+of one produced with either the -D or -d options. However it does not
+use the catalog, it directly parses the information in the data region
+and is intended for people whose catalog has been corrupted/destroyed.
+This is a bit tricky as the file length is not included in the data region
+headers (the field is always 0xFFFFFFFF). For each file found the program
+searches ahead for what looks like a another file header block and estimates
+the length. This seems to work, but is chancey. Use the -sd option to
+control where the search starts. If you have a damaged or non-existant VTBL
+header you will also need to use the -sc option to set the end of the data
+region. For a group of decompressed segments extracted from a file use
+-sd0 to start at the beginning and -sc### where the numbers represent the
+length of the datafile. Note that there is no next header for the last file
+in an archive or archive segment. Appropriate use of the -sc parameter can
+limit garbage appended to the end of a file, however in an intact archive the
+'garbage' is typically NUL bytes inserted to pad to the end of the segment.
+The optional filter selects files by case
+insensitive matches with the MSDOS (short) file names. Note that during
+extraction all files are written to the current directory.
+
+When using the -r option
+there are a couple interactive prompts. In particular it asks for
+the data format (Win95 versus Win98), whether you want to display
+or extract files, and if extracting whether you want to interactively select
+the files to be extracted. It needs to know which OS version created the
+archive to estimate the file lengths (Win95 has 12 extra bytes after the
+file's data) but the default of Win98 will display files from either type
+of archive correctly. If it thinks you have choosen unwisely it will tell
+you as a warning.
+
+Another 'feature' introduced in Version 1.03 is -sv. This is not documented in the
+standard usage display as its a fairly dangerous option. It allows the
+use of a data file to create or overwrite the VTBL region. Sample data files and
+instruction available on request, but this is only recommended in an emergency.
+Its the only operation preformed by this program that writes to the archive.
+
+Ralf Westram from Germany has been a consistent gadfly, friend, and great
+tester. He has so far discovered some signed/unsigned errors in my
+4 GB file access implementation and bugs in my parsing of the catalog for compressed
+files when they exceeded one segment (Ver < 1.03), and the directory tree generation
+(Ver < 1.04) which affects file extraction via the -x option. He also
+introduced me to the CYGWIN Linux environment under windows.
+Thanks for the tips Ralf.
+
+
+
+NTBKUP Ver 1.07c compiled for OS_STR with MAX_PATH = 100
+Copyright 2003 William T. Kranz
+...
+
+usage: ntbkup <file name> [-x[filter]] [-l] [-p] [@<cmd>] [-c] [-d] [-f] [-j#] [-s#] [-t] [-v]
+ -x to unconditionally extract all files matching filter
+ -l<path> where full case sensitive path limits extract
+ -p<path> recursive path based directory extract
+ @<cmd> use path based extract and redirection command file
+ all extracts use [filter] from -x, default filter is *.*
+ -c to display catalog regions for TAG == TFDD
+ -d display directory tree from raw data region
+ -f<tag>[:start:[len]] to find 4 char tag with optional start pos and length
+ -j#[:#] jump to start position in file for data recovery (modulo 0x400)
+ optionally follow start offset with :# for an end offset
+ -s# to limit options above to a particular SET by #
+ -t[:start[:end]] display tags only from start to end offset
+ -v to set verbose mode
+
+
+The <file name> argument is required, without it you get the display above.
+Under MSDOS the file name must be a 8.3 style short filename.
+The OS_STR above indicates the Operating System the program was compiled for:
+MSDOS, WIN32, Unix, or CYGWIN.
+
+
+Currently NTBKUP will by default display all the tags in the source archive file.
+The -c option lists the files in the archive by parsing the catalog region(s).
+The rest of the options ignore the catalog and parse (or control parsing in) the data
+region directly which can be useful for data recovery.
+The -d option only lists the directories (DIRB tags)
+and can be useful to determine the paths to be used with the -l, -p, or @ commands.
+As indicated below the -x has an optional filter argument.
+When -x is used without -p or -l it ignores the directories and extracts all files
+in the archive matching the filter. You can use -l with a path description
+to limit file extraction to a single directory (this path description should not
+include a drive specification, to select a drive use the -s option).
+The -p option is similar but also extracts and optionally creates subdirectories
+below the specified archive directory.
+The -l and -p options are mutually exclusive.
+In both cases the extraction starts in the current directory. You can additionally
+define a filter to be used in these directories with -x. This filter specification
+is also used with the @<cmd> file. The default filter is *.* for all files.
+Since version 1.02 the time stamp and attribute (READ/WRITE status) is preserved
+when the files are extracted. A command file can also be used to specify
+a series of paths for the extraction with the @ command, its format is discussed below.
+Under MSDOS the short, 8.3, filename is used when the file is extracted.
+Under WIN32 and Linux the full file name is used.
+The -s# option may be used in multi-set volumes to restrict the operations
+above to a single set. Run NTBKUP with no options to see the set numbers.
+If -s is not used, operations are performed on all sets in the archive.
+
+Although its not indicated in the program output above because there isn't
+space on the console screen, the options -f, -j, and -t may all be
+followed by the letter 'h'. By default the arguments are treated as
+unsigned integers (ie decimal). Appending either an 'h' or an 'H' ahead
+of the numeric value interpruts the value as hexidecimal. Ie -jA would
+fail and be ignored, but -jhA would jump to hex offset 0xA before starting
+to process the file.
+
+The -p and @ options are new with version 1.02 (and relatively untested so use
+prudence). They mimic logic developed in MSQIC. A preliminary pass is made through
+the data file and the names and location of all the directoires (DIRB regions) in
+the archive are stored in a dynamically allocated tree structure. Then this tree is
+searched for the user supplied directory strings. Unlike -l, a drive specification
+is required, ie "C:". The source path is the name stored in the archive, these paths
+may be viewed with the -d option. A source path
+description is valid if it has one of 3 terminators. 1) the OS specific directory
+terminator, '/' in unix and '\' in an MSDOS or WIN32 environment. This denotes only
+this directory should be extracted. 2) A '*' which denotes that this directory and
+all those below it in the directory tree should be extracted if, and only if, the
+matching directory exists on the target system. 3) A '+' is similar to '*', but
+will attempt to create sub directories as required.
+With the -p option you just specify the source path and extraction starts at the
+current directory. With option 2 or 3 above the archive subdirectories below the
+source directory will be copied to corresponding location below the current directory
+on the target system.
+With the @<cmd> option, the <cmd> represents a command file name. This
+ascii text file will be opened and read line by line. Each line should contain
+an archive source path as described above, one or more spaces, ' ', and a
+redirection path. Rather than starting in the current directory, the redirection
+path is used for the starting directory. For proper parsing the redirection path
+must end with the OS specific delimitor, '\' or '/'.
+
+Note that if any of the directory names in a path include spaces, ' ', then
+the entire path must be quoted. Further more there are some odd side effects with the -p
+option when processing a quoted path that ends in '\' as required by MSDOS and WIN32.
+See the examples and discussion page.
+
+CAUTION: Due to the way this program evolved, files are extracted by changing
+the current directory on the target machine to a desired directory and then
+doing the extraction in this directory. This has a couple implications. First,
+your current directory is likely to change after using the @ command.
+Second, this has not been extensively tested. I've tried to trap errors, but
+if it gets out of sync during extraction from a large archive you could have a
+real mess. Be cautious with use of the '+' terminator enabling directory
+creation. Try some small sub paths to be sure you know how it works before attempting
+to extract the entire archive as indicated in my example. I've done this successfully,
+but you might not be so fortunate.
+
+Potential file command lines are shown in quotes below followed by a comment
+which should NOT be in the file:
+
+"c:+ \croot\" create & extact all archive files & directories at or below C:\
+ in the archive to \croot and below on the current drive
+
+"c:\temp\ d:\temp\" extract files from archive c:\temp to d:\temp
+
+"c:\csource* \csource" extact files and directories from archive at pr below
+ c:\csource to matching directorys on current drive if
+ the directory exists, otherwise skip over it.
+
+
+I've now talked to a couple people with corrupted *.BKF files. I was
+surprised, but apparently NTBackUp isn't always happy with archive files
+after they have been created. Especially if they have been passed around
+networks. Still exploring this, but added a few more command line
+options. The -j and -f options should not be used together. The
+program terminates after -f. You can force it to start at a
+particular offset with -j. -f will search for identifiers at ALL
+file offsets rather than just block boundries. You must enter 4 ascii
+characters for the desired tag, you may optionally enter a ':' delimited start
+offset and byte length for the search area. Use -fh if hex offsets
+are to be used for these qualifiers. Note I was sloppy about this,
+the maximum value you can enter here is a 32 bit unsigned integer. If
+you have a file that requires 64 bit offsets you won't be able to
+specify start or end points beyond 4GB.
+
+It appears that the lack of file compression and the advent of
+large disks causes people to make what I concider very large
+backup archives, see large file issues.
+Dan and I worked together in March of 2004 to debug my -p and @
+logic. Ultimately he was able to extract 4GB from the directory
+tree in a 20GB *.bkf file with a single -p command line option.
+
+Warning in the Spring of 2007 I was contacted by two different people who
+were attempting to recover files with NTBackUp and discovered to their dismay that
+it completely ignores unicode in the file names when it tires to extract them. Sorry
+I was pretty lazy about this. The *.BKF file format supports the use of unicode file names,
+but I ignore the 16 bit information and just strip the 8 bit char portion in the
+function getn_unicode() in ntbkup.c. This could be corrected fairly easily if you have
+a compiler that supports unicode in its open() function (my MSVC 5.0 does not, but I believe linux gcc with glibc-2.2 does). At the moment I have no plan to revisit this myself, but will post here if someone
+else takes the effort.
+
+
+
+
+I am making these executable and the associated source code freely available. The source
+code is distributed under the
+GNU General Public License in the hope of promting
+free, expandable software. The source code for both MSQIC and NTBKUP
+are availabe in a single archive. Binary OS specific distributions are
+available in separate archives. All distributions are LHA compatible as
+that has become my standard since it was one of the first early freely available
+archive systems. As a favor to Angelique I'm also making the raw *.exe for
+the WIN32 versions directly available if you don't want to expand the archive
+and are runing a 32 bit version of Microsoft Windows.
+Current versions: MSQIC V 1.12 NTBkUp V 1.07c
+
+
MSBackup Source archive for QIC and BKF files (see environments below)
+
MSBackup
+ archive of binaries for MSDOS (via Microsoft QuickC 2.5)
+
MSBackup
+ archive of binaries for WIN32 (via Microsoft Visual C 5.0)
+
MSBackup
+ archive of binaries for Linux V2.4 (via gcc 2.91.66)
+
+I own/have compilers for each of the environments above and have attempted
+to test these programs in these environments. I've also verified the Linux
+binaries run under Linux 2.2. For reasons I currently don't understand the
+binaries give a 'segmentation fault' under the Linux 2.0 kernel and the
+version of make (3.76.1) with this kernel doesn't like my msbkup.lin make file.
+Version 3.79 of GNU make is perfectly happy. I can manually build the programs
+with my 2.0.36 kernel, but gcc 2.7.2.3 does not appear to support the 64 bit
+file offset required by NTBKUP (although it can be built without this if the
+error check in main() is removed). There are also a couple of
+gcc ports. Ralf introduced me to CYGWIN
+which provides a GNU/Linux environment under Win32. I have not provided
+CYGWIN binaries, but the source compiles and runs with gcc 3.3.1 in the
+CYGWIN 1.3.x environment.
+This implies it would also compile with
+DJGPP but I have not tested this. I'd greatly appreciate some
+input from a DJGPP user on this issue.
+
+Note that although this is 'free' software I'm very willing
+to accept job offers or cash contributions if this
+does something useful for you. To date exactly one person has
+given me a $50 Ebay gift certificate (I would have preferred something at
+the local liquor store). Three people have paid me for a few hours actively
+consulting on issues related to corrupted archives, and one person
+(Thanks Jack) has failed to send me their promised compensation.
+One person paid for a minnor modification and recompilation of NTBKUP.
+This works out to about $0.15 per hour for my effort.
+I guess freeware isn't very cost effective...
+Version history:
+02/07/04 Initial source release was MSQIC 1.09 and NTBKUP 1.02
+02/08/04 MSQIC 1.09a, correct minnor error preserving file attributes with -r option
+
+03/19/04 MSQIC 1.10 and NTBKUP 1.03,
+ In MSQIC correct error in file attribute preservation with -r option
+ and add interactive option to continue if the VTBL is corrupt by
+ using the -sc and -sd options
+ In NTBKUP correct some errors in -p and @ options so it correctly
+ creates the directory tree with the '+' terminator.
+ In both versions change get_paths() logic so correctly handles a
+ quoted destination path when the @ option is used.
+ Todo: Add Unix code supplied by Berend de Boer to auto configure make file
+04/29/04 MSQIC 1.11
+ Add more interactive logic for the case where the VTBL is not
+ found to recreate enough of this information that an entire volume
+ may be decompressed via -D.
+
+05/17/04 NTBKUP 1.04,
+ Allow volume name to be a network share. Previously assumed it was
+ a drive letter. Also change filter parsing to allow more than one
+ period in file names per user requests.
+
+06/03/04 NTBKUP 1.05, minnor change to display logic, removes display
+ of directory parsing during -d, -p options in the hope that error
+ messages will be noticed.
+
+07/08/04 NTBKUP 1.06, create empty files when there is no file data
+ associated with a FILE region (ie STAN stream is not found). Remove
+ 'do_file' error message which was displayed in this case.
+
+04/06/2005 NTBKUP 1.07, fix some bugs in parsing of command line arguments
+ for -s and -f arguments which had stopped working. Add
+ optional end specification for the -j command line argument.
+ Allow specification of 64 bit long integer file offsets on the command
+ line for those OS which support files larger than 4Gb. Warning
+ it appears the -p and @ options may not create empty directories when
+ recreating a sub-tree. I doubt I have the energy to chase this, but
+ be careful, empty nodes may be missing in re-created trees.
+
+
+12/30/2006 NTBKUP 1.07c, was created and is now offered as the default WIN32
+ download.
+
+06/01/2007 MSQIC 1.12, was notified by a user the Win95 backups are have a
+ slightly different format, add -st95 command line option to force this mode.
+
+01/22/2008 Update source file distribution archive to reflect changes in
+ NTBKUP 1.07c.
+
+
+
+I think I'll give it a rest for a while, but if you find a bug
+or want to contribute some information please
+contact me. I'll attempt to fix bugs, and look at enhancements,
+but I want to move on to something else. I'd rather provide a link to
+your enhancement and let you support it yourself.
+
+I was contacted in January about moving the project to SourcForge, and
+this will probably happen soon. When it does I will provide a link here.
+
+
+
Acknowledgments
+I want to give thanks to a couple people who gave significant
+help or feedback during the development cycle:
+
+Ralf Westram for significant testing and bug detection on MSQIC
+Alan Stewart for making the MTF_100a specification available
+Wolfgang Keller for showing me where to find MTF_100a, and for
+ providing sample *.bkf test files
+Phillip Luebke for sending me my first set of *.bkf test files
+Berend de Boer for the Unix auto configuration logic
+Dan Boyne for identifing bugs in NTBKUP and testing my fixes
+Darryl Hunt for sample output and motivation for MSQIC 1.11 enhancements
+Peter Feldman for sample NTBackup archives containing network shares per NTBKUP 1.04 enhancement
+Geoff Nordli for clarification of the empty file issue per NTBKUP 1.06
+
+Also a couple people who followed through after offering to pay me to help
+recover data. Thanks to a nice gentalman from Canada who wishes to remain
+annoymous and Gregg Paquette who is starting a computer consulting business . The latest version of NTBKUP, 1.07c,
+was requested and funded by Andrew Deatherage of symcas.com, thanks for the check!
+
+
+
+
+
+
+ QIC80 (quarter inch tape) standards (interesting but NOT *.QIC archive format)
+ Search for QIC122B.pdf which defines the *.QIC compression algorithm
+
+ Alan Stewart's MTF web page
+ includes MTF_100a.PDF specification and source code
+ for an MTF Tape reader (as opposed to files)
+
+Reading DOS/Windows Backup tapes James Klaas contacted me. He wants to start a
+project to look at some of the vendor specific variations used on QIC80 tape drives.
+
+
+
+
+
\ No newline at end of file
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.LIN" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.LIN"
new file mode 100644
index 0000000..17ac793
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.LIN"
@@ -0,0 +1,47 @@
+# make build file for msbackup programs
+# in a single compiler environment just include the correct file
+
+#macro defines for gcc environment
+OS_STR = UNIX
+EXE =
+OBJ = o
+DEL = rm
+DELIM =
+CC = gcc -c -ggdb
+LNK = gcc -ggdb
+
+
+
+#dependancies below generic for Unix gcc and the Evil Empire's cl
+#remove match, its not unix compatible right now...
+
+ALL: msqic ntbkup
+
+CLEAN:
+ $(DEL) *.$(OBJ)
+
+#*.QIC reader
+msqic: msqic.$(OBJ) qicdcomp.$(OBJ) msqicrcv.$(OBJ)
+ $(LNK) -o msqic msqic.$(OBJ) qicdcomp.$(OBJ) msqicrcv.$(OBJ) $(DELIM)
+
+msqic.$(OBJ): msqic.c msqic.h
+ $(CC) msqic.c
+
+msqicrcv.$(OBJ): msqicrcv.c msqic.h
+ $(CC) msqicrcv.c
+
+qicdcomp.$(OBJ): qicdcomp.c msqic.h
+ $(CC) qicdcomp.c
+
+
+#*.BKF reader
+ntbkup: ntbkup.$(OBJ) nttree.$(OBJ)
+ $(LNK) -o ntbkup ntbkup.$(OBJ) nttree.$(OBJ) $(DELIM)
+
+ntbkup.$(OBJ): ntbkup.c ntbkup.h
+ $(CC) -D_FILE_OFFSET_BITS=64 ntbkup.c
+
+
+nttree.$(OBJ): nttree.c ntbkup.h
+ $(CC) -D_FILE_OFFSET_BITS=64 nttree.c
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.MAK" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.MAK"
new file mode 100644
index 0000000..61e0ae0
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.MAK"
@@ -0,0 +1,48 @@
+#Microsoft Nmake build file for msbackup programs
+#OS = {MSDOS or WIN32} in environment
+# in a single compiler environment just include the correct file
+
+!if defined(OS)
+! if "$(OS)" == "MSDOS"
+!include "macro.qcl"
+! else
+!include "macro.vc"
+! endif
+!endif
+
+
+#dependancies below generic for Unix gcc and the Evil Empire's qcl/cl
+
+ALL: msqic.$(EXE) ntbkup.$(EXE)
+
+CLEAN:
+ $(DEL) *.$(OBJ)
+# delete *.exe cause can conflict with CYGWIN
+ $(DEL) ntbkup.$(EXE)
+ $(DEL) msqic.$(EXE)
+
+
+#*.QIC reader
+msqic.$(EXE): msqic.$(OBJ) qicdcomp.$(OBJ) msqicrcv.$(OBJ)
+ $(LNK) msqic qicdcomp msqicrcv $(DELIM)
+
+msqic.$(OBJ): msqic.c msqic.h
+ $(CC) msqic.c
+
+msqicrcv.$(OBJ): msqicrcv.c msqic.h
+ $(CC) msqicrcv.c
+
+qicdcomp.$(OBJ): qicdcomp.c msqic.h
+ $(CC) qicdcomp.c
+
+#*.BKF reader
+ntbkup.$(EXE): ntbkup.$(OBJ) nttree.$(OBJ)
+ $(LNK) ntbkup.$(OBJ) nttree.$(OBJ) $(DELIM)
+
+ntbkup.$(OBJ): ntbkup.c ntbkup.h
+ $(CC) ntbkup.c
+
+nttree.$(OBJ): nttree.c ntbkup.h
+ $(CC) nttree.c
+
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.C"
new file mode 100644
index 0000000..c2a638b
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.C"
@@ -0,0 +1,1408 @@
+/* msqic.c *.QIC file reader for Win9x MSBackUp images
+ Check http://www.fpns.net/willy/msbackup.htm for updates.
+ contact info above or via snailmail: p.o. box 333, bradford, nh 03221
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+-----------------------------------------------------------------------------------
+ History:
+ Look at MSBackup fall of 2003
+ see also earlier work in qic80.c doing raw read of QIC80 trakker tapes
+ with dd in Linux with ftape.
+
+ Note below is abridged, see msqic8.c for full cumulative history.
+
+
+9/30/03 copy qic80.c to this file and modify.
+ Turns out there isn't a lot of similarity...
+ Worked on it pretty much full time for a week
+
+10/9/03 oh dear, win95 version has different VTBL structure
+ and some differences in how to get to catalog.
+ see notes in dcomp.txt. Add display of major, minnor versions.
+ Change how I get to the catalog (directory set).
+
+10/15/03 tweek for gcc, replace getchar() with fgetc(stdin)
+ gets() with fgets() add defines for strnicmp and strncmp
+ #pragma pack(1) doesn't work with GCC under linux 2.4
+ see changes in msqic.h
+ add some logic to be sure a final '\n' printed for linux
+
+11/29/03 current version on disk and in msqic.lzh is
+ MSQIC C 46,720 10-17-03 9:23a msqic.c
+ works reasonably well with a consistent file, try
+ adding -r recovery option.
+ most of file depends on VTBL but can override some of it
+ with -sd and -sc
+
+ add free_cat_list() so can use cat_list routines to
+ grow each node during recovery. Turned out didn't need this!
+
+ The path is in the data region, but the file length isn't
+ I guess at the length. Seems to work with uncompressed
+ WinME backup, messier with compressed and haven't dealt with
+ Win95 issues
+
+12/01/03 File up to 57kb, time to split again.
+ create msqicrcv.c for recovery and patch code.
+ Ie do_recover() and related routines
+ write_file() and next_data_sig()
+
+
+12/03/03 expand -fs a little
+ CAUTION need to verify this with multiple DRIVE WINME backups
+ it calls find_seg() to get location of catalog for segment of interest
+
+12/05/03 recompiled in linux, interesting -ss option was a little different.
+ Changed include file to pack in both versions.
+ Watch -t option, have email that says it blows up but can't reproduce.
+
+12/13/03 ok Ralf send a sample archive. Turns out it gives an error message
+ and exits 'gracefully'. Problem only seems to occur with a compressed
+ archive because there are seg_head regions in the catalog. His
+ sample file contains more than one directory segment and the seg_head
+ occurs in the middle of a file name. Sigh. Need to fix this
+ and strip them out when totally decompress a file. change
+ argument to do_decompress() to make this work
+
+12/21/03 look at Ralf's 4_dir example of how my -t option was WRONG
+ over a day on this! Go to version 1.04 and archive as msqicv4.lzh
+
+12/22/03 look into 4 Gb large file issues now. Try define 4GB
+ to force DWORD offsets via use of FOFFSET as my offset parameter
+ ignore large file, 64GB issue for now as FAT32 doesn't support
+ define _4GB at compile time to enable lseek4()
+ see FOFFSET and LSEEK_ERR defines to handle this semi portably
+ move tree routines and do_extract() to msqicrcv.c for space
+ Bump version to 1.05
+
+01/11/04 change arguments to do_extract() so pass ccat pointer
+ force name in tree to MSDOS name from fix2 when MSDOS defined
+ allows MSDOS to use MSDOS names, do_extract() doesn't care
+ NM_LEN was 128, bump to 255
+ Include Ralf's CYGWIN compiler defines
+
+01/20/04 backup up current version with included DELIM now
+ defined in msqic.h in place of '\\' for root.
+ Now try replacing with "ROOT"
+
+01/22/04 allow '+' as optional terminator for -p option
+ change args to do_extract()
+
+02/03/04 minnor change to find_seg() appears to have a logic error.
+ create msqic8.c with these changes.
+ Now change to version 1.09 and go to all hex display and input
+ as I have done in qicdcomp.c to prepare for possible 64 bit
+ large file version.
+ also change logic for -d so it uses start address of cseg_head
+ rather than data following it so now consistent with -fs option
+
+ oh ho, add start and end logic in get_vtbl() remove call to find_seg()
+02/12/04 minnor correction in msqicrcv.c, the -r option did the
+ timestamp/attrib preservation wrong cause I didn't save them before
+ the global buffer was overwritten.
+ Also add a bques() after getting the vtbl so one can attempt to
+ continue. Bump to version 1.10
+04/28/04 add interactive options to update memory image
+ of VTBL if it is known to be corrupt. See msqicrcv.c create_vtbl()
+ Add interactive prompt in find_seg() to optional look at next chain.
+ Add argument to bques() to allow a little customization,
+ ie returning -1 on ESC. Bump to version 1.11
+
+06/01/07 minnor update, bump version to 1.12 after copy current to msqic9.c
+ see changes for Win95 compression I just detected from email
+ add option -st95 for force to Win95 format, default is old behavior Win98 & ME
+ add argument to find_seg() to handle this
+
+----------------------
+
+deleted inserts from QIC113G.pdf to get more memory for ED
+
+*/
+
+#include
+#include
+#include
+#include // define memset
+#include
+#include /* for open() defines */
+
+#if defined(MSDOS) || defined(_WIN32)
+# ifdef MSDOS
+# define OS_STR "MSDOS"
+# else
+# define OS_STR "WIN32"
+# endif
+# include
+# include // for lseek
+#else
+# ifdef unix
+# include // for read/write etc.
+# define strnicmp strncasecmp
+# define stricmp strcasecmp
+# ifdef __CYGWIN__
+# define OS_STR "CYGWIN"
+#else
+# define OS_STR "LINUX"
+# endif
+# endif
+#endif
+
+#ifndef OS_STR
+# error Unknown build environment.
+#endif
+
+#include "msqic.h"
+
+
+#define BLK_SZ 1024
+BYTE buf[BLK_SZ]; // used by most read routines. Assumes
+ // all blocks smaller than this, checks if correct
+
+
+#define BASEYR 1970 // for Unix or 1904 for Mac just to be different!
+unsigned char mondays[] = {31,28,31,30,31,30,31,31,31,30,31,31};
+
+#ifdef _4GB
+#define MAXINC 0x7fffffffL
+/* FOFFSET is an unsigned long when _4GB is defined
+ this limits SEEK_CUR and SEEK_END to calls with off == 0
+ to get file size or current position, ie not a generic routine
+*/
+FOFFSET lseek4(int fp,FOFFSET off,int origin)
+{
+ FOFFSET roff,lret=LSEEK_ERR;
+ long inc;
+ if(origin == SEEK_CUR || origin == SEEK_END)
+ {
+ if(off != 0L)
+ printf(
+"\nfatal error lseek called with none zero offset for SEEK_CUR or SEEK_END");
+ else
+ return((FOFFSET)lseek(fp,0L,origin));
+ }
+ // at two calls may be required
+ else if(origin == SEEK_SET)
+ {
+ while(lret == LSEEK_ERR)
+ {
+ if(off <= MAXINC)
+ inc = off;
+ if((roff = lseek(fp,inc,origin)) == LSEEK_ERR)
+ break;
+ off -= inc;
+ if(off == 0)
+ lret = roff; // we are done
+ }
+ }
+ return(lret);
+}
+#endif
+
+// see pp 204 in runtime C lib ref for my C/C++ ref (used only in testing)
+void disp_dosdt(unsigned long date)
+{
+ int mon,day,yr,hour,min,sec;
+ sec = 2 *(date & 0x1f);
+ date = date >> 5;
+ min = (date & 0x3f);
+ date = date >> 6;
+ hour = date & 0x1f;
+ date = date >> 5;
+ day = date & 0x1f;
+ date = date >> 5;
+ mon = date & 0xf;
+ date = date >> 4;
+ yr = date+1980; // high order 7 bits
+ printf("%02d/%02d/%04d %02d:%02d:%02d",mon+1,day,yr,hour,min,sec);
+}
+
+/* may be an off by one day in below, but seems to work
+ should be able to do more directly!
+*/
+void disp_datetime(unsigned long date)
+{
+ unsigned short yr=BASEYR,mon=0,day,hour,min,sec;
+ char lpyr;
+ sec = date % 60;
+ date /=60;
+ min = date % 60;
+ date /= 60;
+ hour = date % 24;
+ date /= 24;
+ do {
+ if((yr % 100) == 0 || (yr % 4) != 0)
+ {
+ day = 365; // not a leap year
+ lpyr=0;
+ }
+ else
+ {
+ day = 366; // is a leap year
+ lpyr = 1;
+ }
+ if(date > day)
+ {
+ yr++;
+ date -= day;
+ }
+ } while (date > day);
+ day = date;
+
+ mondays[1] += lpyr;
+ while(mon < 12)
+ {
+ if(mondays[mon] >= day)
+ break;
+ else
+ day -= mondays[mon++];
+ }
+ mondays[1] -= lpyr;
+ printf("%02d/%02d/%04d %02d:%02d:%02d",mon+1,day,yr,hour,min,sec);
+}
+
+/* try to reverse process above. Currently only called from
+ msqicrcv.c create_vtbl()
+
+ assumes buf[] delimited with '/' and has format
+ "mon/day/year"
+
+ returns seconds elapsed since 1970 till date in string
+
+*/
+unsigned long mk_date(char *buf)
+{
+ unsigned long date=0;
+ int i,mon=0,day=0,yr=0;
+ char *ptr=buf;
+ if(sscanf(ptr,"%d",&mon) == 1 &&
+ (ptr = strchr(ptr,'/')) != NULL &&
+ sscanf(ptr+1,"%d",&day) == 1 &&
+ (ptr = strchr(ptr+1,'/')) != NULL &&
+ sscanf(ptr+1,"%d",&yr) == 1)
+ {
+ if(yr < 70) // probably only entered 2 digits
+ yr+= 2000;
+ if(mon > 0)
+ mon--; // make an index, ie zero based
+ if(yr >= BASEYR)
+ {
+ date = (yr - BASEYR) * 365; // total normal year days
+ date += (yr - BASEYR)/4;
+ if(yr >= 2000)
+ date--; // this adds number of leap years, ie extra days
+ for(i=0;ipseg buffer for compressed files
+ this is all done in cat_read() routine which replaces original read()
+ This strips the embedded cseg_head structures from a
+ compressed archives catalog region
+*/
+int cat_read(int fp,struct cseg_head *pseg,BYTE *buf,WORD sz)
+{
+ int i,rd,trd = 0,suc=0;
+ if(pseg == NULL) // just do a normal read operation (old behavior)
+ trd = read(fp,buf,sz);
+ else
+ while(suc == 0 && trd < sz)
+ {
+ rd = 0;
+ if(pseg->seg_sz == 0) // read next seg head
+ {
+ if((i = read(fp,pseg,sizeof(struct cseg_head))) != sizeof(struct cseg_head))
+ break; // error reading next seg head
+ pseg->seg_sz &= ~RAW_SEG; // clear raw bit
+ }
+ else if(pseg->seg_sz < sz)
+ { // must read last bytes in this segment
+ if((rd = read(fp,buf+trd,pseg->seg_sz)) != pseg->seg_sz)
+ suc++;
+ }
+ else if ((rd = read(fp,buf+trd,sz-trd)) != sz-trd)
+ suc++;
+ if(rd > 0)
+ {
+ trd += rd;
+ if(pseg->seg_sz < rd)
+ pseg->seg_sz = 0; // corrected, no longer seems to occur
+ pseg->seg_sz -= rd;
+ }
+ }
+ return(trd);
+}
+
+// returns bytes read on success and ptr->err == 0
+// or a value < 0 indicating error and ptr->err == bytes read
+// on success *ptr is initialized with directory fields
+int next_dir(int fp,struct dir_blk *ptr, BYTE *buf,WORD bsz,BYTE mode)
+{
+ int i,len,rd=0,suc=0;
+ char tst;
+ ptr->fix1 = NULL; ptr->fix2 = NULL;
+ ptr->nm1 = ptr->nm2 = NULL;
+ ptr->err = 0;
+
+ if((i=cat_read(fp,ptr->pseg,buf,sizeof(struct ms_dir_fixed))) !=
+ sizeof(struct ms_dir_fixed))
+ {
+ printf("\nerror reading 1st fixed region");
+ suc = -2;
+ }
+ else
+ {
+ ptr->fix1 = (struct ms_dir_fixed *)buf;
+ rd +=i;
+ i = 0; // name can be empty for root
+ tst = 0; // assume false, ignores rec_len
+ if(!(mode & W98) && // in catalog or W95 data mode, rec_len is valid
+ ptr->fix1->nm_len +rd > ptr->fix1->rec_len +2)
+ tst = 1;
+ if(ptr->fix1->nm_len > 0 && (ptr->fix1->nm_len > bsz - rd || tst ||
+ (i=cat_read(fp,ptr->pseg,buf+rd,ptr->fix1->nm_len)) != ptr->fix1->nm_len) )
+ {
+ printf("\nerror reading 1st fixed region");
+ suc = -3;
+ }
+ else
+ {
+ if( i > 0)
+ {
+ ptr->nm1 = buf+rd;
+ rd +=i;
+ }
+ len = sizeof(struct ms_dir_fixed2);
+ /* prior versions just warned if couldn't read all
+ of fixed2, but its now fatal error, don't know where we are!
+ */
+ if(len+rd > bsz || (i=cat_read(fp,ptr->pseg,buf+rd,(WORD)len)) != len)
+ {
+ printf("\nerror reading 2nd fixed region");
+ suc = -4;
+ }
+ else
+ {
+ ptr->fix2 = (struct ms_dir_fixed2 *)(buf + rd);
+ rd += i;
+ i = 0; // name can be empty for root
+ if(ptr->fix2->nm_len > 0)
+ {
+ tst = 0; // assume false, ignores rec_len
+ if(!(mode & W98) && // in catalog or W95 data mode, rec_len is valid
+ ptr->fix2->nm_len +rd > ptr->fix1->rec_len +2)
+ tst = 1;
+
+ if ( ptr->fix2->nm_len > bsz - rd || tst ||
+ (i=cat_read(fp,ptr->pseg,buf+rd,ptr->fix2->nm_len)) != ptr->fix2->nm_len)
+ {
+ printf("\nerror reading 2nd name");
+ suc = -5;
+ }
+ else if(i > 0)
+ {
+ ptr->nm2 = buf+rd;
+ rd +=i;
+ }
+ }
+ }
+ }
+
+ }
+ if(suc == 0)
+ suc = rd;
+ else
+ ptr->err = suc; // save it here for fun
+ return(suc);
+}
+
+
+
+#define RD_SZ (BLK_SZ -3)
+int do_search(int fp,DWORD targ)
+{
+ int len,i;
+ FOFFSET offset;
+ DWORD *lptr;
+ printf("\nsearching file for occurances of 0x%lx",targ);
+ if((offset = lseek(fp,0L,SEEK_CUR)) < LSEEK_ERR || read(fp,buf,3) != 3)
+ return(-1);
+ do {
+ if((len = read(fp,buf+3,RD_SZ)) != RD_SZ)
+ return(-1);
+ for(i=0;idesc,vtbl->nseg);
+// oh dear added 10/4, thought would be easy, but need to fudge? why?
+// 63072000 would be two years in seconds + 691200 is 8 days???
+// above is value of VTBL_DATE_FUDGE also used in msqicrcv.c:create_vtbl()
+ printf("\ncreated (aprox): "); disp_datetime(vtbl->date - VTBL_DATE_FUDGE);
+// printf("\ncreated: "); disp_dosdt(vtbl->date); way off yr=2519
+
+ printf("\nflag 0x%x:",vtbl->flag);
+ for(i=0,rd=1;i<5;i++)
+ {
+ if(rd & vtbl->flag)
+ printf("\n%s",flagbits[i]);
+ rd = rd << 1;
+ }
+ if((vtbl->flag & 1) == 0) // generic, not vendor specific
+ {
+ // fields after flag not valid if vendor specific
+
+ // ignore quad word, assume vtbl->dataSz[1] == 0
+ printf("\nversion: %0x:%0x",vtbl->rev_major,vtbl->rev_minor);
+ printf("\ndir size 0x%lx data size 0x%lx",
+ vtbl->dirSz, vtbl->dataSz[0]);
+ printf("\nQFA physical start block 0x%lx end block 0x%lx",
+ vtbl->start, vtbl->end );
+ printf("\ncompression byte 0x%x",vtbl->comp);
+ if(vtbl->comp & 0x80)
+ printf("\nCompression used, type 0x%x",vtbl->comp & 0x3f);
+ if(vtbl->OStype < 8)
+ printf("\nOS type: %s",OStype[vtbl->OStype]);
+ }
+}
+
+/* 1/16/04 update so ctrl controls display mode and
+ if it gets cur_pos from current file pos
+ or seeks to end param passed (note this is called from get_vtbl()
+
+ also change to FOFFSET and LSEEK_ERR for 4GB option
+ I don't think *end = -1 was ever used.
+ now done with ctrl & FIND_POS
+ 2/2/04 see avik.c for alternative search handles Avik's file
+ 2/29/04 If ctrl & DISPLAY add interactive prompt to allow skipping
+ ahead to next chain. Useful in multi-volume file.
+ 6/01/07 add arg ver to allow WIN95 format to be forced
+ add min_seg_sz test for WIN95
+*/
+
+int find_seg(int fp,char ctrl,FOFFSET *end,BYTE ver)
+{
+ char hit = 0,ques=0;
+ int i,tstsz,min_seg_sz,rd,sz,cnt=SEG_SZ/BLK_SZ +2; // 2 blocks more than a segment
+ FOFFSET cur_pos,tst_pos,adv;
+ struct cseg_head cseg,*pcseg;
+ tstsz = SEG_SZ - sizeof(struct cseg_head); // bytes in a full segment
+ min_seg_sz = tstsz - 0x20; // wild guess WIN95 uses actual bytes used
+ if(ctrl & FIND_POS) // equivalent to *end = -1 in prior version
+ cur_pos = lseek(fp,0L,SEEK_CUR); // where are we now
+ else
+ cur_pos = *end; // force position
+ if(cur_pos == LSEEK_ERR)
+ return(LSEEK_ERR);
+ if((tst_pos = lseek(fp,0L,SEEK_END)) != LSEEK_ERR && // found EOF
+ ((FOFFSET)cnt * BLK_SZ) +SEG_SZ > tst_pos)
+ cnt = (tst_pos - SEG_SZ)/BLK_SZ; // remainder fits in a segment
+ // won't try if there isn't more than one segment in file...
+
+ tst_pos = cur_pos; // will still duplicate old logic if no hit
+ while(hit==0 && cnt-- > 0 &&
+ (FOFFSET)lseek(fp,tst_pos,SEEK_SET) == tst_pos &&
+ (rd = read(fp,buf,BLK_SZ)) == BLK_SZ)
+ {
+ for(i = 0;iseg_sz & ~RAW_SEG) == tstsz ||
+ ((ver&WIN_MASK) == WIN95 && sz <= tstsz && sz >= min_seg_sz) || // less restrictive
+ (pcseg->cum_sz == 0 && pcseg->cum_sz_hi == 0 && sz > 0) )
+ {
+ adv = tst_pos + SEG_SZ +i; // should be ok WIN95 & WINME
+/*
+ adv = tst_pos+i;
+ 6/14/07 this must be wrong! think I just added, but not right, above already did most
+ if(ver == WIN95) // new 6/1/07 else default to prior Win98
+ adv += SEG_SZ;
+ else
+ adv += sz+sizeof(struct cseg_head);
+*/
+ // try a seek to next segment header
+ if((FOFFSET)lseek(fp,adv,SEEK_SET) != adv ||
+ (rd = read(fp,&cseg,sizeof(cseg))) != sizeof(cseg))
+ hit--; // fatal error testing for chain
+ else if((cseg.cum_sz == 0 && cseg.cum_sz_hi == 0 && pcseg->seg_sz == 0) ||
+ // special case below if 1st uncompressed seg, has cksum?
+ (pcseg->seg_sz & RAW_SEG && pcseg->cum_sz == 0 && sz < 0x7F6 &&
+ cseg.cum_sz & 0xFFFF0000 == 0 && cseg.cum_sz_hi == 0 &&
+ cseg.seg_sz == 0) ||
+
+ // last is a lose test, is cum_sz consistent?
+ (cseg.cum_sz >= pcseg->cum_sz + sz &&
+ cseg.cum_sz_hi == 0)) // asssume <= 4GB
+ hit++;
+ cur_pos = tst_pos+i;
+ }
+ }
+ tst_pos += BLK_SZ -sizeof(struct cseg_head);
+ }
+ if(ctrl & DISPLAY && hit > 0)
+ {
+ printf("\nFound what looks like a valid segment chain");
+ *end = cur_pos; // set start point if interactive
+ }
+ cnt = 0;
+ while((FOFFSET)lseek(fp,cur_pos,SEEK_SET) == cur_pos &&
+ (rd = read(fp,&cseg,sizeof(cseg))) == sizeof(cseg))
+ {
+ cnt++;
+ sz = cseg.seg_sz & ~RAW_SEG;
+ if(ctrl & DISPLAY)
+ {
+ printf("\n%3d: @ 0x%lx cum size = 0x%lx segment size 0x%x",
+ cnt,cur_pos,cseg.cum_sz,sz);
+ if(cseg.seg_sz & RAW_SEG)
+ printf(" - not compressed");
+ if(cseg.seg_sz == 0)
+ {
+ printf("\nend of current list, try to step into next? (Y/N) ");
+ ques = bques(0); // only do interactive logic in DISPLAY mode
+ }
+ }
+ cur_pos += SEG_SZ; // change 6/01/07 should work for all versions
+ if(sz == 0)
+ {
+ if(ques == 1) // attempt to advance through catalog to next segment
+ {
+ tst_pos = (cur_pos - *end) % SEG_SZ;
+ if(tst_pos != 0)
+ cur_pos += SEG_SZ - tst_pos;
+ // used compressed flag below, must be if has cseg_heads!
+ adv = get_dir_len(fp,1,cur_pos);
+ printf("\nCatalog expected at 0x%lx",cur_pos);
+ if(adv != LSEEK_ERR)
+ {
+ printf(" len 0x%ld",adv);
+ i = adv / SEG_SZ; // # of segments
+ tst_pos = adv % SEG_SZ;
+ if(tst_pos != 0)
+ {
+ i++;
+ adv += SEG_SZ - tst_pos; // next boundry
+ }
+ // catalog always an even # of full segments
+ cur_pos += i * SEG_SZ;
+ printf("\n\nAdvance to 0x%lx for next segment chain",cur_pos);
+ }
+ else
+ {
+ printf(" can't determine length");
+ ques = 0;
+ }
+
+ }
+ if(ques == 0)
+ {
+ *end = cur_pos; // return where we are
+ return(cnt); // # of segments
+ }
+ }
+ rd = 0;
+ }
+ if(ctrl & DISPLAY)
+ printf("\nnever found an end segment\n");
+ return(LSEEK_ERR);
+}
+
+char *VTBL_TAG = "VTBL",
+ *MDID_TAG = "MDID";
+
+/* side effect leave positioned where I think data starts
+ note the WinME logic is messy as must call find_seg() to
+ locate where each compressed data region ends, then round
+ up to next segment boundry to find start of its catalog
+ 2/4/04 oh how use start and end -3 for zero based
+ index to start data and start catalog
+
+*/
+
+int get_vtbl(int fp,struct vtbl_ver *cur_vtbl)
+{
+ char query[30];
+ long tag,*lptr = (DWORD *)&cur_vtbl->vtbl;
+ int suc = 0,cnt = 0,i,rd,trd=0;
+ cur_vtbl->ver = WIN95; // default
+
+ while(read(fp,&tag,sizeof(long)) == sizeof(long) &&
+ tag == *((long *)VTBL_TAG) &&
+ (rd = read(fp,lptr+1,sizeof(struct qic_vtbl) - sizeof(DWORD))) ==
+ sizeof(struct qic_vtbl) - sizeof(DWORD))
+ {
+ *lptr = tag; // insert at start of vtbl
+ trd += rd+sizeof(DWORD);
+ cnt++;
+ if(cnt == 1)
+ printf("\n %.44s",cur_vtbl->vtbl.desc);
+ // Win95 has no sdrv, for winME they are valid
+ if(cur_vtbl->vtbl.sdrv[0] > 0)
+ printf("\n%d: %c: %.16s",cnt,cur_vtbl->vtbl.ldev-1+'A',
+ cur_vtbl->vtbl.sdrv);
+ else printf("\n%d: logical dev 0x%x",cnt,cur_vtbl->vtbl.ldev);
+
+ }
+ if(trd == 0 || trd != cnt * sizeof(struct qic_vtbl))
+ {
+ printf("\nerror reading VTBL region");
+ suc++;
+ }
+ if(tag == *((long *)MDID_TAG) )
+ {
+ printf("\n%s tag found at end of VTLB region => WinME format",MDID_TAG);
+ cur_vtbl->ver = WINME;
+ cur_vtbl->database = 128; // extra record at end
+ }
+ else
+ cur_vtbl->database = 0;
+
+ cur_vtbl->cnt = cnt;
+ if((cur_vtbl->flen = (DWORD)lseek(fp,0L,SEEK_END)) == LSEEK_ERR ||
+ cur_vtbl->flen < SEG_SZ)
+ {
+ printf("\nerror finding file length");
+ suc++;
+ }
+
+
+ // need additional logic for multiple embedded archives
+ // note only one copy of VTBL at start of file
+ if(cnt > 1) // WinME generates a VTBL for each drive accessed!
+ {
+ printf("\nSelect one of archives above (1 - %d): ",cnt);
+ fgets(query,10,stdin);
+ if(sscanf(query,"%d",&i) != 1 ||
+ (cur_vtbl->ndx = (BYTE) i) < 1 || cur_vtbl->ndx > cnt)
+ {
+ printf("\nabort - invalid choice");
+ suc++;
+ }
+ else // must reread selected entry
+ {
+ i--; // above is 1 base, want 0 based
+ // seek to next vtbl entry and read it
+ if(lseek(fp,128L*i,SEEK_SET) != 128L*i ||
+ (rd = read(fp,&cur_vtbl->vtbl,sizeof(struct qic_vtbl))) !=
+ sizeof(struct qic_vtbl))
+ {
+ suc++;
+ printf("\nfailed to reread desired VTBL");
+ }
+ }
+ }
+ else
+ cur_vtbl->ndx = cnt;
+
+ if(suc == 0)
+ {
+ if((cur_vtbl->ver & WIN_MASK) == WINME &&
+ (cur_vtbl->vtbl.start < 3 || cur_vtbl->vtbl.end < 3))
+ {
+ printf("\nInvalid QFA block #'s");
+ if(cnt > 1)
+ suc++; // fatal error
+ }
+ }
+
+ if(suc == 0)
+ {
+ // all are relative to end VTBL region
+ cur_vtbl->database += cnt * 128;
+ cur_vtbl->dirbase = cur_vtbl->database;
+ if(cnt > 1 && (cur_vtbl->ver & WIN_MASK)== WINME)
+ { // may have multiple volumes
+ cur_vtbl->database +=
+ (FOFFSET)(cur_vtbl->vtbl.start -3) * SEG_SZ;
+
+ cur_vtbl->dirbase +=
+ (FOFFSET)(cur_vtbl->vtbl.end -3) * SEG_SZ;
+ }
+ else // assume one contiguous volume to EOF
+ {
+ i = cur_vtbl->vtbl.dirSz/SEG_SZ;
+ if(cur_vtbl->vtbl.dirSz % SEG_SZ != 0)
+ i++;
+ cur_vtbl->dirbase = cur_vtbl->flen - (FOFFSET)i * SEG_SZ;
+ }
+ }
+ else // try fudging, set data to full range of file
+ {
+ cur_vtbl->database = 0;
+ cur_vtbl->dirbase = cur_vtbl->flen;
+ }
+ printf("\nstart data 0x%lx start dir 0x%lx",
+ cur_vtbl->database,cur_vtbl->dirbase);
+
+
+ if((DWORD)lseek(fp,cur_vtbl->database,SEEK_SET) !=cur_vtbl->database)
+ {
+ printf("\nfailed to seek to start of data region %lx",cur_vtbl->database);
+ suc++;
+ }
+
+ return(suc);
+}
+
+/* adjust to handle multiple questions,
+ unless in raw mode, doesn't return without CR
+ so need to eat trailing control char
+ 4/28/04 add esc argument
+*/
+int bques(char esc)
+{
+ int ret=0;
+ char ch;
+ // Enter on pc returns 0x10, exit loop on any control key
+ while((ch = fgetc(stdin)) > ' ' && ch != esc)
+ {
+ if(ch == 'y' || ch == 'Y')
+ ret = 1;
+ else
+ ret = 0;
+ }
+ if(ch == esc)
+ ret = -1;
+ return(ret);
+}
+
+
+#define NM_LEN 255 // size preallocated buffer for node name
+
+PATH_ELEM paths[MAX_PATH];
+
+// the tstbackup.qic files directory set starts at off = 0x2B90A
+// have auto detect working now, can override with -s178442
+int main(int argc,char *argv[])
+{
+ extern int fin,fout; // decompression globals
+ int fo=EOF,fp=EOF,i,cnt = 0,dcnt=0;
+ int nm_len=0,targetlen,rcnt=0,rd,suc=1;
+ long dir_len=0,*lptr;
+ FOFFSET off = 0,datapos,l;
+ DWORD fdata=0;
+ char *target=NULL,dch,ch,flag=0,tmode=0,doexit=0;
+ char name[NM_LEN+1];
+ struct cseg_head seg;
+ struct dir_blk dir;
+ struct vtbl_ver cur_vtbl;
+ // root,current,temp, next child
+ CAT_LIST *root=NULL,*ccat,*tcat,*ncat=NULL;
+ cur_vtbl.vtbl.dataSz[0] = 0; // used in test for validity below
+
+ printf("\nMSQIC Ver 1.12 compiled for %s",OS_STR);
+#ifdef _4GB
+ printf("\n this version compiled for 4 Gb file access");
+#endif
+ printf("\nCopyright (C) 2003 William T. Kranz");
+ printf("\nMSQIC comes with ABSOLUTELY NO WARRANTY");
+ printf(
+"\nFree software distributed under the terms of the GNU General Public license");
+ printf(
+"\nSee http://www.gnu.org/licenses/gpl.html for license information");
+ printf(
+"\nCheck http://www.fpns.net/willy/msbackup.htm for Updates & Documentation\n");
+ // debug aid check structure sizes
+ if(argc > 1 && strnicmp(argv[1],"-ss",3) == 0)
+ {
+ printf("\nstructure sizes");
+ printf("\nsizeof(struct qic_vtbl) = %d",sizeof(struct qic_vtbl));
+ printf("\nsizeof(struct cseg_head) = %d",sizeof(struct cseg_head));
+ printf("\nsizeof(struct ms_dir_fixed) = %d",sizeof(struct ms_dir_fixed));
+ printf("\nsizeof(struct ms_dir_fixed2) = %d",sizeof(struct ms_dir_fixed2));
+ fputc('\n',stdout);
+ exit(0);
+ }
+ if(argc < 2)
+ {
+
+ printf(
+"\nmsqic [@cmd] [-p] [-x] [-v] [-t] [-s{c|d}#] [-f{d|e|s}] [-d] [-r]");
+ printf("\n@cmd to extract directories based on cmd file");
+ printf("\n-p extract ALL files from ONE path in tree structure");
+ printf("\n-x to extract a file, nm, using paths in tree structure");
+ printf("\n-v just display VTBL and exit");
+ printf("\n-t[ds] to display catalog as tree, d=> dir only, s=> with segment info");
+ printf("\n-fd find file id 0x%lx in data",DAT_SIG);
+ printf("\n-fe find file id 0x%lx in data",EDAT_SIG);
+ printf("\n-fs find & display compressed file segments");
+ printf("\n-sc# force start catalog (directory set) at hex offset");
+ printf("\n-sd# force start data region at hex offset");
+ printf("\n-st95 force Win95 decompression decode, default is Win98 & ME");
+ // -sv is hidden, update VTBL from data file
+ printf("\n-D to decompress archive write output to %s",dcompfnm);
+ printf("\n-d##[:#] to decompress segment(s) starting at hex offset ## in file");
+ printf("\n use optional hex :cnt to decompress cnt contiguous segments");
+ printf("\n-r[filter] attempt raw file data recovery, use -sd# to force data region start");
+ printf("\n use optional filter string, ie *.txt to limit hits");
+ doexit++;
+ }
+ else if ((fp = open(argv[1],O_BINARY|O_RDONLY)) == EOF)
+ {
+ printf("\nfailed to open %s",argv[1]);
+ doexit++;
+ }
+ else if((suc=get_vtbl(fp,&cur_vtbl)) != 0)
+ {
+ printf("\nfailed to find a valid vtbl, try using -sc and -sd?");
+ // above positions at start data if found
+ printf("\nContinue with invalid VTBL? (Y/N) ");
+ if(bques(0) == 1)
+ {
+ suc = create_vtbl(&cur_vtbl); // allow to continue
+ }
+ }
+
+
+ for(i=2;doexit == 0 && fp != EOF && i 0)
+ {
+ flag |= TREE;
+ }
+ // don't overwrite @, give it precedence
+ else if (strnicmp(argv[i],"-p",2) == 0 && rcnt <= 0 &&
+ (rd = strlen(argv[i]+2)) > 0)
+ { // extract a directory to current directory
+ // set up for a call to do_redirect() with one path
+ paths[0].term = *(argv[i]+rd+1);
+ if(paths[0].term != DELIM && paths[0].term != '*' && paths[0].term != '+')
+ {
+ printf("\nerror in -p option, path must end in %c, '*', or '+'",DELIM);
+ doexit++; // exit so see error message
+ }
+ else
+ {
+
+ if((paths[0].path = malloc(rd)) == NULL)
+ {
+ printf("\nfailed to allocate path for -p option");
+ doexit++; // exit so see error message
+ }
+ else
+ {
+ rd--;
+ strncpy(paths[0].path,argv[i]+2,rd);
+ *(paths[0].path+rd) = 0; // terminator, del DELIM
+ paths[0].redirect = paths[0].path+rd; // current dir => empty redirect
+ rcnt = 1;
+ flag |= TREE; // tree is required
+ }
+ }
+ }
+ else if(strnicmp(argv[i],"-t",2) == 0)
+ {
+ flag |= TREE;
+ // may have two conditional arguments
+ for(ch=2;ch<5 && argv[i][(int)ch] != 0;ch++)
+ {
+ dch=argv[i][(int)ch];
+ if( dch == 'd' || dch == 'D')
+ tmode |= DIRONLY; // show dir only
+ else if( dch == 's' || dch == 'S')
+ tmode |= S_SEGS; // show segs
+ }
+ }
+ else if(strnicmp(argv[i],"-sc",3) == 0 &&
+ sscanf(argv[i]+3,"%lx",&l) == 1)
+ {
+ cur_vtbl.dirbase = l;
+ }
+ else if(strnicmp(argv[i],"-sd",3) == 0 &&
+ sscanf(argv[i]+3,"%lx",&l) == 1)
+ {
+ if((FOFFSET)lseek(fp,l,SEEK_SET) != l)
+ printf("\nfailed to seek to data start at %lx",l);
+ else
+ printf("\nfile pointer at data start %lx",l);
+ cur_vtbl.database = l;
+ }
+ else if(strnicmp(argv[i],"-st95",5) == 0 )
+ cur_vtbl.ver = WIN95; // force WIN95 flag for decompression
+ else if(strnicmp(argv[i],"-sv",3) == 0)
+ {
+ if(fp != EOF)
+ close(fp);
+ write_vtbl(argv[1],argv[i]+3);
+ doexit++;
+ }
+ else if(strnicmp(argv[i],"-fd",3) == 0)
+ {
+ do_search(fp,DAT_SIG);
+ doexit++;
+ }
+ else if(strnicmp(argv[i],"-fe",3) == 0)
+ {
+ do_search(fp,EDAT_SIG);
+ doexit++;
+ }
+ else if(strnicmp(argv[i],"-fs",3) == 0)
+ {
+ if((cur_vtbl.vtbl.comp & 0x80) == 0)
+ {
+ printf("\nWarning, this file not compressed, continue (Y/N) ");
+ if(bques(0) == 0) // No
+ doexit++;
+ }
+ if(!doexit)
+ {
+ l = cur_vtbl.database; // use currently assigned database
+ find_seg(fp,DISPLAY,&l,cur_vtbl.ver); // display compressed segment list
+ doexit++;
+ }
+ }
+ else if(strnicmp(argv[i],"-d",2) == 0)
+ {
+ if(!(cur_vtbl.vtbl.comp & 0x80))
+ {
+ printf("\nVTBL says file data was not compressed! force it (Y/N)? ");
+ if(bques(0) != 1)
+ exit(-1);
+ }
+ if ((fo=open(dcompfnm,O_BINARY|O_RDWR|O_CREAT|O_TRUNC,
+ S_IREAD|S_IWRITE)) == EOF)
+ printf("\nfailed to open decompress output file");
+ else if(*(argv[i]+1) == 'D') // do entire file
+ {
+ do_decompress(fp,fo,&cur_vtbl);
+ doexit++;
+ }
+ else if(*(argv[i]+1) == 'd')
+ {
+ // Note argument should be address AFTER cseg_head
+ if(strlen(argv[i]) > 2 && sscanf(argv[i]+2,"%lx",&l) == 1)
+ {
+ // for DATA RECOVERY check for a length qualifier
+ if((target = strchr(argv[i]+2,':')) != NULL &&
+ sscanf(target+1,"%x",&cnt) == 1 && cnt > 0)
+ printf(
+ "\n Attempt to read 0x%x continguous segments",cnt);
+ else if(target != NULL)
+ {
+ printf("\nsyntax error, ignore :# format");
+ target = NULL;
+ }
+ rd = sizeof(struct cseg_head);
+
+ if((FOFFSET)lseek(fp,l,SEEK_SET) != l)
+ {
+ printf("\nseek to %lx failed",l);
+ doexit++;
+ }
+ if(target == NULL)
+ {
+ cnt = 1;
+ l += rd; // skip cseg_head, go right to data
+ }
+ fin = fp; fout = fo; // set globals
+ while(!doexit && cnt > 0)
+ {
+ if(target != NULL)
+ {
+ if(read(fin,&seg,rd) != rd)
+ {
+ printf("\nabort, failed to read segment header");
+ break;
+ }
+ else if(seg.seg_sz == 0)
+ {
+ printf("\nend of segment header chain");
+ break;
+ }
+ // use ch as compressed flag
+ if(seg.seg_sz & RAW_SEG)
+ {
+ ch = 0;
+ seg.seg_sz &= ~RAW_SEG; // clear it
+ }
+ else
+ ch = 1; // is compressed
+ printf("\n\nseg start %lx seg size %x",
+ l,seg.seg_sz);
+ if(!ch) // don't decompress this one!
+ {
+ printf("\nRaw copy of uncompressed segment starting at %lx",l);
+ // do a raw copy of the segment, ignore return....
+ copy_region((long)(seg.seg_sz));
+ }
+ else
+ decomp_seg(); // repeat for cnt segs with no checks
+ // new 6/1/07
+ l += SEG_SZ;
+ // was WINME specific: l += seg.seg_sz+rd;
+ }
+ else
+ decomp_seg(); // let it rip ONCE with no checks
+ if(--cnt > 0 && (FOFFSET)lseek(fin,l,SEEK_SET) != l)
+ {
+ printf("\nerror seeking to next segment header at %lx",l);
+ break;
+ }
+ }
+ doexit++;
+ }
+
+ }
+ }
+ else if(strnicmp(argv[i],"-r",2) == 0)
+ {
+ target = argv[i]+2;
+ if(strlen(target) == 0)
+ target = NULL;
+ // flen set via lseek() in get_vtbl() its valid even if vtbl isn't
+ if(cur_vtbl.dirbase > cur_vtbl.flen)
+ cur_vtbl.dirbase = cur_vtbl.flen;
+ do_recover(fp,cur_vtbl.database,cur_vtbl.dirbase,buf,BLK_SZ,target);
+ doexit++; // just display VTBL
+ }
+ else if(strnicmp(argv[i],"-v",2) == 0)
+ {
+ disp_vtbl(&cur_vtbl.vtbl);
+ doexit++; // just display VTBL
+ }
+
+ if(suc || doexit)
+ {
+#ifdef unix
+ printf("\n"); // cleanup for unix
+#endif
+ exit(suc);
+ }
+
+ // we have parsed command line args, and need to process catalog
+ // move to start dir region
+ else if((DWORD)lseek(fp,cur_vtbl.dirbase,SEEK_SET) !=cur_vtbl.dirbase)
+ {
+ printf("\nfailed to seek to start of catalog at %lx\n\n",cur_vtbl.dirbase);
+ suc++;
+ }
+ else // parse directory display files, or build tree
+ {
+ // cseg_head values are relative to start data region.
+ datapos = 0; // initialize to start data region
+ // developed logic for WinME where root is not named
+ ccat = new_cat_list(NULL,"ROOT",datapos,NULL); // create root node
+ printf("\ndirectory display starting at offset %lx",cur_vtbl.dirbase);
+ // faults out at end of list terminate before then!
+
+ if( cur_vtbl.vtbl.comp & 0x80) // compressed file
+ {
+ dir.pseg = &seg; // uncompressed flag
+ memset(&seg,0,sizeof(struct cseg_head));
+ }
+ else
+ dir.pseg = NULL; // uncompressed flag
+ while((suc = next_dir(fp,&dir,buf,BLK_SZ,0)) > 0)
+ {
+ if(dir.fix1 == NULL || dir.fix1->rec_len == 0)
+ {
+ printf("\nbig oops, must be end of directory");
+ break;
+ }
+ if((dir.fix1->flag & SUBDIR))
+ {
+ dch = 'D';
+ dcnt++;
+ }
+ else
+ {
+ dch = ' ';
+ cnt++;
+ fdata +=dir.fix1->file_len;
+ }
+ dir_len += suc;
+ off += suc;
+ if(suc != dir.fix1->rec_len+2) // len is for rest of rec
+ printf("\nwarning rec_len = %d != bytes read %d",
+ dir.fix1->rec_len,suc);
+
+
+ // rec_len is length of rest of record without its own WORD length
+ datapos += dir.fix1->rec_len + 2 + sizeof(long)+ // DAT_SIG
+ dir.fix1->path_len; // path data in data section
+ // if sub dir pointing to next DAT_SIG in next subdir
+ // if its a file advance a little further
+ if(dir.fix1->file_len > 0) // to start of file data
+ datapos += sizeof(long)+2; // EDAT_SIG + WORD(?)
+ else if((cur_vtbl.ver & WIN_MASK) == WIN95) // assume subdir & win95
+ datapos +=18; // some other EDAT_SIG stuff in here!
+ // helps but not enough
+
+ // this skips root record which has no name in WinME
+ // Win95 style do have a name
+ if(dir.fix1->nm_len > 0 && dir.nm1 != NULL)
+ {
+ /* convert first, LFN style, unicode name
+ to a string, made NM_LEN 255 so should fit but validate below
+ */
+ for(i=0,nm_len=0;inm_len;i++)
+ {
+ ch = *(dir.nm1+i++); // unicode, skip alternate chars
+ if(nm_len < NM_LEN)
+ name[nm_len++] = ch;// turncates name to NM_LEN
+ }
+ name[nm_len] = 0; // make it a string
+
+ if(!(flag & TREE)) // display raw catalog
+ {
+ printf("\n\n%-18.18s %c %8ld ",name,dch,dir.fix1->file_len);
+ disp_datetime(dir.fix1->m_datetime);
+ printf(" attrib: %2x",dir.fix1->attrib);
+#ifdef VERBOSE
+ // display more of data on 2nd line
+ putchar('\n');
+ disp_datetime(dir.fix1->c_datetime);
+ putchar(' ');
+ disp_datetime(dir.fix1->a_datetime);
+ lptr = (long *)(buf+2);
+ printf(" @ 2 = 0x%4lx",*lptr); // something, no idea....
+
+// mostly zeros, and they same, unknwn3[20] seems to be attributes
+ printf("\nunknww1 %x unknww2 %x ",dir.fix1->unknww1,dir.fix1->unknww2);
+ printf("\n unknwb1[] = ");
+ for(i=0;i < 20;i++)
+ {
+ printf(" %02x",dir.fix1->unknwb1[i]);
+ }
+ printf("\n unknwb2[] = ");
+ for(i=0;i < 4;i++)
+ printf(" %02x",dir.fix1->unknwb2[i]);
+#endif
+ }
+ else // TREE mode is active
+ {
+#ifdef MSDOS // need to use 8.3 MSDOS names LFN is NOT supported
+ if(dir.nm2 != 0 && // there is an MSDOS 8.3 style name
+ dir.fix2 != NULL && dir.fix2->nm_len > 0)
+ {
+ for(i=0,nm_len=0;inm_len && nm_len< NM_LEN;i++)
+ name[nm_len++] = *(dir.nm2+i++); // unicode, skip alternate chars
+ name[nm_len] = 0; // make it a string
+ }
+
+#endif
+ // dynamically build tree of file/subdir nodes
+ // keep appending till find DIRLAST flag, then adjust
+ if(ccat == NULL)
+ {
+ printf("\nfatal error ccat undefined");
+ break;
+ }
+ else if((tcat =
+ new_cat_list(ccat,name,datapos,dir.fix1)) == NULL)
+ {
+ printf("\nnew_cat_list() alloc error");
+ break;
+ }
+
+ else
+ { // add to list of children
+ if(ccat->child == NULL)
+ { // create first child
+ ccat->child = tcat;
+ ncat = tcat;
+ }
+ else if(ncat != NULL)
+ { // append to end of child list
+ ncat->next = tcat;
+ ncat = tcat;
+ }
+ else
+ {
+ printf("\nncat undefined");
+ break;
+ }
+ }
+ }
+
+ }
+// flags are different from QIC113
+ if((dir.fix1->flag & DIRLAST) && (flag & TREE))
+ /* Before Ralf's work (pre 1/04) thought
+ an empty sub dir is a SUBDIR and DIRLAST
+ as I had never included an empty subdir in a backup
+ but its perfectly valid
+
+ need to adjust CAT_LIST any time see DIRLAST
+ */
+ { // printf("\nDIRLAST");
+ if(root == NULL) // ignore 1st one for root with WinME
+ {
+ root = ccat; // set root to clear it
+ if((cur_vtbl.ver & WIN_MASK) == WIN95 && // assume win95?
+ ccat->child != NULL)
+ {
+ root = ccat->child; // root has a name
+ root->parent = NULL; // tested by -t for out of bounds
+ free(ccat);
+ ccat = root; // assed backwards way to do it
+ }
+
+ }
+ else // normal transition on DIRLAST
+ {
+ ncat = ccat; // save current location
+ // try to advance to a child or next node
+ ccat = next_tree_node(ccat);
+ /* if ccat == NULL still, everything below and at this level failed,
+ try for a parent's next
+ by definition a parent is a subdir, but next may not be....
+ must ignore direct parent and its children
+ */
+ while(ccat == NULL && ncat->parent != NULL)
+ {
+ ncat = ncat->parent;
+ if(ncat->next != NULL)
+ {
+ if(ncat->next->flag & SUBDIR)
+ ccat = ncat->next;
+ else
+ ccat = next_tree_node(ncat->next);
+ }
+ }
+ if(ccat != NULL)
+ ncat = ccat->next;
+ else
+ ncat = NULL;
+ }
+ }
+
+
+ if(dir.fix1->flag & DIREND)
+ {
+ printf("\nend of volume directory");
+ printf("\n%d files contain %lx bytes of data, %d directories found",
+ cnt,fdata,dcnt);
+ break; // probably VOL END & END this dir
+ }
+ if(dir.fix1->file_len > 0)
+ {
+ datapos += dir.fix1->file_len; // append this files length
+ if((cur_vtbl.ver & WIN_MASK)== WIN95) // assume subdir & win95
+ datapos +=12; // some more EDAT_SIG stuff in here!
+ }
+ } // while no errors
+
+ // ------- now have a directory tree, use it for display or extract
+ if(rcnt)
+ {
+ flag = 0; // block other options
+ printf("\n extracted %ld files",
+ do_redirect(fp,&cur_vtbl,root,paths,rcnt));
+ }
+
+ if(flag & TREE) // show tree
+ disp_tree(root,0,tmode);
+ if((flag & EXTRACT)) // look at tree, extract named file
+ {
+ /* Change logic 1/20/04 must have a named root in extact
+ if Win98 format use "ROOT" if WIN98 use actual name
+ adds a couple letters must input for -x
+ but cleans up -p and @ logic
+ */
+ ccat = tree_node(root,target,ISFILE);
+ if(ccat != NULL)
+ {
+ printf("\nfound %s at data section offset %lx length %lx",
+ target,ccat->data_off,ccat->file_len);
+ printf("\nExtracting to current directory");
+#ifdef MSDOS
+ printf("\nForcing MSDOS filename (ie 8.3 format)");
+#endif
+ if(cur_vtbl.vtbl.comp & 0x80)
+ printf("\nCaution overwrites temporary file %s in process",dcompfnm);
+ do_extract(fp,&cur_vtbl,ccat->name,ccat);
+
+ }
+ else
+ printf("\n target %s not found (its case sensitive!)",target);
+ }
+ }
+ fputc('\n',stdout); // some cleanup for linux
+ return(suc); // printf() above and this return makes gcc happy
+}
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.H" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.H"
new file mode 100644
index 0000000..9a2d87b
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.H"
@@ -0,0 +1,295 @@
+/* defines for msqic work
+ made separate file 10/14/03
+
+ need __attribute__ ((packed))
+ for linux gcc
+
+note the qic_vtbl seems ok without PACKED, but cseg_head requires!
+
+see section 7.1.3 of QIC 113 Spec for this info, below works in qic80.c
+in *.qic there are two related structures which I named ms_qic_fix*
+
+struct dir_fixed {
+BYTE len, // of the rest of the record + vendor data
+ attrib;
+DWORD datetime,
+ size; // file size in bytes + data header size, or 0 for empty dir
+BYTE extra; // flag byte containing file info
+// min length is 0xA, ie include extra but no vendor data
+} __attribute__ ((packed));
+
+above not what Win9x MSBackUp uses!
+also note EDAT_SIG and DAT_SIG only occur in data region not dir set
+
+11/31/03 add some bitmap defines and .err field in struct dir_blk
+12/14/03
+add field struct cseg_head *pseg;
+ in struct dir_blk
+12/16/03 per Ralf Westram's input try to clean up some of
+signed unsigned issues in DWORD verus long for file offsets
+If I use as offset should probably be a long!
+in struct vtbl_ver change .database and .dirbase
+12/22/03 try some FOFFSET logic
+ change struct vtbl_ver offset fields back to DWORD
+
+1/7/04 add Ralf's EMPTYDIR define
+1/11/04 change params to do_extract()
+ remove #define SEARCH 2 may never have been used...
+ update tree_node() adding BYTE mode argumnet
+ add do_redirect() prototype
+1/20/04 add DELIM definition for path separator
+1/22/04 change args for do_extract()
+4/28/04 add RECREAT to indicate the original VTBL was invalid
+ Also requires WIN_MASK and change value of WIN95 and WINME
+ Need in do_decompress() if attempt to reconstruct archive
+ Note there are a couple places where I should be using
+ FOFFSET instead of DWORD, I've left them alone for ease
+ in displaying data, but see vtbl_ver and seg_head
+
+ add mk_date() prototype
+ add define for VTBL_DATE_FUDGE
+*/
+
+#if defined(MSDOS) || defined(_WIN32)
+#pragma pack(1)
+// default for MSVC _WIN32 should be byte packing, but need pack(1)!
+#define PACKED ;
+#define DELIM '\\'
+#else // Unix
+#define DELIM '/'
+#ifdef __CYGWIN__ // note O_BINARY is defined
+#pragma pack(1) // Linux gcc won't compile with this, see above
+#define PACKED ;
+#else
+// to pack linux structures selectively
+#define PACKED __attribute__ ((packed));
+#define O_BINARY 0 // this Microsoft mode flag undefined in Linux gcc
+#endif
+#endif
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+#ifdef _4GB
+typedef unsigned long FOFFSET;
+#define LSEEK_ERR ((DWORD)-1L)
+#else
+ #ifdef HAS_INT64 // force this manually for 64 bit access
+// for now WIN32 specific
+ typedef __int64 FOFFSET;
+ #define lseek _lseeki64 // redirect to 64 bit access routine
+ #define LSEEK_ERR (-1) // this works for standard lseek stuff
+ #else
+ typedef long FOFFSET; // 2 GB is default std C
+ #define LSEEK_ERR (-1L)
+ #endif
+#endif
+
+#define SEG_SZ 29696 // MSBackUP wants data and dir segs to be multiple of this
+// in compressed file each segment including catalog start with cseg_head
+
+
+// from pp9 of QIC113G,
+struct qic_vtbl {
+BYTE tag[4]; // should be 'VTBL'
+DWORD nseg; // # of logical segments
+char desc[44];
+DWORD date; // date and time created
+BYTE flag; // bitmap
+BYTE seq; // multi catridge sequence #
+WORD rev_major,rev_minor; // revision numberrs
+BYTE vres[14]; // reserved for vendor extensions
+DWORD start,end; // physical QFA block numbers
+ /* In Win98 & ME subtract 3 from above for zero based SEGMENT index
+ to start first data segment and start first directory segment
+ */
+BYTE passwd[8]; // if not used, start with a 0 byte
+DWORD dirSz, // size of file set directory region in bytes
+ dataSz[2]; // total size of data region in bytes
+BYTE OSver[2]; // major and minor #
+BYTE sdrv[16]; // source drive volume lable
+BYTE ldev, // logical dev file set originated from
+ res, // should be 0
+ comp, // compression bitmap, 0 if not used
+ OStype,
+ res2[2]; // more reserved stuff
+} PACKED
+
+
+
+
+/* If its a compressed volume there will be cseg_head
+ records ahead of each segment. The first immediately
+ follows the Volume Table.
+ For the sake of argument, lets assume per QIC133 segments are
+ supposed to be < 32K, ie seg_sz high order bit isn't required.
+ So its a flag bit, set to indicate raw data? IE do not
+ decompress this segment. Use seg_sz to jump to the
+ next segment header.
+*/
+#define RAW_SEG 0x8000 // flag for a raw data segment
+
+struct cseg_head {
+DWORD cum_sz, // cumlative uncompressed bytes at end this segment
+ cum_sz_hi;// normally zero. High order DWORD of above for > 4Gb
+WORD seg_sz; // physical bytes in this segment, offset to next header
+} PACKED
+
+
+struct ms_dir_fixed {
+WORD rec_len; // only valid in dir set or Win95 Data region
+DWORD ndx[2]; // thought this was quad word pointer to data? apparently not
+ // ndx[0] varies, ndx[1] = 0, was unknow[8]
+ // in data section always seems to be 0xffffffff
+WORD path_len, // @ 0xA # path chars, exits in catalog and data section
+ // however path chars only present in data section
+ unknww1; // 0xA always?
+BYTE flag; // flag bytes
+WORD unknww2; // 0x7 always?
+DWORD file_len; // @ 0x11 # bytes in original file
+BYTE unknwb1[20], // was flags[0x18] but attrib at flags[20]
+ attrib,
+ unknwb2[3];
+DWORD c_datetime, // created
+ unknwl1, // always 0xFFFFFFFF?
+ a_datetime, // accessed
+ unknwl2, // always 0xFFFFFFFF?
+ m_datetime, // modified, as shown in DOS
+ unknwl3; // so can be expanded? always 0xFFFFFFFF?
+WORD nm_len; // length of the long variable length name
+} PACKED
+
+// contains var length name, case sensitive, unicode
+
+struct ms_dir_fixed2 {
+BYTE unkwn1[13]; // was [0x15]; this region fairly constant
+DWORD var1; // these vars change file to file
+DWORD var2;
+WORD nm_len; // length of 2nd, short ie DOS, variable length name
+} PACKED
+
+#ifdef MSDOS
+#pragma pack() // don't read rest from file so don't care
+#endif
+
+// var length name, always upper case => DOS, unicode
+// if in data region path follows, not in directory set
+// var length path per ms_dir_fixed.path_len, unicode
+
+
+// use to return VTBL header and version detection from get_vtbl()
+#define WIN95 1
+#define WINME 3 // used for 98 and ME, ie had an MDID
+#define RECREAT 0x80 // original VTBL corrupt, recreated
+#define WIN_MASK 0x7
+
+// tmode defines passed to disp_tree()
+#define S_SEGS 1 // show segments in tree
+#define DIRONLY 2
+// bitmap flags for control flags in main and next_dir()
+#define EXTRACT 2 // used in main and msqicrcv.c
+#define TREE 4
+// below only used in msqicrcv.c:do_recover( mode)
+// but W95 and W98 set in main?
+#define QUERY 1
+#define W95 4 // not used in same flag as TREE so no conflict
+#define W98 8
+
+struct vtbl_ver {
+BYTE ver,
+ cnt, // # of regions
+ ndx; // which VTBL when more than one (1 based!)
+DWORD database; // start of data segment
+DWORD dirbase; // start of directory segment
+DWORD flen; // file length
+struct qic_vtbl vtbl; // copy of vtbl of interest
+};
+
+
+/* use to return data from next_dir() routine
+ Not pointers are to buffer locations which can be over written
+ if care not taken after call!
+*/
+struct dir_blk {
+char err;
+struct ms_dir_fixed *fix1;
+BYTE *nm1;
+struct ms_dir_fixed2 *fix2;
+BYTE *nm2,
+ *path;
+struct cseg_head *pseg; // NULL if NOT compressed, otherwise points to buffer
+};
+
+/* for my dynamic link list of nodes
+ order of subdirs in msbkup2.qic
+ root|DIRLAST,temp,dos,csource
+ could save entire record, but not now....
+*/
+typedef struct cat_list {
+char *name;
+BYTE flag, // its a subdir (maybe only one) or file
+ attrib; // file attributes
+FOFFSET data_off; // offset into uncompressed data region for start
+DWORD datetime, // m_datetime -> dos time, ie modified
+ file_len; // @ 0x11 # bytes in original file
+struct cat_list * next,
+ * child, // only subdir has children
+ * parent;
+} CAT_LIST;
+
+// used in path remapping functions
+typedef struct path_elem {
+char term,
+ *path,
+ *redirect;
+} PATH_ELEM;
+
+#define MAX_PATH 25 // max PATH_ELEM array allocation
+
+// bitmap flags for ctrl in find_seg
+#define DISPLAY 1
+#define FIND_POS 2 // probably not used...
+
+/* Bitmap defines for flags
+ flags below work with my QIC tape images, see qic80.c
+ msc seems to do this differently
+#define SUBDIR 0x20 // this is a directory entry, not a file
+#define DIRLAST 0x40 // last entry in this directory
+#define DIREND 0x80 // last entry in entire volume directory
+
+ below seem to work with my current ms_dir_fixed.flags[2]
+ Thanks to Ralf Westram for identifing this flag
+ Given the unused bits, there are more to be identified!
+*/
+#define ISFILE 0 // no bit set for file, this define for readability
+#define SUBDIR 0x1 // this is a directory entry, not a file
+#define EMPTYDIR 0x2 // this marks an empty subdirectory
+#define DIRLAST 0x8 // last entry in this directory
+#define DIREND 0x30 // last entry in entire volume directory
+
+#define DAT_SIG 0x33CC33CCL // signature at start of Data Segment
+#define EDAT_SIG 0x66996699L // just before start of data file
+#define UDEF_SIG 0xFFFFFFFFL // undefined DWORDS in Data Segment
+
+#define VTBL_DATE_FUDGE 63072000L // date fudge for vtbl date
+
+
+// add some function prototypes
+// following in msqic.c
+int next_dir(int fp,struct dir_blk *ptr, BYTE *buf,WORD bsz,BYTE mode);
+// following in msqicrcv.c
+int get_paths(char *fn,struct path_elem paths[],int sz);
+void disp_tree(CAT_LIST *ccat,int level,BYTE mode);
+CAT_LIST *tree_node(CAT_LIST *ccat,char *str,BYTE mode);
+CAT_LIST *next_tree_node(CAT_LIST * ccat);
+CAT_LIST *new_cat_list(CAT_LIST *prev,char *name,long off,struct ms_dir_fixed *fixed);
+void free_cat_list(CAT_LIST *root);
+int do_extract(int fp,struct vtbl_ver *v,char *nm,CAT_LIST * ccat);
+long do_redirect(int fp,struct vtbl_ver *v,CAT_LIST *root,PATH_ELEM paths[],int rcnt);
+WORD do_recover(int fp,DWORD dataoff,DWORD dataend, BYTE *buf,WORD bsz,char *target);
+int write_vtbl(char *qicnm,char *datnm);
+// following in qicdcomp.c, see sloppy use of globals, sorry
+int copy_region(long len);
+void decomp_seg();
+int do_decompress(int fi,int fo,struct vtbl_ver *cvtbl);
+unsigned long mk_date(char *str);
\ No newline at end of file
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQICRCV.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQICRCV.C"
new file mode 100644
index 0000000..a032f0f
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQICRCV.C"
@@ -0,0 +1,1342 @@
+/* msqicrcv.c - support routines for main() in msqic.c
+ Check http://www.fpns.net/willy/msbackup.htm for updates.
+ contact info above or via snailmail: p.o. box 333, bradford, nh 03221
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+-----------------------------------------------------------------------------------
+ History:
+
+11/31/03
+split out of msqic V1.01 11/31/03
+add write_vtbl() stub
+copy current version to msqicrc1.c
+ it had path error and seems to be in an infinit loop.
+ exceeds path length, breaks out of search and then
+ tests for EDAT_SIG beyond end of buffer. This fials
+ off wasn't updated so calls next_data_sig with dataoff
+ that failed last time....
+
+12/05/03 seems ok under linux also, but reguires a strupr.c
+ modules, must look equivantlent unix funcion and define.
+12/16/03 wrote and tested write_vtbl(), seems ok
+12/22/03 add some code changes, ie FOFFSET and LSEEK_ERR
+ so new defines in msqic.h for _4GB can work affected next_data_sig()
+ will probably screw up some of Ralf's warning issues again....
+
+ also append tree routines and do_extract() as ran out of space
+ for ed in msqic.c
+
+1/8/04 was out of sync with Ralf's stuff, see sub dir \ralf
+ Apparently his didn't crash with '-t' but mine did.
+ see notes in msqicsrc.lst
+
+ apparently disp_tree() blew up if nesting was too deep
+ checked out by reducing nesting level. see new IND_LEV define
+ Yes this seems to produce problem, have corrected so appending
+ last NUL doesn't overwrite stack.
+
+ Also added EMPTYDIR logic based on Ralf's info.
+ seems to work with my test file: \tempty2.qic
+
+ very odd, do_extract() does fgets() to obtain file name
+ but doesn't wipe /n so open fails. How did it every work?
+ Oh I bet I started with gets() converted to linux and never rechecked it
+
+1/11/04 diddle do_extract() changing arguments so pass in
+ ccat pointer with file name. Also change tree routines to used
+ MSDOS name if MSDOS defined and it exists (ie there is a long name)
+ add ralf's CYGWIN compiler defines
+ add mode to disp_tree so can change mode at run time
+ add get_paths(), tree_copy(), and do_redirect()
+ required new parameter in call to tree_node()
+1/15/04 do_extract was decompression one too many segments
+ see off by one error on incrementation
+ testing with big note by change
+ found some more FOFFSET issues and an error in next_data_sig()
+ major error in logic to estimate file length in do_recover()
+ how did it ever work.
+1/17/04 add check for ".\" as redirect path in get_paths()
+ tweak do_redirect() to ignore empty redirect path which
+ is current directory and must exits.
+ also required changing where tree_path() appends DELIM
+1/22/04 start adding setting timestamp and attribute on file extraction
+ and '+' option in do_redirect to create directories.
+ remove strupr() again, only required if MSDOS when it exists in stdlib
+1/24/04 in do_redirect() add ccat != NULL before copy_tree(..ccat->child
+ in get_paths() add incrementation of i while looking for redirect path
+2/08/04 error in do_recover() needed to save attrib and datetime
+ before next_data_sig() call, was restoring incorrectly!
+3/19/04 tweak get_paths() to match nttree.c, think it didn't handle
+ termination of quoated destination correctly
+4/28/04 add create_vtbl() attempt to create a vtbl if the archives is
+ unreadable.
+5/03/04 oops need to set cvtbl->ver not cvtbl->vtbl.OStype!
+*/
+#include
+#include
+#include
+#include // for toupper()
+#include // define memset
+#include
+#include /* for open() defines */
+
+#if defined(MSDOS) || defined(_WIN32)
+# include
+# include // for lseek
+#include // for utime()
+#else
+# ifdef unix
+# include // for read/write etc.
+# include
+# define strnicmp strncasecmp
+# define stricmp strcasecmp
+// note I have a custom strupr() below
+# else
+# error Unknown build environment.
+# endif
+#endif
+
+#include "msqic.h"
+
+extern int bques();
+
+
+/* trash global buffer looking for start next DAT_SIG
+ note 11/30/03 was working with just lptr and code below for WinME
+ if( *lptr == DAT_SIG && *(lptr+1) == UDEF_SIG && *(lptr+2) == UDEF_SIG)
+ for win95 the WORD after DAT_SIG is defined, so this test failed
+ new one should be generic
+ 1/15/04 oops! revisting, when I made change above I left
+ minsz = 3*sizeof(long)
+ must add sizeof(WORD) I've added
+
+ This is looing for following hex pattern of bytes where ?? is don't care
+ {CC,33,CC,33,??,??,FF,FF,FF,FF,FF,FF,FF,FF}
+*/
+FOFFSET next_data_sig(int fp,FOFFSET off,BYTE *buf,WORD bsz)
+{
+ int i,rd,suc=0,minsz = 3*sizeof(DWORD) + sizeof(WORD);
+ DWORD *lptr,*lptru;
+ while(suc == 0)
+ {
+ // read minsz more than need each pass,
+ // then inc off by rd - minsz so end up seeking back minsz
+ if((FOFFSET)lseek(fp,off,SEEK_SET) != off ||
+ (rd = read(fp,buf,bsz)) < minsz)
+ suc = -1;
+ else
+ {
+
+ i=0;
+ do {
+ lptr = (DWORD *)(buf+i);
+ // skip WORD which may be defined in Win95 format
+ lptru = (DWORD *)(buf+sizeof(DWORD)+sizeof(WORD) +i);
+ if( *lptr == DAT_SIG)
+ {
+ if( *(lptru) == UDEF_SIG && *(lptru+1) == UDEF_SIG)
+ suc = 1;
+ }
+ if(suc == 0)
+ i++;
+ } while(suc == 0 && i 0)
+ return(off);
+ else
+ return(LSEEK_ERR);
+}
+
+int write_file(char *nm,int fp,FOFFSET start,FOFFSET end,BYTE *buf,WORD bsz)
+{
+ int fo=-2,wr,rd,ret=0;
+ FOFFSET l;
+ if ( (l = (FOFFSET)lseek(fp,start,SEEK_SET)) != start ||
+ (fo=open(nm,O_BINARY|O_RDWR|O_CREAT|O_TRUNC,
+ S_IREAD|S_IWRITE)) == EOF)
+ ret = -1;
+ while(ret == 0 && start < end)
+ {
+ if(bsz + start < end)
+ rd = bsz;
+ else
+ rd = end - start;
+ if((wr = read(fp,buf,rd)) != rd || (rd = write(fo,buf,wr)) != wr)
+ ret = -2;
+ start += rd;
+ }
+ if(fo > 0)
+ close(fo);
+ return(ret);
+}
+
+
+
+int is_match(char *str,char *filter)
+{
+ char ret=1;
+#ifdef MSDOS
+ strupr(str); // DOS name compare, case insensitive
+ strupr(filter);
+#endif
+ while(*str != 0 && ret == 1)
+ {
+ if(*str == '.' && *filter == '*')
+ filter++;
+ if(*filter == '*' || *filter == '?') // match all
+ str++;
+ else if (*filter++ != *str++)
+ ret = 0;
+ if(*filter == '?')
+ filter++;
+
+ }
+ return(ret);
+}
+
+
+/* set_fattrib() to set both the datetime, and file attribute after written
+ called from do_recover() and do_extract()
+*/
+int set_fattrib(char *name,DWORD datetime,BYTE attrib)
+{
+ struct utimbuf times;
+ int i,suc=0;
+ times.actime = times.modtime = datetime;
+ if(utime(name,×) != 0) // set time stamp
+ suc = 1;
+
+ i = S_IFREG | S_IREAD;
+ if((attrib & 1) == 0) // its writable
+ i |= S_IWRITE;
+ /* via generic chmod, I fail to see how to do DOS
+ hidden 0x20 (under linux can insert '.' at start name)
+ system 0x40
+ archive 0x20
+ executable in DOS is done by file name extension
+ */
+ if(chmod(name,i) != 0)
+ suc |= 2;
+ if(suc)
+ printf("\nerror setting file: ");
+ if(suc & 1)
+ printf("timestamp ");
+ if(suc & 2)
+ printf("attributes");
+ return(suc);
+}
+
+/* try to parse data region
+ use the DOS 8.3 file names
+
+ Tricky as don't know file length! not in valid in fixed region
+
+ think will need extra for Win95?
+
+ bitmap defines below for cntrl byte set interactively
+
+move to msqic.h
+#define QUERY 1
+// EXTRACT 2 defined above
+#define W95 4 // else its W98 version of archive
+
+
+11/31/03
+ 1st mode, change to incrementally read buffer via calls to
+ next_dir()
+
+extra=6 defined for W98 as {DWORD,WORD} the EDAT_SIG and WORD following
+ it in Win 98 files, It turns out to be the same in Win95 files,
+ but is 3 * large, ie 18 bytes = 3 pairs for subdir entries.
+ since I ignore them, its always 6 below.
+
+However
+extrae=0 for Win98, but for W95 there are 12 extra words after the
+ file before the next DAT_SIG, ie if don't do the format properly
+ on gets files that are 12 bytes too long.
+12/14/14 initialize dir.pseg = NULL to show we are parsing
+ a data region, not a catalog
+1/15/04 weird, after searching for next_sig after start file did:
+ if(next_sig != LSEEK_ERR && (next_sig = lseek(fp,0L,SEEK_END)) > dataoff)
+ printf("\nWarning searched to EOF with no DAT_SIG");
+ what was I thinking?
+ add dataend and change both to DWORD as set from
+ cur_vtbl.database and cur_vtbl.dirbase in main
+*/
+
+
+
+WORD do_recover(int fp,DWORD dataoff,DWORD dataend,BYTE *buf,WORD bsz,char *filter)
+{
+ BYTE attrib;
+ DWORD datetime;
+ int rd,i,off,extra=6,extrae=0;
+ WORD fcnt=0,xfcnt=0,tcnt=0;
+ FOFFSET next_sig;
+ char name[15],*ch,cntrl=W98,match = 1,warn=0;
+ struct dir_blk dir; // storage space for dir pointers
+ printf("\nDATA recovery options:");
+ printf("\n Default is Win98 format, use Win95 instead (Y/N)? ");
+ if(bques(0) == 1)
+ {
+ cntrl = W95;
+ // adjust extra for additional data after 1st EDAT_SIG
+ extrae=12; // two pairs of {DWORD,WORD} before next DAT_SIG
+ }
+ printf("\n Default displays files, extract them (Y/N)? ");
+ if(bques(0) == 1)
+ {
+ cntrl |= EXTRACT;
+ printf("\n Default, interactive query for each file, extract all (Y/N)? ");
+ if(bques(0) != 1)
+ cntrl |= QUERY;
+ if(filter != NULL && strlen(filter) > 0)
+ {
+ printf("Match extracted files to: %s (Y/N)? ",filter);
+ if(bques(0) != 1)
+ filter = NULL;
+ }
+
+ }
+
+
+ dir.pseg = NULL; // signal not a compressed catalog region
+ while((dataoff = next_data_sig(fp,dataoff,buf,bsz)) != LSEEK_ERR &&
+ dataoff < dataend &&
+ (FOFFSET)lseek(fp,dataoff+sizeof(DWORD),SEEK_SET) == dataoff+sizeof(DWORD))
+ { // now pointing to start of a File spec, read it
+ dataoff += sizeof(DWORD);
+ off = 0;
+ tcnt++; // increment total count think we found something
+ if((rd = next_dir(fp,&dir,buf,bsz,cntrl)) > 0)
+ {
+ off += rd; // bytes read in record
+ // ignore if subdir 0x10 or volid 0x8
+ if(dir.fix1->attrib & 0x18)
+ {
+ if(dir.fix1->attrib & 0x10 && warn == 0 && fcnt == 0)
+ /* 1st hit on subdir verify format
+ W95 subdir has two extra pairs of {EDAT_SIG,WORD}
+ but the file entries don't
+ */
+ {
+ if(cntrl & W98 &&
+ *((DWORD *)(buf+off+dir.fix1->path_len+6)) == EDAT_SIG)
+ printf("\nWarning, looks like Win95 format, files may be 12 bytes too long");
+ else if(cntrl & W95 &&
+ *((DWORD *)(buf+off+dir.fix1->path_len+6)) != EDAT_SIG)
+ printf("\nWarning, looks like Win98 format, files may be 12 bytes too short");
+ warn++; //only give message once
+ }
+ dataoff += off;
+ continue;
+ }
+ }
+ else
+ {
+ printf("\nfatal error reading dir spec @%lx skip to next",
+ dataoff);
+ dataoff += off; // search for next
+ continue;
+ }
+
+ // next_dir() does NOT read path info, not valid in a catalog
+ rd = dir.fix1->path_len + extra;
+ if(rd < 1 || rd > bsz -off || dir.nm2 == NULL ||
+ (i = read(fp,buf+off,rd)) != rd ||
+ *((DWORD *)(buf+off+dir.fix1->path_len)) != EDAT_SIG)
+ {
+ printf("\nerror verifing or reading to start data file @%lx skip to next",
+ dataoff);
+ continue;
+ }
+
+ fcnt++; // found a valid file
+ dataoff += off + rd; // skip extra words to start file
+ ch = dir.nm2; // start MSDOS name
+ for(i=0;inm_len/2 && i < 14;i++)
+ {
+ name[i] = *(ch++);
+ ch++; // skip 2nd unicode
+ }
+ name[i] = 0;
+ if(filter != NULL)
+ match = is_match(name,filter);
+ if(match)
+ {
+ printf("\n@%lx Path: ",dataoff);
+ for(i=0;ipath_len;i++)
+ {
+ if(*ch < ' ')
+ fputc(DELIM,stdout); // 0 or 0xA ?
+ else
+ fputc(*ch,stdout);
+ ch++;ch++;i++; // skip 2nd unicode byte
+ }
+ // must save these, io below overwrites them as use global buf
+ attrib = dir.fix1->attrib;
+ datetime = dir.fix1->m_datetime;
+ // overwrite buffer to find file length, destorys dir.* ptrs
+ next_sig = next_data_sig(fp,dataoff,buf,bsz);
+ if(next_sig == LSEEK_ERR || next_sig > dataend)
+ {
+ if(next_sig == LSEEK_ERR)
+ printf("\nSearched to EOF with no next DAT_SIG, ");
+ else
+ printf("\nNext DAT_SIG after end of data, ");
+ printf("use end data region %lx",dataend);
+ next_sig = dataend;
+ }
+ if(next_sig < dataoff)
+ printf("\ncan't estimate length for %s",name);
+ else
+ {
+ if(match && cntrl & EXTRACT)
+ i = 1;
+ else
+ i = 0;
+
+ if(match)
+ {
+ if(cntrl & QUERY)
+ {
+ printf("\nsave as %14s est length %lu (Y/N) ?",
+ name,(DWORD)next_sig-dataoff);
+ i = bques(0); // set to 0 to skip save
+ }
+ else
+ printf("\n%14s est length %lu",name,(DWORD)next_sig-dataoff);
+ }
+ if(i)
+ {
+ if((i=write_file(name,fp,dataoff,next_sig,buf,bsz)) != 0)
+ printf(" - error %d saving file\n",i);
+ else
+ {
+ set_fattrib(name,datetime,attrib);
+ xfcnt++;
+ }
+ }
+ dataoff = next_sig; // always advance
+ }
+ }
+ }
+ printf("\n%u potential files, %u parsed file names",tcnt,fcnt);
+ if(xfcnt > 0)
+ printf(", %u files extracted\n",xfcnt);
+ return(fcnt);
+}
+
+#define VTBL_SZ 128
+
+
+char *key_reg[] = {"VTBL","MDID",NULL};
+char *reg_opts[] = {"null","read",NULL};
+char *key_words[2][7] =
+ {
+ {"desc","flag","dirsz","datasz","label","comp",NULL},
+ {"MediumID","VR","CS","FM","UL","DT",NULL},
+ };
+
+#define MDID_SZ 6
+#define MDID_TERM ((char) 0xb0)
+char *mdid_str[MDID_SZ];
+
+
+int has_key_word(char *targ,char *str[],char **args)
+{
+ int len=0,klen,i = 0;
+ while(*targ == ' ')
+ targ++;
+ while(*(targ+len) != ' ' && *(targ+len) != 0)
+ len++;
+
+ if(args != NULL)
+ {
+ *args = targ+len; // return start of args
+ while(**args == ' ')
+ (*args)++;
+ }
+ while(len > 0 && str[i] != NULL)
+ {
+ klen = strlen(str[i]);
+ if(len >= klen && strnicmp(targ,str[i],klen) == 0)
+ return(i);
+ i++;
+ }
+ return(-1);
+}
+
+
+int get_mdid(char *buf)
+{
+ int i,key,len,klen,suc = 0;
+ char *str,*arg;
+ for(i=0;i= 0 &&
+ key < MDID_SZ)
+ {
+ klen = strlen(key_words[1][key]);
+ arg = buf+i+klen;
+ while(*(arg+len) != MDID_TERM && i+klen+len < VTBL_SZ)
+ len++;
+
+ if(len == 0)
+ str = NULL;
+ else
+ {
+ if((str = malloc(len+1)) == NULL)
+ suc++;
+ else
+ {
+ strncpy(str,buf+i+klen,len);
+ *(str+len) = 0; // make it a string
+ }
+ }
+ mdid_str[key] = str;
+
+ }
+ i+= len+klen;
+ while(*(buf+i) != MDID_TERM && i < VTBL_SZ)
+ i++;
+ i++;
+ }
+ return(suc);
+}
+
+
+void put_mdid(char *buf)
+{
+ int i,j=4,len,klen;
+ memset(buf+j,0,VTBL_SZ-j);
+
+ for(i=0;i VTBL_SZ)
+ {
+ printf("\nskipping MDID: %s",mdid_str[i]);
+ continue;
+ }
+ strcpy(buf+j,key_words[1][i]);
+ j+=klen;
+ strcpy(buf+j,mdid_str[i]);
+ j+=len;
+ *(buf+j) = MDID_TERM;
+ j++;
+ }
+ }
+
+}
+
+#define LN_LEN 100
+/* start write data to VTBL
+ Each key_reg[] word advances loc by 128 bytes
+ if any changes done, they are written
+ ie following would write new UL to MDID at offset 128
+-------
+ VTBL
+ MDID read
+ UL a new title
+-------
+*/
+int write_vtbl(char *qicnm,char *datnm)
+{
+ int fp=EOF,suc=-1,vcnt=-1,hits=0,reg=-1,key,v,tst;
+ FILE *fd = NULL;
+ FOFFSET off;
+ DWORD lv,*lptr;
+ char *args,*lret,ln[LN_LEN+1],buf[VTBL_SZ],*str;
+ struct qic_vtbl *vtbl = (struct qic_vtbl *) buf;
+ if ((fp = open(qicnm,O_BINARY|O_RDWR)) == EOF)
+ printf("\nFailed to open archive %s",qicnm);
+ else if ((fd = fopen(datnm,"r")) == NULL)
+ printf("\nFailed to open VTBL data file %s",datnm);
+ else
+ {
+ do
+ {
+ lret = fgets(ln,LN_LEN,fd);
+ v = strlen(ln)-1;
+ if(v >= 0 && *(ln+v) == '\n')
+ *(ln+v) = 0; // remove CR
+
+ if(lret == NULL || (key=has_key_word(ln,key_reg,&args)) >= 0)
+ { // terminate current region, and optionally start a new one
+ if(hits && vcnt >= 0)
+ {
+ off = vcnt * VTBL_SZ;
+ if(reg == 1) // MDID
+ put_mdid(buf); // fill buffer with new strings
+ if((FOFFSET)lseek(fp,off,SEEK_SET) != off ||
+ write(fp,buf,VTBL_SZ) != VTBL_SZ)
+ {
+ printf("\nerror writting region at %lx",off);
+ break;
+ }
+ }
+ if(lret == NULL)
+ break; // we are done
+ reg = key;
+ hits = 0;
+ vcnt++;
+ if((key = has_key_word(args,reg_opts,NULL)) >= 0)
+ {
+ if(key == 0) // null it out
+ {
+ hits = 1; // force overwrite on close
+ memset(buf,0,VTBL_SZ);
+ }
+ else // read it in
+ {
+ off = vcnt * VTBL_SZ;
+ if((FOFFSET)lseek(fp,off,SEEK_SET) != off ||
+ read(fp,buf,VTBL_SZ) != VTBL_SZ)
+ {
+ printf("\nerror reading region at %lx",off);
+ break;
+ }
+
+ }
+ if(reg ==1 && get_mdid(buf))
+ {
+ printf("\nfailed to parse MDID strings");
+ break;
+ }
+ // force tag
+ lptr = (DWORD*) key_reg[reg];
+ *((DWORD *)buf) = *lptr; // set keyword identifier
+ }
+ }
+ else if((key=has_key_word(ln,key_words[reg],&args)) >= 0)
+ {
+
+ printf("\nkey: %s arg %s",key_words[reg][key],args);
+ hits++;
+ if(reg == 0) // doing a VTBL
+ {
+ tst = 1;
+ switch(key)
+ { //"desc","flag","dirsz","datasz","label","comp"
+ // should desc and lable be padded with blanks?
+ case 0: // desc
+ strncpy(vtbl->desc,args,44);
+ break;
+ case 1: // flag
+ if((tst=sscanf(args,"%x",&v)) == 1)
+ vtbl->flag = v & 0xff;
+ break;
+ case 2: // dirsz
+ if((tst=sscanf(args,"%lx",&lv)) == 1)
+ vtbl->dirSz = lv;
+ break;
+ case 3: // datasz
+ if((tst=sscanf(args,"%lx",&lv)) == 1)
+ vtbl->dataSz[0] = lv;
+ break;
+ case 4: // label
+ strncpy(vtbl->sdrv,args,16);
+ break;
+ case 5: // comp
+ if((tst=sscanf(args,"%x",&v)) == 1)
+ vtbl->comp = v & 0xff;
+ break;
+ }
+ if(tst != 1)
+ printf("\nfailed to parse: %s",ln);
+ }
+ else if(reg == 1 && key < MDID_SZ)
+ {
+ hits++;
+ tst = strlen(args);
+ if(tst == 0)
+ str = NULL;
+ else if((str = malloc(tst)) == NULL)
+ {
+ printf("\nalloc error for: %s",args);
+ break;
+ }
+ else
+ strcpy(str,args);
+
+ if(mdid_str[key] != NULL)
+ free(mdid_str[key]);
+ mdid_str[key] = str; // new string
+
+// MediumID - unique 19 decimal digits for identification
+// VR - version? always 0100
+// CS - ? followed by 4 hex bytes
+// FM - ? always followed by '2'? format?
+// UL - user label, ascii input string
+// DT - datetime of archive creation as 8 hex bytes
+ }
+ }
+ } while(lret != NULL);
+ }
+ if(fp != EOF)
+ close(fp);
+ if(fd != NULL)
+ fclose(fd);
+ return(suc);
+}
+
+int getnstr(char *buf,int len)
+{
+ int ret;
+ char *res;
+ if((res = fgets(buf,len,stdin)) != NULL)
+ {
+ ret = strlen(buf);
+ if(ret > 0 && buf[ret-1] == '\n')
+ buf[--ret] = 0; // remove it
+ }
+ return(ret);
+}
+
+int create_vtbl(struct vtbl_ver *cvtbl)
+{
+ int i,len,ret=0;
+ char cdate[15];
+ while(ret == 0)
+ {
+ printf(
+"\nVTBL memory image has been cleared, Win95 output format is set");
+ printf(
+"\nThe offsets for data and catalog set will be obtained from -sd and -sc");
+ printf(
+"\n if the -sd option is omitted the catalog is assumed to follow last data segment");
+ memset((char *)cvtbl, 0, sizeof(struct vtbl_ver));
+ strncpy(cvtbl->vtbl.tag,"VTBL",4);
+ cvtbl->ver = RECREAT;
+ cvtbl->vtbl.OStype = 7; // Win 95 format, I ignore, but use this as default
+ printf(
+"\nDefault is WINME format, ie first segment of each volume's data not compressed");
+ printf("\nIs this actually WIN95 format - ie no MDID region (Y/N) ");
+ if(bques(0) == 1) // this controls headers written
+ cvtbl->ver |= WIN95;
+ else
+ cvtbl->ver |= WINME;
+
+ cvtbl->vtbl.rev_major = 1; // these are my ver # not MicroSoft
+ cvtbl->vtbl.rev_minor = 11;
+ printf("\nWhat date would you like displayed (format mon/day/year): ");
+ i = getnstr(cdate,14);
+// never understood this fudge, see msqic.c:disp_vtbl()
+// my file dates work why does this require the fudge
+ cvtbl->vtbl.date = mk_date(cdate) + VTBL_DATE_FUDGE;
+ cvtbl->cnt = cvtbl->ndx = 1; // make do_decompress do WIN95 style output
+ printf("\nForce data compression flag? (Y/N) ");
+ if(bques(0) == 1)
+ {
+ cvtbl->vtbl.comp = 0x81;
+ // only update descriptions for compressed file, only time vtbl may be written
+ printf("\nDevice Lable (max 15 chars): ");
+ i = getnstr(cvtbl->vtbl.sdrv,15);
+ printf("\nDescription (max 43 chars): ");
+ i = getnstr(cvtbl->vtbl.desc,43);
+ }
+ printf("\nESC to abort, 'N' repeat above, or 'Y' to use above: ");
+ ret = bques(27);
+ }
+ if(ret == 1)
+ ret = 0;
+ else
+ ret = 1;
+ return(ret);
+}
+
+/* from msqic.c on 12/22/03 */
+// directory tree routines are recursive (ie not efficient, but easy)
+
+#define IND_LEV 25 // was hard coded as 10
+
+void disp_tree(CAT_LIST *ccat,int level,BYTE mode)
+{
+ BYTE i;
+ // make static so they don't take a lot of stack space
+ static char indent[IND_LEV*2+1]; // need a +1 for trailing nul
+ // definately was crashing without it
+ if(level <= 0)
+ {
+ i = 0;
+ indent[0] = 0;
+ if(mode & DIRONLY)
+ printf("\nListing of directory tree, no FILES displayed");
+ }
+ else
+ {
+ i = (level-1) *2;
+ if(i < IND_LEV)
+ strcpy(&indent[i]," ");
+ }
+ while(ccat != NULL) // remove debug break on: cnt++ < 30
+ { // add offsets in segment 10/8 off a little if compressed
+ if(!(mode & DIRONLY) || (ccat->flag & SUBDIR)) // display name
+ {
+ printf("\n%s%s",indent,ccat->name);
+ if(mode & S_SEGS) // display segment/offset debug info
+ printf(" %lx:%lx",
+ ccat->data_off / SEG_SZ, ccat->data_off % SEG_SZ);
+ }
+ if(ccat->child != NULL)
+ disp_tree(ccat->child,level+1,mode);
+ ccat = ccat->next;
+ }
+ indent[i] = 0; // remove padding on exit
+}
+
+// add mode flag so can search for SUBDIR or a FILE == 0
+CAT_LIST *tree_node(CAT_LIST *ccat,char *str,BYTE mode)
+{
+ int len;
+ while(ccat != NULL)
+ {
+ len = strlen(ccat->name);
+ if(strncmp(str,ccat->name,len)==0)
+ {
+ if(*(str+len ) == 0 && (ccat->flag & mode) == mode)
+ // exact match, end of target string, has correct mode
+ return(ccat);
+ else if ((ccat->flag & SUBDIR) && *(str+len) == DELIM &&
+ ccat->child != NULL)
+ return(tree_node(ccat->child,str+len+1,mode));
+ }
+ ccat = ccat->next;
+ }
+ return(NULL); // not found
+}
+
+/* recursively look for next node
+ at first tried depth first, but it isn't really
+ or wouldn't allow sub dir's at same level before those below
+ see sample from ralf and notes in 4_dir.lst
+ this routine tries to localize search from current node
+ when hit ENDDIR
+ Interesting, it can go down as may have added some directories
+ below this. An can scan next, but don't let it recursively
+ scan up
+*/
+CAT_LIST * next_tree_node(CAT_LIST * ccat)
+{
+ CAT_LIST * rnode=NULL, *ncat;
+ // check for valid children after this node
+
+ if(ccat != NULL && !(ccat->flag & EMPTYDIR) && (ccat->flag & SUBDIR))
+ {
+ if(ccat->child != NULL &&
+ !(ccat->child->flag & EMPTYDIR) && (ccat->child->flag & SUBDIR))
+ rnode = ccat->child;
+ else // check for others after immediate child
+ rnode = next_tree_node(ccat->child);
+ }
+
+ /* this node has no valid children, try next
+ some serious questions about order here.
+ apparently trace down children of next (depth first)
+ rather than (breadth first) checking all next in current chain
+ */
+ if(rnode == NULL && ccat != NULL)
+ {
+ ncat = ccat->next;
+ while(ncat != NULL && rnode == NULL)
+ {
+ if(!(ncat->flag & EMPTYDIR) && (ncat->flag & SUBDIR))
+ { // check for children before using this node
+ if((rnode = next_tree_node(ncat->child)) == NULL)
+ rnode = ncat; // use this as has no valid children
+ }
+ else
+ ncat = ncat->next;
+ }
+ }
+ return(rnode);
+}
+
+
+CAT_LIST *new_cat_list(CAT_LIST *prev,char *name,long off,struct ms_dir_fixed *fixed)
+{ char *str;
+ CAT_LIST * new=NULL;
+ if((str = strdup(name)) != NULL &&
+ (new = malloc(sizeof(CAT_LIST))) != NULL)
+ {
+ new->name = str;
+ new->parent = prev;
+ new->child = new->next = NULL;
+ new->data_off = off;
+ if(fixed != NULL)
+ {
+ new->flag = fixed->flag;
+ new->attrib = fixed->attrib;
+ new->datetime = fixed->m_datetime;
+ new->file_len = fixed->file_len;
+ }
+ else
+ {
+ new->flag = SUBDIR; // this is all to handle root, must be better way
+ new->attrib = 0;
+ new->datetime = 0;
+ new->file_len = 0;
+ }
+ }
+ return(new);
+}
+
+
+void free_cat_list(CAT_LIST *root)
+{
+ CAT_LIST *this;
+ while(root != NULL)
+ {
+ free_cat_list(root->child);
+ this = root;
+ root = root->next;
+ free(this);
+ }
+}
+
+/* extract one file. may be called from main via -x or from do_redirect()
+ CAUTION if the file is compressed, it repeats the decompression cycle
+ for EACH file. If you have a lot of small files in a segment this is
+ REDUNDANT. Oh well. Maybe you should decompress the entire file first.
+*/
+int do_extract(int fp,struct vtbl_ver *v,char *name,CAT_LIST *ccat)
+{
+ // extern references to stuff in decompression section which may get moved
+ extern int fin,fout;
+ extern long comp_wr;
+ extern char *dcompfnm; // working file name
+ int i,j,rd,fo=EOF,ft=EOF,suc = 1,cnt=0;
+ WORD sz; // temp store for segment size with RAW_SEG cleared
+ char *suc_xmsg = "\nSuccessful extraction";
+ FOFFSET soff=LSEEK_ERR, // save start offset into working file dcompfnm after decompress
+ cur_pos, // position in input file
+ tlng;
+ struct cseg_head shead;
+
+
+ if(access(name,0) == 0)
+ {
+ printf("\nfile exits: %s\n overwrite (Y/N) ",name);
+ if(bques(0) != 1)
+ return(-1);
+ }
+ if ((fo=open(name,O_BINARY|O_RDWR|O_CREAT|O_TRUNC,
+ S_IREAD|S_IWRITE)) == EOF)
+ printf("\nfailed to open %s",name);
+ else if((v->vtbl.comp & 0x80) == 0) // not compressed, just grab it
+ {
+ fin = fp;
+ fout = fo;
+ if(lseek(fp,ccat->data_off+v->database,SEEK_SET) == LSEEK_ERR)
+ {
+ printf("\nseek failed");
+ suc = -1;
+ }
+ else if((suc = copy_region(ccat->file_len)) != 0)
+ printf("\nextract failed");
+ else
+ printf(suc_xmsg);
+ }
+ else // its compressed, need to decompress seg(s) of interest first
+ // do limited range of logic in do_decompress()
+ {
+ if ((ft=open(dcompfnm,O_BINARY|O_RDWR|O_CREAT|O_TRUNC,
+ S_IREAD|S_IWRITE)) == EOF)
+ printf("\nfailed to open working file: %s",dcompfnm);
+ fin = fp; // set up globals for decompression
+ fout = ft;
+
+ soff = cur_pos = v->database; // start of data section
+ rd = 0;
+ // loop through looking for segment(s) or interest
+ while((tlng=lseek(fin,cur_pos,SEEK_SET)) == cur_pos &&
+ (rd=read(fin,&shead,sizeof(shead))) == sizeof(shead))
+ {
+/* either it is known to start in prior segment or
+ prior is last segment need to process to find out for sure
+*/
+ if(ccat->data_off < shead.cum_sz ||
+ shead.seg_sz == 0)
+ cnt++;
+ else if(cnt == 0) // update soff
+ soff = cur_pos;
+ if(ccat->data_off + ccat->file_len <= shead.cum_sz ||
+ shead.seg_sz == 0) // there are no more segments!
+ break; // know we got it all in prior segment
+ sz = shead.seg_sz & ~RAW_SEG; // clear flag
+ // add 6/01/07 for WIN95 decompress
+ cur_pos += SEG_SZ;
+ // cur_pos += sz+rd; // old code was WINME specific
+ rd = 0; // be sure this gets updated
+ }
+ if(cnt ==0)
+ {
+ printf("\nFailed to find segment(s) containing data");
+ return(suc);
+ }
+ // now extract data region of interest, cnt segments to working file
+ rd = 0;
+ cur_pos = soff;
+ soff = LSEEK_ERR;
+ i = 0;
+ while((tlng=lseek(fin,cur_pos,SEEK_SET)) == cur_pos &&
+ (rd=read(fin,&shead,sizeof(shead))) == sizeof(shead))
+ {
+ sz = shead.seg_sz & ~RAW_SEG; // clear flag
+ if(soff == LSEEK_ERR) // get start offset in 1st working file seg
+ soff = ccat->data_off - shead.cum_sz;
+ if(shead.seg_sz & RAW_SEG) // its not compressed
+ {
+ if((j = copy_region((long)sz)) != 0)
+ break;
+ }
+ else
+ decomp_seg(); // currently has no return!
+ if(++i >= cnt)
+ break; // do not clear rd, use as read(cseg_head) test
+ // input file position is a little random, do a seek
+ cur_pos += rd+sz; // where we want to be for next shead
+ rd = 0; // be sure this gets updated
+ }
+ // depends on global comp_wr being set by copy_region & decomp_seg()
+ // last seg needs special handling as shead.cum_sz = 0
+ if(shead.cum_sz == 0 && shead.seg_sz == 0)
+ shead.cum_sz = v->vtbl.dataSz[0];
+ if(rd < sizeof(shead) || i < cnt ||
+ ccat->data_off + ccat->file_len > shead.cum_sz+comp_wr)
+ printf("\nabort: error decompressing data");
+ else
+ {
+ printf("\nDecompressed %d segments to temp working file",cnt);
+ fin = ft;
+ fout = fo;
+ if( (cur_pos = lseek(ft,soff,SEEK_SET)) != soff ||
+ (suc = copy_region(ccat->file_len)) != 0)
+ printf("\nextract from working file failed");
+ else
+ printf(suc_xmsg);
+ }
+
+ }
+ if(fo != EOF)
+ {
+ close(fo);
+ set_fattrib(name,ccat->datetime,ccat->attrib);
+ }
+ if(ft != EOF)
+ close(ft);
+ return(suc);
+}
+
+
+#define PATH_LEN 255
+// make the following global to limit amount of stack space used in tree_copy()
+char tree_path[PATH_LEN+1]; // make this global to simplify recursion stack
+static struct stat sbuf; // used by do_redirect() and tree_copy()
+
+
+/* this is a recursive copy routine
+ copies all files in this subdir
+ if term == '*' will also copy all files in all subdir's
+
+ note uses global tree_path[PATH_LEN] to store file paths
+
+ccat->flag & SUBDIR && !(ccat->flag & EMPTYDIR)
+*/
+int tree_copy(int fp,struct vtbl_ver *v,CAT_LIST *ccat,int start,char term)
+{
+ int len,cnt=0,err;
+ while(ccat != NULL)
+ {
+ err = 0;
+ len = strlen(ccat->name);
+ if(start+len >= PATH_LEN)
+ {
+ printf("\nbuffer PATH_LEN exceeded: %s%c%s",
+ tree_path,DELIM,ccat->name);
+ if(term == '*' || term == '+')
+ fputc(DELIM,stdout);
+ err++;
+ }
+ else if(len > 0)
+ strcpy(tree_path+start,ccat->name); // append file of sub dir name
+ else
+ {
+ printf("\ndirectory name can not be empty");
+ err++;
+ }
+
+ if(ccat->flag & SUBDIR)
+ {
+ if(err == 0 && (term == '*' || term == '+')&& !(ccat->flag & EMPTYDIR))
+ {
+ if((err = stat(tree_path,&sbuf)) == 0 && // it exists
+ !(sbuf.st_mode & S_IFDIR) )
+ {
+ printf("\nsub dir path used as file: %s",tree_path);
+ err++;
+ }
+ else if(term == '+') // try to create the directory
+ {
+#ifdef unix
+ // accessable & searchable
+ err = mkdir(tree_path,S_IREAD | S_IWRITE | S_IEXEC);
+#else
+ err = mkdir(tree_path);
+#endif
+ if(err)
+ printf("\nmkdir(%s) failed",tree_path);
+ }
+
+ if(err == 0)
+ {
+ // attempts to copy all sub directories
+ tree_path[start+len++] = DELIM;
+ cnt += tree_copy(fp,v,ccat->child,start+len,term);
+ }
+ }
+ // else if term == DELIM ignore subdir
+ }
+ else // must be a file in this dir, extract it
+ if(err == 0 &&
+ (len=do_extract(fp,v,tree_path,ccat)) == 0)
+ cnt++;
+ ccat = ccat->next;
+ }
+ return(cnt);
+}
+
+/* step through list of paths
+ find via tree_node()
+ then step through copying file
+*/
+long do_redirect(int fp,struct vtbl_ver *v,CAT_LIST *root,PATH_ELEM paths[],int rcnt)
+{
+ CAT_LIST *ccat;
+ PATH_ELEM *pe;
+ char *des;
+ int p,i,derr;
+ long cnt=0;
+ printf("\nDoing Redirectable path based extract with %d source paths",rcnt);
+ for(i=0;iredirect != NULL)
+ des = pe->redirect; // use redirect path (may be empty)
+ else
+ des = pe->path; // use source path
+ /*
+ 1/20/04 try new system, named root for all trees, Win98 or Win95
+ */
+ ccat = tree_node(root,pe->path,SUBDIR);
+
+ derr = 0;
+ p = strlen(des);
+ if(ccat == NULL)
+ printf("\nsource path %d not found: %s",i,pe->path);
+ else if(ccat->flag & EMPTYDIR || ccat->child == NULL)
+ printf("\nsource directory empty: %s",pe->path);
+ else if(p > 0 && ((derr = stat(des,&sbuf)) != 0 ||
+ !(sbuf.st_mode & S_IFDIR) ) )
+ // if p == 0 we are using current directory, it better exist!
+ {
+ if(!(sbuf.st_mode & S_IFDIR))
+ {
+ printf("\nredirect path is not a subdir: %s",des);
+ derr++;
+ }
+ else if(pe->term == '+') // try to create the directory
+ {
+#ifdef unix
+ // accessable & searchable
+ derr = mkdir(des,S_IREAD | S_IWRITE | S_IEXEC);
+#else
+ derr = mkdir(des);
+#endif
+ if(derr)
+ printf("\nmkdir(%s) failed",des);
+ }
+ }
+
+ if(derr)
+ printf("\n unavailable destination path: %s",des);
+ else if(p+1 >= PATH_LEN)
+ printf("\nabort, path buffer length exceeded");
+ else if(ccat != NULL)
+ {
+ if(p > 0) // there is a des path, its not current directory
+ {
+ strcpy(tree_path,des); // global tree_path[] modified by tree_copy
+ tree_path[p++] = DELIM;
+ }
+ cnt += tree_copy(fp,v,ccat->child,p,pe->term);
+ }
+ }
+ return(cnt);
+}
+
+
+/* path remapping routines added 1/11/04
+ handles quoted strings which are required for LFN access
+*/
+#define LN_SZ 255
+
+int get_paths(char *fn,struct path_elem paths[],int sz)
+{
+ FILE *fp;
+ int i,r=0,cnt = 0,ln=0,len;
+ char quoted,*ch,line[LN_SZ+1];
+ if((fp = fopen(fn,"r")) == NULL)
+ {
+ printf("\nfailed to open redirect command file: %s ",fn);
+ return(-1); // open error
+ }
+ while(cnt < sz)
+ {
+ if(fgets(line,LN_SZ,fp) == NULL)
+ {
+ if(feof(fp))
+ printf("\neof after line %d",ln);
+ break;
+ }
+ else
+ {
+ paths[cnt].term = 0; // validatation
+ quoted = 0;
+ ln++;
+ len = strlen(line);
+ while(len > 0)
+ if(line[len-1] == '\n' || line[len-1] == ' ')
+ line[--len] = 0; // strip lf && trailing spaces so have string
+ else
+ break;
+ if(len <= 0 || line[0] == '#')
+ {
+ printf("\nskipping empty line %d",ln);
+ continue;
+ }
+
+ ch = line;
+ // skip leading blanks and possible quote
+ while(*ch != 0 && *ch == ' ')
+ {
+ len--;
+ ch++;
+ }
+
+ // allocate room for trailing NUL
+ if((paths[cnt].path = malloc(len+1)) == NULL)
+ {
+ printf("\nfatal alloc error");
+ break;
+ }
+ i = 0;
+ while(*ch != 0 && (quoted || *ch != ' ') && i < len)
+ {
+ if(*ch == '"')
+ quoted = quoted ^ 1;
+ else
+ *(paths[cnt].path+ i++) = *ch;
+ ch++;
+ }
+
+ if(i > 0)
+ paths[cnt].term = *(paths[cnt].path+ --i); // has a last char, save it
+ if(i == 0)
+ printf("\nerror line %d source path has length 0!",ln);
+ if(paths[cnt].term != DELIM && paths[cnt].term != '*' &&
+ paths[cnt].term != '+')
+ {
+ printf("\nerror line %d in source path must end in %c, *, +",
+ ln,DELIM);
+ i = -1;
+ }
+ else if (quoted)
+ {
+ printf("\nerror line %d unmatched quotes in source path",ln);
+ i = -1;
+ }
+
+ if(i > 0)
+ {
+ *(paths[cnt].path+ i++) = 0; // terminate source path
+
+ while(*ch != 0 && *ch == ' ' && i < len) // find start next
+ {
+ ch++;
+ }
+
+
+ if(*ch != 0 && i < len)
+ paths[cnt].redirect = paths[cnt].path+ i; // 2nd path exists
+ else
+ paths[cnt].redirect = NULL; // not redirected
+ while(*ch != 0 && (quoted || *ch != ' ') && i < len)
+ {
+ if(*ch == '"')
+ quoted = quoted ^ 1;
+ else
+ *(paths[cnt].path+ i++) = *ch;
+ ch++;r++;
+ }
+ *(paths[cnt].path+ i) = 0; // terminate redirect
+ if(!(*ch == 0 || *ch == ' ') || quoted || r == 0)
+ {
+ printf("\nerror line %d invalid redirect path %s",
+ ln,paths[cnt].redirect);
+ i = -1;
+ }
+ // check termination, will append strings to this.
+ // for consistency must end with DELIM
+ else if(*(paths[cnt].path+ i -1) != DELIM)
+ {
+ printf("redirect path must end with %c\n %s",
+ DELIM,paths[cnt].redirect);
+ i = -1;
+ }
+ else
+ {
+ if(strcmp(paths[cnt].redirect,"./") == 0 ||
+ strcmp(paths[cnt].redirect,".\\") == 0)
+ // its redirected to current directory, redirect path empty
+ paths[cnt].redirect = paths[cnt].path+ i;
+ // terminate string, r == strlen(paths[].redirect)
+ *(paths[cnt].path+ i-1) = 0; // delete DELIM
+ cnt++;
+ }
+ }
+ if(i <= 0)
+ {
+ printf("\n skipping: %s",line);
+ free(paths[cnt].path);
+ continue;
+ }
+ }
+ }
+ return(cnt); // # valid paths
+}
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.C"
new file mode 100644
index 0000000..48d8838
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.C"
@@ -0,0 +1,1438 @@
+/* ntbkup.c
+ Copyright (C) 2003 William T. Kranz
+ Check http://www.fpns.net/willy/msbackup.htm for updates.
+ contact info above or via snailmail: p.o. box 333, bradford, nh 03221
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+-----------------------------------------------------------------
+ History:
+
+ 12/29/03 rename xpbkup.c version 1.03 and change to
+ the MTF structures which I was forcing with the _MTF define.
+ 12/31/03 seems to be working. Should add CSUM stuff and
+ do a little more testing, but think its there.
+ 01/01/04 added FOFFSET logic for 4GB
+ also tweak do_file() changing return value if read file
+ and try to add CSUM logic. Think I have it, but not
+ quite sure why my padding algorithm works!
+
+ Interesting MSVC 5.0 cl seemed to promote the addition of
+ two WORDs to sizeof(int). see disp_unicode() calls.
+ I moved addition of params outside paramater block
+ ie ud = start+end
+ disp_unicode(start,ud) instead of disp_unicode(start,start+end)
+ and warnings went away. Seems dumb!
+
+ watch leading '/' stuff in do_filter() logic
+
+ 01/15/04 add prt_foff() to handle FOFFSET display
+ gets a bit messy, I only parse command line args for start
+ position as longs, then cast so limits some of my searches.
+
+ changed some of returns link do_tfdd() to FOFFSET
+ 01/19/04 fix some Linux -Wall warnings about unused vars
+ and add my own strupr() if unix (may be a problem in CYGWIN...)
+ 01/23/04 change version to 1.04, change -p to -l option
+ to limit based on path and use -p in future as I do in msqic
+ Add mode & LPATH define to control new -l option instead of depending
+ on path != null. Want to use path in XTREE mode also
+
+ 01/24/04 make return from do_file and FOFFSET, should have been
+ add SILENT to mode in main() for most options
+ 01/27/04 add set exclusion concept to tree creation, if -s
+ is used, it limits what's added to directory tree
+ -p no call do_redirect allowing * and + as terminator
+ 03/16/04 bump to version 1.03 see nttree and -p issue
+ also tweak mode flags for LPATH so display less info
+ 05/17/04 bump to version 1.04 tweat is_match() for multiple '.'
+ and nttree.c:find_path() to allow network drive specifications
+ 06/02/04 bump version to 1.05, add mode argument
+ to do_dirb() so don't display directories if mode & SILENT
+ 07/08/04 bump version to 1.06, change do_file() so that it creates
+ an empty file for FILE tags where there is no STAN stream
+ remove obsolete error message regarding 'failed to find STAN'
+ 04/05/05 bump version to 1.07 copy prior source to ntbkup04.c
+ correct some bugs parsing args, -s and -f
+ add include get_foff.c for parsing 64 bit offsets
+
+ may be getting carried away, but change type of len
+ in fnd_target() to FOFFSET, oops it had an error also
+ change how deal with rd
+
+ add optional end option via -j#:# for main loop
+ 12/20/06 change version to 1.07b to reflect change in nttree increasing
+ LN_SZ from 255 to 600
+ 12/30/06 change version to 1.07c as surround MAX_PATH define
+ with an ifndef to allow changing on command line and
+ increase default for 25 to 100. Also add display at startup
+-------
+ per dos.h: File attribute constants
+
+#define _A_NORMAL 0x00 Normal file - No read/write restrictions
+#define _A_RDONLY 0x01 Read only file
+#define _A_HIDDEN 0x02 Hidden file
+#define _A_SYSTEM 0x04 System file
+#define _A_VOLID 0x08 Volume ID file
+#define _A_SUBDIR 0x10 Subdirectory
+#define _A_ARCH 0x20 Archive file
+
+*/
+
+#include
+#include
+#include
+#include // for toupper()
+#include // define memset
+#include
+#include /* for open() defines */
+
+#if defined(MSDOS) || defined(_WIN32)
+# ifdef MSDOS
+# define OS_STR "MSDOS"
+# else
+# define OS_STR "WIN32"
+# endif
+#define DELIM '\\'
+
+#include
+#include // for lseek
+#include
+#else
+# ifdef __CYGWIN__
+# define OS_STR "CYGWIN"
+#else
+# define OS_STR "LINUX"
+#endif
+#include // for lseek, read, write
+#include
+#define DELIM '/'
+#define strnicmp strncasecmp
+#define stricmp strcasecmp
+#endif
+
+#ifdef _WIN32
+// this redefinition must be last! ie after io.h
+#define lseek _lseeki64
+#endif
+
+#include "ntbkup.h" // includes FOFFSET defines
+
+#include "get_foff.c"
+
+// a prototypes for functions called before defined
+FOFFSET do_file(int fp, FOFFSET soff,int mode); // uses global char *filter
+// prototypes for functions in nttree.c
+DIR_LIST *find_tree(DIR_LIST *next,char *str);
+DIR_LIST *find_path(DIR_LIST *next, char *str);
+void disp_tree(DIR_LIST *d);
+int add_tree_node(DIR_LIST **root, FOFFSET off, int key);
+int path_extract(int fp, DIR_LIST *node,unsigned long *fskip); // not used in this module
+int get_paths(char *fn,struct path_elem paths[],int sz);
+int do_redirect(int fp, DIR_LIST *root,struct path_elem *paths,int rcnt);
+
+
+
+
+#define LBUF_SZ 512 // local buffer on stack
+
+BYTE buf[XPBLK_SZ]; // used by most read routines. Assumes
+ // all blocks smaller than this, checks if correct
+
+char tree_path[PATH_LEN+1]; // make this global to simplify recursion stack
+
+#ifdef _4GB
+#define MAXINC 0x7fffffffL
+
+/* FOFFSET is an unsigned long when _4GB is defined
+ this limits SEEK_CUR and SEEK_END to calls with off == 0
+ to get file size or current position, ie not a generic routine
+*/
+FOFFSET lseek4(int fp,FOFFSET off,int origin)
+{
+ FOFFSET roff,lret=LSEEK_ERR;
+ long inc;
+ if(origin == SEEK_CUR || origin == SEEK_END)
+ {
+ if(off != 0L)
+ printf(
+"\nfatal error lseek called with none zero offset for SEEK_CUR or SEEK_END");
+ else
+ return((FOFFSET)lseek(fp,0L,origin));
+ }
+ // at two calls may be required
+ else if(origin == SEEK_SET)
+ {
+ while(lret == LSEEK_ERR)
+ {
+ if(off <= MAXINC)
+ inc = off;
+ if((roff = lseek(fp,inc,origin)) == LSEEK_ERR)
+ break;
+ off -= inc;
+ if(off == 0)
+ lret = roff; // we are done
+ }
+ }
+ return(lret);
+}
+#endif
+
+
+
+// see ntbkup.h for index defines into array below for is_keyword()
+char *keywords[] = {
+ "TAPE", // 0
+ "SFMB",
+ "SSET", // 2
+ "VOLB",
+ "DIRB", // 4
+ "FILE", // 5
+ "ESET",
+ "EOTM",
+ "CFIL",
+ "ESPB", // 9
+// MTF_MAX_BLK = ESPB = 9 rest are stream hdrs, although some on blk boundry
+ "TSMP",
+ "TFDD", // 11
+ // from here on not on XPBLK_SZ boundry, above seem to be
+ "NACL",
+ "NTQU", // 13
+ "CSUM",
+ "STAN",
+ "SPAD",
+ "FEND", // 17 terminates TFDD region
+ "NTOI", // follows CSUM in an NACL region
+ NULL
+};
+
+// return -1 on failure, or index into keywords[]
+int is_keyword(char *str)
+{
+ int i=0,j;
+ if(*((long *)str) == 0)
+ return(NUL_KEY);
+ while(keywords[i] != NULL)
+ {
+ for(j=0;j<4;j++)
+ if(*(keywords[i]+j) != *(str+j))
+ break;
+ if(j == 4)
+ return(i);
+ i++;
+ }
+ return(-1);
+}
+
+void get_xpdatetime(BYTE t[],unsigned short *yr, BYTE *mon, BYTE *day, BYTE *hr,
+ BYTE *min, BYTE *sec)
+{
+ *sec = t[4] & 0x3f; // 6 bits
+ *min = ((t[3] << 2) + (t[4] >> 6)) & 0x3f; // next 6 bits
+ *hr = t[3] >> 4; // low order 4 bits of hour.
+ if(t[2] & 1) // high bit of hour for full 5 bit value
+ *hr += 0x10;
+ *day = (t[2] >> 1) & 0x1f; // 5 bits
+ *mon = ((t[1] << 2) + (t[2] >> 6)) & 0xf; // 4 bits
+ *yr = t[0];
+ *yr = (*yr << 6) + (t[1] >> 2); // 14 bits
+}
+
+
+// warning no length qualifiers allowed in fmt, "ld" or "lx" expected
+void disp_offset(char *fmt,FOFFSET off)
+{
+ unsigned long ll=0x8000000L;
+ if(off < 0)
+ printf("< 0");
+ else if(off < ll) // can display directly
+ {
+ ll = off;
+ printf(fmt,ll);
+ }
+ else // its a 64 bit long long, break it up
+ {
+ ll = off >> 32;
+ printf(fmt,ll); // high order bytes
+ ll = off & 0xffffffff;
+ printf(fmt,ll); // low order bytes
+ }
+}
+
+void disp_xpdatetime(BYTE t[])
+{
+ WORD yr;
+ BYTE mon,day,hr,min,sec;
+ char str[4];
+ get_xpdatetime(t,&yr,&mon,&day,&hr,&min,&sec);
+ if(hr < 12)
+ {
+ strcpy(str,"AM");
+ if (hr == 0)
+ hr = 12;
+ }
+ else
+ {
+ strcpy(str,"PM");
+ if(hr > 12)
+ hr -= 12;
+ }
+ printf("%02d/%02d/%04d %02d:%02d:%02d %s",
+ mon,day,yr,hr,min,sec,str);
+
+}
+
+/* both unicode routines assume global buf[start] is start of unicode
+ and MAX is next char after end of string
+ ignores every 2nd char, ie treats as ascii
+ return # chars transfered just for giggles
+*/
+int disp_unicode(WORD start, WORD max)
+{
+ int len = 0;
+ char *ch= (char*)buf+start;
+ while(start < max)
+ {
+ fputc(*ch,stdout);
+ ch+=2;
+ start+=2;
+ len++;
+ }
+ return(len);
+}
+/* my original notes suggested if it was just the root the unicode
+ started with '/' but if there was a path it started with first
+ char.
+
+ Above seem incorrect. There is never a leading '/'
+ The path delimiter is a WORD = 0 in the string.
+ These should be replaced with the OS specific DELIM
+ Prior to 1/23/04 I used '/'
+ I insert a leading '/' if the path does not start with 0
+ AND contains more than one char.
+ ie its not just the root, but sub dir spec follows.
+ replace all occurances of 0 with DELIM in string which
+ always ends with a 0.
+*/
+int disp_unipath(WORD start, WORD max)
+{
+ int len = 0;
+ char *ch= (char*)buf+start,cout;
+ // odd, if root start with '/', else start with 1st path and no '/'
+ // maybe it has a zero for last '/' ??
+ if(max - start > 0 && *ch != 0)
+ fputc(DELIM,stdout);
+ // else code below puts up the leading and trailing '/' is 0
+ while(start < max)
+ {
+ if((cout = *ch) == 0)
+ cout = DELIM;
+
+ fputc(cout,stdout);
+ ch+=2;
+ start+=2;
+ len++;
+ }
+ return(len);
+}
+
+/* Warning returns chars read, but may insert a leading '/'
+ so string is longer by 1 char
+*/
+int getn_unipath(char *ch, WORD start, WORD max, int len)
+{
+ char inc=0;
+ if(max - start > 0 && *((char*)buf+start) != 0)
+ {
+ *(ch++) = DELIM;
+ inc = 1;
+ }
+ if(start+2*(len-inc) < max)
+ max = start+2*(len-inc);
+ len = 0; // use as counter now for chars read, does not include 1st DELIM
+ // else code below puts up the leading and trailing '/' is 0
+ while(start < max)
+ {
+ *ch = *((char *)buf+start);
+ if(*ch == 0)
+ *ch = DELIM;
+ ch++;
+ start+=2;
+ len++;
+ }
+ *ch = 0; // always terminate
+ return(len);
+}
+
+
+// copy uni-code to ascii buffer with max len chars
+int getn_unicode(char *ch,WORD start,WORD max,int len)
+{
+ if(start+2*len < max)
+ max = start+2*len;
+ len = 0; // use as counter now
+ while(start < max)
+ {
+ *(ch++) = buf[start];
+ start+=2;
+ len++;
+ }
+ *ch = 0; // assume space after len for NUL, always terminate
+ return(len);
+}
+
+
+// do_ routines below which don't return a long assume all data in one block
+// those that do return a long return # bytes in region or -1 if don't know
+void do_tape() // assume fits in one block
+{
+ MTF_TAPE *tptr = (MTF_TAPE *)buf;
+ MTF_TAPE_ADR *sdat;
+ WORD end;
+ fputc('\n',stdout);
+ sdat = &tptr->media_name;
+ end = sdat->offset+sdat->size;
+ // add end for MSVC 5.0, gives warning if don't construct per below
+ disp_unicode(sdat->offset,end);
+ fputc('\n',stdout);
+ sdat = &tptr->media_label;
+ end = sdat->offset+sdat->size;
+ disp_unicode(sdat->offset,end);
+ fputc('\n',stdout);
+ sdat = &tptr->software_name;
+ end = sdat->offset+sdat->size;
+ disp_unicode(sdat->offset,end);
+ fputc('\n',stdout);
+ fputc('\n',stdout);
+}
+
+void do_sset(int set) // assume fits in one block
+{
+ WORD end;
+ MTF_SSET *sptr = (MTF_SSET *)buf;
+ printf("\nSet %d: ",sptr->set_num);
+ printf("\nName: ");
+ end = sptr->set_name.offset +sptr->set_name.size;
+ disp_unicode(sptr->set_name.offset,end);
+ printf("\nDescription: ");
+ end = sptr->set_descript.offset +sptr->set_descript.size;
+ disp_unicode(sptr->set_descript.offset,end);
+ printf("\nUser: ");
+ end = sptr->user_name.offset +sptr->user_name.size;
+ disp_unicode(sptr->user_name.offset,end);
+ printf("\n");
+}
+
+// note main reads XPBLK_SZ bytes, first MTF_STREAM_HDR will always fit
+// data following may...
+// add display arg 12/8/05 if 0 advances without display to skip region
+FOFFSET do_tfdd(int fp, FOFFSET diroff,char display)
+{
+ int i,j,key,rd;
+ WORD ud,*wptr,off;
+ FOFFSET foff=0; // foff for cumulative read offset
+ // initializations below only valid after reads
+ MTF_FDD_FILE *file = (MTF_FDD_FILE *)buf; // same as DIRB
+ MTF_FDD_VOLB *volb = (MTF_FDD_VOLB *)buf;
+ MTF_FDD_HDR *head = (MTF_FDD_HDR *)buf;
+
+ off = sizeof(MTF_STREAM_HDR); // skip FDD stream header
+ /* the sub sections are all part of FDD, contained in .var_sz
+ should terminate with FEND, but watch for next block ID
+ next MTF_FDD_HDR starts with {WORD length, BYTE tag[]} ie 8 bytes
+ */
+ while((key=is_keyword(buf+off+sizeof(WORD))) != FEND && key > -1)
+ {
+ if(key != VOLB && key != DIRB && key != FILE_K)
+ break; // invalid key for FDD area
+ wptr = (WORD *)(buf+off); // length next region
+ foff+=off;
+ off = *wptr; // next offset after read
+ i = j = 0;
+ rd = *wptr + 8; // makes next length and tag valid
+ if(rd > XPBLK_SZ ||(FOFFSET) lseek(fp,diroff+foff,SEEK_SET) != diroff+foff ||
+ (j = read(fp,buf,rd)) != rd)
+ {
+ printf("\nTFDD fatal read error");
+ return(LSEEK_ERR);
+ }
+ else if(display)
+ {
+ printf("\n%4.4s: ",head->tag);
+ if(key == VOLB)
+ {
+ if(volb->dev_name.size > 0)
+ {
+ printf("\nDevice Name: ");
+ ud = volb->dev_name.offset+volb->dev_name.size;
+ disp_unicode(volb->dev_name.offset,ud);
+ }
+ if(volb->vol_name.size > 0)
+ {
+ printf("\nVolume Name: ");
+ ud = volb->vol_name.offset+volb->vol_name.size;
+ disp_unicode(volb->vol_name.offset,ud);
+ }
+ if(volb->mach_name.size > 0)
+ {
+ printf("\nMachine Name: ");
+ ud = volb->mach_name.offset+volb->mach_name.size;
+ disp_unicode(volb->mach_name.offset,ud);
+ }
+
+ }
+ else if (key == DIRB)
+ {
+ if(file->name.size)
+ {
+ ud = file->name.offset+file->name.size;
+ disp_unipath(file->name.offset,ud);
+ }
+ }
+ else if(key == FILE_K)
+ {
+ printf(" %8lu @ blk = %4lu ",
+ (DWORD)head->disp_sz,(DWORD)head->fmt_adr);
+ disp_xpdatetime(file->modify);
+ printf(" ");
+ // this is the long name [need to add MTF_TAPE_ADR]
+ ud = file->name.offset+file->name.size;
+ disp_unicode(file->name.offset,ud);
+ }
+ }
+ if(key == FEND)
+ {
+ if(display)
+ printf(" end of directory\n");
+ foff+= 8; // we read last 8 bytes, add it
+ break;
+ }
+
+ }
+ return(foff); // bytes read
+
+}
+
+
+
+/*
+ was unsure of this. MTF clearifies
+ 12/08/03 with additional examples, below isn't generic
+ Phillip's C: hits an NACL region first and its different.
+ I think its safer to just scan for next tag than guess at this
+ leave this routine for reference, but don't call it!
+ This worked when keyword at offset tag_head.var_sz was NTQU
+
+ Below was developed from only two drives so may well be
+ specific to them. No idea how many other keywords might be here!
+
+ if((key = is_keyword(buf+off)) == NTQU)
+ {
+ wptr = (WORD *)(buf+off+20); // offset into NTQU region
+ lret = off + *wptr; // data length of DIRB, may have word checksum at end?
+ } above takes one to within 3 bytes of end of SPAD region
+ below is better.
+
+ Algorithm below will fail if doesn't find an SPAD and
+ on return(-1) should resync
+*/
+long do_dirb(int fp,FOFFSET soff,int mode, int *nmlen)
+{
+ long lret=0;
+ MTF_DIRB *dir = (MTF_DIRB *)buf;
+ WORD sz,start;
+ int i;
+ char ch,*cptr;
+
+ if(dir->head.str_type) // have OS string
+ {
+ sz = dir->head.OS_data.size;
+ start = dir->head.OS_data.offset;
+#ifdef TEST
+ printf("\nOS data[%d] @ 0x%x ",sz,start);
+#endif
+ }
+ sz = dir->name.size;
+ start = dir->name.offset;
+
+ if(!(mode & SILENT) && sz && start+sz <= XPBLK_SZ)
+ {
+ printf("\nDir Name[%d]: ",sz); // leading slash off root assumed
+ disp_unipath(start,(WORD)(start+sz-1));
+ }
+
+ // return actual name length, copy up to max length to global tree_path[]
+ i = getn_unipath(tree_path,start,start+sz-1,PATH_LEN);
+ if(i != sz/2) // read truncated
+ i = -1;
+ else
+ i = 1;
+ *nmlen = i * strlen(tree_path); // actual string length always valid
+
+ return(lret);
+}
+
+int match_path(char *path,int path_len)
+{
+ int i,sz,suc = 0;
+ char *ch;
+ MTF_DIRB *dir = (MTF_DIRB *)buf;
+ sz = dir->name.size;
+ ch = buf+dir->name.offset;
+ if(sz/2 >= path_len && dir->name.offset+path_len <= XPBLK_SZ)
+ {
+ if(*(path+path_len-1) != '*' && sz/2 != path_len)
+ path_len = -1; // no wild card, no match
+ for(i=0;i XPBLK_SZ)
+ {
+ coff += off;
+ off = 0;
+ if ((FOFFSET)lseek(fp,coff,SEEK_SET) != coff ||
+ (rd=read(fp,buf,XPBLK_SZ)) != XPBLK_SZ)
+ {
+ if((off=(FOFFSET)lseek(fp,0L,SEEK_END)) != coff+rd)
+ {
+ printf("\nEOF at ");disp_foffset(off);
+ }
+ else
+ {
+ printf("\ndo_streams() file advance error at ");
+ disp_foffset(coff);
+ }
+ break;
+ }
+ blk = coff/XPBLK_SZ; // current block # were are in
+ // may have started read anywhere in this block
+ if(LSEEK_ERR != end)
+ if(coff >= end)
+ break; // end of range
+ }
+ key=is_keyword(buf+off);
+ toff = coff+off;
+ if(key == -1)
+ {
+ printf("\nkey 0x%lx not recognized at",*((DWORD*)(buf+off)));
+ disp_foffset(toff);
+ printf(", skip to next block boundry");
+ off = XPBLK_SZ;
+ }
+ else if(key >= 0 && key <= MTF_MAX_BLK)
+ {
+ hdr =(MTF_DB_HDR *)(buf+off);
+ printf("\n\n%4.4s Block Region attrib 0x%08lx @ ",hdr->tag,hdr->attrib);
+ disp_foffset(toff);
+ printf(" contains:");
+ off += hdr->var_sz;
+ }
+ else if(key > MTF_MAX_BLK)
+ {
+ shdr = (MTF_STREAM_HDR *)(buf+off);
+ printf("\n %4.4s attrib 0x%04x 0x%04x @ ",
+ shdr->tag,shdr->sys_attrib,shdr->media_attrib);
+ disp_foffset(toff);
+ off += sizeof(MTF_STREAM_HDR)+shdr->length; // skip struct + data
+ }
+ else
+ break;
+ // at least on case with ESET were (hdr->var_sz % 4) != 0
+ // most shdr->length need modulo work
+ if((rd = off % 4) != 0)
+ off += 4-rd; // next DWORD boundry
+ }
+
+ return(suc);
+}
+
+unsigned char mondays[] = {31,28,31,30,31,30,31,31,31,30,31,31};
+#define BASEYR 1970
+DWORD unix_time(BYTE t[])
+{
+ DWORD ret = t[4] & 0x3f; // low order 6 bits for seconds;
+ int i,tmp;
+ long mult = 60;
+ tmp = ((t[3] << 2) + (t[4] >> 6)) & 0x3f; // next 6 bits for min
+ ret += mult * tmp;
+ mult *= 60;
+ tmp = t[3] >> 4; // low order 4 bits of hour.
+ if(t[2] & 1) // high bit of hour for full 5 bit value
+ tmp += 0x10;
+ ret += mult * tmp;
+ mult *= 24;
+ tmp = (t[2] >> 1) & 0x1f; // 5 bits for day of month
+ if(tmp > 0)
+ tmp--;
+ ret += mult * tmp;
+ tmp = ((t[1] << 2) + (t[2] >> 6)) & 0xf; // 4 bits for month of year
+ if(tmp > 12)
+ tmp = 12; // an error, don't let array go out of bounds
+ if(tmp > 0)
+ tmp--; // make it a zero based array index
+ for(i=0;i> 2); // 14 bits for year this is base 0
+ if(tmp < BASEYR)
+ tmp = BASEYR;
+ i = BASEYR;
+ while(i < tmp) // step through years from base checking for leap years
+ {
+ if((i %100) == 0 || (i % 4) == 0)
+ ret += mult * 366; // leap year
+ else
+ ret += mult *365;
+ i++;
+ };
+ return(ret);
+}
+
+// may want to add a VERBOSE mode to query about extract later
+// note this is called both to skip over data, and extract depending on mode
+// CAREFUL my lbuf use a little screwy with the pad factor
+// may read further into it than LBUF_SZ bytes?
+BYTE lbuf[LBUF_SZ+4]; // make global so not on stack
+
+// global filter, set in main if used
+char *filter = NULL;
+
+FOFFSET do_file(int fp, FOFFSET soff,int mode)
+{
+ int fo=EOF,rd,ir,iw,key;
+ WORD wlen;
+ BYTE docksum=0,pad=0; // local buffer
+ DWORD flen=0,tlen,cksum=0,*cptr; // where we are now
+ FOFFSET off,toff;
+ MTF_FILE *ff; // fixed region
+ NT_FILE *ff2; // 2nd fixed region
+ MTF_STREAM_HDR *shdr;
+ struct utimbuf times;
+ ff = (MTF_FILE *)buf; // point to start local buffer
+ /* ff->var_sz points to first in list of streams as before
+ but now know structure, and should be able to skip ahead
+ to find the file data in STAN
+ 1st cut lets assume its in the buffer
+ */
+ off = ff->head.var_sz;
+ key = -1;
+ while(off < XPBLK_SZ && key != STAN)
+ {
+ shdr = (MTF_STREAM_HDR *)(buf+off);
+ key=is_keyword(buf+off);
+ if(key == NUL_KEY || (key >=0 && key <= MTF_MAX_BLK))
+ break; // end of this data region
+ off += sizeof(MTF_STREAM_HDR); // always add header length
+ if(key != STAN) // advance to next stream
+ {
+ off += shdr->length; // skip data
+ if((rd = off % 4) != 0)
+ off += 4-rd; // next DWORD boundry
+ }
+ else // the file is the data
+ flen = shdr->length; // warning its a QWORD in stream header!
+ }
+
+ /* Notes:
+ as of 6/8/04 try removing error message below, let flen==0 for an empty file
+ If there is no file data, the file is empty but should be created
+
+ if(key != STAN)
+ {
+ mode &= ~EXTRACT; // cancel xtract if active
+ mode |= SILENT; // display nothing more
+ printf("\ndo_file() failed to find STAN");
+ }
+
+ below was a much older note when first found MTF spec:
+ this is a simple patch to my older none MTF code
+ it stops searching the stream on detecting STAN
+ There is more after this, see do_stream() with -v option
+ Typically at least a checksum
+ */
+
+ if(flen > 0 && (mode & EXTRACT || !(mode & SILENT)) )
+ {
+ printf(" data from ");
+ toff = soff+off;
+ disp_foffset(toff);
+ printf(" to ");
+ toff = soff+off+flen;
+ disp_foffset(toff);
+ }
+
+
+ // if mode | extract, get the file
+ if(mode & EXTRACT)
+ {
+ if(shdr->media_attrib & STREAM_CHECKSUMED)
+ docksum++; // only updated if actually do extract, otherwise ignore
+
+ wlen = ff->name.offset; // LFN filename length
+ // 1st string from end struct to var_sz
+ ff2 = (NT_FILE *)(buf+ff->head.OS_data.offset); // 2nd fixed region
+ // ff2->nmlen > 0 if 2nd MSDOS file name exists
+#ifndef HAS_INT64 // it doesn't handle long file names either!
+ if(ff2->size > 0) // get the msdos name if it exists
+ {
+ wlen = ff->head.OS_data.offset+ff2->offset; // point to start short name
+ getn_unicode(lbuf,wlen,(WORD)(wlen+ff2->size),15);
+ }
+ else
+#endif // always use LFN if HAS_INT64 =>(LINUX || WIN32)
+ // use LFN if there is no MSDOS name, ie name is MSDOS 8.3 compatible
+ getn_unicode(lbuf,wlen,(WORD)(wlen+ff->name.size),LBUF_SZ);
+
+ // buf is never overwritten as use lbuf for copy
+ // so ff->name etc stay valid
+ // 12/4/03 add filter to skip some files
+ if (filter != NULL && !is_match(lbuf,filter))
+ docksum=0; // no-op do nothing
+ else if((fo=open(lbuf,O_BINARY|O_RDWR|O_CREAT|O_TRUNC,
+ S_IREAD|S_IWRITE)) == EOF)
+ printf("\nfailed to open %s for output",lbuf);
+
+ else if(flen > 0 && (FOFFSET)lseek(fp,soff+off,SEEK_SET) != soff+off)
+ // file data starts immediately after MTF_STREAM_HDR
+ printf("\nfailed to seek to start of data");
+ else
+ { // ff2->attrib is a DWORD, MSDOS byte is low order one, rest 0?
+ printf("\nlength %6ld atrib 0x%2x ",flen,(BYTE)ff2->attrib);
+ disp_xpdatetime(ff->modify);
+ printf("\nextracing: %s: ",lbuf);
+ // use local buffer for copy, preserve FILE info in buf[]
+ tlen = flen;
+ if(flen > 0 && docksum)
+ docksum++; // increment to 2, we are trying extract
+ // if flen == 0 skip check sum logic
+ while(tlen > 0)
+ {
+ if(tlen > LBUF_SZ)
+ rd = LBUF_SZ;
+ else
+ {
+ rd = tlen; // tail end of the file
+ /* 1/1/04 add pading based on off+flen, ie enough
+ to get me to the next stream header, normally a CSUM
+ */
+ pad = (off+flen) % 4;
+ if(pad) // wasn't modulo of 4 bytes pad the read
+ pad = 4 - pad;
+ memset(lbuf,0,LBUF_SZ); // clear buffer before last read
+ // this is an attempt to get a valid cksum
+ }
+ if((ir=read(fp,lbuf,rd+pad)) != rd+pad ||
+ (iw = write(fo,lbuf,rd)) != rd) // only write rd bytes
+ {
+ printf("\nio error writing file");
+ break;
+ }
+ else if(mode & VERBOSE)
+ fputc('.',stdout);
+ tlen -= rd; // tlen goes to zero breaking us out of loop
+
+ if(docksum) // do checksum logic
+ { /* below seems to work, but worries me
+ the MTF_STREAM_HDR is 22 bytes long
+ a two byte file would have a pad of 0, ir = 2
+ the cksum below would wrap two extra letters into lbuf
+ */
+ cptr = (DWORD*)lbuf;
+ for(iw=0;iwsize > 0) // get the msdos name if it exists
+ {
+ wlen = ff->head.OS_data.offset+ff2->offset; // point to start short name
+ getn_unicode(lbuf,wlen,(WORD)(wlen+ff2->size),15);
+ }
+ else
+#endif // always use LFN if HAS_INT64 =>(LINUX || WIN32)
+ // use LFN if there is no MSDOS name, ie name is MSDOS 8.3 compatible
+ getn_unicode(lbuf,wlen,(WORD)(wlen+ff->name.size),LBUF_SZ);
+
+ // attempt to set time stamp and attribute with generic functions
+ times.actime = times.modtime = unix_time(ff->modify);
+ rd = 0;
+ if(utime(lbuf,×) != 0) // set time stamp
+ rd = 1;
+
+ ir = S_IFREG | S_IREAD;
+ if((ff->attrib & 1) == 0) // its writable
+ ir |= S_IWRITE;
+ if(chmod(lbuf,ir) != 0)
+ rd |= 2;
+ if(rd)
+ printf("\nerror setting file: ");
+ if(rd & 1)
+ printf("timestamp ");
+ if(rd & 2)
+ printf("attributes");
+ fputc('\n',stdout); // be sure there is an lf
+ }
+ }
+ // always need to skip over file data, if extracted or not
+ off += flen+pad; // offset to end of data + pad alignment if extract
+ // WARNING for MTF should scan to end of stream list, and possibly do CSUM
+ if(docksum > 1) // we tried to extract file, advanced fp, cksum is valid
+ {
+ shdr = (MTF_STREAM_HDR *)lbuf;
+ if((rd = read(fp,lbuf,sizeof(MTF_STREAM_HDR))) !=
+ sizeof(MTF_STREAM_HDR) || is_keyword(lbuf) != CSUM ||
+ (ir = read(fp,lbuf+rd,(int)shdr->length)) != shdr->length)
+ printf("\ninput error reading file checksum");
+ else if(cksum != *((DWORD*)(lbuf+rd)) )
+ printf(" - checksum error!");
+ off += rd+ir;
+ }
+ return(off); // bytes to read or skip to get past file data
+ // Warning, still must advance to next block boundry
+}
+
+// warning, the input variables start and len are DWORD
+// I was orignally lazy parsing in main, but change to
+// len and start as FOFFSET 4/6/05
+void fnd_target(int fp,DWORD targ,FOFFSET start,FOFFSET len)
+{
+ int i,rd;
+
+ printf("\nsearching file for 0x%lx => ascii:%4.4s from offset ",
+ targ,(char *)&targ);
+ disp_foffset(start);
+ printf(" to ");
+ if(len == LSEEK_ERR)
+ printf("EOF");
+ else
+ {
+ disp_foffset(start+len);
+ }
+ if((FOFFSET)lseek(fp,start,SEEK_SET) != start)
+ {
+ printf("\nseek to start failed");
+ return;
+ }
+
+ while((len == LSEEK_ERR || len > 0) && (rd = read(fp,buf,XPBLK_SZ)) > 4)
+ {
+ if(len > 0)
+ {
+ if(len < rd)
+ {
+ rd = len;
+ len = 0;
+ }
+ else
+ len -= rd;
+ }
+ for(i=0;idev_name.offset;
+ sz = volb->dev_name.size;
+ max = start+sz-1;
+ if(sz > 0 && max < XPBLK_SZ)
+ {
+ printf("\ndevice name: ");
+ disp_unicode(start,max);
+ }
+ start = volb->vol_name.offset;
+ sz = volb->vol_name.size;
+ max = start+sz-1;
+ if(sz > 0 && max < XPBLK_SZ)
+ {
+ printf("\nvolume name: ");
+ disp_unicode(start,max);
+ }
+ start = volb->dev_name.offset;
+ sz = volb->dev_name.size;
+ nmlen = getn_unicode(tree_path,start,start+sz-1,PATH_LEN);
+ return(nmlen);
+}
+
+#ifndef MAX_PATH
+#define MAX_PATH 100
+#endif
+
+int main(int argc, char *argv[])
+{
+ struct path_elem paths[MAX_PATH];
+ MTF_DB_HDR *th;
+ DIR_LIST *root=NULL;
+ int fp,i,rd,key,mode = 0,tmode,path_len,dir_len,rcnt=0,
+ tset=0,cset=0; // target and current set values, tset = 0 for all
+ // NTbackup uses 1 based sets, ie 1st is #1, 2nd is #2
+ FOFFSET off = 0,skip,len,toff,start=LSEEK_ERR,end= LSEEK_ERR;
+ DWORD targ=0;
+ char *ch,flag,*fmt,*path=NULL;
+ printf("\nNTBKUP Ver 1.07c compiled for %s with MAX_PATH = %d",
+ OS_STR,MAX_PATH);
+#ifdef _4GB
+ printf("\n max file size 4Gb");
+#endif
+#ifdef HAS_INT64
+ printf("\n compiled for 64 bit file offsets");
+ if(sizeof(FOFFSET) != 2 * sizeof(long))
+ {
+ printf("\nfatal error, however sizeof(FOFFSET) = %d",sizeof(FOFFSET));
+ printf(
+"\nif compiled with gcc, you probably forgot -D_FILE_OFFSET_BITS=64");
+ exit(0);
+ }
+#endif
+ printf("\nCopyright (C) 2003 William T. Kranz");
+ printf("\nNTBKUP comes with ABSOLUTELY NO WARRANTY");
+ printf(
+"\nFree software distributed under the terms of the GNU General Public license");
+ printf(
+"\nSee http://www.gnu.org/licenses/gpl.html for license information");
+
+ printf(
+"\nCheck http://www.fpns.net/willy/msbackup.htm for Updates & Documentation\n");
+
+ // debug aid, check structure sizes
+ if(argc > 1 && strnicmp(argv[1],"-ss",3) == 0)
+ {
+ printf("\nknown strutures:");
+ printf("\nsizeof(FOFFSET) = %d",sizeof(FOFFSET));
+ printf("\nsizeof(MTF_TAPE_ADR) = %d",sizeof(MTF_TAPE_ADR));
+ printf("\nsizeof(MTF_DB_HDR) = %d",sizeof(MTF_DB_HDR));
+ printf("\nsizeof(MTF_STREAM_HDR) = %d",sizeof(MTF_STREAM_HDR));
+ printf("\nsizeof(MTF_TAPE) = %d",sizeof(MTF_TAPE));
+ printf("\nsizeof(MTF_VOLB) = %d",sizeof(MTF_VOLB));
+ printf("\nsizeof(MTF_DIRB) = %d",sizeof(MTF_DIRB));
+ printf("\nsizeof(MTF_SSET) = %d",sizeof(MTF_SSET));
+ printf("\nsizeof(MTF_FILE) = %d",sizeof(MTF_FILE));
+ printf("\nsizeof(MTF_FDD_HDR) = %d",sizeof(MTF_FDD_HDR));
+ printf("\nsizeof(MTF_FDD_VOLB) = %d",sizeof(MTF_FDD_VOLB));
+ printf("\nsizeof(MTF_FDD_DIRB) = %d",sizeof(MTF_FDD_DIRB));
+ printf("\nsizeof(NT_FILE) = %d",sizeof(NT_FILE));
+
+ fputc('\n',stdout); //cleanup for linux
+ exit(0); // we are done
+ }
+
+ if(argc < 2)
+ {
+ printf(
+"\nntbkup [-x] [-l] [-p] [@] [-c] [-d] [-f] [-j#] [-s#] [-t] [-v]");
+ printf("\n -x[filter] to unconditionally extract all files matching filter");
+ printf("\n -l where full case sensitive path limits extract");
+ printf("\n -p recursive path based directory extract");
+ printf("\n @ use path based extract and redirection command file");
+ printf("\n all extracts use [filter] from -x, default filter is *.*");
+ printf("\n -c to display catalog regions for TAG == TFDD");
+ printf("\n -d display directory tree from raw data region");
+ printf(
+"\n -f[:start[:len]] finds 4 char tag, optional start pos and length");
+ printf(
+"\n -j#[:#] jump to start position in file for data recovery (modulo 0x400)");
+ printf("\n optionally follow start offset with :# for an end offset");
+ printf("\n -s# to limit options above to a particular SET by #");
+ printf("\n -t[:start[:end]] display tags only from start to end");
+ printf("\n -v to set verbose mode");
+ // -ss to show structure sizes is hidden
+ fputc('\n',stdout); //cleanup for linux
+ exit(0);
+ }
+ else // start at i = 1 as special case so can have -ss as 1st argument
+ for(i=1;i 0)
+ {
+ printf("\nresrict operations to backup set %d",rd);
+ tset = rd;
+ }
+ else if(strnicmp(argv[i],"-v",2) == 0)
+ {
+ mode &= ~SILENT;
+ mode |= VERBOSE;
+ }
+ else if(strnicmp(argv[i],"-f",2) == 0)
+ {
+ ch = argv[i] + 2; // start target string
+ if(strlen(ch) >= 4)
+ {
+ targ = *((long *)ch);
+ start = 0L;len = LSEEK_ERR; // to eof
+ if((ch = strchr(ch,':')) != NULL) // have start
+ {
+ get_foffset(ch+1,&start); // aborts on error
+ if((ch = strchr(ch+1,':')) != NULL) // have len
+ get_foffset(ch+1,&len);
+ }
+ }
+ else
+ printf("\ninvalid argument for -f, min 4 chars required\n");
+ }
+ else if(strnicmp(argv[i],"-t",2) == 0)
+ {
+ start = 0L;len = LSEEK_ERR; // to eof
+ get_foffset(argv[i]+2,&start); // aborts on error!
+ if((ch = strchr(argv[i]+3,':')) != NULL) // there is a len
+ get_foffset(ch+1,&len);
+ mode |= TAGS;
+ }
+ else if(strnicmp(argv[i],"-j",2) == 0)
+ {
+ get_foffset(argv[i]+2,&toff); // aborts on error!
+
+ if((rd = (toff % XPBLK_SZ)) != 0)
+ toff -=rd; // modulo 0x400
+ off = toff;
+ if((ch = strchr(argv[i]+3,':')) != NULL) // there is an end
+ get_foffset(ch+1,&end);
+ }
+ else if(strnicmp(argv[i],"-p",2) == 0)
+ {
+ mode &= ~LPATH; // clear it, last options gets control
+ mode |= TREE|XTREE|SILENT;
+ path = argv[i]+2;
+ }
+ else if(strnicmp(argv[i],"-l",2) == 0)
+ {
+ // this was first try, worked, limits on -x style extract
+ // to a single path, change to -l 1/23/04
+ mode &= ~XTREE; // clear it
+ mode |= LPATH|SILENT;
+ // force extract mode so don't need -x also
+ path = argv[i]+2;
+ path_len = strlen(path);
+ if(path_len == 0)
+ continue;
+ if(*(path+path_len-1) != '\\' &&
+ *(path+path_len-1) != '/')
+ path_len++; // treat trailing NUL as last slash
+ for(rd = 0;rd 1)
+ {
+ path_len--;
+ path++; // skip expected leading '/', but ignore if not there
+ }
+ }
+ else if(strnicmp(argv[i],"-x",2) == 0)
+ {
+ mode |= EXTRACT|SILENT;
+ if(*(argv[i]+2) != 0)
+ filter = argv[i]+2;
+ }
+ else if(*argv[i] == '@' &&
+ (rcnt = get_paths(argv[i]+1,paths,MAX_PATH)) > 0)
+ mode |= TREE|SILENT;
+
+ }
+
+ if((fp = open(argv[1],O_BINARY|O_RDONLY)) == EOF)
+ printf("\nfailed to open %s",argv[1]);
+ else if(targ != 0L) // just do search and exit
+ fnd_target(fp,targ,start,len);
+ else if(mode & TAGS) // just display range of tags
+ do_streams(fp,start,len);
+ else
+ {
+ if(off != 0L)
+ {
+ printf("\nSEEK to file position ");
+ disp_foffset(off);
+ printf(" for data recovery");
+ if((FOFFSET)lseek(fp,off,SEEK_SET) != off)
+ {
+ printf(" - FAILED");
+ off = 0L; // best guess, affects display of position
+ }
+ }
+ while((rd = read(fp,buf,XPBLK_SZ)) > 4)
+ {
+ /*
+ 12/03/04 think I now skip in most cases, see skip below
+ except DIRB. Can conditionally enable skip there.
+ */
+ skip = 0;
+ if((key=is_keyword((char *)buf)) > -1 && !(mode & SILENT))
+ {
+ printf("\n%4.4s found keyword at offset ",buf);
+ disp_foffset(off);
+ }
+
+ // look at MTF checksum now that I know it exists
+ if(key >= 0 && key <= MTF_MAX_BLK)
+ i = hd_cksum((WORD *)buf,0x19); // block header cksum
+ else if (key > 0) // assume stream header
+ i = hd_cksum((WORD *)buf,0xa);
+ if(key >= 0 && (mode & VERBOSE))
+ {
+ if(i !=0)
+ printf("\ncksum error in header");
+ }
+
+ if(key == TAPE && !(mode & SILENT))
+ {
+ do_tape(); // descriptive strings
+ }
+ else if(key == SSET)
+ {
+ if(!(mode & SILENT))
+ printf(" blk = %3lu",(unsigned long)(off/XPBLK_SZ));
+ cset++; //increment current set
+ if(tset == 0 || cset == tset)
+ do_sset(cset); // display descriptive strings
+ }
+ else if(key == VOLB)
+ {
+ dir_len = do_volb();
+ // don't care if truncated, although not likely, use string
+ if((tset == 0 || cset == tset) && mode & TREE)
+ rd = add_tree_node(&root, off, key);
+ }
+ else if(key == DIRB)
+ {
+ /* both examples I've seen have had tag_head
+ followed by what looks like a disk specific structure
+ */
+ th = (MTF_DB_HDR *)buf;
+ if(!(mode & SILENT))
+ printf(" 1st Stream: %4.4s",buf+th->var_sz);
+ if(mode & LPATH && path != NULL) // its _MTF aware, and doing an extract
+ {
+ if(match_path(path,path_len))
+ mode |= EXTRACT; // turn it on
+ else
+ mode &= ~EXTRACT; // turn off
+ }
+ skip = do_dirb(fp,off,mode,&dir_len); // this can be big...
+ if((tset == 0 || cset == tset) && mode & TREE)
+ {
+ if(dir_len <= 0)
+ printf("\nskip truncated path");
+ else
+ rd = add_tree_node(&root, off, key);
+ }
+ }
+ else if(key == FILE_K)
+ {
+ tmode = mode;
+ if(tset > 0 && tset != cset)
+ {
+ tmode &= ~EXTRACT; // don't extract, not current set
+ tmode |= SILENT; // in fact, display nothing!
+ }
+ skip = do_file(fp,off,tmode); // skips over file data, could extract
+ }
+ else if(key == TFDD)
+ {
+ if( (mode & CATALOG) &&
+ (tset == 0 || tset == cset)) // doing all or just this one
+ flag = 1;
+ else
+ flag = 0; // parse region so skip over all of it
+
+ skip = do_tfdd(fp,off,flag);
+ }
+ if(skip == LSEEK_ERR)
+ printf("\nWaring skip error"); // 12/8/03 do nothing break; // fatal error
+ if(skip > XPBLK_SZ)
+ off += skip;
+ else
+ off += XPBLK_SZ; // always advances one block
+
+ /* did routine actually do the advance? do_file() only does if EXTRACT
+ and if it did, did to skip ahead to next block boundry
+ */
+ if((skip = (FOFFSET)lseek(fp,0L,SEEK_CUR)) != off || skip % XPBLK_SZ != 0)
+ {
+ if(off % XPBLK_SZ)
+ off -= off % XPBLK_SZ;
+ if((skip = (FOFFSET)lseek(fp,off,SEEK_SET)) != off)
+ {
+ printf(" failed to skip ahead to next block");
+ exit(1);
+ }
+
+ }
+ if(end != LSEEK_ERR && off >= end)
+ {
+ printf("\nparse terminated by end value from -j option at ");
+ disp_foffset(off);
+ break;
+ }
+ }
+
+ }
+
+ // TREE routines require a pass through the file to build the tree
+ if(root != NULL)
+ {
+ if(mode & DTREE)
+ disp_tree(root);
+ if(mode & TREE && rcnt > 0)
+ do_redirect(fp,root,paths,rcnt);
+ else if(mode & XTREE && path != NULL)
+ {
+ i = strlen(path);
+ i--;
+ paths[0].term = *(path+i);
+ if(i > 0 && (paths[0].term == DELIM ||
+ paths[0].term == '*' || paths[0].term == '+'))
+ {
+ paths[0].path = path;
+ *(path+i) = DELIM; // force for level detection
+ paths[0].redirect = path+i+1; // empty string
+ do_redirect(fp,root,paths,1); // allows recursive directory creation
+ }
+ else
+ printf("\n-p option, invalid path %s",path);
+/* note: following logic is a good test for path_extract()
+ assumes path terminated with DELIM?
+ have since replace with call to do_redirect() which I
+ developed later to allow sub directory creation
+ DIR_LIST *dir; // required allocation
+ unsigned long fskipped=0;
+ if((dir = find_path(root,path)) != NULL)
+ {
+ printf("\nDIRB record containing path at offset ");
+ disp_foffset(dir->data_off);
+ path_extract(fp,dir,&fskiped);
+ }
+*/
+ }
+
+ }
+ fputc('\n',stdout); //cleanup for linux
+ return(0);
+}
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.H" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.H"
new file mode 100644
index 0000000..04170c8
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.H"
@@ -0,0 +1,359 @@
+/* ntbkup.h see ntbkup.c and xpbkup.h
+ original concepts developed in xpbkup.c
+ Then discovered MTF100a.pdf and attempt to
+ change over to MTF structures.
+
+Sorry about the mess below with structure packing.
+#pragma pack(1) is a little sloppy, but works well with
+ MSDOS, WIN32, and CYGWIN
+However my Linux 2.4 gcc Version doesn't like it
+while CYGWIN's gcc Version 3.3.1 ignores this alternate approach
+ __attribute__ ((packed)) for each structure
+If someone can tell me why I'd like to know.
+See the hidden -ss option in main to check your
+structure sizes if you try another compiler.
+
+
+time is an array of 5 bytes which is treated as
+a bit field with the 1st byte being the MSB and the 5th
+byte being the LSB. Parsing from LSB to MSB
+seconds 6 bits range 0-59
+minutes 6 bits range 0-59
+hour 5 bits range 0-23
+day 5 bits range 1-31
+month 4 bits range 1-12
+year 14 bits range 0 - 16383
+
+definately room after/in year for other info, but for
+files I've looked at which were created in 2003, the value
+of year has been 2003
+
+see dos.h for DOS bitmap of attribute byte.
+
+12/29/03 copy from xpbkup.h go to MTF specific defines
+ which were triggered by defining _MTF in prior work
+01/01/04 add the FOFFSET logic from Msqic to allow
+ 4GB files under dos
+01/02/04 add WIN32 logic for MSVC 5.0
+01/17/04 neither MTF_DB_HDR nor MTF_FDD_VOLB needs PACKED
+12/30/06 remove define MAX_PATH=25, its done in ntbkup.c and
+ then passed as an argument to get_paths in nttree.c
+ Can one modify on command line when compile ntbkup.c
+*/
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+
+
+#ifndef MSDOS // => _WIN32 or Linux
+#define HAS_INT64
+#define LSEEK_ERR (-1) // this works for standard lseek stuff
+#ifdef _WIN32
+#pragma pack(1)
+#define PACKED ;
+typedef __int64 FOFFSET;
+#define QWORD FOFFSET // close enough
+#else // Unix gcc assumed
+typedef unsigned long long QWORD;
+// return from lseek, or lseeki64 controlled via -D_FILE_OFFSET_BITS=64
+typedef off_t FOFFSET;
+#ifdef __CYGWIN__
+#pragma pack(1) // Linux gcc won't compile with this, see above
+#define PACKED ;
+#else
+// to pack linux structures selectively
+#define PACKED __attribute__ ((packed));
+#define O_BINARY 0 // this Microsoft mode flag undefined in Linux gcc
+#endif
+#endif
+
+#else // its MSDOS
+// in DOS just pack everything
+#pragma pack(1)
+#define PACKED ;
+
+#ifdef _4GB
+typedef unsigned long FOFFSET;
+#define LSEEK_ERR ((DWORD)-1L)
+#else
+typedef long FOFFSET; // 2 GB is default std C
+#define LSEEK_ERR (-1L)
+#endif
+
+#endif
+
+
+#define XPBLK_SZ 0x400 // max blocking factor
+#define PATH_LEN 255 // max path length in tree_path[]
+
+
+/* had defined mode bits 0-3, through SILENT and tested
+ for mode == 0 to display normal message.
+ should now be mode & SILENT inhibits messages
+ mode & VERBOSE gives additional messages
+*/
+#define EXTRACT 1
+#define CATALOG 2
+#define SILENT 4
+#define VERBOSE 8
+#define LPATH 0x10 // limit xtract based on path
+#define TAGS 0x20
+#define TREE 0x40
+#define DTREE 0x80
+#define XTREE 0x100
+
+
+// index defines into array string for is_keyword() in ntbkup.c
+// they make error checks generic, but must be updated if array changed
+#define NUL_KEY (-2) // returns if the long is == 0
+#define TAPE 0
+#define SSET 2
+#define VOLB 3
+#define DIRB 4
+#define FILE_K 5 // FILE would conflict stdio.h
+#define MTF_MAX_BLK 9
+#define TFDD 11
+#define NACL 12
+#define NTQU 13
+#define CSUM 14
+#define STAN 15
+#define SPAD 16
+#define FEND 17
+#define NTOI 18
+
+
+
+
+// MTF defines
+// string type: id MTF_DB_HDR.str_type
+#define NO_STRINGS 0
+#define ANSI_STR 1
+#define UNICODE_STR 2
+
+
+typedef struct mtf_tape_adr {
+WORD size, // string length (its not nul terminated)
+ offset; // from start struct containing MTF_TAPE_ADR
+} MTF_TAPE_ADR;
+
+typedef struct mtf_db_hdr {
+BYTE tag[4];
+DWORD attrib;
+WORD var_sz;
+BYTE OS_id,OS_ver; // see MTF appendix A, {0xE,0x2} in samples
+#ifdef HAS_INT64
+QWORD disp_sz,
+ fmt_adr;
+#else
+DWORD disp_sz,
+ disp_sz_hi,
+ fmt_adr,
+ fmt_adr_hi;
+#endif
+BYTE res_MBC[2],
+ res[6];
+DWORD blk_id;
+BYTE res2[4];
+MTF_TAPE_ADR OS_data;
+BYTE str_type,
+ res3;
+WORD cksum;
+} MTF_DB_HDR;
+
+// note the common header above embedded at start of each common block
+struct mtf_tape {
+MTF_DB_HDR head;
+DWORD media_id,
+ attrib;
+WORD media_seq,
+ pass_encryp,
+ soft_blk_sz,
+ media_cat_type;
+MTF_TAPE_ADR media_name,
+ media_label,
+ media_pass,
+ software_name;
+DWORD fmt_blk_sz,
+ vendor_id;
+BYTE media_date[5],
+ MTF_major_ver;
+} PACKED
+
+typedef struct mtf_tape MTF_TAPE;
+
+struct mtf_volb {
+MTF_DB_HDR head;
+DWORD attrib;
+MTF_TAPE_ADR dev_name,
+ vol_name,
+ mach_name;
+BYTE date_written[5];
+} PACKED
+
+typedef struct mtf_volb MTF_VOLB;
+
+typedef struct mtf_dirb {
+MTF_DB_HDR head;
+DWORD attrib;
+// following are 4 times
+BYTE modify[5],
+ create[5],
+ backup[5],
+ access[5];
+DWORD dir_id;
+MTF_TAPE_ADR name; // includes trailing NUL => '/'
+} MTF_DIRB;
+
+struct mtf_sset {
+MTF_DB_HDR head;
+DWORD attrib;
+WORD encrypt_alg,
+ comp_alg,
+ vendor_id,
+ set_num;
+MTF_TAPE_ADR set_name,
+ set_descript,
+ set_passwd,
+ user_name;
+#ifdef HAS_INT64
+QWORD blk_adr;
+#else
+DWORD blk_adr,
+ blk_adr_hi;
+#endif
+BYTE written[5], // date/time written
+ sft_major_ver,
+ sft_minor_ver,
+ time_zone,
+ MTF_minor_ver,
+ media_cat_ver;
+} PACKED
+typedef struct mtf_sset MTF_SSET;
+
+typedef struct mtf_file {
+MTF_DB_HDR head;
+DWORD attrib;
+// following are 4 times
+BYTE modify[5],
+ create[5],
+ backup[5],
+ access[5];
+DWORD dir_id,
+ file_id;
+MTF_TAPE_ADR name;
+} MTF_FILE;
+
+
+
+// Stream Media Format Attributes
+#define STREAM_CONTINUE 1 //This is a continuation stream. BIT0
+#define STREAM_VARIABLE 2 //Data size for this stream is variable. BIT1
+#define STREAM_VAR_END 4 //Last piece of the variable length data. BIT2
+#define STREAM_ENCRYPTED 8 //This stream is encrypted. BIT3
+#define STREAM_COMPRESSED 0x10 //This stream is compressed. BIT4
+#define STREAM_CHECKSUMED 0x20 // checksum stream follows. BIT5
+#define STREAM_EMBEDDED_LENGTH 0x40 //Stream length embedded in data. BIT6
+
+struct mtf_stream_hdr {
+BYTE tag[4];
+WORD sys_attrib,
+ media_attrib;
+#ifdef HAS_INT64
+QWORD length;
+#else
+DWORD length,
+ length_hi; // and you better hope its 0!
+#endif
+WORD encrypt_alg,
+ compress_alg,
+ cksum;
+} PACKED
+typedef struct mtf_stream_hdr MTF_STREAM_HDR;
+
+
+// embedded in TFDD stream, mini headers:
+typedef struct mtf_fdd_hdr {
+WORD length;
+BYTE tag[4];
+WORD media_seq;
+DWORD attrib; // of parent common block
+#ifdef HAS_INT64
+QWORD fmt_adr, // maps to XPBLK_SZ block # in file (was blk in xpbkup.h)
+ disp_sz; // for a data file = file length, ie in MTF_FDD_FILE
+#else
+DWORD fmt_adr,
+ fmt_adr_hi,
+ disp_sz,
+ disp_sz_hi;
+#endif
+long link;
+BYTE os_id,
+ os_ver,
+ str_type,
+ pad;
+} MTF_FDD_HDR;
+
+struct mtf_fdd_volb {
+MTF_FDD_HDR head;
+DWORD attrib; // distrinct from head.attrib!?
+MTF_TAPE_ADR dev_name,
+ vol_name,
+ mach_name,
+ OS_data;
+BYTE date_written[5];
+} PACKED
+typedef struct mtf_fdd_volb MTF_FDD_VOLB;
+
+struct mtf_fdd_dirb {
+MTF_FDD_HDR head;
+// following are 4 date/times
+BYTE modify[5],
+ create[5],
+ backup[5],
+ access[5];
+DWORD attrib;
+MTF_TAPE_ADR name, // includes trailing NUL => '/'
+ OS_data;
+};
+
+typedef struct mtf_fdd_dirb MTF_FDD_DIRB;
+typedef struct mtf_fdd_dirb MTF_FDD_FILE;
+// note MTF_FDD_FILE is currently identical to MTF_FDD_DIRB
+
+/* The only place things become slightly unclear is with regard
+ to the OS_data for these last two typedefs.
+ Its OS ID and OS version specific. I assume ID=14 for NT
+ in this program, and use the Version 1 file structure.
+ The current files I've seen are OS Version 2, but it doesn't seem
+ to have changed. There is a similar structure (without the flags field)
+ for NT_DIRB which I am currently ignoring.
+*/
+typedef struct nt_file {
+DWORD attrib; // low order byte matches MSDOS attribute byte
+WORD offset, // point to short file name unicode string
+ size;
+DWORD flags;
+} NT_FILE;
+
+
+//------------------- new tree routines for lookup -------------
+/* for my dynamic link list of directory nodes
+*/
+typedef struct dir_list {
+char *name;
+BYTE key; // VOLB or DIRB
+FOFFSET data_off; // offset to DIRB record in file
+struct dir_list * next,
+ * child; // only subdir has children
+ // ignore parent, shouldn't need
+} DIR_LIST;
+
+// used in path remapping functions
+typedef struct path_elem {
+char term,
+ *path,
+ *redirect;
+} PATH_ELEM;
+
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTTREE.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTTREE.C"
new file mode 100644
index 0000000..39edd7c
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTTREE.C"
@@ -0,0 +1,845 @@
+/* nttree.c tree and path routines for ntbkup.c
+
+Add overview below 3/17/04 as on review for version 1.03
+I find I've forgotten the underlying logic.
+The tree is created via calls to add_tree_node()
+This dynamically allocates space for the new node
+and if the full path string copied from global tree_path[].
+It then calls insert_tree() to put the new node into tree
+order in memory. The 2/6/04 1.02 version of this seems to
+work correctly.
+
+From ntbkup.h
+typedef struct dir_list {
+char *name;
+BYTE key; // VOLB or DIRB
+FOFFSET data_off; // offset to DIRB record in file
+struct dir_list * next, // at same logical level
+ * child; // of current node, always points to a DIRB
+ // ignore parent,used in msqic, shouldn't need
+} DIR_LIST;
+
+The intent is to allow seeking to the location of the
+record in the archive using data_off.
+If its a VOLB record next points to the next physical drive
+on the system and child points to the directory tree on this drive.
+name is the drive specification (or as of 5/17/05 network drive spec).
+
+If its a DIRB record, next points to directories at the same
+level, ie the chain representing all the subdirectories of the parent.
+FILEs are not included in this tree. One must seek to the
+DIRB record in the archive and traverse the FILE data that
+follows these records to get at FILE information. See
+path_extract() which calls do_file() once for each FILE found.
+
+Each name in a DIRB record is the full path within its volume,
+ie it does not include the drive specification "?:".
+This is important for my restore logic as I allow redirection
+where the beginning of the destination path may be specified
+by the user and the remainder taken from the archives structure.
+
+do_redirect() is the routine that handles the command line
+options -p and @ in NTBKUP. The -p is just a special case
+where the user specifies one top level archive source node
+and the extraction is done starting at the current level.
+@ allows the user to specify multiple top level source nodes
+and corresponding destination nodes in a command file.
+
+do_redirect() makes one call to the recursive do_path()
+routine for each top level archive node to be extracted.
+If called via the @ option, prior to each call to do_path()
+it sets the current directory to the desired destination directory.
+do_path() is called with a parameter, par, which is the amount
+of the source path to be masked when changing to the next
+directory.
+
+With hind site I'm not real comfortable with my use of
+chdir() system calls to handle this. It is easy to get out
+of sync and cause errors. A rewrite where the full paths
+are used instead of relative paths is probably desirable.
+
+find_tree() is the lookup routine, it can be entered at any
+level in the tree and recursively searches from that point
+down (through the children in the tree) for a full
+case sensitive match with the supplied path string.
+Per above, unless the tree is entered at to top level
+the search path should NOT include a drive spec.
+
+1/24/04 created, splitting earlier tree work out of ntbkup.c
+1/25/04 this compiled and tst_path() with chg2path() seem
+ to work with relative paths. As initially written
+ it assumed the gap between a parent/child was one
+ level. This is not the case. Add logic to create
+ children child by child in chg2path().
+
+ Also change get_paths() a little more. The concept of
+ using the current directory just doesn't make sense
+ with this implimentation as it did in MSQIC.
+
+2/6/04 see do_redirect, play with l and lev
+ there was a short int mismatch in calls to chg2path
+3/16/04 fix error in chg2path when term == '+'
+3/17/04 back up version above to nttree2.c apparently it has
+ problems with level stuff. Make some significant changes.
+ see nt_dan.c and dan_files.txt for some notes an testing.
+ do_path() was heart of changes.
+ some error messages added included skipped file
+ count in path_extract() and skipped directories
+ in do_redirect().
+ tweak get_paths() to allow quoated string for destination
+
+5/17/04 see emails from Peter Feldman
+ turns out drive (ie VOLB spec) and be a network spec.
+ change find_tree()
+4/06/05 troubles with Jack's big archive, in add_tree_node()
+ test or empty path, only allocated if plen > 0
+ Its messy, maybe just shouldn't add to tree at all????
+ at least warn now.
+
+12/20/06 increase LN_SZ define from 255 to 600
+*/
+
+#include
+#include
+#include
+#include // for toupper()
+#include // define memset
+#include
+#include // for open() defines
+
+#if defined(MSDOS) || defined(_WIN32)
+# ifdef MSDOS
+# define OS_STR "MSDOS"
+# else
+# define OS_STR "WIN32"
+# endif
+#define DELIM '\\'
+#include
+#include
+#include // for lseek
+#include
+#else
+# ifdef __CYGWIN__
+# define OS_STR "CYGWIN"
+#else
+# define OS_STR "LINUX"
+#endif
+#include // for lseek, read, write
+#include
+#define DELIM '/'
+#define strnicmp strncasecmp
+#define stricmp strcasecmp
+#endif
+
+#ifdef _WIN32
+// this redefinition must be last! ie after io.h
+#define lseek _lseeki64
+#endif
+
+
+#include "ntbkup.h" // includes FOFFSET logic
+
+// called in ntbkup.c to copy one file
+FOFFSET do_file(int fp, FOFFSET soff,int mode); // uses global char *filter
+
+// add some tree routines
+
+// make the following global to limit amount of stack space used in tree_copy()
+extern char tree_path[]; // see ntbkup.c
+extern BYTE buf[]; // " "
+static struct stat sbuf; // used by do_redirect() and tree_copy()
+
+// recursive: see insert_tree() this routine mimic that behavior to find a node
+DIR_LIST *find_tree(DIR_LIST *next,char *str)
+{
+ int i,t;
+ char ch;
+ while(next != NULL)
+ {
+ i=0;
+ if(next->name != NULL) // test for no name, shouldn't happen, but...
+ {
+ while((t = *(next->name+i) - *(str+i)) == 0 &&
+ (ch = *(str+i)) != 0)
+ i++;
+ if(*(next->name+i) == 0)
+ {
+ if( t != 0)
+ {
+ if(next->child == NULL)
+ next = NULL;
+ else
+ next = find_tree(next->child,str);
+ }
+ break; // we found it
+ }
+ }
+ next = next->next;
+ }
+ return(next);
+}
+
+/* prior to 5/17/04 I looked for 1st DELIM and assumed VOLB name
+ was everything ahead of this DELIM. Works fine for C:
+ but not network drive spec, ie \\ANDY\C
+ so change to any match with Volume name
+*/
+DIR_LIST *find_path(DIR_LIST *next, char *str)
+{
+ int len,slen=strlen(str);
+ while(next != NULL) // find volume
+ {
+ if(next->name != NULL) // add error check
+ {
+ len = strlen(next->name);
+ if(strnicmp(str,next->name,len) == 0)
+ {
+ next = next->child;
+ break;
+ }
+ }
+ next = next->next;
+ }
+ // 2nd case below allows + or * to recursively do entire volume
+ if(next == NULL || (slen-len == 1 && *(str+len) == DELIM))
+ return(next); // NULL or top level node in a volume
+ else
+ return(find_tree(next,str+len)); // recursive search of a volumes tree
+}
+
+void disp_tree(DIR_LIST *d)
+{
+ while(d != NULL)
+ {
+ if(d->name == NULL) // add error check
+ printf("\n oops there was no name!");
+ else if(d->key == VOLB)
+ printf("\n%s",d->name);
+ else
+ printf("\n %s",d->name);
+ if(d->child != NULL)
+ disp_tree(d->child);
+ d = d->next;
+ }
+}
+
+/* Make some assumptions. Mainly it inserts top node first and additional ones
+ will be below those in tree already. therefore only check for children
+ try if at end of current node name, the other is an actual child
+ for now assume all end with a DELIM
+ otherwise insert at end of next
+*/
+
+int insert_tree(DIR_LIST *next, DIR_LIST *node)
+{
+
+ int i,t,ldelim=0,suc=0,depth=1;
+ char ch;
+ while(next != NULL)
+ {
+ i=0;
+ if(next->name != NULL && node->name != NULL) // add error check
+ {
+ while((t = *(next->name+i) - *(node->name+i)) == 0
+ && (ch = *(node->name+i)) != 0)
+ {
+ if(t == 0 && ch == DELIM)
+ ldelim = i;
+ i++;
+ }
+ if(*(next->name+i) == 0)
+ {
+ if( t != 0)
+ {
+ if(next->child == NULL)
+ next->child = node;
+ else
+ depth += insert_tree(next->child,node);
+ break;
+ }
+ else
+ printf("\nerror duplicate DIRB %s",next->name);
+ }
+ }
+ if(next->next == NULL)
+ {
+ next->next = node;
+ break;
+ }
+ next = next->next;
+ }
+ return(depth); // just for fun
+}
+
+int add_tree_node(DIR_LIST **root, FOFFSET off, int key)
+{
+ int suc = 0,plen = strlen(tree_path);
+ DIR_LIST *node,*tmp;
+ if((node = malloc(sizeof(DIR_LIST))) == NULL ||
+ (plen > 0 && (node->name = malloc(plen+1)) == NULL))
+ {
+ suc++;
+ printf("\nDIR Tree allocation error");
+ }
+ else
+ {
+ node->next = node->child = NULL;
+ node->key = key;
+ node->data_off = off;
+ if(plen <= 0)
+ {
+ printf("\nWarning empty path in add_tree_node()");
+ node->name = NULL;
+ }
+ else
+ {
+ strncpy(node->name,tree_path,plen);
+ *(node->name+plen) = 0; // insure termination
+ }
+
+ tmp = *root;
+ if(tmp != NULL) // find last node at drive level
+ while(tmp->next != NULL)
+ tmp = tmp->next;
+
+ if(*root == NULL) // always want a VOLB for a drive at root level
+ {
+ if(key != VOLB)
+ {
+ printf("\nDIR Tree warning, 1st node not a VOLUME! force '?:'");
+ strcpy(tree_path,"?:");
+ if((suc = add_tree_node(root,(FOFFSET)0,VOLB)) != 0)
+ return(suc);
+ else
+ tmp = *root; // we just forced a root
+ }
+ else
+ *root = node; // set 1st VOLB to root
+ }
+ else if(key == VOLB)
+ tmp->next = node; // its definately a volume spec, append
+
+ if(key == DIRB) // in partial archive may hit DIRB with no VOLB
+ {
+ if(tmp->child == NULL)
+ tmp->child = node;
+ else
+ insert_tree(tmp->child,node);
+ }
+ else if(key != VOLB)
+ {
+ printf("\ninvalid key %d",key);
+ suc++;
+ }
+ }
+
+ return(suc);
+}
+
+
+/* extract all files in DIRB block to current directory via do_file()
+ note this means its filtered by global filter -x option sets
+ 3/18/04 find from Dan's stdout.txt that the algorithm below
+ periodically hits a SPAD or CSUM key. Think this happens
+ if file ends near the XPBLK_SZ increment, the trialing stuff
+ in the stream header is hit. Used to quite if key != FILE_K
+ add logic to ignore these false hits. Also ignore if key < 0
+ as DIRB often more the XPBLK_SZ bytes long so get some
+ unrecognized hits in here.
+
+ Returns a negative # on error, or # >=0 which is the number
+ of files successfully extracted
+*/
+int path_extract(int fp, DIR_LIST *node,unsigned long *fskip)
+{
+ FOFFSET off,skip;
+ int key,rd;
+ unsigned int cnt=0;
+ char dir_cnt=0;
+ // seek to location of DIRB for directory in the archive
+ if((off = (FOFFSET)lseek(fp,node->data_off,SEEK_SET)) != node->data_off)
+ return(-1);
+ // extract all FILE tags
+ while((rd = read(fp,buf,XPBLK_SZ)) > 4)
+ {
+ skip = 0;
+ key=is_keyword((char *)buf);
+ if(dir_cnt > 0 && key >= 0 && key != FILE_K && key <= MTF_MAX_BLK)
+ return(cnt); // have done all files in directory
+ if(dir_cnt == 0) // only do this once on first pass
+ {
+ if(key != DIRB)
+ return(-2); // should have landed on a dirb
+ else
+ dir_cnt++; // assume its the correct one!
+ }
+ if(key == FILE_K)
+ {
+ skip = do_file(fp,off,EXTRACT|SILENT);
+ if(skip > 0 && skip != LSEEK_ERR)
+ cnt++;
+ else
+ (*fskip)++; // extract failed
+ }
+ if(skip != LSEEK_ERR && skip > XPBLK_SZ)
+ off += skip;
+ else
+ off += rd; // always tries to advance one block
+
+ /* did routine actually do the advance? do_file() only does if EXTRACT
+ and if it did, did to skip ahead to next block boundry
+ */
+ if((skip = (FOFFSET)lseek(fp,0L,SEEK_CUR)) != off || skip % XPBLK_SZ != 0)
+ {
+ if(off % XPBLK_SZ)
+ off -= off % XPBLK_SZ;
+ if((skip = (FOFFSET)lseek(fp,off,SEEK_SET)) != off)
+ {
+ printf(" failed to skip ahead to next block");
+ return(-1);
+ }
+ }
+ }
+ return(-2); // only get to here on read error
+}
+
+/* path remapping routines copied from msqicrcv.c 1/24/04
+ handles quoted strings which are required for LFN access
+ 3/19/04 looks like delimiter check on destination saws '"'
+ instead of delim if path was quoated
+
+ note 12/20/06 increase LN_SZ from 255
+*/
+#define LN_SZ 600
+
+
+int get_paths(char *fn,struct path_elem paths[],int sz)
+{
+ FILE *fp;
+ int i,r=0,cnt = 0,ln=0,len;
+ char quoted,*ch,line[LN_SZ+1];
+ if((fp = fopen(fn,"r")) == NULL)
+ {
+ printf("\nfailed to open redirect command file: %s ",fn);
+ return(-1); // open error
+ }
+ while(cnt < sz)
+ {
+ if(fgets(line,LN_SZ,fp) == NULL)
+ {
+ if(feof(fp))
+ printf("\neof after line %d",ln);
+ break;
+ }
+ else
+ {
+ paths[cnt].term = 0; // validatation
+ quoted = 0;
+ ln++;
+ len = strlen(line);
+ while(len > 0)
+ if(line[len-1] == '\n' || line[len-1] == ' ')
+ line[--len] = 0; // strip lf && trailing spaces so have string
+ else
+ break;
+ if(len <= 0 || line[0] == '#') // allow comments in line
+ {
+ printf("\nskipping empty line %d",ln);
+ continue;
+ }
+
+ ch = line;
+ // skip leading blanks
+ while(*ch != 0 && *ch == ' ')
+ {
+ len--;
+ ch++;
+ }
+
+ // allocate room for trailing NUL
+ // both source and destination are in same allocation
+ if((paths[cnt].path = malloc(len+2)) == NULL)
+ {
+ printf("\nfatal alloc error");
+ break;
+ }
+ i = 0;
+ while(*ch != 0 && (quoted || *ch != ' ') && i <= len)
+ {
+ if(*ch == '"')
+ quoted = quoted ^ 1;
+ else
+ *(paths[cnt].path+ i++) = *ch;
+ ch++;
+ }
+
+ if(i > 0)
+ {
+/* below for msqic where do not want trialing delimiter
+ paths[cnt].term = *(paths[cnt].path+ --i); // has a last char, save it
+ change 1/24/04 for NTBKUP to insert terminator
+*/
+ paths[cnt].term = *(paths[cnt].path+ i-1); // has a last char, save it
+ *(paths[cnt].path+ i -1) = DELIM; // replace with DELIM
+
+ }
+ if(i == 0)
+ printf("\nerror line %d source path has length 0!",ln);
+ if(paths[cnt].term != DELIM && paths[cnt].term != '*' &&
+ paths[cnt].term != '+')
+ {
+ printf("\nerror line %d in source path must end in %c, *, +",
+ ln,DELIM);
+ i = -1;
+ }
+ else if (quoted)
+ {
+ printf("\nerror line %d unmatched quotes in source path",ln);
+ i = -1;
+ }
+
+ if(i > 0)
+ {
+ *(paths[cnt].path+ i++) = 0; // terminate source path
+
+ while(*ch != 0 && *ch == ' ' && i <= len) // find start next
+ {
+ ch++;
+ }
+
+
+ if(*ch != 0 && i <= len)
+ paths[cnt].redirect = paths[cnt].path+ i; // 2nd path exists
+ else
+ paths[cnt].redirect = NULL; // not redirected
+ while(*ch != 0 && (quoted || *ch != ' ') && i <= len)
+ {
+ if(*ch == '"')
+ quoted = quoted ^ 1;
+ else
+ *(paths[cnt].path+ i++) = *ch;
+ ch++;r++;
+ }
+ *(paths[cnt].path+ i) = 0; // terminate redirect
+ if(!(*ch == 0 || *ch == ' ') || quoted || r == 0)
+ {
+ printf("\nerror line %d invalid redirect path %s",
+ ln,paths[cnt].redirect);
+ i = -1;
+ }
+ // check termination, will append strings to this.
+ // for consistency must end with DELIM
+ else if(*(paths[cnt].path+ i -1) != DELIM)
+ {
+ printf("redirect path must end with %c",DELIM);
+ i = -1;
+ }
+ else
+ {
+ // 1/25/04 remove empty string for current directory logic
+ // in msqic.c I delete delimiter, leave for NTBKUP
+ cnt++;
+ }
+ }
+ if(i <= 0)
+ {
+ printf("\n skipping: %s",line);
+ free(paths[cnt].path);
+ continue;
+ }
+ }
+ }
+ return(cnt); // # valid paths
+}
+
+/* set current directory based on path
+ checks for a leading DELIM (only passed from do_redirect() with level == 0)
+ to determine if its an absolute or relative path
+ It may step down more than one directory level to new path
+ The number of levels stepped down is returned
+
+ 1/28/04 change return, add parameter level that used to be return on success
+ NOTE if term == '+' this will attempt to create directories
+ return -1 on fatal error,
+ 1 on warning
+ 3/16/03 add reset of suc = 0 when term == '+' and
+ create directory successfully after not finding it
+ also always return suc, the return was undefined on a error!
+ 3/17/03 This routine passes back *level=0 if its the root
+ else *level = # of subdir's created
+ appears this is always 1 and we don't need a while loop!
+*/
+int chg2path(char *path,char term,WORD *level)
+{
+ int suc = 0,l = strlen(path),rd;
+ char savch;
+ *level=0;
+ l--;
+ /* at this point *(path + l) should be the last DELIM
+ however if its a path to the root this can also be the
+ root path spec, ie l == 0. This routine attempts to
+ handle both absolute paths (start with DELIM) and relative
+ paths that don't begin with DELIM.
+ This routine aborts if:
+ no path, length 1 but not root, or a subdir without trailing DELIM
+ */
+ if(l < 0 || (l == 0 && *path != DELIM) ||
+ (l > 0 && *(path+l) != DELIM))
+ suc = 1;
+ else
+ {
+ while(suc == 0 && l > 0)
+ {
+ rd = 0;
+ while(*(path+rd) != 0 && *(path+rd) != DELIM)
+ rd++;
+ if(rd > 0)
+ *(path+rd) = 0; // overwrite all but 1st DELIM with 0, then restore below
+ else if(*level == 0 && l > 0)
+ {
+ savch = *(path+rd+1); // allow absolute path to root at level 0
+ *(path+rd+1) = 0;
+ }
+ if(rd > 0 )
+ {
+ suc = stat(path,&sbuf); // check status of directory
+ if(term == '+') // try to create the sub dir(s) if not there
+ {
+ if(suc == 0 && // directory exists
+ !(sbuf.st_mode & S_IFDIR) ) // as a file
+ suc = -1; // fatal error
+ else if (suc) // it doesn't exist try to create
+ {
+#ifdef unix
+ // accessable & searchable
+ if(mkdir(path,S_IREAD | S_IWRITE | S_IEXEC) != 0)
+ suc = -1;
+#else
+ if(mkdir(path) != 0)
+ suc = -1;
+#endif
+ else
+ suc = 0; // clear error from test for directory above
+ }
+ }
+ else if(suc || !(sbuf.st_mode & S_IFDIR))
+ suc = 1; // just a warning, its no a directory
+ }
+ if(suc == 0 && chdir(path)) // finally try to go there
+ suc = -1;
+ if(rd > 0)
+ {
+ *(path+l) = DELIM; // restore
+ (*level)++; // don't increment for root
+ }
+ else if(*level == 0 && l > 0)
+ *(path+rd+1) = savch;
+ rd++; // skip the DELIM
+ path+=rd; // advance to next node
+ l -= rd;
+ }
+ }
+ if(suc != 0)
+ printf("\nFailed: chdir(%s)",path);
+ return(suc);
+}
+
+
+/* called from do_redirect() only if recursive extract
+ attempts to step through the nested paths
+ and extract files with a call to path_extract() for each sub directory
+
+ 4/17/04 think was messed up
+ a) its when level == 0 that there is no parent, had logic reversed
+ b) did initialize lev, and didn't pass new nested level to
+ recursive do_path() cause of how I used level. Change.
+ if there is no child need lev = 0 so don't step back up
+ c) add a chdir("..") before chg2dir() while getting next
+ at end. Getting fuzzy, but looks right
+ d) move display of node->name and its len inside while loop
+ so each node displayed and len, which is critical to chg2dir()
+ and do_path(), gets updated as move between directories at the
+ same level.
+ e) add lev == 1 test and error message on failure to do child
+ f) add skip parameter to track if directories skipped
+*/
+short do_path(int fp,DIR_LIST *node,WORD level,WORD *dskip,unsigned long *fskip,short par,char term)
+{
+ short suc = 0,len;
+ short lev;
+ while(suc == 0 && node != NULL)
+ {
+ printf("\nlevel %d: %s",level,node->name);
+#ifndef TEST // define to test directory routines without file extraction
+ if((len=path_extract(fp,node,fskip)) < 0) // get all files for this node
+ {
+ printf("\nfatal error while extracting files from this directory");
+ (*dskip)++;
+ }
+
+#endif
+ len=strlen(node->name);
+ lev = 0; // assume don't change level
+ if(node->child != NULL)
+ {
+ if(strlen(node->child->name) > len &&
+ chg2path(node->child->name+len,term,&lev) == 0 && lev == 1)
+ suc = do_path(fp,node->child,level+lev,dskip,fskip,len,term);
+ else
+ {
+ printf("\nskipping unreachable source child node lev %d:\n %s",
+ lev,node->child->name);
+ (*dskip)++;
+ }
+ /* prior to 1/28/04 was fatal error if chg2path failed
+ now continue, just skip this directory
+ This allows term '*' to use directory existance as a filter
+ */
+ }
+
+ if(level == 0)
+ break; // there is no parent for start node
+ // do_path() returns to current directory level
+ // if not level 0, restore to parent level so chg2dir(next->node) will work
+ else if(suc == 0 && chdir("..") != 0)
+ suc = -2;
+ else // its not the start node, has a valid parent
+ {
+ while(suc==0)
+ { // look for a valid next directory
+ if((node = node->next) == NULL)
+ break; // no more nodes
+ if(par >= strlen(node->name))
+ {
+ printf("\nskip, invalid next node: %s",node->name);
+ (*dskip)++;
+ }
+ else if(chg2path(node->name+par,term,&lev) == 0)
+ { if(lev == 1)
+ break; // positioned correctly
+ else
+ {
+ printf("\nwarning can't reach next source node lev %d:\n %s",
+ lev,node->name);
+ while(lev > 0 && chdir("..") == 0)
+ lev--; // attempt to recover
+ if(lev != 0)
+ suc = -2; // fatal if can't
+ else
+ (*dskip)++;
+ }
+ }
+ }
+ }
+ }
+
+ if(suc == -2)
+ printf("\nfatal error returning to parent dir");
+ return(suc);
+}
+
+/* step through path list extracting files
+ one call to do_path() for each entry in paths[] array
+ This routine makes each top level redirected path the current directory.
+ It handles drive parsing, routines below this assume current drive.
+
+*/
+int do_redirect(int fp, DIR_LIST *root,struct path_elem *paths,int rcnt)
+{
+ int i,l,drvn,suc=0;
+ WORD lev,dskip;
+ unsigned long fskip=0;
+ char *drv,*path,doit;
+ DIR_LIST *node,*parent=NULL;
+ printf("\nDoing redirect:");
+ for(i=0;ipath);
+ if(paths->redirect != NULL)
+ printf("\nredirect: %s",paths->redirect);
+ if((node = find_path(root,paths->path)) == NULL)
+ {
+ printf("\ncan't find source path");
+ continue;
+ }
+ else
+ {
+ suc = 0;
+#ifndef unix // its MSDOS or WIN32 so has physical drives
+ if(paths->redirect == NULL)
+ path = paths->path;
+ else
+ path = paths->redirect;
+
+ l = 0;
+ drv = path;
+ while(*path != 0 && *path != DELIM)
+ {
+ path++;
+ l++;
+ }
+ if(l && *path == DELIM) // there is a drive spec
+ { // temp terminator for drive spec
+ *path = 0;
+ strupr(drv);
+ if(l != 2 || *(drv+1) != ':' || (drvn = *drv - 'A') < 0)
+ suc = 1;
+ else // DOS WIN32 specific A: is 1, C: is 3 etc
+ suc = _chdrive(*drv-'A'+1); // failed to change drive
+ if(suc)
+ {
+ continue;
+ printf("\nfailed to select drive: %s",drv);
+ }
+ *path = DELIM; // restore
+ }
+#else
+ // need to redirect physical drive to a mount point
+ if(paths->redirect == NULL)
+ {
+ printf("\nSKIP, for Unix redirect path must be defined or current dir");
+ continue;
+ }
+ else
+ path = paths->redirect;
+#endif
+ // now see if we can select the directory
+ doit = 1;
+ if((l=strlen(path)) == 0 && rcnt > 1)
+ {
+ printf("\n Warning, empty path not allowed with multiple sources, its unpredictable!");
+ doit = 0;
+ }
+ if(doit && (l == 0 || chg2path(path,paths->term,&lev) == 0) )
+ { // chg2path handles directory creation if there is a path
+ fskip = dskip = 0; // initialize
+ if(paths->term == DELIM) // just this directory
+ {
+ if((l = path_extract(fp,node,&fskip)) > 0)
+ {
+ printf("\n%d files extracted from this directory",l);
+ l = 0; // so no error message below
+ }
+ }
+ else // do nested directories
+ {
+ l = do_path(fp,node,0,&dskip,&fskip,0,paths->term);
+ }
+ // no error check, an error in one path does NOT terminate extact
+ if(l != 0)
+ printf("\n extract aborted, fatal error parsing this path");
+ if(dskip != 0)
+ printf("\n warning %u subdirectories were skipped",dskip);
+ if(fskip != 0)
+ printf("\n warning %lu files were skipped",fskip);
+ }
+
+ }
+ paths++;
+ }
+ return(suc);
+}
+
+
+
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/QICDCOMP.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/QICDCOMP.C"
new file mode 100644
index 0000000..b137095
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/QICDCOMP.C"
@@ -0,0 +1,766 @@
+/* qicdcomp.c decompression *.qic files, see main() in msqic.c
+ Check http://www.fpns.net/willy/msbackup.htm for updates.
+ contact info above or via snailmail: p.o. box 333, bradford, nh 03221
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+-----------------------------------------------------------------------------
+ History:
+
+
+ see qic docs: qic122b.pdf
+ should add some exit codes etc, got it working quick and dirty
+ then for space reasons split out of msqic.c
+
+ 10/18/03 the do_decompress is messed up for multiple drives
+ under WinME. add this comment, thought about a patch,
+ but a little messy. Don't need to do this NOW!
+
+ 12/13/03 start to change code per above
+ do_decompress needs to handle stripping seg_head regions
+ from catalog. In process add passing vtbl_ver to
+ do_decompress() instead of just data_off. Want to be able
+ to create a single volume generic archive from one of members
+ in Win98 multi volume.
+
+ 12/14/03
+ change msqic.c so cvtbl->dirbase points to actual base rather
+ than offset after cseg_head
+
+ 12/22/03 go to FOFFSET and LSEEK_ERR defines in msqic.h
+ 1/11/08 add Ralf's CYGWIN compiler defines
+ 2/2/04 add HAS_INT64 logic for Avik, basically just add prt_foff()
+ and add calls to it. Most of FOFFSET logic already in place
+ to get me to the 4Gb boundry
+ 5/03/04 try to make minimal modifications to do_decompress()
+ so it will work with a recreated VTBL See RECREAT tests.
+ Also set vtbl.comp = 0, was only clear high order bit
+
+ Looks like can normally determine the length of the data
+ region by chasing down the segment chain, but not
+ the catalogs which always seem to have cseg_head.seg_sz =
+ SEG_SZ & 0x8000. So add get_dir_len() routine to chase
+ down chain to find length.
+ 5/31/07 backuped up current version to qicdcomp2.c Now try to fix
+ WIN95 issues per Sven Verhaegen's email of 5/22/07 his sample archive
+ us like my single b95c.qic backup. see moeenv~1.qic
+ see qicdcmp4.c for modes which depend on the ver info flag
+ I create and pass in, but just realized this is DUMB
+ both the WIN95 and WINME format advance SEG_SZ bytes
+ each time, they just treat shead->seg_sz differently
+ Old code: only works for WINME were seg_sz is 0x73F6
+ cur_pos += shead.seg_sz; // where we want to be next
+ seems to me the WIN95 logic should work for all! try it
+ cur_pos += SEG_SZ - sizeof(shead); // always 0x73F6
+
+ yuk, do_decompress() was incorrectly testing cvtbl->ver
+ without WIN_MASK so always assumed WINME format
+
+*/
+#include
+#include
+#include
+#include // define memset
+#include
+#include /* for open() defines */
+
+#if defined(MSDOS) || defined(_WIN32)
+# include
+# include // for lseek
+#else
+# ifdef unix
+# include // for read/write etc.
+//# define exit _exit // ralf likes this..
+# define strnicmp strncasecmp
+# define stricmp strcasecmp
+# ifndef O_BINARY
+# define O_BINARY 0 // no difference between text and binary mode for LINUX
+# endif
+# else
+# error Unknown build environment.
+# endif
+#endif
+
+#include "msqic.h"
+
+void prt_foff(FOFFSET * off)
+{
+#ifndef HAS_INT64
+ printf("%lx",*off);
+#else
+ DWORD *l = (DWORD *)off;
+ if(*(l+1) != 0)
+ printf("%lx%08lx",*(l+1),*l);
+ else
+ printf("%lx",*l);
+#endif
+}
+
+/*------------DECOMPRESSION see dcomp.c for initial trial ----------*/
+#define HBUF_SZ 2048
+unsigned char hbuf[HBUF_SZ]; // history buffer
+
+int fin,fout,bits=0,bcnt = 0,hptr=0;
+FOFFSET comp_rd=0,comp_wr;
+
+void flush_hbuf()
+{
+ int i;
+ if((i=write(fout,hbuf,hptr)) != hptr)
+ {
+ printf("\nerror writting output file");
+ exit(2);
+ }
+ hptr = 0;
+ comp_wr += i;
+}
+
+unsigned char getbit()
+{
+ unsigned char mask,ret;
+ if(bcnt == 0)
+ {
+ if(read(fin,&bits,1) == 1) // not efficent, but easy
+ {
+ bcnt = 8;
+ comp_rd++;
+ }
+ else
+ {
+ printf("\n fatal error reading byte stream");
+ flush_hbuf(); // write anything we can....
+ exit(1);
+ }
+ }
+ bcnt--;
+ if(bcnt < 0)
+ {
+ printf("\nlogic error getbit() cnt went negative");
+ flush_hbuf(); // write anything we can....
+ exit(1);
+ }
+ else if (bcnt == 0)
+ mask = 1;
+ else
+ mask = 1 << bcnt;
+ if(mask & bits)
+ {
+ ret = 1;
+ bits &= ~mask; // clear this bit if set
+ }
+ else
+ ret = 0;
+ return(ret);
+}
+
+unsigned char getbyte() // for raw char in stream
+{
+ unsigned char ret = bits;
+ int nbit = bcnt; //
+ bcnt = 0;
+ // this could be done a lot more efficently, but not now
+ while(nbit < 8)
+ {
+ ret = (ret << 1) + getbit();
+ nbit++;
+ }
+ return(ret);
+}
+
+int getsoff()
+{
+ unsigned short off = 0,bits=7,i;
+ if(getbit() == 0)
+ bits = 11;
+ for(i=0;iseg_sz & ~RAW_SEG) == 0)
+ {
+ printf("\nWarning 0 length segment in catalog");
+ break; // don't think this happens, would be end of chain
+ }
+ if(!(cseg->seg_sz & RAW_SEG))
+ {
+ printf("\ncompressed segment found in catalog");
+ suc++;
+ }
+ }
+ }
+ if(suc) break;
+
+ if(comp == 0) // simple case, no compression headers
+ {
+ if((brd=read(fp,&rd,2)) != 2)
+ suc++;
+ else if(rd == 0)
+ break; // end of catalog
+ else if(read(fp,hbuf,rd) != rd)
+ suc++;
+ else
+ dlen += brd + rd;
+ }
+ else // compressed and len != 0
+ {
+ // get word len byte by byte as may span cseg_head
+ while(brd < 2 && len > 0 && suc == 0)
+ if(read(fp,prd + brd++,1) != 1)
+ suc++;
+ else
+ len--;
+
+ if(brd == 2 && len > 0 && suc == 0)
+ {
+ // ok now rd value is valid
+ if(rd == 0) // we are done
+ break;
+ else if(rd <= len) // get the data
+ {
+ if(read(fp,hbuf,rd) != rd)
+ suc++;
+ else
+ {
+ len -= brd + rd;
+ dlen += brd +rd;
+ brd = 0; // clear for next read
+ }
+ }
+ else // can only read part of it, spans a cseg_head
+ {
+ if(read(fp,hbuf,(int)len) != len)
+ suc++;
+ else
+ {
+ dlen += len;
+ rd -=len; // did partial read
+ len = 0; // will force cseg_head read
+ }
+ }
+ }
+ }
+ }
+ if(lseek(fp,ioff,SEEK_CUR) == LSEEK_ERR)
+ printf("\nWarning failed to restore initial file position");
+
+ if(suc)
+ return(LSEEK_ERR);
+ else
+ return(dlen);
+
+}
+
+
+/*-----------------
+
+ do {
+ if(rd < 2) // read more into buffer
+ {
+ if(lseek(fp,off,SEEK_SET) == LSEEK_ERR ||
+ (rd = read(fp,hbuf,HBUF_SZ)) < 2)
+ suc++;
+ else
+ w = (WORD *)hbuf;
+ }
+
+ while(suc == 0 && rd >= 2)
+ {
+ if(comp && (off -soff) % SEG_SZ == 0)
+ { all fucked uo
+ }
+ off += *w +2; // add data length + length word itself
+ rd -= *w +2;
+ w += *w +2; // advance pointer (may go past end of hbuf!)
+ n++; // count directory entries found
+ }
+ } while(suc == 0 && *w != 0);
+----------*/
+
+
+void decomp_seg() // was being sloppy, no return!
+{
+ int off,len,i,rawndx=-1,limit=29696; // was 5000 prior to 10/14/03
+ char verb=0;
+ FOFFSET foff;
+
+ hptr=0;bits=0;bcnt=0;comp_rd=0,comp_wr=0; // init so repeat calls can be made
+ // early termination so don't worry about buffer wraps
+ while(hptr < limit) // or till terminator, pat == 0x180, off == 0
+ {
+ if(getbit() == 0) // raw byte
+ {
+ if(hptr == HBUF_SZ)
+ flush_hbuf();
+ if(rawndx < 0) // always track this, use verb to control display
+ rawndx = hptr; // get first raw byte entered
+ // only raw chars go in history buffer
+ hbuf[hptr] = getbyte();
+ hptr++;
+ }
+ else // a string
+ {
+ if(rawndx >= 0)
+ {
+ if(verb)
+ {
+ printf("\nraw chars: ");
+ for(i=rawndx;i= ' ') // is printable, not control char
+ putchar(hbuf[i]);
+ printf(" 0x%02x, ",hbuf[i]);
+ }
+ }
+ rawndx = -1; // end raw data block (1 or more chars)
+ }
+ off = getsoff();
+ if(off == 0)
+ {
+ foff = (FOFFSET)lseek(fin,0L,SEEK_CUR);
+ printf("\nfound compression terminator in byte stream at 0x");
+ prt_foff(&foff);
+ break; // this is the terminator
+ }
+
+ len = getslen();
+ if(verb)
+ printf("\nstring: len = %d offset = %d",len,off);
+// note the unmodified offset is always displayed
+#ifdef CARRAY
+// the base 0 or 1 for array off handled below
+// if don't comment out decrement example is better, qic dcomp worse
+ off--; // convert to C array offset where 0 is valid
+#endif
+ // copy from history buffer to output buffer
+// off in *.qic is displacement from current buffer position
+// error check it
+ if((i = hptr-off) < 0)
+ {
+ if(verb)
+ printf(
+"\nwarning offset %d from current pos %d out of range, wrap it around",
+ off,hptr);
+ i += HBUF_SZ; // allow it to continue
+ }
+ else if (i >= HBUF_SZ)
+ {
+ if(verb)
+ printf(
+"\nwarning offset %d subtracted from current pos %d out of range, use modulo",
+ off,hptr);
+ i = i % HBUF_SZ; // wrap it
+ }
+ while(len-- > 0)
+ {
+ if(hptr == HBUF_SZ)
+ flush_hbuf();
+ hbuf[hptr++] = hbuf[i++]; // copy in string
+ i = i % HBUF_SZ;
+ }
+ }
+
+ }
+ if(hptr)
+ flush_hbuf();
+ printf("\nread 0x");
+ prt_foff(&comp_rd);
+ printf(" input bytes, generated 0x");
+ prt_foff(&comp_wr);
+ printf(" output bytes");
+}
+
+/* copy from len bytes from fin to fout
+ file points must be positioned before call
+ side effect comp_wr set to # bytes written
+ uses global hbuf[] as transfer area, clobbering any data in it
+*/
+
+// similar to below but write len NUL bytes to fout
+int pad_seg(FOFFSET len)
+{
+ int i,sz,suc = 0;
+ memset(hbuf,0,HBUF_SZ); // clear buffer
+ comp_wr = 0;
+ while(suc == 0 && len > 0)
+ {
+ if(len > HBUF_SZ)
+ sz = HBUF_SZ;
+ else
+ sz = len;
+ if(sz > 0 && (i = write(fout,hbuf,sz)) != sz)
+ suc = -2;
+ else
+ len -= sz;
+ }
+ return(suc);
+}
+
+int copy_region(long len)
+{
+ int i,sz,suc = 0;
+
+ comp_wr = 0;
+ while(suc == 0 && comp_wr < len)
+ {
+ if(len - comp_wr > HBUF_SZ)
+ sz = HBUF_SZ;
+ else
+ sz = len - comp_wr;
+ if(sz > 0 && ((i = read(fin,hbuf,sz)) != sz ||
+ write(fout,hbuf,sz) != sz))
+ suc = -2;
+ else
+ comp_wr += sz;
+ }
+ return(suc);
+}
+
+/* add top level logic
+ assumes fo is a the beginning of a new empty file.
+ seeks back and rereads VTBL on fi
+
+ Needs work for multiple drives in WinME, see get_vtbl()
+
+ Originally hoped could convert from one format to another,
+ but its more than the header that is different, so give that up.
+ VER 1.02 copied database bytes from beginning of original file to
+ new file. Thus allowing recreation of Win95 or WinME style.
+
+ Now check cvtbl->ver and ->cnt to determin if MDID required
+
+ 12/13/03 change from int database
+ to struct vtbl_ver *cvtbl
+ keep a local copy of modified header and write at end
+
+ 2/5/04 ah ha just realized what vtbl.start and .end
+ fields are really for. Normally change when decompress a
+ volume, attempt to add this logic
+
+ Oh dear, in process find never dealt with multi-volume
+ WINME stuff, ie never did seek(fin, to cvtbl->database
+
+ 4/28/04 add logic for cvtbl->OSver == RECREAT
+ to do this easily remove test below on cum_wr in read loops
+ This may allow archive larger than 4GB, and solve the problem
+ that one does NOT know dataSz for RECREAT case
+ cum_wr >= (long)vtbl.dataSz[0]) // safety net...
+
+ Remove ability to use -sc## to set location of catalog for RECREAT
+ assume it immediately follows data region. This makes it 64 bit compatible.
+
+ 6/12/07 added some breaks in main loop on error and about a week
+ ago logic to change ad
+
+*/
+int do_decompress(int fi,int fo,struct vtbl_ver *cvtbl)
+{
+ int i,suc = 0,sz,nshead=1; // always reads 1 seg_head past last segment
+ FOFFSET cum_rd = 0,cum_wr=0,cur_pos,pad;
+ struct qic_vtbl vtbl;
+ struct cseg_head shead;
+// minimize parameter passing by making fin and fout locally global
+ fin = fi; fout = fo;
+ sz = sizeof(struct qic_vtbl);
+
+ if(!(cvtbl->vtbl.comp & 0x80))
+ {
+ printf("\nimage is not compressed, nothing to do!");
+ suc = 1; // do nothing
+ }
+
+ else if((cvtbl->ver & WIN_MASK) == WINME) // obtain and write MDID region
+ {
+ if(cvtbl->ver & RECREAT) // create empty block with tag
+ {
+ memset(hbuf,0,sz);
+ strncpy(hbuf,"MDID",4);
+ // required so parsed correctly after decompression
+ }
+ else // get copy of existing
+ {
+ cur_pos = 128L*cvtbl->cnt;
+ if (lseek(fin,cur_pos,SEEK_SET) != cur_pos ||
+ (i = read(fin,hbuf,sz)) != sz)
+ suc = 3;
+ }
+
+ if( suc == 0 && lseek(fout,(FOFFSET)128,SEEK_SET) != 128 ||
+ (i = write(fout,hbuf,sz)) != sz)
+ suc = 3;
+ else
+ sz *= 2; // bytes in header region VTBL + MDID
+ if(suc != 0)
+ printf("\nerror creating WinME MDID");
+ }
+
+
+ if((FOFFSET)lseek(fin,cvtbl->database,SEEK_SET) !=cvtbl->database)
+ {
+ printf("\nerror positioning input file to data region");
+ suc = 2;
+ }
+ else if((FOFFSET)lseek(fout,(FOFFSET)sz,SEEK_SET) != (FOFFSET)sz)
+ {
+ printf("\nerror positioning output file after vtbl");
+ suc = 2;
+ }
+
+
+ if(suc == 0)
+ {
+ // get copy of vtbl in local buffer
+ memcpy((void *)&vtbl,(void*)&cvtbl->vtbl,sizeof(struct qic_vtbl)); // save for mods
+ /* Compressed dir starts with a cseg_head, must pad to next seg boundry
+ as remove cseg_head from dir, so dir now starts earlier
+ Note in Win98&ME dirSz = # segments * SEG_SZ
+ in Win95 dirSz = space used in segment
+ in RECREAT don't know them obtain from seg_heads, force WIN95 format
+ 12/13/03 couple issues here
+ a) may be more than one segment I ignored that and
+ the additional seg_heads that must be removed previously
+ b) what if its a multi drive backup from WinME/98??
+ Answer: only write the currently selected volume
+ */
+ vtbl.comp = 0; // clear compressed byte
+ if((cvtbl->ver & WIN_MASK) == WINME)
+ vtbl.start = 3; // I only write single volumes starting after VTBL
+ /* was hopping to make ME to 95 conversion, but takes more work
+ there are extra bytes in data stream....
+ */
+ }
+ while(suc == 0)
+ {
+ if((i=read(fin,&shead,sizeof(struct cseg_head))) !=
+ sizeof(struct cseg_head))
+ {
+ suc = -2;
+ break; // 6/12/07 can't do anything more exit loop!
+ }
+ else if(shead.seg_sz == 0 || // end of data
+ (!(cvtbl->ver & RECREAT) && cum_wr >= (long)vtbl.dataSz[0])) // saftety net
+ {
+ printf("\nDecompressed all data segments");
+ break;
+ }
+ // get current position, may need to seek ahead to next block
+ else if((cur_pos = lseek(fin,0L,SEEK_CUR)) == LSEEK_ERR)
+ {
+ suc = -1;
+ break; // 6/12/07 shouldn't do anything more exit loop!
+ }
+ else if(shead.seg_sz & RAW_SEG) // don't decompress this one!
+ {
+ printf("\n\nRaw copy of uncompressed segment starting at 0x");
+ prt_foff(&cur_pos);
+ // do a raw copy of the segment
+ suc = copy_region((long)(shead.seg_sz & ~RAW_SEG));
+ comp_rd = comp_wr; //setup for totals at end of loop
+ }
+ else // decompress the segment
+ {
+ printf("\n\nDecompress segment starting at 0x");
+ prt_foff(&cur_pos);
+ decomp_seg(); // currently has no return!
+ // input file position is a little random, do a seek
+ // but force advance to SEG_SZ between headers
+ cur_pos += SEG_SZ - sizeof(shead); // always 0x73F6
+ if(lseek(fin,cur_pos,SEEK_SET) != cur_pos)
+ suc = -1;
+ }
+ nshead++; // read another seg_head, required for RECREAT
+ cum_rd += comp_rd;
+ cum_wr += comp_wr;
+ printf("\ncumulative decompressed bytes written 0x");
+ prt_foff(&cum_wr);
+
+ }
+ /* must still write table of contents to complete process
+ 12/13/03 It turns out although a compressed files table
+ of contents is not compressed, it does have seg_head regions
+ which must be stripped (stupid, but true)
+ */
+ if(suc == 0)
+ {
+ if((cvtbl->ver & WIN_MASK) == WINME)
+ {
+ // update end field, must be done before pad_seg() overwrites cum_wr
+ vtbl.end = 3; // base
+ vtbl.end += cum_wr/SEG_SZ; // point to 1st directory segment
+ if(cum_wr%SEG_SZ)
+ vtbl.end++; // round up
+ }
+ if(cvtbl->ver & RECREAT)
+ {
+ vtbl.dataSz[0] = cum_wr;
+ // assume no overflow into upper 32 bits, vtbl.dataSz[1]
+ }
+ else if(cum_wr != vtbl.dataSz[0]) // ignore high order long
+ printf("\nWarning: Decompressed Data bytes != dataSz");
+ printf("\n append a copy of the original directory");
+ // fudge it, use dataSz as abs position by include of VTBL sizes
+ // compressed files dataSz is actually the decompressed size!
+ // dir must start on a Segment boundry
+ // for WinME, two 128 byte headers before data
+ // for Win95, only one
+ pad = vtbl.dataSz[0] % SEG_SZ;
+ if(pad)
+ pad = SEG_SZ - pad;
+ if((cur_pos = lseek(fout,0L,SEEK_CUR)) > vtbl.dataSz[0]+sz) // have written too far
+ suc = -1; // separate these for debugging so I can see who fails
+ else if(pad && pad_seg(pad) != 0) // pad output data region to next segment
+ suc = -2;
+ else // add RECREAT test to set dirbase, assume next segment
+ {
+ if(cvtbl->ver & RECREAT)
+ {
+ if(cvtbl->dirbase == 0)
+ {
+ // if it was set from command line it won't be zero
+ cvtbl->dirbase = cum_rd + nshead * sizeof(struct cseg_head);
+ pad = cvtbl->dirbase % SEG_SZ;
+ if(pad)
+ cvtbl->dirbase += SEG_SZ - pad;
+ cvtbl->dirbase += cvtbl->database; // add in start offset
+ }
+ if((pad = get_dir_len(fin,1,cvtbl->dirbase)) == LSEEK_ERR)
+ {
+ printf("\nError determining catalog length");
+ suc = -4;
+ }
+ else
+ vtbl.dirSz = pad;
+ }
+ if(suc == 0 && (cur_pos = lseek(fin,cvtbl->dirbase,SEEK_SET)) !=cvtbl->dirbase)
+ suc = -3; // failed to move to dir cseg_head
+ }
+
+ if(suc == 0)
+ {
+ /* may want validation above, below assumes we are at seg_head
+ strip cseg_head regions
+ */
+ nshead = 1;
+ cum_wr = 0;
+ do {
+ if(read(fin,&shead,sizeof(struct cseg_head)) != sizeof(struct cseg_head))
+ suc = 5;
+ else if((shead.seg_sz & RAW_SEG) == 0)
+ {
+ printf("\ncompressed catalog detected???");
+ suc = 6;
+ }
+ else if((pad = (shead.seg_sz & ~ RAW_SEG)) == 0)
+ break; // done with directory [oops doesn't happen in single volume reach EOF]
+ else if(copy_region((long)pad) != 0)
+ suc = 5;
+ else
+ cum_wr += comp_wr;
+ if(cum_wr >= vtbl.dirSz) break;
+ } while(suc == 0);
+ // if(cvtbl->ver & RECREAT) don't know dirSz, set from cum_wr
+ if(suc == 5 && (cum_wr == vtbl.dirSz || cvtbl->ver == RECREAT))
+ suc = 0; // read it all, probably a single volume archive
+ pad = cum_wr % SEG_SZ;
+ if(suc == 0 && pad)
+ {
+ pad = SEG_SZ - pad;
+ suc = pad_seg(pad); // go to next seg boundry
+ }
+ if((cvtbl->ver & WIN_MASK) == WINME)
+ vtbl.dirSz = cum_wr +pad;
+ else
+ vtbl.dirSz = cum_wr; // WIN95 std
+ }
+
+ sz = sizeof(struct qic_vtbl);
+
+ if(suc != 0)
+ printf("\nerror %d during copy",suc);
+ else if(lseek(fout,0L,SEEK_SET) != 0L ||
+ (i = write(fout,&vtbl,sz)) != sz)
+ {
+ printf("\nerror writing final VTBL header");
+ suc = -2;
+ }
+
+ }
+ close(fout);
+ return(suc);
+}
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/README.TXT" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/README.TXT"
new file mode 100644
index 0000000..be5320d
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/README.TXT"
@@ -0,0 +1,32 @@
+Distribution date 07/17/05
+NTBKUP version 1.07
+MSQIC version 1.11
+
+This is a source code distribtion library for Windows backup
+readers. I have included a snapshot of my current
+web page, msbackup.htm. Use it as your documentation.
+
+These will remain beta distributions. I've done as much
+testing as I can, but trying to support 3 OS leads to
+too many combinations to be sure I've tested every option.
+If you have a problem or bug please get in touch but
+don't expect too much, my interest has dwindled.
+See contact.htm.
+
+Expand everything in a directory and try one of the make files.
+I apologize for the current state of the Linux build.
+
+msbkup.mak works with various Microsoft compilers
+ as indicated in the file. It should detect the OS.
+ If you use nmake, it will probably make sense.
+
+msbkup.lin works in my linux environment. Its fairly
+ generic if you use 'make -f msbkup.lin'
+ However Berend de Boer went to the trouble of explaining
+ and creating a sample autoconfigure script for me.
+ I've been attempting to understand this and incorporate
+ it, but time is tight and energy is low so I'm afraid
+ its still not yet in this source distribution. Sorry,
+ as its the right way to do it....
+
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/UpgradeLog.XML" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/UpgradeLog.XML"
new file mode 100644
index 0000000..c7b19d0
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/UpgradeLog.XML"
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.css" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.css"
new file mode 100644
index 0000000..3411f63
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.css"
@@ -0,0 +1,207 @@
+BODY
+{
+ BACKGROUND-COLOR: white;
+ FONT-FAMILY: "Verdana", sans-serif;
+ FONT-SIZE: 100%;
+ MARGIN-LEFT: 0px;
+ MARGIN-TOP: 0px
+}
+P
+{
+ FONT-FAMILY: "Verdana", sans-serif;
+ FONT-SIZE: 70%;
+ LINE-HEIGHT: 12pt;
+ MARGIN-BOTTOM: 0px;
+ MARGIN-LEFT: 10px;
+ MARGIN-TOP: 10px
+}
+.note
+{
+ BACKGROUND-COLOR: #ffffff;
+ COLOR: #336699;
+ FONT-FAMILY: "Verdana", sans-serif;
+ FONT-SIZE: 100%;
+ MARGIN-BOTTOM: 0px;
+ MARGIN-LEFT: 0px;
+ MARGIN-TOP: 0px;
+ PADDING-RIGHT: 10px
+}
+.infotable
+{
+ BACKGROUND-COLOR: #f0f0e0;
+ BORDER-BOTTOM: #ffffff 0px solid;
+ BORDER-COLLAPSE: collapse;
+ BORDER-LEFT: #ffffff 0px solid;
+ BORDER-RIGHT: #ffffff 0px solid;
+ BORDER-TOP: #ffffff 0px solid;
+ FONT-SIZE: 70%;
+ MARGIN-LEFT: 10px
+}
+.issuetable
+{
+ BACKGROUND-COLOR: #ffffe8;
+ BORDER-COLLAPSE: collapse;
+ COLOR: #000000;
+ FONT-SIZE: 100%;
+ MARGIN-BOTTOM: 10px;
+ MARGIN-LEFT: 13px;
+ MARGIN-TOP: 0px
+}
+.issuetitle
+{
+ BACKGROUND-COLOR: #ffffff;
+ BORDER-BOTTOM: #dcdcdc 1px solid;
+ BORDER-TOP: #dcdcdc 1px;
+ COLOR: #003366;
+ FONT-WEIGHT: normal
+}
+.header
+{
+ BACKGROUND-COLOR: #cecf9c;
+ BORDER-BOTTOM: #ffffff 1px solid;
+ BORDER-LEFT: #ffffff 1px solid;
+ BORDER-RIGHT: #ffffff 1px solid;
+ BORDER-TOP: #ffffff 1px solid;
+ COLOR: #000000;
+ FONT-WEIGHT: bold
+}
+.issuehdr
+{
+ BACKGROUND-COLOR: #E0EBF5;
+ BORDER-BOTTOM: #dcdcdc 1px solid;
+ BORDER-TOP: #dcdcdc 1px solid;
+ COLOR: #000000;
+ FONT-WEIGHT: normal
+}
+.issuenone
+{
+ BACKGROUND-COLOR: #ffffff;
+ BORDER-BOTTOM: 0px;
+ BORDER-LEFT: 0px;
+ BORDER-RIGHT: 0px;
+ BORDER-TOP: 0px;
+ COLOR: #000000;
+ FONT-WEIGHT: normal
+}
+.content
+{
+ BACKGROUND-COLOR: #e7e7ce;
+ BORDER-BOTTOM: #ffffff 1px solid;
+ BORDER-LEFT: #ffffff 1px solid;
+ BORDER-RIGHT: #ffffff 1px solid;
+ BORDER-TOP: #ffffff 1px solid;
+ PADDING-LEFT: 3px
+}
+.issuecontent
+{
+ BACKGROUND-COLOR: #ffffff;
+ BORDER-BOTTOM: #dcdcdc 1px solid;
+ BORDER-TOP: #dcdcdc 1px solid;
+ PADDING-LEFT: 3px
+}
+A:link
+{
+ COLOR: #cc6633;
+ TEXT-DECORATION: underline
+}
+A:visited
+{
+ COLOR: #cc6633;
+}
+A:active
+{
+ COLOR: #cc6633;
+}
+A:hover
+{
+ COLOR: #cc3300;
+ TEXT-DECORATION: underline
+}
+H1
+{
+ BACKGROUND-COLOR: #003366;
+ BORDER-BOTTOM: #336699 6px solid;
+ COLOR: #ffffff;
+ FONT-SIZE: 130%;
+ FONT-WEIGHT: normal;
+ MARGIN: 0em 0em 0em -20px;
+ PADDING-BOTTOM: 8px;
+ PADDING-LEFT: 30px;
+ PADDING-TOP: 16px
+}
+H2
+{
+ COLOR: #000000;
+ FONT-SIZE: 80%;
+ FONT-WEIGHT: bold;
+ MARGIN-BOTTOM: 3px;
+ MARGIN-LEFT: 10px;
+ MARGIN-TOP: 20px;
+ PADDING-LEFT: 0px
+}
+H3
+{
+ COLOR: #000000;
+ FONT-SIZE: 80%;
+ FONT-WEIGHT: bold;
+ MARGIN-BOTTOM: -5px;
+ MARGIN-LEFT: 10px;
+ MARGIN-TOP: 20px
+}
+H4
+{
+ COLOR: #000000;
+ FONT-SIZE: 70%;
+ FONT-WEIGHT: bold;
+ MARGIN-BOTTOM: 0px;
+ MARGIN-TOP: 15px;
+ PADDING-BOTTOM: 0px
+}
+UL
+{
+ COLOR: #000000;
+ FONT-SIZE: 70%;
+ LIST-STYLE: square;
+ MARGIN-BOTTOM: 0pt;
+ MARGIN-TOP: 0pt
+}
+OL
+{
+ COLOR: #000000;
+ FONT-SIZE: 70%;
+ LIST-STYLE: square;
+ MARGIN-BOTTOM: 0pt;
+ MARGIN-TOP: 0pt
+}
+LI
+{
+ LIST-STYLE: square;
+ MARGIN-LEFT: 0px
+}
+.expandable
+{
+ CURSOR: hand
+}
+.expanded
+{
+ color: black
+}
+.collapsed
+{
+ DISPLAY: none
+}
+.foot
+{
+BACKGROUND-COLOR: #ffffff;
+BORDER-BOTTOM: #cecf9c 1px solid;
+BORDER-TOP: #cecf9c 2px solid
+}
+.settings
+{
+MARGIN-LEFT: 25PX;
+}
+.help
+{
+TEXT-ALIGN: right;
+margin-right: 10px;
+}
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.xslt" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.xslt"
new file mode 100644
index 0000000..142057a
--- /dev/null
+++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.xslt"
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Minus.gif" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Minus.gif"
new file mode 100644
index 0000000..17751cb
Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Minus.gif" differ
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Plus.gif" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Plus.gif"
new file mode 100644
index 0000000..f6009ca
Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Plus.gif" differ
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/msbksrc.lzh" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/msbksrc.lzh"
new file mode 100644
index 0000000..67f54db
Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/msbksrc.lzh" differ
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/\346\267\261\345\205\245\347\240\224\347\251\266Windows 7\344\270\255\347\232\204Virtual Hard Disk (VHD) \346\212\200\346\234\257.doc" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/\346\267\261\345\205\245\347\240\224\347\251\266Windows 7\344\270\255\347\232\204Virtual Hard Disk (VHD) \346\212\200\346\234\257.doc"
new file mode 100644
index 0000000..3c1c0bc
Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/\346\267\261\345\205\245\347\240\224\347\251\266Windows 7\344\270\255\347\232\204Virtual Hard Disk (VHD) \346\212\200\346\234\257.doc" differ