VirtualBox

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

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

Runtime/tarcmd: fixed a loop and a memory leak

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