/* $Id$ */
/** @file
* IPRT - Tar archive I/O.
*/
/*
* Copyright (C) 2009-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "tar.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** @name RTTARRECORD::h::linkflag
* @{ */
/** @} */
/**
* A tar file header.
*/
typedef union RTTARRECORD
{
char d[512];
struct h
{
char linkflag;
} h;
} RTTARRECORD;
/** Pointer to a tar file header. */
/** Pointer to a tar file handle. */
/**
* The internal data of a tar handle.
*/
typedef struct RTTARINTERNAL
{
/** The magic (RTTAR_MAGIC). */
/** The handle to the tar file. */
/** The open mode for hTarFile. */
/** Whether a file within the archive is currently open for writing.
* Only one can be open. */
bool fFileOpenForWrite;
/** The tar file VFS handle (for reading). */
/** The tar file system VFS handle. */
/** Set if hVfsFss is at the start of the stream and doesn't need rewinding. */
bool fFssAtStart;
/** Pointer to a the internal data of a tar handle. */
/**
* The internal data of a file within a tar file.
*/
typedef struct RTTARFILEINTERNAL
{
/** The magic (RTTARFILE_MAGIC). */
/** The open mode. */
/** Pointer to back to the tar file. */
/** The name of the file. */
char *pszFilename;
/** The offset into the archive where the file header starts. */
/** The size of the file. */
/** The size set by RTTarFileSetSize(). */
/** The current offset within this file. */
/** The VFS I/O stream (only for reading atm). */
/** Pointer to the internal data of a tar file. */
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
/* RTTAR */
do { \
} while (0)
/* RTTARFILE */
do { \
} while (0)
/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
/* RTTAR */
/* RTTARFILE */
/** Validates a handle and returns (void) if not valid. */
/* RTTAR */
do { \
} while (0)
/* RTTARFILE */
do { \
} while (0)
{
/*
* Create a tar instance.
*/
if (!pThis)
return VERR_NO_MEMORY;
pThis->fFssAtStart = false;
/*
* Open the tar file.
*/
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
return rc;
}
{
return VINF_SUCCESS;
/* gtar gives a warning, but the documentation says EOF is indicated by a
* zero block. Disabled for now. */
#if 0
{
/* Append the EOF record which is filled all by zeros */
}
#endif
{
}
{
}
{
}
return rc;
}
/**
* Creates a tar file handle for a read-only VFS stream object.
*
* @returns IPRT status code.
* @param pszName The file name. Automatically freed on failure.
* @param hVfsIos The VFS I/O stream we create the handle around.
* The reference is NOT consumed.
* @param fOpen The open flags.
* @param ppFile Where to return the handle.
*/
static int rtTarFileCreateHandleForReadOnly(char *pszName, RTVFSIOSTREAM hVfsIos, uint32_t fOpen, PRTTARFILEINTERNAL *ppFile)
{
int rc;
if (pNewFile)
{
if (RT_SUCCESS(rc))
{
pNewFile->offCurrent = 0;
return VINF_SUCCESS;
}
}
else
rc = VERR_NO_MEMORY;
return rc;
}
/* Only used for write handles. */
static PRTTARFILEINTERNAL rtTarFileCreateForWrite(PRTTARINTERNAL pInt, const char *pszFilename, uint32_t fOpen)
{
if (!pFileInt)
return NULL;
if (pFileInt->pszFilename)
return pFileInt;
return NULL;
}
{
/* Write only interface now. */
return VERR_INVALID_HANDLE;
if (fOpen & RTFILE_O_WRITE)
{
return VERR_WRITE_PROTECT;
if (pInt->fFileOpenForWrite)
return VERR_TOO_MANY_OPEN_FILES;
}
if (!(fOpen & RTFILE_O_WRITE))
{
/*
* Rewind the stream if necessary.
*/
if (!pInt->fFssAtStart)
{
{
}
{
if (RT_FAILURE(rc))
return rc;
}
if (RT_FAILURE(rc))
return rc;
}
/*
* Search the file system stream.
*/
pInt->fFssAtStart = false;
for (;;)
{
char *pszName;
return VERR_FILE_NOT_FOUND;
if (RT_FAILURE(rc))
return rc;
{
else
{
}
break;
}
} /* Search loop. */
}
else
{
if (!pFileInt)
return VERR_NO_MEMORY;
pInt->fFileOpenForWrite = true;
/* If we are in write mode, we also in append mode. Add an dummy
* header at the end of the current file. It will be filled by the
* close operation. */
if (RT_SUCCESS(rc))
{
}
if (RT_SUCCESS(rc))
else
{
/* Cleanup on failure */
if (pFileInt->pszFilename)
}
}
return rc;
}
/**
* Calculates the TAR header checksums and detects if it's all zeros.
*
* @returns true if all zeros, false if not.
* @param pHdr The header to checksum.
* @param pi32Unsigned Where to store the checksum calculated using
* unsigned chars. This is the one POSIX
* specifies.
* @param pi32Signed Where to store the checksum calculated using
* signed chars.
*
* @remarks The reason why we calculate the checksum as both signed and unsigned
* has to do with various the char C type being signed on some hosts
* and unsigned on others.
*
* @remarks Borrowed from tarvfs.cpp.
*/
{
/*
* Sum up the entire header.
*/
do
{
i32Unsigned += *(unsigned char *)pch;
/*
* Check if it's all zeros and replace the chksum field with spaces.
*/
do
{
i32Unsigned -= *(unsigned char *)pch;
if (pi32Signed)
*pi32Signed = i32Signed;
return fZeroHdr;
}
{
/*
* Small enough for the standard octal string encoding?
*
* Note! We could actually use the terminator character as well if we liked,
* but let not do that as it's easier to test this way.
*/
else
{
/*
* Base 256 extension. Set the highest bit of the left most character.
* We don't deal with negatives here, cause the size have to be greater
* than zero.
*
* Note! The base-256 extension are never used by gtar or libarchive
* with the "ustar \0" format version, only the later
* "ustar\000" version. However, this shouldn't cause much
* trouble as they are not picky about what they read.
*/
puchField[0] = 0x80;
do
{
cbSize >>= 8;
} while (cchField);
}
}
{
/** @todo check for field overflows. */
/* Fill the header record */
// RT_ZERO(pRecord); - done by the caller.
/** @todo use RTStrCopy */
return VERR_BUFFER_OVERFLOW;
/* Create the checksum out of the new header */
return VERR_TAR_END_OF_FILE;
/* Format the checksum */
return VINF_SUCCESS;
}
{
*pcbSize = 0;
/* Allocate a reasonably large buffer, fall back on a tiny one.
* Note: has to be 512 byte aligned and >= 512 byte. */
if (!pvTmp)
{
cbTmp = sizeof(RTTARRECORD);
}
return pvTmp;
}
{
/* Allocate a temporary buffer for copying the tar content in blocks. */
if (!pvTmp)
return VERR_NO_MEMORY;
for (;;)
{
if (cbAllWritten >= cbSize)
break;
if (RT_FAILURE(rc))
break;
}
return rc;
}
{
/* Already closed? */
if (hFile == NIL_RTTARFILE)
return VINF_SUCCESS;
/* In write mode: */
{
do
{
/* If the user has called RTTarFileSetSize in the meantime, we have
to make sure the file has the right size. */
{
if (RT_FAILURE(rc))
break;
}
/* If the written size isn't 512 byte aligned, we need to fix this. */
{
/* Note the RTFile method. We didn't increase the cbSize or cbCurrentPos here. */
&record,
NULL);
if (RT_FAILURE(rc))
break;
}
/* Create a header record for the file */
/* Todo: mode, gid, uid, mtime should be setable (or detected myself) */
if (RT_FAILURE(rc))
break;
/* Write this at the start of the file data */
rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->offStart, &record, sizeof(RTTARRECORD), NULL);
if (RT_FAILURE(rc))
break;
}
while (0);
}
/*
* Now cleanup and delete the handle.
*/
if (pFileInt->pszFilename)
{
}
return rc;
}
RTR3DECL(int) RTTarFileReadAt(RTTARFILE hFile, uint64_t off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
{
int rc = RTVfsIoStrmReadAt(pFileInt->hVfsIos, off, pvBuf, cbToRead, true /*fBlocking*/, &cbTmpRead);
if (RT_SUCCESS(rc))
{
if (pcbRead)
}
else if (pcbRead)
*pcbRead = 0;
return rc;
}
RTR3DECL(int) RTTarFileWriteAt(RTTARFILE hFile, uint64_t off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
{
return VERR_ACCESS_DENIED;
size_t cbTmpWritten = 0;
int rc = RTFileWriteAt(pFileInt->pTar->hTarFile, pFileInt->offStart + 512 + off, pvBuf, cbToWrite, &cbTmpWritten);
if (pcbWritten)
return rc;
}
{
/* Validate input */
return VINF_SUCCESS;
}
{
return VERR_WRITE_ERROR;
/** @todo If cbSize is smaller than pFileInt->cbSize we have to
* truncate the current file. */
return VINF_SUCCESS;
}