VirtualBox

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

Last change on this file since 78365 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • 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 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - TAR Virtual Filesystem, Writer.
4 */
5
6/*
7 * Copyright (C) 2010-2019 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_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
822 rtZipTarWriterPush_SetMode,
823 rtZipTarWriterPush_SetTimes,
824 rtZipTarWriterPush_SetOwner,
825 RTVFSOBJSETOPS_VERSION
826 },
827 rtZipTarWriterPush_Seek,
828 rtZipTarWriterPush_QuerySize,
829 NULL /*SetSize*/,
830 NULL /*QueryMaxSize*/,
831 RTVFSFILEOPS_VERSION
832};
833
834
835
836/**
837 * Checks rcFatal and completes any current push file.
838 *
839 * On return the output stream position will be at the next header location.
840 *
841 * After this call, the push object no longer can write anything.
842 *
843 * @returns IPRT status code.
844 * @param pThis The TAR writer instance.
845 */
846static int rtZipTarFssWriter_CompleteCurrentPushFile(PRTZIPTARFSSTREAMWRITER pThis)
847{
848 /*
849 * Check if there is a push file pending, remove it if there is.
850 * We also check for fatal errors at this point so the caller doesn't need to.
851 */
852 PRTZIPTARFSSTREAMWRITERPUSH pPush = pThis->pPush;
853 if (!pPush)
854 {
855 AssertRC(pThis->rcFatal);
856 return pThis->rcFatal;
857 }
858
859 pThis->pPush = NULL;
860 pPush->pParent = NULL;
861
862 int rc = pThis->rcFatal;
863 AssertRCReturn(rc, rc);
864
865 /*
866 * Do we need to update the header. pThis->aHdrs[0] will retain the current
867 * content at pPush->offHdr and we only need to update the size.
868 */
869 if (pPush->fOpenEnded)
870 {
871 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, pPush->cbCurrent);
872 if (RT_SUCCESS(rc))
873 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
874 if (RT_SUCCESS(rc))
875 {
876 rc = RTVfsFileWriteAt(pThis->hVfsFile, pPush->offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
877 if (RT_SUCCESS(rc))
878 rc = RTVfsFileSeek(pThis->hVfsFile, pPush->offData + pPush->cbCurrent, RTFILE_SEEK_BEGIN, NULL);
879 }
880 }
881 /*
882 * Check that we've received all the data we were promissed in the PushFile
883 * call, fail if we weren't.
884 */
885 else
886 AssertMsgStmt(pPush->cbCurrent == pPush->cbExpected,
887 ("cbCurrent=%#RX64 cbExpected=%#RX64\n", pPush->cbCurrent, pPush->cbExpected),
888 rc = VERR_BUFFER_UNDERFLOW);
889 if (RT_SUCCESS(rc))
890 {
891 /*
892 * Do zero padding if necessary.
893 */
894 if (pPush->cbCurrent & (RTZIPTAR_BLOCKSIZE - 1))
895 {
896 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (pPush->cbCurrent & (RTZIPTAR_BLOCKSIZE - 1));
897 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
898 if (RT_SUCCESS(rc))
899 pThis->cbWritten += cbToZero;
900 }
901 }
902
903 if (RT_SUCCESS(rc))
904 return VINF_SUCCESS;
905 pThis->rcFatal = rc;
906 return rc;
907}
908
909
910/**
911 * Allocates a buffer for transfering file data.
912 *
913 * @note Will use the 3rd TAR header as fallback buffer if we're out of
914 * memory!
915 *
916 * @returns Pointer to buffer (won't ever fail).
917 * @param pThis The TAR writer instance.
918 * @param pcbBuf Where to return the buffer size. This will be a
919 * multiple of the TAR block size.
920 * @param ppvFree Where to return the pointer to pass to RTMemTmpFree
921 * when done with the buffer.
922 * @param cbFile The file size. Used as a buffer size hint.
923 */
924static uint8_t *rtZipTarFssWrite_AllocBuf(PRTZIPTARFSSTREAMWRITER pThis, size_t *pcbBuf, void **ppvFree, uint64_t cbObject)
925{
926 uint8_t *pbBuf;
927
928 /*
929 * If this is a large file, try for a large buffer with 16KB alignment.
930 */
931 if (cbObject >= _64M)
932 {
933 pbBuf = (uint8_t *)RTMemTmpAlloc(_2M + _16K - 1);
934 if (pbBuf)
935 {
936 *pcbBuf = _2M;
937 *ppvFree = pbBuf;
938 return RT_ALIGN_PT(pbBuf, _16K, uint8_t *);
939 }
940 }
941 /*
942 * 4KB aligned 512KB buffer if larger 512KB or larger.
943 */
944 else if (cbObject >= _512K)
945 {
946 pbBuf = (uint8_t *)RTMemTmpAlloc(_512K + _4K - 1);
947 if (pbBuf)
948 {
949 *pcbBuf = _512K;
950 *ppvFree = pbBuf;
951 return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
952 }
953 }
954 /*
955 * Otherwise a 4KB aligned 128KB buffer.
956 */
957 else
958 {
959 pbBuf = (uint8_t *)RTMemTmpAlloc(_128K + _4K - 1);
960 if (pbBuf)
961 {
962 *pcbBuf = _128K;
963 *ppvFree = pbBuf;
964 return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
965 }
966 }
967
968 /*
969 * If allocation failed, fallback on a 16KB buffer without any extra alignment.
970 */
971 pbBuf = (uint8_t *)RTMemTmpAlloc(_16K);
972 if (pbBuf)
973 {
974 *pcbBuf = _16K;
975 *ppvFree = pbBuf;
976 return pbBuf;
977 }
978
979 /*
980 * Final fallback, 512KB buffer using the 3rd header.
981 */
982 AssertCompile(RT_ELEMENTS(pThis->aHdrs) >= 3);
983 *pcbBuf = sizeof(pThis->aHdrs[2]);
984 *ppvFree = NULL;
985 return (uint8_t *)&pThis->aHdrs[2];
986}
987
988
989/**
990 * Frees the sparse info for a TAR file.
991 *
992 * @param pSparse The sparse info to free.
993 */
994static void rtZipTarFssWriter_SparseInfoDestroy(PRTZIPTARSPARSE pSparse)
995{
996 PRTZIPTARSPARSECHUNK pCur;
997 PRTZIPTARSPARSECHUNK pNext;
998 RTListForEachSafe(&pSparse->ChunkHead, pCur, pNext, RTZIPTARSPARSECHUNK, Entry)
999 RTMemTmpFree(pCur);
1000 RTMemTmpFree(pSparse);
1001}
1002
1003
1004/**
1005 * Adds a data span to the sparse info.
1006 *
1007 * @returns VINF_SUCCESS or VINF_NO_TMP_MEMORY.
1008 * @param pSparse The sparse info to free.
1009 * @param offSpan Offset of the span.
1010 * @param cbSpan Number of bytes.
1011 */
1012static int rtZipTarFssWriter_SparseInfoAddSpan(PRTZIPTARSPARSE pSparse, uint64_t offSpan, uint64_t cbSpan)
1013{
1014 /*
1015 * Get the chunk we're adding it to.
1016 */
1017 PRTZIPTARSPARSECHUNK pChunk;
1018 if (pSparse->iNextSpan != 0)
1019 {
1020 pChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1021 Assert(pSparse->iNextSpan < RT_ELEMENTS(pChunk->aSpans));
1022 }
1023 else
1024 {
1025 pChunk = (PRTZIPTARSPARSECHUNK)RTMemTmpAllocZ(sizeof(*pChunk));
1026 if (!pChunk)
1027 return VERR_NO_TMP_MEMORY;
1028 RTListAppend(&pSparse->ChunkHead, &pChunk->Entry);
1029 }
1030
1031 /*
1032 * Append it.
1033 */
1034 pSparse->cDataSpans += 1;
1035 pSparse->cbDataSpans += cbSpan;
1036 pChunk->aSpans[pSparse->iNextSpan].cb = cbSpan;
1037 pChunk->aSpans[pSparse->iNextSpan].off = offSpan;
1038 if (++pSparse->iNextSpan >= RT_ELEMENTS(pChunk->aSpans))
1039 pSparse->iNextSpan = 0;
1040 return VINF_SUCCESS;
1041}
1042
1043
1044/**
1045 * Scans the input stream recording non-zero blocks.
1046 */
1047static int rtZipTarFssWriter_ScanSparseFile(PRTZIPTARFSSTREAMWRITER pThis, RTVFSFILE hVfsFile, uint64_t cbFile,
1048 size_t cbBuf, uint8_t *pbBuf, PRTZIPTARSPARSE *ppSparse)
1049{
1050 RT_NOREF(pThis);
1051
1052 /*
1053 * Create an empty sparse info bundle.
1054 */
1055 PRTZIPTARSPARSE pSparse = (PRTZIPTARSPARSE)RTMemTmpAlloc(sizeof(*pSparse));
1056 AssertReturn(pSparse, VERR_NO_MEMORY);
1057 pSparse->cbDataSpans = 0;
1058 pSparse->cDataSpans = 0;
1059 pSparse->iNextSpan = 0;
1060 RTListInit(&pSparse->ChunkHead);
1061
1062 /*
1063 * Scan the file from the start.
1064 */
1065 int rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
1066 if (RT_SUCCESS(rc))
1067 {
1068 bool fZeroSpan = false;
1069 uint64_t offSpan = 0;
1070 uint64_t cbSpan = 0;
1071
1072 for (uint64_t off = 0; off < cbFile;)
1073 {
1074 uint64_t cbLeft = cbFile - off;
1075 size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
1076 rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
1077 if (RT_FAILURE(rc))
1078 break;
1079 size_t cBlocks = cbToRead / RTZIPTAR_BLOCKSIZE;
1080
1081 /* Zero pad the final buffer to a multiple of the blocksize. */
1082 if (!(cbToRead & (RTZIPTAR_BLOCKSIZE - 1)))
1083 { /* likely */ }
1084 else
1085 {
1086 AssertBreakStmt(cbLeft == cbToRead, rc = VERR_INTERNAL_ERROR_3);
1087 RT_BZERO(&pbBuf[cbToRead], RTZIPTAR_BLOCKSIZE - (cbToRead & (RTZIPTAR_BLOCKSIZE - 1)));
1088 cBlocks++;
1089 }
1090
1091 /*
1092 * Process the blocks we've just read one by one.
1093 */
1094 uint8_t const *pbBlock = pbBuf;
1095 for (size_t iBlock = 0; iBlock < cBlocks; iBlock++)
1096 {
1097 bool fZeroBlock = ASMMemIsZero(pbBlock, RTZIPTAR_BLOCKSIZE);
1098 if (fZeroBlock == fZeroSpan)
1099 cbSpan += RTZIPTAR_BLOCKSIZE;
1100 else
1101 {
1102 if (!fZeroSpan && cbSpan)
1103 {
1104 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
1105 if (RT_FAILURE(rc))
1106 break;
1107 }
1108 fZeroSpan = fZeroBlock;
1109 offSpan = off;
1110 cbSpan = RTZIPTAR_BLOCKSIZE;
1111 }
1112
1113 /* next block. */
1114 pbBlock += RTZIPTAR_BLOCKSIZE;
1115 off += RTZIPTAR_BLOCKSIZE;
1116 }
1117 }
1118
1119 /*
1120 * Deal with the final span. If we've got zeros thowards the end, we
1121 * must add a zero byte data span at the end.
1122 */
1123 if (RT_SUCCESS(rc))
1124 {
1125 if (!fZeroSpan && cbSpan)
1126 {
1127 if (cbFile & (RTZIPTAR_BLOCKSIZE - 1))
1128 {
1129 Assert(!(cbSpan & (RTZIPTAR_BLOCKSIZE - 1)));
1130 cbSpan -= RTZIPTAR_BLOCKSIZE;
1131 cbSpan |= cbFile & (RTZIPTAR_BLOCKSIZE - 1);
1132 }
1133 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
1134 }
1135 if (RT_SUCCESS(rc))
1136 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, cbFile, 0);
1137 }
1138 }
1139
1140 if (RT_SUCCESS(rc))
1141 {
1142 /*
1143 * Return the file back to the start position before we return so that we
1144 * can segue into the regular rtZipTarFssWriter_AddFile without further ado.
1145 */
1146 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
1147 if (RT_SUCCESS(rc))
1148 {
1149 *ppSparse = pSparse;
1150 return VINF_SUCCESS;
1151 }
1152 }
1153
1154 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1155 *ppSparse = NULL;
1156 return rc;
1157}
1158
1159
1160/**
1161 * Writes GNU the sparse file headers.
1162 *
1163 * @returns IPRT status code.
1164 * @param pThis The TAR writer instance.
1165 * @param pszPath The path to the file.
1166 * @param pObjInfo The object information.
1167 * @param pszOwnerNm The owner name.
1168 * @param pszGroupNm The group name.
1169 * @param pSparse The sparse file info.
1170 */
1171static int rtZipTarFssWriter_WriteGnuSparseHeaders(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
1172 const char *pszOwnerNm, const char *pszGroupNm, PCRTZIPTARSPARSE pSparse)
1173{
1174 /*
1175 * Format the first header.
1176 */
1177 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_GNU_SPARSE);
1178 AssertRCReturn(rc, rc);
1179 AssertReturn(pThis->cHdrs == 1, VERR_INTERNAL_ERROR_2);
1180
1181 /* data size. */
1182 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, pSparse->cbDataSpans);
1183 AssertRCReturn(rc, rc);
1184
1185 /* realsize. */
1186 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Gnu.realsize, pObjInfo->cbObject);
1187 AssertRCReturn(rc, rc);
1188
1189 Assert(pThis->aHdrs[0].Gnu.isextended == 0);
1190
1191 /*
1192 * Walk the sparse spans, fill and write headers one by one.
1193 */
1194 PRTZIPTARGNUSPARSE paSparse = &pThis->aHdrs[0].Gnu.sparse[0];
1195 uint32_t cSparse = RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse);
1196 uint32_t iSparse = 0;
1197
1198 PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1199 PRTZIPTARSPARSECHUNK pChunk;
1200 RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
1201 {
1202 uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
1203 ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
1204 for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
1205 {
1206 /* Flush the header? */
1207 if (iSparse >= cSparse)
1208 {
1209 if (cSparse != RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse))
1210 pThis->aHdrs[0].GnuSparse.isextended = 1; /* more headers to come */
1211 else
1212 {
1213 pThis->aHdrs[0].Gnu.isextended = 1; /* more headers to come */
1214 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1215 }
1216 if (RT_SUCCESS(rc))
1217 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1218 if (RT_FAILURE(rc))
1219 return rc;
1220 RT_ZERO(pThis->aHdrs[0]);
1221 cSparse = RT_ELEMENTS(pThis->aHdrs[0].GnuSparse.sp);
1222 iSparse = 0;
1223 paSparse = &pThis->aHdrs[0].GnuSparse.sp[0];
1224 }
1225
1226 /* Append sparse data segment. */
1227 rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].offset, pChunk->aSpans[iSpan].off);
1228 AssertRCReturn(rc, rc);
1229 rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].numbytes, pChunk->aSpans[iSpan].cb);
1230 AssertRCReturn(rc, rc);
1231 iSparse++;
1232 }
1233 }
1234
1235 /*
1236 * The final header.
1237 */
1238 if (iSparse != 0)
1239 {
1240 if (cSparse != RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse))
1241 Assert(pThis->aHdrs[0].GnuSparse.isextended == 0);
1242 else
1243 {
1244 Assert(pThis->aHdrs[0].Gnu.isextended == 0);
1245 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1246 }
1247 if (RT_SUCCESS(rc))
1248 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1249 }
1250 pThis->cHdrs = 0;
1251 return rc;
1252}
1253
1254
1255/**
1256 * Adds a potentially sparse file to the output.
1257 *
1258 * @returns IPRT status code.
1259 * @param pThis The TAR writer instance.
1260 * @param pszPath The path to the file.
1261 * @param hVfsFile The potentially sparse file.
1262 * @param hVfsIos The I/O stream of the file. Same as @a hVfsFile.
1263 * @param pObjInfo The object information.
1264 * @param pszOwnerNm The owner name.
1265 * @param pszGroupNm The group name.
1266 */
1267static int rtZipTarFssWriter_AddFileSparse(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSFILE hVfsFile,
1268 RTVFSIOSTREAM hVfsIos, PCRTFSOBJINFO pObjInfo,
1269 const char *pszOwnerNm, const char *pszGroupNm)
1270{
1271 /*
1272 * Scan the input file to locate all zero blocks.
1273 */
1274 void *pvBufFree;
1275 size_t cbBuf;
1276 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
1277
1278 PRTZIPTARSPARSE pSparse;
1279 int rc = rtZipTarFssWriter_ScanSparseFile(pThis, hVfsFile, pObjInfo->cbObject, cbBuf, pbBuf, &pSparse);
1280 if (RT_SUCCESS(rc))
1281 {
1282 /*
1283 * If there aren't at least 2 zero blocks in the file, don't bother
1284 * doing the sparse stuff and store it as a normal file.
1285 */
1286 if (pSparse->cbDataSpans + RTZIPTAR_BLOCKSIZE > (uint64_t)pObjInfo->cbObject)
1287 {
1288 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1289 RTMemTmpFree(pvBufFree);
1290 return rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, pObjInfo, pszOwnerNm, pszGroupNm);
1291 }
1292
1293 /*
1294 * Produce and write the headers.
1295 */
1296 if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
1297 rc = rtZipTarFssWriter_WriteGnuSparseHeaders(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, pSparse);
1298 else
1299 AssertStmt(pThis->enmFormat != RTZIPTARFORMAT_GNU, rc = VERR_NOT_IMPLEMENTED);
1300 if (RT_SUCCESS(rc))
1301 {
1302 /*
1303 * Write the file bytes.
1304 */
1305 PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1306 PRTZIPTARSPARSECHUNK pChunk;
1307 RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
1308 {
1309 uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
1310 ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
1311 for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
1312 {
1313 rc = RTVfsFileSeek(hVfsFile, pChunk->aSpans[iSpan].off, RTFILE_SEEK_BEGIN, NULL);
1314 if (RT_FAILURE(rc))
1315 break;
1316 uint64_t cbLeft = pChunk->aSpans[iSpan].cb;
1317 Assert( !(cbLeft & (RTZIPTAR_BLOCKSIZE - 1))
1318 || (iSpan + 1 == cSpans && pChunk == pLastChunk));
1319 while (cbLeft > 0)
1320 {
1321 size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
1322 rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
1323 if (RT_SUCCESS(rc))
1324 {
1325 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToRead, true /*fBlocking*/, NULL);
1326 if (RT_SUCCESS(rc))
1327 {
1328 pThis->cbWritten += cbToRead;
1329 cbLeft -= cbToRead;
1330 continue;
1331 }
1332 }
1333 break;
1334 }
1335 if (RT_FAILURE(rc))
1336 break;
1337 }
1338 }
1339
1340 /*
1341 * Do the zero padding.
1342 */
1343 if ( RT_SUCCESS(rc)
1344 && (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1)))
1345 {
1346 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1));
1347 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1348 if (RT_SUCCESS(rc))
1349 pThis->cbWritten += cbToZero;
1350 }
1351 }
1352
1353 if (RT_FAILURE(rc))
1354 pThis->rcFatal = rc;
1355 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1356 }
1357 RTMemTmpFree(pvBufFree);
1358 return rc;
1359}
1360
1361
1362/**
1363 * Adds an I/O stream of indeterminate length to the TAR file.
1364 *
1365 * This requires the output to be seekable, i.e. a file, because we need to go
1366 * back and update @c size field of the TAR header after pumping all the data
1367 * bytes thru and establishing the file length.
1368 *
1369 * @returns IPRT status code.
1370 * @param pThis The TAR writer instance.
1371 * @param pszPath The path to the file.
1372 * @param hVfsIos The I/O stream of the file.
1373 * @param pObjInfo The object information.
1374 * @param pszOwnerNm The owner name.
1375 * @param pszGroupNm The group name.
1376 */
1377static int rtZipTarFssWriter_AddFileStream(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
1378 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1379{
1380 AssertReturn(pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
1381
1382 /*
1383 * Append the header.
1384 */
1385 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1386 if (RT_SUCCESS(rc))
1387 {
1388 RTFOFF const offHdr = RTVfsFileTell(pThis->hVfsFile);
1389 if (offHdr >= 0)
1390 {
1391 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1392 if (RT_SUCCESS(rc))
1393 {
1394 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1395
1396 /*
1397 * Transfer the bytes.
1398 */
1399 void *pvBufFree;
1400 size_t cbBuf;
1401 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree,
1402 pObjInfo->cbObject > 0 && pObjInfo->cbObject != RTFOFF_MAX
1403 ? pObjInfo->cbObject : _1G);
1404
1405 uint64_t cbReadTotal = 0;
1406 for (;;)
1407 {
1408 size_t cbRead = 0;
1409 int rc2 = rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbBuf, true /*fBlocking*/, &cbRead);
1410 if (RT_SUCCESS(rc))
1411 {
1412 cbReadTotal += cbRead;
1413 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
1414 if (RT_SUCCESS(rc))
1415 {
1416 pThis->cbWritten += cbRead;
1417 if (rc2 != VINF_EOF)
1418 continue;
1419 }
1420 }
1421 Assert(rc != VERR_EOF /* expecting VINF_EOF! */);
1422 break;
1423 }
1424
1425 RTMemTmpFree(pvBufFree);
1426
1427 /*
1428 * Do the zero padding.
1429 */
1430 if ((cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1)) && RT_SUCCESS(rc))
1431 {
1432 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1));
1433 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1434 if (RT_SUCCESS(rc))
1435 pThis->cbWritten += cbToZero;
1436 }
1437
1438 /*
1439 * Update the header. We ASSUME that aHdr[0] is unmodified
1440 * from before the data pumping above and just update the size.
1441 */
1442 if ((RTFOFF)cbReadTotal != pObjInfo->cbObject && RT_SUCCESS(rc))
1443 {
1444 RTFOFF const offRestore = RTVfsFileTell(pThis->hVfsFile);
1445 if (offRestore >= 0)
1446 {
1447 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, cbReadTotal);
1448 if (RT_SUCCESS(rc))
1449 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1450 if (RT_SUCCESS(rc))
1451 {
1452 rc = RTVfsFileWriteAt(pThis->hVfsFile, offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
1453 if (RT_SUCCESS(rc))
1454 rc = RTVfsFileSeek(pThis->hVfsFile, offRestore, RTFILE_SEEK_BEGIN, NULL);
1455 }
1456 }
1457 else
1458 rc = (int)offRestore;
1459 }
1460
1461 if (RT_SUCCESS(rc))
1462 return VINF_SUCCESS;
1463 }
1464 }
1465 else
1466 rc = (int)offHdr;
1467 pThis->rcFatal = rc;
1468 }
1469 return rc;
1470}
1471
1472
1473/**
1474 * Adds a file to the stream.
1475 *
1476 * @returns IPRT status code.
1477 * @param pThis The TAR writer instance.
1478 * @param pszPath The path to the file.
1479 * @param hVfsIos The I/O stream of the file.
1480 * @param fFlags The RTVFSFSSTREAMOPS::pfnAdd flags.
1481 * @param pObjInfo The object information.
1482 * @param pszOwnerNm The owner name.
1483 * @param pszGroupNm The group name.
1484 */
1485static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
1486 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1487{
1488 /*
1489 * Append the header.
1490 */
1491 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1492 if (RT_SUCCESS(rc))
1493 {
1494 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1495 if (RT_SUCCESS(rc))
1496 {
1497 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1498
1499 /*
1500 * Copy the bytes. Padding the last buffer to a multiple of 512.
1501 */
1502 void *pvBufFree;
1503 size_t cbBuf;
1504 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
1505
1506 uint64_t cbLeft = pObjInfo->cbObject;
1507 while (cbLeft > 0)
1508 {
1509 size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbLeft;
1510 rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
1511 if (RT_FAILURE(rc))
1512 break;
1513
1514 size_t cbToWrite = cbRead;
1515 if (cbRead & (RTZIPTAR_BLOCKSIZE - 1))
1516 {
1517 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbRead & (RTZIPTAR_BLOCKSIZE - 1));
1518 memset(&pbBuf[cbRead], 0, cbToZero);
1519 cbToWrite += cbToZero;
1520 }
1521
1522 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
1523 if (RT_FAILURE(rc))
1524 break;
1525 pThis->cbWritten += cbToWrite;
1526 cbLeft -= cbRead;
1527 }
1528
1529 RTMemTmpFree(pvBufFree);
1530
1531 if (RT_SUCCESS(rc))
1532 return VINF_SUCCESS;
1533 }
1534 pThis->rcFatal = rc;
1535 }
1536 return rc;
1537}
1538
1539
1540/**
1541 * Adds a symbolic link to the stream.
1542 *
1543 * @returns IPRT status code.
1544 * @param pThis The TAR writer instance.
1545 * @param pszPath The path to the object.
1546 * @param hVfsSymlink The symbolic link object to add.
1547 * @param pObjInfo The object information.
1548 * @param pszOwnerNm The owner name.
1549 * @param pszGroupNm The group name.
1550 */
1551static int rtZipTarFssWriter_AddSymlink(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSSYMLINK hVfsSymlink,
1552 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1553{
1554 /*
1555 * Read the symlink target first and check that it's not too long.
1556 * Flip DOS slashes.
1557 */
1558 char szTarget[RTPATH_MAX];
1559 int rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
1560 if (RT_SUCCESS(rc))
1561 {
1562#if RTPATH_STYLE != RTPATH_STR_F_STYLE_UNIX
1563 char *pszDosSlash = strchr(szTarget, '\\');
1564 while (pszDosSlash)
1565 {
1566 *pszDosSlash = '/';
1567 pszDosSlash = strchr(pszDosSlash + 1, '\\');
1568 }
1569#endif
1570 size_t cchTarget = strlen(szTarget);
1571 if (cchTarget < sizeof(pThis->aHdrs[0].Common.linkname))
1572 {
1573 /*
1574 * Create a header, add the link target and push it out.
1575 */
1576 rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1577 if (RT_SUCCESS(rc))
1578 {
1579 memcpy(pThis->aHdrs[0].Common.linkname, szTarget, cchTarget + 1);
1580 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1581 if (RT_SUCCESS(rc))
1582 {
1583 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]),
1584 true /*fBlocking*/, NULL);
1585 if (RT_SUCCESS(rc))
1586 {
1587 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1588 return VINF_SUCCESS;
1589 }
1590 pThis->rcFatal = rc;
1591 }
1592 }
1593 }
1594 else
1595 {
1596 /** @todo implement gnu and pax long name extensions. */
1597 rc = VERR_TAR_NAME_TOO_LONG;
1598 }
1599 }
1600 return rc;
1601}
1602
1603
1604/**
1605 * Adds a simple object to the stream.
1606 *
1607 * Simple objects only contains metadata, no actual data bits. Directories,
1608 * devices, fifos, sockets and such.
1609 *
1610 * @returns IPRT status code.
1611 * @param pThis The TAR writer instance.
1612 * @param pszPath The path to the object.
1613 * @param pObjInfo The object information.
1614 * @param pszOwnerNm The owner name.
1615 * @param pszGroupNm The group name.
1616 */
1617static int rtZipTarFssWriter_AddSimpleObject(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
1618 const char *pszOwnerNm, const char *pszGroupNm)
1619{
1620 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1621 if (RT_SUCCESS(rc))
1622 {
1623 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1624 if (RT_SUCCESS(rc))
1625 {
1626 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1627 return VINF_SUCCESS;
1628 }
1629 pThis->rcFatal = rc;
1630 }
1631 return rc;
1632}
1633
1634
1635/**
1636 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1637 */
1638static DECLCALLBACK(int) rtZipTarFssWriter_Close(void *pvThis)
1639{
1640 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1641
1642 rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1643
1644 RTVfsIoStrmRelease(pThis->hVfsIos);
1645 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
1646
1647 if (pThis->hVfsFile != NIL_RTVFSFILE)
1648 {
1649 RTVfsFileRelease(pThis->hVfsFile);
1650 pThis->hVfsFile = NIL_RTVFSFILE;
1651 }
1652
1653 if (pThis->pszOwner)
1654 {
1655 RTStrFree(pThis->pszOwner);
1656 pThis->pszOwner = NULL;
1657 }
1658 if (pThis->pszGroup)
1659 {
1660 RTStrFree(pThis->pszGroup);
1661 pThis->pszGroup = NULL;
1662 }
1663 if (pThis->pszPrefix)
1664 {
1665 RTStrFree(pThis->pszPrefix);
1666 pThis->pszPrefix = NULL;
1667 }
1668
1669 return VINF_SUCCESS;
1670}
1671
1672
1673/**
1674 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1675 */
1676static DECLCALLBACK(int) rtZipTarFssWriter_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1677{
1678 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1679 /* Take the lazy approach here, with the sideffect of providing some info
1680 that is actually kind of useful. */
1681 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
1682}
1683
1684
1685/**
1686 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnAdd}
1687 */
1688static DECLCALLBACK(int) rtZipTarFssWriter_Add(void *pvThis, const char *pszPath, RTVFSOBJ hVfsObj, uint32_t fFlags)
1689{
1690 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1691
1692 /*
1693 * Before we continue we must complete any current push file and check rcFatal.
1694 */
1695 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1696 if (RT_FAILURE(rc))
1697 return rc;
1698
1699 /*
1700 * Query information about the object.
1701 */
1702 RTFSOBJINFO ObjInfo;
1703 rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX);
1704 AssertRCReturn(rc, rc);
1705
1706 RTFSOBJINFO ObjOwnerName;
1707 rc = RTVfsObjQueryInfo(hVfsObj, &ObjOwnerName, RTFSOBJATTRADD_UNIX_OWNER);
1708 if (RT_FAILURE(rc) || ObjOwnerName.Attr.u.UnixOwner.szName[0] == '\0')
1709 strcpy(ObjOwnerName.Attr.u.UnixOwner.szName, "someone");
1710
1711 RTFSOBJINFO ObjGrpName;
1712 rc = RTVfsObjQueryInfo(hVfsObj, &ObjGrpName, RTFSOBJATTRADD_UNIX_GROUP);
1713 if (RT_FAILURE(rc) || ObjGrpName.Attr.u.UnixGroup.szName[0] == '\0')
1714 strcpy(ObjGrpName.Attr.u.UnixGroup.szName, "somegroup");
1715
1716 /*
1717 * Do type specific handling. File have several options and variations to
1718 * take into account, thus the mess.
1719 */
1720 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
1721 {
1722 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
1723 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE);
1724
1725 if (fFlags & RTVFSFSSTRM_ADD_F_STREAM)
1726 rc = rtZipTarFssWriter_AddFileStream(pThis, pszPath, hVfsIos, &ObjInfo,
1727 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1728 else if ( !(pThis->fFlags & RTZIPTAR_C_SPARSE)
1729 || ObjInfo.cbObject < RTZIPTAR_MIN_SPARSE)
1730 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
1731 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1732 else
1733 {
1734 RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
1735 if (hVfsFile != NIL_RTVFSFILE)
1736 {
1737 rc = rtZipTarFssWriter_AddFileSparse(pThis, pszPath, hVfsFile, hVfsIos, &ObjInfo,
1738 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1739 RTVfsFileRelease(hVfsFile);
1740 }
1741 else
1742 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
1743 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1744 }
1745 RTVfsIoStrmRelease(hVfsIos);
1746 }
1747 else if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
1748 {
1749 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
1750 AssertReturn(hVfsSymlink != NIL_RTVFSSYMLINK, VERR_WRONG_TYPE);
1751 rc = rtZipTarFssWriter_AddSymlink(pThis, pszPath, hVfsSymlink, &ObjInfo,
1752 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1753 RTVfsSymlinkRelease(hVfsSymlink);
1754 }
1755 else
1756 rc = rtZipTarFssWriter_AddSimpleObject(pThis, pszPath, &ObjInfo,
1757 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1758
1759 return rc;
1760}
1761
1762
1763/**
1764 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnPushFile}
1765 */
1766static DECLCALLBACK(int) rtZipTarFssWriter_PushFile(void *pvThis, const char *pszPath, uint64_t cbFile, PCRTFSOBJINFO paObjInfo,
1767 uint32_t cObjInfo, uint32_t fFlags, PRTVFSIOSTREAM phVfsIos)
1768{
1769 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1770
1771 /*
1772 * We can only deal with output of indeterminate length if the output is
1773 * seekable (see also rtZipTarFssWriter_AddFileStream).
1774 */
1775 AssertReturn(cbFile != UINT64_MAX || pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
1776 AssertReturn(RT_BOOL(cbFile == UINT64_MAX) == RT_BOOL(fFlags & RTVFSFSSTRM_ADD_F_STREAM), VERR_INVALID_FLAGS);
1777
1778 /*
1779 * Before we continue we must complete any current push file and check rcFatal.
1780 */
1781 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1782 if (RT_FAILURE(rc))
1783 return rc;
1784
1785 /*
1786 * If no object info was provideded, fake up some.
1787 */
1788 const char *pszOwnerNm = "someone";
1789 const char *pszGroupNm = "somegroup";
1790 RTFSOBJINFO ObjInfo;
1791 if (cObjInfo == 0)
1792 {
1793 /* Fake up a info. */
1794 RT_ZERO(ObjInfo);
1795 ObjInfo.cbObject = cbFile != UINT64_MAX ? cbFile : 0;
1796 ObjInfo.cbAllocated = cbFile != UINT64_MAX ? RT_ALIGN_64(cbFile, RTZIPTAR_BLOCKSIZE) : UINT64_MAX;
1797 RTTimeNow(&ObjInfo.ModificationTime);
1798 ObjInfo.BirthTime = ObjInfo.ModificationTime;
1799 ObjInfo.ChangeTime = ObjInfo.ModificationTime;
1800 ObjInfo.AccessTime = ObjInfo.ModificationTime;
1801 ObjInfo.Attr.fMode = RTFS_TYPE_FILE | 0666;
1802 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1803 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
1804 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
1805 ObjInfo.Attr.u.Unix.cHardlinks = 1;
1806 //ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
1807 //ObjInfo.Attr.u.Unix.INodeId = 0;
1808 //ObjInfo.Attr.u.Unix.fFlags = 0;
1809 //ObjInfo.Attr.u.Unix.GenerationId = 0;
1810 //ObjInfo.Attr.u.Unix.Device = 0;
1811 }
1812 else
1813 {
1814 /* Make a copy of the object info and adjust the size, if necessary. */
1815 ObjInfo = paObjInfo[0];
1816 Assert(ObjInfo.Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
1817 Assert(RTFS_IS_FILE(ObjInfo.Attr.fMode));
1818 if ((uint64_t)ObjInfo.cbObject != cbFile)
1819 {
1820 ObjInfo.cbObject = cbFile != UINT64_MAX ? cbFile : 0;
1821 ObjInfo.cbAllocated = cbFile != UINT64_MAX ? RT_ALIGN_64(cbFile, RTZIPTAR_BLOCKSIZE) : UINT64_MAX;
1822 }
1823
1824 /* Lookup the group and user names. */
1825 for (uint32_t i = 0; i < cObjInfo; i++)
1826 if ( paObjInfo[i].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_OWNER
1827 && paObjInfo[i].Attr.u.UnixOwner.szName[0] != '\0')
1828 pszOwnerNm = paObjInfo[i].Attr.u.UnixOwner.szName;
1829 else if ( paObjInfo[i].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_GROUP
1830 && paObjInfo[i].Attr.u.UnixGroup.szName[0] != '\0')
1831 pszGroupNm = paObjInfo[i].Attr.u.UnixGroup.szName;
1832 }
1833
1834 /*
1835 * Create an I/O stream object for the caller to use.
1836 */
1837 RTFOFF const offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
1838 AssertReturn(offHdr >= 0, (int)offHdr);
1839
1840 PRTZIPTARFSSTREAMWRITERPUSH pPush;
1841 RTVFSIOSTREAM hVfsIos;
1842 if (pThis->hVfsFile == NIL_RTVFSFILE)
1843 {
1844 rc = RTVfsNewIoStream(&g_rtZipTarWriterIoStrmOps, sizeof(*pPush), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
1845 &hVfsIos, (void **)&pPush);
1846 if (RT_FAILURE(rc))
1847 return rc;
1848 }
1849 else
1850 {
1851 RTVFSFILE hVfsFile;
1852 rc = RTVfsNewFile(&g_rtZipTarWriterFileOps, sizeof(*pPush), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
1853 &hVfsFile, (void **)&pPush);
1854 if (RT_FAILURE(rc))
1855 return rc;
1856 hVfsIos = RTVfsFileToIoStream(hVfsFile);
1857 RTVfsFileRelease(hVfsFile);
1858 }
1859 pPush->pParent = NULL;
1860 pPush->cbExpected = cbFile;
1861 pPush->offHdr = (uint64_t)offHdr;
1862 pPush->offData = 0;
1863 pPush->offCurrent = 0;
1864 pPush->cbCurrent = 0;
1865 pPush->ObjInfo = ObjInfo;
1866 pPush->fOpenEnded = cbFile == UINT64_MAX;
1867
1868 /*
1869 * Produce and write file headers.
1870 */
1871 rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, &ObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_NORMAL);
1872 if (RT_SUCCESS(rc))
1873 {
1874 size_t cbHdrs = pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1875 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, cbHdrs, true /*fBlocking*/, NULL);
1876 if (RT_SUCCESS(rc))
1877 {
1878 pThis->cbWritten += cbHdrs;
1879
1880 /*
1881 * Complete the object and return.
1882 */
1883 pPush->offData = pPush->offHdr + cbHdrs;
1884 if (cbFile == UINT64_MAX)
1885 pPush->cbExpected = (uint64_t)(RTFOFF_MAX - _4K) - pPush->offData;
1886 pPush->pParent = pThis;
1887 pThis->pPush = pPush;
1888
1889 *phVfsIos = hVfsIos;
1890 return VINF_SUCCESS;
1891 }
1892 pThis->rcFatal = rc;
1893 }
1894
1895 RTVfsIoStrmRelease(hVfsIos);
1896 return rc;
1897}
1898
1899
1900/**
1901 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnEnd}
1902 */
1903static DECLCALLBACK(int) rtZipTarFssWriter_End(void *pvThis)
1904{
1905 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1906
1907 /*
1908 * Make sure to complete any pending push file and that rcFatal is fine.
1909 */
1910 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1911 if (RT_SUCCESS(rc))
1912 {
1913 /*
1914 * There are supposed to be two zero headers at the end of the archive.
1915 * GNU tar may write more because of the way it does buffering,
1916 * libarchive OTOH writes exactly two.
1917 */
1918 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, RTZIPTAR_BLOCKSIZE * 2, true /*fBlocking*/, NULL);
1919 if (RT_SUCCESS(rc))
1920 {
1921 pThis->cbWritten += RTZIPTAR_BLOCKSIZE * 2;
1922
1923 /*
1924 * Flush the output.
1925 */
1926 rc = RTVfsIoStrmFlush(pThis->hVfsIos);
1927 if (RT_SUCCESS(rc))
1928 return rc;
1929 }
1930 pThis->rcFatal = rc;
1931 }
1932 return rc;
1933}
1934
1935
1936/**
1937 * Tar filesystem stream operations.
1938 */
1939static const RTVFSFSSTREAMOPS g_rtZipTarFssOps =
1940{
1941 { /* Obj */
1942 RTVFSOBJOPS_VERSION,
1943 RTVFSOBJTYPE_FS_STREAM,
1944 "TarFsStreamWriter",
1945 rtZipTarFssWriter_Close,
1946 rtZipTarFssWriter_QueryInfo,
1947 RTVFSOBJOPS_VERSION
1948 },
1949 RTVFSFSSTREAMOPS_VERSION,
1950 0,
1951 NULL,
1952 rtZipTarFssWriter_Add,
1953 rtZipTarFssWriter_PushFile,
1954 rtZipTarFssWriter_End,
1955 RTVFSFSSTREAMOPS_VERSION
1956};
1957
1958
1959RTDECL(int) RTZipTarFsStreamToIoStream(RTVFSIOSTREAM hVfsIosOut, RTZIPTARFORMAT enmFormat,
1960 uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
1961{
1962 /*
1963 * Input validation.
1964 */
1965 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
1966 *phVfsFss = NIL_RTVFSFSSTREAM;
1967 AssertPtrReturn(hVfsIosOut, VERR_INVALID_HANDLE);
1968 AssertReturn(enmFormat > RTZIPTARFORMAT_INVALID && enmFormat < RTZIPTARFORMAT_END, VERR_INVALID_PARAMETER);
1969 AssertReturn(!(fFlags & ~RTZIPTAR_C_VALID_MASK), VERR_INVALID_FLAGS);
1970
1971 if (enmFormat == RTZIPTARFORMAT_DEFAULT)
1972 enmFormat = RTZIPTARFORMAT_GNU;
1973 AssertReturn( enmFormat == RTZIPTARFORMAT_GNU
1974 || enmFormat == RTZIPTARFORMAT_USTAR
1975 , VERR_NOT_IMPLEMENTED); /* Only implementing GNU and USTAR output at the moment. */
1976
1977 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosOut);
1978 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1979
1980 /*
1981 * Retain the input stream and create a new filesystem stream handle.
1982 */
1983 PRTZIPTARFSSTREAMWRITER pThis;
1984 RTVFSFSSTREAM hVfsFss;
1985 int rc = RTVfsNewFsStream(&g_rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, false /*fReadOnly*/,
1986 &hVfsFss, (void **)&pThis);
1987 if (RT_SUCCESS(rc))
1988 {
1989 pThis->hVfsIos = hVfsIosOut;
1990 pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIosOut);
1991
1992 pThis->enmFormat = enmFormat;
1993 pThis->fFlags = fFlags;
1994 pThis->rcFatal = VINF_SUCCESS;
1995
1996 pThis->uidOwner = NIL_RTUID;
1997 pThis->pszOwner = NULL;
1998 pThis->gidGroup = NIL_RTGID;
1999 pThis->pszGroup = NULL;
2000 pThis->pszPrefix = NULL;
2001 pThis->pModTime = NULL;
2002 pThis->fFileModeAndMask = ~(RTFMODE)0;
2003 pThis->fFileModeOrMask = 0;
2004 pThis->fDirModeAndMask = ~(RTFMODE)0;
2005 pThis->fDirModeOrMask = 0;
2006
2007 *phVfsFss = hVfsFss;
2008 return VINF_SUCCESS;
2009 }
2010
2011 RTVfsIoStrmRelease(hVfsIosOut);
2012 return rc;
2013}
2014
2015
2016RTDECL(int) RTZipTarFsStreamSetOwner(RTVFSFSSTREAM hVfsFss, RTUID uid, const char *pszOwner)
2017{
2018 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2019 AssertReturn(pThis, VERR_WRONG_TYPE);
2020
2021 pThis->uidOwner = uid;
2022 if (pThis->pszOwner)
2023 {
2024 RTStrFree(pThis->pszOwner);
2025 pThis->pszOwner = NULL;
2026 }
2027 if (pszOwner)
2028 {
2029 pThis->pszOwner = RTStrDup(pszOwner);
2030 AssertReturn(pThis->pszOwner, VERR_NO_STR_MEMORY);
2031 }
2032
2033 return VINF_SUCCESS;
2034}
2035
2036
2037RTDECL(int) RTZipTarFsStreamSetGroup(RTVFSFSSTREAM hVfsFss, RTGID gid, const char *pszGroup)
2038{
2039 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2040 AssertReturn(pThis, VERR_WRONG_TYPE);
2041
2042 pThis->gidGroup = gid;
2043 if (pThis->pszGroup)
2044 {
2045 RTStrFree(pThis->pszGroup);
2046 pThis->pszGroup = NULL;
2047 }
2048 if (pszGroup)
2049 {
2050 pThis->pszGroup = RTStrDup(pszGroup);
2051 AssertReturn(pThis->pszGroup, VERR_NO_STR_MEMORY);
2052 }
2053
2054 return VINF_SUCCESS;
2055}
2056
2057
2058RTDECL(int) RTZipTarFsStreamSetPrefix(RTVFSFSSTREAM hVfsFss, const char *pszPrefix)
2059{
2060 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2061 AssertReturn(pThis, VERR_WRONG_TYPE);
2062 AssertReturn(!pszPrefix || *pszPrefix, VERR_INVALID_NAME);
2063
2064 if (pThis->pszPrefix)
2065 {
2066 RTStrFree(pThis->pszPrefix);
2067 pThis->pszPrefix = NULL;
2068 pThis->cchPrefix = 0;
2069 }
2070 if (pszPrefix)
2071 {
2072 /*
2073 * Make a copy of the prefix, make sure it ends with a slash,
2074 * then flip DOS slashes.
2075 */
2076 size_t cchPrefix = strlen(pszPrefix);
2077 char *pszCopy = RTStrAlloc(cchPrefix + 3);
2078 AssertReturn(pszCopy, VERR_NO_STR_MEMORY);
2079 memcpy(pszCopy, pszPrefix, cchPrefix + 1);
2080
2081 RTPathEnsureTrailingSeparator(pszCopy, cchPrefix + 3);
2082
2083#if RTPATH_STYLE != RTPATH_STR_F_STYLE_UNIX
2084 char *pszDosSlash = strchr(pszCopy, '\\');
2085 while (pszDosSlash)
2086 {
2087 *pszDosSlash = '/';
2088 pszDosSlash = strchr(pszDosSlash + 1, '\\');
2089 }
2090#endif
2091
2092 pThis->cchPrefix = cchPrefix + strlen(&pszCopy[cchPrefix]);
2093 pThis->pszPrefix = pszCopy;
2094 }
2095
2096 return VINF_SUCCESS;
2097}
2098
2099
2100RTDECL(int) RTZipTarFsStreamSetModTime(RTVFSFSSTREAM hVfsFss, PCRTTIMESPEC pModificationTime)
2101{
2102 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2103 AssertReturn(pThis, VERR_WRONG_TYPE);
2104
2105 if (pModificationTime)
2106 {
2107 pThis->ModTime = *pModificationTime;
2108 pThis->pModTime = &pThis->ModTime;
2109 }
2110 else
2111 pThis->pModTime = NULL;
2112
2113 return VINF_SUCCESS;
2114}
2115
2116
2117RTDECL(int) RTZipTarFsStreamSetFileMode(RTVFSFSSTREAM hVfsFss, RTFMODE fAndMode, RTFMODE fOrMode)
2118{
2119 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2120 AssertReturn(pThis, VERR_WRONG_TYPE);
2121
2122 pThis->fFileModeAndMask = fAndMode | ~RTFS_UNIX_ALL_PERMS;
2123 pThis->fFileModeOrMask = fOrMode & RTFS_UNIX_ALL_PERMS;
2124 return VINF_SUCCESS;
2125}
2126
2127
2128RTDECL(int) RTZipTarFsStreamSetDirMode(RTVFSFSSTREAM hVfsFss, RTFMODE fAndMode, RTFMODE fOrMode)
2129{
2130 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2131 AssertReturn(pThis, VERR_WRONG_TYPE);
2132
2133 pThis->fDirModeAndMask = fAndMode | ~RTFS_UNIX_ALL_PERMS;
2134 pThis->fDirModeOrMask = fOrMode & RTFS_UNIX_ALL_PERMS;
2135 return VINF_SUCCESS;
2136}
2137
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