VirtualBox

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

Last change on this file since 48780 was 48780, checked in by vboxsync, 11 years ago

Started on a XAR file system stream similar to what we have for TAR already.

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