int handle_datagram(datagram *dptr, FILE *fp, uint32_t *skips) { int status = STATUS_CONTINUE; // Get the length of the data portion in bytes (not including the header) uint8_t data_length = dptr->length - sizeof(datagram); // Get the datagram type uint8_t type = dptr->type; if (*skips > 0 || dptr->data.skip_bit) { // Handle skipping N datagrams and the skip bit if (*skips > 0) { --(*skips); } return skip_datagram(fp, data_length); } // Make sure the version is valid if (dptr->version < 1 || dptr->version > 3) { printf("Invalid version: %u\n", dptr->version); return status; } // Normally, we only run things once int run_count = 1; // When the dupe bit is set, run things twice if (dptr->version == 2 && dptr->data.version2.dupe_bit) { run_count = 2; } if (dptr->version == 2 || dptr->version == 3) { // Skip the datagram if the checksum is invalid (ignoring the length) if (!valid_checksum(dptr)) { while (run_count--) { printf("Checksum is invalid!!!\n"); } return status; } } if (dptr->length < sizeof(datagram)) { // Make sure the length is valid while (run_count--) { printf("Length is invalid: %u\n", dptr->length); } status = STATUS_FAIL; } else if ((type >= 0 && type <= 3) || type == 7) { // Handle datagram containing data // Allocate memory to read the data void *data = malloc(data_length); // Read the data from the file into the buffer size_t bytes_read = fread(data, 1, data_length, fp); if (bytes_read == data_length) { // Print the data read as the correct type while (run_count--) { print_data(type, data, data_length); } } else { while (run_count--) { printf("Read %lu/%u bytes of the datagram.\n", bytes_read, data_length); } status = STATUS_FAIL; } // Free the memory allocated for the data free(data); } else if (type == CONTROL_SKIP) { // Handle datagram containing SKIP instruction // Read the number of datagrams to skip from the file (store this number in skips) size_t bytes_read = fread(skips, 1, sizeof(*skips), fp); // Modify the skip value according to the run count. // (This doubles the skips if the dupe bit is set.) (*skips) *= run_count; // If the number could not be read, then fail if (bytes_read != sizeof(*skips)) { status = STATUS_FAIL; } } else if (type == CONTROL_BURN) { // Handle datagram containing BURN instruction while (run_count--) { status = handle_burn(); } } else if (type == CONTROL_STOP) { // Handle datagram containing STOP instruction // Stop reading the file status = STATUS_STOP; } else if (type == TYPE_JUNK) { // Handle datagram containing junk data // Skip junk data status = skip_datagram(fp, data_length); } else { // Handle datagram with unrecognized type value printf("Unknown datagram type: %u\n", type); status = STATUS_FAIL; } return status; }
/* Tar file extraction * gzFile in, handle of input tarball opened with gzopen * int cm, compressionMethod * int junkPaths, nonzero indicates to ignore stored path (don't create directories) * enum KeepMode keep, indicates to perform if file exists * int iCnt, char *iList[], argv style list of files to extract, {0,NULL} for all * int xCnt, char *xList[], argv style list of files NOT to extract, {0,NULL} for none * int failOnHardLinks, if nonzero then will treat failure to create a hard link same as * failure to create a regular file, 0 prints a warning if fails - note that hardlinks * will always fail on Windows prior to NT 5 (Win 2000) or later and non NTFS file systems. * * returns 0 (or positive value) on success * returns negative value on error, where * -1 means error reading from tarball * -2 means error extracting file from tarball * -3 means error creating hard link */ int tgz_extract(gzFile in, int cm, int junkPaths, enum KeepMode keep, int iCnt, TCHAR *iList[], int xCnt, TCHAR *xList[], int failOnHardLinks) { int getheader = 1; /* assume initial input has a tar header */ HANDLE outfile = INVALID_HANDLE_VALUE; union tar_buffer buffer; unsigned long remaining; TCHAR fname[BLOCKSIZE]; /* must be >= BLOCKSIZE bytes */ time_t tartime; /* do any prep work for extracting from compressed TAR file */ if (cm_init(in, cm)) { PrintMessage(_T("tgz_extract: unable to initialize decompression method.")); cm_cleanup(cm); return -1; } while (1) { if (readBlock(cm, &buffer) < 0) return -1; /* * If we have to get a tar header */ if (getheader >= 1) { /* * if we met the end of the tar * or the end-of-tar block, * we are done */ if (/* (len == 0) || */ (buffer.header.name[0]== 0)) break; /* compute and check header checksum, support signed or unsigned */ if (!valid_checksum(&(buffer.header))) { PrintMessage(_T("tgz_extract: bad header checksum")); cm_cleanup(cm); return -1; } /* store time, so we can set the timestamp on files */ tartime = (time_t)getoct(buffer.header.mtime,12); /* copy over filename chunk from header, avoiding overruns */ if (getheader == 1) /* use normal (short or posix long) filename from header */ { /* NOTE: prepends any prefix, including separator, and ensures terminated */ memset(fname, 0, sizeof(fname)); getFullName(&buffer, fname); } else /* use (GNU) long filename that preceeded this header */ { #if 0 /* if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0) */ char fs[SHORTNAMESIZE]; /* force strings to same max len, then compare */ lstrcpyn(fs, fname, SHORTNAMESIZE); fs[SHORTNAMESIZE-1] = '\0'; buffer.header.name[SHORTNAMESIZE-1] = '\0'; if (lstrcmp(fs, buffer.header.name) != 0) { PrintMessage(_T("tgz_extract: mismatched long filename")); cm_cleanup(cm); return -1; } #else PrintMessage(_T("tgz_extract: using GNU long filename [%s]"), fname); #endif } /* LogMessage("buffer.header.name is:"); LogMessage(fname); */ switch (buffer.header.typeflag) { case DIRTYPE: dirEntry: if (!junkPaths) { safetyStrip(fname); makedir(fname); } break; case LNKTYPE: /* hard link */ case CONTTYPE: /* contiguous file, for compatibility treat as normal */ case REGTYPE: case AREGTYPE: /* Note: a file ending with a / may actually be a BSD tar directory entry */ if (fname[strlen(fname)-1] == '/') goto dirEntry; remaining = getoct(buffer.header.size,12); if ( /* add (remaining > 0) && to ignore 0 zero byte files */ ( (iList == NULL) || (matchname(fname, iCnt, iList, junkPaths)) ) && (!matchname(fname, xCnt, xList, junkPaths)) ) { if (!junkPaths) /* if we want to use paths as stored */ { /* try creating directory */ TCHAR *p = tstrrchr(fname, '/'); if (p != NULL) { *p = '\0'; makedir(fname); *p = '/'; } } else { /* try ignoring directory */ TCHAR *p = tstrrchr(fname, '/'); if (p != NULL) { /* be sure terminating '\0' is copied and */ /* use ansi memcpy equivalent that handles overlapping regions */ MoveMemory(fname, p+1, (strlen(p+1) + 1) * sizeof(TCHAR) ); } } if (*fname) /* if after stripping path a fname still exists */ { /* Attempt to open the output file and report action taken to user */ const TCHAR szERRMsg[] = _T("Error: Could not create file "), szSUCMsg[] = _T("Writing "), szSKPMsg[] = _T("Skipping "); const TCHAR * szMsg = szSUCMsg; safetyStrip(fname); if (buffer.header.typeflag == LNKTYPE) { outfile = INVALID_HANDLE_VALUE; /* create a hardlink if possible, else produce just a warning unless failOnHardLinks is true */ if (!MakeHardLink(fname, buffer.header.linkname)) { PrintMessage(_T("Warning: unable to create hard link %s [%d]"), fname, GetLastError()); if (failOnHardLinks) { cm_cleanup(cm); return -3; } } else { outfile = CreateFile(fname,GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); goto setTimeAndCloseFile; } } else { /* Open the file for writing mode, creating if doesn't exist and truncating if exists and overwrite mode */ outfile = CreateFile(fname,GENERIC_WRITE,FILE_SHARE_READ,NULL,(keep==OVERWRITE)?CREATE_ALWAYS:CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); /* failed to open file, either valid error (like open) or it already exists and in a keep mode */ if (outfile == INVALID_HANDLE_VALUE) { /* if skip existing or only update existing and failed to open becauses exists */ if ((keep!=OVERWRITE) && (GetLastError()==ERROR_FILE_EXISTS)) { /* assume skipping initially (mode==SKIP or ==UPDATE with existing file newer) */ szMsg = szSKPMsg; /* and update output message accordingly */ /* if in update mode, check filetimes and reopen in overwrite mode */ if (keep == UPDATE) { FILETIME ftm_a; HANDLE h; WIN32_FIND_DATA ffData; cnv_tar2win_time(tartime, &ftm_a); /* archive file time */ h = FindFirstFile(fname, &ffData); /* existing file time */ if (h!=INVALID_HANDLE_VALUE) FindClose(h); /* cleanup search handle */ else goto ERR_OPENING; /* compare date+times, is one in tarball newer? */ if (*((LONGLONG *)&ftm_a) > *((LONGLONG *)&(ffData.ftLastWriteTime))) { outfile = CreateFile(fname,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (outfile == INVALID_HANDLE_VALUE) goto ERR_OPENING; szMsg = szSUCMsg; } } } else /* in overwrite mode or failed for some other error than exists */ { ERR_OPENING: PrintMessage(_T("%s%s [%d]"), szERRMsg, fname, GetLastError()); cm_cleanup(cm); return -2; } } /* Inform user of current extraction action (writing, skipping file XYZ) */ PrintMessage(_T("%s%s"), szMsg, fname); } } } else outfile = INVALID_HANDLE_VALUE; /* * could have no contents, in which case we close the file and set the times */ if (remaining > 0) getheader = 0; else { setTimeAndCloseFile: getheader = 1; if (outfile != INVALID_HANDLE_VALUE) { FILETIME ftm; cnv_tar2win_time(tartime, &ftm); SetFileTime(outfile,&ftm,NULL,&ftm); CloseHandle(outfile); outfile = INVALID_HANDLE_VALUE; } } break; case GNUTYPE_LONGLINK: case GNUTYPE_LONGNAME: { remaining = getoct(buffer.header.size,12); if (readBlock(cm, fname) < 0) return -1; fname[BLOCKSIZE-1] = '\0'; if ((remaining >= BLOCKSIZE) || ((unsigned)strlen(fname) > remaining)) { PrintMessage(_T("tgz_extract: invalid long name")); cm_cleanup(cm); return -1; } getheader = 2; break; } default: /* if (action == TGZ_LIST) printf(" %s <---> %s\n",strtime(&tartime),fname); */ break; } } else /* (getheader == 0) */ { unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining; unsigned long bwritten; if (outfile != INVALID_HANDLE_VALUE) { WriteFile(outfile,buffer.buffer,bytes,&bwritten,NULL); if (bwritten != bytes) { PrintMessage(_T("Error: write failed for %s"), fname); CloseHandle(outfile); DeleteFile(fname); cm_cleanup(cm); return -2; } } remaining -= bytes; if (remaining == 0) goto setTimeAndCloseFile; } } /* while(1) */ cm_cleanup(cm); return 0; }