VirtualBox

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

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

IPRT: rtZipTarFssWriter_WriteGnuSparseHeaders: isextended location differs between the normal GNU header and the sparse GNU header.

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

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