VirtualBox

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

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

RTZipTarCmd: Added --use-push-file to test the RTVfsFsStrmPushFile API.

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

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