VirtualBox

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

Last change on this file since 98092 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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