diff options
Diffstat (limited to 'src/VBox/Runtime/common/zip/tarcmd.cpp')
-rw-r--r-- | src/VBox/Runtime/common/zip/tarcmd.cpp | 745 |
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); |