VirtualBox

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

最後變更 在這個檔案是 107455,由 vboxsync 提交於 2 月 前

Runtime/common/zip/tarvfswriter.cpp: Don't call a function inside RT_MIN() due to multiple expansions (harmless), bugref:3409

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

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