Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
/* 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;
}