VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/tarvfswriter.cpp@ 68246

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

tarvfswriter: fixed regression

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 74.3 KB
Line 
1/* $Id: tarvfswriter.cpp 67279 2017-06-06 16:50:10Z vboxsync $ */
2/** @file
3 * IPRT - TAR Virtual Filesystem, Writer.
4 */
5
6/*
7 * Copyright (C) 2010-2017 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 "internal/iprt.h"
32#include <iprt/zip.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/err.h>
37#include <iprt/file.h>
38#include <iprt/mem.h>
39#include <iprt/path.h>
40#include <iprt/string.h>
41#include <iprt/vfs.h>
42#include <iprt/vfslowlevel.h>
43#include <iprt/zero.h>
44
45#include "tar.h"
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51/** The TAR block size we're using in this implementation.
52 * @remarks Should technically be user configurable, but we don't currently need that. */
53#define RTZIPTAR_BLOCKSIZE sizeof(RTZIPTARHDR)
54
55/** Minimum file size we consider for sparse files. */
56#define RTZIPTAR_MIN_SPARSE _64K
57
58
59/*********************************************************************************************************************************
60* Structures and Typedefs *
61*********************************************************************************************************************************/
62/**
63 * A data span descriptor in a sparse file.
64 */
65typedef struct RTZIPTARSPARSESPAN
66{
67 /** Byte offset into the file of the data. */
68 uint64_t off;
69 /** Number of bytes of data, rounded up to a multiple of blocksize. */
70 uint64_t cb;
71} RTZIPTARSPARSESPAN;
72/** Pointer to a data span. */
73typedef RTZIPTARSPARSESPAN *PRTZIPTARSPARSESPAN;
74/** Pointer to a const data span. */
75typedef RTZIPTARSPARSESPAN const *PCRTZIPTARSPARSESPAN;
76
77/**
78 * Chunk of TAR sparse file data spans.
79 */
80typedef struct RTZIPTARSPARSECHUNK
81{
82 /** List entry. */
83 RTLISTNODE Entry;
84 /** Array of data spans. */
85 RTZIPTARSPARSESPAN aSpans[63];
86} RTZIPTARSPARSECHUNK;
87AssertCompile(sizeof(RTZIPTARSPARSECHUNK) <= 1024);
88AssertCompile(sizeof(RTZIPTARSPARSECHUNK) >= 1008);
89/** Pointer to a chunk of TAR data spans. */
90typedef RTZIPTARSPARSECHUNK *PRTZIPTARSPARSECHUNK;
91/** Pointer to a const chunk of TAR data spans. */
92typedef RTZIPTARSPARSECHUNK const *PCRTZIPTARSPARSECHUNK;
93
94/**
95 * TAR sparse file info.
96 */
97typedef struct RTZIPTARSPARSE
98{
99 /** Number of data bytes (real size). */
100 uint64_t cbDataSpans;
101 /** Number of data spans. */
102 uint32_t cDataSpans;
103 /** The index of the next span in the tail chunk (to avoid modulus 63). */
104 uint32_t iNextSpan;
105 /** Head of the data span chunk list (PRTZIPTARSPARSECHUNK). */
106 RTLISTANCHOR ChunkHead;
107} RTZIPTARSPARSE;
108/** Pointer to TAR sparse file info. */
109typedef RTZIPTARSPARSE *PRTZIPTARSPARSE;
110/** Pointer to a const TAR sparse file info. */
111typedef RTZIPTARSPARSE const *PCRTZIPTARSPARSE;
112
113
114/** Pointer to a the private data of a TAR filesystem stream. */
115typedef struct RTZIPTARFSSTREAMWRITER *PRTZIPTARFSSTREAMWRITER;
116
117
118/**
119 * Instance data for a file or I/O stream returned by
120 * RTVFSFSSTREAMOPS::pfnPushFile.
121 */
122typedef struct RTZIPTARFSSTREAMWRITERPUSH
123{
124 /** Pointer to the parent FS stream writer instance.
125 * This is set to NULL should the push object live longer than the stream. */
126 PRTZIPTARFSSTREAMWRITER pParent;
127 /** The header offset, UINT64_MAX if non-seekable output. */
128 uint64_t offHdr;
129 /** The data offset, UINT64_MAX if non-seekable output. */
130 uint64_t offData;
131 /** The current I/O stream position (relative to offData). */
132 uint64_t offCurrent;
133 /** The expected size amount of file content, or max file size if open-ended. */
134 uint64_t cbExpected;
135 /** The current amount of file content written. */
136 uint64_t cbCurrent;
137 /** Object info copy for rtZipTarWriterPush_QueryInfo. */
138 RTFSOBJINFO ObjInfo;
139 /** Set if open-ended file size requiring a tar header update when done. */
140 bool fOpenEnded;
141} RTZIPTARFSSTREAMWRITERPUSH;
142/** Pointer to a push I/O instance. */
143typedef RTZIPTARFSSTREAMWRITERPUSH *PRTZIPTARFSSTREAMWRITERPUSH;
144
145
146/**
147 * Tar filesystem stream private data.
148 */
149typedef struct RTZIPTARFSSTREAMWRITER
150{
151 /** The output I/O stream. */
152 RTVFSIOSTREAM hVfsIos;
153 /** Non-nil if the output is a file. */
154 RTVFSFILE hVfsFile;
155
156 /** The current push file. NULL if none. */
157 PRTZIPTARFSSTREAMWRITERPUSH pPush;
158
159 /** The TAR format. */
160 RTZIPTARFORMAT enmFormat;
161 /** Set if we've encountered a fatal error. */
162 int rcFatal;
163 /** Flags. */
164 uint32_t fFlags;
165
166 /** Number of bytes written. */
167 uint64_t cbWritten;
168
169 /** @name Attribute overrides.
170 * @{
171 */
172 RTUID uidOwner; /**< Owner, NIL_RTUID if no change. */
173 char *pszOwner; /**< Owner, NULL if no change. */
174 RTGID gidGroup; /**< Group, NIL_RTGID if no change. */
175 char *pszGroup; /**< Group, NULL if no change. */
176 char *pszPrefix; /**< Path prefix, NULL if no change. */
177 size_t cchPrefix; /**< The length of pszPrefix. */
178 PRTTIMESPEC pModTime; /**< Modification time, NULL of no change. */
179 RTTIMESPEC ModTime; /**< pModTime points to this. */
180 RTFMODE fFileModeAndMask; /**< File mode AND mask. */
181 RTFMODE fFileModeOrMask; /**< File mode OR mask. */
182 RTFMODE fDirModeAndMask; /**< Directory mode AND mask. */
183 RTFMODE fDirModeOrMask; /**< Directory mode OR mask. */
184 /** @} */
185
186
187 /** Number of headers returned by rtZipTarFssWriter_ObjInfoToHdr. */
188 uint32_t cHdrs;
189 /** Header buffers returned by rtZipTarFssWriter_ObjInfoToHdr. */
190 RTZIPTARHDR aHdrs[3];
191} RTZIPTARFSSTREAMWRITER;
192
193
194/*********************************************************************************************************************************
195* Internal Functions *
196*********************************************************************************************************************************/
197static DECLCALLBACK(int) rtZipTarWriterPush_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual);
198static int rtZipTarFssWriter_CompleteCurrentPushFile(PRTZIPTARFSSTREAMWRITER pThis);
199static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
200 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm);
201
202
203/**
204 * Calculates the header checksum and stores it in the chksum field.
205 *
206 * @returns IPRT status code.
207 * @param pHdr The header.
208 */
209static int rtZipTarFssWriter_ChecksumHdr(PRTZIPTARHDR pHdr)
210{
211 int32_t iUnsignedChksum;
212 rtZipTarCalcChkSum(pHdr, &iUnsignedChksum, NULL);
213
214 int rc = RTStrFormatU32(pHdr->Common.chksum, sizeof(pHdr->Common.chksum), iUnsignedChksum,
215 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pHdr->Common.chksum) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
216 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
217 return VINF_SUCCESS;
218}
219
220
221
222/**
223 * Formats a 12 character wide file offset or size field.
224 *
225 * This is mainly used for RTZIPTARHDR::Common.size, but also for formatting the
226 * sparse map.
227 *
228 * @returns IPRT status code.
229 * @param pach12Field The 12 character wide destination field.
230 * @param off The offset to set.
231 */
232static int rtZipTarFssWriter_FormatOffset(char pach12Field[12], uint64_t off)
233{
234 /*
235 * Is the size small enough for the standard octal string encoding?
236 *
237 * Note! We could actually use the terminator character as well if we liked,
238 * but let not do that as it's easier to test this way.
239 */
240 if (off < _4G * 2U)
241 {
242 int rc = RTStrFormatU64(pach12Field, 12, off, 8 /*uBase*/, -1 /*cchWidth*/, 12 - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
243 AssertRCReturn(rc, rc);
244 }
245 /*
246 * No, use the base 256 extension. Set the highest bit of the left most
247 * character. We don't deal with negatives here, cause the size have to
248 * be greater than zero.
249 *
250 * Note! The base-256 extension are never used by gtar or libarchive
251 * with the "ustar \0" format version, only the later
252 * "ustar\000" version. However, this shouldn't cause much
253 * trouble as they are not picky about what they read.
254 */
255 /** @todo above note is wrong: GNU tar only uses base-256 with the GNU tar
256 * format, i.e. "ustar \0", see create.c line 303 in v1.29. */
257 else
258 {
259 size_t cchField = 12 - 1;
260 unsigned char *puchField = (unsigned char *)pach12Field;
261 puchField[0] = 0x80;
262 do
263 {
264 puchField[cchField--] = off & 0xff;
265 off >>= 8;
266 } while (cchField);
267 }
268
269 return VINF_SUCCESS;
270}
271
272
273/**
274 * Creates one or more tar headers for the object.
275 *
276 * Returns RTZIPTARFSSTREAMWRITER::aHdrs and RTZIPTARFSSTREAMWRITER::cHdrs.
277 *
278 * @returns IPRT status code.
279 * @param pThis The TAR writer instance.
280 * @param pszPath The path to the file.
281 * @param hVfsIos The I/O stream of the file.
282 * @param fFlags The RTVFSFSSTREAMOPS::pfnAdd flags.
283 * @param pObjInfo The object information.
284 * @param pszOwnerNm The owner name.
285 * @param pszGroupNm The group name.
286 * @param chType The tar record type, UINT8_MAX for default.
287 */
288static int rtZipTarFssWriter_ObjInfoToHdr(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
289 const char *pszOwnerNm, const char *pszGroupNm, uint8_t chType)
290{
291 pThis->cHdrs = 0;
292 RT_ZERO(pThis->aHdrs[0]);
293
294 /*
295 * The path name first. Make sure to flip DOS slashes.
296 */
297 size_t cchPath = strlen(pszPath);
298 if (cchPath < sizeof(pThis->aHdrs[0].Common.name))
299 {
300 memcpy(pThis->aHdrs[0].Common.name, pszPath, cchPath + 1);
301#if RTPATH_STYLE != RTPATH_STR_F_STYLE_UNIX
302 char *pszDosSlash = strchr(pThis->aHdrs[0].Common.name, '\\');
303 while (pszDosSlash)
304 {
305 *pszDosSlash = '/';
306 pszDosSlash = strchr(pszDosSlash + 1, '\\');
307 }
308#endif
309 }
310 else
311 {
312 /** @todo implement gnu and pax long name extensions. */
313 return VERR_TAR_NAME_TOO_LONG;
314 }
315
316 /*
317 * File mode. ASSUME that the unix part of the IPRT mode mask is
318 * compatible with the TAR/Unix world.
319 */
320 uint32_t uValue = pObjInfo->Attr.fMode & RTFS_UNIX_MASK;
321 if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
322 uValue = (uValue & pThis->fDirModeAndMask) | pThis->fDirModeOrMask;
323 else
324 uValue = (uValue & pThis->fFileModeAndMask) | pThis->fFileModeOrMask;
325 int rc = RTStrFormatU32(pThis->aHdrs[0].Common.mode, sizeof(pThis->aHdrs[0].Common.mode), uValue, 8 /*uBase*/,
326 -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mode) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
327 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
328
329 /*
330 * uid & gid. Just guard against NIL values as they won't fit.
331 */
332 uValue = pThis->uidOwner != NIL_RTUID ? pThis->uidOwner
333 : pObjInfo->Attr.u.Unix.uid != NIL_RTUID ? pObjInfo->Attr.u.Unix.uid : 0;
334 rc = RTStrFormatU32(pThis->aHdrs[0].Common.uid, sizeof(pThis->aHdrs[0].Common.uid), uValue,
335 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.uid) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
336 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
337
338 uValue = pThis->gidGroup != NIL_RTGID ? pThis->gidGroup
339 : pObjInfo->Attr.u.Unix.gid != NIL_RTGID ? pObjInfo->Attr.u.Unix.gid : 0;
340 rc = RTStrFormatU32(pThis->aHdrs[0].Common.gid, sizeof(pThis->aHdrs[0].Common.gid), uValue,
341 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.gid) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
342 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
343
344 /*
345 * The file size.
346 */
347 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, pObjInfo->cbObject);
348 AssertRCReturn(rc, rc);
349
350 /*
351 * Modification time relative to unix epoc.
352 */
353 rc = RTStrFormatU64(pThis->aHdrs[0].Common.mtime, sizeof(pThis->aHdrs[0].Common.mtime),
354 RTTimeSpecGetSeconds(pThis->pModTime ? pThis->pModTime : &pObjInfo->ModificationTime),
355 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mtime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
356 AssertRCReturn(rc, rc);
357
358 /* Skipping checksum for now */
359
360 /*
361 * The type flag.
362 */
363 if (chType == UINT8_MAX)
364 switch (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
365 {
366 case RTFS_TYPE_FIFO: chType = RTZIPTAR_TF_FIFO; break;
367 case RTFS_TYPE_DEV_CHAR: chType = RTZIPTAR_TF_CHR; break;
368 case RTFS_TYPE_DIRECTORY: chType = RTZIPTAR_TF_DIR; break;
369 case RTFS_TYPE_DEV_BLOCK: chType = RTZIPTAR_TF_BLK; break;
370 case RTFS_TYPE_FILE: chType = RTZIPTAR_TF_NORMAL; break;
371 case RTFS_TYPE_SYMLINK: chType = RTZIPTAR_TF_SYMLINK; break;
372 case RTFS_TYPE_SOCKET: chType = RTZIPTAR_TF_FIFO; break;
373 case RTFS_TYPE_WHITEOUT: AssertFailedReturn(VERR_WRONG_TYPE);
374 }
375 pThis->aHdrs[0].Common.typeflag = chType;
376
377 /* No link name, at least not for now. Caller might set it. */
378
379 /*
380 * Set TAR record magic and version.
381 */
382 if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
383 memcpy(pThis->aHdrs[0].Gnu.magic, RTZIPTAR_GNU_MAGIC, sizeof(pThis->aHdrs[0].Gnu.magic));
384 else if ( pThis->enmFormat == RTZIPTARFORMAT_USTAR
385 || pThis->enmFormat == RTZIPTARFORMAT_PAX)
386 {
387 memcpy(pThis->aHdrs[0].Common.magic, RTZIPTAR_USTAR_MAGIC, sizeof(pThis->aHdrs[0].Common.magic));
388 memcpy(pThis->aHdrs[0].Common.version, RTZIPTAR_USTAR_VERSION, sizeof(pThis->aHdrs[0].Common.version));
389 }
390 else
391 AssertFailedReturn(VERR_INTERNAL_ERROR_4);
392
393 /*
394 * Owner and group names. Silently truncate them for now.
395 */
396 RTStrCopy(pThis->aHdrs[0].Common.uname, sizeof(pThis->aHdrs[0].Common.uname), pThis->pszOwner ? pThis->pszOwner : pszOwnerNm);
397 RTStrCopy(pThis->aHdrs[0].Common.gname, sizeof(pThis->aHdrs[0].Common.uname), pThis->pszGroup ? pThis->pszGroup : pszGroupNm);
398
399 /*
400 * Char/block device numbers.
401 */
402 if ( RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode)
403 || RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode) )
404 {
405 rc = RTStrFormatU32(pThis->aHdrs[0].Common.devmajor, sizeof(pThis->aHdrs[0].Common.devmajor),
406 RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device),
407 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
408 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
409
410 rc = RTStrFormatU32(pThis->aHdrs[0].Common.devminor, sizeof(pThis->aHdrs[0].Common.devmajor),
411 RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device),
412 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
413 AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
414 }
415
416#if 0 /** @todo why doesn't this work? */
417 /*
418 * Set GNU specific stuff.
419 */
420 if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
421 {
422 rc = RTStrFormatU64(pThis->aHdrs[0].Gnu.ctime, sizeof(pThis->aHdrs[0].Gnu.ctime),
423 RTTimeSpecGetSeconds(&pObjInfo->ChangeTime),
424 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Gnu.ctime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
425 AssertRCReturn(rc, rc);
426
427 rc = RTStrFormatU64(pThis->aHdrs[0].Gnu.atime, sizeof(pThis->aHdrs[0].Gnu.atime),
428 RTTimeSpecGetSeconds(&pObjInfo->ChangeTime),
429 8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Gnu.atime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
430 AssertRCReturn(rc, rc);
431 }
432#endif
433
434 /*
435 * Finally the checksum.
436 */
437 pThis->cHdrs = 1;
438 return rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
439}
440
441
442
443
444/**
445 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
446 */
447static DECLCALLBACK(int) rtZipTarWriterPush_Close(void *pvThis)
448{
449 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
450 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
451 if (pParent)
452 {
453 if (pParent->pPush == pPush)
454 rtZipTarFssWriter_CompleteCurrentPushFile(pParent);
455 else
456 AssertFailedStmt(pPush->pParent = NULL);
457 }
458 return VINF_SUCCESS;
459}
460
461
462/**
463 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
464 */
465static DECLCALLBACK(int) rtZipTarWriterPush_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
466{
467 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
468
469 /* Basic info (w/ additional unix attribs). */
470 *pObjInfo = pPush->ObjInfo;
471 pObjInfo->cbObject = pPush->cbCurrent;
472 pObjInfo->cbAllocated = RT_ALIGN_64(pPush->cbCurrent, RTZIPTAR_BLOCKSIZE);
473
474 /* Additional info. */
475 switch (enmAddAttr)
476 {
477 case RTFSOBJATTRADD_NOTHING:
478 case RTFSOBJATTRADD_UNIX:
479 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
480 break;
481
482 case RTFSOBJATTRADD_UNIX_OWNER:
483 pObjInfo->Attr.u.UnixOwner.uid = pPush->ObjInfo.Attr.u.Unix.uid;
484 if (pPush->pParent)
485 strcpy(pObjInfo->Attr.u.UnixOwner.szName, pPush->pParent->aHdrs[0].Common.uname);
486 else
487 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
488 pObjInfo->Attr.enmAdditional = enmAddAttr;
489 break;
490
491 case RTFSOBJATTRADD_UNIX_GROUP:
492 pObjInfo->Attr.u.UnixGroup.gid = pPush->ObjInfo.Attr.u.Unix.gid;
493 if (pPush->pParent)
494 strcpy(pObjInfo->Attr.u.UnixGroup.szName, pPush->pParent->aHdrs[0].Common.uname);
495 else
496 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
497 pObjInfo->Attr.enmAdditional = enmAddAttr;
498 break;
499
500 case RTFSOBJATTRADD_EASIZE:
501 pObjInfo->Attr.u.EASize.cb = 0;
502 pObjInfo->Attr.enmAdditional = enmAddAttr;
503 break;
504
505 default:
506 AssertFailed();
507 }
508
509 return VINF_SUCCESS;
510}
511
512
513/**
514 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
515 */
516static DECLCALLBACK(int) rtZipTarWriterPush_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
517{
518 /* No read support, sorry. */
519 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbRead);
520 AssertFailed();
521 return VERR_ACCESS_DENIED;
522}
523
524
525/**
526 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
527 */
528static DECLCALLBACK(int) rtZipTarWriterPush_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
529{
530 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
531 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
532 AssertPtrReturn(pParent, VERR_WRONG_ORDER);
533
534 int rc = pParent->rcFatal;
535 AssertRCReturn(rc, rc);
536
537 /*
538 * Single segment at a time.
539 */
540 Assert(pSgBuf->cSegs == 1);
541 size_t cbToWrite = pSgBuf->paSegs[0].cbSeg;
542 void const *pvToWrite = pSgBuf->paSegs[0].pvSeg;
543
544 /*
545 * Hopefully we don't need to seek. But if we do, let the seek method do
546 * it as it's not entirely trivial.
547 */
548 if ( off < 0
549 || (uint64_t)off == pPush->offCurrent)
550 rc = VINF_SUCCESS;
551 else
552 rc = rtZipTarWriterPush_Seek(pvThis, off, RTFILE_SEEK_BEGIN, NULL);
553 if (RT_SUCCESS(rc))
554 {
555 Assert(pPush->offCurrent <= pPush->cbExpected);
556 Assert(pPush->offCurrent <= pPush->cbCurrent);
557 AssertMsgReturn(cbToWrite <= pPush->cbExpected - pPush->offCurrent,
558 ("offCurrent=%#RX64 + cbToWrite=%#zx = %#RX64; cbExpected=%RX64\n",
559 pPush->offCurrent, cbToWrite, pPush->offCurrent + cbToWrite, pPush->cbExpected),
560 VERR_DISK_FULL);
561 size_t cbWritten = 0;
562 rc = RTVfsIoStrmWrite(pParent->hVfsIos, pvToWrite, cbToWrite, fBlocking, &cbWritten);
563 if (RT_SUCCESS(rc))
564 {
565 pPush->offCurrent += cbWritten;
566 if (pPush->offCurrent > pPush->cbCurrent)
567 {
568 pParent->cbWritten = pPush->offCurrent - pPush->cbCurrent;
569 pPush->cbCurrent = pPush->offCurrent;
570 }
571 if (pcbWritten)
572 *pcbWritten = cbWritten;
573 }
574 }
575
576 /*
577 * Fatal errors get down here, non-fatal ones returns earlier.
578 */
579 if (RT_SUCCESS(rc))
580 return VINF_SUCCESS;
581 pParent->rcFatal = rc;
582 return rc;
583}
584
585
586/**
587 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
588 */
589static DECLCALLBACK(int) rtZipTarWriterPush_Flush(void *pvThis)
590{
591 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
592 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
593 AssertPtrReturn(pParent, VERR_WRONG_ORDER);
594 int rc = pParent->rcFatal;
595 if (RT_SUCCESS(rc))
596 pParent->rcFatal = rc = RTVfsIoStrmFlush(pParent->hVfsIos);
597 return rc;
598}
599
600
601/**
602 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
603 */
604static DECLCALLBACK(int) rtZipTarWriterPush_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
605 uint32_t *pfRetEvents)
606{
607 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
608 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
609 AssertPtrReturn(pParent, VERR_WRONG_ORDER);
610 return RTVfsIoStrmPoll(pParent->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
611}
612
613
614/**
615 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
616 */
617static DECLCALLBACK(int) rtZipTarWriterPush_Tell(void *pvThis, PRTFOFF poffActual)
618{
619 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
620 *poffActual = (RTFOFF)pPush->offCurrent;
621 return VINF_SUCCESS;
622}
623
624
625/**
626 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
627 */
628static DECLCALLBACK(int) rtZipTarWriterPush_Skip(void *pvThis, RTFOFF cb)
629{
630 RT_NOREF(pvThis, cb);
631 AssertFailed();
632 return VERR_ACCESS_DENIED;
633}
634
635
636/**
637 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
638 */
639static DECLCALLBACK(int) rtZipTarWriterPush_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
640{
641 RT_NOREF(pvThis, fMode, fMask);
642 AssertFailed();
643 return VERR_ACCESS_DENIED;
644}
645
646
647/**
648 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
649 */
650static DECLCALLBACK(int) rtZipTarWriterPush_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
651 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
652{
653 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
654 AssertFailed();
655 return VERR_ACCESS_DENIED;
656}
657
658
659/**
660 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
661 */
662static DECLCALLBACK(int) rtZipTarWriterPush_SetOwner(void *pvThis, RTUID uid, RTGID gid)
663{
664 RT_NOREF(pvThis, uid, gid);
665 AssertFailed();
666 return VERR_ACCESS_DENIED;
667}
668
669
670/**
671 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
672 */
673static DECLCALLBACK(int) rtZipTarWriterPush_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
674{
675 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
676 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
677 AssertPtrReturn(pParent, VERR_WRONG_ORDER);
678
679 int rc = pParent->rcFatal;
680 AssertRCReturn(rc, rc);
681 Assert(pPush->offCurrent <= pPush->cbCurrent);
682
683 /*
684 * Calculate the new file offset.
685 */
686 RTFOFF offNewSigned;
687 switch (uMethod)
688 {
689 case RTFILE_SEEK_BEGIN:
690 offNewSigned = offSeek;
691 break;
692 case RTFILE_SEEK_CURRENT:
693 offNewSigned = pPush->offCurrent + offSeek;
694 break;
695 case RTFILE_SEEK_END:
696 offNewSigned = pPush->cbCurrent + offSeek;
697 break;
698 default:
699 AssertFailedReturn(VERR_INVALID_PARAMETER);
700 }
701
702 /*
703 * Check the new file offset against expectations.
704 */
705 AssertMsgReturn(offNewSigned >= 0, ("offNewSigned=%RTfoff\n", offNewSigned), VERR_NEGATIVE_SEEK);
706
707 uint64_t offNew = (uint64_t)offNewSigned;
708 AssertMsgReturn(offNew <= pPush->cbExpected, ("offNew=%#RX64 cbExpected=%#Rx64\n", offNew, pPush->cbExpected), VERR_SEEK);
709
710 /*
711 * Any change at all? We can always hope...
712 */
713 if (offNew == pPush->offCurrent)
714 { }
715 /*
716 * Gap that needs zero filling?
717 */
718 else if (offNew > pPush->cbCurrent)
719 {
720 if (pPush->offCurrent != pPush->cbCurrent)
721 {
722 AssertReturn(pParent->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
723 rc = RTVfsFileSeek(pParent->hVfsFile, pPush->offData + pPush->cbCurrent, RTFILE_SEEK_BEGIN, NULL);
724 if (RT_FAILURE(rc))
725 return pParent->rcFatal = rc;
726 pPush->offCurrent = pPush->cbCurrent;
727 }
728
729 uint64_t cbToZero = offNew - pPush->cbCurrent;
730 rc = RTVfsIoStrmZeroFill(pParent->hVfsIos, cbToZero);
731 if (RT_FAILURE(rc))
732 return pParent->rcFatal = rc;
733 pParent->cbWritten += cbToZero;
734 pPush->cbCurrent = pPush->offCurrent = offNew;
735 }
736 /*
737 * Just change the file position to somewhere we've already written.
738 */
739 else
740 {
741 AssertReturn(pParent->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
742 rc = RTVfsFileSeek(pParent->hVfsFile, pPush->offData + offNew, RTFILE_SEEK_BEGIN, NULL);
743 if (RT_FAILURE(rc))
744 return pParent->rcFatal = rc;
745 pPush->offCurrent = offNew;
746 }
747 Assert(pPush->offCurrent <= pPush->cbCurrent);
748
749 if (poffActual)
750 *poffActual = pPush->offCurrent;
751 return VINF_SUCCESS;
752}
753
754
755/**
756 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
757 */
758static DECLCALLBACK(int) rtZipTarWriterPush_QuerySize(void *pvThis, uint64_t *pcbFile)
759{
760 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
761 *pcbFile = pPush->cbCurrent;
762 return VINF_SUCCESS;
763}
764
765
766/**
767 * TAR writer push I/O stream operations.
768 */
769DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_rtZipTarWriterIoStrmOps =
770{
771 { /* Obj */
772 RTVFSOBJOPS_VERSION,
773 RTVFSOBJTYPE_IO_STREAM,
774 "TAR push I/O Stream",
775 rtZipTarWriterPush_Close,
776 rtZipTarWriterPush_QueryInfo,
777 RTVFSOBJOPS_VERSION
778 },
779 RTVFSIOSTREAMOPS_VERSION,
780 RTVFSIOSTREAMOPS_FEAT_NO_SG,
781 rtZipTarWriterPush_Read,
782 rtZipTarWriterPush_Write,
783 rtZipTarWriterPush_Flush,
784 rtZipTarWriterPush_PollOne,
785 rtZipTarWriterPush_Tell,
786 rtZipTarWriterPush_Skip,
787 NULL /*ZeroFill*/,
788 RTVFSIOSTREAMOPS_VERSION,
789};
790
791
792/**
793 * TAR writer push file operations.
794 */
795DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtZipTarWriterFileOps =
796{
797 { /* Stream */
798 { /* Obj */
799 RTVFSOBJOPS_VERSION,
800 RTVFSOBJTYPE_FILE,
801 "TAR push file",
802 rtZipTarWriterPush_Close,
803 rtZipTarWriterPush_QueryInfo,
804 RTVFSOBJOPS_VERSION
805 },
806 RTVFSIOSTREAMOPS_VERSION,
807 RTVFSIOSTREAMOPS_FEAT_NO_SG,
808 rtZipTarWriterPush_Read,
809 rtZipTarWriterPush_Write,
810 rtZipTarWriterPush_Flush,
811 rtZipTarWriterPush_PollOne,
812 rtZipTarWriterPush_Tell,
813 rtZipTarWriterPush_Skip,
814 NULL /*ZeroFill*/,
815 RTVFSIOSTREAMOPS_VERSION,
816 },
817 RTVFSFILEOPS_VERSION,
818 0,
819 { /* ObjSet */
820 RTVFSOBJSETOPS_VERSION,
821 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
822 rtZipTarWriterPush_SetMode,
823 rtZipTarWriterPush_SetTimes,
824 rtZipTarWriterPush_SetOwner,
825 RTVFSOBJSETOPS_VERSION
826 },
827 rtZipTarWriterPush_Seek,
828 rtZipTarWriterPush_QuerySize,
829 RTVFSFILEOPS_VERSION
830};
831
832
833
834/**
835 * Checks rcFatal and completes any current push file.
836 *
837 * On return the output stream position will be at the next header location.
838 *
839 * After this call, the push object no longer can write anything.
840 *
841 * @returns IPRT status code.
842 * @param pThis The TAR writer instance.
843 */
844static int rtZipTarFssWriter_CompleteCurrentPushFile(PRTZIPTARFSSTREAMWRITER pThis)
845{
846 /*
847 * Check if there is a push file pending, remove it if there is.
848 * We also check for fatal errors at this point so the caller doesn't need to.
849 */
850 PRTZIPTARFSSTREAMWRITERPUSH pPush = pThis->pPush;
851 if (!pPush)
852 {
853 AssertRC(pThis->rcFatal);
854 return pThis->rcFatal;
855 }
856
857 pThis->pPush = NULL;
858 pPush->pParent = NULL;
859
860 int rc = pThis->rcFatal;
861 AssertRCReturn(rc, rc);
862
863 /*
864 * Do we need to update the header. pThis->aHdrs[0] will retain the current
865 * content at pPush->offHdr and we only need to update the size.
866 */
867 if (pPush->fOpenEnded)
868 {
869 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, pPush->cbCurrent);
870 if (RT_SUCCESS(rc))
871 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
872 if (RT_SUCCESS(rc))
873 {
874 rc = RTVfsFileWriteAt(pThis->hVfsFile, pPush->offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
875 if (RT_SUCCESS(rc))
876 rc = RTVfsFileSeek(pThis->hVfsFile, pPush->offData + pPush->cbCurrent, RTFILE_SEEK_BEGIN, NULL);
877 }
878 }
879 /*
880 * Check that we've received all the data we were promissed in the PushFile
881 * call, fail if we weren't.
882 */
883 else
884 AssertMsgStmt(pPush->cbCurrent == pPush->cbExpected,
885 ("cbCurrent=%#RX64 cbExpected=%#RX64\n", pPush->cbCurrent, pPush->cbExpected),
886 rc = VERR_BUFFER_UNDERFLOW);
887 if (RT_SUCCESS(rc))
888 {
889 /*
890 * Do zero padding if necessary.
891 */
892 if (pPush->cbCurrent & (RTZIPTAR_BLOCKSIZE - 1))
893 {
894 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (pPush->cbCurrent & (RTZIPTAR_BLOCKSIZE - 1));
895 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
896 if (RT_SUCCESS(rc))
897 pThis->cbWritten += cbToZero;
898 }
899 }
900
901 if (RT_SUCCESS(rc))
902 return VINF_SUCCESS;
903 pThis->rcFatal = rc;
904 return rc;
905}
906
907
908/**
909 * Allocates a buffer for transfering file data.
910 *
911 * @note Will use the 3rd TAR header as fallback buffer if we're out of
912 * memory!
913 *
914 * @returns Pointer to buffer (won't ever fail).
915 * @param pThis The TAR writer instance.
916 * @param pcbBuf Where to return the buffer size. This will be a
917 * multiple of the TAR block size.
918 * @param ppvFree Where to return the pointer to pass to RTMemTmpFree
919 * when done with the buffer.
920 * @param cbFile The file size. Used as a buffer size hint.
921 */
922static uint8_t *rtZipTarFssWrite_AllocBuf(PRTZIPTARFSSTREAMWRITER pThis, size_t *pcbBuf, void **ppvFree, uint64_t cbObject)
923{
924 uint8_t *pbBuf;
925
926 /*
927 * If this is a large file, try for a large buffer with 16KB alignment.
928 */
929 if (cbObject >= _64M)
930 {
931 pbBuf = (uint8_t *)RTMemTmpAlloc(_2M + _16K - 1);
932 if (pbBuf)
933 {
934 *pcbBuf = _2M;
935 *ppvFree = pbBuf;
936 return RT_ALIGN_PT(pbBuf, _16K, uint8_t *);
937 }
938 }
939 /*
940 * 4KB aligned 512KB buffer if larger 512KB or larger.
941 */
942 else if (cbObject >= _512K)
943 {
944 pbBuf = (uint8_t *)RTMemTmpAlloc(_512K + _4K - 1);
945 if (pbBuf)
946 {
947 *pcbBuf = _512K;
948 *ppvFree = pbBuf;
949 return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
950 }
951 }
952 /*
953 * Otherwise a 4KB aligned 128KB buffer.
954 */
955 else
956 {
957 pbBuf = (uint8_t *)RTMemTmpAlloc(_128K + _4K - 1);
958 if (pbBuf)
959 {
960 *pcbBuf = _128K;
961 *ppvFree = pbBuf;
962 return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
963 }
964 }
965
966 /*
967 * If allocation failed, fallback on a 16KB buffer without any extra alignment.
968 */
969 pbBuf = (uint8_t *)RTMemTmpAlloc(_16K);
970 if (pbBuf)
971 {
972 *pcbBuf = _16K;
973 *ppvFree = pbBuf;
974 return pbBuf;
975 }
976
977 /*
978 * Final fallback, 512KB buffer using the 3rd header.
979 */
980 AssertCompile(RT_ELEMENTS(pThis->aHdrs) >= 3);
981 *pcbBuf = sizeof(pThis->aHdrs[2]);
982 *ppvFree = NULL;
983 return (uint8_t *)&pThis->aHdrs[2];
984}
985
986
987/**
988 * Frees the sparse info for a TAR file.
989 *
990 * @param pSparse The sparse info to free.
991 */
992static void rtZipTarFssWriter_SparseInfoDestroy(PRTZIPTARSPARSE pSparse)
993{
994 PRTZIPTARSPARSECHUNK pCur;
995 PRTZIPTARSPARSECHUNK pNext;
996 RTListForEachSafe(&pSparse->ChunkHead, pCur, pNext, RTZIPTARSPARSECHUNK, Entry)
997 RTMemTmpFree(pCur);
998 RTMemTmpFree(pSparse);
999}
1000
1001
1002/**
1003 * Adds a data span to the sparse info.
1004 *
1005 * @returns VINF_SUCCESS or VINF_NO_TMP_MEMORY.
1006 * @param pSparse The sparse info to free.
1007 * @param offSpan Offset of the span.
1008 * @param cbSpan Number of bytes.
1009 */
1010static int rtZipTarFssWriter_SparseInfoAddSpan(PRTZIPTARSPARSE pSparse, uint64_t offSpan, uint64_t cbSpan)
1011{
1012 /*
1013 * Get the chunk we're adding it to.
1014 */
1015 PRTZIPTARSPARSECHUNK pChunk;
1016 if (pSparse->iNextSpan != 0)
1017 {
1018 pChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1019 Assert(pSparse->iNextSpan < RT_ELEMENTS(pChunk->aSpans));
1020 }
1021 else
1022 {
1023 pChunk = (PRTZIPTARSPARSECHUNK)RTMemTmpAllocZ(sizeof(*pChunk));
1024 if (!pChunk)
1025 return VERR_NO_TMP_MEMORY;
1026 RTListAppend(&pSparse->ChunkHead, &pChunk->Entry);
1027 }
1028
1029 /*
1030 * Append it.
1031 */
1032 pSparse->cDataSpans += 1;
1033 pSparse->cbDataSpans += cbSpan;
1034 pChunk->aSpans[pSparse->iNextSpan].cb = cbSpan;
1035 pChunk->aSpans[pSparse->iNextSpan].off = offSpan;
1036 if (++pSparse->iNextSpan >= RT_ELEMENTS(pChunk->aSpans))
1037 pSparse->iNextSpan = 0;
1038 return VINF_SUCCESS;
1039}
1040
1041
1042/**
1043 * Scans the input stream recording non-zero blocks.
1044 */
1045static int rtZipTarFssWriter_ScanSparseFile(PRTZIPTARFSSTREAMWRITER pThis, RTVFSFILE hVfsFile, uint64_t cbFile,
1046 size_t cbBuf, uint8_t *pbBuf, PRTZIPTARSPARSE *ppSparse)
1047{
1048 RT_NOREF(pThis);
1049
1050 /*
1051 * Create an empty sparse info bundle.
1052 */
1053 PRTZIPTARSPARSE pSparse = (PRTZIPTARSPARSE)RTMemTmpAlloc(sizeof(*pSparse));
1054 AssertReturn(pSparse, VERR_NO_MEMORY);
1055 pSparse->cbDataSpans = 0;
1056 pSparse->cDataSpans = 0;
1057 pSparse->iNextSpan = 0;
1058 RTListInit(&pSparse->ChunkHead);
1059
1060 /*
1061 * Scan the file from the start.
1062 */
1063 int rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
1064 if (RT_SUCCESS(rc))
1065 {
1066 bool fZeroSpan = false;
1067 uint64_t offSpan = 0;
1068 uint64_t cbSpan = 0;
1069
1070 for (uint64_t off = 0; off < cbFile;)
1071 {
1072 uint64_t cbLeft = cbFile - off;
1073 size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
1074 rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
1075 if (RT_FAILURE(rc))
1076 break;
1077 size_t cBlocks = cbToRead / RTZIPTAR_BLOCKSIZE;
1078
1079 /* Zero pad the final buffer to a multiple of the blocksize. */
1080 if (!(cbToRead & (RTZIPTAR_BLOCKSIZE - 1)))
1081 { /* likely */ }
1082 else
1083 {
1084 AssertBreakStmt(cbLeft == cbToRead, rc = VERR_INTERNAL_ERROR_3);
1085 RT_BZERO(&pbBuf[cbToRead], RTZIPTAR_BLOCKSIZE - (cbToRead & (RTZIPTAR_BLOCKSIZE - 1)));
1086 cBlocks++;
1087 }
1088
1089 /*
1090 * Process the blocks we've just read one by one.
1091 */
1092 uint8_t const *pbBlock = pbBuf;
1093 for (size_t iBlock = 0; iBlock < cBlocks; iBlock++)
1094 {
1095 bool fZeroBlock = ASMMemIsZero(pbBlock, RTZIPTAR_BLOCKSIZE);
1096 if (fZeroBlock == fZeroSpan)
1097 cbSpan += RTZIPTAR_BLOCKSIZE;
1098 else
1099 {
1100 if (!fZeroSpan && cbSpan)
1101 {
1102 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
1103 if (RT_FAILURE(rc))
1104 break;
1105 }
1106 fZeroSpan = fZeroBlock;
1107 offSpan = off;
1108 cbSpan = RTZIPTAR_BLOCKSIZE;
1109 }
1110
1111 /* next block. */
1112 pbBlock += RTZIPTAR_BLOCKSIZE;
1113 off += RTZIPTAR_BLOCKSIZE;
1114 }
1115 }
1116
1117 /*
1118 * Deal with the final span. If we've got zeros thowards the end, we
1119 * must add a zero byte data span at the end.
1120 */
1121 if (RT_SUCCESS(rc))
1122 {
1123 if (!fZeroSpan && cbSpan)
1124 {
1125 if (cbFile & (RTZIPTAR_BLOCKSIZE - 1))
1126 {
1127 Assert(!(cbSpan & (RTZIPTAR_BLOCKSIZE - 1)));
1128 cbSpan -= RTZIPTAR_BLOCKSIZE;
1129 cbSpan |= cbFile & (RTZIPTAR_BLOCKSIZE - 1);
1130 }
1131 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
1132 }
1133 if (RT_SUCCESS(rc))
1134 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, cbFile, 0);
1135 }
1136 }
1137
1138 if (RT_SUCCESS(rc))
1139 {
1140 /*
1141 * Return the file back to the start position before we return so that we
1142 * can segue into the regular rtZipTarFssWriter_AddFile without further ado.
1143 */
1144 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
1145 if (RT_SUCCESS(rc))
1146 {
1147 *ppSparse = pSparse;
1148 return VINF_SUCCESS;
1149 }
1150 }
1151
1152 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1153 *ppSparse = NULL;
1154 return rc;
1155}
1156
1157
1158/**
1159 * Writes GNU the sparse file headers.
1160 *
1161 * @returns IPRT status code.
1162 * @param pThis The TAR writer instance.
1163 * @param pszPath The path to the file.
1164 * @param pObjInfo The object information.
1165 * @param pszOwnerNm The owner name.
1166 * @param pszGroupNm The group name.
1167 * @param pSparse The sparse file info.
1168 */
1169static int rtZipTarFssWriter_WriteGnuSparseHeaders(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
1170 const char *pszOwnerNm, const char *pszGroupNm, PCRTZIPTARSPARSE pSparse)
1171{
1172 /*
1173 * Format the first header.
1174 */
1175 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_GNU_SPARSE);
1176 AssertRCReturn(rc, rc);
1177 AssertReturn(pThis->cHdrs == 1, VERR_INTERNAL_ERROR_2);
1178
1179 /* data size. */
1180 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, pSparse->cbDataSpans);
1181 AssertRCReturn(rc, rc);
1182
1183 /* realsize. */
1184 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Gnu.realsize, pObjInfo->cbObject);
1185 AssertRCReturn(rc, rc);
1186
1187 Assert(pThis->aHdrs[0].Gnu.isextended == 0);
1188
1189 /*
1190 * Walk the sparse spans, fill and write headers one by one.
1191 */
1192 PRTZIPTARGNUSPARSE paSparse = &pThis->aHdrs[0].Gnu.sparse[0];
1193 uint32_t cSparse = RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse);
1194 uint32_t iSparse = 0;
1195
1196 PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1197 PRTZIPTARSPARSECHUNK pChunk;
1198 RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
1199 {
1200 uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
1201 ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
1202 for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
1203 {
1204 /* Flush the header? */
1205 if (iSparse >= cSparse)
1206 {
1207 if (cSparse != RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse))
1208 pThis->aHdrs[0].GnuSparse.isextended = 1; /* more headers to come */
1209 else
1210 {
1211 pThis->aHdrs[0].Gnu.isextended = 1; /* more headers to come */
1212 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1213 }
1214 if (RT_SUCCESS(rc))
1215 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1216 if (RT_FAILURE(rc))
1217 return rc;
1218 RT_ZERO(pThis->aHdrs[0]);
1219 cSparse = RT_ELEMENTS(pThis->aHdrs[0].GnuSparse.sp);
1220 iSparse = 0;
1221 paSparse = &pThis->aHdrs[0].GnuSparse.sp[0];
1222 }
1223
1224 /* Append sparse data segment. */
1225 rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].offset, pChunk->aSpans[iSpan].off);
1226 AssertRCReturn(rc, rc);
1227 rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].numbytes, pChunk->aSpans[iSpan].cb);
1228 AssertRCReturn(rc, rc);
1229 iSparse++;
1230 }
1231 }
1232
1233 /*
1234 * The final header.
1235 */
1236 if (iSparse != 0)
1237 {
1238 if (cSparse != RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse))
1239 Assert(pThis->aHdrs[0].GnuSparse.isextended == 0);
1240 else
1241 {
1242 Assert(pThis->aHdrs[0].Gnu.isextended == 0);
1243 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1244 }
1245 if (RT_SUCCESS(rc))
1246 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1247 }
1248 pThis->cHdrs = 0;
1249 return rc;
1250}
1251
1252
1253/**
1254 * Adds a potentially sparse file to the output.
1255 *
1256 * @returns IPRT status code.
1257 * @param pThis The TAR writer instance.
1258 * @param pszPath The path to the file.
1259 * @param hVfsFile The potentially sparse file.
1260 * @param hVfsIos The I/O stream of the file. Same as @a hVfsFile.
1261 * @param pObjInfo The object information.
1262 * @param pszOwnerNm The owner name.
1263 * @param pszGroupNm The group name.
1264 */
1265static int rtZipTarFssWriter_AddFileSparse(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSFILE hVfsFile,
1266 RTVFSIOSTREAM hVfsIos, PCRTFSOBJINFO pObjInfo,
1267 const char *pszOwnerNm, const char *pszGroupNm)
1268{
1269 /*
1270 * Scan the input file to locate all zero blocks.
1271 */
1272 void *pvBufFree;
1273 size_t cbBuf;
1274 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
1275
1276 PRTZIPTARSPARSE pSparse;
1277 int rc = rtZipTarFssWriter_ScanSparseFile(pThis, hVfsFile, pObjInfo->cbObject, cbBuf, pbBuf, &pSparse);
1278 if (RT_SUCCESS(rc))
1279 {
1280 /*
1281 * If there aren't at least 2 zero blocks in the file, don't bother
1282 * doing the sparse stuff and store it as a normal file.
1283 */
1284 if (pSparse->cbDataSpans + RTZIPTAR_BLOCKSIZE > (uint64_t)pObjInfo->cbObject)
1285 {
1286 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1287 RTMemTmpFree(pvBufFree);
1288 return rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, pObjInfo, pszOwnerNm, pszGroupNm);
1289 }
1290
1291 /*
1292 * Produce and write the headers.
1293 */
1294 if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
1295 rc = rtZipTarFssWriter_WriteGnuSparseHeaders(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, pSparse);
1296 else
1297 AssertStmt(pThis->enmFormat != RTZIPTARFORMAT_GNU, rc = VERR_NOT_IMPLEMENTED);
1298 if (RT_SUCCESS(rc))
1299 {
1300 /*
1301 * Write the file bytes.
1302 */
1303 PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1304 PRTZIPTARSPARSECHUNK pChunk;
1305 RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
1306 {
1307 uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
1308 ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
1309 for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
1310 {
1311 rc = RTVfsFileSeek(hVfsFile, pChunk->aSpans[iSpan].off, RTFILE_SEEK_BEGIN, NULL);
1312 if (RT_FAILURE(rc))
1313 break;
1314 uint64_t cbLeft = pChunk->aSpans[iSpan].cb;
1315 Assert( !(cbLeft & (RTZIPTAR_BLOCKSIZE - 1))
1316 || (iSpan + 1 == cSpans && pChunk == pLastChunk));
1317 while (cbLeft > 0)
1318 {
1319 size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
1320 rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
1321 if (RT_SUCCESS(rc))
1322 {
1323 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToRead, true /*fBlocking*/, NULL);
1324 if (RT_SUCCESS(rc))
1325 {
1326 pThis->cbWritten += cbToRead;
1327 cbLeft -= cbToRead;
1328 continue;
1329 }
1330 }
1331 break;
1332 }
1333 if (RT_FAILURE(rc))
1334 break;
1335 }
1336 }
1337
1338 /*
1339 * Do the zero padding.
1340 */
1341 if ( RT_SUCCESS(rc)
1342 && (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1)))
1343 {
1344 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1));
1345 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1346 if (RT_SUCCESS(rc))
1347 pThis->cbWritten += cbToZero;
1348 }
1349 }
1350
1351 if (RT_FAILURE(rc))
1352 pThis->rcFatal = rc;
1353 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1354 }
1355 RTMemTmpFree(pvBufFree);
1356 return rc;
1357}
1358
1359
1360/**
1361 * Adds an I/O stream of indeterminate length to the TAR file.
1362 *
1363 * This requires the output to be seekable, i.e. a file, because we need to go
1364 * back and update @c size field of the TAR header after pumping all the data
1365 * bytes thru and establishing the file length.
1366 *
1367 * @returns IPRT status code.
1368 * @param pThis The TAR writer instance.
1369 * @param pszPath The path to the file.
1370 * @param hVfsIos The I/O stream of the file.
1371 * @param pObjInfo The object information.
1372 * @param pszOwnerNm The owner name.
1373 * @param pszGroupNm The group name.
1374 */
1375static int rtZipTarFssWriter_AddFileStream(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
1376 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1377{
1378 AssertReturn(pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
1379
1380 /*
1381 * Append the header.
1382 */
1383 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1384 if (RT_SUCCESS(rc))
1385 {
1386 RTFOFF const offHdr = RTVfsFileTell(pThis->hVfsFile);
1387 if (offHdr >= 0)
1388 {
1389 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1390 if (RT_SUCCESS(rc))
1391 {
1392 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1393
1394 /*
1395 * Transfer the bytes.
1396 */
1397 void *pvBufFree;
1398 size_t cbBuf;
1399 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree,
1400 pObjInfo->cbObject > 0 && pObjInfo->cbObject != RTFOFF_MAX
1401 ? pObjInfo->cbObject : _1G);
1402
1403 uint64_t cbReadTotal = 0;
1404 for (;;)
1405 {
1406 size_t cbRead = 0;
1407 int rc2 = rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbBuf, true /*fBlocking*/, &cbRead);
1408 if (RT_SUCCESS(rc))
1409 {
1410 cbReadTotal += cbRead;
1411 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
1412 if (RT_SUCCESS(rc))
1413 {
1414 pThis->cbWritten += cbRead;
1415 if (rc2 != VINF_EOF)
1416 continue;
1417 }
1418 }
1419 Assert(rc != VERR_EOF /* expecting VINF_EOF! */);
1420 break;
1421 }
1422
1423 RTMemTmpFree(pvBufFree);
1424
1425 /*
1426 * Do the zero padding.
1427 */
1428 if ((cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1)) && RT_SUCCESS(rc))
1429 {
1430 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1));
1431 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1432 if (RT_SUCCESS(rc))
1433 pThis->cbWritten += cbToZero;
1434 }
1435
1436 /*
1437 * Update the header. We ASSUME that aHdr[0] is unmodified
1438 * from before the data pumping above and just update the size.
1439 */
1440 if ((RTFOFF)cbReadTotal != pObjInfo->cbObject && RT_SUCCESS(rc))
1441 {
1442 RTFOFF const offRestore = RTVfsFileTell(pThis->hVfsFile);
1443 if (offRestore >= 0)
1444 {
1445 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, cbReadTotal);
1446 if (RT_SUCCESS(rc))
1447 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1448 if (RT_SUCCESS(rc))
1449 {
1450 rc = RTVfsFileWriteAt(pThis->hVfsFile, offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
1451 if (RT_SUCCESS(rc))
1452 rc = RTVfsFileSeek(pThis->hVfsFile, offRestore, RTFILE_SEEK_BEGIN, NULL);
1453 }
1454 }
1455 else
1456 rc = (int)offRestore;
1457 }
1458
1459 if (RT_SUCCESS(rc))
1460 return VINF_SUCCESS;
1461 }
1462 }
1463 else
1464 rc = (int)offHdr;
1465 pThis->rcFatal = rc;
1466 }
1467 return rc;
1468}
1469
1470
1471/**
1472 * Adds a file to the stream.
1473 *
1474 * @returns IPRT status code.
1475 * @param pThis The TAR writer instance.
1476 * @param pszPath The path to the file.
1477 * @param hVfsIos The I/O stream of the file.
1478 * @param fFlags The RTVFSFSSTREAMOPS::pfnAdd flags.
1479 * @param pObjInfo The object information.
1480 * @param pszOwnerNm The owner name.
1481 * @param pszGroupNm The group name.
1482 */
1483static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
1484 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1485{
1486 /*
1487 * Append the header.
1488 */
1489 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1490 if (RT_SUCCESS(rc))
1491 {
1492 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1493 if (RT_SUCCESS(rc))
1494 {
1495 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1496
1497 /*
1498 * Copy the bytes. Padding the last buffer to a multiple of 512.
1499 */
1500 void *pvBufFree;
1501 size_t cbBuf;
1502 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
1503
1504 uint64_t cbLeft = pObjInfo->cbObject;
1505 while (cbLeft > 0)
1506 {
1507 size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbLeft;
1508 rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
1509 if (RT_FAILURE(rc))
1510 break;
1511
1512 size_t cbToWrite = cbRead;
1513 if (cbRead & (RTZIPTAR_BLOCKSIZE - 1))
1514 {
1515 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbRead & (RTZIPTAR_BLOCKSIZE - 1));
1516 memset(&pbBuf[cbRead], 0, cbToZero);
1517 cbToWrite += cbToZero;
1518 }
1519
1520 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
1521 if (RT_FAILURE(rc))
1522 break;
1523 pThis->cbWritten += cbToWrite;
1524 cbLeft -= cbRead;
1525 }
1526
1527 RTMemTmpFree(pvBufFree);
1528
1529 if (RT_SUCCESS(rc))
1530 return VINF_SUCCESS;
1531 }
1532 pThis->rcFatal = rc;
1533 }
1534 return rc;
1535}
1536
1537
1538/**
1539 * Adds a symbolic link to the stream.
1540 *
1541 * @returns IPRT status code.
1542 * @param pThis The TAR writer instance.
1543 * @param pszPath The path to the object.
1544 * @param hVfsSymlink The symbolic link object to add.
1545 * @param pObjInfo The object information.
1546 * @param pszOwnerNm The owner name.
1547 * @param pszGroupNm The group name.
1548 */
1549static int rtZipTarFssWriter_AddSymlink(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSSYMLINK hVfsSymlink,
1550 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1551{
1552 /*
1553 * Read the symlink target first and check that it's not too long.
1554 * Flip DOS slashes.
1555 */
1556 char szTarget[RTPATH_MAX];
1557 int rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
1558 if (RT_SUCCESS(rc))
1559 {
1560#if RTPATH_STYLE != RTPATH_STR_F_STYLE_UNIX
1561 char *pszDosSlash = strchr(szTarget, '\\');
1562 while (pszDosSlash)
1563 {
1564 *pszDosSlash = '/';
1565 pszDosSlash = strchr(pszDosSlash + 1, '\\');
1566 }
1567#endif
1568 size_t cchTarget = strlen(szTarget);
1569 if (cchTarget < sizeof(pThis->aHdrs[0].Common.linkname))
1570 {
1571 /*
1572 * Create a header, add the link target and push it out.
1573 */
1574 rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1575 if (RT_SUCCESS(rc))
1576 {
1577 memcpy(pThis->aHdrs[0].Common.linkname, szTarget, cchTarget + 1);
1578 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1579 if (RT_SUCCESS(rc))
1580 {
1581 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]),
1582 true /*fBlocking*/, NULL);
1583 if (RT_SUCCESS(rc))
1584 {
1585 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1586 return VINF_SUCCESS;
1587 }
1588 pThis->rcFatal = rc;
1589 }
1590 }
1591 }
1592 else
1593 {
1594 /** @todo implement gnu and pax long name extensions. */
1595 rc = VERR_TAR_NAME_TOO_LONG;
1596 }
1597 }
1598 return rc;
1599}
1600
1601
1602/**
1603 * Adds a simple object to the stream.
1604 *
1605 * Simple objects only contains metadata, no actual data bits. Directories,
1606 * devices, fifos, sockets and such.
1607 *
1608 * @returns IPRT status code.
1609 * @param pThis The TAR writer instance.
1610 * @param pszPath The path to the object.
1611 * @param pObjInfo The object information.
1612 * @param pszOwnerNm The owner name.
1613 * @param pszGroupNm The group name.
1614 */
1615static int rtZipTarFssWriter_AddSimpleObject(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
1616 const char *pszOwnerNm, const char *pszGroupNm)
1617{
1618 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1619 if (RT_SUCCESS(rc))
1620 {
1621 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1622 if (RT_SUCCESS(rc))
1623 {
1624 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1625 return VINF_SUCCESS;
1626 }
1627 pThis->rcFatal = rc;
1628 }
1629 return rc;
1630}
1631
1632
1633/**
1634 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1635 */
1636static DECLCALLBACK(int) rtZipTarFssWriter_Close(void *pvThis)
1637{
1638 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1639
1640 rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1641
1642 RTVfsIoStrmRelease(pThis->hVfsIos);
1643 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
1644
1645 if (pThis->hVfsFile != NIL_RTVFSFILE)
1646 {
1647 RTVfsFileRelease(pThis->hVfsFile);
1648 pThis->hVfsFile = NIL_RTVFSFILE;
1649 }
1650
1651 if (pThis->pszOwner)
1652 {
1653 RTStrFree(pThis->pszOwner);
1654 pThis->pszOwner = NULL;
1655 }
1656 if (pThis->pszGroup)
1657 {
1658 RTStrFree(pThis->pszGroup);
1659 pThis->pszGroup = NULL;
1660 }
1661 if (pThis->pszPrefix)
1662 {
1663 RTStrFree(pThis->pszPrefix);
1664 pThis->pszPrefix = NULL;
1665 }
1666
1667 return VINF_SUCCESS;
1668}
1669
1670
1671/**
1672 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1673 */
1674static DECLCALLBACK(int) rtZipTarFssWriter_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1675{
1676 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1677 /* Take the lazy approach here, with the sideffect of providing some info
1678 that is actually kind of useful. */
1679 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
1680}
1681
1682
1683/**
1684 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnAdd}
1685 */
1686static DECLCALLBACK(int) rtZipTarFssWriter_Add(void *pvThis, const char *pszPath, RTVFSOBJ hVfsObj, uint32_t fFlags)
1687{
1688 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1689
1690 /*
1691 * Before we continue we must complete any current push file and check rcFatal.
1692 */
1693 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1694 if (RT_FAILURE(rc))
1695 return rc;
1696
1697 /*
1698 * Query information about the object.
1699 */
1700 RTFSOBJINFO ObjInfo;
1701 rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX);
1702 AssertRCReturn(rc, rc);
1703
1704 RTFSOBJINFO ObjOwnerName;
1705 rc = RTVfsObjQueryInfo(hVfsObj, &ObjOwnerName, RTFSOBJATTRADD_UNIX_OWNER);
1706 if (RT_FAILURE(rc) || ObjOwnerName.Attr.u.UnixOwner.szName[0] == '\0')
1707 strcpy(ObjOwnerName.Attr.u.UnixOwner.szName, "someone");
1708
1709 RTFSOBJINFO ObjGrpName;
1710 rc = RTVfsObjQueryInfo(hVfsObj, &ObjGrpName, RTFSOBJATTRADD_UNIX_GROUP);
1711 if (RT_FAILURE(rc) || ObjGrpName.Attr.u.UnixGroup.szName[0] == '\0')
1712 strcpy(ObjGrpName.Attr.u.UnixGroup.szName, "somegroup");
1713
1714 /*
1715 * Do type specific handling. File have several options and variations to
1716 * take into account, thus the mess.
1717 */
1718 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
1719 {
1720 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
1721 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE);
1722
1723 if (fFlags & RTVFSFSSTRM_ADD_F_STREAM)
1724 rc = rtZipTarFssWriter_AddFileStream(pThis, pszPath, hVfsIos, &ObjInfo,
1725 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1726 else if ( !(pThis->fFlags & RTZIPTAR_C_SPARSE)
1727 || ObjInfo.cbObject < RTZIPTAR_MIN_SPARSE)
1728 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
1729 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1730 else
1731 {
1732 RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
1733 if (hVfsFile != NIL_RTVFSFILE)
1734 {
1735 rc = rtZipTarFssWriter_AddFileSparse(pThis, pszPath, hVfsFile, hVfsIos, &ObjInfo,
1736 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1737 RTVfsFileRelease(hVfsFile);
1738 }
1739 else
1740 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
1741 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1742 }
1743 RTVfsIoStrmRelease(hVfsIos);
1744 }
1745 else if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
1746 {
1747 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
1748 AssertReturn(hVfsSymlink != NIL_RTVFSSYMLINK, VERR_WRONG_TYPE);
1749 rc = rtZipTarFssWriter_AddSymlink(pThis, pszPath, hVfsSymlink, &ObjInfo,
1750 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1751 RTVfsSymlinkRelease(hVfsSymlink);
1752 }
1753 else
1754 rc = rtZipTarFssWriter_AddSimpleObject(pThis, pszPath, &ObjInfo,
1755 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1756
1757 return rc;
1758}
1759
1760
1761/**
1762 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnPushFile}
1763 */
1764static DECLCALLBACK(int) rtZipTarFssWriter_PushFile(void *pvThis, const char *pszPath, uint64_t cbFile, PCRTFSOBJINFO paObjInfo,
1765 uint32_t cObjInfo, uint32_t fFlags, PRTVFSIOSTREAM phVfsIos)
1766{
1767 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1768
1769 /*
1770 * We can only deal with output of indeterminate length if the output is
1771 * seekable (see also rtZipTarFssWriter_AddFileStream).
1772 */
1773 AssertReturn(cbFile != UINT64_MAX || pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
1774 AssertReturn(RT_BOOL(cbFile == UINT64_MAX) == RT_BOOL(fFlags & RTVFSFSSTRM_ADD_F_STREAM), VERR_INVALID_FLAGS);
1775
1776 /*
1777 * Before we continue we must complete any current push file and check rcFatal.
1778 */
1779 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1780 if (RT_FAILURE(rc))
1781 return rc;
1782
1783 /*
1784 * If no object info was provideded, fake up some.
1785 */
1786 const char *pszOwnerNm = "someone";
1787 const char *pszGroupNm = "somegroup";
1788 RTFSOBJINFO ObjInfo;
1789 if (cObjInfo == 0)
1790 {
1791 /* Fake up a info. */
1792 RT_ZERO(ObjInfo);
1793 ObjInfo.cbObject = cbFile != UINT64_MAX ? cbFile : 0;
1794 ObjInfo.cbAllocated = cbFile != UINT64_MAX ? RT_ALIGN_64(cbFile, RTZIPTAR_BLOCKSIZE) : UINT64_MAX;
1795 RTTimeNow(&ObjInfo.ModificationTime);
1796 ObjInfo.BirthTime = ObjInfo.ModificationTime;
1797 ObjInfo.ChangeTime = ObjInfo.ModificationTime;
1798 ObjInfo.AccessTime = ObjInfo.ModificationTime;
1799 ObjInfo.Attr.fMode = RTFS_TYPE_FILE | 0666;
1800 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1801 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
1802 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
1803 ObjInfo.Attr.u.Unix.cHardlinks = 1;
1804 //ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
1805 //ObjInfo.Attr.u.Unix.INodeId = 0;
1806 //ObjInfo.Attr.u.Unix.fFlags = 0;
1807 //ObjInfo.Attr.u.Unix.GenerationId = 0;
1808 //ObjInfo.Attr.u.Unix.Device = 0;
1809 }
1810 else
1811 {
1812 /* Make a copy of the object info and adjust the size, if necessary. */
1813 ObjInfo = paObjInfo[0];
1814 Assert(ObjInfo.Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
1815 Assert(RTFS_IS_FILE(ObjInfo.Attr.fMode));
1816 if ((uint64_t)ObjInfo.cbObject != cbFile)
1817 {
1818 ObjInfo.cbObject = cbFile != UINT64_MAX ? cbFile : 0;
1819 ObjInfo.cbAllocated = cbFile != UINT64_MAX ? RT_ALIGN_64(cbFile, RTZIPTAR_BLOCKSIZE) : UINT64_MAX;
1820 }
1821
1822 /* Lookup the group and user names. */
1823 for (uint32_t i = 0; i < cObjInfo; i++)
1824 if ( paObjInfo[i].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_OWNER
1825 && paObjInfo[i].Attr.u.UnixOwner.szName[0] != '\0')
1826 pszOwnerNm = paObjInfo[i].Attr.u.UnixOwner.szName;
1827 else if ( paObjInfo[i].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_GROUP
1828 && paObjInfo[i].Attr.u.UnixGroup.szName[0] != '\0')
1829 pszGroupNm = paObjInfo[i].Attr.u.UnixGroup.szName;
1830 }
1831
1832 /*
1833 * Create an I/O stream object for the caller to use.
1834 */
1835 RTFOFF const offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
1836 AssertReturn(offHdr >= 0, (int)offHdr);
1837
1838 PRTZIPTARFSSTREAMWRITERPUSH pPush;
1839 RTVFSIOSTREAM hVfsIos;
1840 if (pThis->hVfsFile == NIL_RTVFSFILE)
1841 {
1842 rc = RTVfsNewIoStream(&g_rtZipTarWriterIoStrmOps, sizeof(*pPush), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
1843 &hVfsIos, (void **)&pPush);
1844 if (RT_FAILURE(rc))
1845 return rc;
1846 }
1847 else
1848 {
1849 RTVFSFILE hVfsFile;
1850 rc = RTVfsNewFile(&g_rtZipTarWriterFileOps, sizeof(*pPush), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
1851 &hVfsFile, (void **)&pPush);
1852 if (RT_FAILURE(rc))
1853 return rc;
1854 hVfsIos = RTVfsFileToIoStream(hVfsFile);
1855 RTVfsFileRelease(hVfsFile);
1856 }
1857 pPush->pParent = NULL;
1858 pPush->cbExpected = cbFile;
1859 pPush->offHdr = (uint64_t)offHdr;
1860 pPush->offData = 0;
1861 pPush->offCurrent = 0;
1862 pPush->cbCurrent = 0;
1863 pPush->ObjInfo = ObjInfo;
1864 pPush->fOpenEnded = cbFile == UINT64_MAX;
1865
1866 /*
1867 * Produce and write file headers.
1868 */
1869 rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, &ObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_NORMAL);
1870 if (RT_SUCCESS(rc))
1871 {
1872 size_t cbHdrs = pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1873 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, cbHdrs, true /*fBlocking*/, NULL);
1874 if (RT_SUCCESS(rc))
1875 {
1876 pThis->cbWritten += cbHdrs;
1877
1878 /*
1879 * Complete the object and return.
1880 */
1881 pPush->offData = pPush->offHdr + cbHdrs;
1882 if (cbFile == UINT64_MAX)
1883 pPush->cbExpected = (uint64_t)(RTFOFF_MAX - _4K) - pPush->offData;
1884 pPush->pParent = pThis;
1885 pThis->pPush = pPush;
1886
1887 *phVfsIos = hVfsIos;
1888 return VINF_SUCCESS;
1889 }
1890 pThis->rcFatal = rc;
1891 }
1892
1893 RTVfsIoStrmRelease(hVfsIos);
1894 return rc;
1895}
1896
1897
1898/**
1899 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnEnd}
1900 */
1901static DECLCALLBACK(int) rtZipTarFssWriter_End(void *pvThis)
1902{
1903 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1904
1905 /*
1906 * Make sure to complete any pending push file and that rcFatal is fine.
1907 */
1908 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1909 if (RT_SUCCESS(rc))
1910 {
1911 /*
1912 * There are supposed to be two zero headers at the end of the archive.
1913 * GNU tar may write more because of the way it does buffering,
1914 * libarchive OTOH writes exactly two.
1915 */
1916 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, RTZIPTAR_BLOCKSIZE * 2, true /*fBlocking*/, NULL);
1917 if (RT_SUCCESS(rc))
1918 {
1919 pThis->cbWritten += RTZIPTAR_BLOCKSIZE * 2;
1920
1921 /*
1922 * Flush the output.
1923 */
1924 rc = RTVfsIoStrmFlush(pThis->hVfsIos);
1925 if (RT_SUCCESS(rc))
1926 return rc;
1927 }
1928 pThis->rcFatal = rc;
1929 }
1930 return rc;
1931}
1932
1933
1934/**
1935 * Tar filesystem stream operations.
1936 */
1937static const RTVFSFSSTREAMOPS g_rtZipTarFssOps =
1938{
1939 { /* Obj */
1940 RTVFSOBJOPS_VERSION,
1941 RTVFSOBJTYPE_FS_STREAM,
1942 "TarFsStreamWriter",
1943 rtZipTarFssWriter_Close,
1944 rtZipTarFssWriter_QueryInfo,
1945 RTVFSOBJOPS_VERSION
1946 },
1947 RTVFSFSSTREAMOPS_VERSION,
1948 0,
1949 NULL,
1950 rtZipTarFssWriter_Add,
1951 rtZipTarFssWriter_PushFile,
1952 rtZipTarFssWriter_End,
1953 RTVFSFSSTREAMOPS_VERSION
1954};
1955
1956
1957RTDECL(int) RTZipTarFsStreamToIoStream(RTVFSIOSTREAM hVfsIosOut, RTZIPTARFORMAT enmFormat,
1958 uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
1959{
1960 /*
1961 * Input validation.
1962 */
1963 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
1964 *phVfsFss = NIL_RTVFSFSSTREAM;
1965 AssertPtrReturn(hVfsIosOut, VERR_INVALID_HANDLE);
1966 AssertReturn(enmFormat > RTZIPTARFORMAT_INVALID && enmFormat < RTZIPTARFORMAT_END, VERR_INVALID_PARAMETER);
1967 AssertReturn(!(fFlags & ~RTZIPTAR_C_VALID_MASK), VERR_INVALID_FLAGS);
1968
1969 if (enmFormat == RTZIPTARFORMAT_DEFAULT)
1970 enmFormat = RTZIPTARFORMAT_GNU;
1971 AssertReturn( enmFormat == RTZIPTARFORMAT_GNU
1972 || enmFormat == RTZIPTARFORMAT_USTAR
1973 , VERR_NOT_IMPLEMENTED); /* Only implementing GNU and USTAR output at the moment. */
1974
1975 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosOut);
1976 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1977
1978 /*
1979 * Retain the input stream and create a new filesystem stream handle.
1980 */
1981 PRTZIPTARFSSTREAMWRITER pThis;
1982 RTVFSFSSTREAM hVfsFss;
1983 int rc = RTVfsNewFsStream(&g_rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, false /*fReadOnly*/,
1984 &hVfsFss, (void **)&pThis);
1985 if (RT_SUCCESS(rc))
1986 {
1987 pThis->hVfsIos = hVfsIosOut;
1988 pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIosOut);
1989
1990 pThis->enmFormat = enmFormat;
1991 pThis->fFlags = fFlags;
1992 pThis->rcFatal = VINF_SUCCESS;
1993
1994 pThis->uidOwner = NIL_RTUID;
1995 pThis->pszOwner = NULL;
1996 pThis->gidGroup = NIL_RTGID;
1997 pThis->pszGroup = NULL;
1998 pThis->pszPrefix = NULL;
1999 pThis->pModTime = NULL;
2000 pThis->fFileModeAndMask = ~(RTFMODE)0;
2001 pThis->fFileModeOrMask = 0;
2002 pThis->fDirModeAndMask = ~(RTFMODE)0;
2003 pThis->fDirModeOrMask = 0;
2004
2005 *phVfsFss = hVfsFss;
2006 return VINF_SUCCESS;
2007 }
2008
2009 RTVfsIoStrmRelease(hVfsIosOut);
2010 return rc;
2011}
2012
2013
2014RTDECL(int) RTZipTarFsStreamSetOwner(RTVFSFSSTREAM hVfsFss, RTUID uid, const char *pszOwner)
2015{
2016 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2017 AssertReturn(pThis, VERR_WRONG_TYPE);
2018
2019 pThis->uidOwner = uid;
2020 if (pThis->pszOwner)
2021 {
2022 RTStrFree(pThis->pszOwner);
2023 pThis->pszOwner = NULL;
2024 }
2025 if (pszOwner)
2026 {
2027 pThis->pszOwner = RTStrDup(pszOwner);
2028 AssertReturn(pThis->pszOwner, VERR_NO_STR_MEMORY);
2029 }
2030
2031 return VINF_SUCCESS;
2032}
2033
2034
2035RTDECL(int) RTZipTarFsStreamSetGroup(RTVFSFSSTREAM hVfsFss, RTGID gid, const char *pszGroup)
2036{
2037 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2038 AssertReturn(pThis, VERR_WRONG_TYPE);
2039
2040 pThis->gidGroup = gid;
2041 if (pThis->pszGroup)
2042 {
2043 RTStrFree(pThis->pszGroup);
2044 pThis->pszGroup = NULL;
2045 }
2046 if (pszGroup)
2047 {
2048 pThis->pszGroup = RTStrDup(pszGroup);
2049 AssertReturn(pThis->pszGroup, VERR_NO_STR_MEMORY);
2050 }
2051
2052 return VINF_SUCCESS;
2053}
2054
2055
2056RTDECL(int) RTZipTarFsStreamSetPrefix(RTVFSFSSTREAM hVfsFss, const char *pszPrefix)
2057{
2058 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2059 AssertReturn(pThis, VERR_WRONG_TYPE);
2060 AssertReturn(!pszPrefix || *pszPrefix, VERR_INVALID_NAME);
2061
2062 if (pThis->pszPrefix)
2063 {
2064 RTStrFree(pThis->pszPrefix);
2065 pThis->pszPrefix = NULL;
2066 pThis->cchPrefix = 0;
2067 }
2068 if (pszPrefix)
2069 {
2070 /*
2071 * Make a copy of the prefix, make sure it ends with a slash,
2072 * then flip DOS slashes.
2073 */
2074 size_t cchPrefix = strlen(pszPrefix);
2075 char *pszCopy = RTStrAlloc(cchPrefix + 3);
2076 AssertReturn(pszCopy, VERR_NO_STR_MEMORY);
2077 memcpy(pszCopy, pszPrefix, cchPrefix + 1);
2078
2079 RTPathEnsureTrailingSeparator(pszCopy, cchPrefix + 3);
2080
2081#if RTPATH_STYLE != RTPATH_STR_F_STYLE_UNIX
2082 char *pszDosSlash = strchr(pszCopy, '\\');
2083 while (pszDosSlash)
2084 {
2085 *pszDosSlash = '/';
2086 pszDosSlash = strchr(pszDosSlash + 1, '\\');
2087 }
2088#endif
2089
2090 pThis->cchPrefix = cchPrefix + strlen(&pszCopy[cchPrefix]);
2091 pThis->pszPrefix = pszCopy;
2092 }
2093
2094 return VINF_SUCCESS;
2095}
2096
2097
2098RTDECL(int) RTZipTarFsStreamSetModTime(RTVFSFSSTREAM hVfsFss, PCRTTIMESPEC pModificationTime)
2099{
2100 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2101 AssertReturn(pThis, VERR_WRONG_TYPE);
2102
2103 if (pModificationTime)
2104 {
2105 pThis->ModTime = *pModificationTime;
2106 pThis->pModTime = &pThis->ModTime;
2107 }
2108 else
2109 pThis->pModTime = NULL;
2110
2111 return VINF_SUCCESS;
2112}
2113
2114
2115RTDECL(int) RTZipTarFsStreamSetFileMode(RTVFSFSSTREAM hVfsFss, RTFMODE fAndMode, RTFMODE fOrMode)
2116{
2117 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2118 AssertReturn(pThis, VERR_WRONG_TYPE);
2119
2120 pThis->fFileModeAndMask = fAndMode | ~RTFS_UNIX_ALL_PERMS;
2121 pThis->fFileModeOrMask = fOrMode & RTFS_UNIX_ALL_PERMS;
2122 return VINF_SUCCESS;
2123}
2124
2125
2126RTDECL(int) RTZipTarFsStreamSetDirMode(RTVFSFSSTREAM hVfsFss, RTFMODE fAndMode, RTFMODE fOrMode)
2127{
2128 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2129 AssertReturn(pThis, VERR_WRONG_TYPE);
2130
2131 pThis->fDirModeAndMask = fAndMode | ~RTFS_UNIX_ALL_PERMS;
2132 pThis->fDirModeOrMask = fOrMode & RTFS_UNIX_ALL_PERMS;
2133 return VINF_SUCCESS;
2134}
2135
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