/************************************************************************* * * * convert.c - converts backup.ds into entry.nds, value.nds, block.nds, * * and partitio.nds * * * * Programmer - Simple Nomad - Nomad Mobile Research Centre * * * *-----------------------------------------------------------------------* * * * 6/3/97 - Initial Revision * * * *-----------------------------------------------------------------------* * * * 6/5/97 - Completed program sans header handling * * * *-----------------------------------------------------------------------* * * * 6/7/97 - Started header analysis * * * *-----------------------------------------------------------------------* * * * 6/9/97 - Rewrote all routines. Moved a lot of stuff out of main * * to clean up the program. Fixed a problem when trying to * * move past the header section. * * * *-----------------------------------------------------------------------* * * * 6/10/97 - Fixed a real stupid pointer problem and a typo that * * altered the block record size. Tested successfully with * * small BACKUP.DS file. * * * *-----------------------------------------------------------------------* * * * 6/13/97 - Decided to simplify moving past the header and just look * * for the first ENTRY.NDS record. Was able to delete a lot * * of code as a result. * * * *-----------------------------------------------------------------------* * * * 6/28/97 - Switched the int used for record counting to a long to * * allow converting huge BACKUP.DS with tons of records. * * Added an endian conversion routine. This should be used * * if a copy of BACKUP.DS is retrieved from a little endian * * server but convert is being compiled and run on a big * * endian box. * * * *************************************************************************/ /* General Notes - This code isn't the greatest, but it works. Send changes or bugs to pandora@nmrc.org. I tested this on four different BACKUP.DS files -- a couple that were 125K or so, a big one that was 19MB, and a whopper that was 47MB. The large files were why longs had to be used in places where a regular int looks like it would do the job just fine. A note on the endian problem -- Novell runs on the Intel platform, i.e. little endian, but it is conceivable that this code could be compiled on a box that supports big endian, such as Alpha, AIX, and other RISC systems. This code compiles fine on Unix like so - cc -o convert convert.c If you are on a big endian server, try this - cc -o convert convert.c -DENDIAN */ /* * Includes */ #include #include /* * Typedefs for program */ typedef unsigned long uint32; typedef unsigned int uint16; typedef unsigned char uint8; typedef unsigned int unicode; /* * Global constants */ #define TRUE 1 #define FALSE 0 #define typeEntry 3 #define typeValue 2 #define typeBlock 1 #define typePartitio 0 uint32 target = 0xfffffffe; /* * The structs...... */ /* * struct for ENTRY.NDS records */ typedef struct entry { uint32 selfOffset; /* Offset in ENTRY.NDS. If this is the first record, it is 0x00000000 followed by 0x0000014e for the second record, etc. */ uint32 checkSum; /* I assume a checksum */ uint32 val1; /* Unsure, usually 0xfeffffff. */ uint32 val2; /* Unsure, usually 0xffffffff. */ uint32 peer; /* Offset to a peer record. */ uint32 firstChild; /* Offset to first child record. If no kids, 0xffffffff. */ uint32 lastChild; /* Offset to second child record. If no kids, 0xffffffff. */ uint32 firstValue; /* Offset in VALUE.NDS of first attribute. They are usually kept in order in VALUE.NDS, but since they are crossed referenced in VALUE.NDS they don't have to be.*/ uint32 id; /* The Object ID of the record. */ uint32 partitionID; /* The partition ID of the record. */ uint32 parentID; /* The parent's Object ID, if no parent it is 0xffffffff. */ uint32 val3; /* No idea. Usually a small number.*/ uint32 val4; /* No idea. 0x00000000. */ uint32 subordinates; /* Number of subordinates. This can include other objects besides children. */ uint32 classID; /* The "type" of Object ID. */ uint32 creatTime1, /* When object was created. */ creatTime2; uint32 modTime1, /* When object was last modified. */ modTime2; uint8 name[258]; /* Dreaded unicode describing the record. If a user object it will be the common name. */ } ENTRY; /* size=334 */ /* * struct for VALUE.NDS records */ typedef struct value { uint32 selfOffset; /* Offset in VALUE.NDS. If this is the first record, it is 0x00000000 followed by 0x00000040 for the second record, etc. */ uint32 checkSum; /* I assume a checksum */ uint32 val1; /* Unsure, usually 0xfeffffff. */ uint32 val2; /* Unsure, usually 0xffffffff. */ uint32 nextVal; /* The next Value record's offset. */ uint32 firstBlock; /* Offset in BLOCK.NDS if used. */ uint32 entryID; /* Type of record in ENTRY.NDS. */ uint32 typeID; /* Type of VALUE record. */ uint32 val3; /* No idea. Usually a small number.*/ uint32 creatTime1, /* When object was created(?). */ creatTime2; uint32 length; /* Length of data. */ uint8 data[16]; /* Start of data, unless there is a small amount of data, then it's all here. */ } VALUE; /* size=64 */ /* * struct for BLOCK.NDS records */ typedef struct block { uint32 selfOffset; /* Offset in BLOCK.NDS. If this is the first record, it is 0x00000000 followed by 0x00000080 for the second record, etc. */ uint32 checkSum; /* I assume a checksum */ uint32 val1; /* Unsure. */ uint32 nextBlock; /* Next record if data>120. */ uint32 valueOffset; /* Offset in VALUE.NDS (backlink) */ uint8 data[120]; } BLOCK; /* size=128 */ /* * struct for PARTITION.NDS records */ typedef struct partition { uint32 selfOffset; /* Offset in PARTITIO.NDS. If this is the first record, it is 0x00000000 followed by 0x00000028 for the second record, etc. */ uint32 checkSum; /* I assume a checksum */ uint32 val1; /* Unsure. */ uint32 id; /* ID of record. */ uint32 entryID; /* ID in ENTRY.NDS */ uint32 replicaID; /* Replica ID (??) in ENTRY.NDS */ uint32 val2; /* Unsure. */ uint32 val3; /* Unsure. */ uint32 timeStamp1, /* Probably used to keep things in sync */ timeStamp2; } PARTITIO; /* size=40 */ typedef struct just4bytes { uint32 offset; } OFFSET; #ifdef ENDIAN union { uint32 longData; uint8 shortData[4]; } output; union { uint32 longData; uint8 shortData[4]; } input; #endif /* * Global variables */ OFFSET inHeader; FILE *fBackup; FILE *fTemp; uint32 SCAN; /* * This routine switches byte ordering to get around the big * endian -- little endian problem. I did this because systems * like AIX offer no solutions for a big endian reading a little * endian created file like BACKUP.DS. Send it a uint32 and it * returned the converted uint32. */ #ifdef ENDIAN uint32 make_conversion(uint32 k) { int i; uint32 j; input.longData=k; for (i=0; i<4; i++) output.shortData[i]=input.shortData[3-i]; return(output.longData); } #endif /* * This routine simply gets and returns the value of an offset. */ uint32 get_an_offset(void) { uint32 k; uint32 j; fseek(fBackup,SCAN,SEEK_SET); fread(&j,4,1,fBackup); k=j; #ifdef ENDIAN k=make_conversion(k); #endif return(k); } /* * This routine looks for 0xfffffffe. The first occurance of * this will be in the first ENTRY.NDS record. Once found, * SCAN is updated and fBackup is seeked so that the next * offset to be read will be the number of records in * ENTRY.NDS. */ int findFirstEntryRecord(void) { uint32 k; int FOUND; long int counter; FOUND=FALSE; while(!(feof(fBackup))) { k=get_an_offset(); if (k==target) { SCAN=(ftell(fBackup)-16); fseek(fBackup,SCAN,SEEK_SET); FOUND=TRUE; break; } SCAN++; } printf("\n"); return(FOUND); } /* * This routine creates each file for NDS as it is called. It is * passed the number of records, the filename, and the file type. */ void makeNDS(long int records, char *filename, int filetype) { ENTRY pentry; VALUE pvalue; BLOCK pblock; PARTITIO ppartitio; int cc,cb,i; i=0; fTemp = fopen(filename,"w+b"); if (fTemp == NULL) { printf("\nUnable to open %s\n",filename); exit(1); } for(i=0;i