VirtualBox

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

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

build fix

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