summaryrefslogtreecommitdiff
path: root/src/VBox/Runtime/common/zip/tarcmd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/zip/tarcmd.cpp')
-rw-r--r--src/VBox/Runtime/common/zip/tarcmd.cpp745
1 files changed, 636 insertions, 109 deletions
diff --git a/src/VBox/Runtime/common/zip/tarcmd.cpp b/src/VBox/Runtime/common/zip/tarcmd.cpp
index 6e7f84a9..b018b283 100644
--- a/src/VBox/Runtime/common/zip/tarcmd.cpp
+++ b/src/VBox/Runtime/common/zip/tarcmd.cpp
@@ -1,10 +1,10 @@
/* $Id: tarcmd.cpp $ */
/** @file
- * IPRT - TAR Command.
+ * IPRT - A mini TAR Command.
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -33,35 +33,58 @@
#include <iprt/asm.h>
#include <iprt/buildconfig.h>
#include <iprt/ctype.h>
+#include <iprt/dir.h>
#include <iprt/file.h>
#include <iprt/getopt.h>
#include <iprt/initterm.h>
#include <iprt/mem.h>
#include <iprt/message.h>
#include <iprt/param.h>
+#include <iprt/path.h>
#include <iprt/stream.h>
#include <iprt/string.h>
+#include <iprt/symlink.h>
#include <iprt/vfs.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
-#define RTZIPTARCMD_OPT_DELETE 1000
-#define RTZIPTARCMD_OPT_OWNER 1001
-#define RTZIPTARCMD_OPT_GROUP 1002
-#define RTZIPTARCMD_OPT_UTC 1003
-#define RTZIPTARCMD_OPT_PREFIX 1004
+#define RTZIPTARCMD_OPT_DELETE 1000
+#define RTZIPTARCMD_OPT_OWNER 1001
+#define RTZIPTARCMD_OPT_GROUP 1002
+#define RTZIPTARCMD_OPT_UTC 1003
+#define RTZIPTARCMD_OPT_PREFIX 1004
+#define RTZIPTARCMD_OPT_FILE_MODE_AND_MASK 1005
+#define RTZIPTARCMD_OPT_FILE_MODE_OR_MASK 1006
+#define RTZIPTARCMD_OPT_DIR_MODE_AND_MASK 1007
+#define RTZIPTARCMD_OPT_DIR_MODE_OR_MASK 1008
+#define RTZIPTARCMD_OPT_FORMAT 1009
+
+/** File format. */
+typedef enum RTZIPTARFORMAT
+{
+ RTZIPTARFORMAT_INVALID = 0,
+ /** Autodetect if possible, defaulting to TAR. */
+ RTZIPTARFORMAT_AUTO_DEFAULT,
+ /** TAR. */
+ RTZIPTARFORMAT_TAR,
+ /** XAR. */
+ RTZIPTARFORMAT_XAR
+} RTZIPTARFORMAT;
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
- * IPT TAR option structure.
+ * IPRT TAR option structure.
*/
typedef struct RTZIPTARCMDOPS
{
+ /** The file format. */
+ RTZIPTARFORMAT enmFormat;
+
/** The operation (Acdrtux or RTZIPTARCMD_OPT_DELETE). */
int iOperation;
/** The long operation option name. */
@@ -73,21 +96,36 @@ typedef struct RTZIPTARCMDOPS
const char *pszFile;
/** Whether we're verbose or quiet. */
bool fVerbose;
- /** Whether to preserve permissions when restoring. */
- bool fPreservePermissions;
+ /** Whether to preserve the original file owner when restoring. */
+ bool fPreserveOwner;
+ /** Whether to preserve the original file group when restoring. */
+ bool fPreserveGroup;
+ /** Whether to skip restoring the modification time (only time stored by the
+ * traditional TAR format). */
+ bool fNoModTime;
/** The compressor/decompressor method to employ (0, z or j). */
char chZipper;
- /** The owner to set. */
+ /** The owner to set. NULL if not applicable.
+ * Always resolved into uidOwner for extraction. */
const char *pszOwner;
- /** The owner ID to set when unpacking if pszOwner is not NULL. */
+ /** The owner ID to set. NIL_RTUID if not applicable. */
RTUID uidOwner;
- /** The group to set. */
+ /** The group to set. NULL if not applicable.
+ * Always resolved into gidGroup for extraction. */
const char *pszGroup;
- /** The group ID to set when unpacking if pszGroup is not NULL. */
+ /** The group ID to set. NIL_RTGUID if not applicable. */
RTGID gidGroup;
/** Display the modification times in UTC instead of local time. */
bool fDisplayUtc;
+ /** File mode AND mask. */
+ RTFMODE fFileModeAndMask;
+ /** File mode OR mask. */
+ RTFMODE fFileModeOrMask;
+ /** Directory mode AND mask. */
+ RTFMODE fDirModeAndMask;
+ /** Directory mode OR mask. */
+ RTFMODE fDirModeOrMask;
/** What to prefix all names with when creating, adding, whatever. */
const char *pszPrefix;
@@ -101,6 +139,17 @@ typedef struct RTZIPTARCMDOPS
/** Pointer to the IPRT tar options. */
typedef RTZIPTARCMDOPS *PRTZIPTARCMDOPS;
+/**
+ * Callback used by rtZipTarDoWithMembers
+ *
+ * @returns rcExit or RTEXITCODE_FAILURE.
+ * @param pOpts The tar options.
+ * @param hVfsObj The tar object to display
+ * @param pszName The name.
+ * @param rcExit The current exit code.
+ */
+typedef RTEXITCODE (*PFNDOWITHMEMBER)(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit);
+
/**
* Checks if @a pszName is a member of @a papszNames, optionally returning the
@@ -213,9 +262,18 @@ static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTR
}
/*
- * Open the tar filesystem stream.
+ * Open the filesystem stream.
*/
- rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
+ if (pOpts->enmFormat == RTZIPTARFORMAT_TAR)
+ rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
+ else if (pOpts->enmFormat == RTZIPTARFORMAT_XAR)
+#ifdef IPRT_WITH_XAR /* Requires C++ and XML, so only in some configruation of IPRT. */
+ rc = RTZipXarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
+#else
+ rc = VERR_NOT_SUPPORTED;
+#endif
+ else /** @todo make RTZipTarFsStreamFromIoStream fail if not tar file! */
+ rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
RTVfsIoStrmRelease(hVfsIos);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
@@ -225,17 +283,424 @@ static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTR
/**
- * Display a tar entry in the verbose form.
+ * Worker for the --list and --extract commands.
+ *
+ * @returns The appropriate exit code.
+ * @param pOpts The tar options.
+ * @param pfnCallback The command specific callback.
+ */
+static RTEXITCODE rtZipTarDoWithMembers(PRTZIPTARCMDOPS pOpts, PFNDOWITHMEMBER pfnCallback)
+{
+ /*
+ * Allocate a bitmap to go with the file list. This will be used to
+ * indicate which files we've processed and which not.
+ */
+ uint32_t *pbmFound = NULL;
+ if (pOpts->cFiles)
+ {
+ pbmFound = (uint32_t *)RTMemAllocZ(((pOpts->cFiles + 31) / 32) * sizeof(uint32_t));
+ if (!pbmFound)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate the found-file-bitmap");
+ }
+
+
+ /*
+ * Open the input archive.
+ */
+ RTVFSFSSTREAM hVfsFssIn;
+ RTEXITCODE rcExit = rtZipTarCmdOpenInputArchive(pOpts, &hVfsFssIn);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Process the stream.
+ */
+ for (;;)
+ {
+ /*
+ * Retrive the next object.
+ */
+ char *pszName;
+ RTVFSOBJ hVfsObj;
+ int rc = RTVfsFsStrmNext(hVfsFssIn, &pszName, NULL, &hVfsObj);
+ if (RT_FAILURE(rc))
+ {
+ if (rc != VERR_EOF)
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext returned %Rrc", rc);
+ break;
+ }
+
+ /*
+ * Should we process this entry?
+ */
+ uint32_t iFile = UINT32_MAX;
+ if ( !pOpts->cFiles
+ || rtZipTarCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile) )
+ {
+ if (pbmFound)
+ ASMBitSet(pbmFound, iFile);
+
+ rcExit = pfnCallback(pOpts, hVfsObj, pszName, rcExit);
+ }
+
+ /*
+ * Release the current object and string.
+ */
+ RTVfsObjRelease(hVfsObj);
+ RTStrFree(pszName);
+ }
+
+ /*
+ * Complain about any files we didn't find.
+ */
+ for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
+ if (!ASMBitTest(pbmFound, iFile))
+ {
+ RTMsgError("%s: Was not found in the archive", pOpts->papszFiles[iFile]);
+ rcExit = RTEXITCODE_FAILURE;
+ }
+ }
+ RTMemFree(pbmFound);
+ return rcExit;
+}
+
+
+/**
+ * Checks if the name contains any escape sequences.
+ *
+ * An escape sequence would generally be one or more '..' references. On DOS
+ * like system, something that would make up a drive letter reference is also
+ * considered an escape sequence.
+ *
+ * @returns true / false.
+ * @param pszName The name to consider.
+ */
+static bool rtZipTarHasEscapeSequence(const char *pszName)
+{
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ if (pszName[0] == ':')
+ return true;
+#endif
+ while (*pszName)
+ {
+ while (RTPATH_IS_SEP(*pszName))
+ pszName++;
+ if ( pszName[0] == '.'
+ && pszName[1] == '.'
+ && (pszName[2] == '\0' || RTPATH_IS_SLASH(pszName[2])) )
+ return true;
+ while (*pszName && !RTPATH_IS_SEP(*pszName))
+ pszName++;
+ }
+
+ return false;
+}
+
+
+/**
+ * Queries the user ID to use when extracting a member.
*
* @returns rcExit or RTEXITCODE_FAILURE.
+ * @param pOpts The tar options.
+ * @param pUser The user info.
+ * @param pszName The file name to use when complaining.
* @param rcExit The current exit code.
- * @param hVfsObj The tar object to display
- * @param pszName The name.
+ * @param pUid Where to return the user ID.
+ */
+static RTEXITCODE rtZipTarQueryExtractOwner(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pOwner, const char *pszName, RTEXITCODE rcExit,
+ PRTUID pUid)
+{
+ if (pOpts->uidOwner != NIL_RTUID)
+ *pUid = pOpts->uidOwner;
+ else if (pOpts->fPreserveGroup)
+ {
+ if (!pOwner->Attr.u.UnixGroup.szName[0])
+ *pUid = pOwner->Attr.u.UnixOwner.uid;
+ else
+ {
+ *pUid = NIL_RTUID;
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: User resolving is not implemented.", pszName);
+ }
+ }
+ else
+ *pUid = NIL_RTUID;
+ return rcExit;
+}
+
+
+/**
+ * Queries the group ID to use when extracting a member.
+ *
+ * @returns rcExit or RTEXITCODE_FAILURE.
* @param pOpts The tar options.
+ * @param pGroup The group info.
+ * @param pszName The file name to use when complaining.
+ * @param rcExit The current exit code.
+ * @param pGid Where to return the group ID.
*/
-static RTEXITCODE rtZipTarCmdDisplayEntryVerbose(RTEXITCODE rcExit, RTVFSOBJ hVfsObj, const char *pszName,
- PRTZIPTARCMDOPS pOpts)
+static RTEXITCODE rtZipTarQueryExtractGroup(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pGroup, const char *pszName, RTEXITCODE rcExit,
+ PRTGID pGid)
{
+ if (pOpts->gidGroup != NIL_RTGID)
+ *pGid = pOpts->gidGroup;
+ else if (pOpts->fPreserveGroup)
+ {
+ if (!pGroup->Attr.u.UnixGroup.szName[0])
+ *pGid = pGroup->Attr.u.UnixGroup.gid;
+ else
+ {
+ *pGid = NIL_RTGID;
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Group resolving is not implemented.", pszName);
+ }
+ }
+ else
+ *pGid = NIL_RTGID;
+ return rcExit;
+}
+
+
+
+/**
+ * Extracts a file.
+ *
+ * Since we can restore permissions and attributes more efficiently by working
+ * directly on the file handle, we have special code path for files.
+ *
+ * @returns rcExit or RTEXITCODE_FAILURE.
+ * @param pOpts The tar options.
+ * @param hVfsObj The tar object to display
+ * @param rcExit The current exit code.
+ * @param pUnixInfo The unix fs object info.
+ * @param pOwner The owner info.
+ * @param pGroup The group info.
+ */
+static RTEXITCODE rtZipTarCmdExtractFile(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, RTEXITCODE rcExit,
+ const char *pszDst, PCRTFSOBJINFO pUnixInfo, PCRTFSOBJINFO pOwner, PCRTFSOBJINFO pGroup)
+{
+ /*
+ * Open the destination file and create a stream object for it.
+ */
+ uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_ACCESS_ATTR_DEFAULT
+ | ((RTFS_UNIX_IWUSR | RTFS_UNIX_IRUSR) << RTFILE_O_CREATE_MODE_SHIFT);
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, pszDst, fOpen);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating file: %Rrc", pszDst, rc);
+
+ RTVFSIOSTREAM hVfsIosDst;
+ rc = RTVfsIoStrmFromRTFile(hFile, fOpen, true /*fLeaveOpen*/, &hVfsIosDst);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Pump the data thru.
+ */
+ RTVFSIOSTREAM hVfsIosSrc = RTVfsObjToIoStream(hVfsObj);
+ rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (uint32_t)RT_MIN(pUnixInfo->cbObject, _1M));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Correct the file mode and other attributes.
+ */
+ if (!pOpts->fNoModTime)
+ {
+ rc = RTFileSetTimes(hFile, NULL, &pUnixInfo->ModificationTime, NULL, NULL);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error setting times: %Rrc", pszDst, rc);
+ }
+
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+ if ( pOpts->uidOwner != NIL_RTUID
+ || pOpts->gidGroup != NIL_RTGID
+ || pOpts->fPreserveOwner
+ || pOpts->fPreserveGroup)
+ {
+ RTUID uidFile;
+ rcExit = rtZipTarQueryExtractOwner(pOpts, pOwner, pszDst, rcExit, &uidFile);
+
+ RTGID gidFile;
+ rcExit = rtZipTarQueryExtractGroup(pOpts, pGroup, pszDst, rcExit, &gidFile);
+ if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
+ {
+ rc = RTFileSetOwner(hFile, uidFile, gidFile);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", pszDst, rc);
+ }
+ }
+#endif
+
+ RTFMODE fMode = (pUnixInfo->Attr.fMode & pOpts->fFileModeAndMask) | pOpts->fFileModeOrMask;
+ rc = RTFileSetMode(hFile, fMode | RTFS_TYPE_FILE);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", pszDst, rc);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error writing out file: %Rrc", pszDst, rc);
+ RTVfsIoStrmRelease(hVfsIosSrc);
+ RTVfsIoStrmRelease(hVfsIosDst);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating I/O stream for file: %Rrc", pszDst, rc);
+ RTFileClose(hFile);
+ return rcExit;
+}
+
+
+/**
+ * @callback_method_impl{PFNDOWITHMEMBER, Implements --extract.}
+ */
+static RTEXITCODE rtZipTarCmdExtractCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
+{
+ if (pOpts->fVerbose)
+ RTPrintf("%s\n", pszName);
+
+ /*
+ * Query all the information.
+ */
+ RTFSOBJINFO UnixInfo;
+ int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
+
+ RTFSOBJINFO Owner;
+ rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE,
+ "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
+ rc, pszName);
+
+ RTFSOBJINFO Group;
+ rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE,
+ "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
+ rc, pszName);
+
+ const char *pszLinkType = NULL;
+ char szTarget[RTPATH_MAX];
+ szTarget[0] = '\0';
+ RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
+ if (hVfsSymlink != NIL_RTVFSSYMLINK)
+ {
+ rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
+ RTVfsSymlinkRelease(hVfsSymlink);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: RTVfsSymlinkRead failed: %Rrc", pszName, rc);
+ if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Hardlinks are not supported.", pszName);
+ if (!szTarget[0])
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Link target is empty.", pszName);
+ }
+ else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
+
+ if (rtZipTarHasEscapeSequence(pszName))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Name '%s' contains an escape sequence.", pszName);
+
+ /*
+ * Construct the path to the extracted member.
+ */
+ char szDst[RTPATH_MAX];
+ rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pszName);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to construct destination path for: %Rrc", pszName, rc);
+
+ /*
+ * Extract according to the type.
+ */
+ switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_FILE:
+ return rtZipTarCmdExtractFile(pOpts, hVfsObj, rcExit, szDst, &UnixInfo, &Owner, &Group);
+
+ case RTFS_TYPE_DIRECTORY:
+ rc = RTDirCreateFullPath(szDst, UnixInfo.Attr.fMode & RTFS_UNIX_ALL_ACCESS_PERMS);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating directory: %Rrc", szDst, rc);
+ break;
+
+ case RTFS_TYPE_SYMLINK:
+ rc = RTSymlinkCreate(szDst, szTarget, RTSYMLINKTYPE_UNKNOWN, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating symbolic link: %Rrc", szDst, rc);
+ break;
+
+ case RTFS_TYPE_FIFO:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
+ case RTFS_TYPE_DEV_CHAR:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
+ case RTFS_TYPE_DEV_BLOCK:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Block devices are not supported.", pszName);
+ case RTFS_TYPE_SOCKET:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Sockets are not supported.", pszName);
+ case RTFS_TYPE_WHITEOUT:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Whiteouts are not support.", pszName);
+ default:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Unknown file type.", pszName);
+ }
+
+ /*
+ * Set other attributes as requested .
+ * .
+ * Note! File extraction does get here.
+ */
+ if (!pOpts->fNoModTime)
+ {
+ rc = RTPathSetTimesEx(szDst, NULL, &UnixInfo.ModificationTime, NULL, NULL, RTPATH_F_ON_LINK);
+ if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED && rc != VERR_NS_SYMLINK_SET_TIME)
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing modification time: %Rrc.", pszName, rc);
+ }
+
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+ if ( pOpts->uidOwner != NIL_RTUID
+ || pOpts->gidGroup != NIL_RTGID
+ || pOpts->fPreserveOwner
+ || pOpts->fPreserveGroup)
+ {
+ RTUID uidFile;
+ rcExit = rtZipTarQueryExtractOwner(pOpts, &Owner, szDst, rcExit, &uidFile);
+
+ RTGID gidFile;
+ rcExit = rtZipTarQueryExtractGroup(pOpts, &Group, szDst, rcExit, &gidFile);
+ if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
+ {
+ rc = RTPathSetOwnerEx(szDst, uidFile, gidFile, RTPATH_F_ON_LINK);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", szDst, rc);
+ }
+ }
+#endif
+
+#if !defined(RT_OS_WINDOWS) /** @todo implement RTPathSetMode on windows... */
+ if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode)) /* RTPathSetMode follows symbolic links atm. */
+ {
+ RTFMODE fMode;
+ if (RTFS_IS_DIRECTORY(UnixInfo.Attr.fMode))
+ fMode = (UnixInfo.Attr.fMode & (pOpts->fDirModeAndMask | RTFS_TYPE_MASK)) | pOpts->fDirModeOrMask;
+ else
+ fMode = (UnixInfo.Attr.fMode & (pOpts->fFileModeAndMask | RTFS_TYPE_MASK)) | pOpts->fFileModeOrMask;
+ rc = RTPathSetMode(szDst, fMode);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", szDst, rc);
+ }
+#endif
+
+ return rcExit;
+}
+
+
+/**
+ * @callback_method_impl{PFNDOWITHMEMBER, Implements --list.}
+ */
+static RTEXITCODE rtZipTarCmdListCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
+{
+ /*
+ * This is very simple in non-verbose mode.
+ */
+ if (!pOpts->fVerbose)
+ {
+ RTPrintf("%s\n", pszName);
+ return rcExit;
+ }
+
/*
* Query all the information.
*/
@@ -392,91 +857,86 @@ static RTEXITCODE rtZipTarCmdDisplayEntryVerbose(RTEXITCODE rcExit, RTVFSOBJ hVf
return rcExit;
}
+
/**
- * Implements the -t/--list operation.
+ * Display usage.
*
- * @returns The appropriate exit code.
- * @param pOpts The tar options.
+ * @param pszProgName The program name.
*/
-static RTEXITCODE rtZipTarCmdList(PRTZIPTARCMDOPS pOpts)
+static void rtZipTarUsage(const char *pszProgName)
{
/*
- * Allocate a bitmap to go with the file list. This will be used to
- * indicate which files we've processed and which not.
+ * 0 1 2 3 4 5 6 7 8
+ * 012345678901234567890123456789012345678901234567890123456789012345678901234567890
*/
- uint32_t *pbmFound = NULL;
- if (pOpts->cFiles)
- {
- pbmFound = (uint32_t *)RTMemAllocZ(((pOpts->cFiles + 31) / 32) * sizeof(uint32_t));
- if (!pbmFound)
- return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate the found-file-bitmap");
- }
-
-
- /*
- * Open the input archive.
- */
- RTVFSFSSTREAM hVfsFssIn;
- RTEXITCODE rcExit = rtZipTarCmdOpenInputArchive(pOpts, &hVfsFssIn);
- if (rcExit == RTEXITCODE_SUCCESS)
- {
- /*
- * Process the stream.
- */
- for (;;)
- {
- /*
- * Retrive the next object.
- */
- char *pszName;
- RTVFSOBJ hVfsObj;
- int rc = RTVfsFsStrmNext(hVfsFssIn, &pszName, NULL, &hVfsObj);
- if (RT_FAILURE(rc))
- {
- if (rc != VERR_EOF)
- rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext returned %Rrc", rc);
- break;
- }
-
- /*
- * Should we display this entry?
- */
- uint32_t iFile = UINT32_MAX;
- if ( !pOpts->cFiles
- || rtZipTarCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile) )
- {
- if (pbmFound)
- ASMBitSet(pbmFound, iFile);
-
- if (!pOpts->fVerbose)
- RTPrintf("%s\n", pszName);
- else
- rcExit = rtZipTarCmdDisplayEntryVerbose(rcExit, hVfsObj, pszName, pOpts);
- }
-
- /*
- * Release the current object and string.
- */
- RTVfsObjRelease(hVfsObj);
- RTStrFree(pszName);
- }
-
- /*
- * Complain about any files we didn't find.
- */
- for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
- if (!ASMBitTest(pbmFound, iFile))
- {
- RTMsgError("%s: Was not found in the archive", pOpts->papszFiles[iFile]);
- rcExit = RTEXITCODE_FAILURE;
- }
- }
- RTMemFree(pbmFound);
- return rcExit;
+ RTPrintf("Usage: %s [options]\n"
+ "\n",
+ pszProgName);
+ RTPrintf("Operations:\n"
+ " -A, --concatenate, --catenate\n"
+ " Append the content of one tar archive to another. (not impl)\n"
+ " -c, --create\n"
+ " Create a new tar archive. (not impl)\n"
+ " -d, --diff, --compare\n"
+ " Compare atar archive with the file system. (not impl)\n"
+ " -r, --append\n"
+ " Append more files to the tar archive. (not impl)\n"
+ " -t, --list\n"
+ " List the contents of the tar archive.\n"
+ " -u, --update\n"
+ " Update the archive, adding files that are newer than the\n"
+ " ones in the archive. (not impl)\n"
+ " -x, --extract, --get\n"
+ " Extract the files from the tar archive.\n"
+ " --delete\n"
+ " Delete files from the tar archive.\n"
+ "\n"
+ );
+ RTPrintf("Basic Options:\n"
+ " -C <dir>, --directory <dir> (-A, -C, -d, -r, -u, -x)\n"
+ " Sets the base directory for input and output file members.\n"
+ " This does not apply to --file, even if it preceeds it.\n"
+ " -f <archive>, --file <archive> (all)\n"
+ " The tar file to create or process. '-' indicates stdout/stdin,\n"
+ " which is is the default.\n"
+ " -v, --verbose (all)\n"
+ " Verbose operation.\n"
+ " -p, --preserve-permissions (-x)\n"
+ " Preserve all permissions when extracting. Must be used\n"
+ " before the mode mask options as it will change some of these.\n"
+ " -j, --bzip2 (all)\n"
+ " Compress/decompress the archive with bzip2.\n"
+ " -z, --gzip, --gunzip, --ungzip (all)\n"
+ " Compress/decompress the archive with gzip.\n"
+ "\n");
+ RTPrintf("Misc Options:\n"
+ " --owner <uid/username> (-A, -C, -d, -r, -u, -x)\n"
+ " Set the owner of extracted and archived files to the user specified.\n"
+ " --group <uid/username> (-A, -C, -d, -r, -u, -x)\n"
+ " Set the group of extracted and archived files to the group specified.\n"
+ " --utc (-t)\n"
+ " Display timestamps as UTC instead of local time.\n"
+ "\n");
+ RTPrintf("IPRT Options:\n"
+ " --prefix <dir-prefix> (-A, -C, -d, -r, -u)\n"
+ " Directory prefix to give the members added to the archive.\n"
+ " --file-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
+ " Restrict the access mode of regular and special files.\n"
+ " --file-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
+ " Include the given access mode for regular and special files.\n"
+ " --dir-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
+ " Restrict the access mode of directories.\n"
+ " --dir-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
+ " Include the given access mode for directories.\n"
+ "\n");
+ RTPrintf("Standard Options:\n"
+ " -h, -?, --help\n"
+ " Display this help text.\n"
+ " -V, --version\n"
+ " Display version number.\n");
}
-
RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
{
/*
@@ -514,10 +974,15 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
/* other options. */
{ "--owner", RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
{ "--group", RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
- { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
+ { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
/* IPRT extensions */
- { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
+ { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
+ { "--file-mode-and-mask", RTZIPTARCMD_OPT_FILE_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
+ { "--file-mode-or-mask", RTZIPTARCMD_OPT_FILE_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
+ { "--dir-mode-and-mask", RTZIPTARCMD_OPT_DIR_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
+ { "--dir-mode-or-mask", RTZIPTARCMD_OPT_DIR_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
+ { "--format", RTZIPTARCMD_OPT_FORMAT, RTGETOPT_REQ_STRING },
};
RTGETOPTSTATE GetState;
@@ -527,7 +992,21 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
RTZIPTARCMDOPS Opts;
- RT_ZERO(Opts); /* nice defaults :-) */
+ RT_ZERO(Opts);
+ Opts.enmFormat = RTZIPTARFORMAT_AUTO_DEFAULT;
+ Opts.uidOwner = NIL_RTUID;
+ Opts.gidGroup = NIL_RTUID;
+ Opts.fFileModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
+ Opts.fDirModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
+#if 0
+ if (RTPermIsSuperUser())
+ {
+ Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
+ Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
+ Opts.fPreserveOwner = true;
+ Opts.fPreserveGroup = true;
+ }
+#endif
RTGETOPTUNION ValueUnion;
while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
@@ -569,7 +1048,10 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
break;
case 'p':
- Opts.fPreservePermissions = true;
+ Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
+ Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
+ Opts.fPreserveOwner = true;
+ Opts.fPreserveGroup = true;
break;
case 'j':
@@ -583,12 +1065,32 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
if (Opts.pszOwner)
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --owner once");
Opts.pszOwner = ValueUnion.psz;
+
+ rc = RTStrToUInt32Full(Opts.pszOwner, 0, &ValueUnion.u32);
+ if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+ "Error convering --owner '%s' into a number: %Rrc", Opts.pszOwner, rc);
+ if (RT_SUCCESS(rc))
+ {
+ Opts.uidOwner = ValueUnion.u32;
+ Opts.pszOwner = NULL;
+ }
break;
case RTZIPTARCMD_OPT_GROUP:
if (Opts.pszGroup)
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --group once");
Opts.pszGroup = ValueUnion.psz;
+
+ rc = RTStrToUInt32Full(Opts.pszGroup, 0, &ValueUnion.u32);
+ if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+ "Error convering --group '%s' into a number: %Rrc", Opts.pszGroup, rc);
+ if (RT_SUCCESS(rc))
+ {
+ Opts.gidGroup = ValueUnion.u32;
+ Opts.pszGroup = NULL;
+ }
break;
case RTZIPTARCMD_OPT_UTC:
@@ -602,13 +1104,36 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
Opts.pszPrefix = ValueUnion.psz;
break;
+ case RTZIPTARCMD_OPT_FILE_MODE_AND_MASK:
+ Opts.fFileModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
+ break;
+
+ case RTZIPTARCMD_OPT_FILE_MODE_OR_MASK:
+ Opts.fFileModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
+ break;
+
+ case RTZIPTARCMD_OPT_DIR_MODE_AND_MASK:
+ Opts.fDirModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
+ break;
+
+ case RTZIPTARCMD_OPT_DIR_MODE_OR_MASK:
+ Opts.fDirModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
+ break;
+
+ case RTZIPTARCMD_OPT_FORMAT:
+ if (!strcmp(ValueUnion.psz, "auto") || !strcmp(ValueUnion.psz, "default"))
+ Opts.enmFormat = RTZIPTARFORMAT_AUTO_DEFAULT;
+ else if (!strcmp(ValueUnion.psz, "tar"))
+ Opts.enmFormat = RTZIPTARFORMAT_TAR;
+ else if (!strcmp(ValueUnion.psz, "xar"))
+ Opts.enmFormat = RTZIPTARFORMAT_XAR;
+ else
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
+ break;
+
+ /* Standard bits. */
case 'h':
- RTPrintf("Usage: to be written\nOption dump:\n");
- for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
- if (RT_C_IS_PRINT(s_aOptions[i].iShort))
- RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
- else
- RTPrintf(" %s\n", s_aOptions[i].pszLong);
+ rtZipTarUsage(RTPathFilename(papszArgs[0]));
return RTEXITCODE_SUCCESS;
case 'V':
@@ -651,14 +1176,16 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
switch (Opts.iOperation)
{
case 't':
- return rtZipTarCmdList(&Opts);
+ return rtZipTarDoWithMembers(&Opts, rtZipTarCmdListCallback);
+
+ case 'x':
+ return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
case 'A':
case 'c':
case 'd':
case 'r':
case 'u':
- case 'x':
case RTZIPTARCMD_OPT_DELETE:
return RTMsgErrorExit(RTEXITCODE_FAILURE, "The operation %s is not implemented yet", Opts.pszOperation);