VirtualBox

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

Last change on this file since 47646 was 47359, checked in by vboxsync, 12 years ago

IPRT: Added compression to the gzip VFS I/O stream class (RTZipGzipCompressIoStream) and implemented compression in the RTGzip example tool (testcase\RTGzip.exe).

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