VirtualBox

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

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

(C) year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 63.6 KB
Line 
1/* $Id: tarcmd.cpp 69111 2017-10-17 14:26:02Z 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 RTMsgErrorExit(RTEXITCODE_FAILURE, "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 = RTMsgErrorExit(RTEXITCODE_FAILURE, "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 RTMsgErrorExit(RTEXITCODE_FAILURE, "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 RTMsgErrorExit(RTEXITCODE_FAILURE, "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 RTMsgErrorExit(RTEXITCODE_FAILURE, "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 RTMsgErrorExit(RTEXITCODE_FAILURE, "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 = RTMsgErrorExit(RTEXITCODE_FAILURE, "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#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
777
778/**
779 * Queries the user ID to use when extracting a member.
780 *
781 * @returns rcExit or RTEXITCODE_FAILURE.
782 * @param pOpts The tar options.
783 * @param pUser The user info.
784 * @param pszName The file name to use when complaining.
785 * @param rcExit The current exit code.
786 * @param pUid Where to return the user ID.
787 */
788static RTEXITCODE rtZipTarQueryExtractOwner(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pOwner, const char *pszName, RTEXITCODE rcExit,
789 PRTUID pUid)
790{
791 if (pOpts->uidOwner != NIL_RTUID)
792 *pUid = pOpts->uidOwner;
793 else if (pOpts->fPreserveGroup)
794 {
795 if (!pOwner->Attr.u.UnixGroup.szName[0])
796 *pUid = pOwner->Attr.u.UnixOwner.uid;
797 else
798 {
799 *pUid = NIL_RTUID;
800 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: User resolving is not implemented.", pszName);
801 }
802 }
803 else
804 *pUid = NIL_RTUID;
805 return rcExit;
806}
807
808
809/**
810 * Queries the group ID to use when extracting a member.
811 *
812 * @returns rcExit or RTEXITCODE_FAILURE.
813 * @param pOpts The tar options.
814 * @param pGroup The group info.
815 * @param pszName The file name to use when complaining.
816 * @param rcExit The current exit code.
817 * @param pGid Where to return the group ID.
818 */
819static RTEXITCODE rtZipTarQueryExtractGroup(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pGroup, const char *pszName, RTEXITCODE rcExit,
820 PRTGID pGid)
821{
822 if (pOpts->gidGroup != NIL_RTGID)
823 *pGid = pOpts->gidGroup;
824 else if (pOpts->fPreserveGroup)
825 {
826 if (!pGroup->Attr.u.UnixGroup.szName[0])
827 *pGid = pGroup->Attr.u.UnixGroup.gid;
828 else
829 {
830 *pGid = NIL_RTGID;
831 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Group resolving is not implemented.", pszName);
832 }
833 }
834 else
835 *pGid = NIL_RTGID;
836 return rcExit;
837}
838
839#endif /* !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) */
840
841
842/**
843 * Extracts a file.
844 *
845 * Since we can restore permissions and attributes more efficiently by working
846 * directly on the file handle, we have special code path for files.
847 *
848 * @returns rcExit or RTEXITCODE_FAILURE.
849 * @param pOpts The tar options.
850 * @param hVfsObj The tar object to display
851 * @param rcExit The current exit code.
852 * @param pUnixInfo The unix fs object info.
853 * @param pOwner The owner info.
854 * @param pGroup The group info.
855 */
856static RTEXITCODE rtZipTarCmdExtractFile(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, RTEXITCODE rcExit,
857 const char *pszDst, PCRTFSOBJINFO pUnixInfo, PCRTFSOBJINFO pOwner, PCRTFSOBJINFO pGroup)
858{
859 /*
860 * Open the destination file and create a stream object for it.
861 */
862 uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_ACCESS_ATTR_DEFAULT
863 | ((RTFS_UNIX_IWUSR | RTFS_UNIX_IRUSR) << RTFILE_O_CREATE_MODE_SHIFT);
864 RTFILE hFile;
865 int rc = RTFileOpen(&hFile, pszDst, fOpen);
866 if (RT_FAILURE(rc))
867 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating file: %Rrc", pszDst, rc);
868
869 RTVFSIOSTREAM hVfsIosDst;
870 rc = RTVfsIoStrmFromRTFile(hFile, fOpen, true /*fLeaveOpen*/, &hVfsIosDst);
871 if (RT_SUCCESS(rc))
872 {
873 /*
874 * Convert source to a stream and optionally add a read ahead stage.
875 */
876 RTVFSIOSTREAM hVfsIosSrc = RTVfsObjToIoStream(hVfsObj);
877 if (pOpts->fReadAhead)
878 {
879 RTVFSIOSTREAM hVfsReadAhead;
880 rc = RTVfsCreateReadAheadForIoStream(hVfsIosSrc, 0 /*fFlag*/, 16 /*cBuffers*/, _256K /*cbBuffer*/, &hVfsReadAhead);
881 if (RT_SUCCESS(rc))
882 {
883 RTVfsIoStrmRelease(hVfsIosSrc);
884 hVfsIosSrc = hVfsReadAhead;
885 }
886 else
887 AssertRC(rc); /* can be ignored in release builds. */
888 }
889
890 /*
891 * Pump the data thru.
892 */
893 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (uint32_t)RT_MIN(pUnixInfo->cbObject, _1M));
894 if (RT_SUCCESS(rc))
895 {
896 /*
897 * Correct the file mode and other attributes.
898 */
899 if (!pOpts->fNoModTime)
900 {
901 rc = RTFileSetTimes(hFile, NULL, &pUnixInfo->ModificationTime, NULL, NULL);
902 if (RT_FAILURE(rc))
903 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error setting times: %Rrc", pszDst, rc);
904 }
905
906#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
907 if ( pOpts->uidOwner != NIL_RTUID
908 || pOpts->gidGroup != NIL_RTGID
909 || pOpts->fPreserveOwner
910 || pOpts->fPreserveGroup)
911 {
912 RTUID uidFile;
913 rcExit = rtZipTarQueryExtractOwner(pOpts, pOwner, pszDst, rcExit, &uidFile);
914
915 RTGID gidFile;
916 rcExit = rtZipTarQueryExtractGroup(pOpts, pGroup, pszDst, rcExit, &gidFile);
917 if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
918 {
919 rc = RTFileSetOwner(hFile, uidFile, gidFile);
920 if (RT_FAILURE(rc))
921 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", pszDst, rc);
922 }
923 }
924#else
925 RT_NOREF_PV(pOwner); RT_NOREF_PV(pGroup);
926#endif
927
928 RTFMODE fMode = (pUnixInfo->Attr.fMode & pOpts->fFileModeAndMask) | pOpts->fFileModeOrMask;
929 rc = RTFileSetMode(hFile, fMode | RTFS_TYPE_FILE);
930 if (RT_FAILURE(rc))
931 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", pszDst, rc);
932 }
933 else
934 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error writing out file: %Rrc", pszDst, rc);
935 RTVfsIoStrmRelease(hVfsIosSrc);
936 RTVfsIoStrmRelease(hVfsIosDst);
937 }
938 else
939 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating I/O stream for file: %Rrc", pszDst, rc);
940 RTFileClose(hFile);
941 return rcExit;
942}
943
944
945/**
946 * @callback_method_impl{PFNDOWITHMEMBER, Implements --extract.}
947 */
948static RTEXITCODE rtZipTarCmdExtractCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
949{
950 if (pOpts->fVerbose)
951 RTPrintf("%s\n", pszName);
952
953 /*
954 * Query all the information.
955 */
956 RTFSOBJINFO UnixInfo;
957 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
958 if (RT_FAILURE(rc))
959 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
960
961 RTFSOBJINFO Owner;
962 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
963 if (RT_FAILURE(rc))
964 return RTMsgErrorExit(RTEXITCODE_FAILURE,
965 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
966 rc, pszName);
967
968 RTFSOBJINFO Group;
969 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
970 if (RT_FAILURE(rc))
971 return RTMsgErrorExit(RTEXITCODE_FAILURE,
972 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
973 rc, pszName);
974
975 char szTarget[RTPATH_MAX];
976 szTarget[0] = '\0';
977 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
978 if (hVfsSymlink != NIL_RTVFSSYMLINK)
979 {
980 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
981 RTVfsSymlinkRelease(hVfsSymlink);
982 if (RT_FAILURE(rc))
983 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: RTVfsSymlinkRead failed: %Rrc", pszName, rc);
984 if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
985 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Hardlinks are not supported.", pszName);
986 if (!szTarget[0])
987 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Link target is empty.", pszName);
988 }
989 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
990 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
991
992 if (rtZipTarHasEscapeSequence(pszName))
993 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Name '%s' contains an escape sequence.", pszName);
994
995 /*
996 * Construct the path to the extracted member.
997 */
998 char szDst[RTPATH_MAX];
999 rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pszName);
1000 if (RT_FAILURE(rc))
1001 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to construct destination path for: %Rrc", pszName, rc);
1002
1003 /*
1004 * Extract according to the type.
1005 */
1006 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1007 {
1008 case RTFS_TYPE_FILE:
1009 return rtZipTarCmdExtractFile(pOpts, hVfsObj, rcExit, szDst, &UnixInfo, &Owner, &Group);
1010
1011 case RTFS_TYPE_DIRECTORY:
1012 rc = RTDirCreateFullPath(szDst, UnixInfo.Attr.fMode & RTFS_UNIX_ALL_ACCESS_PERMS);
1013 if (RT_FAILURE(rc))
1014 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating directory: %Rrc", szDst, rc);
1015 break;
1016
1017 case RTFS_TYPE_SYMLINK:
1018 rc = RTSymlinkCreate(szDst, szTarget, RTSYMLINKTYPE_UNKNOWN, 0);
1019 if (RT_FAILURE(rc))
1020 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating symbolic link: %Rrc", szDst, rc);
1021 break;
1022
1023 case RTFS_TYPE_FIFO:
1024 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
1025 case RTFS_TYPE_DEV_CHAR:
1026 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
1027 case RTFS_TYPE_DEV_BLOCK:
1028 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Block devices are not supported.", pszName);
1029 case RTFS_TYPE_SOCKET:
1030 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Sockets are not supported.", pszName);
1031 case RTFS_TYPE_WHITEOUT:
1032 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Whiteouts are not support.", pszName);
1033 default:
1034 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Unknown file type.", pszName);
1035 }
1036
1037 /*
1038 * Set other attributes as requested.
1039 *
1040 * Note! File extraction does get here.
1041 */
1042 if (!pOpts->fNoModTime)
1043 {
1044 rc = RTPathSetTimesEx(szDst, NULL, &UnixInfo.ModificationTime, NULL, NULL, RTPATH_F_ON_LINK);
1045 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED && rc != VERR_NS_SYMLINK_SET_TIME)
1046 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing modification time: %Rrc.", pszName, rc);
1047 }
1048
1049#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
1050 if ( pOpts->uidOwner != NIL_RTUID
1051 || pOpts->gidGroup != NIL_RTGID
1052 || pOpts->fPreserveOwner
1053 || pOpts->fPreserveGroup)
1054 {
1055 RTUID uidFile;
1056 rcExit = rtZipTarQueryExtractOwner(pOpts, &Owner, szDst, rcExit, &uidFile);
1057
1058 RTGID gidFile;
1059 rcExit = rtZipTarQueryExtractGroup(pOpts, &Group, szDst, rcExit, &gidFile);
1060 if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
1061 {
1062 rc = RTPathSetOwnerEx(szDst, uidFile, gidFile, RTPATH_F_ON_LINK);
1063 if (RT_FAILURE(rc))
1064 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", szDst, rc);
1065 }
1066 }
1067#endif
1068
1069#if !defined(RT_OS_WINDOWS) /** @todo implement RTPathSetMode on windows... */
1070 if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode)) /* RTPathSetMode follows symbolic links atm. */
1071 {
1072 RTFMODE fMode;
1073 if (RTFS_IS_DIRECTORY(UnixInfo.Attr.fMode))
1074 fMode = (UnixInfo.Attr.fMode & (pOpts->fDirModeAndMask | RTFS_TYPE_MASK)) | pOpts->fDirModeOrMask;
1075 else
1076 fMode = (UnixInfo.Attr.fMode & (pOpts->fFileModeAndMask | RTFS_TYPE_MASK)) | pOpts->fFileModeOrMask;
1077 rc = RTPathSetMode(szDst, fMode);
1078 if (RT_FAILURE(rc))
1079 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", szDst, rc);
1080 }
1081#endif
1082
1083 return rcExit;
1084}
1085
1086
1087/**
1088 * @callback_method_impl{PFNDOWITHMEMBER, Implements --list.}
1089 */
1090static RTEXITCODE rtZipTarCmdListCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
1091{
1092 /*
1093 * This is very simple in non-verbose mode.
1094 */
1095 if (!pOpts->fVerbose)
1096 {
1097 RTPrintf("%s\n", pszName);
1098 return rcExit;
1099 }
1100
1101 /*
1102 * Query all the information.
1103 */
1104 RTFSOBJINFO UnixInfo;
1105 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
1106 if (RT_FAILURE(rc))
1107 {
1108 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
1109 RT_ZERO(UnixInfo);
1110 }
1111
1112 RTFSOBJINFO Owner;
1113 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
1114 if (RT_FAILURE(rc))
1115 {
1116 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
1117 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
1118 rc, pszName);
1119 RT_ZERO(Owner);
1120 }
1121
1122 RTFSOBJINFO Group;
1123 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
1124 if (RT_FAILURE(rc))
1125 {
1126 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
1127 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
1128 rc, pszName);
1129 RT_ZERO(Group);
1130 }
1131
1132 const char *pszLinkType = NULL;
1133 char szTarget[RTPATH_MAX];
1134 szTarget[0] = '\0';
1135 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
1136 if (hVfsSymlink != NIL_RTVFSSYMLINK)
1137 {
1138 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
1139 if (RT_FAILURE(rc))
1140 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsSymlinkRead returned %Rrc on '%s'", rc, pszName);
1141 RTVfsSymlinkRelease(hVfsSymlink);
1142 pszLinkType = RTFS_IS_SYMLINK(UnixInfo.Attr.fMode) ? "->" : "link to";
1143 }
1144 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
1145 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
1146
1147 /*
1148 * Translate the mode mask.
1149 */
1150 char szMode[16];
1151 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1152 {
1153 case RTFS_TYPE_FIFO: szMode[0] = 'f'; break;
1154 case RTFS_TYPE_DEV_CHAR: szMode[0] = 'c'; break;
1155 case RTFS_TYPE_DIRECTORY: szMode[0] = 'd'; break;
1156 case RTFS_TYPE_DEV_BLOCK: szMode[0] = 'b'; break;
1157 case RTFS_TYPE_FILE: szMode[0] = '-'; break;
1158 case RTFS_TYPE_SYMLINK: szMode[0] = 'l'; break;
1159 case RTFS_TYPE_SOCKET: szMode[0] = 's'; break;
1160 case RTFS_TYPE_WHITEOUT: szMode[0] = 'w'; break;
1161 default: szMode[0] = '?'; break;
1162 }
1163 if (pszLinkType && szMode[0] != 's')
1164 szMode[0] = 'h';
1165
1166 szMode[1] = UnixInfo.Attr.fMode & RTFS_UNIX_IRUSR ? 'r' : '-';
1167 szMode[2] = UnixInfo.Attr.fMode & RTFS_UNIX_IWUSR ? 'w' : '-';
1168 szMode[3] = UnixInfo.Attr.fMode & RTFS_UNIX_IXUSR ? 'x' : '-';
1169
1170 szMode[4] = UnixInfo.Attr.fMode & RTFS_UNIX_IRGRP ? 'r' : '-';
1171 szMode[5] = UnixInfo.Attr.fMode & RTFS_UNIX_IWGRP ? 'w' : '-';
1172 szMode[6] = UnixInfo.Attr.fMode & RTFS_UNIX_IXGRP ? 'x' : '-';
1173
1174 szMode[7] = UnixInfo.Attr.fMode & RTFS_UNIX_IROTH ? 'r' : '-';
1175 szMode[8] = UnixInfo.Attr.fMode & RTFS_UNIX_IWOTH ? 'w' : '-';
1176 szMode[9] = UnixInfo.Attr.fMode & RTFS_UNIX_IXOTH ? 'x' : '-';
1177 szMode[10] = '\0';
1178
1179 /** @todo sticky and set-uid/gid bits. */
1180
1181 /*
1182 * Make sure we've got valid owner and group strings.
1183 */
1184 if (!Owner.Attr.u.UnixGroup.szName[0])
1185 RTStrPrintf(Owner.Attr.u.UnixOwner.szName, sizeof(Owner.Attr.u.UnixOwner.szName),
1186 "%u", UnixInfo.Attr.u.Unix.uid);
1187
1188 if (!Group.Attr.u.UnixOwner.szName[0])
1189 RTStrPrintf(Group.Attr.u.UnixGroup.szName, sizeof(Group.Attr.u.UnixGroup.szName),
1190 "%u", UnixInfo.Attr.u.Unix.gid);
1191
1192 /*
1193 * Format the modification time.
1194 */
1195 char szModTime[32];
1196 RTTIME ModTime;
1197 PRTTIME pTime;
1198 if (!pOpts->fDisplayUtc)
1199 pTime = RTTimeLocalExplode(&ModTime, &UnixInfo.ModificationTime);
1200 else
1201 pTime = RTTimeExplode(&ModTime, &UnixInfo.ModificationTime);
1202 if (!pTime)
1203 RT_ZERO(ModTime);
1204 RTStrPrintf(szModTime, sizeof(szModTime), "%04d-%02u-%02u %02u:%02u",
1205 ModTime.i32Year, ModTime.u8Month, ModTime.u8MonthDay, ModTime.u8Hour, ModTime.u8Minute);
1206
1207 /*
1208 * Format the size and figure how much space is needed between the
1209 * user/group and the size.
1210 */
1211 char szSize[64];
1212 size_t cchSize;
1213 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1214 {
1215 case RTFS_TYPE_DEV_CHAR:
1216 case RTFS_TYPE_DEV_BLOCK:
1217 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%u,%u",
1218 RTDEV_MAJOR(UnixInfo.Attr.u.Unix.Device), RTDEV_MINOR(UnixInfo.Attr.u.Unix.Device));
1219 break;
1220 default:
1221 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%RU64", UnixInfo.cbObject);
1222 break;
1223 }
1224
1225 size_t cchUserGroup = strlen(Owner.Attr.u.UnixOwner.szName)
1226 + 1
1227 + strlen(Group.Attr.u.UnixGroup.szName);
1228 ssize_t cchPad = cchUserGroup + cchSize + 1 < 19
1229 ? 19 - (cchUserGroup + cchSize + 1)
1230 : 0;
1231
1232 /*
1233 * Go to press.
1234 */
1235 if (pszLinkType)
1236 RTPrintf("%s %s/%s%*s %s %s %s %s %s\n",
1237 szMode,
1238 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
1239 cchPad, "",
1240 szSize,
1241 szModTime,
1242 pszName,
1243 pszLinkType,
1244 szTarget);
1245 else
1246 RTPrintf("%s %s/%s%*s %s %s %s\n",
1247 szMode,
1248 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
1249 cchPad, "",
1250 szSize,
1251 szModTime,
1252 pszName);
1253
1254 return rcExit;
1255}
1256
1257
1258/**
1259 * Display usage.
1260 *
1261 * @param pszProgName The program name.
1262 */
1263static void rtZipTarUsage(const char *pszProgName)
1264{
1265 /*
1266 * 0 1 2 3 4 5 6 7 8
1267 * 012345678901234567890123456789012345678901234567890123456789012345678901234567890
1268 */
1269 RTPrintf("Usage: %s [options]\n"
1270 "\n",
1271 pszProgName);
1272 RTPrintf("Operations:\n"
1273 " -A, --concatenate, --catenate\n"
1274 " Append the content of one tar archive to another. (not impl)\n"
1275 " -c, --create\n"
1276 " Create a new tar archive. (not impl)\n"
1277 " -d, --diff, --compare\n"
1278 " Compare atar archive with the file system. (not impl)\n"
1279 " -r, --append\n"
1280 " Append more files to the tar archive. (not impl)\n"
1281 " -t, --list\n"
1282 " List the contents of the tar archive.\n"
1283 " -u, --update\n"
1284 " Update the archive, adding files that are newer than the\n"
1285 " ones in the archive. (not impl)\n"
1286 " -x, --extract, --get\n"
1287 " Extract the files from the tar archive.\n"
1288 " --delete\n"
1289 " Delete files from the tar archive.\n"
1290 "\n"
1291 );
1292 RTPrintf("Basic Options:\n"
1293 " -C <dir>, --directory <dir> (-A, -c, -d, -r, -u, -x)\n"
1294 " Sets the base directory for input and output file members.\n"
1295 " This does not apply to --file, even if it preceeds it.\n"
1296 " -f <archive>, --file <archive> (all)\n"
1297 " The tar file to create or process. '-' indicates stdout/stdin,\n"
1298 " which is is the default.\n"
1299 " -v, --verbose (all)\n"
1300 " Verbose operation.\n"
1301 " -p, --preserve-permissions (-x)\n"
1302 " Preserve all permissions when extracting. Must be used\n"
1303 " before the mode mask options as it will change some of these.\n"
1304 " -j, --bzip2 (all)\n"
1305 " Compress/decompress the archive with bzip2.\n"
1306 " -z, --gzip, --gunzip, --ungzip (all)\n"
1307 " Compress/decompress the archive with gzip.\n"
1308 "\n");
1309 RTPrintf("Misc Options:\n"
1310 " --owner <uid/username> (-A, -c, -d, -r, -u, -x)\n"
1311 " Set the owner of extracted and archived files to the user specified.\n"
1312 " --group <uid/username> (-A, -c, -d, -r, -u, -x)\n"
1313 " Set the group of extracted and archived files to the group specified.\n"
1314 " --utc (-t)\n"
1315 " Display timestamps as UTC instead of local time.\n"
1316 " -S, --sparse (-A, -c, -u)\n"
1317 " Detect sparse files and store them (gnu tar extension).\n"
1318 " --format <format> (-A, -c, -u, but also -d, -r, -x)\n"
1319 " The file format:\n"
1320 " auto (gnu tar)\n"
1321 " default (gnu tar)\n"
1322 " tar (gnu tar)"
1323 " gnu (tar v1.13+), "
1324 " ustar (tar POSIX.1-1988), "
1325 " pax (tar POSIX.1-2001),\n"
1326 " xar\n"
1327 " Note! Because XAR/TAR detection isn't implemented yet, it\n"
1328 " is necessary to specifcy --format=xar when reading a\n"
1329 " XAR file. Otherwise this option is only for creation.\n"
1330 "\n");
1331 RTPrintf("IPRT Options:\n"
1332 " --prefix <dir-prefix> (-A, -c, -d, -r, -u)\n"
1333 " Directory prefix to give the members added to the archive.\n"
1334 " --file-mode-and-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1335 " Restrict the access mode of regular and special files.\n"
1336 " --file-mode-or-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1337 " Include the given access mode for regular and special files.\n"
1338 " --dir-mode-and-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1339 " Restrict the access mode of directories.\n"
1340 " --dir-mode-or-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1341 " Include the given access mode for directories.\n"
1342 " --read-ahead (-x)\n"
1343 " Enabled read ahead thread when extracting files.\n"
1344 " --push-file (-A, -c, -u)\n"
1345 " Use RTVfsFsStrmPushFile instead of RTVfsFsStrmAdd.\n"
1346 "\n");
1347 RTPrintf("Standard Options:\n"
1348 " -h, -?, --help\n"
1349 " Display this help text.\n"
1350 " -V, --version\n"
1351 " Display version number.\n");
1352}
1353
1354
1355RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
1356{
1357 /*
1358 * Parse the command line.
1359 *
1360 * N.B. This is less flexible that your regular tar program in that it
1361 * requires the operation to be specified as an option. On the other
1362 * hand, you can specify it where ever you like in the command line.
1363 */
1364 static const RTGETOPTDEF s_aOptions[] =
1365 {
1366 /* operations */
1367 { "--concatenate", 'A', RTGETOPT_REQ_NOTHING },
1368 { "--catenate", 'A', RTGETOPT_REQ_NOTHING },
1369 { "--create", 'c', RTGETOPT_REQ_NOTHING },
1370 { "--diff", 'd', RTGETOPT_REQ_NOTHING },
1371 { "--compare", 'd', RTGETOPT_REQ_NOTHING },
1372 { "--append", 'r', RTGETOPT_REQ_NOTHING },
1373 { "--list", 't', RTGETOPT_REQ_NOTHING },
1374 { "--update", 'u', RTGETOPT_REQ_NOTHING },
1375 { "--extract", 'x', RTGETOPT_REQ_NOTHING },
1376 { "--get", 'x', RTGETOPT_REQ_NOTHING },
1377 { "--delete", RTZIPTARCMD_OPT_DELETE, RTGETOPT_REQ_NOTHING },
1378
1379 /* basic options */
1380 { "--directory", 'C', RTGETOPT_REQ_STRING },
1381 { "--file", 'f', RTGETOPT_REQ_STRING },
1382 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1383 { "--preserve-permissions", 'p', RTGETOPT_REQ_NOTHING },
1384 { "--bzip2", 'j', RTGETOPT_REQ_NOTHING },
1385 { "--gzip", 'z', RTGETOPT_REQ_NOTHING },
1386 { "--gunzip", 'z', RTGETOPT_REQ_NOTHING },
1387 { "--ungzip", 'z', RTGETOPT_REQ_NOTHING },
1388
1389 /* other options. */
1390 { "--owner", RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
1391 { "--group", RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
1392 { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
1393 { "--sparse", 'S', RTGETOPT_REQ_NOTHING },
1394 { "--format", RTZIPTARCMD_OPT_FORMAT, RTGETOPT_REQ_STRING },
1395
1396 /* IPRT extensions */
1397 { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
1398 { "--file-mode-and-mask", RTZIPTARCMD_OPT_FILE_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1399 { "--file-mode-or-mask", RTZIPTARCMD_OPT_FILE_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1400 { "--dir-mode-and-mask", RTZIPTARCMD_OPT_DIR_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1401 { "--dir-mode-or-mask", RTZIPTARCMD_OPT_DIR_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1402 { "--read-ahead", RTZIPTARCMD_OPT_READ_AHEAD, RTGETOPT_REQ_NOTHING },
1403 { "--use-push-file", RTZIPTARCMD_OPT_USE_PUSH_FILE, RTGETOPT_REQ_NOTHING },
1404 };
1405
1406 RTGETOPTSTATE GetState;
1407 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1408 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1409 if (RT_FAILURE(rc))
1410 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
1411
1412 RTZIPTARCMDOPS Opts;
1413 RT_ZERO(Opts);
1414 Opts.enmFormat = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
1415 Opts.uidOwner = NIL_RTUID;
1416 Opts.gidGroup = NIL_RTUID;
1417 Opts.fFileModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
1418 Opts.fDirModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
1419#if 0
1420 if (RTPermIsSuperUser())
1421 {
1422 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1423 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1424 Opts.fPreserveOwner = true;
1425 Opts.fPreserveGroup = true;
1426 }
1427#endif
1428 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1429
1430 RTGETOPTUNION ValueUnion;
1431 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
1432 && rc != VINF_GETOPT_NOT_OPTION)
1433 {
1434 switch (rc)
1435 {
1436 /* operations */
1437 case 'A':
1438 case 'c':
1439 case 'd':
1440 case 'r':
1441 case 't':
1442 case 'u':
1443 case 'x':
1444 case RTZIPTARCMD_OPT_DELETE:
1445 if (Opts.iOperation)
1446 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Conflicting tar operation (%s already set, now %s)",
1447 Opts.pszOperation, ValueUnion.pDef->pszLong);
1448 Opts.iOperation = rc;
1449 Opts.pszOperation = ValueUnion.pDef->pszLong;
1450 break;
1451
1452 /* basic options */
1453 case 'C':
1454 if (Opts.pszDirectory)
1455 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -C/--directory once");
1456 Opts.pszDirectory = ValueUnion.psz;
1457 break;
1458
1459 case 'f':
1460 if (Opts.pszFile)
1461 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -f/--file once");
1462 Opts.pszFile = ValueUnion.psz;
1463 break;
1464
1465 case 'v':
1466 Opts.fVerbose = true;
1467 break;
1468
1469 case 'p':
1470 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1471 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1472 Opts.fPreserveOwner = true;
1473 Opts.fPreserveGroup = true;
1474 break;
1475
1476 case 'j':
1477 case 'z':
1478 if (Opts.chZipper)
1479 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify one compressor / decompressor");
1480 Opts.chZipper = rc;
1481 break;
1482
1483 case RTZIPTARCMD_OPT_OWNER:
1484 if (Opts.pszOwner)
1485 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --owner once");
1486 Opts.pszOwner = ValueUnion.psz;
1487
1488 rc = RTStrToUInt32Full(Opts.pszOwner, 0, &ValueUnion.u32);
1489 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1490 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1491 "Error convering --owner '%s' into a number: %Rrc", Opts.pszOwner, rc);
1492 if (RT_SUCCESS(rc))
1493 {
1494 Opts.uidOwner = ValueUnion.u32;
1495 Opts.pszOwner = NULL;
1496 }
1497 break;
1498
1499 case RTZIPTARCMD_OPT_GROUP:
1500 if (Opts.pszGroup)
1501 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --group once");
1502 Opts.pszGroup = ValueUnion.psz;
1503
1504 rc = RTStrToUInt32Full(Opts.pszGroup, 0, &ValueUnion.u32);
1505 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1506 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1507 "Error convering --group '%s' into a number: %Rrc", Opts.pszGroup, rc);
1508 if (RT_SUCCESS(rc))
1509 {
1510 Opts.gidGroup = ValueUnion.u32;
1511 Opts.pszGroup = NULL;
1512 }
1513 break;
1514
1515 case RTZIPTARCMD_OPT_UTC:
1516 Opts.fDisplayUtc = true;
1517 break;
1518
1519 /* GNU */
1520 case 'S':
1521 Opts.fTarCreate |= RTZIPTAR_C_SPARSE;
1522 break;
1523
1524 /* iprt extensions */
1525 case RTZIPTARCMD_OPT_PREFIX:
1526 if (Opts.pszPrefix)
1527 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --prefix once");
1528 Opts.pszPrefix = ValueUnion.psz;
1529 break;
1530
1531 case RTZIPTARCMD_OPT_FILE_MODE_AND_MASK:
1532 Opts.fFileModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1533 break;
1534
1535 case RTZIPTARCMD_OPT_FILE_MODE_OR_MASK:
1536 Opts.fFileModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1537 break;
1538
1539 case RTZIPTARCMD_OPT_DIR_MODE_AND_MASK:
1540 Opts.fDirModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1541 break;
1542
1543 case RTZIPTARCMD_OPT_DIR_MODE_OR_MASK:
1544 Opts.fDirModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1545 break;
1546
1547 case RTZIPTARCMD_OPT_FORMAT:
1548 if (!strcmp(ValueUnion.psz, "auto") || !strcmp(ValueUnion.psz, "default"))
1549 {
1550 Opts.enmFormat = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
1551 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1552 }
1553 else if (!strcmp(ValueUnion.psz, "tar"))
1554 {
1555 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1556 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1557 }
1558 else if (!strcmp(ValueUnion.psz, "gnu"))
1559 {
1560 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1561 Opts.enmTarFormat = RTZIPTARFORMAT_GNU;
1562 }
1563 else if (!strcmp(ValueUnion.psz, "ustar"))
1564 {
1565 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1566 Opts.enmTarFormat = RTZIPTARFORMAT_USTAR;
1567 }
1568 else if ( !strcmp(ValueUnion.psz, "posix")
1569 || !strcmp(ValueUnion.psz, "pax"))
1570 {
1571 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1572 Opts.enmTarFormat = RTZIPTARFORMAT_PAX;
1573 }
1574 else if (!strcmp(ValueUnion.psz, "xar"))
1575 Opts.enmFormat = RTZIPTARCMDFORMAT_XAR;
1576 else
1577 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
1578 break;
1579
1580 case RTZIPTARCMD_OPT_READ_AHEAD:
1581 Opts.fReadAhead = true;
1582 break;
1583
1584 case RTZIPTARCMD_OPT_USE_PUSH_FILE:
1585 Opts.fUsePushFile = true;
1586 break;
1587
1588 /* Standard bits. */
1589 case 'h':
1590 rtZipTarUsage(RTPathFilename(papszArgs[0]));
1591 return RTEXITCODE_SUCCESS;
1592
1593 case 'V':
1594 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1595 return RTEXITCODE_SUCCESS;
1596
1597 default:
1598 return RTGetOptPrintError(rc, &ValueUnion);
1599 }
1600 }
1601
1602 if (rc == VINF_GETOPT_NOT_OPTION)
1603 {
1604 /* this is kind of ugly. */
1605 Assert((unsigned)GetState.iNext - 1 <= cArgs);
1606 Opts.papszFiles = (const char * const *)&papszArgs[GetState.iNext - 1];
1607 Opts.cFiles = cArgs - GetState.iNext + 1;
1608 }
1609
1610 /*
1611 * Post proceess the options.
1612 */
1613 if (Opts.iOperation == 0)
1614 {
1615 Opts.iOperation = 't';
1616 Opts.pszOperation = "--list";
1617 }
1618
1619 if ( Opts.iOperation == 'x'
1620 && Opts.pszOwner)
1621 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --owner with %s has not implemented yet", Opts.pszOperation);
1622
1623 if ( Opts.iOperation == 'x'
1624 && Opts.pszGroup)
1625 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --group with %s has not implemented yet", Opts.pszOperation);
1626
1627 /*
1628 * Do the job.
1629 */
1630 switch (Opts.iOperation)
1631 {
1632 case 't':
1633 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdListCallback);
1634
1635 case 'x':
1636 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
1637
1638 case 'c':
1639 return rtZipTarCreate(&Opts);
1640
1641 case 'A':
1642 case 'd':
1643 case 'r':
1644 case 'u':
1645 case RTZIPTARCMD_OPT_DELETE:
1646 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The operation %s is not implemented yet", Opts.pszOperation);
1647
1648 default:
1649 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Internal error");
1650 }
1651}
1652
Note: See TracBrowser for help on using the repository browser.

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