VirtualBox

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

最後變更 在這個檔案從67255是 67255,由 vboxsync 提交於 8 年 前

RTZipTarCmd: pass --owner, --group, and file and dir mode masks to the TAR creator.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 63.6 KB
 
1/* $Id: tarcmd.cpp 67255 2017-06-02 18:05:34Z vboxsync $ */
2/** @file
3 * IPRT - A mini TAR Command.
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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
293 /*
294 * Open the output file.
295 */
296 RTVFSIOSTREAM hVfsIos;
297 if ( pOpts->pszFile
298 && strcmp(pOpts->pszFile, "-") != 0)
299 {
300 uint32_t offError = 0;
301 RTERRINFOSTATIC ErrInfo;
302 rc = RTVfsChainOpenIoStream(pOpts->pszFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
303 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
304 if (RT_FAILURE(rc))
305 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pOpts->pszFile, rc, offError, &ErrInfo.Core);
306 }
307 else
308 {
309 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT,
310 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
311 true /*fLeaveOpen*/,
312 &hVfsIos);
313 if (RT_FAILURE(rc))
314 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard output for writing: %Rrc", rc);
315 }
316
317 /*
318 * Pass it thru a compressor?
319 */
320 RTVFSIOSTREAM hVfsIosComp = NIL_RTVFSIOSTREAM;
321 switch (pOpts->chZipper)
322 {
323 /* no */
324 case '\0':
325 rc = VINF_SUCCESS;
326 break;
327
328 /* gunzip */
329 case 'z':
330 rc = RTZipGzipCompressIoStream(hVfsIos, 0 /*fFlags*/, 6, &hVfsIosComp);
331 if (RT_FAILURE(rc))
332 RTMsgError("Failed to open gzip decompressor: %Rrc", rc);
333 break;
334
335 /* bunzip2 */
336 case 'j':
337 rc = VERR_NOT_SUPPORTED;
338 RTMsgError("bzip2 is not supported by this build");
339 break;
340
341 /* bug */
342 default:
343 rc = VERR_INTERNAL_ERROR_2;
344 RTMsgError("unknown decompression method '%c'", pOpts->chZipper);
345 break;
346 }
347 if (RT_FAILURE(rc))
348 {
349 RTVfsIoStrmRelease(hVfsIos);
350 return RTEXITCODE_FAILURE;
351 }
352
353 if (hVfsIosComp != NIL_RTVFSIOSTREAM)
354 {
355 RTVfsIoStrmRelease(hVfsIos);
356 hVfsIos = hVfsIosComp;
357 hVfsIosComp = NIL_RTVFSIOSTREAM;
358 }
359
360 /*
361 * Open the filesystem stream creator.
362 */
363 if ( pOpts->enmFormat == RTZIPTARCMDFORMAT_TAR
364 || pOpts->enmFormat == RTZIPTARCMDFORMAT_AUTO_DEFAULT)
365 {
366 RTVFSFSSTREAM hVfsFss;
367 rc = RTZipTarFsStreamToIoStream(hVfsIos, pOpts->enmTarFormat, pOpts->fTarCreate, &hVfsFss);
368 if (RT_SUCCESS(rc))
369 {
370 /*
371 * Set transformation options.
372 */
373 rc = RTZipTarFsStreamSetFileMode(hVfsFss, pOpts->fFileModeAndMask, pOpts->fFileModeOrMask);
374 if (RT_SUCCESS(rc))
375 {
376 rc = RTZipTarFsStreamSetDirMode(hVfsFss, pOpts->fDirModeAndMask, pOpts->fDirModeOrMask);
377 if (RT_FAILURE(rc))
378 RTMsgError("RTZipTarFsStreamSetDirMode(%o,%o) failed: %Rrc", pOpts->fDirModeAndMask, pOpts->fDirModeOrMask, rc);
379 }
380 else
381 RTMsgError("RTZipTarFsStreamSetFileMode(%o,%o) failed: %Rrc", pOpts->fFileModeAndMask, pOpts->fFileModeOrMask, rc);
382 if ((pOpts->pszOwner || pOpts->uidOwner != NIL_RTUID) && RT_SUCCESS(rc))
383 {
384 rc = RTZipTarFsStreamSetOwner(hVfsFss, pOpts->uidOwner, pOpts->pszOwner);
385 if (RT_FAILURE(rc))
386 RTMsgError("RTZipTarFsStreamSetOwner(%d,%s) failed: %Rrc", pOpts->uidOwner, pOpts->pszOwner, rc);
387 }
388 if ((pOpts->pszGroup || pOpts->gidGroup != NIL_RTGID) && RT_SUCCESS(rc))
389 {
390 rc = RTZipTarFsStreamSetGroup(hVfsFss, pOpts->gidGroup, pOpts->pszGroup);
391 if (RT_FAILURE(rc))
392 RTMsgError("RTZipTarFsStreamSetGroup(%d,%s) failed: %Rrc", pOpts->gidGroup, pOpts->pszGroup, rc);
393 }
394 if (RT_SUCCESS(rc))
395 *phVfsFss = hVfsFss;
396 else
397 {
398 RTVfsFsStrmRelease(hVfsFss);
399 *phVfsFss = NIL_RTVFSFSSTREAM;
400 }
401 }
402 else
403 rc = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
404 }
405 else
406 rc = VERR_NOT_SUPPORTED;
407 RTVfsIoStrmRelease(hVfsIos);
408 if (RT_FAILURE(rc))
409 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
410
411 return RTEXITCODE_SUCCESS;
412}
413
414
415/**
416 * Implements archive creation.
417 *
418 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
419 * @param pOpts The options.
420 */
421static RTEXITCODE rtZipTarCreate(PRTZIPTARCMDOPS pOpts)
422{
423 /*
424 * Refuse to create empty archive.
425 */
426 if (pOpts->cFiles == 0)
427 return RTMsgErrorExitFailure("Nothing to archive - refusing to create empty archive!");
428
429 /*
430 * First open the output file.
431 */
432 RTVFSFSSTREAM hVfsFss;
433 RTEXITCODE rcExit = rtZipTarCmdOpenOutputArchive(pOpts, &hVfsFss);
434 if (rcExit != RTEXITCODE_SUCCESS)
435 return rcExit;
436
437 /*
438 * Process the input files.
439 */
440 for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
441 {
442 const char *pszFile = pOpts->papszFiles[iFile];
443
444 /*
445 * Construct/copy the source name.
446 */
447 int rc = VINF_SUCCESS;
448 char szSrc[RTPATH_MAX];
449 if ( RTPathStartsWithRoot(pszFile)
450 || RTVfsChainIsSpec(pszFile))
451 rc = RTStrCopy(szSrc, sizeof(szSrc), pszFile);
452 else
453 rc = RTPathJoin(szSrc, sizeof(szSrc), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pOpts->papszFiles[iFile]);
454 if (RT_SUCCESS(rc))
455 {
456 /*
457 * Construct the archived name. We must strip leading root specifier.
458 */
459 char *pszFinalPath = NULL;
460 char szDst[RTPATH_MAX];
461 const char *pszDst = pszFile;
462 if (RTVfsChainIsSpec(pszFile))
463 {
464 uint32_t offError;
465 rc = RTVfsChainQueryFinalPath(pszFile, &pszFinalPath, &offError);
466 if (RT_SUCCESS(rc))
467 pszDst = pszFinalPath;
468 else
469 rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryFinalPath", pszFile, rc, offError, NULL);
470 }
471 if (RT_SUCCESS(rc))
472 {
473 pszDst = RTPathSkipRootSpec(pszDst);
474 if (*pszDst == '\0')
475 {
476 pszDst = pOpts->pszPrefix ? pOpts->pszPrefix : ".";
477 rc = RTStrCopy(szDst, sizeof(szDst), pszDst);
478 }
479 else
480 {
481 if (pOpts->pszPrefix)
482 rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszPrefix, pszDst);
483 else
484 rc = RTStrCopy(szDst, sizeof(szDst), pszDst);
485 }
486 if (RT_SUCCESS(rc))
487 {
488 /*
489 * What kind of object is this and what affiliations does it have?
490 */
491 RTERRINFOSTATIC ErrInfo;
492 uint32_t offError;
493 RTFSOBJINFO aObjInfo[3];
494 rc = RTVfsChainQueryInfo(szSrc, &aObjInfo[0], RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK,
495 &offError, RTErrInfoInitStatic(&ErrInfo));
496 if (RT_SUCCESS(rc))
497 {
498
499 rc = RTVfsChainQueryInfo(szSrc, &aObjInfo[1], RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK,
500 &offError, RTErrInfoInitStatic(&ErrInfo));
501 if (RT_SUCCESS(rc))
502 {
503 rc = RTVfsChainQueryInfo(szSrc, &aObjInfo[2], RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK,
504 &offError, RTErrInfoInitStatic(&ErrInfo));
505 if (RT_FAILURE(rc))
506 RT_ZERO(aObjInfo[2]);
507 }
508 else
509 {
510 RT_ZERO(aObjInfo[1]);
511 RT_ZERO(aObjInfo[2]);
512 }
513
514 /*
515 * Process on an object type basis.
516 */
517 RTEXITCODE rcExit2;
518 if (RTFS_IS_DIRECTORY(aObjInfo[0].Attr.fMode))
519 rcExit2 = rtZipTarCmdArchiveDir(pOpts, hVfsFss, szSrc, strlen(szSrc), aObjInfo,
520 szDst, strlen(szDst), &ErrInfo);
521 else if (RTFS_IS_FILE(aObjInfo[0].Attr.fMode))
522 rcExit2 = rtZipTarCmdArchiveFile(pOpts, hVfsFss, szSrc, aObjInfo, szDst, &ErrInfo);
523 else if (RTFS_IS_SYMLINK(aObjInfo[0].Attr.fMode))
524 rcExit2 = RTMsgErrorExitFailure("Symlink archiving is not implemented");
525 else if (RTFS_IS_FIFO(aObjInfo[0].Attr.fMode))
526 rcExit2 = RTMsgErrorExitFailure("FIFO archiving is not implemented");
527 else if (RTFS_IS_SOCKET(aObjInfo[0].Attr.fMode))
528 rcExit2 = RTMsgErrorExitFailure("Socket archiving is not implemented");
529 else if (RTFS_IS_DEV_CHAR(aObjInfo[0].Attr.fMode) || RTFS_IS_DEV_BLOCK(aObjInfo[0].Attr.fMode))
530 rcExit2 = RTMsgErrorExitFailure("Device archiving is not implemented");
531 else if (RTFS_IS_WHITEOUT(aObjInfo[0].Attr.fMode))
532 rcExit2 = RTEXITCODE_SUCCESS;
533 else
534 rcExit2 = RTMsgErrorExitFailure("Unknown file type: %#x\n", aObjInfo[0].Attr.fMode);
535 if (rcExit2 != RTEXITCODE_SUCCESS)
536 rcExit = rcExit2;
537 }
538 else
539 rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszFile, rc, offError, &ErrInfo.Core);
540 }
541 else
542 rcExit = RTMsgErrorExitFailure("archived file name is too long, skipping '%s' (%s)", pszDst, pszFile);
543 }
544 }
545 else
546 rcExit = RTMsgErrorExitFailure("input file name is too long, skipping '%s'", pszFile);
547 }
548
549 /*
550 * Finalize the archive.
551 */
552 int rc = RTVfsFsStrmEnd(hVfsFss);
553 if (RT_FAILURE(rc))
554 rcExit = RTMsgErrorExitFailure("RTVfsFsStrmEnd failed: %Rrc", rc);
555
556 RTVfsFsStrmRelease(hVfsFss);
557 return rcExit;
558}
559
560
561/**
562 * Opens the input archive specified by the options.
563 *
564 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
565 * @param pOpts The options.
566 * @param phVfsFss Where to return the TAR filesystem stream handle.
567 */
568static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTREAM phVfsFss)
569{
570 int rc;
571
572 /*
573 * Open the input file.
574 */
575 RTVFSIOSTREAM hVfsIos;
576 if ( pOpts->pszFile
577 && strcmp(pOpts->pszFile, "-") != 0)
578 {
579 uint32_t offError = 0;
580 RTERRINFOSTATIC ErrInfo;
581 rc = RTVfsChainOpenIoStream(pOpts->pszFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
582 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
583 if (RT_FAILURE(rc))
584 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pOpts->pszFile, rc, offError, &ErrInfo.Core);
585 }
586 else
587 {
588 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT,
589 RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
590 true /*fLeaveOpen*/,
591 &hVfsIos);
592 if (RT_FAILURE(rc))
593 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard in for reading: %Rrc", rc);
594 }
595
596 /*
597 * Pass it thru a decompressor?
598 */
599 RTVFSIOSTREAM hVfsIosDecomp = NIL_RTVFSIOSTREAM;
600 switch (pOpts->chZipper)
601 {
602 /* no */
603 case '\0':
604 rc = VINF_SUCCESS;
605 break;
606
607 /* gunzip */
608 case 'z':
609 rc = RTZipGzipDecompressIoStream(hVfsIos, 0 /*fFlags*/, &hVfsIosDecomp);
610 if (RT_FAILURE(rc))
611 RTMsgError("Failed to open gzip decompressor: %Rrc", rc);
612 break;
613
614 /* bunzip2 */
615 case 'j':
616 rc = VERR_NOT_SUPPORTED;
617 RTMsgError("bzip2 is not supported by this build");
618 break;
619
620 /* bug */
621 default:
622 rc = VERR_INTERNAL_ERROR_2;
623 RTMsgError("unknown decompression method '%c'", pOpts->chZipper);
624 break;
625 }
626 if (RT_FAILURE(rc))
627 {
628 RTVfsIoStrmRelease(hVfsIos);
629 return RTEXITCODE_FAILURE;
630 }
631
632 if (hVfsIosDecomp != NIL_RTVFSIOSTREAM)
633 {
634 RTVfsIoStrmRelease(hVfsIos);
635 hVfsIos = hVfsIosDecomp;
636 hVfsIosDecomp = NIL_RTVFSIOSTREAM;
637 }
638
639 /*
640 * Open the filesystem stream.
641 */
642 if (pOpts->enmFormat == RTZIPTARCMDFORMAT_TAR)
643 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
644 else if (pOpts->enmFormat == RTZIPTARCMDFORMAT_XAR)
645#ifdef IPRT_WITH_XAR /* Requires C++ and XML, so only in some configruation of IPRT. */
646 rc = RTZipXarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
647#else
648 rc = VERR_NOT_SUPPORTED;
649#endif
650 else /** @todo make RTZipTarFsStreamFromIoStream fail if not tar file! */
651 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
652 RTVfsIoStrmRelease(hVfsIos);
653 if (RT_FAILURE(rc))
654 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
655
656 return RTEXITCODE_SUCCESS;
657}
658
659
660/**
661 * Worker for the --list and --extract commands.
662 *
663 * @returns The appropriate exit code.
664 * @param pOpts The tar options.
665 * @param pfnCallback The command specific callback.
666 */
667static RTEXITCODE rtZipTarDoWithMembers(PRTZIPTARCMDOPS pOpts, PFNDOWITHMEMBER pfnCallback)
668{
669 /*
670 * Allocate a bitmap to go with the file list. This will be used to
671 * indicate which files we've processed and which not.
672 */
673 uint32_t *pbmFound = NULL;
674 if (pOpts->cFiles)
675 {
676 pbmFound = (uint32_t *)RTMemAllocZ(((pOpts->cFiles + 31) / 32) * sizeof(uint32_t));
677 if (!pbmFound)
678 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate the found-file-bitmap");
679 }
680
681
682 /*
683 * Open the input archive.
684 */
685 RTVFSFSSTREAM hVfsFssIn;
686 RTEXITCODE rcExit = rtZipTarCmdOpenInputArchive(pOpts, &hVfsFssIn);
687 if (rcExit == RTEXITCODE_SUCCESS)
688 {
689 /*
690 * Process the stream.
691 */
692 for (;;)
693 {
694 /*
695 * Retrive the next object.
696 */
697 char *pszName;
698 RTVFSOBJ hVfsObj;
699 int rc = RTVfsFsStrmNext(hVfsFssIn, &pszName, NULL, &hVfsObj);
700 if (RT_FAILURE(rc))
701 {
702 if (rc != VERR_EOF)
703 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext returned %Rrc", rc);
704 break;
705 }
706
707 /*
708 * Should we process this entry?
709 */
710 uint32_t iFile = UINT32_MAX;
711 if ( !pOpts->cFiles
712 || rtZipTarCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile) )
713 {
714 if (pbmFound)
715 ASMBitSet(pbmFound, iFile);
716
717 rcExit = pfnCallback(pOpts, hVfsObj, pszName, rcExit);
718 }
719
720 /*
721 * Release the current object and string.
722 */
723 RTVfsObjRelease(hVfsObj);
724 RTStrFree(pszName);
725 }
726
727 /*
728 * Complain about any files we didn't find.
729 */
730 for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
731 if (!ASMBitTest(pbmFound, iFile))
732 {
733 RTMsgError("%s: Was not found in the archive", pOpts->papszFiles[iFile]);
734 rcExit = RTEXITCODE_FAILURE;
735 }
736
737 RTVfsFsStrmRelease(hVfsFssIn);
738 }
739 RTMemFree(pbmFound);
740 return rcExit;
741}
742
743
744/**
745 * Checks if the name contains any escape sequences.
746 *
747 * An escape sequence would generally be one or more '..' references. On DOS
748 * like system, something that would make up a drive letter reference is also
749 * considered an escape sequence.
750 *
751 * @returns true / false.
752 * @param pszName The name to consider.
753 */
754static bool rtZipTarHasEscapeSequence(const char *pszName)
755{
756#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
757 if (pszName[0] == ':')
758 return true;
759#endif
760 while (*pszName)
761 {
762 while (RTPATH_IS_SEP(*pszName))
763 pszName++;
764 if ( pszName[0] == '.'
765 && pszName[1] == '.'
766 && (pszName[2] == '\0' || RTPATH_IS_SLASH(pszName[2])) )
767 return true;
768 while (*pszName && !RTPATH_IS_SEP(*pszName))
769 pszName++;
770 }
771
772 return false;
773}
774
775#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
776
777/**
778 * Queries the user ID to use when extracting a member.
779 *
780 * @returns rcExit or RTEXITCODE_FAILURE.
781 * @param pOpts The tar options.
782 * @param pUser The user info.
783 * @param pszName The file name to use when complaining.
784 * @param rcExit The current exit code.
785 * @param pUid Where to return the user ID.
786 */
787static RTEXITCODE rtZipTarQueryExtractOwner(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pOwner, const char *pszName, RTEXITCODE rcExit,
788 PRTUID pUid)
789{
790 if (pOpts->uidOwner != NIL_RTUID)
791 *pUid = pOpts->uidOwner;
792 else if (pOpts->fPreserveGroup)
793 {
794 if (!pOwner->Attr.u.UnixGroup.szName[0])
795 *pUid = pOwner->Attr.u.UnixOwner.uid;
796 else
797 {
798 *pUid = NIL_RTUID;
799 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: User resolving is not implemented.", pszName);
800 }
801 }
802 else
803 *pUid = NIL_RTUID;
804 return rcExit;
805}
806
807
808/**
809 * Queries the group ID to use when extracting a member.
810 *
811 * @returns rcExit or RTEXITCODE_FAILURE.
812 * @param pOpts The tar options.
813 * @param pGroup The group info.
814 * @param pszName The file name to use when complaining.
815 * @param rcExit The current exit code.
816 * @param pGid Where to return the group ID.
817 */
818static RTEXITCODE rtZipTarQueryExtractGroup(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pGroup, const char *pszName, RTEXITCODE rcExit,
819 PRTGID pGid)
820{
821 if (pOpts->gidGroup != NIL_RTGID)
822 *pGid = pOpts->gidGroup;
823 else if (pOpts->fPreserveGroup)
824 {
825 if (!pGroup->Attr.u.UnixGroup.szName[0])
826 *pGid = pGroup->Attr.u.UnixGroup.gid;
827 else
828 {
829 *pGid = NIL_RTGID;
830 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Group resolving is not implemented.", pszName);
831 }
832 }
833 else
834 *pGid = NIL_RTGID;
835 return rcExit;
836}
837
838#endif /* !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) */
839
840
841/**
842 * Extracts a file.
843 *
844 * Since we can restore permissions and attributes more efficiently by working
845 * directly on the file handle, we have special code path for files.
846 *
847 * @returns rcExit or RTEXITCODE_FAILURE.
848 * @param pOpts The tar options.
849 * @param hVfsObj The tar object to display
850 * @param rcExit The current exit code.
851 * @param pUnixInfo The unix fs object info.
852 * @param pOwner The owner info.
853 * @param pGroup The group info.
854 */
855static RTEXITCODE rtZipTarCmdExtractFile(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, RTEXITCODE rcExit,
856 const char *pszDst, PCRTFSOBJINFO pUnixInfo, PCRTFSOBJINFO pOwner, PCRTFSOBJINFO pGroup)
857{
858 /*
859 * Open the destination file and create a stream object for it.
860 */
861 uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_ACCESS_ATTR_DEFAULT
862 | ((RTFS_UNIX_IWUSR | RTFS_UNIX_IRUSR) << RTFILE_O_CREATE_MODE_SHIFT);
863 RTFILE hFile;
864 int rc = RTFileOpen(&hFile, pszDst, fOpen);
865 if (RT_FAILURE(rc))
866 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating file: %Rrc", pszDst, rc);
867
868 RTVFSIOSTREAM hVfsIosDst;
869 rc = RTVfsIoStrmFromRTFile(hFile, fOpen, true /*fLeaveOpen*/, &hVfsIosDst);
870 if (RT_SUCCESS(rc))
871 {
872 /*
873 * Convert source to a stream and optionally add a read ahead stage.
874 */
875 RTVFSIOSTREAM hVfsIosSrc = RTVfsObjToIoStream(hVfsObj);
876 if (pOpts->fReadAhead)
877 {
878 RTVFSIOSTREAM hVfsReadAhead;
879 rc = RTVfsCreateReadAheadForIoStream(hVfsIosSrc, 0 /*fFlag*/, 16 /*cBuffers*/, _256K /*cbBuffer*/, &hVfsReadAhead);
880 if (RT_SUCCESS(rc))
881 {
882 RTVfsIoStrmRelease(hVfsIosSrc);
883 hVfsIosSrc = hVfsReadAhead;
884 }
885 else
886 AssertRC(rc); /* can be ignored in release builds. */
887 }
888
889 /*
890 * Pump the data thru.
891 */
892 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (uint32_t)RT_MIN(pUnixInfo->cbObject, _1M));
893 if (RT_SUCCESS(rc))
894 {
895 /*
896 * Correct the file mode and other attributes.
897 */
898 if (!pOpts->fNoModTime)
899 {
900 rc = RTFileSetTimes(hFile, NULL, &pUnixInfo->ModificationTime, NULL, NULL);
901 if (RT_FAILURE(rc))
902 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error setting times: %Rrc", pszDst, rc);
903 }
904
905#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
906 if ( pOpts->uidOwner != NIL_RTUID
907 || pOpts->gidGroup != NIL_RTGID
908 || pOpts->fPreserveOwner
909 || pOpts->fPreserveGroup)
910 {
911 RTUID uidFile;
912 rcExit = rtZipTarQueryExtractOwner(pOpts, pOwner, pszDst, rcExit, &uidFile);
913
914 RTGID gidFile;
915 rcExit = rtZipTarQueryExtractGroup(pOpts, pGroup, pszDst, rcExit, &gidFile);
916 if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
917 {
918 rc = RTFileSetOwner(hFile, uidFile, gidFile);
919 if (RT_FAILURE(rc))
920 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", pszDst, rc);
921 }
922 }
923#else
924 RT_NOREF_PV(pOwner); RT_NOREF_PV(pGroup);
925#endif
926
927 RTFMODE fMode = (pUnixInfo->Attr.fMode & pOpts->fFileModeAndMask) | pOpts->fFileModeOrMask;
928 rc = RTFileSetMode(hFile, fMode | RTFS_TYPE_FILE);
929 if (RT_FAILURE(rc))
930 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", pszDst, rc);
931 }
932 else
933 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error writing out file: %Rrc", pszDst, rc);
934 RTVfsIoStrmRelease(hVfsIosSrc);
935 RTVfsIoStrmRelease(hVfsIosDst);
936 }
937 else
938 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating I/O stream for file: %Rrc", pszDst, rc);
939 RTFileClose(hFile);
940 return rcExit;
941}
942
943
944/**
945 * @callback_method_impl{PFNDOWITHMEMBER, Implements --extract.}
946 */
947static RTEXITCODE rtZipTarCmdExtractCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
948{
949 if (pOpts->fVerbose)
950 RTPrintf("%s\n", pszName);
951
952 /*
953 * Query all the information.
954 */
955 RTFSOBJINFO UnixInfo;
956 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
957 if (RT_FAILURE(rc))
958 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
959
960 RTFSOBJINFO Owner;
961 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
962 if (RT_FAILURE(rc))
963 return RTMsgErrorExit(RTEXITCODE_FAILURE,
964 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
965 rc, pszName);
966
967 RTFSOBJINFO Group;
968 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
969 if (RT_FAILURE(rc))
970 return RTMsgErrorExit(RTEXITCODE_FAILURE,
971 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
972 rc, pszName);
973
974 char szTarget[RTPATH_MAX];
975 szTarget[0] = '\0';
976 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
977 if (hVfsSymlink != NIL_RTVFSSYMLINK)
978 {
979 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
980 RTVfsSymlinkRelease(hVfsSymlink);
981 if (RT_FAILURE(rc))
982 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: RTVfsSymlinkRead failed: %Rrc", pszName, rc);
983 if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
984 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Hardlinks are not supported.", pszName);
985 if (!szTarget[0])
986 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Link target is empty.", pszName);
987 }
988 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
989 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
990
991 if (rtZipTarHasEscapeSequence(pszName))
992 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Name '%s' contains an escape sequence.", pszName);
993
994 /*
995 * Construct the path to the extracted member.
996 */
997 char szDst[RTPATH_MAX];
998 rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pszName);
999 if (RT_FAILURE(rc))
1000 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to construct destination path for: %Rrc", pszName, rc);
1001
1002 /*
1003 * Extract according to the type.
1004 */
1005 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1006 {
1007 case RTFS_TYPE_FILE:
1008 return rtZipTarCmdExtractFile(pOpts, hVfsObj, rcExit, szDst, &UnixInfo, &Owner, &Group);
1009
1010 case RTFS_TYPE_DIRECTORY:
1011 rc = RTDirCreateFullPath(szDst, UnixInfo.Attr.fMode & RTFS_UNIX_ALL_ACCESS_PERMS);
1012 if (RT_FAILURE(rc))
1013 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating directory: %Rrc", szDst, rc);
1014 break;
1015
1016 case RTFS_TYPE_SYMLINK:
1017 rc = RTSymlinkCreate(szDst, szTarget, RTSYMLINKTYPE_UNKNOWN, 0);
1018 if (RT_FAILURE(rc))
1019 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating symbolic link: %Rrc", szDst, rc);
1020 break;
1021
1022 case RTFS_TYPE_FIFO:
1023 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
1024 case RTFS_TYPE_DEV_CHAR:
1025 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
1026 case RTFS_TYPE_DEV_BLOCK:
1027 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Block devices are not supported.", pszName);
1028 case RTFS_TYPE_SOCKET:
1029 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Sockets are not supported.", pszName);
1030 case RTFS_TYPE_WHITEOUT:
1031 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Whiteouts are not support.", pszName);
1032 default:
1033 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Unknown file type.", pszName);
1034 }
1035
1036 /*
1037 * Set other attributes as requested.
1038 *
1039 * Note! File extraction does get here.
1040 */
1041 if (!pOpts->fNoModTime)
1042 {
1043 rc = RTPathSetTimesEx(szDst, NULL, &UnixInfo.ModificationTime, NULL, NULL, RTPATH_F_ON_LINK);
1044 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED && rc != VERR_NS_SYMLINK_SET_TIME)
1045 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing modification time: %Rrc.", pszName, rc);
1046 }
1047
1048#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
1049 if ( pOpts->uidOwner != NIL_RTUID
1050 || pOpts->gidGroup != NIL_RTGID
1051 || pOpts->fPreserveOwner
1052 || pOpts->fPreserveGroup)
1053 {
1054 RTUID uidFile;
1055 rcExit = rtZipTarQueryExtractOwner(pOpts, &Owner, szDst, rcExit, &uidFile);
1056
1057 RTGID gidFile;
1058 rcExit = rtZipTarQueryExtractGroup(pOpts, &Group, szDst, rcExit, &gidFile);
1059 if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
1060 {
1061 rc = RTPathSetOwnerEx(szDst, uidFile, gidFile, RTPATH_F_ON_LINK);
1062 if (RT_FAILURE(rc))
1063 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", szDst, rc);
1064 }
1065 }
1066#endif
1067
1068#if !defined(RT_OS_WINDOWS) /** @todo implement RTPathSetMode on windows... */
1069 if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode)) /* RTPathSetMode follows symbolic links atm. */
1070 {
1071 RTFMODE fMode;
1072 if (RTFS_IS_DIRECTORY(UnixInfo.Attr.fMode))
1073 fMode = (UnixInfo.Attr.fMode & (pOpts->fDirModeAndMask | RTFS_TYPE_MASK)) | pOpts->fDirModeOrMask;
1074 else
1075 fMode = (UnixInfo.Attr.fMode & (pOpts->fFileModeAndMask | RTFS_TYPE_MASK)) | pOpts->fFileModeOrMask;
1076 rc = RTPathSetMode(szDst, fMode);
1077 if (RT_FAILURE(rc))
1078 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", szDst, rc);
1079 }
1080#endif
1081
1082 return rcExit;
1083}
1084
1085
1086/**
1087 * @callback_method_impl{PFNDOWITHMEMBER, Implements --list.}
1088 */
1089static RTEXITCODE rtZipTarCmdListCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
1090{
1091 /*
1092 * This is very simple in non-verbose mode.
1093 */
1094 if (!pOpts->fVerbose)
1095 {
1096 RTPrintf("%s\n", pszName);
1097 return rcExit;
1098 }
1099
1100 /*
1101 * Query all the information.
1102 */
1103 RTFSOBJINFO UnixInfo;
1104 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
1105 if (RT_FAILURE(rc))
1106 {
1107 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
1108 RT_ZERO(UnixInfo);
1109 }
1110
1111 RTFSOBJINFO Owner;
1112 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
1113 if (RT_FAILURE(rc))
1114 {
1115 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
1116 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
1117 rc, pszName);
1118 RT_ZERO(Owner);
1119 }
1120
1121 RTFSOBJINFO Group;
1122 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
1123 if (RT_FAILURE(rc))
1124 {
1125 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
1126 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
1127 rc, pszName);
1128 RT_ZERO(Group);
1129 }
1130
1131 const char *pszLinkType = NULL;
1132 char szTarget[RTPATH_MAX];
1133 szTarget[0] = '\0';
1134 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
1135 if (hVfsSymlink != NIL_RTVFSSYMLINK)
1136 {
1137 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
1138 if (RT_FAILURE(rc))
1139 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsSymlinkRead returned %Rrc on '%s'", rc, pszName);
1140 RTVfsSymlinkRelease(hVfsSymlink);
1141 pszLinkType = RTFS_IS_SYMLINK(UnixInfo.Attr.fMode) ? "->" : "link to";
1142 }
1143 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
1144 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
1145
1146 /*
1147 * Translate the mode mask.
1148 */
1149 char szMode[16];
1150 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1151 {
1152 case RTFS_TYPE_FIFO: szMode[0] = 'f'; break;
1153 case RTFS_TYPE_DEV_CHAR: szMode[0] = 'c'; break;
1154 case RTFS_TYPE_DIRECTORY: szMode[0] = 'd'; break;
1155 case RTFS_TYPE_DEV_BLOCK: szMode[0] = 'b'; break;
1156 case RTFS_TYPE_FILE: szMode[0] = '-'; break;
1157 case RTFS_TYPE_SYMLINK: szMode[0] = 'l'; break;
1158 case RTFS_TYPE_SOCKET: szMode[0] = 's'; break;
1159 case RTFS_TYPE_WHITEOUT: szMode[0] = 'w'; break;
1160 default: szMode[0] = '?'; break;
1161 }
1162 if (pszLinkType && szMode[0] != 's')
1163 szMode[0] = 'h';
1164
1165 szMode[1] = UnixInfo.Attr.fMode & RTFS_UNIX_IRUSR ? 'r' : '-';
1166 szMode[2] = UnixInfo.Attr.fMode & RTFS_UNIX_IWUSR ? 'w' : '-';
1167 szMode[3] = UnixInfo.Attr.fMode & RTFS_UNIX_IXUSR ? 'x' : '-';
1168
1169 szMode[4] = UnixInfo.Attr.fMode & RTFS_UNIX_IRGRP ? 'r' : '-';
1170 szMode[5] = UnixInfo.Attr.fMode & RTFS_UNIX_IWGRP ? 'w' : '-';
1171 szMode[6] = UnixInfo.Attr.fMode & RTFS_UNIX_IXGRP ? 'x' : '-';
1172
1173 szMode[7] = UnixInfo.Attr.fMode & RTFS_UNIX_IROTH ? 'r' : '-';
1174 szMode[8] = UnixInfo.Attr.fMode & RTFS_UNIX_IWOTH ? 'w' : '-';
1175 szMode[9] = UnixInfo.Attr.fMode & RTFS_UNIX_IXOTH ? 'x' : '-';
1176 szMode[10] = '\0';
1177
1178 /** @todo sticky and set-uid/gid bits. */
1179
1180 /*
1181 * Make sure we've got valid owner and group strings.
1182 */
1183 if (!Owner.Attr.u.UnixGroup.szName[0])
1184 RTStrPrintf(Owner.Attr.u.UnixOwner.szName, sizeof(Owner.Attr.u.UnixOwner.szName),
1185 "%u", UnixInfo.Attr.u.Unix.uid);
1186
1187 if (!Group.Attr.u.UnixOwner.szName[0])
1188 RTStrPrintf(Group.Attr.u.UnixGroup.szName, sizeof(Group.Attr.u.UnixGroup.szName),
1189 "%u", UnixInfo.Attr.u.Unix.gid);
1190
1191 /*
1192 * Format the modification time.
1193 */
1194 char szModTime[32];
1195 RTTIME ModTime;
1196 PRTTIME pTime;
1197 if (!pOpts->fDisplayUtc)
1198 pTime = RTTimeLocalExplode(&ModTime, &UnixInfo.ModificationTime);
1199 else
1200 pTime = RTTimeExplode(&ModTime, &UnixInfo.ModificationTime);
1201 if (!pTime)
1202 RT_ZERO(ModTime);
1203 RTStrPrintf(szModTime, sizeof(szModTime), "%04d-%02u-%02u %02u:%02u",
1204 ModTime.i32Year, ModTime.u8Month, ModTime.u8MonthDay, ModTime.u8Hour, ModTime.u8Minute);
1205
1206 /*
1207 * Format the size and figure how much space is needed between the
1208 * user/group and the size.
1209 */
1210 char szSize[64];
1211 size_t cchSize;
1212 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
1213 {
1214 case RTFS_TYPE_DEV_CHAR:
1215 case RTFS_TYPE_DEV_BLOCK:
1216 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%u,%u",
1217 RTDEV_MAJOR(UnixInfo.Attr.u.Unix.Device), RTDEV_MINOR(UnixInfo.Attr.u.Unix.Device));
1218 break;
1219 default:
1220 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%RU64", UnixInfo.cbObject);
1221 break;
1222 }
1223
1224 size_t cchUserGroup = strlen(Owner.Attr.u.UnixOwner.szName)
1225 + 1
1226 + strlen(Group.Attr.u.UnixGroup.szName);
1227 ssize_t cchPad = cchUserGroup + cchSize + 1 < 19
1228 ? 19 - (cchUserGroup + cchSize + 1)
1229 : 0;
1230
1231 /*
1232 * Go to press.
1233 */
1234 if (pszLinkType)
1235 RTPrintf("%s %s/%s%*s %s %s %s %s %s\n",
1236 szMode,
1237 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
1238 cchPad, "",
1239 szSize,
1240 szModTime,
1241 pszName,
1242 pszLinkType,
1243 szTarget);
1244 else
1245 RTPrintf("%s %s/%s%*s %s %s %s\n",
1246 szMode,
1247 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
1248 cchPad, "",
1249 szSize,
1250 szModTime,
1251 pszName);
1252
1253 return rcExit;
1254}
1255
1256
1257/**
1258 * Display usage.
1259 *
1260 * @param pszProgName The program name.
1261 */
1262static void rtZipTarUsage(const char *pszProgName)
1263{
1264 /*
1265 * 0 1 2 3 4 5 6 7 8
1266 * 012345678901234567890123456789012345678901234567890123456789012345678901234567890
1267 */
1268 RTPrintf("Usage: %s [options]\n"
1269 "\n",
1270 pszProgName);
1271 RTPrintf("Operations:\n"
1272 " -A, --concatenate, --catenate\n"
1273 " Append the content of one tar archive to another. (not impl)\n"
1274 " -c, --create\n"
1275 " Create a new tar archive. (not impl)\n"
1276 " -d, --diff, --compare\n"
1277 " Compare atar archive with the file system. (not impl)\n"
1278 " -r, --append\n"
1279 " Append more files to the tar archive. (not impl)\n"
1280 " -t, --list\n"
1281 " List the contents of the tar archive.\n"
1282 " -u, --update\n"
1283 " Update the archive, adding files that are newer than the\n"
1284 " ones in the archive. (not impl)\n"
1285 " -x, --extract, --get\n"
1286 " Extract the files from the tar archive.\n"
1287 " --delete\n"
1288 " Delete files from the tar archive.\n"
1289 "\n"
1290 );
1291 RTPrintf("Basic Options:\n"
1292 " -C <dir>, --directory <dir> (-A, -c, -d, -r, -u, -x)\n"
1293 " Sets the base directory for input and output file members.\n"
1294 " This does not apply to --file, even if it preceeds it.\n"
1295 " -f <archive>, --file <archive> (all)\n"
1296 " The tar file to create or process. '-' indicates stdout/stdin,\n"
1297 " which is is the default.\n"
1298 " -v, --verbose (all)\n"
1299 " Verbose operation.\n"
1300 " -p, --preserve-permissions (-x)\n"
1301 " Preserve all permissions when extracting. Must be used\n"
1302 " before the mode mask options as it will change some of these.\n"
1303 " -j, --bzip2 (all)\n"
1304 " Compress/decompress the archive with bzip2.\n"
1305 " -z, --gzip, --gunzip, --ungzip (all)\n"
1306 " Compress/decompress the archive with gzip.\n"
1307 "\n");
1308 RTPrintf("Misc Options:\n"
1309 " --owner <uid/username> (-A, -c, -d, -r, -u, -x)\n"
1310 " Set the owner of extracted and archived files to the user specified.\n"
1311 " --group <uid/username> (-A, -c, -d, -r, -u, -x)\n"
1312 " Set the group of extracted and archived files to the group specified.\n"
1313 " --utc (-t)\n"
1314 " Display timestamps as UTC instead of local time.\n"
1315 " -S, --sparse (-A, -c, -u)\n"
1316 " Detect sparse files and store them (gnu tar extension).\n"
1317 " --format <format> (-A, -c, -u, but also -d, -r, -x)\n"
1318 " The file format:\n"
1319 " auto (gnu tar)\n"
1320 " default (gnu tar)\n"
1321 " tar (gnu tar)"
1322 " gnu (tar v1.13+), "
1323 " ustar (tar POSIX.1-1988), "
1324 " pax (tar POSIX.1-2001),\n"
1325 " xar\n"
1326 " Note! Because XAR/TAR detection isn't implemented yet, it\n"
1327 " is necessary to specifcy --format=xar when reading a\n"
1328 " XAR file. Otherwise this option is only for creation.\n"
1329 "\n");
1330 RTPrintf("IPRT Options:\n"
1331 " --prefix <dir-prefix> (-A, -c, -d, -r, -u)\n"
1332 " Directory prefix to give the members added to the archive.\n"
1333 " --file-mode-and-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1334 " Restrict the access mode of regular and special files.\n"
1335 " --file-mode-or-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1336 " Include the given access mode for regular and special files.\n"
1337 " --dir-mode-and-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1338 " Restrict the access mode of directories.\n"
1339 " --dir-mode-or-mask <octal-mode> (-A, -c, -d, -r, -u, -x)\n"
1340 " Include the given access mode for directories.\n"
1341 " --read-ahead (-x)\n"
1342 " Enabled read ahead thread when extracting files.\n"
1343 " --push-file (-A, -c, -u)\n"
1344 " Use RTVfsFsStrmPushFile instead of RTVfsFsStrmAdd.\n"
1345 "\n");
1346 RTPrintf("Standard Options:\n"
1347 " -h, -?, --help\n"
1348 " Display this help text.\n"
1349 " -V, --version\n"
1350 " Display version number.\n");
1351}
1352
1353
1354RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
1355{
1356 /*
1357 * Parse the command line.
1358 *
1359 * N.B. This is less flexible that your regular tar program in that it
1360 * requires the operation to be specified as an option. On the other
1361 * hand, you can specify it where ever you like in the command line.
1362 */
1363 static const RTGETOPTDEF s_aOptions[] =
1364 {
1365 /* operations */
1366 { "--concatenate", 'A', RTGETOPT_REQ_NOTHING },
1367 { "--catenate", 'A', RTGETOPT_REQ_NOTHING },
1368 { "--create", 'c', RTGETOPT_REQ_NOTHING },
1369 { "--diff", 'd', RTGETOPT_REQ_NOTHING },
1370 { "--compare", 'd', RTGETOPT_REQ_NOTHING },
1371 { "--append", 'r', RTGETOPT_REQ_NOTHING },
1372 { "--list", 't', RTGETOPT_REQ_NOTHING },
1373 { "--update", 'u', RTGETOPT_REQ_NOTHING },
1374 { "--extract", 'x', RTGETOPT_REQ_NOTHING },
1375 { "--get", 'x', RTGETOPT_REQ_NOTHING },
1376 { "--delete", RTZIPTARCMD_OPT_DELETE, RTGETOPT_REQ_NOTHING },
1377
1378 /* basic options */
1379 { "--directory", 'C', RTGETOPT_REQ_STRING },
1380 { "--file", 'f', RTGETOPT_REQ_STRING },
1381 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1382 { "--preserve-permissions", 'p', RTGETOPT_REQ_NOTHING },
1383 { "--bzip2", 'j', RTGETOPT_REQ_NOTHING },
1384 { "--gzip", 'z', RTGETOPT_REQ_NOTHING },
1385 { "--gunzip", 'z', RTGETOPT_REQ_NOTHING },
1386 { "--ungzip", 'z', RTGETOPT_REQ_NOTHING },
1387
1388 /* other options. */
1389 { "--owner", RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
1390 { "--group", RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
1391 { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
1392 { "--sparse", 'S', RTGETOPT_REQ_NOTHING },
1393 { "--format", RTZIPTARCMD_OPT_FORMAT, RTGETOPT_REQ_STRING },
1394
1395 /* IPRT extensions */
1396 { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
1397 { "--file-mode-and-mask", RTZIPTARCMD_OPT_FILE_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1398 { "--file-mode-or-mask", RTZIPTARCMD_OPT_FILE_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1399 { "--dir-mode-and-mask", RTZIPTARCMD_OPT_DIR_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1400 { "--dir-mode-or-mask", RTZIPTARCMD_OPT_DIR_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1401 { "--read-ahead", RTZIPTARCMD_OPT_READ_AHEAD, RTGETOPT_REQ_NOTHING },
1402 { "--use-push-file", RTZIPTARCMD_OPT_USE_PUSH_FILE, RTGETOPT_REQ_NOTHING },
1403 };
1404
1405 RTGETOPTSTATE GetState;
1406 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1407 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1408 if (RT_FAILURE(rc))
1409 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
1410
1411 RTZIPTARCMDOPS Opts;
1412 RT_ZERO(Opts);
1413 Opts.enmFormat = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
1414 Opts.uidOwner = NIL_RTUID;
1415 Opts.gidGroup = NIL_RTUID;
1416 Opts.fFileModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
1417 Opts.fDirModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
1418#if 0
1419 if (RTPermIsSuperUser())
1420 {
1421 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1422 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1423 Opts.fPreserveOwner = true;
1424 Opts.fPreserveGroup = true;
1425 }
1426#endif
1427 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1428
1429 RTGETOPTUNION ValueUnion;
1430 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
1431 && rc != VINF_GETOPT_NOT_OPTION)
1432 {
1433 switch (rc)
1434 {
1435 /* operations */
1436 case 'A':
1437 case 'c':
1438 case 'd':
1439 case 'r':
1440 case 't':
1441 case 'u':
1442 case 'x':
1443 case RTZIPTARCMD_OPT_DELETE:
1444 if (Opts.iOperation)
1445 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Conflicting tar operation (%s already set, now %s)",
1446 Opts.pszOperation, ValueUnion.pDef->pszLong);
1447 Opts.iOperation = rc;
1448 Opts.pszOperation = ValueUnion.pDef->pszLong;
1449 break;
1450
1451 /* basic options */
1452 case 'C':
1453 if (Opts.pszDirectory)
1454 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -C/--directory once");
1455 Opts.pszDirectory = ValueUnion.psz;
1456 break;
1457
1458 case 'f':
1459 if (Opts.pszFile)
1460 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -f/--file once");
1461 Opts.pszFile = ValueUnion.psz;
1462 break;
1463
1464 case 'v':
1465 Opts.fVerbose = true;
1466 break;
1467
1468 case 'p':
1469 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1470 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1471 Opts.fPreserveOwner = true;
1472 Opts.fPreserveGroup = true;
1473 break;
1474
1475 case 'j':
1476 case 'z':
1477 if (Opts.chZipper)
1478 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify one compressor / decompressor");
1479 Opts.chZipper = rc;
1480 break;
1481
1482 case RTZIPTARCMD_OPT_OWNER:
1483 if (Opts.pszOwner)
1484 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --owner once");
1485 Opts.pszOwner = ValueUnion.psz;
1486
1487 rc = RTStrToUInt32Full(Opts.pszOwner, 0, &ValueUnion.u32);
1488 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1489 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1490 "Error convering --owner '%s' into a number: %Rrc", Opts.pszOwner, rc);
1491 if (RT_SUCCESS(rc))
1492 {
1493 Opts.uidOwner = ValueUnion.u32;
1494 Opts.pszOwner = NULL;
1495 }
1496 break;
1497
1498 case RTZIPTARCMD_OPT_GROUP:
1499 if (Opts.pszGroup)
1500 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --group once");
1501 Opts.pszGroup = ValueUnion.psz;
1502
1503 rc = RTStrToUInt32Full(Opts.pszGroup, 0, &ValueUnion.u32);
1504 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1505 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1506 "Error convering --group '%s' into a number: %Rrc", Opts.pszGroup, rc);
1507 if (RT_SUCCESS(rc))
1508 {
1509 Opts.gidGroup = ValueUnion.u32;
1510 Opts.pszGroup = NULL;
1511 }
1512 break;
1513
1514 case RTZIPTARCMD_OPT_UTC:
1515 Opts.fDisplayUtc = true;
1516 break;
1517
1518 /* GNU */
1519 case 'S':
1520 Opts.fTarCreate |= RTZIPTAR_C_SPARSE;
1521 break;
1522
1523 /* iprt extensions */
1524 case RTZIPTARCMD_OPT_PREFIX:
1525 if (Opts.pszPrefix)
1526 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --prefix once");
1527 Opts.pszPrefix = ValueUnion.psz;
1528 break;
1529
1530 case RTZIPTARCMD_OPT_FILE_MODE_AND_MASK:
1531 Opts.fFileModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1532 break;
1533
1534 case RTZIPTARCMD_OPT_FILE_MODE_OR_MASK:
1535 Opts.fFileModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1536 break;
1537
1538 case RTZIPTARCMD_OPT_DIR_MODE_AND_MASK:
1539 Opts.fDirModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1540 break;
1541
1542 case RTZIPTARCMD_OPT_DIR_MODE_OR_MASK:
1543 Opts.fDirModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1544 break;
1545
1546 case RTZIPTARCMD_OPT_FORMAT:
1547 if (!strcmp(ValueUnion.psz, "auto") || !strcmp(ValueUnion.psz, "default"))
1548 {
1549 Opts.enmFormat = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
1550 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1551 }
1552 else if (!strcmp(ValueUnion.psz, "tar"))
1553 {
1554 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1555 Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
1556 }
1557 else if (!strcmp(ValueUnion.psz, "gnu"))
1558 {
1559 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1560 Opts.enmTarFormat = RTZIPTARFORMAT_GNU;
1561 }
1562 else if (!strcmp(ValueUnion.psz, "ustar"))
1563 {
1564 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1565 Opts.enmTarFormat = RTZIPTARFORMAT_USTAR;
1566 }
1567 else if ( !strcmp(ValueUnion.psz, "posix")
1568 || !strcmp(ValueUnion.psz, "pax"))
1569 {
1570 Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
1571 Opts.enmTarFormat = RTZIPTARFORMAT_PAX;
1572 }
1573 else if (!strcmp(ValueUnion.psz, "xar"))
1574 Opts.enmFormat = RTZIPTARCMDFORMAT_XAR;
1575 else
1576 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
1577 break;
1578
1579 case RTZIPTARCMD_OPT_READ_AHEAD:
1580 Opts.fReadAhead = true;
1581 break;
1582
1583 case RTZIPTARCMD_OPT_USE_PUSH_FILE:
1584 Opts.fUsePushFile = true;
1585 break;
1586
1587 /* Standard bits. */
1588 case 'h':
1589 rtZipTarUsage(RTPathFilename(papszArgs[0]));
1590 return RTEXITCODE_SUCCESS;
1591
1592 case 'V':
1593 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1594 return RTEXITCODE_SUCCESS;
1595
1596 default:
1597 return RTGetOptPrintError(rc, &ValueUnion);
1598 }
1599 }
1600
1601 if (rc == VINF_GETOPT_NOT_OPTION)
1602 {
1603 /* this is kind of ugly. */
1604 Assert((unsigned)GetState.iNext - 1 <= cArgs);
1605 Opts.papszFiles = (const char * const *)&papszArgs[GetState.iNext - 1];
1606 Opts.cFiles = cArgs - GetState.iNext + 1;
1607 }
1608
1609 /*
1610 * Post proceess the options.
1611 */
1612 if (Opts.iOperation == 0)
1613 {
1614 Opts.iOperation = 't';
1615 Opts.pszOperation = "--list";
1616 }
1617
1618 if ( Opts.iOperation == 'x'
1619 && Opts.pszOwner)
1620 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --owner with %s has not implemented yet", Opts.pszOperation);
1621
1622 if ( Opts.iOperation == 'x'
1623 && Opts.pszGroup)
1624 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --group with %s has not implemented yet", Opts.pszOperation);
1625
1626 /*
1627 * Do the job.
1628 */
1629 switch (Opts.iOperation)
1630 {
1631 case 't':
1632 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdListCallback);
1633
1634 case 'x':
1635 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
1636
1637 case 'c':
1638 return rtZipTarCreate(&Opts);
1639
1640 case 'A':
1641 case 'd':
1642 case 'r':
1643 case 'u':
1644 case RTZIPTARCMD_OPT_DELETE:
1645 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The operation %s is not implemented yet", Opts.pszOperation);
1646
1647 default:
1648 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Internal error");
1649 }
1650}
1651
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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