VirtualBox

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

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.6 KB
Line 
1/* $Id: tarvfswriter.cpp 103005 2024-01-23 23:55:58Z vboxsync $ */
2/** @file
3 * IPRT - TAR Virtual Filesystem, Writer.
4 */
5
6/*
7 * Copyright (C) 2010-2023 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-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 pObjInfo->cbAllocated = RT_ALIGN_64(pPush->cbCurrent, rtZipTarFssWrite_GetBlockSize(pPush->pParent));
710
711 /* Additional info. */
712 switch (enmAddAttr)
713 {
714 case RTFSOBJATTRADD_NOTHING:
715 case RTFSOBJATTRADD_UNIX:
716 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
717 break;
718
719 case RTFSOBJATTRADD_UNIX_OWNER:
720 pObjInfo->Attr.u.UnixOwner.uid = pPush->ObjInfo.Attr.u.Unix.uid;
721 if ( pPush->pParent
722 && rtZipTarFssWrite_IsTar(pPush->pParent))
723 strcpy(pObjInfo->Attr.u.UnixOwner.szName, pPush->pParent->aHdrsTar[0].Common.uname);
724 else
725 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
726 pObjInfo->Attr.enmAdditional = enmAddAttr;
727 break;
728
729 case RTFSOBJATTRADD_UNIX_GROUP:
730 pObjInfo->Attr.u.UnixGroup.gid = pPush->ObjInfo.Attr.u.Unix.gid;
731 if ( pPush->pParent
732 && rtZipTarFssWrite_IsTar(pPush->pParent))
733 strcpy(pObjInfo->Attr.u.UnixGroup.szName, pPush->pParent->aHdrsTar[0].Common.uname);
734 else
735 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
736 pObjInfo->Attr.enmAdditional = enmAddAttr;
737 break;
738
739 case RTFSOBJATTRADD_EASIZE:
740 pObjInfo->Attr.u.EASize.cb = 0;
741 pObjInfo->Attr.enmAdditional = enmAddAttr;
742 break;
743
744 default:
745 AssertFailed();
746 }
747
748 return VINF_SUCCESS;
749}
750
751
752/**
753 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
754 */
755static DECLCALLBACK(int) rtZipTarWriterPush_Read(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
756{
757 /* No read support, sorry. */
758 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbRead);
759 AssertFailed();
760 return VERR_ACCESS_DENIED;
761}
762
763
764/**
765 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
766 */
767static DECLCALLBACK(int) rtZipTarWriterPush_Write(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
768{
769 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
770 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
771 AssertPtrReturn(pParent, VERR_WRONG_ORDER);
772
773 int rc = pParent->rcFatal;
774 AssertRCReturn(rc, rc);
775
776 /*
777 * Single segment at a time.
778 */
779 Assert(pSgBuf->cSegs == 1);
780 size_t cbToWrite = pSgBuf->paSegs[0].cbSeg;
781 void const *pvToWrite = pSgBuf->paSegs[0].pvSeg;
782
783 /*
784 * Hopefully we don't need to seek. But if we do, let the seek method do
785 * it as it's not entirely trivial.
786 */
787 if ( off < 0
788 || (uint64_t)off == pPush->offCurrent)
789 rc = VINF_SUCCESS;
790 else
791 rc = rtZipTarWriterPush_Seek(pvThis, off, RTFILE_SEEK_BEGIN, NULL);
792 if (RT_SUCCESS(rc))
793 {
794 Assert(pPush->offCurrent <= pPush->cbExpected);
795 Assert(pPush->offCurrent <= pPush->cbCurrent);
796 AssertMsgReturn(cbToWrite <= pPush->cbExpected - pPush->offCurrent,
797 ("offCurrent=%#RX64 + cbToWrite=%#zx = %#RX64; cbExpected=%RX64\n",
798 pPush->offCurrent, cbToWrite, pPush->offCurrent + cbToWrite, pPush->cbExpected),
799 VERR_DISK_FULL);
800 size_t cbWritten = 0;
801 rc = RTVfsIoStrmWrite(pParent->hVfsIos, pvToWrite, cbToWrite, fBlocking, &cbWritten);
802 if (RT_SUCCESS(rc))
803 {
804 pPush->offCurrent += cbWritten;
805 if (pPush->offCurrent > pPush->cbCurrent)
806 {
807 pParent->cbWritten = pPush->offCurrent - pPush->cbCurrent;
808 pPush->cbCurrent = pPush->offCurrent;
809 }
810 if (pcbWritten)
811 *pcbWritten = cbWritten;
812 RTSgBufAdvance(pSgBuf, cbWritten);
813 }
814 }
815
816 /*
817 * Fatal errors get down here, non-fatal ones returns earlier.
818 */
819 if (RT_SUCCESS(rc))
820 return VINF_SUCCESS;
821 pParent->rcFatal = rc;
822 return rc;
823}
824
825
826/**
827 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
828 */
829static DECLCALLBACK(int) rtZipTarWriterPush_Flush(void *pvThis)
830{
831 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
832 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
833 AssertPtrReturn(pParent, VERR_WRONG_ORDER);
834 int rc = pParent->rcFatal;
835 if (RT_SUCCESS(rc))
836 pParent->rcFatal = rc = RTVfsIoStrmFlush(pParent->hVfsIos);
837 return rc;
838}
839
840
841/**
842 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
843 */
844static DECLCALLBACK(int) rtZipTarWriterPush_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
845 uint32_t *pfRetEvents)
846{
847 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
848 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
849 AssertPtrReturn(pParent, VERR_WRONG_ORDER);
850 return RTVfsIoStrmPoll(pParent->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
851}
852
853
854/**
855 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
856 */
857static DECLCALLBACK(int) rtZipTarWriterPush_Tell(void *pvThis, PRTFOFF poffActual)
858{
859 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
860 *poffActual = (RTFOFF)pPush->offCurrent;
861 return VINF_SUCCESS;
862}
863
864
865/**
866 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
867 */
868static DECLCALLBACK(int) rtZipTarWriterPush_Skip(void *pvThis, RTFOFF cb)
869{
870 RT_NOREF(pvThis, cb);
871 AssertFailed();
872 return VERR_ACCESS_DENIED;
873}
874
875
876/**
877 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
878 */
879static DECLCALLBACK(int) rtZipTarWriterPush_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
880{
881 RT_NOREF(pvThis, fMode, fMask);
882 AssertFailed();
883 return VERR_ACCESS_DENIED;
884}
885
886
887/**
888 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
889 */
890static DECLCALLBACK(int) rtZipTarWriterPush_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
891 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
892{
893 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
894 AssertFailed();
895 return VERR_ACCESS_DENIED;
896}
897
898
899/**
900 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
901 */
902static DECLCALLBACK(int) rtZipTarWriterPush_SetOwner(void *pvThis, RTUID uid, RTGID gid)
903{
904 RT_NOREF(pvThis, uid, gid);
905 AssertFailed();
906 return VERR_ACCESS_DENIED;
907}
908
909
910/**
911 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
912 */
913static DECLCALLBACK(int) rtZipTarWriterPush_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
914{
915 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
916 PRTZIPTARFSSTREAMWRITER pParent = pPush->pParent;
917 AssertPtrReturn(pParent, VERR_WRONG_ORDER);
918
919 int rc = pParent->rcFatal;
920 AssertRCReturn(rc, rc);
921 Assert(pPush->offCurrent <= pPush->cbCurrent);
922
923 /*
924 * Calculate the new file offset.
925 */
926 RTFOFF offNewSigned;
927 switch (uMethod)
928 {
929 case RTFILE_SEEK_BEGIN:
930 offNewSigned = offSeek;
931 break;
932 case RTFILE_SEEK_CURRENT:
933 offNewSigned = pPush->offCurrent + offSeek;
934 break;
935 case RTFILE_SEEK_END:
936 offNewSigned = pPush->cbCurrent + offSeek;
937 break;
938 default:
939 AssertFailedReturn(VERR_INVALID_PARAMETER);
940 }
941
942 /*
943 * Check the new file offset against expectations.
944 */
945 AssertMsgReturn(offNewSigned >= 0, ("offNewSigned=%RTfoff\n", offNewSigned), VERR_NEGATIVE_SEEK);
946
947 uint64_t offNew = (uint64_t)offNewSigned;
948 AssertMsgReturn(offNew <= pPush->cbExpected, ("offNew=%#RX64 cbExpected=%#Rx64\n", offNew, pPush->cbExpected), VERR_SEEK);
949
950 /*
951 * Any change at all? We can always hope...
952 */
953 if (offNew == pPush->offCurrent)
954 { }
955 /*
956 * Gap that needs zero filling?
957 */
958 else if (offNew > pPush->cbCurrent)
959 {
960 if (pPush->offCurrent != pPush->cbCurrent)
961 {
962 AssertReturn(pParent->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
963 rc = RTVfsFileSeek(pParent->hVfsFile, pPush->offData + pPush->cbCurrent, RTFILE_SEEK_BEGIN, NULL);
964 if (RT_FAILURE(rc))
965 return pParent->rcFatal = rc;
966 pPush->offCurrent = pPush->cbCurrent;
967 }
968
969 uint64_t cbToZero = offNew - pPush->cbCurrent;
970 rc = RTVfsIoStrmZeroFill(pParent->hVfsIos, cbToZero);
971 if (RT_FAILURE(rc))
972 return pParent->rcFatal = rc;
973 pParent->cbWritten += cbToZero;
974 pPush->cbCurrent = pPush->offCurrent = offNew;
975 }
976 /*
977 * Just change the file position to somewhere we've already written.
978 */
979 else
980 {
981 AssertReturn(pParent->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
982 rc = RTVfsFileSeek(pParent->hVfsFile, pPush->offData + offNew, RTFILE_SEEK_BEGIN, NULL);
983 if (RT_FAILURE(rc))
984 return pParent->rcFatal = rc;
985 pPush->offCurrent = offNew;
986 }
987 Assert(pPush->offCurrent <= pPush->cbCurrent);
988
989 if (poffActual)
990 *poffActual = pPush->offCurrent;
991 return VINF_SUCCESS;
992}
993
994
995/**
996 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
997 */
998static DECLCALLBACK(int) rtZipTarWriterPush_QuerySize(void *pvThis, uint64_t *pcbFile)
999{
1000 PRTZIPTARFSSTREAMWRITERPUSH pPush = (PRTZIPTARFSSTREAMWRITERPUSH)pvThis;
1001 *pcbFile = pPush->cbCurrent;
1002 return VINF_SUCCESS;
1003}
1004
1005
1006/**
1007 * TAR writer push I/O stream operations.
1008 */
1009DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_rtZipTarWriterIoStrmOps =
1010{
1011 { /* Obj */
1012 RTVFSOBJOPS_VERSION,
1013 RTVFSOBJTYPE_IO_STREAM,
1014 "TAR push I/O Stream",
1015 rtZipTarWriterPush_Close,
1016 rtZipTarWriterPush_QueryInfo,
1017 NULL,
1018 RTVFSOBJOPS_VERSION
1019 },
1020 RTVFSIOSTREAMOPS_VERSION,
1021 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1022 rtZipTarWriterPush_Read,
1023 rtZipTarWriterPush_Write,
1024 rtZipTarWriterPush_Flush,
1025 rtZipTarWriterPush_PollOne,
1026 rtZipTarWriterPush_Tell,
1027 rtZipTarWriterPush_Skip,
1028 NULL /*ZeroFill*/,
1029 RTVFSIOSTREAMOPS_VERSION,
1030};
1031
1032
1033/**
1034 * TAR writer push file operations.
1035 */
1036DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtZipTarWriterFileOps =
1037{
1038 { /* Stream */
1039 { /* Obj */
1040 RTVFSOBJOPS_VERSION,
1041 RTVFSOBJTYPE_FILE,
1042 "TAR push file",
1043 rtZipTarWriterPush_Close,
1044 rtZipTarWriterPush_QueryInfo,
1045 NULL,
1046 RTVFSOBJOPS_VERSION
1047 },
1048 RTVFSIOSTREAMOPS_VERSION,
1049 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1050 rtZipTarWriterPush_Read,
1051 rtZipTarWriterPush_Write,
1052 rtZipTarWriterPush_Flush,
1053 rtZipTarWriterPush_PollOne,
1054 rtZipTarWriterPush_Tell,
1055 rtZipTarWriterPush_Skip,
1056 NULL /*ZeroFill*/,
1057 RTVFSIOSTREAMOPS_VERSION,
1058 },
1059 RTVFSFILEOPS_VERSION,
1060 0,
1061 { /* ObjSet */
1062 RTVFSOBJSETOPS_VERSION,
1063 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
1064 rtZipTarWriterPush_SetMode,
1065 rtZipTarWriterPush_SetTimes,
1066 rtZipTarWriterPush_SetOwner,
1067 RTVFSOBJSETOPS_VERSION
1068 },
1069 rtZipTarWriterPush_Seek,
1070 rtZipTarWriterPush_QuerySize,
1071 NULL /*SetSize*/,
1072 NULL /*QueryMaxSize*/,
1073 RTVFSFILEOPS_VERSION
1074};
1075
1076
1077
1078/**
1079 * Checks rcFatal and completes any current push file.
1080 *
1081 * On return the output stream position will be at the next header location.
1082 *
1083 * After this call, the push object no longer can write anything.
1084 *
1085 * @returns IPRT status code.
1086 * @param pThis The TAR writer instance.
1087 */
1088static int rtZipTarFssWriter_CompleteCurrentPushFile(PRTZIPTARFSSTREAMWRITER pThis)
1089{
1090 /*
1091 * Check if there is a push file pending, remove it if there is.
1092 * We also check for fatal errors at this point so the caller doesn't need to.
1093 */
1094 PRTZIPTARFSSTREAMWRITERPUSH pPush = pThis->pPush;
1095 if (!pPush)
1096 {
1097 AssertRC(pThis->rcFatal);
1098 return pThis->rcFatal;
1099 }
1100
1101 pThis->pPush = NULL;
1102 pPush->pParent = NULL;
1103
1104 int rc = pThis->rcFatal;
1105 AssertRCReturn(rc, rc);
1106
1107 /*
1108 * Do we need to update the header. pThis->aHdrsTar[0] will retain the current
1109 * content at pPush->offHdr and we only need to update the size.
1110 */
1111 if (pPush->fOpenEnded)
1112 {
1113 if (rtZipTarFssWrite_IsTar(pThis))
1114 {
1115 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrsTar[0].Common.size, pPush->cbCurrent);
1116 if (RT_SUCCESS(rc))
1117 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrsTar[0]);
1118 }
1119 else
1120 {
1121 if (pPush->cbCurrent == (uint32_t)pPush->cbCurrent)
1122 rtZipTarFssWriter_CpioFmtU32(pThis->Cpio.Hdr.AsciiNew.achFileSize, (uint32_t)pPush->cbCurrent);
1123 else
1124 rc = VERR_FILE_TOO_BIG;
1125 }
1126
1127 if (RT_SUCCESS(rc))
1128 {
1129 rc = RTVfsFileWriteAt(pThis->hVfsFile, pPush->offHdr, &pThis->abHdrs[0], pThis->cbHdr, NULL);
1130 if (RT_SUCCESS(rc))
1131 rc = RTVfsFileSeek(pThis->hVfsFile, pPush->offData + pPush->cbCurrent, RTFILE_SEEK_BEGIN, NULL);
1132 }
1133 }
1134 /*
1135 * Check that we've received all the data we were promissed in the PushFile
1136 * call, fail if we weren't.
1137 */
1138 else
1139 AssertMsgStmt(pPush->cbCurrent == pPush->cbExpected,
1140 ("cbCurrent=%#RX64 cbExpected=%#RX64\n", pPush->cbCurrent, pPush->cbExpected),
1141 rc = VERR_BUFFER_UNDERFLOW);
1142 if (RT_SUCCESS(rc))
1143 {
1144 /*
1145 * Do zero padding if necessary.
1146 */
1147 size_t cbBlock = rtZipTarFssWrite_GetBlockSize(pThis);
1148 if (pPush->cbCurrent & (cbBlock - 1))
1149 {
1150 size_t cbToZero = cbBlock - (pPush->cbCurrent & (cbBlock - 1));
1151 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1152 if (RT_SUCCESS(rc))
1153 pThis->cbWritten += cbToZero;
1154 }
1155 }
1156
1157 if (RT_SUCCESS(rc))
1158 return VINF_SUCCESS;
1159 pThis->rcFatal = rc;
1160 return rc;
1161}
1162
1163
1164/**
1165 * Does the actual work for rtZipTarFssWriter_SwitchToWriteMode().
1166 *
1167 * @note We won't be here if we've truncate the tar file. Truncation
1168 * switches it into write mode.
1169 */
1170DECL_NO_INLINE(static, int) rtZipTarFssWriter_SwitchToWriteModeSlow(PRTZIPTARFSSTREAMWRITER pThis)
1171{
1172 /* Always go thru rtZipTarFssWriter_SwitchToWriteMode(). */
1173 AssertRCReturn(pThis->rcFatal, pThis->rcFatal);
1174 AssertReturn(!pThis->fWriting, VINF_SUCCESS);
1175 AssertReturn(pThis->fFlags & RTZIPTAR_C_UPDATE, VERR_INTERNAL_ERROR_3);
1176
1177 /*
1178 * If we're not at the end, locate the end of the tar file.
1179 * Because I'm lazy, we do that using rtZipTarFss_Next. This isn't entirely
1180 * optimial as it involves VFS object instantations and such.
1181 */
1182 /** @todo Optimize skipping to end of tar file in update mode. */
1183 while (!pThis->pReadTar->fEndOfStream)
1184 {
1185 int rc = rtZipTarFss_Next(pThis->pReadTar, NULL, NULL, NULL);
1186 if (rc == VERR_EOF)
1187 break;
1188 AssertRCReturn(rc, rc);
1189 }
1190
1191 /*
1192 * Seek to the desired cut-off point and indicate that we've switched to writing.
1193 */
1194 Assert(pThis->pReadTar->offNextHdr == pThis->pReadTar->offCurHdr);
1195 int rc = RTVfsFileSeek(pThis->hVfsFile, pThis->pReadTar->offNextHdr, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
1196 if (RT_SUCCESS(rc))
1197 pThis->fWriting = true;
1198 else
1199 pThis->rcFatal = rc;
1200
1201 return rc;
1202}
1203
1204
1205/**
1206 * Switches the stream into writing mode if necessary.
1207 *
1208 * @returns VBox status code.
1209 * @param pThis The TAR writer instance.
1210 *
1211 */
1212DECLINLINE(int) rtZipTarFssWriter_SwitchToWriteMode(PRTZIPTARFSSTREAMWRITER pThis)
1213{
1214 if (pThis->fWriting)
1215 return VINF_SUCCESS; /* ASSUMES caller already checked pThis->rcFatal. */
1216 return rtZipTarFssWriter_SwitchToWriteModeSlow(pThis);
1217}
1218
1219
1220/**
1221 * Allocates a buffer for transfering file data.
1222 *
1223 * @note Will use the 3rd TAR header as fallback buffer if we're out of
1224 * memory!
1225 *
1226 * @returns Pointer to buffer (won't ever fail).
1227 * @param pThis The TAR writer instance.
1228 * @param pcbBuf Where to return the buffer size. This will be a
1229 * multiple of the TAR block size.
1230 * @param ppvFree Where to return the pointer to pass to RTMemTmpFree
1231 * when done with the buffer.
1232 * @param cbFile The file size. Used as a buffer size hint.
1233 */
1234static uint8_t *rtZipTarFssWriter_AllocBuf(PRTZIPTARFSSTREAMWRITER pThis, size_t *pcbBuf, void **ppvFree, uint64_t cbObject)
1235{
1236 uint8_t *pbBuf;
1237
1238 /*
1239 * If this is a large file, try for a large buffer with 16KB alignment.
1240 */
1241 if (cbObject >= _64M)
1242 {
1243 pbBuf = (uint8_t *)RTMemTmpAlloc(_2M + _16K - 1);
1244 if (pbBuf)
1245 {
1246 *pcbBuf = _2M;
1247 *ppvFree = pbBuf;
1248 return RT_ALIGN_PT(pbBuf, _16K, uint8_t *);
1249 }
1250 }
1251 /*
1252 * 4KB aligned 512KB buffer if larger 512KB or larger.
1253 */
1254 else if (cbObject >= _512K)
1255 {
1256 pbBuf = (uint8_t *)RTMemTmpAlloc(_512K + _4K - 1);
1257 if (pbBuf)
1258 {
1259 *pcbBuf = _512K;
1260 *ppvFree = pbBuf;
1261 return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
1262 }
1263 }
1264 /*
1265 * Otherwise a 4KB aligned 128KB buffer.
1266 */
1267 else
1268 {
1269 pbBuf = (uint8_t *)RTMemTmpAlloc(_128K + _4K - 1);
1270 if (pbBuf)
1271 {
1272 *pcbBuf = _128K;
1273 *ppvFree = pbBuf;
1274 return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
1275 }
1276 }
1277
1278 /*
1279 * If allocation failed, fallback on a 16KB buffer without any extra alignment.
1280 */
1281 pbBuf = (uint8_t *)RTMemTmpAlloc(_16K);
1282 if (pbBuf)
1283 {
1284 *pcbBuf = _16K;
1285 *ppvFree = pbBuf;
1286 return pbBuf;
1287 }
1288
1289 /*
1290 * Final fallback, 512B buffer using the fallback buffer.
1291 */
1292 *pcbBuf = sizeof(pThis->abIoBuf);
1293 *ppvFree = NULL;
1294 return &pThis->abIoBuf[0];
1295}
1296
1297
1298/**
1299 * Frees the sparse info for a TAR file.
1300 *
1301 * @param pSparse The sparse info to free.
1302 */
1303static void rtZipTarFssWriter_SparseInfoDestroy(PRTZIPTARSPARSE pSparse)
1304{
1305 PRTZIPTARSPARSECHUNK pCur;
1306 PRTZIPTARSPARSECHUNK pNext;
1307 RTListForEachSafe(&pSparse->ChunkHead, pCur, pNext, RTZIPTARSPARSECHUNK, Entry)
1308 RTMemTmpFree(pCur);
1309 RTMemTmpFree(pSparse);
1310}
1311
1312
1313/**
1314 * Adds a data span to the sparse info.
1315 *
1316 * @returns VINF_SUCCESS or VINF_NO_TMP_MEMORY.
1317 * @param pSparse The sparse info to free.
1318 * @param offSpan Offset of the span.
1319 * @param cbSpan Number of bytes.
1320 */
1321static int rtZipTarFssWriter_SparseInfoAddSpan(PRTZIPTARSPARSE pSparse, uint64_t offSpan, uint64_t cbSpan)
1322{
1323 /*
1324 * Get the chunk we're adding it to.
1325 */
1326 PRTZIPTARSPARSECHUNK pChunk;
1327 if (pSparse->iNextSpan != 0)
1328 {
1329 pChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1330 Assert(pSparse->iNextSpan < RT_ELEMENTS(pChunk->aSpans));
1331 }
1332 else
1333 {
1334 pChunk = (PRTZIPTARSPARSECHUNK)RTMemTmpAllocZ(sizeof(*pChunk));
1335 if (!pChunk)
1336 return VERR_NO_TMP_MEMORY;
1337 RTListAppend(&pSparse->ChunkHead, &pChunk->Entry);
1338 }
1339
1340 /*
1341 * Append it.
1342 */
1343 pSparse->cDataSpans += 1;
1344 pSparse->cbDataSpans += cbSpan;
1345 pChunk->aSpans[pSparse->iNextSpan].cb = cbSpan;
1346 pChunk->aSpans[pSparse->iNextSpan].off = offSpan;
1347 if (++pSparse->iNextSpan >= RT_ELEMENTS(pChunk->aSpans))
1348 pSparse->iNextSpan = 0;
1349 return VINF_SUCCESS;
1350}
1351
1352
1353/**
1354 * Scans the input stream recording non-zero blocks.
1355 */
1356static int rtZipTarFssWriter_ScanSparseFile(PRTZIPTARFSSTREAMWRITER pThis, RTVFSFILE hVfsFile, uint64_t cbFile,
1357 size_t cbBuf, uint8_t *pbBuf, PRTZIPTARSPARSE *ppSparse)
1358{
1359 RT_NOREF(pThis);
1360
1361 /*
1362 * Create an empty sparse info bundle.
1363 */
1364 PRTZIPTARSPARSE pSparse = (PRTZIPTARSPARSE)RTMemTmpAlloc(sizeof(*pSparse));
1365 AssertReturn(pSparse, VERR_NO_MEMORY);
1366 pSparse->cbDataSpans = 0;
1367 pSparse->cDataSpans = 0;
1368 pSparse->iNextSpan = 0;
1369 RTListInit(&pSparse->ChunkHead);
1370
1371 /*
1372 * Scan the file from the start.
1373 */
1374 int rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
1375 if (RT_SUCCESS(rc))
1376 {
1377 bool fZeroSpan = false;
1378 uint64_t offSpan = 0;
1379 uint64_t cbSpan = 0;
1380
1381 for (uint64_t off = 0; off < cbFile;)
1382 {
1383 uint64_t cbLeft = cbFile - off;
1384 size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
1385 rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
1386 if (RT_FAILURE(rc))
1387 break;
1388 size_t cBlocks = cbToRead / RTZIPTAR_BLOCKSIZE;
1389
1390 /* Zero pad the final buffer to a multiple of the blocksize. */
1391 if (!(cbToRead & (RTZIPTAR_BLOCKSIZE - 1)))
1392 { /* likely */ }
1393 else
1394 {
1395 AssertBreakStmt(cbLeft == cbToRead, rc = VERR_INTERNAL_ERROR_3);
1396 RT_BZERO(&pbBuf[cbToRead], RTZIPTAR_BLOCKSIZE - (cbToRead & (RTZIPTAR_BLOCKSIZE - 1)));
1397 cBlocks++;
1398 }
1399
1400 /*
1401 * Process the blocks we've just read one by one.
1402 */
1403 uint8_t const *pbBlock = pbBuf;
1404 for (size_t iBlock = 0; iBlock < cBlocks; iBlock++)
1405 {
1406 bool fZeroBlock = ASMMemIsZero(pbBlock, RTZIPTAR_BLOCKSIZE);
1407 if (fZeroBlock == fZeroSpan)
1408 cbSpan += RTZIPTAR_BLOCKSIZE;
1409 else
1410 {
1411 if (!fZeroSpan && cbSpan)
1412 {
1413 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
1414 if (RT_FAILURE(rc))
1415 break;
1416 }
1417 fZeroSpan = fZeroBlock;
1418 offSpan = off;
1419 cbSpan = RTZIPTAR_BLOCKSIZE;
1420 }
1421
1422 /* next block. */
1423 pbBlock += RTZIPTAR_BLOCKSIZE;
1424 off += RTZIPTAR_BLOCKSIZE;
1425 }
1426 }
1427
1428 /*
1429 * Deal with the final span. If we've got zeros thowards the end, we
1430 * must add a zero byte data span at the end.
1431 */
1432 if (RT_SUCCESS(rc))
1433 {
1434 if (!fZeroSpan && cbSpan)
1435 {
1436 if (cbFile & (RTZIPTAR_BLOCKSIZE - 1))
1437 {
1438 Assert(!(cbSpan & (RTZIPTAR_BLOCKSIZE - 1)));
1439 cbSpan -= RTZIPTAR_BLOCKSIZE;
1440 cbSpan |= cbFile & (RTZIPTAR_BLOCKSIZE - 1);
1441 }
1442 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
1443 }
1444 if (RT_SUCCESS(rc))
1445 rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, cbFile, 0);
1446 }
1447 }
1448
1449 if (RT_SUCCESS(rc))
1450 {
1451 /*
1452 * Return the file back to the start position before we return so that we
1453 * can segue into the regular rtZipTarFssWriter_AddFile without further ado.
1454 */
1455 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
1456 if (RT_SUCCESS(rc))
1457 {
1458 *ppSparse = pSparse;
1459 return VINF_SUCCESS;
1460 }
1461 }
1462
1463 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1464 *ppSparse = NULL;
1465 return rc;
1466}
1467
1468
1469/**
1470 * Writes GNU the sparse file headers.
1471 *
1472 * @returns IPRT status code.
1473 * @param pThis The TAR writer instance.
1474 * @param pszPath The path to the file.
1475 * @param pObjInfo The object information.
1476 * @param pszOwnerNm The owner name.
1477 * @param pszGroupNm The group name.
1478 * @param pSparse The sparse file info.
1479 */
1480static int rtZipTarFssWriter_WriteGnuSparseHeaders(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
1481 const char *pszOwnerNm, const char *pszGroupNm, PCRTZIPTARSPARSE pSparse)
1482{
1483 /*
1484 * Format the first header.
1485 */
1486 int rc = rtZipTarFssWriter_ObjInfoToHdrTar(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_GNU_SPARSE);
1487 AssertRCReturn(rc, rc);
1488 AssertReturn(pThis->cHdrs == 1, VERR_INTERNAL_ERROR_2);
1489
1490 /* data size. */
1491 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrsTar[0].Common.size, pSparse->cbDataSpans);
1492 AssertRCReturn(rc, rc);
1493
1494 /* realsize. */
1495 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrsTar[0].Gnu.realsize, pObjInfo->cbObject);
1496 AssertRCReturn(rc, rc);
1497
1498 Assert(pThis->aHdrsTar[0].Gnu.isextended == 0);
1499
1500 /*
1501 * Walk the sparse spans, fill and write headers one by one.
1502 */
1503 PRTZIPTARGNUSPARSE paSparse = &pThis->aHdrsTar[0].Gnu.sparse[0];
1504 uint32_t cSparse = RT_ELEMENTS(pThis->aHdrsTar[0].Gnu.sparse);
1505 uint32_t iSparse = 0;
1506
1507 PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1508 PRTZIPTARSPARSECHUNK pChunk;
1509 RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
1510 {
1511 uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
1512 ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
1513 for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
1514 {
1515 /* Flush the header? */
1516 if (iSparse >= cSparse)
1517 {
1518 if (cSparse != RT_ELEMENTS(pThis->aHdrsTar[0].Gnu.sparse))
1519 pThis->aHdrsTar[0].GnuSparse.isextended = 1; /* more headers to come */
1520 else
1521 {
1522 pThis->aHdrsTar[0].Gnu.isextended = 1; /* more headers to come */
1523 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrsTar[0]);
1524 }
1525 if (RT_SUCCESS(rc))
1526 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrsTar[0], sizeof(pThis->aHdrsTar[0]), true /*fBlocking*/, NULL);
1527 if (RT_FAILURE(rc))
1528 return rc;
1529 RT_ZERO(pThis->aHdrsTar[0]);
1530 cSparse = RT_ELEMENTS(pThis->aHdrsTar[0].GnuSparse.sp);
1531 iSparse = 0;
1532 paSparse = &pThis->aHdrsTar[0].GnuSparse.sp[0];
1533 }
1534
1535 /* Append sparse data segment. */
1536 rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].offset, pChunk->aSpans[iSpan].off);
1537 AssertRCReturn(rc, rc);
1538 rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].numbytes, pChunk->aSpans[iSpan].cb);
1539 AssertRCReturn(rc, rc);
1540 iSparse++;
1541 }
1542 }
1543
1544 /*
1545 * The final header.
1546 */
1547 if (iSparse != 0)
1548 {
1549 if (cSparse != RT_ELEMENTS(pThis->aHdrsTar[0].Gnu.sparse))
1550 Assert(pThis->aHdrsTar[0].GnuSparse.isextended == 0);
1551 else
1552 {
1553 Assert(pThis->aHdrsTar[0].Gnu.isextended == 0);
1554 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrsTar[0]);
1555 }
1556 if (RT_SUCCESS(rc))
1557 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrsTar[0], sizeof(pThis->aHdrsTar[0]), true /*fBlocking*/, NULL);
1558 }
1559 pThis->cHdrs = 0;
1560 return rc;
1561}
1562
1563
1564/**
1565 * Adds a potentially sparse file to the output.
1566 *
1567 * @returns IPRT status code.
1568 * @param pThis The TAR writer instance.
1569 * @param pszPath The path to the file.
1570 * @param hVfsFile The potentially sparse file.
1571 * @param hVfsIos The I/O stream of the file. Same as @a hVfsFile.
1572 * @param pObjInfo The object information.
1573 * @param pszOwnerNm The owner name.
1574 * @param pszGroupNm The group name.
1575 */
1576static int rtZipTarFssWriter_AddFileSparse(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSFILE hVfsFile,
1577 RTVFSIOSTREAM hVfsIos, PCRTFSOBJINFO pObjInfo,
1578 const char *pszOwnerNm, const char *pszGroupNm)
1579{
1580 AssertReturn(rtZipTarFssWrite_IsTar(pThis), VERR_INTERNAL_ERROR_2);
1581
1582 /*
1583 * Scan the input file to locate all zero blocks.
1584 */
1585 void *pvBufFree;
1586 size_t cbBuf;
1587 uint8_t *pbBuf = rtZipTarFssWriter_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
1588
1589 PRTZIPTARSPARSE pSparse;
1590 int rc = rtZipTarFssWriter_ScanSparseFile(pThis, hVfsFile, pObjInfo->cbObject, cbBuf, pbBuf, &pSparse);
1591 if (RT_SUCCESS(rc))
1592 {
1593 /*
1594 * If there aren't at least 2 zero blocks in the file, don't bother
1595 * doing the sparse stuff and store it as a normal file.
1596 */
1597 if (pSparse->cbDataSpans + RTZIPTAR_BLOCKSIZE > (uint64_t)pObjInfo->cbObject)
1598 {
1599 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1600 RTMemTmpFree(pvBufFree);
1601 return rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, pObjInfo, pszOwnerNm, pszGroupNm);
1602 }
1603
1604 /*
1605 * Produce and write the headers.
1606 */
1607 if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
1608 rc = rtZipTarFssWriter_WriteGnuSparseHeaders(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, pSparse);
1609 else
1610 AssertStmt(pThis->enmFormat != RTZIPTARFORMAT_GNU, rc = VERR_NOT_IMPLEMENTED);
1611 if (RT_SUCCESS(rc))
1612 {
1613 /*
1614 * Write the file bytes.
1615 */
1616 PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1617 PRTZIPTARSPARSECHUNK pChunk;
1618 RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
1619 {
1620 uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
1621 ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
1622 for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
1623 {
1624 rc = RTVfsFileSeek(hVfsFile, pChunk->aSpans[iSpan].off, RTFILE_SEEK_BEGIN, NULL);
1625 if (RT_FAILURE(rc))
1626 break;
1627 uint64_t cbLeft = pChunk->aSpans[iSpan].cb;
1628 Assert( !(cbLeft & (RTZIPTAR_BLOCKSIZE - 1))
1629 || (iSpan + 1 == cSpans && pChunk == pLastChunk));
1630 while (cbLeft > 0)
1631 {
1632 size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
1633 rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
1634 if (RT_SUCCESS(rc))
1635 {
1636 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToRead, true /*fBlocking*/, NULL);
1637 if (RT_SUCCESS(rc))
1638 {
1639 pThis->cbWritten += cbToRead;
1640 cbLeft -= cbToRead;
1641 continue;
1642 }
1643 }
1644 break;
1645 }
1646 if (RT_FAILURE(rc))
1647 break;
1648 }
1649 }
1650
1651 /*
1652 * Do the zero padding.
1653 */
1654 if ( RT_SUCCESS(rc)
1655 && (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1)))
1656 {
1657 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1));
1658 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1659 if (RT_SUCCESS(rc))
1660 pThis->cbWritten += cbToZero;
1661 }
1662 }
1663
1664 if (RT_FAILURE(rc))
1665 pThis->rcFatal = rc;
1666 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1667 }
1668 RTMemTmpFree(pvBufFree);
1669 return rc;
1670}
1671
1672
1673/**
1674 * Adds an I/O stream of indeterminate length to the TAR file.
1675 *
1676 * This requires the output to be seekable, i.e. a file, because we need to go
1677 * back and update @c size field of the TAR header after pumping all the data
1678 * bytes thru and establishing the file length.
1679 *
1680 * @returns IPRT status code.
1681 * @param pThis The TAR writer instance.
1682 * @param pszPath The path to the file.
1683 * @param hVfsIos The I/O stream of the file.
1684 * @param pObjInfo The object information.
1685 * @param pszOwnerNm The owner name.
1686 * @param pszGroupNm The group name.
1687 */
1688static int rtZipTarFssWriter_AddFileStream(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
1689 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1690{
1691 AssertReturn(pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
1692
1693 /*
1694 * Append the header.
1695 */
1696 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1697 if (RT_SUCCESS(rc))
1698 {
1699 RTFOFF const offHdr = RTVfsFileTell(pThis->hVfsFile);
1700 if (offHdr >= 0)
1701 {
1702 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->abHdrs, pThis->cHdrs * pThis->cbHdr, true /*fBlocking*/, NULL);
1703 if (RT_SUCCESS(rc))
1704 {
1705 pThis->cbWritten += pThis->cHdrs * pThis->cbHdr;
1706
1707 /*
1708 * Transfer the bytes.
1709 */
1710 void *pvBufFree;
1711 size_t cbBuf;
1712 uint8_t *pbBuf = rtZipTarFssWriter_AllocBuf(pThis, &cbBuf, &pvBufFree,
1713 pObjInfo->cbObject > 0 && pObjInfo->cbObject != RTFOFF_MAX
1714 ? pObjInfo->cbObject : _1G);
1715
1716 uint64_t cbReadTotal = 0;
1717 for (;;)
1718 {
1719 size_t cbRead = 0;
1720 int rc2 = rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbBuf, true /*fBlocking*/, &cbRead);
1721 if (RT_SUCCESS(rc))
1722 {
1723 cbReadTotal += cbRead;
1724 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
1725 if (RT_SUCCESS(rc))
1726 {
1727 pThis->cbWritten += cbRead;
1728 if (rc2 != VINF_EOF)
1729 continue;
1730 }
1731 }
1732 Assert(rc != VERR_EOF /* expecting VINF_EOF! */);
1733 break;
1734 }
1735
1736 RTMemTmpFree(pvBufFree);
1737
1738 /*
1739 * Do the zero padding.
1740 */
1741 size_t cbBlock = rtZipTarFssWrite_GetBlockSize(pThis);
1742 if ((cbReadTotal & (cbBlock - 1)) && RT_SUCCESS(rc))
1743 {
1744 size_t cbToZero = cbBlock - (cbReadTotal & (cbBlock - 1));
1745 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1746 if (RT_SUCCESS(rc))
1747 pThis->cbWritten += cbToZero;
1748 }
1749
1750 /*
1751 * Update the header. We ASSUME that aHdr[0] is unmodified
1752 * from before the data pumping above and just update the size.
1753 */
1754 if ((RTFOFF)cbReadTotal != pObjInfo->cbObject && RT_SUCCESS(rc))
1755 {
1756 RTFOFF const offRestore = RTVfsFileTell(pThis->hVfsFile);
1757 if (offRestore >= 0)
1758 {
1759 if (rtZipTarFssWrite_IsTar(pThis))
1760 {
1761 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrsTar[0].Common.size, cbReadTotal);
1762 if (RT_SUCCESS(rc))
1763 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrsTar[0]);
1764 }
1765 else
1766 {
1767 if (pObjInfo->cbObject == (uint32_t)pObjInfo->cbObject)
1768 rtZipTarFssWriter_CpioFmtU32(pThis->Cpio.Hdr.AsciiNew.achFileSize, (uint32_t)pObjInfo->cbObject);
1769 else
1770 rc = VERR_FILE_TOO_BIG;
1771 }
1772
1773 if (RT_SUCCESS(rc))
1774 {
1775 rc = RTVfsFileWriteAt(pThis->hVfsFile, offHdr, &pThis->abHdrs[0], pThis->cbHdr, NULL);
1776 if (RT_SUCCESS(rc))
1777 rc = RTVfsFileSeek(pThis->hVfsFile, offRestore, RTFILE_SEEK_BEGIN, NULL);
1778 }
1779 }
1780 else
1781 rc = (int)offRestore;
1782 }
1783
1784 if (RT_SUCCESS(rc))
1785 return VINF_SUCCESS;
1786 }
1787 }
1788 else
1789 rc = (int)offHdr;
1790 pThis->rcFatal = rc;
1791 }
1792 return rc;
1793}
1794
1795
1796/**
1797 * Adds a file to the stream.
1798 *
1799 * @returns IPRT status code.
1800 * @param pThis The TAR writer instance.
1801 * @param pszPath The path to the file.
1802 * @param hVfsIos The I/O stream of the file.
1803 * @param fFlags The RTVFSFSSTREAMOPS::pfnAdd flags.
1804 * @param pObjInfo The object information.
1805 * @param pszOwnerNm The owner name.
1806 * @param pszGroupNm The group name.
1807 */
1808static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
1809 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1810{
1811 /*
1812 * Append the header.
1813 */
1814 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1815 if (RT_SUCCESS(rc))
1816 {
1817 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->abHdrs, pThis->cHdrs * pThis->cbHdr, true /*fBlocking*/, NULL);
1818 if (RT_SUCCESS(rc))
1819 {
1820 pThis->cbWritten += pThis->cHdrs * pThis->cbHdr;
1821
1822 /*
1823 * Copy the bytes. Padding the last buffer to a multiple of the block size.
1824 */
1825 void *pvBufFree;
1826 size_t cbBuf;
1827 uint8_t *pbBuf = rtZipTarFssWriter_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
1828
1829 uint64_t cbLeft = pObjInfo->cbObject;
1830 size_t cbBlock = rtZipTarFssWrite_GetBlockSize(pThis);
1831 while (cbLeft > 0)
1832 {
1833 size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbLeft;
1834 rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
1835 if (RT_FAILURE(rc))
1836 break;
1837
1838 size_t cbToWrite = cbRead;
1839 if (cbRead & (cbBlock - 1))
1840 {
1841 size_t cbToZero = cbBlock - (cbRead & (cbBlock - 1));
1842 memset(&pbBuf[cbRead], 0, cbToZero);
1843 cbToWrite += cbToZero;
1844 }
1845
1846 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
1847 if (RT_FAILURE(rc))
1848 break;
1849 pThis->cbWritten += cbToWrite;
1850 cbLeft -= cbRead;
1851 }
1852
1853 RTMemTmpFree(pvBufFree);
1854
1855 if (RT_SUCCESS(rc))
1856 return VINF_SUCCESS;
1857 }
1858 pThis->rcFatal = rc;
1859 }
1860 return rc;
1861}
1862
1863
1864/**
1865 * Adds a symbolic link to the stream.
1866 *
1867 * @returns IPRT status code.
1868 * @param pThis The TAR writer instance.
1869 * @param pszPath The path to the object.
1870 * @param hVfsSymlink The symbolic link object to add.
1871 * @param pObjInfo The object information.
1872 * @param pszOwnerNm The owner name.
1873 * @param pszGroupNm The group name.
1874 */
1875static int rtZipTarFssWriter_AddSymlink(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSSYMLINK hVfsSymlink,
1876 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1877{
1878 /*
1879 * Read the symlink target first and check that it's not too long.
1880 * Flip DOS slashes.
1881 */
1882 char szTarget[RTPATH_MAX];
1883 int rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
1884 if (RT_SUCCESS(rc))
1885 {
1886#if RTPATH_STYLE != RTPATH_STR_F_STYLE_UNIX
1887 char *pszDosSlash = strchr(szTarget, '\\');
1888 while (pszDosSlash)
1889 {
1890 *pszDosSlash = '/';
1891 pszDosSlash = strchr(pszDosSlash + 1, '\\');
1892 }
1893#endif
1894 size_t cchTarget = strlen(szTarget);
1895 if (rtZipTarFssWrite_IsTar(pThis))
1896 {
1897 if (cchTarget < sizeof(pThis->aHdrsTar[0].Common.linkname))
1898 {
1899 /*
1900 * Create a header, add the link target and push it out.
1901 */
1902 rc = rtZipTarFssWriter_ObjInfoToHdrTar(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1903 if (RT_SUCCESS(rc))
1904 {
1905 memcpy(pThis->aHdrsTar[0].Common.linkname, szTarget, cchTarget + 1);
1906 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrsTar[0]);
1907 if (RT_SUCCESS(rc))
1908 {
1909 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->abHdrs, pThis->cHdrs * pThis->cbHdr,
1910 true /*fBlocking*/, NULL);
1911 if (RT_SUCCESS(rc))
1912 {
1913 pThis->cbWritten += pThis->cHdrs * pThis->cbHdr;
1914 return VINF_SUCCESS;
1915 }
1916 pThis->rcFatal = rc;
1917 }
1918 }
1919 }
1920 else
1921 {
1922 /** @todo implement gnu and pax long name extensions. */
1923 rc = VERR_TAR_NAME_TOO_LONG;
1924 }
1925 }
1926 else
1927 {
1928 /* CPIO stores the target path as file data after the header, without any zero terminator. */
1929 rc = rtZipTarFssWriter_ObjInfoToHdrCpio(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm);
1930 if (RT_SUCCESS(rc))
1931 {
1932 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->abHdrs, pThis->cHdrs * pThis->cbHdr,
1933 true /*fBlocking*/, NULL);
1934 if (RT_SUCCESS(rc))
1935 {
1936 rc = RTVfsIoStrmWrite(pThis->hVfsIos, szTarget, cchTarget, true /*fBlocking*/, NULL);
1937 if (RT_SUCCESS(rc))
1938 {
1939 /* Need to pad the entry to a 4 byte aligned boundary. */
1940 size_t cbPad = RT_ALIGN_32(cchTarget, 4) - cchTarget;
1941 if (cbPad)
1942 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->Cpio.abPad[0], cbPad, true /*fBlocking*/, NULL);
1943 if (RT_SUCCESS(rc))
1944 {
1945 pThis->cbWritten += pThis->cHdrs * pThis->cbHdr + cbPad;
1946 return VINF_SUCCESS;
1947 }
1948 }
1949 }
1950 pThis->rcFatal = rc;
1951 }
1952 }
1953 }
1954 return rc;
1955}
1956
1957
1958/**
1959 * Adds a simple object to the stream.
1960 *
1961 * Simple objects only contains metadata, no actual data bits. Directories,
1962 * devices, fifos, sockets and such.
1963 *
1964 * @returns IPRT status code.
1965 * @param pThis The TAR writer instance.
1966 * @param pszPath The path to the object.
1967 * @param pObjInfo The object information.
1968 * @param pszOwnerNm The owner name.
1969 * @param pszGroupNm The group name.
1970 */
1971static int rtZipTarFssWriter_AddSimpleObject(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
1972 const char *pszOwnerNm, const char *pszGroupNm)
1973{
1974 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1975 if (RT_SUCCESS(rc))
1976 {
1977 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->abHdrs, pThis->cHdrs * pThis->cbHdr, true /*fBlocking*/, NULL);
1978 if (RT_SUCCESS(rc))
1979 {
1980 pThis->cbWritten += pThis->cHdrs * pThis->cbHdr;
1981 return VINF_SUCCESS;
1982 }
1983 pThis->rcFatal = rc;
1984 }
1985 return rc;
1986}
1987
1988
1989/**
1990 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1991 */
1992static DECLCALLBACK(int) rtZipTarFssWriter_Close(void *pvThis)
1993{
1994 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1995
1996 rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1997
1998 RTVfsIoStrmRelease(pThis->hVfsIos);
1999 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
2000
2001 if (pThis->hVfsFile != NIL_RTVFSFILE)
2002 {
2003 RTVfsFileRelease(pThis->hVfsFile);
2004 pThis->hVfsFile = NIL_RTVFSFILE;
2005 }
2006
2007 if (pThis->pszOwner)
2008 {
2009 RTStrFree(pThis->pszOwner);
2010 pThis->pszOwner = NULL;
2011 }
2012 if (pThis->pszGroup)
2013 {
2014 RTStrFree(pThis->pszGroup);
2015 pThis->pszGroup = NULL;
2016 }
2017 if (pThis->pszPrefix)
2018 {
2019 RTStrFree(pThis->pszPrefix);
2020 pThis->pszPrefix = NULL;
2021 }
2022
2023 return VINF_SUCCESS;
2024}
2025
2026
2027/**
2028 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2029 */
2030static DECLCALLBACK(int) rtZipTarFssWriter_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2031{
2032 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
2033 /* Take the lazy approach here, with the sideffect of providing some info
2034 that is actually kind of useful. */
2035 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
2036}
2037
2038
2039/**
2040 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
2041 */
2042static DECLCALLBACK(int) rtZipTarFssWriter_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
2043{
2044 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
2045
2046 /*
2047 * This only works in update mode and up to the point where
2048 * modifications takes place (truncating the archive or appending files).
2049 */
2050 AssertReturn(pThis->pReadTar || pThis->pReadCpio, VERR_ACCESS_DENIED);
2051 AssertReturn(pThis->fFlags & RTZIPTAR_C_UPDATE, VERR_ACCESS_DENIED);
2052
2053 AssertReturn(!pThis->fWriting, VERR_WRONG_ORDER);
2054
2055 if (rtZipTarFssWrite_IsTar(pThis))
2056 return rtZipTarFss_Next(pThis->pReadTar, ppszName, penmType, phVfsObj);
2057 else
2058 return rtZipCpioFss_Next(pThis->pReadCpio, ppszName, penmType, phVfsObj);
2059}
2060
2061
2062/**
2063 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnAdd}
2064 */
2065static DECLCALLBACK(int) rtZipTarFssWriter_Add(void *pvThis, const char *pszPath, RTVFSOBJ hVfsObj, uint32_t fFlags)
2066{
2067 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
2068
2069 /*
2070 * Before we continue we must complete any current push file and check rcFatal.
2071 */
2072 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
2073 AssertRCReturn(rc, rc);
2074
2075 /*
2076 * Query information about the object.
2077 */
2078 RTFSOBJINFO ObjInfo;
2079 rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX);
2080 AssertRCReturn(rc, rc);
2081
2082 RTFSOBJINFO ObjOwnerName;
2083 rc = RTVfsObjQueryInfo(hVfsObj, &ObjOwnerName, RTFSOBJATTRADD_UNIX_OWNER);
2084 if (RT_FAILURE(rc) || ObjOwnerName.Attr.u.UnixOwner.szName[0] == '\0')
2085 strcpy(ObjOwnerName.Attr.u.UnixOwner.szName, "someone");
2086
2087 RTFSOBJINFO ObjGrpName;
2088 rc = RTVfsObjQueryInfo(hVfsObj, &ObjGrpName, RTFSOBJATTRADD_UNIX_GROUP);
2089 if (RT_FAILURE(rc) || ObjGrpName.Attr.u.UnixGroup.szName[0] == '\0')
2090 strcpy(ObjGrpName.Attr.u.UnixGroup.szName, "somegroup");
2091
2092 /*
2093 * Switch the stream into write mode if necessary.
2094 */
2095 rc = rtZipTarFssWriter_SwitchToWriteMode(pThis);
2096 AssertRCReturn(rc, rc);
2097
2098 /*
2099 * Do type specific handling. File have several options and variations to
2100 * take into account, thus the mess.
2101 */
2102 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
2103 {
2104 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
2105 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE);
2106
2107 if (fFlags & RTVFSFSSTRM_ADD_F_STREAM)
2108 rc = rtZipTarFssWriter_AddFileStream(pThis, pszPath, hVfsIos, &ObjInfo,
2109 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
2110 else if ( !(pThis->fFlags & RTZIPTAR_C_SPARSE)
2111 || ObjInfo.cbObject < RTZIPTAR_MIN_SPARSE
2112 || !rtZipTarFssWrite_IsTar(pThis))
2113 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
2114 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
2115 else
2116 {
2117 RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
2118 if (hVfsFile != NIL_RTVFSFILE)
2119 {
2120 rc = rtZipTarFssWriter_AddFileSparse(pThis, pszPath, hVfsFile, hVfsIos, &ObjInfo,
2121 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
2122 RTVfsFileRelease(hVfsFile);
2123 }
2124 else
2125 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
2126 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
2127 }
2128 RTVfsIoStrmRelease(hVfsIos);
2129 }
2130 else if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
2131 {
2132 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
2133 AssertReturn(hVfsSymlink != NIL_RTVFSSYMLINK, VERR_WRONG_TYPE);
2134 rc = rtZipTarFssWriter_AddSymlink(pThis, pszPath, hVfsSymlink, &ObjInfo,
2135 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
2136 RTVfsSymlinkRelease(hVfsSymlink);
2137 }
2138 else
2139 rc = rtZipTarFssWriter_AddSimpleObject(pThis, pszPath, &ObjInfo,
2140 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
2141
2142 return rc;
2143}
2144
2145
2146/**
2147 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnPushFile}
2148 */
2149static DECLCALLBACK(int) rtZipTarFssWriter_PushFile(void *pvThis, const char *pszPath, uint64_t cbFile, PCRTFSOBJINFO paObjInfo,
2150 uint32_t cObjInfo, uint32_t fFlags, PRTVFSIOSTREAM phVfsIos)
2151{
2152 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
2153
2154 /*
2155 * We can only deal with output of indeterminate length if the output is
2156 * seekable (see also rtZipTarFssWriter_AddFileStream).
2157 */
2158 AssertReturn(cbFile != UINT64_MAX || pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
2159 AssertReturn(RT_BOOL(cbFile == UINT64_MAX) == RT_BOOL(fFlags & RTVFSFSSTRM_ADD_F_STREAM), VERR_INVALID_FLAGS);
2160
2161 /*
2162 * Before we continue we must complete any current push file and check rcFatal.
2163 */
2164 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
2165 AssertRCReturn(rc, rc);
2166
2167 /*
2168 * If no object info was provideded, fake up some.
2169 */
2170 const char *pszOwnerNm = "someone";
2171 const char *pszGroupNm = "somegroup";
2172 const size_t cbBlock = rtZipTarFssWrite_GetBlockSize(pThis);
2173 RTFSOBJINFO ObjInfo;
2174 if (cObjInfo == 0)
2175 {
2176 /* Fake up a info. */
2177 RT_ZERO(ObjInfo);
2178 ObjInfo.cbObject = cbFile != UINT64_MAX ? cbFile : 0;
2179 ObjInfo.cbAllocated = cbFile != UINT64_MAX ? RT_ALIGN_64(cbFile, cbBlock) : UINT64_MAX;
2180 RTTimeNow(&ObjInfo.ModificationTime);
2181 ObjInfo.BirthTime = ObjInfo.ModificationTime;
2182 ObjInfo.ChangeTime = ObjInfo.ModificationTime;
2183 ObjInfo.AccessTime = ObjInfo.ModificationTime;
2184 ObjInfo.Attr.fMode = RTFS_TYPE_FILE | 0666;
2185 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
2186 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
2187 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
2188 ObjInfo.Attr.u.Unix.cHardlinks = 1;
2189 //ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
2190 //ObjInfo.Attr.u.Unix.INodeId = 0;
2191 //ObjInfo.Attr.u.Unix.fFlags = 0;
2192 //ObjInfo.Attr.u.Unix.GenerationId = 0;
2193 //ObjInfo.Attr.u.Unix.Device = 0;
2194 }
2195 else
2196 {
2197 /* Make a copy of the object info and adjust the size, if necessary. */
2198 ObjInfo = paObjInfo[0];
2199 Assert(ObjInfo.Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
2200 Assert(RTFS_IS_FILE(ObjInfo.Attr.fMode));
2201 if ((uint64_t)ObjInfo.cbObject != cbFile)
2202 {
2203 ObjInfo.cbObject = cbFile != UINT64_MAX ? cbFile : 0;
2204 ObjInfo.cbAllocated = cbFile != UINT64_MAX ? RT_ALIGN_64(cbFile, cbBlock) : UINT64_MAX;
2205 }
2206
2207 /* Lookup the group and user names. */
2208 for (uint32_t i = 0; i < cObjInfo; i++)
2209 if ( paObjInfo[i].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_OWNER
2210 && paObjInfo[i].Attr.u.UnixOwner.szName[0] != '\0')
2211 pszOwnerNm = paObjInfo[i].Attr.u.UnixOwner.szName;
2212 else if ( paObjInfo[i].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_GROUP
2213 && paObjInfo[i].Attr.u.UnixGroup.szName[0] != '\0')
2214 pszGroupNm = paObjInfo[i].Attr.u.UnixGroup.szName;
2215 }
2216
2217 /*
2218 * Switch the stream into write mode if necessary.
2219 */
2220 rc = rtZipTarFssWriter_SwitchToWriteMode(pThis);
2221 AssertRCReturn(rc, rc);
2222
2223 /*
2224 * Create an I/O stream object for the caller to use.
2225 */
2226 RTFOFF const offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
2227 AssertReturn(offHdr >= 0, (int)offHdr);
2228
2229 PRTZIPTARFSSTREAMWRITERPUSH pPush;
2230 RTVFSIOSTREAM hVfsIos;
2231 if (pThis->hVfsFile == NIL_RTVFSFILE)
2232 {
2233 rc = RTVfsNewIoStream(&g_rtZipTarWriterIoStrmOps, sizeof(*pPush), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
2234 &hVfsIos, (void **)&pPush);
2235 if (RT_FAILURE(rc))
2236 return rc;
2237 }
2238 else
2239 {
2240 RTVFSFILE hVfsFile;
2241 rc = RTVfsNewFile(&g_rtZipTarWriterFileOps, sizeof(*pPush), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
2242 &hVfsFile, (void **)&pPush);
2243 if (RT_FAILURE(rc))
2244 return rc;
2245 hVfsIos = RTVfsFileToIoStream(hVfsFile);
2246 RTVfsFileRelease(hVfsFile);
2247 }
2248 pPush->pParent = NULL;
2249 pPush->cbExpected = cbFile;
2250 pPush->offHdr = (uint64_t)offHdr;
2251 pPush->offData = 0;
2252 pPush->offCurrent = 0;
2253 pPush->cbCurrent = 0;
2254 pPush->ObjInfo = ObjInfo;
2255 pPush->fOpenEnded = cbFile == UINT64_MAX;
2256
2257 /*
2258 * Produce and write file headers.
2259 */
2260 rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, &ObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_NORMAL);
2261 if (RT_SUCCESS(rc))
2262 {
2263 size_t cbHdrs = pThis->cHdrs * pThis->cbHdr;
2264 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->abHdrs, cbHdrs, true /*fBlocking*/, NULL);
2265 if (RT_SUCCESS(rc))
2266 {
2267 pThis->cbWritten += cbHdrs;
2268
2269 /*
2270 * Complete the object and return.
2271 */
2272 pPush->offData = pPush->offHdr + cbHdrs;
2273 if (cbFile == UINT64_MAX)
2274 pPush->cbExpected = (uint64_t)(RTFOFF_MAX - _4K) - pPush->offData;
2275 pPush->pParent = pThis;
2276 pThis->pPush = pPush;
2277
2278 *phVfsIos = hVfsIos;
2279 return VINF_SUCCESS;
2280 }
2281 pThis->rcFatal = rc;
2282 }
2283
2284 RTVfsIoStrmRelease(hVfsIos);
2285 return rc;
2286}
2287
2288
2289/**
2290 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnEnd}
2291 */
2292static DECLCALLBACK(int) rtZipTarFssWriter_End(void *pvThis)
2293{
2294 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
2295
2296 /*
2297 * Make sure to complete any pending push file and that rcFatal is fine.
2298 */
2299 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
2300 if (RT_SUCCESS(rc))
2301 {
2302 if (rtZipTarFssWrite_IsTar(pThis))
2303 {
2304 /*
2305 * There are supposed to be two zero headers at the end of the archive.
2306 * GNU tar may write more because of the way it does buffering,
2307 * libarchive OTOH writes exactly two.
2308 */
2309 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, RTZIPTAR_BLOCKSIZE * 2, true /*fBlocking*/, NULL);
2310 if (RT_SUCCESS(rc))
2311 pThis->cbWritten += RTZIPTAR_BLOCKSIZE * 2;
2312 }
2313 else
2314 {
2315 /* CPIO has a special trailer marker. */
2316 RTFSOBJINFO ObjInfoEos; RT_ZERO(ObjInfoEos);
2317 rc = rtZipTarFssWriter_ObjInfoToHdrCpio(pThis, CPIO_EOS_FILE_NAME, &ObjInfoEos, NULL, NULL);
2318 if (RT_SUCCESS(rc))
2319 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->abHdrs, pThis->cbHdr, true /*fBlocking*/, NULL);
2320 if (RT_SUCCESS(rc))
2321 pThis->cbWritten += pThis->cbHdr;
2322 }
2323 if (RT_SUCCESS(rc))
2324 {
2325 /*
2326 * Flush the output.
2327 */
2328 rc = RTVfsIoStrmFlush(pThis->hVfsIos);
2329
2330 /*
2331 * If we're in update mode, set the end-of-file here to make sure
2332 * unwanted bytes are really discarded.
2333 */
2334 if (RT_SUCCESS(rc) && (pThis->fFlags & RTZIPTAR_C_UPDATE))
2335 {
2336 RTFOFF cbTarFile = RTVfsFileTell(pThis->hVfsFile);
2337 if (cbTarFile >= 0)
2338 rc = RTVfsFileSetSize(pThis->hVfsFile, (uint64_t)cbTarFile, RTVFSFILE_SIZE_F_NORMAL);
2339 else
2340 rc = (int)cbTarFile;
2341 }
2342
2343 /*
2344 * Success?
2345 */
2346 if (RT_SUCCESS(rc))
2347 return rc;
2348 }
2349 pThis->rcFatal = rc;
2350 }
2351 return rc;
2352}
2353
2354
2355/**
2356 * Tar filesystem stream operations.
2357 */
2358static const RTVFSFSSTREAMOPS g_rtZipTarFssOps =
2359{
2360 { /* Obj */
2361 RTVFSOBJOPS_VERSION,
2362 RTVFSOBJTYPE_FS_STREAM,
2363 "TarFsStreamWriter",
2364 rtZipTarFssWriter_Close,
2365 rtZipTarFssWriter_QueryInfo,
2366 NULL,
2367 RTVFSOBJOPS_VERSION
2368 },
2369 RTVFSFSSTREAMOPS_VERSION,
2370 0,
2371 rtZipTarFssWriter_Next,
2372 rtZipTarFssWriter_Add,
2373 rtZipTarFssWriter_PushFile,
2374 rtZipTarFssWriter_End,
2375 RTVFSFSSTREAMOPS_VERSION
2376};
2377
2378
2379RTDECL(int) RTZipTarFsStreamToIoStream(RTVFSIOSTREAM hVfsIosOut, RTZIPTARFORMAT enmFormat,
2380 uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
2381{
2382 /*
2383 * Input validation.
2384 */
2385 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
2386 *phVfsFss = NIL_RTVFSFSSTREAM;
2387 AssertPtrReturn(hVfsIosOut, VERR_INVALID_HANDLE);
2388 AssertReturn(enmFormat > RTZIPTARFORMAT_INVALID && enmFormat < RTZIPTARFORMAT_END, VERR_INVALID_PARAMETER);
2389 AssertReturn(!(fFlags & ~RTZIPTAR_C_VALID_MASK), VERR_INVALID_FLAGS);
2390 AssertReturn(!(fFlags & RTZIPTAR_C_UPDATE), VERR_NOT_SUPPORTED); /* Must use RTZipTarFsStreamForFile! */
2391
2392 if (enmFormat == RTZIPTARFORMAT_DEFAULT)
2393 enmFormat = RTZIPTARFORMAT_GNU;
2394 AssertReturn( enmFormat == RTZIPTARFORMAT_GNU
2395 || enmFormat == RTZIPTARFORMAT_USTAR
2396 || enmFormat == RTZIPTARFORMAT_CPIO_ASCII_NEW
2397 , VERR_NOT_IMPLEMENTED); /* Only implementing GNU and USTAR output at the moment. */
2398
2399 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosOut);
2400 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2401
2402 /*
2403 * Retain the input stream and create a new filesystem stream handle.
2404 */
2405 PRTZIPTARFSSTREAMWRITER pThis;
2406 RTVFSFSSTREAM hVfsFss;
2407 int rc = RTVfsNewFsStream(&g_rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, RTFILE_O_WRITE,
2408 &hVfsFss, (void **)&pThis);
2409 if (RT_SUCCESS(rc))
2410 {
2411 pThis->hVfsIos = hVfsIosOut;
2412 pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIosOut);
2413
2414 pThis->enmFormat = enmFormat;
2415 pThis->fFlags = fFlags;
2416 pThis->rcFatal = VINF_SUCCESS;
2417
2418 pThis->uidOwner = NIL_RTUID;
2419 pThis->pszOwner = NULL;
2420 pThis->gidGroup = NIL_RTGID;
2421 pThis->pszGroup = NULL;
2422 pThis->pszPrefix = NULL;
2423 pThis->pModTime = NULL;
2424 pThis->fFileModeAndMask = ~(RTFMODE)0;
2425 pThis->fFileModeOrMask = 0;
2426 pThis->fDirModeAndMask = ~(RTFMODE)0;
2427 pThis->fDirModeOrMask = 0;
2428 pThis->fWriting = true;
2429
2430 *phVfsFss = hVfsFss;
2431 return VINF_SUCCESS;
2432 }
2433
2434 RTVfsIoStrmRelease(hVfsIosOut);
2435 return rc;
2436}
2437
2438
2439RTDECL(int) RTZipTarFsStreamForFile(RTVFSFILE hVfsFile, RTZIPTARFORMAT enmFormat, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
2440{
2441 /*
2442 * Input validation.
2443 */
2444 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
2445 *phVfsFss = NIL_RTVFSFSSTREAM;
2446 AssertReturn(hVfsFile != NIL_RTVFSFILE, VERR_INVALID_HANDLE);
2447 AssertReturn(enmFormat > RTZIPTARFORMAT_INVALID && enmFormat < RTZIPTARFORMAT_END, VERR_INVALID_PARAMETER);
2448 AssertReturn(!(fFlags & ~RTZIPTAR_C_VALID_MASK), VERR_INVALID_FLAGS);
2449
2450 if (enmFormat == RTZIPTARFORMAT_DEFAULT)
2451 enmFormat = RTZIPTARFORMAT_GNU;
2452 AssertReturn( enmFormat == RTZIPTARFORMAT_GNU
2453 || enmFormat == RTZIPTARFORMAT_USTAR
2454 || enmFormat == RTZIPTARFORMAT_CPIO_ASCII_NEW
2455 , VERR_NOT_IMPLEMENTED); /* Only implementing GNU, USTAR and CPIO output at the moment. */
2456
2457 RTFOFF const offStart = RTVfsFileTell(hVfsFile);
2458 AssertReturn(offStart >= 0, (int)offStart);
2459
2460 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
2461 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2462
2463 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
2464 AssertReturnStmt(hVfsIos != NIL_RTVFSIOSTREAM, RTVfsFileRelease(hVfsFile), VERR_INVALID_HANDLE);
2465
2466 /*
2467 * Retain the input stream and create a new filesystem stream handle.
2468 */
2469 PRTZIPTARFSSTREAMWRITER pThis;
2470 size_t const cbThis = sizeof(*pThis) + ( fFlags & RTZIPTAR_C_UPDATE
2471 ? enmFormat == RTZIPTARFORMAT_CPIO_ASCII_NEW
2472 ? sizeof(*pThis->pReadTar)
2473 : sizeof(*pThis->pReadCpio)
2474 : 0);
2475 RTVFSFSSTREAM hVfsFss;
2476 int rc = RTVfsNewFsStream(&g_rtZipTarFssOps, cbThis, NIL_RTVFS, NIL_RTVFSLOCK,
2477 fFlags & RTZIPTAR_C_UPDATE ? RTFILE_O_READWRITE : RTFILE_O_WRITE,
2478 &hVfsFss, (void **)&pThis);
2479 if (RT_SUCCESS(rc))
2480 {
2481 pThis->hVfsIos = hVfsIos;
2482 pThis->hVfsFile = hVfsFile;
2483
2484 pThis->enmFormat = enmFormat;
2485 pThis->fFlags = fFlags;
2486 pThis->rcFatal = VINF_SUCCESS;
2487
2488 pThis->uidOwner = NIL_RTUID;
2489 pThis->pszOwner = NULL;
2490 pThis->gidGroup = NIL_RTGID;
2491 pThis->pszGroup = NULL;
2492 pThis->pszPrefix = NULL;
2493 pThis->pModTime = NULL;
2494 pThis->fFileModeAndMask = ~(RTFMODE)0;
2495 pThis->fFileModeOrMask = 0;
2496 pThis->fDirModeAndMask = ~(RTFMODE)0;
2497 pThis->fDirModeOrMask = 0;
2498 if (!(fFlags & RTZIPTAR_C_UPDATE))
2499 pThis->fWriting = true;
2500 else
2501 {
2502 pThis->fWriting = false;
2503 if (rtZipTarFssWrite_IsTar(pThis))
2504 {
2505 pThis->pReadTar = (PRTZIPTARFSSTREAM)(pThis + 1);
2506 rtZipTarReaderInit(pThis->pReadTar, hVfsIos, (uint64_t)offStart);
2507 }
2508 else
2509 {
2510 pThis->pReadCpio = (PRTZIPCPIOFSSTREAM)(pThis + 1);
2511 rtZipCpioReaderInit(pThis->pReadCpio, hVfsIos, (uint64_t)offStart);
2512 }
2513 }
2514
2515 *phVfsFss = hVfsFss;
2516 return VINF_SUCCESS;
2517 }
2518
2519 RTVfsIoStrmRelease(hVfsIos);
2520 RTVfsFileRelease(hVfsFile);
2521 return rc;
2522}
2523
2524
2525RTDECL(int) RTZipTarFsStreamSetOwner(RTVFSFSSTREAM hVfsFss, RTUID uid, const char *pszOwner)
2526{
2527 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2528 AssertReturn(pThis, VERR_WRONG_TYPE);
2529
2530 pThis->uidOwner = uid;
2531 if (pThis->pszOwner)
2532 {
2533 RTStrFree(pThis->pszOwner);
2534 pThis->pszOwner = NULL;
2535 }
2536 if (pszOwner)
2537 {
2538 pThis->pszOwner = RTStrDup(pszOwner);
2539 AssertReturn(pThis->pszOwner, VERR_NO_STR_MEMORY);
2540 }
2541
2542 return VINF_SUCCESS;
2543}
2544
2545
2546RTDECL(int) RTZipTarFsStreamSetGroup(RTVFSFSSTREAM hVfsFss, RTGID gid, const char *pszGroup)
2547{
2548 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2549 AssertReturn(pThis, VERR_WRONG_TYPE);
2550
2551 pThis->gidGroup = gid;
2552 if (pThis->pszGroup)
2553 {
2554 RTStrFree(pThis->pszGroup);
2555 pThis->pszGroup = NULL;
2556 }
2557 if (pszGroup)
2558 {
2559 pThis->pszGroup = RTStrDup(pszGroup);
2560 AssertReturn(pThis->pszGroup, VERR_NO_STR_MEMORY);
2561 }
2562
2563 return VINF_SUCCESS;
2564}
2565
2566
2567RTDECL(int) RTZipTarFsStreamSetPrefix(RTVFSFSSTREAM hVfsFss, const char *pszPrefix)
2568{
2569 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2570 AssertReturn(pThis, VERR_WRONG_TYPE);
2571 AssertReturn(!pszPrefix || *pszPrefix, VERR_INVALID_NAME);
2572
2573 if (pThis->pszPrefix)
2574 {
2575 RTStrFree(pThis->pszPrefix);
2576 pThis->pszPrefix = NULL;
2577 pThis->cchPrefix = 0;
2578 }
2579 if (pszPrefix)
2580 {
2581 /*
2582 * Make a copy of the prefix, make sure it ends with a slash,
2583 * then flip DOS slashes.
2584 */
2585 size_t cchPrefix = strlen(pszPrefix);
2586 char *pszCopy = RTStrAlloc(cchPrefix + 3);
2587 AssertReturn(pszCopy, VERR_NO_STR_MEMORY);
2588 memcpy(pszCopy, pszPrefix, cchPrefix + 1);
2589
2590 RTPathEnsureTrailingSeparator(pszCopy, cchPrefix + 3);
2591
2592#if RTPATH_STYLE != RTPATH_STR_F_STYLE_UNIX
2593 char *pszDosSlash = strchr(pszCopy, '\\');
2594 while (pszDosSlash)
2595 {
2596 *pszDosSlash = '/';
2597 pszDosSlash = strchr(pszDosSlash + 1, '\\');
2598 }
2599#endif
2600
2601 pThis->cchPrefix = cchPrefix + strlen(&pszCopy[cchPrefix]);
2602 pThis->pszPrefix = pszCopy;
2603 }
2604
2605 return VINF_SUCCESS;
2606}
2607
2608
2609RTDECL(int) RTZipTarFsStreamSetModTime(RTVFSFSSTREAM hVfsFss, PCRTTIMESPEC pModificationTime)
2610{
2611 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2612 AssertReturn(pThis, VERR_WRONG_TYPE);
2613
2614 if (pModificationTime)
2615 {
2616 pThis->ModTime = *pModificationTime;
2617 pThis->pModTime = &pThis->ModTime;
2618 }
2619 else
2620 pThis->pModTime = NULL;
2621
2622 return VINF_SUCCESS;
2623}
2624
2625
2626RTDECL(int) RTZipTarFsStreamSetFileMode(RTVFSFSSTREAM hVfsFss, RTFMODE fAndMode, RTFMODE fOrMode)
2627{
2628 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2629 AssertReturn(pThis, VERR_WRONG_TYPE);
2630
2631 pThis->fFileModeAndMask = fAndMode | ~RTFS_UNIX_ALL_PERMS;
2632 pThis->fFileModeOrMask = fOrMode & RTFS_UNIX_ALL_PERMS;
2633 return VINF_SUCCESS;
2634}
2635
2636
2637RTDECL(int) RTZipTarFsStreamSetDirMode(RTVFSFSSTREAM hVfsFss, RTFMODE fAndMode, RTFMODE fOrMode)
2638{
2639 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2640 AssertReturn(pThis, VERR_WRONG_TYPE);
2641
2642 pThis->fDirModeAndMask = fAndMode | ~RTFS_UNIX_ALL_PERMS;
2643 pThis->fDirModeOrMask = fOrMode & RTFS_UNIX_ALL_PERMS;
2644 return VINF_SUCCESS;
2645}
2646
2647
2648RTDECL(int) RTZipTarFsStreamTruncate(RTVFSFSSTREAM hVfsFss, RTVFSOBJ hVfsObj, bool fAfter)
2649{
2650 /*
2651 * Translate and validate the input.
2652 */
2653 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)RTVfsFsStreamToPrivate(hVfsFss, &g_rtZipTarFssOps);
2654 AssertReturn(pThis, VERR_WRONG_TYPE);
2655
2656 AssertReturn(hVfsObj != NIL_RTVFSOBJ, VERR_INVALID_HANDLE);
2657 AssertReturn(pThis->pReadTar || pThis->pReadCpio, VERR_ACCESS_DENIED);
2658 AssertReturn(pThis->fFlags & RTZIPTAR_C_UPDATE, VERR_ACCESS_DENIED);
2659 AssertReturn(!pThis->fWriting, VERR_WRONG_ORDER);
2660
2661 int rc;
2662 if (rtZipTarFssWrite_IsTar(pThis))
2663 {
2664 PRTZIPTARBASEOBJ pThisObj = rtZipTarFsStreamBaseObjToPrivate(pThis->pReadTar, hVfsObj);
2665 AssertReturn(pThisObj, VERR_NOT_OWNER);
2666
2667 /*
2668 * Seek to the desired cut-off point and indicate that we've switched to writing.
2669 */
2670 rc = RTVfsFileSeek(pThis->hVfsFile, fAfter ? pThisObj->offNextHdr : pThisObj->offHdr,
2671 RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
2672 }
2673 else
2674 {
2675 PRTZIPCPIOBASEOBJ pThisObj = rtZipCpioFsStreamBaseObjToPrivate(pThis->pReadCpio, hVfsObj);
2676 AssertReturn(pThisObj, VERR_NOT_OWNER);
2677
2678 /*
2679 * Seek to the desired cut-off point and indicate that we've switched to writing.
2680 */
2681 rc = RTVfsFileSeek(pThis->hVfsFile, fAfter ? pThisObj->offNextHdr : pThisObj->offHdr,
2682 RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
2683 }
2684 if (RT_SUCCESS(rc))
2685 pThis->fWriting = true;
2686 else
2687 pThis->rcFatal = rc;
2688 return rc;
2689}
2690
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use