VirtualBox

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

Last change on this file since 65559 was 65559, checked in by vboxsync, 8 years ago

Runtime/tarcmd.cpp: Fix copy and paste bug in help text

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.7 KB
Line 
1/* $Id: tarcmd.cpp 65559 2017-01-31 21:56:43Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/zip.h>
32
33#include <iprt/asm.h>
34#include <iprt/buildconfig.h>
35#include <iprt/ctype.h>
36#include <iprt/dir.h>
37#include <iprt/file.h>
38#include <iprt/getopt.h>
39#include <iprt/initterm.h>
40#include <iprt/mem.h>
41#include <iprt/message.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/stream.h>
45#include <iprt/string.h>
46#include <iprt/symlink.h>
47#include <iprt/vfs.h>
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53#define RTZIPTARCMD_OPT_DELETE 1000
54#define RTZIPTARCMD_OPT_OWNER 1001
55#define RTZIPTARCMD_OPT_GROUP 1002
56#define RTZIPTARCMD_OPT_UTC 1003
57#define RTZIPTARCMD_OPT_PREFIX 1004
58#define RTZIPTARCMD_OPT_FILE_MODE_AND_MASK 1005
59#define RTZIPTARCMD_OPT_FILE_MODE_OR_MASK 1006
60#define RTZIPTARCMD_OPT_DIR_MODE_AND_MASK 1007
61#define RTZIPTARCMD_OPT_DIR_MODE_OR_MASK 1008
62#define RTZIPTARCMD_OPT_FORMAT 1009
63#define RTZIPTARCMD_OPT_READ_AHEAD 1010
64
65/** File format. */
66typedef enum RTZIPTARFORMAT
67{
68 RTZIPTARFORMAT_INVALID = 0,
69 /** Autodetect if possible, defaulting to TAR. */
70 RTZIPTARFORMAT_AUTO_DEFAULT,
71 /** TAR. */
72 RTZIPTARFORMAT_TAR,
73 /** XAR. */
74 RTZIPTARFORMAT_XAR
75} RTZIPTARFORMAT;
76
77
78/*********************************************************************************************************************************
79* Structures and Typedefs *
80*********************************************************************************************************************************/
81/**
82 * IPRT TAR option structure.
83 */
84typedef struct RTZIPTARCMDOPS
85{
86 /** The file format. */
87 RTZIPTARFORMAT enmFormat;
88
89 /** The operation (Acdrtux or RTZIPTARCMD_OPT_DELETE). */
90 int iOperation;
91 /** The long operation option name. */
92 const char *pszOperation;
93
94 /** The directory to change into when packing and unpacking. */
95 const char *pszDirectory;
96 /** The tar file name. */
97 const char *pszFile;
98 /** Whether we're verbose or quiet. */
99 bool fVerbose;
100 /** Whether to preserve the original file owner when restoring. */
101 bool fPreserveOwner;
102 /** Whether to preserve the original file group when restoring. */
103 bool fPreserveGroup;
104 /** Whether to skip restoring the modification time (only time stored by the
105 * traditional TAR format). */
106 bool fNoModTime;
107 /** Whether to add a read ahead thread. */
108 bool fReadAhead;
109 /** The compressor/decompressor method to employ (0, z or j). */
110 char chZipper;
111
112 /** The owner to set. NULL if not applicable.
113 * Always resolved into uidOwner for extraction. */
114 const char *pszOwner;
115 /** The owner ID to set. NIL_RTUID if not applicable. */
116 RTUID uidOwner;
117 /** The group to set. NULL if not applicable.
118 * Always resolved into gidGroup for extraction. */
119 const char *pszGroup;
120 /** The group ID to set. NIL_RTGUID if not applicable. */
121 RTGID gidGroup;
122 /** Display the modification times in UTC instead of local time. */
123 bool fDisplayUtc;
124 /** File mode AND mask. */
125 RTFMODE fFileModeAndMask;
126 /** File mode OR mask. */
127 RTFMODE fFileModeOrMask;
128 /** Directory mode AND mask. */
129 RTFMODE fDirModeAndMask;
130 /** Directory mode OR mask. */
131 RTFMODE fDirModeOrMask;
132
133 /** What to prefix all names with when creating, adding, whatever. */
134 const char *pszPrefix;
135
136 /** The number of files(, directories or whatever) specified. */
137 uint32_t cFiles;
138 /** Array of files(, directories or whatever).
139 * Terminated by a NULL entry. */
140 const char * const *papszFiles;
141} RTZIPTARCMDOPS;
142/** Pointer to the IPRT tar options. */
143typedef RTZIPTARCMDOPS *PRTZIPTARCMDOPS;
144
145/**
146 * Callback used by rtZipTarDoWithMembers
147 *
148 * @returns rcExit or RTEXITCODE_FAILURE.
149 * @param pOpts The tar options.
150 * @param hVfsObj The tar object to display
151 * @param pszName The name.
152 * @param rcExit The current exit code.
153 */
154typedef RTEXITCODE (*PFNDOWITHMEMBER)(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit);
155
156
157/**
158 * Checks if @a pszName is a member of @a papszNames, optionally returning the
159 * index.
160 *
161 * @returns true if the name is in the list, otherwise false.
162 * @param pszName The name to find.
163 * @param papszNames The array of names.
164 * @param piName Where to optionally return the array index.
165 */
166static bool rtZipTarCmdIsNameInArray(const char *pszName, const char * const *papszNames, uint32_t *piName)
167{
168 for (uint32_t iName = 0; papszNames[iName]; iName++)
169 if (!strcmp(papszNames[iName], pszName))
170 {
171 if (piName)
172 *piName = iName;
173 return true;
174 }
175 return false;
176}
177
178
179/**
180 * Opens the input archive specified by the options.
181 *
182 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
183 * @param pOpts The options.
184 * @param phVfsFss Where to return the TAR filesystem stream handle.
185 */
186static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTREAM phVfsFss)
187{
188 int rc;
189
190 /*
191 * Open the input file.
192 */
193 RTVFSIOSTREAM hVfsIos;
194 if ( pOpts->pszFile
195 && strcmp(pOpts->pszFile, "-") != 0)
196 {
197 const char *pszError;
198 rc = RTVfsChainOpenIoStream(pOpts->pszFile,
199 RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
200 &hVfsIos,
201 &pszError);
202 if (RT_FAILURE(rc))
203 {
204 if (pszError && *pszError)
205 return RTMsgErrorExit(RTEXITCODE_FAILURE,
206 "RTVfsChainOpenIoStream failed with rc=%Rrc:\n"
207 " '%s'\n"
208 " %*s^\n",
209 rc, pOpts->pszFile, pszError - pOpts->pszFile, "");
210 return RTMsgErrorExit(RTEXITCODE_FAILURE,
211 "Failed with %Rrc opening the input archive '%s'", rc, pOpts->pszFile);
212 }
213 }
214 else
215 {
216 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT,
217 RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
218 true /*fLeaveOpen*/,
219 &hVfsIos);
220 if (RT_FAILURE(rc))
221 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard in for reading: %Rrc", rc);
222 }
223
224 /*
225 * Pass it thru a decompressor?
226 */
227 RTVFSIOSTREAM hVfsIosDecomp = NIL_RTVFSIOSTREAM;
228 switch (pOpts->chZipper)
229 {
230 /* no */
231 case '\0':
232 rc = VINF_SUCCESS;
233 break;
234
235 /* gunzip */
236 case 'z':
237 rc = RTZipGzipDecompressIoStream(hVfsIos, 0 /*fFlags*/, &hVfsIosDecomp);
238 if (RT_FAILURE(rc))
239 RTMsgError("Failed to open gzip decompressor: %Rrc", rc);
240 break;
241
242 /* bunzip2 */
243 case 'j':
244 rc = VERR_NOT_SUPPORTED;
245 RTMsgError("bzip2 is not supported by this build");
246 break;
247
248 /* bug */
249 default:
250 rc = VERR_INTERNAL_ERROR_2;
251 RTMsgError("unknown decompression method '%c'", pOpts->chZipper);
252 break;
253 }
254 if (RT_FAILURE(rc))
255 {
256 RTVfsIoStrmRelease(hVfsIos);
257 return RTEXITCODE_FAILURE;
258 }
259
260 if (hVfsIosDecomp != NIL_RTVFSIOSTREAM)
261 {
262 RTVfsIoStrmRelease(hVfsIos);
263 hVfsIos = hVfsIosDecomp;
264 hVfsIosDecomp = NIL_RTVFSIOSTREAM;
265 }
266
267 /*
268 * Open the filesystem stream.
269 */
270 if (pOpts->enmFormat == RTZIPTARFORMAT_TAR)
271 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
272 else if (pOpts->enmFormat == RTZIPTARFORMAT_XAR)
273#ifdef IPRT_WITH_XAR /* Requires C++ and XML, so only in some configruation of IPRT. */
274 rc = RTZipXarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
275#else
276 rc = VERR_NOT_SUPPORTED;
277#endif
278 else /** @todo make RTZipTarFsStreamFromIoStream fail if not tar file! */
279 rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
280 RTVfsIoStrmRelease(hVfsIos);
281 if (RT_FAILURE(rc))
282 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
283
284 return RTEXITCODE_SUCCESS;
285}
286
287
288/**
289 * Worker for the --list and --extract commands.
290 *
291 * @returns The appropriate exit code.
292 * @param pOpts The tar options.
293 * @param pfnCallback The command specific callback.
294 */
295static RTEXITCODE rtZipTarDoWithMembers(PRTZIPTARCMDOPS pOpts, PFNDOWITHMEMBER pfnCallback)
296{
297 /*
298 * Allocate a bitmap to go with the file list. This will be used to
299 * indicate which files we've processed and which not.
300 */
301 uint32_t *pbmFound = NULL;
302 if (pOpts->cFiles)
303 {
304 pbmFound = (uint32_t *)RTMemAllocZ(((pOpts->cFiles + 31) / 32) * sizeof(uint32_t));
305 if (!pbmFound)
306 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate the found-file-bitmap");
307 }
308
309
310 /*
311 * Open the input archive.
312 */
313 RTVFSFSSTREAM hVfsFssIn;
314 RTEXITCODE rcExit = rtZipTarCmdOpenInputArchive(pOpts, &hVfsFssIn);
315 if (rcExit == RTEXITCODE_SUCCESS)
316 {
317 /*
318 * Process the stream.
319 */
320 for (;;)
321 {
322 /*
323 * Retrive the next object.
324 */
325 char *pszName;
326 RTVFSOBJ hVfsObj;
327 int rc = RTVfsFsStrmNext(hVfsFssIn, &pszName, NULL, &hVfsObj);
328 if (RT_FAILURE(rc))
329 {
330 if (rc != VERR_EOF)
331 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext returned %Rrc", rc);
332 break;
333 }
334
335 /*
336 * Should we process this entry?
337 */
338 uint32_t iFile = UINT32_MAX;
339 if ( !pOpts->cFiles
340 || rtZipTarCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile) )
341 {
342 if (pbmFound)
343 ASMBitSet(pbmFound, iFile);
344
345 rcExit = pfnCallback(pOpts, hVfsObj, pszName, rcExit);
346 }
347
348 /*
349 * Release the current object and string.
350 */
351 RTVfsObjRelease(hVfsObj);
352 RTStrFree(pszName);
353 }
354
355 /*
356 * Complain about any files we didn't find.
357 */
358 for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
359 if (!ASMBitTest(pbmFound, iFile))
360 {
361 RTMsgError("%s: Was not found in the archive", pOpts->papszFiles[iFile]);
362 rcExit = RTEXITCODE_FAILURE;
363 }
364
365 RTVfsFsStrmRelease(hVfsFssIn);
366 }
367 RTMemFree(pbmFound);
368 return rcExit;
369}
370
371
372/**
373 * Checks if the name contains any escape sequences.
374 *
375 * An escape sequence would generally be one or more '..' references. On DOS
376 * like system, something that would make up a drive letter reference is also
377 * considered an escape sequence.
378 *
379 * @returns true / false.
380 * @param pszName The name to consider.
381 */
382static bool rtZipTarHasEscapeSequence(const char *pszName)
383{
384#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
385 if (pszName[0] == ':')
386 return true;
387#endif
388 while (*pszName)
389 {
390 while (RTPATH_IS_SEP(*pszName))
391 pszName++;
392 if ( pszName[0] == '.'
393 && pszName[1] == '.'
394 && (pszName[2] == '\0' || RTPATH_IS_SLASH(pszName[2])) )
395 return true;
396 while (*pszName && !RTPATH_IS_SEP(*pszName))
397 pszName++;
398 }
399
400 return false;
401}
402
403#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
404
405/**
406 * Queries the user ID to use when extracting a member.
407 *
408 * @returns rcExit or RTEXITCODE_FAILURE.
409 * @param pOpts The tar options.
410 * @param pUser The user info.
411 * @param pszName The file name to use when complaining.
412 * @param rcExit The current exit code.
413 * @param pUid Where to return the user ID.
414 */
415static RTEXITCODE rtZipTarQueryExtractOwner(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pOwner, const char *pszName, RTEXITCODE rcExit,
416 PRTUID pUid)
417{
418 if (pOpts->uidOwner != NIL_RTUID)
419 *pUid = pOpts->uidOwner;
420 else if (pOpts->fPreserveGroup)
421 {
422 if (!pOwner->Attr.u.UnixGroup.szName[0])
423 *pUid = pOwner->Attr.u.UnixOwner.uid;
424 else
425 {
426 *pUid = NIL_RTUID;
427 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: User resolving is not implemented.", pszName);
428 }
429 }
430 else
431 *pUid = NIL_RTUID;
432 return rcExit;
433}
434
435
436/**
437 * Queries the group ID to use when extracting a member.
438 *
439 * @returns rcExit or RTEXITCODE_FAILURE.
440 * @param pOpts The tar options.
441 * @param pGroup The group info.
442 * @param pszName The file name to use when complaining.
443 * @param rcExit The current exit code.
444 * @param pGid Where to return the group ID.
445 */
446static RTEXITCODE rtZipTarQueryExtractGroup(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pGroup, const char *pszName, RTEXITCODE rcExit,
447 PRTGID pGid)
448{
449 if (pOpts->gidGroup != NIL_RTGID)
450 *pGid = pOpts->gidGroup;
451 else if (pOpts->fPreserveGroup)
452 {
453 if (!pGroup->Attr.u.UnixGroup.szName[0])
454 *pGid = pGroup->Attr.u.UnixGroup.gid;
455 else
456 {
457 *pGid = NIL_RTGID;
458 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Group resolving is not implemented.", pszName);
459 }
460 }
461 else
462 *pGid = NIL_RTGID;
463 return rcExit;
464}
465
466#endif /* !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) */
467
468
469/**
470 * Extracts a file.
471 *
472 * Since we can restore permissions and attributes more efficiently by working
473 * directly on the file handle, we have special code path for files.
474 *
475 * @returns rcExit or RTEXITCODE_FAILURE.
476 * @param pOpts The tar options.
477 * @param hVfsObj The tar object to display
478 * @param rcExit The current exit code.
479 * @param pUnixInfo The unix fs object info.
480 * @param pOwner The owner info.
481 * @param pGroup The group info.
482 */
483static RTEXITCODE rtZipTarCmdExtractFile(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, RTEXITCODE rcExit,
484 const char *pszDst, PCRTFSOBJINFO pUnixInfo, PCRTFSOBJINFO pOwner, PCRTFSOBJINFO pGroup)
485{
486 /*
487 * Open the destination file and create a stream object for it.
488 */
489 uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_ACCESS_ATTR_DEFAULT
490 | ((RTFS_UNIX_IWUSR | RTFS_UNIX_IRUSR) << RTFILE_O_CREATE_MODE_SHIFT);
491 RTFILE hFile;
492 int rc = RTFileOpen(&hFile, pszDst, fOpen);
493 if (RT_FAILURE(rc))
494 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating file: %Rrc", pszDst, rc);
495
496 RTVFSIOSTREAM hVfsIosDst;
497 rc = RTVfsIoStrmFromRTFile(hFile, fOpen, true /*fLeaveOpen*/, &hVfsIosDst);
498 if (RT_SUCCESS(rc))
499 {
500 /*
501 * Convert source to a stream and optionally add a read ahead stage.
502 */
503 RTVFSIOSTREAM hVfsIosSrc = RTVfsObjToIoStream(hVfsObj);
504 if (pOpts->fReadAhead)
505 {
506 RTVFSIOSTREAM hVfsReadAhead;
507 rc = RTVfsCreateReadAheadForIoStream(hVfsIosSrc, 0 /*fFlag*/, 16 /*cBuffers*/, _256K /*cbBuffer*/, &hVfsReadAhead);
508 if (RT_SUCCESS(rc))
509 {
510 RTVfsIoStrmRelease(hVfsIosSrc);
511 hVfsIosSrc = hVfsReadAhead;
512 }
513 else
514 AssertRC(rc); /* can be ignored in release builds. */
515 }
516
517 /*
518 * Pump the data thru.
519 */
520 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (uint32_t)RT_MIN(pUnixInfo->cbObject, _1M));
521 if (RT_SUCCESS(rc))
522 {
523 /*
524 * Correct the file mode and other attributes.
525 */
526 if (!pOpts->fNoModTime)
527 {
528 rc = RTFileSetTimes(hFile, NULL, &pUnixInfo->ModificationTime, NULL, NULL);
529 if (RT_FAILURE(rc))
530 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error setting times: %Rrc", pszDst, rc);
531 }
532
533#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
534 if ( pOpts->uidOwner != NIL_RTUID
535 || pOpts->gidGroup != NIL_RTGID
536 || pOpts->fPreserveOwner
537 || pOpts->fPreserveGroup)
538 {
539 RTUID uidFile;
540 rcExit = rtZipTarQueryExtractOwner(pOpts, pOwner, pszDst, rcExit, &uidFile);
541
542 RTGID gidFile;
543 rcExit = rtZipTarQueryExtractGroup(pOpts, pGroup, pszDst, rcExit, &gidFile);
544 if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
545 {
546 rc = RTFileSetOwner(hFile, uidFile, gidFile);
547 if (RT_FAILURE(rc))
548 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", pszDst, rc);
549 }
550 }
551#else
552 RT_NOREF_PV(pOwner); RT_NOREF_PV(pGroup);
553#endif
554
555 RTFMODE fMode = (pUnixInfo->Attr.fMode & pOpts->fFileModeAndMask) | pOpts->fFileModeOrMask;
556 rc = RTFileSetMode(hFile, fMode | RTFS_TYPE_FILE);
557 if (RT_FAILURE(rc))
558 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", pszDst, rc);
559 }
560 else
561 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error writing out file: %Rrc", pszDst, rc);
562 RTVfsIoStrmRelease(hVfsIosSrc);
563 RTVfsIoStrmRelease(hVfsIosDst);
564 }
565 else
566 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating I/O stream for file: %Rrc", pszDst, rc);
567 RTFileClose(hFile);
568 return rcExit;
569}
570
571
572/**
573 * @callback_method_impl{PFNDOWITHMEMBER, Implements --extract.}
574 */
575static RTEXITCODE rtZipTarCmdExtractCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
576{
577 if (pOpts->fVerbose)
578 RTPrintf("%s\n", pszName);
579
580 /*
581 * Query all the information.
582 */
583 RTFSOBJINFO UnixInfo;
584 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
585 if (RT_FAILURE(rc))
586 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
587
588 RTFSOBJINFO Owner;
589 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
590 if (RT_FAILURE(rc))
591 return RTMsgErrorExit(RTEXITCODE_FAILURE,
592 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
593 rc, pszName);
594
595 RTFSOBJINFO Group;
596 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
597 if (RT_FAILURE(rc))
598 return RTMsgErrorExit(RTEXITCODE_FAILURE,
599 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
600 rc, pszName);
601
602 char szTarget[RTPATH_MAX];
603 szTarget[0] = '\0';
604 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
605 if (hVfsSymlink != NIL_RTVFSSYMLINK)
606 {
607 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
608 RTVfsSymlinkRelease(hVfsSymlink);
609 if (RT_FAILURE(rc))
610 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: RTVfsSymlinkRead failed: %Rrc", pszName, rc);
611 if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
612 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Hardlinks are not supported.", pszName);
613 if (!szTarget[0])
614 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Link target is empty.", pszName);
615 }
616 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
617 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
618
619 if (rtZipTarHasEscapeSequence(pszName))
620 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Name '%s' contains an escape sequence.", pszName);
621
622 /*
623 * Construct the path to the extracted member.
624 */
625 char szDst[RTPATH_MAX];
626 rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pszName);
627 if (RT_FAILURE(rc))
628 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to construct destination path for: %Rrc", pszName, rc);
629
630 /*
631 * Extract according to the type.
632 */
633 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
634 {
635 case RTFS_TYPE_FILE:
636 return rtZipTarCmdExtractFile(pOpts, hVfsObj, rcExit, szDst, &UnixInfo, &Owner, &Group);
637
638 case RTFS_TYPE_DIRECTORY:
639 rc = RTDirCreateFullPath(szDst, UnixInfo.Attr.fMode & RTFS_UNIX_ALL_ACCESS_PERMS);
640 if (RT_FAILURE(rc))
641 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating directory: %Rrc", szDst, rc);
642 break;
643
644 case RTFS_TYPE_SYMLINK:
645 rc = RTSymlinkCreate(szDst, szTarget, RTSYMLINKTYPE_UNKNOWN, 0);
646 if (RT_FAILURE(rc))
647 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating symbolic link: %Rrc", szDst, rc);
648 break;
649
650 case RTFS_TYPE_FIFO:
651 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
652 case RTFS_TYPE_DEV_CHAR:
653 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
654 case RTFS_TYPE_DEV_BLOCK:
655 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Block devices are not supported.", pszName);
656 case RTFS_TYPE_SOCKET:
657 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Sockets are not supported.", pszName);
658 case RTFS_TYPE_WHITEOUT:
659 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Whiteouts are not support.", pszName);
660 default:
661 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Unknown file type.", pszName);
662 }
663
664 /*
665 * Set other attributes as requested.
666 *
667 * Note! File extraction does get here.
668 */
669 if (!pOpts->fNoModTime)
670 {
671 rc = RTPathSetTimesEx(szDst, NULL, &UnixInfo.ModificationTime, NULL, NULL, RTPATH_F_ON_LINK);
672 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED && rc != VERR_NS_SYMLINK_SET_TIME)
673 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing modification time: %Rrc.", pszName, rc);
674 }
675
676#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
677 if ( pOpts->uidOwner != NIL_RTUID
678 || pOpts->gidGroup != NIL_RTGID
679 || pOpts->fPreserveOwner
680 || pOpts->fPreserveGroup)
681 {
682 RTUID uidFile;
683 rcExit = rtZipTarQueryExtractOwner(pOpts, &Owner, szDst, rcExit, &uidFile);
684
685 RTGID gidFile;
686 rcExit = rtZipTarQueryExtractGroup(pOpts, &Group, szDst, rcExit, &gidFile);
687 if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
688 {
689 rc = RTPathSetOwnerEx(szDst, uidFile, gidFile, RTPATH_F_ON_LINK);
690 if (RT_FAILURE(rc))
691 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", szDst, rc);
692 }
693 }
694#endif
695
696#if !defined(RT_OS_WINDOWS) /** @todo implement RTPathSetMode on windows... */
697 if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode)) /* RTPathSetMode follows symbolic links atm. */
698 {
699 RTFMODE fMode;
700 if (RTFS_IS_DIRECTORY(UnixInfo.Attr.fMode))
701 fMode = (UnixInfo.Attr.fMode & (pOpts->fDirModeAndMask | RTFS_TYPE_MASK)) | pOpts->fDirModeOrMask;
702 else
703 fMode = (UnixInfo.Attr.fMode & (pOpts->fFileModeAndMask | RTFS_TYPE_MASK)) | pOpts->fFileModeOrMask;
704 rc = RTPathSetMode(szDst, fMode);
705 if (RT_FAILURE(rc))
706 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", szDst, rc);
707 }
708#endif
709
710 return rcExit;
711}
712
713
714/**
715 * @callback_method_impl{PFNDOWITHMEMBER, Implements --list.}
716 */
717static RTEXITCODE rtZipTarCmdListCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
718{
719 /*
720 * This is very simple in non-verbose mode.
721 */
722 if (!pOpts->fVerbose)
723 {
724 RTPrintf("%s\n", pszName);
725 return rcExit;
726 }
727
728 /*
729 * Query all the information.
730 */
731 RTFSOBJINFO UnixInfo;
732 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
733 if (RT_FAILURE(rc))
734 {
735 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
736 RT_ZERO(UnixInfo);
737 }
738
739 RTFSOBJINFO Owner;
740 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
741 if (RT_FAILURE(rc))
742 {
743 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
744 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
745 rc, pszName);
746 RT_ZERO(Owner);
747 }
748
749 RTFSOBJINFO Group;
750 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
751 if (RT_FAILURE(rc))
752 {
753 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
754 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
755 rc, pszName);
756 RT_ZERO(Group);
757 }
758
759 const char *pszLinkType = NULL;
760 char szTarget[RTPATH_MAX];
761 szTarget[0] = '\0';
762 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
763 if (hVfsSymlink != NIL_RTVFSSYMLINK)
764 {
765 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
766 if (RT_FAILURE(rc))
767 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsSymlinkRead returned %Rrc on '%s'", rc, pszName);
768 RTVfsSymlinkRelease(hVfsSymlink);
769 pszLinkType = RTFS_IS_SYMLINK(UnixInfo.Attr.fMode) ? "->" : "link to";
770 }
771 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
772 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
773
774 /*
775 * Translate the mode mask.
776 */
777 char szMode[16];
778 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
779 {
780 case RTFS_TYPE_FIFO: szMode[0] = 'f'; break;
781 case RTFS_TYPE_DEV_CHAR: szMode[0] = 'c'; break;
782 case RTFS_TYPE_DIRECTORY: szMode[0] = 'd'; break;
783 case RTFS_TYPE_DEV_BLOCK: szMode[0] = 'b'; break;
784 case RTFS_TYPE_FILE: szMode[0] = '-'; break;
785 case RTFS_TYPE_SYMLINK: szMode[0] = 'l'; break;
786 case RTFS_TYPE_SOCKET: szMode[0] = 's'; break;
787 case RTFS_TYPE_WHITEOUT: szMode[0] = 'w'; break;
788 default: szMode[0] = '?'; break;
789 }
790 if (pszLinkType && szMode[0] != 's')
791 szMode[0] = 'h';
792
793 szMode[1] = UnixInfo.Attr.fMode & RTFS_UNIX_IRUSR ? 'r' : '-';
794 szMode[2] = UnixInfo.Attr.fMode & RTFS_UNIX_IWUSR ? 'w' : '-';
795 szMode[3] = UnixInfo.Attr.fMode & RTFS_UNIX_IXUSR ? 'x' : '-';
796
797 szMode[4] = UnixInfo.Attr.fMode & RTFS_UNIX_IRGRP ? 'r' : '-';
798 szMode[5] = UnixInfo.Attr.fMode & RTFS_UNIX_IWGRP ? 'w' : '-';
799 szMode[6] = UnixInfo.Attr.fMode & RTFS_UNIX_IXGRP ? 'x' : '-';
800
801 szMode[7] = UnixInfo.Attr.fMode & RTFS_UNIX_IROTH ? 'r' : '-';
802 szMode[8] = UnixInfo.Attr.fMode & RTFS_UNIX_IWOTH ? 'w' : '-';
803 szMode[9] = UnixInfo.Attr.fMode & RTFS_UNIX_IXOTH ? 'x' : '-';
804 szMode[10] = '\0';
805
806 /** @todo sticky and set-uid/gid bits. */
807
808 /*
809 * Make sure we've got valid owner and group strings.
810 */
811 if (!Owner.Attr.u.UnixGroup.szName[0])
812 RTStrPrintf(Owner.Attr.u.UnixOwner.szName, sizeof(Owner.Attr.u.UnixOwner.szName),
813 "%u", UnixInfo.Attr.u.Unix.uid);
814
815 if (!Group.Attr.u.UnixOwner.szName[0])
816 RTStrPrintf(Group.Attr.u.UnixGroup.szName, sizeof(Group.Attr.u.UnixGroup.szName),
817 "%u", UnixInfo.Attr.u.Unix.gid);
818
819 /*
820 * Format the modification time.
821 */
822 char szModTime[32];
823 RTTIME ModTime;
824 PRTTIME pTime;
825 if (!pOpts->fDisplayUtc)
826 pTime = RTTimeLocalExplode(&ModTime, &UnixInfo.ModificationTime);
827 else
828 pTime = RTTimeExplode(&ModTime, &UnixInfo.ModificationTime);
829 if (!pTime)
830 RT_ZERO(ModTime);
831 RTStrPrintf(szModTime, sizeof(szModTime), "%04d-%02u-%02u %02u:%02u",
832 ModTime.i32Year, ModTime.u8Month, ModTime.u8MonthDay, ModTime.u8Hour, ModTime.u8Minute);
833
834 /*
835 * Format the size and figure how much space is needed between the
836 * user/group and the size.
837 */
838 char szSize[64];
839 size_t cchSize;
840 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
841 {
842 case RTFS_TYPE_DEV_CHAR:
843 case RTFS_TYPE_DEV_BLOCK:
844 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%u,%u",
845 RTDEV_MAJOR(UnixInfo.Attr.u.Unix.Device), RTDEV_MINOR(UnixInfo.Attr.u.Unix.Device));
846 break;
847 default:
848 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%RU64", UnixInfo.cbObject);
849 break;
850 }
851
852 size_t cchUserGroup = strlen(Owner.Attr.u.UnixOwner.szName)
853 + 1
854 + strlen(Group.Attr.u.UnixGroup.szName);
855 ssize_t cchPad = cchUserGroup + cchSize + 1 < 19
856 ? 19 - (cchUserGroup + cchSize + 1)
857 : 0;
858
859 /*
860 * Go to press.
861 */
862 if (pszLinkType)
863 RTPrintf("%s %s/%s%*s %s %s %s %s %s\n",
864 szMode,
865 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
866 cchPad, "",
867 szSize,
868 szModTime,
869 pszName,
870 pszLinkType,
871 szTarget);
872 else
873 RTPrintf("%s %s/%s%*s %s %s %s\n",
874 szMode,
875 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
876 cchPad, "",
877 szSize,
878 szModTime,
879 pszName);
880
881 return rcExit;
882}
883
884
885/**
886 * Display usage.
887 *
888 * @param pszProgName The program name.
889 */
890static void rtZipTarUsage(const char *pszProgName)
891{
892 /*
893 * 0 1 2 3 4 5 6 7 8
894 * 012345678901234567890123456789012345678901234567890123456789012345678901234567890
895 */
896 RTPrintf("Usage: %s [options]\n"
897 "\n",
898 pszProgName);
899 RTPrintf("Operations:\n"
900 " -A, --concatenate, --catenate\n"
901 " Append the content of one tar archive to another. (not impl)\n"
902 " -c, --create\n"
903 " Create a new tar archive. (not impl)\n"
904 " -d, --diff, --compare\n"
905 " Compare atar archive with the file system. (not impl)\n"
906 " -r, --append\n"
907 " Append more files to the tar archive. (not impl)\n"
908 " -t, --list\n"
909 " List the contents of the tar archive.\n"
910 " -u, --update\n"
911 " Update the archive, adding files that are newer than the\n"
912 " ones in the archive. (not impl)\n"
913 " -x, --extract, --get\n"
914 " Extract the files from the tar archive.\n"
915 " --delete\n"
916 " Delete files from the tar archive.\n"
917 "\n"
918 );
919 RTPrintf("Basic Options:\n"
920 " -C <dir>, --directory <dir> (-A, -C, -d, -r, -u, -x)\n"
921 " Sets the base directory for input and output file members.\n"
922 " This does not apply to --file, even if it preceeds it.\n"
923 " -f <archive>, --file <archive> (all)\n"
924 " The tar file to create or process. '-' indicates stdout/stdin,\n"
925 " which is is the default.\n"
926 " -v, --verbose (all)\n"
927 " Verbose operation.\n"
928 " -p, --preserve-permissions (-x)\n"
929 " Preserve all permissions when extracting. Must be used\n"
930 " before the mode mask options as it will change some of these.\n"
931 " -j, --bzip2 (all)\n"
932 " Compress/decompress the archive with bzip2.\n"
933 " -z, --gzip, --gunzip, --ungzip (all)\n"
934 " Compress/decompress the archive with gzip.\n"
935 "\n");
936 RTPrintf("Misc Options:\n"
937 " --owner <uid/username> (-A, -C, -d, -r, -u, -x)\n"
938 " Set the owner of extracted and archived files to the user specified.\n"
939 " --group <uid/username> (-A, -C, -d, -r, -u, -x)\n"
940 " Set the group of extracted and archived files to the group specified.\n"
941 " --utc (-t)\n"
942 " Display timestamps as UTC instead of local time.\n"
943 "\n");
944 RTPrintf("IPRT Options:\n"
945 " --prefix <dir-prefix> (-A, -C, -d, -r, -u)\n"
946 " Directory prefix to give the members added to the archive.\n"
947 " --file-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
948 " Restrict the access mode of regular and special files.\n"
949 " --file-mode-or-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
950 " Include the given access mode for regular and special files.\n"
951 " --dir-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
952 " Restrict the access mode of directories.\n"
953 " --dir-mode-or-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
954 " Include the given access mode for directories.\n"
955 " --read-ahead (-x)\n"
956 " Enabled read ahead thread when extracting files.\n"
957 "\n");
958 RTPrintf("Standard Options:\n"
959 " -h, -?, --help\n"
960 " Display this help text.\n"
961 " -V, --version\n"
962 " Display version number.\n");
963}
964
965
966RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
967{
968 /*
969 * Parse the command line.
970 *
971 * N.B. This is less flexible that your regular tar program in that it
972 * requires the operation to be specified as an option. On the other
973 * hand, you can specify it where ever you like in the command line.
974 */
975 static const RTGETOPTDEF s_aOptions[] =
976 {
977 /* operations */
978 { "--concatenate", 'A', RTGETOPT_REQ_NOTHING },
979 { "--catenate", 'A', RTGETOPT_REQ_NOTHING },
980 { "--create", 'c', RTGETOPT_REQ_NOTHING },
981 { "--diff", 'd', RTGETOPT_REQ_NOTHING },
982 { "--compare", 'd', RTGETOPT_REQ_NOTHING },
983 { "--append", 'r', RTGETOPT_REQ_NOTHING },
984 { "--list", 't', RTGETOPT_REQ_NOTHING },
985 { "--update", 'u', RTGETOPT_REQ_NOTHING },
986 { "--extract", 'x', RTGETOPT_REQ_NOTHING },
987 { "--get", 'x', RTGETOPT_REQ_NOTHING },
988 { "--delete", RTZIPTARCMD_OPT_DELETE, RTGETOPT_REQ_NOTHING },
989
990 /* basic options */
991 { "--directory", 'C', RTGETOPT_REQ_STRING },
992 { "--file", 'f', RTGETOPT_REQ_STRING },
993 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
994 { "--preserve-permissions", 'p', RTGETOPT_REQ_NOTHING },
995 { "--bzip2", 'j', RTGETOPT_REQ_NOTHING },
996 { "--gzip", 'z', RTGETOPT_REQ_NOTHING },
997 { "--gunzip", 'z', RTGETOPT_REQ_NOTHING },
998 { "--ungzip", 'z', RTGETOPT_REQ_NOTHING },
999
1000 /* other options. */
1001 { "--owner", RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
1002 { "--group", RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
1003 { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
1004
1005 /* IPRT extensions */
1006 { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
1007 { "--file-mode-and-mask", RTZIPTARCMD_OPT_FILE_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1008 { "--file-mode-or-mask", RTZIPTARCMD_OPT_FILE_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1009 { "--dir-mode-and-mask", RTZIPTARCMD_OPT_DIR_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1010 { "--dir-mode-or-mask", RTZIPTARCMD_OPT_DIR_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
1011 { "--format", RTZIPTARCMD_OPT_FORMAT, RTGETOPT_REQ_STRING },
1012 { "--read-ahead", RTZIPTARCMD_OPT_READ_AHEAD, RTGETOPT_REQ_NOTHING },
1013 };
1014
1015 RTGETOPTSTATE GetState;
1016 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1017 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1018 if (RT_FAILURE(rc))
1019 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
1020
1021 RTZIPTARCMDOPS Opts;
1022 RT_ZERO(Opts);
1023 Opts.enmFormat = RTZIPTARFORMAT_AUTO_DEFAULT;
1024 Opts.uidOwner = NIL_RTUID;
1025 Opts.gidGroup = NIL_RTUID;
1026 Opts.fFileModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
1027 Opts.fDirModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
1028#if 0
1029 if (RTPermIsSuperUser())
1030 {
1031 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1032 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1033 Opts.fPreserveOwner = true;
1034 Opts.fPreserveGroup = true;
1035 }
1036#endif
1037
1038 RTGETOPTUNION ValueUnion;
1039 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
1040 && rc != VINF_GETOPT_NOT_OPTION)
1041 {
1042 switch (rc)
1043 {
1044 /* operations */
1045 case 'A':
1046 case 'c':
1047 case 'd':
1048 case 'r':
1049 case 't':
1050 case 'u':
1051 case 'x':
1052 case RTZIPTARCMD_OPT_DELETE:
1053 if (Opts.iOperation)
1054 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Conflicting tar operation (%s already set, now %s)",
1055 Opts.pszOperation, ValueUnion.pDef->pszLong);
1056 Opts.iOperation = rc;
1057 Opts.pszOperation = ValueUnion.pDef->pszLong;
1058 break;
1059
1060 /* basic options */
1061 case 'C':
1062 if (Opts.pszDirectory)
1063 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -C/--directory once");
1064 Opts.pszDirectory = ValueUnion.psz;
1065 break;
1066
1067 case 'f':
1068 if (Opts.pszFile)
1069 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -f/--file once");
1070 Opts.pszFile = ValueUnion.psz;
1071 break;
1072
1073 case 'v':
1074 Opts.fVerbose = true;
1075 break;
1076
1077 case 'p':
1078 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1079 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1080 Opts.fPreserveOwner = true;
1081 Opts.fPreserveGroup = true;
1082 break;
1083
1084 case 'j':
1085 case 'z':
1086 if (Opts.chZipper)
1087 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify one compressor / decompressor");
1088 Opts.chZipper = rc;
1089 break;
1090
1091 case RTZIPTARCMD_OPT_OWNER:
1092 if (Opts.pszOwner)
1093 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --owner once");
1094 Opts.pszOwner = ValueUnion.psz;
1095
1096 rc = RTStrToUInt32Full(Opts.pszOwner, 0, &ValueUnion.u32);
1097 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1098 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1099 "Error convering --owner '%s' into a number: %Rrc", Opts.pszOwner, rc);
1100 if (RT_SUCCESS(rc))
1101 {
1102 Opts.uidOwner = ValueUnion.u32;
1103 Opts.pszOwner = NULL;
1104 }
1105 break;
1106
1107 case RTZIPTARCMD_OPT_GROUP:
1108 if (Opts.pszGroup)
1109 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --group once");
1110 Opts.pszGroup = ValueUnion.psz;
1111
1112 rc = RTStrToUInt32Full(Opts.pszGroup, 0, &ValueUnion.u32);
1113 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1114 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1115 "Error convering --group '%s' into a number: %Rrc", Opts.pszGroup, rc);
1116 if (RT_SUCCESS(rc))
1117 {
1118 Opts.gidGroup = ValueUnion.u32;
1119 Opts.pszGroup = NULL;
1120 }
1121 break;
1122
1123 case RTZIPTARCMD_OPT_UTC:
1124 Opts.fDisplayUtc = true;
1125 break;
1126
1127 /* iprt extensions */
1128 case RTZIPTARCMD_OPT_PREFIX:
1129 if (Opts.pszPrefix)
1130 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --prefix once");
1131 Opts.pszPrefix = ValueUnion.psz;
1132 break;
1133
1134 case RTZIPTARCMD_OPT_FILE_MODE_AND_MASK:
1135 Opts.fFileModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1136 break;
1137
1138 case RTZIPTARCMD_OPT_FILE_MODE_OR_MASK:
1139 Opts.fFileModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1140 break;
1141
1142 case RTZIPTARCMD_OPT_DIR_MODE_AND_MASK:
1143 Opts.fDirModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1144 break;
1145
1146 case RTZIPTARCMD_OPT_DIR_MODE_OR_MASK:
1147 Opts.fDirModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1148 break;
1149
1150 case RTZIPTARCMD_OPT_FORMAT:
1151 if (!strcmp(ValueUnion.psz, "auto") || !strcmp(ValueUnion.psz, "default"))
1152 Opts.enmFormat = RTZIPTARFORMAT_AUTO_DEFAULT;
1153 else if (!strcmp(ValueUnion.psz, "tar"))
1154 Opts.enmFormat = RTZIPTARFORMAT_TAR;
1155 else if (!strcmp(ValueUnion.psz, "xar"))
1156 Opts.enmFormat = RTZIPTARFORMAT_XAR;
1157 else
1158 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
1159 break;
1160
1161 case RTZIPTARCMD_OPT_READ_AHEAD:
1162 Opts.fReadAhead = true;
1163 break;
1164
1165 /* Standard bits. */
1166 case 'h':
1167 rtZipTarUsage(RTPathFilename(papszArgs[0]));
1168 return RTEXITCODE_SUCCESS;
1169
1170 case 'V':
1171 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1172 return RTEXITCODE_SUCCESS;
1173
1174 default:
1175 return RTGetOptPrintError(rc, &ValueUnion);
1176 }
1177 }
1178
1179 if (rc == VINF_GETOPT_NOT_OPTION)
1180 {
1181 /* this is kind of ugly. */
1182 Assert((unsigned)GetState.iNext - 1 <= cArgs);
1183 Opts.papszFiles = (const char * const *)&papszArgs[GetState.iNext - 1];
1184 Opts.cFiles = cArgs - GetState.iNext + 1;
1185 }
1186
1187 /*
1188 * Post proceess the options.
1189 */
1190 if (Opts.iOperation == 0)
1191 {
1192 Opts.iOperation = 't';
1193 Opts.pszOperation = "--list";
1194 }
1195
1196 if ( Opts.iOperation == 'x'
1197 && Opts.pszOwner)
1198 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --owner with %s has not implemented yet", Opts.pszOperation);
1199
1200 if ( Opts.iOperation == 'x'
1201 && Opts.pszGroup)
1202 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --group with %s has not implemented yet", Opts.pszOperation);
1203
1204 /*
1205 * Do the job.
1206 */
1207 switch (Opts.iOperation)
1208 {
1209 case 't':
1210 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdListCallback);
1211
1212 case 'x':
1213 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
1214
1215 case 'A':
1216 case 'c':
1217 case 'd':
1218 case 'r':
1219 case 'u':
1220 case RTZIPTARCMD_OPT_DELETE:
1221 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The operation %s is not implemented yet", Opts.pszOperation);
1222
1223 default:
1224 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Internal error");
1225 }
1226}
1227
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