VirtualBox

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

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

RTTar: Implemented simple extraction (-x).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.5 KB
Line 
1/* $Id: tarcmd.cpp 44299 2013-01-15 14:58:38Z 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.215389.xyz. 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 (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode)) /* RTPathSetMode follows symbolic links atm. */
648 {
649 RTFMODE fMode;
650 if (RTFS_IS_DIRECTORY(UnixInfo.Attr.fMode))
651 fMode = (UnixInfo.Attr.fMode & (pOpts->fDirModeAndMask | RTFS_TYPE_MASK)) | pOpts->fDirModeOrMask;
652 else
653 fMode = (UnixInfo.Attr.fMode & (pOpts->fFileModeAndMask | RTFS_TYPE_MASK)) | pOpts->fFileModeOrMask;
654 rc = RTPathSetMode(szDst, fMode);
655 if (RT_FAILURE(rc))
656 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", szDst, rc);
657 }
658
659 return rcExit;
660}
661
662
663/**
664 * @callback_method_impl{PFNDOWITHMEMBER, Implements --list.}
665 */
666static RTEXITCODE rtZipTarCmdListCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
667{
668 /*
669 * This is very simple in non-verbose mode.
670 */
671 if (!pOpts->fVerbose)
672 {
673 RTPrintf("%s\n", pszName);
674 return rcExit;
675 }
676
677 /*
678 * Query all the information.
679 */
680 RTFSOBJINFO UnixInfo;
681 int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
682 if (RT_FAILURE(rc))
683 {
684 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
685 RT_ZERO(UnixInfo);
686 }
687
688 RTFSOBJINFO Owner;
689 rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
690 if (RT_FAILURE(rc))
691 {
692 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
693 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
694 rc, pszName);
695 RT_ZERO(Owner);
696 }
697
698 RTFSOBJINFO Group;
699 rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
700 if (RT_FAILURE(rc))
701 {
702 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
703 "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
704 rc, pszName);
705 RT_ZERO(Group);
706 }
707
708 const char *pszLinkType = NULL;
709 char szTarget[RTPATH_MAX];
710 szTarget[0] = '\0';
711 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
712 if (hVfsSymlink != NIL_RTVFSSYMLINK)
713 {
714 rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
715 if (RT_FAILURE(rc))
716 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsSymlinkRead returned %Rrc on '%s'", rc, pszName);
717 RTVfsSymlinkRelease(hVfsSymlink);
718 pszLinkType = RTFS_IS_SYMLINK(UnixInfo.Attr.fMode) ? "->" : "link to";
719 }
720 else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
721 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
722
723 /*
724 * Translate the mode mask.
725 */
726 char szMode[16];
727 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
728 {
729 case RTFS_TYPE_FIFO: szMode[0] = 'f'; break;
730 case RTFS_TYPE_DEV_CHAR: szMode[0] = 'c'; break;
731 case RTFS_TYPE_DIRECTORY: szMode[0] = 'd'; break;
732 case RTFS_TYPE_DEV_BLOCK: szMode[0] = 'b'; break;
733 case RTFS_TYPE_FILE: szMode[0] = '-'; break;
734 case RTFS_TYPE_SYMLINK: szMode[0] = 'l'; break;
735 case RTFS_TYPE_SOCKET: szMode[0] = 's'; break;
736 case RTFS_TYPE_WHITEOUT: szMode[0] = 'w'; break;
737 default: szMode[0] = '?'; break;
738 }
739 if (pszLinkType && szMode[0] != 's')
740 szMode[0] = 'h';
741
742 szMode[1] = UnixInfo.Attr.fMode & RTFS_UNIX_IRUSR ? 'r' : '-';
743 szMode[2] = UnixInfo.Attr.fMode & RTFS_UNIX_IWUSR ? 'w' : '-';
744 szMode[3] = UnixInfo.Attr.fMode & RTFS_UNIX_IXUSR ? 'x' : '-';
745
746 szMode[4] = UnixInfo.Attr.fMode & RTFS_UNIX_IRGRP ? 'r' : '-';
747 szMode[5] = UnixInfo.Attr.fMode & RTFS_UNIX_IWGRP ? 'w' : '-';
748 szMode[6] = UnixInfo.Attr.fMode & RTFS_UNIX_IXGRP ? 'x' : '-';
749
750 szMode[7] = UnixInfo.Attr.fMode & RTFS_UNIX_IROTH ? 'r' : '-';
751 szMode[8] = UnixInfo.Attr.fMode & RTFS_UNIX_IWOTH ? 'w' : '-';
752 szMode[9] = UnixInfo.Attr.fMode & RTFS_UNIX_IXOTH ? 'x' : '-';
753 szMode[10] = '\0';
754
755 /** @todo sticky and set-uid/gid bits. */
756
757 /*
758 * Make sure we've got valid owner and group strings.
759 */
760 if (!Owner.Attr.u.UnixGroup.szName[0])
761 RTStrPrintf(Owner.Attr.u.UnixOwner.szName, sizeof(Owner.Attr.u.UnixOwner.szName),
762 "%u", UnixInfo.Attr.u.Unix.uid);
763
764 if (!Group.Attr.u.UnixOwner.szName[0])
765 RTStrPrintf(Group.Attr.u.UnixGroup.szName, sizeof(Group.Attr.u.UnixGroup.szName),
766 "%u", UnixInfo.Attr.u.Unix.gid);
767
768 /*
769 * Format the modification time.
770 */
771 char szModTime[32];
772 RTTIME ModTime;
773 PRTTIME pTime;
774 if (!pOpts->fDisplayUtc)
775 pTime = RTTimeLocalExplode(&ModTime, &UnixInfo.ModificationTime);
776 else
777 pTime = RTTimeExplode(&ModTime, &UnixInfo.ModificationTime);
778 if (!pTime)
779 RT_ZERO(ModTime);
780 RTStrPrintf(szModTime, sizeof(szModTime), "%04d-%02u-%02u %02u:%02u",
781 ModTime.i32Year, ModTime.u8Month, ModTime.u8MonthDay, ModTime.u8Hour, ModTime.u8Minute);
782
783 /*
784 * Format the size and figure how much space is needed between the
785 * user/group and the size.
786 */
787 char szSize[64];
788 size_t cchSize;
789 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
790 {
791 case RTFS_TYPE_DEV_CHAR:
792 case RTFS_TYPE_DEV_BLOCK:
793 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%u,%u",
794 RTDEV_MAJOR(UnixInfo.Attr.u.Unix.Device), RTDEV_MINOR(UnixInfo.Attr.u.Unix.Device));
795 break;
796 default:
797 cchSize = RTStrPrintf(szSize, sizeof(szSize), "%RU64", UnixInfo.cbObject);
798 break;
799 }
800
801 size_t cchUserGroup = strlen(Owner.Attr.u.UnixOwner.szName)
802 + 1
803 + strlen(Group.Attr.u.UnixGroup.szName);
804 ssize_t cchPad = cchUserGroup + cchSize + 1 < 19
805 ? 19 - (cchUserGroup + cchSize + 1)
806 : 0;
807
808 /*
809 * Go to press.
810 */
811 if (pszLinkType)
812 RTPrintf("%s %s/%s%*s %s %s %s %s %s\n",
813 szMode,
814 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
815 cchPad, "",
816 szSize,
817 szModTime,
818 pszName,
819 pszLinkType,
820 szTarget);
821 else
822 RTPrintf("%s %s/%s%*s %s %s %s\n",
823 szMode,
824 Owner.Attr.u.UnixOwner.szName, Group.Attr.u.UnixGroup.szName,
825 cchPad, "",
826 szSize,
827 szModTime,
828 pszName);
829
830 return rcExit;
831}
832
833
834/**
835 * Display usage.
836 *
837 * @param pszProgName The program name.
838 */
839static void rtZipTarUsage(const char *pszProgName)
840{
841 /*
842 * 0 1 2 3 4 5 6 7 8
843 * 012345678901234567890123456789012345678901234567890123456789012345678901234567890
844 */
845 RTPrintf("Usage: %s [options]\n"
846 "\n",
847 pszProgName);
848 RTPrintf("Operations:\n"
849 " -A, --concatenate, --catenate\n"
850 " Append the content of one tar archive to another. (not impl)\n"
851 " -c, --create\n"
852 " Create a new tar archive. (not impl)\n"
853 " -d, --diff, --compare\n"
854 " Compare atar archive with the file system. (not impl)\n"
855 " -r, --append\n"
856 " Append more files to the tar archive. (not impl)\n"
857 " -t, --list\n"
858 " List the contents of the tar archive.\n"
859 " -u, --update\n"
860 " Update the archive, adding files that are newer than the\n"
861 " ones in the archive. (not impl)\n"
862 " -x, --extract, --get\n"
863 " Extract the files from the tar archive.\n"
864 " --delete\n"
865 " Delete files from the tar archive.\n"
866 "\n"
867 );
868 RTPrintf("Basic Options:\n"
869 " -C <dir>, --directory <dir> (-A, -C, -d, -r, -u, -x)\n"
870 " Sets the base directory for input and output file members.\n"
871 " This does not apply to --file, even if it preceeds it.\n"
872 " -f <archive>, --file <archive> (all)\n"
873 " The tar file to create or process. '-' indicates stdout/stdin,\n"
874 " which is is the default.\n"
875 " -v, --verbose (all)\n"
876 " Verbose operation.\n"
877 " -p, --preserve-permissions (-x)\n"
878 " Preserve all permissions when extracting. Must be used\n"
879 " before the mode mask options as it will change some of these.\n"
880 " -j, --bzip2 (all)\n"
881 " Compress/decompress the archive with bzip2.\n"
882 " -z, --gzip, --gunzip, --ungzip (all)\n"
883 " Compress/decompress the archive with gzip.\n"
884 "\n");
885 RTPrintf("Misc Options:\n"
886 " --owner <uid/username> (-A, -C, -d, -r, -u, -x)\n"
887 " Set the owner of extracted and archived files to the user specified.\n"
888 " --group <uid/username> (-A, -C, -d, -r, -u, -x)\n"
889 " Set the group of extracted and archived files to the group specified.\n"
890 " --utc (-t)\n"
891 " Display timestamps as UTC instead of local time.\n"
892 "\n");
893 RTPrintf("IPRT Options:\n"
894 " --prefix <dir-prefix> (-A, -C, -d, -r, -u)\n"
895 " Directory prefix to give the members added to the archive.\n"
896 " --file-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
897 " Restrict the access mode of regular and special files.\n"
898 " --file-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
899 " Include the given access mode for regular and special files.\n"
900 " --dir-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
901 " Restrict the access mode of directories.\n"
902 " --dir-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
903 " Include the given access mode for directories.\n"
904 "\n");
905 RTPrintf("Standard Options:\n"
906 " -h, -?, --help\n"
907 " Display this help text.\n"
908 " -V, --version\n"
909 " Display version number.\n");
910}
911
912RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
913{
914 /*
915 * Parse the command line.
916 *
917 * N.B. This is less flexible that your regular tar program in that it
918 * requires the operation to be specified as an option. On the other
919 * hand, you can specify it where ever you like in the command line.
920 */
921 static const RTGETOPTDEF s_aOptions[] =
922 {
923 /* operations */
924 { "--concatenate", 'A', RTGETOPT_REQ_NOTHING },
925 { "--catenate", 'A', RTGETOPT_REQ_NOTHING },
926 { "--create", 'c', RTGETOPT_REQ_NOTHING },
927 { "--diff", 'd', RTGETOPT_REQ_NOTHING },
928 { "--compare", 'd', RTGETOPT_REQ_NOTHING },
929 { "--append", 'r', RTGETOPT_REQ_NOTHING },
930 { "--list", 't', RTGETOPT_REQ_NOTHING },
931 { "--update", 'u', RTGETOPT_REQ_NOTHING },
932 { "--extract", 'x', RTGETOPT_REQ_NOTHING },
933 { "--get", 'x', RTGETOPT_REQ_NOTHING },
934 { "--delete", RTZIPTARCMD_OPT_DELETE, RTGETOPT_REQ_NOTHING },
935
936 /* basic options */
937 { "--directory", 'C', RTGETOPT_REQ_STRING },
938 { "--file", 'f', RTGETOPT_REQ_STRING },
939 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
940 { "--preserve-permissions", 'p', RTGETOPT_REQ_NOTHING },
941 { "--bzip2", 'j', RTGETOPT_REQ_NOTHING },
942 { "--gzip", 'z', RTGETOPT_REQ_NOTHING },
943 { "--gunzip", 'z', RTGETOPT_REQ_NOTHING },
944 { "--ungzip", 'z', RTGETOPT_REQ_NOTHING },
945
946 /* other options. */
947 { "--owner", RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
948 { "--group", RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
949 { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
950
951 /* IPRT extensions */
952 { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
953 { "--file-mode-and-mask", RTZIPTARCMD_OPT_FILE_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
954 { "--file-mode-or-mask", RTZIPTARCMD_OPT_FILE_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
955 { "--dir-mode-and-mask", RTZIPTARCMD_OPT_DIR_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
956 { "--dir-mode-or-mask", RTZIPTARCMD_OPT_DIR_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
957 };
958
959 RTGETOPTSTATE GetState;
960 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
961 RTGETOPTINIT_FLAGS_OPTS_FIRST);
962 if (RT_FAILURE(rc))
963 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
964
965 RTZIPTARCMDOPS Opts;
966 RT_ZERO(Opts);
967 Opts.uidOwner = NIL_RTUID;
968 Opts.gidGroup = NIL_RTUID;
969 Opts.fFileModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
970 Opts.fDirModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
971#if 0
972 if (RTPermIsSuperUser())
973 {
974 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
975 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
976 Opts.fPreserveOwner = true;
977 Opts.fPreserveGroup = true;
978 }
979#endif
980
981 RTGETOPTUNION ValueUnion;
982 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
983 && rc != VINF_GETOPT_NOT_OPTION)
984 {
985 switch (rc)
986 {
987 /* operations */
988 case 'A':
989 case 'c':
990 case 'd':
991 case 'r':
992 case 't':
993 case 'u':
994 case 'x':
995 case RTZIPTARCMD_OPT_DELETE:
996 if (Opts.iOperation)
997 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Conflicting tar operation (%s already set, now %s)",
998 Opts.pszOperation, ValueUnion.pDef->pszLong);
999 Opts.iOperation = rc;
1000 Opts.pszOperation = ValueUnion.pDef->pszLong;
1001 break;
1002
1003 /* basic options */
1004 case 'C':
1005 if (Opts.pszDirectory)
1006 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -C/--directory once");
1007 Opts.pszDirectory = ValueUnion.psz;
1008 break;
1009
1010 case 'f':
1011 if (Opts.pszFile)
1012 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -f/--file once");
1013 Opts.pszFile = ValueUnion.psz;
1014 break;
1015
1016 case 'v':
1017 Opts.fVerbose = true;
1018 break;
1019
1020 case 'p':
1021 Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
1022 Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
1023 Opts.fPreserveOwner = true;
1024 Opts.fPreserveGroup = true;
1025 break;
1026
1027 case 'j':
1028 case 'z':
1029 if (Opts.chZipper)
1030 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify one compressor / decompressor");
1031 Opts.chZipper = rc;
1032 break;
1033
1034 case RTZIPTARCMD_OPT_OWNER:
1035 if (Opts.pszOwner)
1036 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --owner once");
1037 Opts.pszOwner = ValueUnion.psz;
1038
1039 rc = RTStrToUInt32Full(Opts.pszOwner, 0, &ValueUnion.u32);
1040 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1041 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1042 "Error convering --owner '%s' into a number: %Rrc", Opts.pszOwner, rc);
1043 if (RT_SUCCESS(rc))
1044 {
1045 Opts.uidOwner = ValueUnion.u32;
1046 Opts.pszOwner = NULL;
1047 }
1048 break;
1049
1050 case RTZIPTARCMD_OPT_GROUP:
1051 if (Opts.pszGroup)
1052 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --group once");
1053 Opts.pszGroup = ValueUnion.psz;
1054
1055 rc = RTStrToUInt32Full(Opts.pszGroup, 0, &ValueUnion.u32);
1056 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
1057 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
1058 "Error convering --group '%s' into a number: %Rrc", Opts.pszGroup, rc);
1059 if (RT_SUCCESS(rc))
1060 {
1061 Opts.gidGroup = ValueUnion.u32;
1062 Opts.pszGroup = NULL;
1063 }
1064 break;
1065
1066 case RTZIPTARCMD_OPT_UTC:
1067 Opts.fDisplayUtc = true;
1068 break;
1069
1070 /* iprt extensions */
1071 case RTZIPTARCMD_OPT_PREFIX:
1072 if (Opts.pszPrefix)
1073 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --prefix once");
1074 Opts.pszPrefix = ValueUnion.psz;
1075 break;
1076
1077 case RTZIPTARCMD_OPT_FILE_MODE_AND_MASK:
1078 Opts.fFileModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1079 break;
1080
1081 case RTZIPTARCMD_OPT_FILE_MODE_OR_MASK:
1082 Opts.fFileModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1083 break;
1084
1085 case RTZIPTARCMD_OPT_DIR_MODE_AND_MASK:
1086 Opts.fDirModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1087 break;
1088
1089 case RTZIPTARCMD_OPT_DIR_MODE_OR_MASK:
1090 Opts.fDirModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
1091 break;
1092
1093
1094 /* Standard bits. */
1095 case 'h':
1096 rtZipTarUsage(RTPathFilename(papszArgs[0]));
1097 return RTEXITCODE_SUCCESS;
1098
1099 case 'V':
1100 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1101 return RTEXITCODE_SUCCESS;
1102
1103 default:
1104 return RTGetOptPrintError(rc, &ValueUnion);
1105 }
1106 }
1107
1108 if (rc == VINF_GETOPT_NOT_OPTION)
1109 {
1110 /* this is kind of ugly. */
1111 Assert((unsigned)GetState.iNext - 1 <= cArgs);
1112 Opts.papszFiles = (const char * const *)&papszArgs[GetState.iNext - 1];
1113 Opts.cFiles = cArgs - GetState.iNext + 1;
1114 }
1115
1116 /*
1117 * Post proceess the options.
1118 */
1119 if (Opts.iOperation == 0)
1120 {
1121 Opts.iOperation = 't';
1122 Opts.pszOperation = "--list";
1123 }
1124
1125 if ( Opts.iOperation == 'x'
1126 && Opts.pszOwner)
1127 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --owner with %s has not implemented yet", Opts.pszOperation);
1128
1129 if ( Opts.iOperation == 'x'
1130 && Opts.pszGroup)
1131 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The use of --group with %s has not implemented yet", Opts.pszOperation);
1132
1133 /*
1134 * Do the job.
1135 */
1136 switch (Opts.iOperation)
1137 {
1138 case 't':
1139 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdListCallback);
1140
1141 case 'x':
1142 return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
1143
1144 case 'A':
1145 case 'c':
1146 case 'd':
1147 case 'r':
1148 case 'u':
1149 case RTZIPTARCMD_OPT_DELETE:
1150 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The operation %s is not implemented yet", Opts.pszOperation);
1151
1152 default:
1153 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Internal error");
1154 }
1155}
1156
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette