VirtualBox

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

Last change on this file since 81106 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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

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