VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/tarcmd.cpp@ 72035

Last change on this file since 72035 was 72035, checked in by vboxsync, 7 years ago

RTTar: Implemented extracting hardlinked files by copying. This is very useful for extracting VBoxAll archives on windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.7 KB
Line 
1/* $Id: tarcmd.cpp 72035 2018-04-26 11:33:32Z vboxsync $ */
2/** @file
3 * IPRT - A mini TAR Command.
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/zip.h>
32
33#include <iprt/asm.h>
34#include <iprt/buildconfig.h>
35#include <iprt/ctype.h>
36#include <iprt/dir.h>
37#include <iprt/file.h>
38#include <iprt/getopt.h>
39#include <iprt/initterm.h>
40#include <iprt/mem.h>
41#include <iprt/message.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/stream.h>
45#include <iprt/string.h>
46#include <iprt/symlink.h>
47#include <iprt/vfs.h>
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53#define RTZIPTARCMD_OPT_DELETE 1000
54#define RTZIPTARCMD_OPT_OWNER 1001
55#define RTZIPTARCMD_OPT_GROUP 1002
56#define RTZIPTARCMD_OPT_UTC 1003
57#define RTZIPTARCMD_OPT_PREFIX 1004
58#define RTZIPTARCMD_OPT_FILE_MODE_AND_MASK 1005
59#define RTZIPTARCMD_OPT_FILE_MODE_OR_MASK 1006
60#define RTZIPTARCMD_OPT_DIR_MODE_AND_MASK 1007
61#define RTZIPTARCMD_OPT_DIR_MODE_OR_MASK 1008
62#define RTZIPTARCMD_OPT_FORMAT 1009
63#define RTZIPTARCMD_OPT_READ_AHEAD 1010
64#define RTZIPTARCMD_OPT_USE_PUSH_FILE 1011
65
66/** File format. */
67typedef enum RTZIPTARCMDFORMAT
68{
69 RTZIPTARCMDFORMAT_INVALID = 0,
70 /** Autodetect if possible, defaulting to TAR. */
71 RTZIPTARCMDFORMAT_AUTO_DEFAULT,
72 /** TAR. */
73 RTZIPTARCMDFORMAT_TAR,
74 /** XAR. */
75 RTZIPTARCMDFORMAT_XAR
76} RTZIPTARCMDFORMAT;
77
78
79/*********************************************************************************************************************************
80* Structures and Typedefs *
81*********************************************************************************************************************************/
82/**
83 * IPRT TAR option structure.
84 */
85typedef struct RTZIPTARCMDOPS
86{
87 /** The file format. */
88 RTZIPTARCMDFORMAT enmFormat;
89
90 /** The operation (Acdrtux or RTZIPTARCMD_OPT_DELETE). */
91 int iOperation;
92 /** The long operation option name. */
93 const char *pszOperation;
94
95 /** The directory to change into when packing and unpacking. */
96 const char *pszDirectory;
97 /** The tar file name. */
98 const char *pszFile;
99 /** Whether we're verbose or quiet. */
100 bool fVerbose;
101 /** Whether to preserve the original file owner when restoring. */
102 bool fPreserveOwner;
103 /** Whether to preserve the original file group when restoring. */
104 bool fPreserveGroup;
105 /** Whether to skip restoring the modification time (only time stored by the
106 * traditional TAR format). */
107 bool fNoModTime;
108 /** Whether to add a read ahead thread. */
109 bool fReadAhead;
110 /** Use RTVfsFsStrmPushFile instead of RTVfsFsStrmAdd for files. */
111 bool fUsePushFile;
112 /** The compressor/decompressor method to employ (0, z or j). */
113 char chZipper;
114
115 /** The owner to set. NULL if not applicable.
116 * Always resolved into uidOwner for extraction. */
117 const char *pszOwner;
118 /** The owner ID to set. NIL_RTUID if not applicable. */
119 RTUID uidOwner;
120 /** The group to set. NULL if not applicable.
121 * Always resolved into gidGroup for extraction. */
122 const char *pszGroup;
123 /** The group ID to set. NIL_RTGUID if not applicable. */
124 RTGID gidGroup;
125 /** Display the modification times in UTC instead of local time. */
126 bool fDisplayUtc;
127 /** File mode AND mask. */
128 RTFMODE fFileModeAndMask;
129 /** File mode OR mask. */
130 RTFMODE fFileModeOrMask;
131 /** Directory mode AND mask. */
132 RTFMODE fDirModeAndMask;
133 /** Directory mode OR mask. */
134 RTFMODE fDirModeOrMask;
135
136 /** What to prefix all names with when creating, adding, whatever. */
137 const char *pszPrefix;
138
139 /** The number of files(, directories or whatever) specified. */
140 uint32_t cFiles;
141 /** Array of files(, directories or whatever).
142 * Terminated by a NULL entry. */
143 const char * const *papszFiles;
144
145 /** The TAR format to create. */
146 RTZIPTARFORMAT enmTarFormat;
147 /** TAR creation flags. */
148 uint32_t fTarCreate;
149
150} RTZIPTARCMDOPS;
151/** Pointer to the IPRT tar options. */
152typedef RTZIPTARCMDOPS *PRTZIPTARCMDOPS;
153
154/**
155 * Callback used by rtZipTarDoWithMembers
156 *
157 * @returns rcExit or RTEXITCODE_FAILURE.
158 * @param pOpts The tar options.
159 * @param hVfsObj The tar object to display
160 * @param pszName The name.
161 * @param rcExit The current exit code.
162 */
163typedef RTEXITCODE (*PFNDOWITHMEMBER)(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit);
164
165
166/**
167 * Checks if @a pszName is a member of @a papszNames, optionally returning the
168 * index.
169 *
170 * @returns true if the name is in the list, otherwise false.
171 * @param pszName The name to find.
172 * @param papszNames The array of names.
173 * @param piName Where to optionally return the array index.
174 */
175static bool rtZipTarCmdIsNameInArray(const char *pszName, const char * const *papszNames, uint32_t *piName)
176{
177 for (uint32_t iName = 0; papszNames[iName]; iName++)
178 if (!strcmp(papszNames[iName], pszName))
179 {
180 if (piName)
181 *piName = iName;
182 return true;
183 }
184 return false;
185}
186
187
188/**
189 * Archives a file.
190 *
191 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
192 * @param pOpts The options.
193 * @param hVfsFss The TAR filesystem stream handle.
194 * @param pszSrc The file path or VFS spec.
195 * @param paObjInfo[3] Array of three FS object info structures. The first
196 * one is always filled with RTFSOBJATTRADD_UNIX info.
197 * The next two may contain owner and group names if
198 * available. Buffers can be modified.
199 * @param pszDst The name to archive the file under.
200 * @param pErrInfo Error info buffer (saves stack space).
201 */
202static RTEXITCODE rtZipTarCmdArchiveFile(PRTZIPTARCMDOPS pOpts, RTVFSFSSTREAM hVfsFss, const char *pszSrc,
203 RTFSOBJINFO paObjInfo[3], const char *pszDst, PRTERRINFOSTATIC pErrInfo)
204{
205 if (pOpts->fVerbose)
206 RTPrintf("%s\n", pszDst);
207
208 /* Open the file. */
209 uint32_t offError;
210 RTVFSIOSTREAM hVfsIosSrc;
211 int rc = RTVfsChainOpenIoStream(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
212 &hVfsIosSrc, &offError, RTErrInfoInitStatic(pErrInfo));
213 if (RT_FAILURE(rc))
214 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pszSrc, rc, offError, &pErrInfo->Core);
215
216 /* I/O stream to base object. */
217 RTVFSOBJ hVfsObjSrc = RTVfsObjFromIoStream(hVfsIosSrc);
218 if (hVfsObjSrc != NIL_RTVFSOBJ)
219 {
220 /*
221 * Add it to the stream. Got to variants here so we can test the
222 * RTVfsFsStrmPushFile API too.
223 */
224 if (!pOpts->fUsePushFile)
225 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
226 else
227 {
228 uint32_t cObjInfo = 1 + (paObjInfo[1].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_OWNER)
229 + (paObjInfo[2].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_GROUP);
230 RTVFSIOSTREAM hVfsIosDst;
231 rc = RTVfsFsStrmPushFile(hVfsFss, pszDst, paObjInfo[0].cbObject, paObjInfo, cObjInfo, 0 /*fFlags*/, &hVfsIosDst);
232 if (RT_SUCCESS(rc))
233 {
234 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
235 RTVfsIoStrmRelease(hVfsIosDst);
236 }
237 }
238 RTVfsIoStrmRelease(hVfsIosSrc);
239 RTVfsObjRelease(hVfsObjSrc);
240
241 if (RT_SUCCESS(rc))
242 {
243 if (rc != VINF_SUCCESS)
244 RTMsgWarning("%Rrc adding '%s'", rc, pszDst);
245 return RTEXITCODE_SUCCESS;
246 }
247 return RTMsgErrorExitFailure("%Rrc adding '%s'", rc, pszDst);
248 }
249 RTVfsIoStrmRelease(hVfsIosSrc);
250 return RTMsgErrorExitFailure("RTVfsObjFromIoStream failed unexpectedly!");
251}
252
253
254/**
255 * Archives a directory recursively .
256 *
257 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
258 * @param pOpts The options.
259 * @param hVfsFss The TAR filesystem stream handle.
260 * @param pszSrc The directory path or VFS spec. We append to the
261 * buffer as we decend.
262 * @param cchSrc The length of the input.
263 * @param paObjInfo[3] Array of three FS object info structures. The first
264 * one is always filled with RTFSOBJATTRADD_UNIX info.
265 * The next two may contain owner and group names if
266 * available. The three buffers can be reused.
267 * @param pszDst The name to archive it the under. We append to the
268 * buffer as we decend.
269 * @param cchDst The length of the input.
270 * @param pErrInfo Error info buffer (saves stack space).
271 */
272static RTEXITCODE rtZipTarCmdArchiveDir(PRTZIPTARCMDOPS pOpts, RTVFSFSSTREAM hVfsFss, char pszSrc[RTPATH_MAX], size_t cchSrc,
273 RTFSOBJINFO paObjInfo[3], char pszDst[RTPATH_MAX], size_t cchDst,
274 PRTERRINFOSTATIC pErrInfo)
275{
276 RT_NOREF(pOpts, hVfsFss, pszSrc, cchSrc, paObjInfo, pszDst, cchDst, pErrInfo);
277 return RTMsgErrorExitFailure("Adding directories has not yet been implemented! Sorry.");
278}
279
280
281
282/**
283 * Opens the output archive specified by the options.
284 *
285 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
286 * @param pOpts The options.
287 * @param phVfsFss Where to return the TAR filesystem stream handle.
288 */
289static RTEXITCODE rtZipTarCmdOpenOutputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTREAM phVfsFss)
290{
291 int rc;
292 *phVfsFss = NIL_RTVFSFSSTREAM;
293
294 /*
295 * Open the output file.
296 */
297 RTVFSIOSTREAM hVfsIos;
298 if ( pOpts->pszFile
299 && strcmp(pOpts->pszFile, "-") != 0)
300 {
301 uint32_t offError = 0;
302 RTERRINFOSTATIC ErrInfo;
303 rc = RTVfsChainOpenIoStream(pOpts->pszFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
304 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
305 if (RT_FAILURE(rc))
306 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pOpts->pszFile, rc, offError, &ErrInfo.Core);
307 }
308 else
309 {
310 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT,
311 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
312 true /*fLeaveOpen*/,
313 &hVfsIos);
314 if (RT_FAILURE(rc))
315 return RTMsgErrorExitFailure("Failed to prepare standard output for writing: %Rrc", rc);
316 }
317
318 /*
319 * Pass it thru a compressor?
320 */
321 RTVFSIOSTREAM hVfsIosComp = NIL_RTVFSIOSTREAM;
322 switch (pOpts->chZipper)
323 {
324 /* no */
325 case '\0':
326 rc = VINF_SUCCESS;
327 break;
328
329 /* gunzip */
330 case 'z':
331 rc = RTZipGzipCompressIoStream(hVfsIos, 0 /*fFlags*/, 6, &hVfsIosComp);
332 if (RT_FAILURE(rc))
333 RTMsgError("Failed to open gzip decompressor: %Rrc", rc);
334 break;
335
336 /* bunzip2 */
337 case 'j':
338 rc = VERR_NOT_SUPPORTED;
339 RTMsgError("bzip2 is not supported by this build");
340 break;
341
342 /* bug */
343 default:
344 rc = VERR_INTERNAL_ERROR_2;
345 RTMsgError("unknown decompression method '%c'", pOpts->chZipper);
346 break;
347 }
348 if (RT_FAILURE(rc))
349 {
350 RTVfsIoStrmRelease(hVfsIos);
351 return RTEXITCODE_FAILURE;
352 }
353
354 if (hVfsIosComp != NIL_RTVFSIOSTREAM)
355 {
356 RTVfsIoStrmRelease(hVfsIos);
357 hVfsIos = hVfsIosComp;
358 hVfsIosComp = NIL_RTVFSIOSTREAM;
359 }
360
361 /*
362 * Open the filesystem stream creator.
363 */
364 if ( pOpts->enmFormat == RTZIPTARCMDFORMAT_TAR
365 || pOpts->enmFormat == RTZIPTARCMDFORMAT_AUTO_DEFAULT)
366 {
367 RTVFSFSSTREAM hVfsFss;
368 rc = RTZipTarFsStreamToIoStream(hVfsIos, pOpts->enmTarFormat, pOpts->fTarCreate, &hVfsFss);
369 if (RT_SUCCESS(rc))
370 {
371 /*
372 * Set transformation options.
373 */
374 rc = RTZipTarFsStreamSetFileMode(hVfsFss, pOpts->fFileModeAndMask, pOpts->fFileModeOrMask);
375 if (RT_SUCCESS(rc))
376 {
377 rc = RTZipTarFsStreamSetDirMode(hVfsFss, pOpts->fDirModeAndMask, pOpts->fDirModeOrMask);
378 if (RT_FAILURE(rc))
379 RTMsgError("RTZipTarFsStreamSetDirMode(%o,%o) failed: %Rrc", pOpts->fDirModeAndMask, pOpts->fDirModeOrMask, rc);
380 }
381 else
382 RTMsgError("RTZipTarFsStreamSetFileMode(%o,%o) failed: %Rrc", pOpts->fFileModeAndMask, pOpts->fFileModeOrMask, rc);
383 if ((pOpts->pszOwner || pOpts->uidOwner != NIL_RTUID) && RT_SUCCESS(rc))
384 {
385 rc = RTZipTarFsStreamSetOwner(hVfsFss, pOpts->uidOwner, pOpts->pszOwner);
386 if (RT_FAILURE(rc))
387 RTMsgError("RTZipTarFsStreamSetOwner(%d,%s) failed: %Rrc", pOpts->uidOwner, pOpts->pszOwner, rc);
388 }
389 if ((pOpts->pszGroup || pOpts->gidGroup != NIL_RTGID) && RT_SUCCESS(rc))
390 {
391 rc = RTZipTarFsStreamSetGroup(hVfsFss, pOpts->gidGroup, pOpts->pszGroup);
392 if (RT_FAILURE(rc))
393 RTMsgError("RTZipTarFsStreamSetGroup(%d,%s) failed: %Rrc", pOpts->gidGroup, pOpts->pszGroup, rc);
394 }
395 if (RT_SUCCESS(rc))
396 *phVfsFss = hVfsFss;
397 else
398 {
399 RTVfsFsStrmRelease(hVfsFss);
400 *phVfsFss = NIL_RTVFSFSSTREAM;
401 }
402 }
403 else
404 rc = RTMsgErrorExitFailure("Failed to open tar filesystem stream: %Rrc", rc);
405 }
406 else
407 rc = VERR_NOT_SUPPORTED;
408 RTVfsIoStrmRelease(hVfsIos);
409 if (RT_FAILURE(rc))
410 return RTMsgErrorExitFailure("Failed to open tar filesystem stream: %Rrc", rc);
411
412 return RTEXITCODE_SUCCESS;
413}
414
415
416/**
417 * Implements archive creation.
418 *
419 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
420 * @param pOpts The options.
421 */
422static RTEXITCODE rtZipTarCreate(PRTZIPTARCMDOPS pOpts)
423{
424 /*
425 * Refuse to create empty archive.
426 */
427 if (pOpts->cFiles == 0)
428 return RTMsgErrorExitFailure("Nothing to archive - refusing to create empty archive!");
429
430 /*
431 * First open the output file.
432 */
433 RTVFSFSSTREAM hVfsFss;
434 RTEXITCODE rcExit = rtZipTarCmdOpenOutputArchive(pOpts, &hVfsFss);
435 if (rcExit != RTEXITCODE_SUCCESS)
436 return rcExit;
437
438 /*
439 * Process the input files.
440 */
441 for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
442 {
443 const char *pszFile = pOpts->papszFiles[iFile];
444
445 /*
446 * Construct/copy the source name.
447 */
448 int rc = VINF_SUCCESS;
449 char szSrc[RTPATH_MAX];
450 if ( RTPathStartsWithRoot(pszFile)
451 || RTVfsChainIsSpec(pszFile))
452 rc = RTStrCopy(szSrc, sizeof(szSrc), pszFile);
453 else
454 rc = RTPathJoin(szSrc, sizeof(szSrc), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pOpts->papszFiles[iFile]);
455 if (RT_SUCCESS(rc))
456 {
457 /*
458 * Construct the archived name. We must strip leading root specifier.
459 */
460 char *pszFinalPath = NULL;
461 char szDst[RTPATH_MAX];
462 const char *pszDst = pszFile;
463 if (RTVfsChainIsSpec(pszFile))
464 {
465 uint32_t offError;
466 rc = RTVfsChainQueryFinalPath(pszFile, &pszFinalPath, &offError);
467 if (RT_SUCCESS(rc))
468 pszDst = pszFinalPath;
469 else
470 rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryFinalPath", pszFile, rc, offError, NULL);
471 }
472 if (RT_SUCCESS(rc))
473 {
474 pszDst = RTPathSkipRootSpec(pszDst);
475 if (*pszDst == '\0')
476 {
477 pszDst = pOpts->pszPrefix ? pOpts->pszPrefix : ".";
478 rc = RTStrCopy(szDst, sizeof(szDst), pszDst);
479 }
480 else
481 {
482 if (pOpts->pszPrefix)
483 rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszPrefix, pszDst);
484 else
485 rc = RTStrCopy(szDst, sizeof(szDst), pszDst);
486 }
487 if (RT_SUCCESS(rc))
488 {
489 /*
490 * What kind of object is this and what affiliations does it have?
491 */
492 RTERRINFOSTATIC ErrInfo;
493 uint32_t offError;
494 RTFSOBJINFO aObjInfo[3];
495 rc = RTVfsChainQueryInfo(szSrc, &aObjInfo[0], RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK,
496 &offError, RTErrInfoInitStatic(&ErrInfo));
497 if (RT_SUCCESS(rc))
498 {
499
500 rc = RTVfsChainQueryInfo(szSrc, &aObjInfo[1], RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK,
501 &offError, RTErrInfoInitStatic(&ErrInfo));
502 if (RT_SUCCESS(rc))
503 {
504 rc = RTVfsChainQueryInfo(szSrc, &aObjInfo[2], RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK,
505 &offError, RTErrInfoInitStatic(&ErrInfo));
506 if (RT_FAILURE(rc))
507 RT_ZERO(aObjInfo[2]);
508 }
509 else
510 {
511 RT_ZERO(aObjInfo[1]);
512 RT_ZERO(aObjInfo[2]);
513 }
514
515 /*
516 * Process on an object type basis.
517 */
518 RTEXITCODE rcExit2;
519 if (RTFS_IS_DIRECTORY(aObjInfo[0].Attr.fMode))
520 rcExit2 = rtZipTarCmdArchiveDir(pOpts, hVfsFss, szSrc, strlen(szSrc), aObjInfo,
521 szDst, strlen(szDst), &ErrInfo);
522 else if (RTFS_IS_FILE(aObjInfo[0].Attr.fMode))
523 rcExit2 = rtZipTarCmdArchiveFile(pOpts, hVfsFss, szSrc, aObjInfo, szDst, &ErrInfo);
524 else if (RTFS_IS_SYMLINK(aObjInfo[0].Attr.fMode))
525 rcExit2 = RTMsgErrorExitFailure("Symlink archiving is not implemented");
526 else if (RTFS_IS_FIFO(aObjInfo[0].Attr.fMode))
527 rcExit2 = RTMsgErrorExitFailure("FIFO archiving is not implemented");
528 else if (RTFS_IS_SOCKET(aObjInfo[0].Attr.fMode))
529 rcExit2 = RTMsgErrorExitFailure("Socket archiving is not implemented");
530 else if (RTFS_IS_DEV_CHAR(aObjInfo[0].Attr.fMode) || RTFS_IS_DEV_BLOCK(aObjInfo[0].Attr.fMode))
531 rcExit2 = RTMsgErrorExitFailure("Device archiving is not implemented");
532 else if (RTFS_IS_WHITEOUT(aObjInfo[0].Attr.fMode))
533 rcExit2 = RTEXITCODE_SUCCESS;
534 else
535 rcExit2 = RTMsgErrorExitFailure("Unknown file type: %#x\n", aObjInfo[0].Attr.fMode);
536 if (rcExit2 != RTEXITCODE_SUCCESS)
537 rcExit = rcExit2;
538 }
539 else
540 rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszFile, rc, offError, &ErrInfo.Core);
541 }
542 else
543 rcExit = RTMsgErrorExitFailure("archived file name is too long, skipping '%s' (%s)", pszDst, pszFile);
544 }
545 }
546 else
547 rcExit = RTMsgErrorExitFailure("input file name is too long, skipping '%s'", pszFile);
548 }
549
550 /*
551 * Finalize the archive.
552 */
553 int rc = RTVfsFsStrmEnd(hVfsFss);
554 if (RT_FAILURE(rc))
555 rcExit = RTMsgErrorExitFailure("RTVfsFsStrmEnd failed: %Rrc", rc);
556
557 RTVfsFsStrmRelease(hVfsFss);
558 return rcExit;
559}
560
561
562/**
563 * Opens the input archive specified by the options.
564 *
565 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
566 * @param pOpts The options.
567 * @param phVfsFss Where to return the TAR filesystem stream handle.
568 */
569static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTREAM phVfsFss)
570{
571 int rc;
572
573 /*
574 * Open the input file.
575 */
576 RTVFSIOSTREAM hVfsIos;
577 if ( pOpts->pszFile
578 && strcmp(pOpts->pszFile, "-") != 0)
579 {
580 uint32_t offError = 0;
581 RTERRINFOSTATIC ErrInfo;
582 rc = RTVfsChainOpenIoStream(pOpts->pszFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
583 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
584 if (RT_FAILURE(rc))
585 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pOpts->pszFile, rc, offError, &ErrInfo.Core);
586 }
587 else
588 {
589 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT,
590 RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
591 true /*fLeaveOpen*/,
592 &hVfsIos);
593 if (RT_FAILURE(rc))
594 return RTMsgErrorExitFailure("Failed to prepare standard in for reading: %Rrc", rc);
595 }
596
597 /*
598 * Pass it thru a decompressor?
599 */
600 RTVFSIOSTREAM hVfsIosDecomp = NIL_RTVFSIOSTREAM;
601 switch (pOpts->chZipper)
602 {
603 /* no */
604 case '\0':
605 rc = VINF_SUCCESS;
606 break;
607
608 /* gunzip */
609 case 'z':
610 rc = RTZipGzipDecompressIoStream(hVfsIos, 0 /*fFlags*/, &hVfsIosDecomp);
611 if (RT_FAILURE(rc))
612 RTMsgError("Failed to open gzip decompressor: %Rrc", rc);
613 break;
614
615 /* bunzip2 */
616 case 'j':
617 rc = VERR_NOT_SUPPORTED;
618 RTMsgError("bzip2 is not supported by this build");
619 break;
620
621 /* bug */
622 default:
623 rc = VERR_INTERNAL_ERROR_2;
624 RTMsgError("unknown decompression method '%c'", pOpts->chZipper);
625 break;
626 }
627 if (RT_FAILURE(rc))
628 {
629 RTVfsIoStrmRelease(hVfsIos);
630 return RTEXITCODE_FAILURE;
631 }
632
633 if (hVfsIosDecomp != NIL_RTVFSIOSTREAM)
634 {
635 RTVfsIoStrmRelease(hVfsIos);
636 hVfsIos = hVfsIosDecomp;
637 hVfsIosDecomp = NIL_RTVFSIOSTREAM;
638 }
639
640 /*
641 * Open the filesystem stream.
642 */
643 if (pOpts->enmFormat == RTZIPTARCMDFORMAT_TAR)
644 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
645 else if (pOpts->enmFormat == RTZIPTARCMDFORMAT_XAR)
646#ifdef IPRT_WITH_XAR /* Requires C++ and XML, so only in some configruation of IPRT. */
647 rc = RTZipXarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
648#else
649 rc = VERR_NOT_SUPPORTED;
650#endif
651 else /** @todo make RTZipTarFsStreamFromIoStream fail if not tar file! */
652 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
653 RTVfsIoStrmRelease(hVfsIos);
654 if (RT_FAILURE(rc))
655 return RTMsgErrorExitFailure("Failed to open tar filesystem stream: %Rrc", rc);
656
657 return RTEXITCODE_SUCCESS;
658}
659
660
661/**
662 * Worker for the --list and --extract commands.
663 *
664 * @returns The appropriate exit code.
665 * @param pOpts The tar options.
666 * @param pfnCallback The command specific callback.
667 */
668static RTEXITCODE rtZipTarDoWithMembers(PRTZIPTARCMDOPS pOpts, PFNDOWITHMEMBER pfnCallback)
669{
670 /*
671 * Allocate a bitmap to go with the file list. This will be used to
672 * indicate which files we've processed and which not.
673 */
674 uint32_t *pbmFound = NULL;
675 if (pOpts->cFiles)
676 {
677 pbmFound = (uint32_t *)RTMemAllocZ(((pOpts->cFiles + 31) / 32) * sizeof(uint32_t));
678 if (!pbmFound)
679 return RTMsgErrorExitFailure("Failed to allocate the found-file-bitmap");
680 }
681
682
683 /*
684 * Open the input archive.
685 */
686 RTVFSFSSTREAM hVfsFssIn;
687 RTEXITCODE rcExit = rtZipTarCmdOpenInputArchive(pOpts, &hVfsFssIn);
688 if (rcExit == RTEXITCODE_SUCCESS)
689 {
690 /*
691 * Process the stream.
692 */
693 for (;;)
694 {
695 /*
696 * Retrive the next object.
697 */
698 char *pszName;
699 RTVFSOBJ hVfsObj;
700 int rc = RTVfsFsStrmNext(hVfsFssIn, &pszName, NULL, &hVfsObj);
701 if (RT_FAILURE(rc))
702 {
703 if (rc != VERR_EOF)
704 rcExit = RTMsgErrorExitFailure("RTVfsFsStrmNext returned %Rrc", rc);
705 break;
706 }
707
708 /*
709 * Should we process this entry?
710 */
711 uint32_t iFile = UINT32_MAX;
712 if ( !pOpts->cFiles
713 || rtZipTarCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile) )
714 {
715 if (pbmFound)
716 ASMBitSet(pbmFound, iFile);
717
718 rcExit = pfnCallback(pOpts, hVfsObj, pszName, rcExit);
719 }
720
721 /*
722 * Release the current object and string.
723 */
724 RTVfsObjRelease(hVfsObj);
725 RTStrFree(pszName);
726 }
727
728 /*
729 * Complain about any files we didn't find.
730 */
731 for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
732 if (!ASMBitTest(pbmFound, iFile))
733 {
734 RTMsgError("%s: Was not found in the archive", pOpts->papszFiles[iFile]);
735 rcExit = RTEXITCODE_FAILURE;
736 }
737
738 RTVfsFsStrmRelease(hVfsFssIn);
739 }
740 RTMemFree(pbmFound);
741 return rcExit;
742}
743
744
745/**
746 * Checks if the name contains any escape sequences.
747 *
748 * An escape sequence would generally be one or more '..' references. On DOS
749 * like system, something that would make up a drive letter reference is also
750 * considered an escape sequence.
751 *
752 * @returns true / false.
753 * @param pszName The name to consider.
754 */
755static bool rtZipTarHasEscapeSequence(const char *pszName)
756{
757#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
758 if (pszName[0] == ':')
759 return true;
760#endif
761 while (*pszName)
762 {
763 while (RTPATH_IS_SEP(*pszName))
764 pszName++;
765 if ( pszName[0] == '.'
766 && pszName[1] == '.'
767 && (pszName[2] == '\0' || RTPATH_IS_SLASH(pszName[2])) )
768 return true;
769 while (*pszName && !RTPATH_IS_SEP(*pszName))
770 pszName++;
771 }
772
773 return false;
774}
775
776
777#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
778
779/**
780 * Queries the user ID to use when extracting a member.
781 *
782 * @returns rcExit or RTEXITCODE_FAILURE.
783 * @param pOpts The tar options.
784 * @param pUser The user info.
785 * @param pszName The file name to use when complaining.
786 * @param rcExit The current exit code.
787 * @param pUid Where to return the user ID.
788 */
789static RTEXITCODE rtZipTarQueryExtractOwner(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pOwner, const char *pszName, RTEXITCODE rcExit,
790 PRTUID pUid)
791{
792 if (pOpts->uidOwner != NIL_RTUID)
793 *pUid = pOpts->uidOwner;
794 else if (pOpts->fPreserveGroup)
795 {
796 if (!pOwner->Attr.u.UnixGroup.szName[0])
797 *pUid = pOwner->Attr.u.UnixOwner.uid;
798 else
799 {
800 *pUid = NIL_RTUID;
801 return RTMsgErrorExitFailure("%s: User resolving is not implemented.", pszName);
802 }
803 }
804 else
805 *pUid = NIL_RTUID;
806 return rcExit;
807}
808
809
810/**
811 * Queries the group ID to use when extracting a member.
812 *
813 * @returns rcExit or RTEXITCODE_FAILURE.
814 * @param pOpts The tar options.
815 * @param pGroup The group info.
816 * @param pszName The file name to use when complaining.
817 * @param rcExit The current exit code.
818 * @param pGid Where to return the group ID.
819 */
820static RTEXITCODE rtZipTarQueryExtractGroup(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pGroup, const char *pszName, RTEXITCODE rcExit,
821 PRTGID pGid)
822{
823 if (pOpts->gidGroup != NIL_RTGID)
824 *pGid = pOpts->gidGroup;
825 else if (pOpts->fPreserveGroup)
826 {
827 if (!pGroup->Attr.u.UnixGroup.szName[0])
828 *pGid = pGroup->Attr.u.UnixGroup.gid;
829 else
830 {
831 *pGid = NIL_RTGID;
832 return RTMsgErrorExitFailure("%s: Group resolving is not implemented.", pszName);
833 }
834 }
835 else
836 *pGid = NIL_RTGID;
837 return rcExit;
838}
839
840#endif /* !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) */
841
842
843/**
844 * Corrects the file mode and other attributes.
845 *
846 * Common worker for rtZipTarCmdExtractFile and rtZipTarCmdExtractHardlink.
847 *
848 * @returns rcExit or RTEXITCODE_FAILURE.
849 * @param pOpts The tar options.
850 * @param rcExit The current exit code.
851 * @param hFile The handle to the destination file.
852 * @param pszDst The destination path (for error reporting).
853 * @param pUnixInfo The unix fs object info.
854 * @param pOwner The owner info.
855 * @param pGroup The group info.
856 */
857static RTEXITCODE rtZipTarCmdExtractSetAttribs(PRTZIPTARCMDOPS pOpts, RTEXITCODE rcExit, RTFILE hFile, const char *pszDst,
858 PCRTFSOBJINFO pUnixInfo, PCRTFSOBJINFO pOwner, PCRTFSOBJINFO pGroup)
859{
860 int rc;
861
862 if (!pOpts->fNoModTime)
863 {
864 rc = RTFileSetTimes(hFile, NULL, &pUnixInfo->ModificationTime, NULL, NULL);
865 if (RT_FAILURE(rc))
866 rcExit = RTMsgErrorExitFailure("%s: Error setting times: %Rrc", pszDst, rc);
867 }
868
869#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
870 if ( pOpts->uidOwner != NIL_RTUID
871 || pOpts->gidGroup != NIL_RTGID
872 || pOpts->fPreserveOwner
873 || pOpts->fPreserveGroup)
874 {
875 RTUID uidFile;
876 rcExit = rtZipTarQueryExtractOwner(pOpts, pOwner, pszDst, rcExit, &uidFile);
877
878 RTGID gidFile;
879 rcExit = rtZipTarQueryExtractGroup(pOpts, pGroup, pszDst, rcExit, &gidFile);
880 if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
881 {
882 rc = RTFileSetOwner(hFile, uidFile, gidFile);
883 if (RT_FAILURE(rc))
884 rcExit = RTMsgErrorExitFailure("%s: Error owner/group: %Rrc", pszDst, rc);
885 }
886 }
887#else
888 RT_NOREF_PV(pOwner); RT_NOREF_PV(pGroup);
889#endif
890
891 RTFMODE fMode = (pUnixInfo->Attr.fMode & pOpts->fFileModeAndMask) | pOpts->fFileModeOrMask;
892 rc = RTFileSetMode(hFile, fMode | RTFS_TYPE_FILE);
893 if (RT_FAILURE(rc))
894 rcExit = RTMsgErrorExitFailure("%s: Error changing mode: %Rrc", pszDst, rc);
895
896 return rcExit;
897}
898
899
900/**
901 * Extracts a hard linked file.
902 *
903 * @returns rcExit or RTEXITCODE_FAILURE.
904 * @param pOpts The tar options.
905 * @param rcExit The current exit code.
906 * @param pszDst The destination path.
907 * @param pszTarget The target relative path.
908 * @param pUnixInfo The unix fs object info.
909 * @param pOwner The owner info.
910 * @param pGroup The group info.
911 */
912static RTEXITCODE rtZipTarCmdExtractHardlink(PRTZIPTARCMDOPS pOpts, RTEXITCODE rcExit, const char *pszDst,
913 const char *pszTarget, PCRTFSOBJINFO pUnixInfo, PCRTFSOBJINFO pOwner,
914 PCRTFSOBJINFO pGroup)
915{
916 /*
917 * Construct full target path and check that it exists.
918 */
919 char szFullTarget[RTPATH_MAX];
920 int rc = RTPathJoin(szFullTarget, sizeof(szFullTarget), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pszTarget);
921 if (RT_FAILURE(rc))
922 return RTMsgErrorExitFailure("%s: Failed to construct full hardlink target path for %s: %Rrc",
923 pszDst, pszTarget, rc);
924
925 if (!RTFileExists(szFullTarget))
926 return RTMsgErrorExitFailure("%s: Hardlink target not found (or not a file): %s", pszDst, szFullTarget, rc);
927
928 /*
929 * Try hardlink the file, falling back on copying.
930 */
931 /** @todo actual hardlinking */
932 if (true)
933 {
934 RTMsgWarning("%s: Hardlinking not available, copying '%s' instead.", pszDst, szFullTarget);
935
936 RTFILE hSrcFile;
937 rc = RTFileOpen(&hSrcFile, szFullTarget, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
938 if (RT_SUCCESS(rc))
939 {
940 uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_ACCESS_ATTR_DEFAULT
941 | ((RTFS_UNIX_IWUSR | RTFS_UNIX_IRUSR) << RTFILE_O_CREATE_MODE_SHIFT);
942 RTFILE hDstFile;
943 rc = RTFileOpen(&hDstFile, pszDst, fOpen);
944 if (RT_SUCCESS(rc))
945 {
946 rc = RTFileCopyByHandles(hSrcFile, hDstFile);
947 if (RT_SUCCESS(rc))
948 {
949 rcExit = rtZipTarCmdExtractSetAttribs(pOpts, rcExit, hDstFile, pszDst, pUnixInfo, pOwner, pGroup);
950 rc = RTFileClose(hDstFile);
951 if (RT_FAILURE(rc))
952 {
953 rcExit = RTMsgErrorExitFailure("%s: Error closing hardlinked file copy: %Rrc", pszDst, szFullTarget, rc);
954 RTFileDelete(pszDst);
955 }
956 }
957 else
958 {
959 rcExit = RTMsgErrorExitFailure("%s: Failed copying hardlinked file %s: %Rrc", pszDst, szFullTarget, rc);
960 rc = RTFileClose(hDstFile);
961 RTFileDelete(pszDst);
962 }
963 }
964 else
965 rcExit = RTMsgErrorExitFailure("%s: Error creating file: %Rrc", pszDst, rc);
966 RTFileClose(hSrcFile);
967 }
968 else
969 rcExit = RTMsgErrorExitFailure("%s: Error opening file '%s' for reading (hardlink target): %Rrc",
970 pszDst, szFullTarget, rc);
971 }
972
973 return rcExit;
974}
975
976
977
978/**
979 * Extracts a file.
980 *
981 * Since we can restore permissions and attributes more efficiently by working
982 * directly on the file handle, we have special code path for files.
983 *
984 * @returns rcExit or RTEXITCODE_FAILURE.
985 * @param pOpts The tar options.
986 * @param hVfsObj The tar object to display
987 * @param rcExit The current exit code.
988 * @param pszDst The destination path.
989 * @param pUnixInfo The unix fs object info.
990 * @param pOwner The owner info.
991 * @param pGroup The group info.
992 */
993static RTEXITCODE rtZipTarCmdExtractFile(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, RTEXITCODE rcExit,
994 const char *pszDst, PCRTFSOBJINFO pUnixInfo, PCRTFSOBJINFO pOwner, PCRTFSOBJINFO pGroup)
995{
996 /*
997 * Open the destination file and create a stream object for it.
998 */
999 uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_ACCESS_ATTR_DEFAULT
1000 | ((RTFS_UNIX_IWUSR | RTFS_UNIX_IRUSR) << RTFILE_O_CREATE_MODE_SHIFT);
1001 RTFILE hFile;
1002 int rc = RTFileOpen(&hFile, pszDst, fOpen);
1003 if (RT_FAILURE(rc))
1004 return RTMsgErrorExitFailure("%s: Error creating file: %Rrc", pszDst, rc);
1005
1006 RTVFSIOSTREAM hVfsIosDst;
1007 rc = RTVfsIoStrmFromRTFile(hFile, fOpen, true /*fLeaveOpen*/, &hVfsIosDst);
1008 if (RT_SUCCESS(rc))
1009 {
1010 /*
1011 * Convert source to a stream and optionally add a read ahead stage.
1012 */
1013 RTVFSIOSTREAM hVfsIosSrc = RTVfsObjToIoStream(hVfsObj);
1014 if (pOpts->fReadAhead)
1015 {
1016 RTVFSIOSTREAM hVfsReadAhead;
1017 rc = RTVfsCreateReadAheadForIoStream(hVfsIosSrc, 0 /*fFlag*/, 16 /*cBuffers*/, _256K /*cbBuffer*/, &hVfsReadAhead);
1018 if (RT_SUCCESS(rc))
1019 {
1020 RTVfsIoStrmRelease(hVfsIosSrc);
1021 hVfsIosSrc = hVfsReadAhead;
1022 }
1023 else
1024 AssertRC(rc); /* can be ignored in release builds. */
1025 }
1026
1027 /*
1028 * Pump the data thru and correct the file attributes.
1029 */
1030 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (uint32_t)RT_MIN(pUnixInfo->cbObject, _1M));
1031 if (RT_SUCCESS(rc))
1032 rcExit = rtZipTarCmdExtractSetAttribs(pOpts, rcExit, hFile, pszDst, pUnixInfo, pOwner, pGroup);
1033 else
1034 rcExit = RTMsgErrorExitFailure("%s: Error writing out file: %Rrc", pszDst, rc);
1035 RTVfsIoStrmRelease(hVfsIosSrc);
1036 RTVfsIoStrmRelease(hVfsIosDst);
1037 }
1038 else
1039 rcExit = RTMsgErrorExitFailure("%s: Error creating I/O stream for file: %Rrc", pszDst, rc);
1040 RTFileClose(hFile);
1041 return rcExit;
1042}
1043
1044
1045/**
1046 * @callback_method_impl{PFNDOWITHMEMBER, Implements --extract.}
1047 */
1048static RTEXITCODE rtZipTarCmdExtractCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
1049{
1050 if (pOpts->fVerbose)
1051 RTPrintf("%s\n", pszName);
1052
1053 /*
1054 * Query all the information.
1055 */
1056 RTFSOBJINFO UnixInfo;
1057 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
1058 if (RT_FAILURE(rc))
1059 return RTMsgErrorExitFailure("RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
1060
1061 RTFSOBJINFO Owner;
1062 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
1063 if (RT_FAILURE(rc))
1064 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1065 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
1066 rc, pszName);
1067
1068 RTFSOBJINFO Group;
1069 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
1070 if (RT_FAILURE(rc))
1071 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1072 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
1073 rc, pszName);
1074
1075 bool fIsHardLink = false;
1076 char szTarget[RTPATH_MAX];
1077 szTarget[0] = '\0';
1078 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
1079 if (hVfsSymlink != NIL_RTVFSSYMLINK)
1080 {
1081 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
1082 RTVfsSymlinkRelease(hVfsSymlink);
1083 if (RT_FAILURE(rc))
1084 return RTMsgErrorExitFailure("%s: RTVfsSymlinkRead failed: %Rrc", pszName, rc);
1085 if (!szTarget[0])
1086 return RTMsgErrorExitFailure("%s: Link target is empty.", pszName);
1087 if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
1088 {
1089 fIsHardLink = true;
1090 if (!RTFS_IS_FILE(UnixInfo.Attr.fMode))
1091 return RTMsgErrorExitFailure("%s: Hardlinks are only supported for regular files (target=%s).",
1092 pszName, szTarget);
1093 if (rtZipTarHasEscapeSequence(pszName))
1094 return RTMsgErrorExitFailure("%s: Hardlink target '%s' contains an escape sequence.",
1095 pszName, szTarget);
1096 }
1097 }
1098 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
1099 return RTMsgErrorExitFailure("Failed to get symlink object for '%s'", pszName);
1100
1101 if (rtZipTarHasEscapeSequence(pszName))
1102 return RTMsgErrorExitFailure("Name '%s' contains an escape sequence.", pszName);
1103
1104 /*
1105 * Construct the path to the extracted member.
1106 */
1107 char szDst[RTPATH_MAX];
1108 rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pszName);
1109 if (RT_FAILURE(rc))
1110 return RTMsgErrorExitFailure("%s: Failed to construct destination path for: %Rrc", pszName, rc);
1111
1112 /*
1113 * Extract according to the type.
1114 */
1115 if (!fIsHardLink)
1116 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1117 {
1118 case RTFS_TYPE_FILE:
1119 return rtZipTarCmdExtractFile(pOpts, hVfsObj, rcExit, szDst, &UnixInfo, &Owner, &Group);
1120
1121 case RTFS_TYPE_DIRECTORY:
1122 rc = RTDirCreateFullPath(szDst, UnixInfo.Attr.fMode & RTFS_UNIX_ALL_ACCESS_PERMS);
1123 if (RT_FAILURE(rc))
1124 return RTMsgErrorExitFailure("%s: Error creating directory: %Rrc", szDst, rc);
1125 break;
1126
1127 case RTFS_TYPE_SYMLINK:
1128 rc = RTSymlinkCreate(szDst, szTarget, RTSYMLINKTYPE_UNKNOWN, 0);
1129 if (RT_FAILURE(rc))
1130 return RTMsgErrorExitFailure("%s: Error creating symbolic link: %Rrc", szDst, rc);
1131 break;
1132
1133 case RTFS_TYPE_FIFO:
1134 return RTMsgErrorExitFailure("%s: FIFOs are not supported.", pszName);
1135 case RTFS_TYPE_DEV_CHAR:
1136 return RTMsgErrorExitFailure("%s: FIFOs are not supported.", pszName);
1137 case RTFS_TYPE_DEV_BLOCK:
1138 return RTMsgErrorExitFailure("%s: Block devices are not supported.", pszName);
1139 case RTFS_TYPE_SOCKET:
1140 return RTMsgErrorExitFailure("%s: Sockets are not supported.", pszName);
1141 case RTFS_TYPE_WHITEOUT:
1142 return RTMsgErrorExitFailure("%s: Whiteouts are not support.", pszName);
1143 default:
1144 return RTMsgErrorExitFailure("%s: Unknown file type.", pszName);
1145 }
1146 else
1147 return rtZipTarCmdExtractHardlink(pOpts, rcExit, szDst, szTarget, &UnixInfo, &Owner, &Group);
1148
1149 /*
1150 * Set other attributes as requested.
1151 *
1152 * Note! File extraction does get here.
1153 */
1154 if (!pOpts->fNoModTime)
1155 {
1156 rc = RTPathSetTimesEx(szDst, NULL, &UnixInfo.ModificationTime, NULL, NULL, RTPATH_F_ON_LINK);
1157 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED && rc != VERR_NS_SYMLINK_SET_TIME)
1158 rcExit = RTMsgErrorExitFailure("%s: Error changing modification time: %Rrc.", pszName, rc);
1159 }
1160
1161#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
1162 if ( pOpts->uidOwner != NIL_RTUID
1163 || pOpts->gidGroup != NIL_RTGID
1164 || pOpts->fPreserveOwner
1165 || pOpts->fPreserveGroup)
1166 {
1167 RTUID uidFile;
1168 rcExit = rtZipTarQueryExtractOwner(pOpts, &Owner, szDst, rcExit, &uidFile);
1169
1170 RTGID gidFile;
1171 rcExit = rtZipTarQueryExtractGroup(pOpts, &Group, szDst, rcExit, &gidFile);
1172 if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
1173 {
1174 rc = RTPathSetOwnerEx(szDst, uidFile, gidFile, RTPATH_F_ON_LINK);
1175 if (RT_FAILURE(rc))
1176 rcExit = RTMsgErrorExitFailure("%s: Error owner/group: %Rrc", szDst, rc);
1177 }
1178 }
1179#endif
1180
1181#if !defined(RT_OS_WINDOWS) /** @todo implement RTPathSetMode on windows... */
1182 if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode)) /* RTPathSetMode follows symbolic links atm. */
1183 {
1184 RTFMODE fMode;
1185 if (RTFS_IS_DIRECTORY(UnixInfo.Attr.fMode))
1186 fMode = (UnixInfo.Attr.fMode & (pOpts->fDirModeAndMask | RTFS_TYPE_MASK)) | pOpts->fDirModeOrMask;
1187 else
1188 fMode = (UnixInfo.Attr.fMode & (pOpts->fFileModeAndMask | RTFS_TYPE_MASK)) | pOpts->fFileModeOrMask;
1189 rc = RTPathSetMode(szDst, fMode);
1190 if (RT_FAILURE(rc))
1191 rcExit = RTMsgErrorExitFailure("%s: Error changing mode: %Rrc", szDst, rc);
1192 }
1193#endif
1194
1195 return rcExit;
1196}
1197
1198
1199/**
1200 * @callback_method_impl{PFNDOWITHMEMBER, Implements --list.}
1201 */
1202static RTEXITCODE rtZipTarCmdListCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
1203{
1204 /*
1205 * This is very simple in non-verbose mode.
1206 */
1207 if (!pOpts->fVerbose)
1208 {
1209 RTPrintf("%s\n", pszName);
1210 return rcExit;
1211 }
1212
1213 /*
1214 * Query all the information.
1215 */
1216 RTFSOBJINFO UnixInfo;
1217 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
1218 if (RT_FAILURE(rc))
1219 {
1220 rcExit = RTMsgErrorExitFailure("RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
1221 RT_ZERO(UnixInfo);
1222 }
1223
1224 RTFSOBJINFO Owner;
1225 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
1226 if (RT_FAILURE(rc))
1227 {
1228 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
1229 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
1230 rc, pszName);
1231 RT_ZERO(Owner);
1232 }
1233
1234 RTFSOBJINFO Group;
1235 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
1236 if (RT_FAILURE(rc))
1237 {
1238 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
1239 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
1240 rc, pszName);
1241 RT_ZERO(Group);
1242 }
1243
1244 const char *pszLinkType = NULL;
1245 char szTarget[RTPATH_MAX];
1246 szTarget[0] = '\0';
1247 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
1248 if (hVfsSymlink != NIL_RTVFSSYMLINK)
1249 {
1250 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
1251 if (RT_FAILURE(rc))
1252 rcExit = RTMsgErrorExitFailure("RTVfsSymlinkRead returned %Rrc on '%s'", rc, pszName);
1253 RTVfsSymlinkRelease(hVfsSymlink);
1254 pszLinkType = RTFS_IS_SYMLINK(UnixInfo.Attr.fMode) ? "->" : "link to";
1255 }
1256 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
1257 rcExit = RTMsgErrorExitFailure("Failed to get symlink object for '%s'", pszName);
1258
1259 /*
1260 * Translate the mode mask.
1261 */
1262 char szMode[16];
1263 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1264 {
1265 case RTFS_TYPE_FIFO: szMode[0] = 'f'; break;
1266 case RTFS_TYPE_DEV_CHAR: szMode[0] = 'c'; break;
1267 case RTFS_TYPE_DIRECTORY: szMode[0] = 'd'; break;
1268 case RTFS_TYPE_DEV_BLOCK: szMode[0] = 'b'; break;
1269 case RTFS_TYPE_FILE: szMode[0] = '-'; break;
1270 case RTFS_TYPE_SYMLINK: szMode[0] = 'l'; break;
1271 case RTFS_TYPE_SOCKET: szMode[0] = 's'; break;
1272 case RTFS_TYPE_WHITEOUT: szMode[0] = 'w'; break;
1273 default: szMode[0] = '?'; break;
1274 }
1275 if (pszLinkType && szMode[0] != 's')
1276 szMode[0] = 'h';
1277
1278 szMode[1] = UnixInfo.Attr.fMode & RTFS_UNIX_IRUSR ? 'r' : '-';
1279 szMode[2] = UnixInfo.Attr.fMode & RTFS_UNIX_IWUSR ? 'w' : '-';
1280 szMode[3] = UnixInfo.Attr.fMode & RTFS_UNIX_IXUSR ? 'x' : '-';
1281
1282 szMode[4] = UnixInfo.Attr.fMode & RTFS_UNIX_IRGRP ? 'r' : '-';
1283 szMode[5] = UnixInfo.Attr.fMode & RTFS_UNIX_IWGRP ? 'w' : '-';
1284 szMode[6] = UnixInfo.Attr.fMode & RTFS_UNIX_IXGRP ? 'x' : '-';
1285
1286 szMode[7] = UnixInfo.Attr.fMode & RTFS_UNIX_IROTH ? 'r' : '-';
1287 szMode[8] = UnixInfo.Attr.fMode & RTFS_UNIX_IWOTH ? 'w' : '-';
1288 szMode[9] = UnixInfo.Attr.fMode & RTFS_UNIX_IXOTH ? 'x' : '-';
1289 szMode[10] = '\0';
1290
1291 /** @todo sticky and set-uid/gid bits. */
1292
1293 /*
1294 * Make sure we've got valid owner and group strings.
1295 */
1296 if (!Owner.Attr.u.UnixGroup.szName[0])
1297 RTStrPrintf(Owner.Attr.u.UnixOwner.szName, sizeof(Owner.Attr.u.UnixOwner.szName),
1298 "%u", UnixInfo.Attr.u.Unix.uid);
1299
1300 if (!Group.Attr.u.UnixOwner.szName[0])
1301 RTStrPrintf(Group.Attr.u.UnixGroup.szName, sizeof(Group.Attr.u.UnixGroup.szName),
1302 "%u", UnixInfo.Attr.u.Unix.gid);
1303
1304 /*
1305 * Format the modification time.
1306 */
1307 char szModTime[32];
1308 RTTIME ModTime;
1309 PRTTIME pTime;
1310 if (!pOpts->fDisplayUtc)
1311 pTime = RTTimeLocalExplode(&ModTime, &UnixInfo.ModificationTime);
1312 else
1313 pTime = RTTimeExplode(&ModTime, &UnixInfo.ModificationTime);
1314 if (!pTime)
1315 RT_ZERO(ModTime);
1316 RTStrPrintf(szModTime, sizeof(szModTime), "%04d-%02u-%02u %02u:%02u",
1317 ModTime.i32Year, ModTime.u8Month, ModTime.u8MonthDay, ModTime.u8Hour, ModTime.u8Minute);
1318
1319 /*
1320 * Format the size and figure how much space is needed between the
1321 * user/group and the size.
1322 */
1323 char szSize[64];
1324 size_t cchSize;
1325 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1326 {
1327 case RTFS_TYPE_DEV_CHAR:
1328 case RTFS_TYPE_DEV_BLOCK:
1329 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%u,%u",
1330 RTDEV_MAJOR(UnixInfo.Attr.u.Unix.Device), RTDEV_MINOR(UnixInfo.Attr.u.Unix.Device));
1331 break;
1332 default:
1333 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%RU64", UnixInfo.cbObject);
1334 break;
1335 }
1336
1337 size_t cchUserGroup = strlen(Owner.Attr.u.UnixOwner.szName)
1338 + 1
1339 + strlen(Group.Attr.u.UnixGroup.szName);
1340 ssize_t cchPad = cchUserGroup + cchSize + 1 < 19
1341 ? 19 - (cchUserGroup + cchSize + 1)
1342 : 0;
1343
1344 /*
1345 * Go to press.
1346 */
1347 if (pszLinkType)
1348 RTPrintf("%s %s/%s%*s %s %s %s %s %s\n",
1349 szMode,
1350 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
1351 cchPad, "",
1352 szSize,
1353 szModTime,
1354 pszName,
1355 pszLinkType,
1356 szTarget);
1357 else
1358 RTPrintf("%s %s/%s%*s %s %s %s\n",
1359 szMode,
1360 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
1361 cchPad, "",
1362 szSize,
1363 szModTime,
1364 pszName);
1365
1366 return rcExit;
1367}
1368
1369
1370/**
1371 * Display usage.
1372 *
1373 * @param pszProgName The program name.
1374 */
1375static void rtZipTarUsage(const char *pszProgName)
1376{
1377 /*
1378 * 0 1 2 3 4 5 6 7 8
1379 * 012345678901234567890123456789012345678901234567890123456789012345678901234567890
1380 */
1381 RTPrintf("Usage: %s [options]\n"
1382 "\n",
1383 pszProgName);
1384 RTPrintf("Operations:\n"
1385 " -A, --concatenate, --catenate\n"
1386 " Append the content of one tar archive to another. (not impl)\n"
1387 " -c, --create\n"
1388 " Create a new tar archive. (not impl)\n"
1389 " -d, --diff, --compare\n"
1390 " Compare atar archive with the file system. (not impl)\n"
1391 " -r, --append\n"
1392 " Append more files to the tar archive. (not impl)\n"
1393 " -t, --list\n"
1394 " List the contents of the tar archive.\n"
1395 " -u, --update\n"
1396 " Update the archive, adding files that are newer than the\n"
1397 " ones in the archive. (not impl)\n"
1398 " -x, --extract, --get\n"
1399 " Extract the files from the tar archive.\n"
1400 " --delete\n"
1401 " Delete files from the tar archive.\n"
1402 "\n"
1403 );
1404 RTPrintf("Basic Options:\n"
1405 " -C <dir>, --directory <dir> (-A, -c, -d, -r, -u, -x)\n"
1406 " Sets the base directory for input and output file members.\n"
1407 " This does not apply to --file, even if it preceeds it.\n"
1408 " -f <archive>, --file <archive> (all)\n"
1409 " The tar file to create or process. '-' indicates stdout/stdin,\n"
1410 " which is is the default.\n"
1411 " -v, --verbose (all)\n"
1412 " Verbose operation.\n"
1413 " -p, --preserve-permissions (-x)\n"
1414 " Preserve all permissions when extracting. Must be used\n"
1415 " before the mode mask options as it will change some of these.\n"
1416 " -j, --bzip2 (all)\n"
1417 " Compress/decompress the archive with bzip2.\n"
1418 " -z, --gzip, --gunzip, --ungzip (all)\n"
1419 " Compress/decompress the archive with gzip.\n"
1420 "\n");
1421 RTPrintf("Misc Options:\n"
1422 " --owner <uid/username> (-A, -c, -d, -r, -u, -x)\n"
1423 " Set the owner of extracted and archived files to the user specified.\n"
1424 " --group <uid/username> (-A, -c, -d, -r, -u, -x)\n"
1425 " Set the group of extracted and archived files to the group specified.\n"
1426 " --utc (-t)\n"
1427 " Display timestamps as UTC instead of local time.\n"
1428 " -S, --sparse (-A, -c, -u)\n"
1429 " Detect sparse files and store them (gnu tar extension).\n"
1430 " --format <format> (-A, -c, -u, but also -d, -r, -x)\n"
1431 " The file format:\n"
1432 " auto (gnu tar)\n"
1433 " default (gnu tar)\n"
1434 " tar (gnu tar)"
1435 " gnu (tar v1.13+), "
1436 " ustar (tar POSIX.1-1988), "
1437 " pax (tar POSIX.1-2001),\n"
1438 " xar\n"
1439 " Note! Because XAR/TAR detection isn't implemented yet, it\n"
1440 " is necessary to specifcy --format=xar when reading a\n"
1441 " XAR file. Otherwise this option is only for creation.\n"
1442 "\n");
1443 RTPrintf("IPRT Options:\n"
1444 " --prefix <dir-prefix> (-A, -c, -d, -r, -u)\n"
1445 " Directory prefix to give the members added to the archive.\n"
1446 " --file-mode-and-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1447 " Restrict the access mode of regular and special files.\n"
1448 " --file-mode-or-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1449 " Include the given access mode for regular and special files.\n"
1450 " --dir-mode-and-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1451 " Restrict the access mode of directories.\n"
1452 " --dir-mode-or-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1453 " Include the given access mode for directories.\n"
1454 " --read-ahead (-x)\n"
1455 " Enabled read ahead thread when extracting files.\n"
1456 " --push-file (-A, -c, -u)\n"
1457 " Use RTVfsFsStrmPushFile instead of RTVfsFsStrmAdd.\n"
1458 "\n");
1459 RTPrintf("Standard Options:\n"
1460 " -h, -?, --help\n"
1461 " Display this help text.\n"
1462 " -V, --version\n"
1463 " Display version number.\n");
1464}
1465
1466
1467RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
1468{
1469 /*
1470 * Parse the command line.
1471 *
1472 * N.B. This is less flexible that your regular tar program in that it
1473 * requires the operation to be specified as an option. On the other
1474 * hand, you can specify it where ever you like in the command line.
1475 */
1476 static const RTGETOPTDEF s_aOptions[] =
1477 {
1478 /* operations */
1479 { "--concatenate", 'A', RTGETOPT_REQ_NOTHING },
1480 { "--catenate", 'A', RTGETOPT_REQ_NOTHING },
1481 { "--create", 'c', RTGETOPT_REQ_NOTHING },
1482 { "--diff", 'd', RTGETOPT_REQ_NOTHING },
1483 { "--compare", 'd', RTGETOPT_REQ_NOTHING },
1484 { "--append", 'r', RTGETOPT_REQ_NOTHING },
1485 { "--list", 't', RTGETOPT_REQ_NOTHING },
1486 { "--update", 'u', RTGETOPT_REQ_NOTHING },
1487 { "--extract", 'x', RTGETOPT_REQ_NOTHING },
1488 { "--get", 'x', RTGETOPT_REQ_NOTHING },
1489 { "--delete", RTZIPTARCMD_OPT_DELETE, RTGETOPT_REQ_NOTHING },
1490
1491 /* basic options */
1492 { "--directory", 'C', RTGETOPT_REQ_STRING },
1493 { "--file", 'f', RTGETOPT_REQ_STRING },
1494 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1495 { "--preserve-permissions", 'p', RTGETOPT_REQ_NOTHING },
1496 { "--bzip2", 'j', RTGETOPT_REQ_NOTHING },
1497 { "--gzip", 'z', RTGETOPT_REQ_NOTHING },
1498 { "--gunzip", 'z', RTGETOPT_REQ_NOTHING },
1499 { "--ungzip", 'z', RTGETOPT_REQ_NOTHING },
1500
1501 /* other options. */
1502 { "--owner", RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
1503 { "--group", RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
1504 { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
1505 { "--sparse", 'S', RTGETOPT_REQ_NOTHING },
1506 { "--format", RTZIPTARCMD_OPT_FORMAT, RTGETOPT_REQ_STRING },
1507
1508 /* IPRT extensions */
1509 { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
1510 { "--file-mode-and-mask", RTZIPTARCMD_OPT_FILE_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1511 { "--file-mode-or-mask", RTZIPTARCMD_OPT_FILE_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1512 { "--dir-mode-and-mask", RTZIPTARCMD_OPT_DIR_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1513 { "--dir-mode-or-mask", RTZIPTARCMD_OPT_DIR_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1514 { "--read-ahead", RTZIPTARCMD_OPT_READ_AHEAD, RTGETOPT_REQ_NOTHING },
1515 { "--use-push-file", RTZIPTARCMD_OPT_USE_PUSH_FILE, RTGETOPT_REQ_NOTHING },
1516 };
1517
1518 RTGETOPTSTATE GetState;
1519 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1520 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1521 if (RT_FAILURE(rc))
1522 return RTMsgErrorExitFailure("RTGetOpt failed: %Rrc", rc);
1523
1524 RTZIPTARCMDOPS Opts;
1525 RT_ZERO(Opts);
1526 Opts.enmFormat = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
1527 Opts.uidOwner = NIL_RTUID;
1528 Opts.gidGroup = NIL_RTUID;
1529 Opts.fFileModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
1530 Opts.fDirModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
1531#if 0
1532 if (RTPermIsSuperUser())
1533 {
1534 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1535 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1536 Opts.fPreserveOwner = true;
1537 Opts.fPreserveGroup = true;
1538 }
1539#endif
1540 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1541
1542 RTGETOPTUNION ValueUnion;
1543 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
1544 && rc != VINF_GETOPT_NOT_OPTION)
1545 {
1546 switch (rc)
1547 {
1548 /* operations */
1549 case 'A':
1550 case 'c':
1551 case 'd':
1552 case 'r':
1553 case 't':
1554 case 'u':
1555 case 'x':
1556 case RTZIPTARCMD_OPT_DELETE:
1557 if (Opts.iOperation)
1558 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Conflicting tar operation (%s already set, now %s)",
1559 Opts.pszOperation, ValueUnion.pDef->pszLong);
1560 Opts.iOperation = rc;
1561 Opts.pszOperation = ValueUnion.pDef->pszLong;
1562 break;
1563
1564 /* basic options */
1565 case 'C':
1566 if (Opts.pszDirectory)
1567 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -C/--directory once");
1568 Opts.pszDirectory = ValueUnion.psz;
1569 break;
1570
1571 case 'f':
1572 if (Opts.pszFile)
1573 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -f/--file once");
1574 Opts.pszFile = ValueUnion.psz;
1575 break;
1576
1577 case 'v':
1578 Opts.fVerbose = true;
1579 break;
1580
1581 case 'p':
1582 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1583 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1584 Opts.fPreserveOwner = true;
1585 Opts.fPreserveGroup = true;
1586 break;
1587
1588 case 'j':
1589 case 'z':
1590 if (Opts.chZipper)
1591 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify one compressor / decompressor");
1592 Opts.chZipper = rc;
1593 break;
1594
1595 case RTZIPTARCMD_OPT_OWNER:
1596 if (Opts.pszOwner)
1597 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --owner once");
1598 Opts.pszOwner = ValueUnion.psz;
1599
1600 rc = RTStrToUInt32Full(Opts.pszOwner, 0, &ValueUnion.u32);
1601 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1602 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1603 "Error convering --owner '%s' into a number: %Rrc", Opts.pszOwner, rc);
1604 if (RT_SUCCESS(rc))
1605 {
1606 Opts.uidOwner = ValueUnion.u32;
1607 Opts.pszOwner = NULL;
1608 }
1609 break;
1610
1611 case RTZIPTARCMD_OPT_GROUP:
1612 if (Opts.pszGroup)
1613 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --group once");
1614 Opts.pszGroup = ValueUnion.psz;
1615
1616 rc = RTStrToUInt32Full(Opts.pszGroup, 0, &ValueUnion.u32);
1617 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1618 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1619 "Error convering --group '%s' into a number: %Rrc", Opts.pszGroup, rc);
1620 if (RT_SUCCESS(rc))
1621 {
1622 Opts.gidGroup = ValueUnion.u32;
1623 Opts.pszGroup = NULL;
1624 }
1625 break;
1626
1627 case RTZIPTARCMD_OPT_UTC:
1628 Opts.fDisplayUtc = true;
1629 break;
1630
1631 /* GNU */
1632 case 'S':
1633 Opts.fTarCreate |= RTZIPTAR_C_SPARSE;
1634 break;
1635
1636 /* iprt extensions */
1637 case RTZIPTARCMD_OPT_PREFIX:
1638 if (Opts.pszPrefix)
1639 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --prefix once");
1640 Opts.pszPrefix = ValueUnion.psz;
1641 break;
1642
1643 case RTZIPTARCMD_OPT_FILE_MODE_AND_MASK:
1644 Opts.fFileModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1645 break;
1646
1647 case RTZIPTARCMD_OPT_FILE_MODE_OR_MASK:
1648 Opts.fFileModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1649 break;
1650
1651 case RTZIPTARCMD_OPT_DIR_MODE_AND_MASK:
1652 Opts.fDirModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1653 break;
1654
1655 case RTZIPTARCMD_OPT_DIR_MODE_OR_MASK:
1656 Opts.fDirModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1657 break;
1658
1659 case RTZIPTARCMD_OPT_FORMAT:
1660 if (!strcmp(ValueUnion.psz, "auto") || !strcmp(ValueUnion.psz, "default"))
1661 {
1662 Opts.enmFormat = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
1663 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1664 }
1665 else if (!strcmp(ValueUnion.psz, "tar"))
1666 {
1667 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1668 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1669 }
1670 else if (!strcmp(ValueUnion.psz, "gnu"))
1671 {
1672 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1673 Opts.enmTarFormat = RTZIPTARFORMAT_GNU;
1674 }
1675 else if (!strcmp(ValueUnion.psz, "ustar"))
1676 {
1677 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1678 Opts.enmTarFormat = RTZIPTARFORMAT_USTAR;
1679 }
1680 else if ( !strcmp(ValueUnion.psz, "posix")
1681 || !strcmp(ValueUnion.psz, "pax"))
1682 {
1683 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1684 Opts.enmTarFormat = RTZIPTARFORMAT_PAX;
1685 }
1686 else if (!strcmp(ValueUnion.psz, "xar"))
1687 Opts.enmFormat = RTZIPTARCMDFORMAT_XAR;
1688 else
1689 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
1690 break;
1691
1692 case RTZIPTARCMD_OPT_READ_AHEAD:
1693 Opts.fReadAhead = true;
1694 break;
1695
1696 case RTZIPTARCMD_OPT_USE_PUSH_FILE:
1697 Opts.fUsePushFile = true;
1698 break;
1699
1700 /* Standard bits. */
1701 case 'h':
1702 rtZipTarUsage(RTPathFilename(papszArgs[0]));
1703 return RTEXITCODE_SUCCESS;
1704
1705 case 'V':
1706 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1707 return RTEXITCODE_SUCCESS;
1708
1709 default:
1710 return RTGetOptPrintError(rc, &ValueUnion);
1711 }
1712 }
1713
1714 if (rc == VINF_GETOPT_NOT_OPTION)
1715 {
1716 /* this is kind of ugly. */
1717 Assert((unsigned)GetState.iNext - 1 <= cArgs);
1718 Opts.papszFiles = (const char * const *)&papszArgs[GetState.iNext - 1];
1719 Opts.cFiles = cArgs - GetState.iNext + 1;
1720 }
1721
1722 /*
1723 * Post proceess the options.
1724 */
1725 if (Opts.iOperation == 0)
1726 {
1727 Opts.iOperation = 't';
1728 Opts.pszOperation = "--list";
1729 }
1730
1731 if ( Opts.iOperation == 'x'
1732 && Opts.pszOwner)
1733 return RTMsgErrorExitFailure("The use of --owner with %s has not implemented yet", Opts.pszOperation);
1734
1735 if ( Opts.iOperation == 'x'
1736 && Opts.pszGroup)
1737 return RTMsgErrorExitFailure("The use of --group with %s has not implemented yet", Opts.pszOperation);
1738
1739 /*
1740 * Do the job.
1741 */
1742 switch (Opts.iOperation)
1743 {
1744 case 't':
1745 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdListCallback);
1746
1747 case 'x':
1748 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
1749
1750 case 'c':
1751 return rtZipTarCreate(&Opts);
1752
1753 case 'A':
1754 case 'd':
1755 case 'r':
1756 case 'u':
1757 case RTZIPTARCMD_OPT_DELETE:
1758 return RTMsgErrorExitFailure("The operation %s is not implemented yet", Opts.pszOperation);
1759
1760 default:
1761 return RTMsgErrorExitFailure("Internal error");
1762 }
1763}
1764
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette