VirtualBox

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

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

IPRT/vfstarwriter.cpp: Initial push-file implementation (untested).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.0 KB
Line 
1/* $Id: tarvfswriter.cpp 67166 2017-05-31 11:14:37Z 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 /*
1164 * Walk the sparse spans, fill and write headers one by one.
1165 */
1166 PRTZIPTARGNUSPARSE paSparse = &pThis->aHdrs[0].Gnu.sparse[0];
1167 uint32_t cSparse = RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse);
1168 uint32_t iSparse = 0;
1169
1170 PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1171 PRTZIPTARSPARSECHUNK pChunk;
1172 RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
1173 {
1174 uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
1175 ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
1176 for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
1177 {
1178 /* Flush the header? */
1179 if (iSparse >= cSparse)
1180 {
1181 pThis->aHdrs[0].Gnu.isextended = 1; /* more headers to come */
1182 if (cSparse == RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse))
1183 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1184 if (RT_SUCCESS(rc))
1185 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1186 if (RT_FAILURE(rc))
1187 return rc;
1188 RT_ZERO(pThis->aHdrs[0]);
1189 cSparse = RT_ELEMENTS(pThis->aHdrs[0].GnuSparse.sp);
1190 iSparse = 0;
1191 paSparse = &pThis->aHdrs[0].GnuSparse.sp[0];
1192 }
1193
1194 /* Append sparse data segment. */
1195 rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].offset, pChunk->aSpans[iSpan].off);
1196 AssertRCReturn(rc, rc);
1197 rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].numbytes, pChunk->aSpans[iSpan].cb);
1198 AssertRCReturn(rc, rc);
1199 iSparse++;
1200 }
1201 }
1202
1203 /*
1204 * The final header.
1205 */
1206 if (iSparse != 0)
1207 {
1208 Assert(pThis->aHdrs[0].Gnu.isextended == 0);
1209 rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1210 }
1211 pThis->cHdrs = 0;
1212 return rc;
1213}
1214
1215
1216/**
1217 * Adds a potentially sparse file to the output.
1218 *
1219 * @returns IPRT status code.
1220 * @param pThis The TAR writer instance.
1221 * @param pszPath The path to the file.
1222 * @param hVfsFile The potentially sparse file.
1223 * @param hVfsIos The I/O stream of the file. Same as @a hVfsFile.
1224 * @param pObjInfo The object information.
1225 * @param pszOwnerNm The owner name.
1226 * @param pszGroupNm The group name.
1227 */
1228static int rtZipTarFssWriter_AddFileSparse(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSFILE hVfsFile,
1229 RTVFSIOSTREAM hVfsIos, PCRTFSOBJINFO pObjInfo,
1230 const char *pszOwnerNm, const char *pszGroupNm)
1231{
1232 /*
1233 * Scan the input file to locate all zero blocks.
1234 */
1235 void *pvBufFree;
1236 size_t cbBuf;
1237 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
1238
1239 PRTZIPTARSPARSE pSparse;
1240 int rc = rtZipTarFssWriter_ScanSparseFile(pThis, hVfsFile, pObjInfo->cbObject, cbBuf, pbBuf, &pSparse);
1241 if (RT_SUCCESS(rc))
1242 {
1243 /*
1244 * If there aren't at least 2 zero blocks in the file, don't bother
1245 * doing the sparse stuff and store it as a normal file.
1246 */
1247 if (pSparse->cbDataSpans + RTZIPTAR_BLOCKSIZE > (uint64_t)pObjInfo->cbObject)
1248 {
1249 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1250 RTMemTmpFree(pvBufFree);
1251 return rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, pObjInfo, pszOwnerNm, pszGroupNm);
1252 }
1253
1254 /*
1255 * Produce and write the headers.
1256 */
1257 if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
1258 rc = rtZipTarFssWriter_WriteGnuSparseHeaders(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, pSparse);
1259 else
1260 AssertStmt(pThis->enmFormat != RTZIPTARFORMAT_GNU, rc = VERR_NOT_IMPLEMENTED);
1261 if (RT_SUCCESS(rc))
1262 {
1263 /*
1264 * Write the file bytes.
1265 */
1266 PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
1267 PRTZIPTARSPARSECHUNK pChunk;
1268 RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
1269 {
1270 uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
1271 ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
1272 for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
1273 {
1274 rc = RTVfsFileSeek(hVfsFile, pChunk->aSpans[iSpan].off, RTFILE_SEEK_BEGIN, NULL);
1275 if (RT_FAILURE(rc))
1276 break;
1277 uint64_t cbLeft = pChunk->aSpans[iSpan].cb;
1278 Assert( !(cbLeft & (RTZIPTAR_BLOCKSIZE - 1))
1279 || (iSpan + 1 == cSpans && pChunk == pLastChunk));
1280 while (cbLeft > 0)
1281 {
1282 size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
1283 rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
1284 if (RT_SUCCESS(rc))
1285 {
1286 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToRead, true /*fBlocking*/, NULL);
1287 if (RT_SUCCESS(rc))
1288 {
1289 pThis->cbWritten += cbToRead;
1290 cbLeft -= cbToRead;
1291 continue;
1292 }
1293 }
1294 break;
1295 }
1296 if (RT_FAILURE(rc))
1297 break;
1298 }
1299 }
1300
1301 /*
1302 * Do the zero padding.
1303 */
1304 if ( RT_SUCCESS(rc)
1305 && (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1)))
1306 {
1307 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1));
1308 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1309 if (RT_SUCCESS(rc))
1310 pThis->cbWritten += cbToZero;
1311 }
1312 }
1313
1314 if (RT_FAILURE(rc))
1315 pThis->rcFatal = rc;
1316 rtZipTarFssWriter_SparseInfoDestroy(pSparse);
1317 }
1318 RTMemTmpFree(pvBufFree);
1319 return rc;
1320}
1321
1322
1323/**
1324 * Adds an I/O stream of indeterminate length to the TAR file.
1325 *
1326 * This requires the output to be seekable, i.e. a file, because we need to go
1327 * back and update @c size field of the TAR header after pumping all the data
1328 * bytes thru and establishing the file length.
1329 *
1330 * @returns IPRT status code.
1331 * @param pThis The TAR writer instance.
1332 * @param pszPath The path to the file.
1333 * @param hVfsIos The I/O stream of the file.
1334 * @param pObjInfo The object information.
1335 * @param pszOwnerNm The owner name.
1336 * @param pszGroupNm The group name.
1337 */
1338static int rtZipTarFssWriter_AddFileStream(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
1339 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1340{
1341 AssertReturn(pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
1342
1343 /*
1344 * Append the header.
1345 */
1346 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1347 if (RT_SUCCESS(rc))
1348 {
1349 RTFOFF const offHdr = RTVfsFileTell(pThis->hVfsFile);
1350 if (offHdr >= 0)
1351 {
1352 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1353 if (RT_SUCCESS(rc))
1354 {
1355 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1356
1357 /*
1358 * Transfer the bytes.
1359 */
1360 void *pvBufFree;
1361 size_t cbBuf;
1362 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree,
1363 pObjInfo->cbObject > 0 && pObjInfo->cbObject != RTFOFF_MAX
1364 ? pObjInfo->cbObject : _1G);
1365
1366 uint64_t cbReadTotal = 0;
1367 for (;;)
1368 {
1369 size_t cbRead = 0;
1370 int rc2 = rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbBuf, true /*fBlocking*/, &cbRead);
1371 if (RT_SUCCESS(rc))
1372 {
1373 cbReadTotal += cbRead;
1374 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
1375 if (RT_SUCCESS(rc))
1376 {
1377 pThis->cbWritten += cbRead;
1378 if (rc2 != VINF_EOF)
1379 continue;
1380 }
1381 }
1382 Assert(rc != VERR_EOF /* expecting VINF_EOF! */);
1383 break;
1384 }
1385
1386 RTMemTmpFree(pvBufFree);
1387
1388 /*
1389 * Do the zero padding.
1390 */
1391 if ((cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1)) && RT_SUCCESS(rc))
1392 {
1393 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1));
1394 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
1395 if (RT_SUCCESS(rc))
1396 pThis->cbWritten += cbToZero;
1397 }
1398
1399 /*
1400 * Update the header. We ASSUME that aHdr[0] is unmodified
1401 * from before the data pumping above and just update the size.
1402 */
1403 if ((RTFOFF)cbReadTotal != pObjInfo->cbObject && RT_SUCCESS(rc))
1404 {
1405 RTFOFF const offRestore = RTVfsFileTell(pThis->hVfsFile);
1406 if (offRestore >= 0)
1407 {
1408 rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, cbReadTotal);
1409 if (RT_SUCCESS(rc))
1410 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1411 if (RT_SUCCESS(rc))
1412 {
1413 rc = RTVfsFileWriteAt(pThis->hVfsFile, offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
1414 if (RT_SUCCESS(rc))
1415 rc = RTVfsFileSeek(pThis->hVfsFile, offRestore, RTFILE_SEEK_BEGIN, NULL);
1416 }
1417 }
1418 else
1419 rc = (int)offRestore;
1420 }
1421
1422 if (RT_SUCCESS(rc))
1423 return VINF_SUCCESS;
1424 }
1425 }
1426 else
1427 rc = (int)offHdr;
1428 pThis->rcFatal = rc;
1429 }
1430 return rc;
1431}
1432
1433
1434/**
1435 * Adds a file to the stream.
1436 *
1437 * @returns IPRT status code.
1438 * @param pThis The TAR writer instance.
1439 * @param pszPath The path to the file.
1440 * @param hVfsIos The I/O stream of the file.
1441 * @param fFlags The RTVFSFSSTREAMOPS::pfnAdd flags.
1442 * @param pObjInfo The object information.
1443 * @param pszOwnerNm The owner name.
1444 * @param pszGroupNm The group name.
1445 */
1446static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
1447 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1448{
1449 /*
1450 * Append the header.
1451 */
1452 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1453 if (RT_SUCCESS(rc))
1454 {
1455 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1456 if (RT_SUCCESS(rc))
1457 {
1458 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1459
1460 /*
1461 * Copy the bytes. Padding the last buffer to a multiple of 512.
1462 */
1463 void *pvBufFree;
1464 size_t cbBuf;
1465 uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
1466
1467 uint64_t cbLeft = pObjInfo->cbObject;
1468 while (cbLeft > 0)
1469 {
1470 size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbLeft;
1471 rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
1472 if (RT_FAILURE(rc))
1473 break;
1474
1475 size_t cbToWrite = cbRead;
1476 if (cbRead & (RTZIPTAR_BLOCKSIZE - 1))
1477 {
1478 size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbRead & (RTZIPTAR_BLOCKSIZE - 1));
1479 memset(&pbBuf[cbRead], 0, cbToZero);
1480 cbToWrite += cbToZero;
1481 }
1482
1483 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
1484 if (RT_FAILURE(rc))
1485 break;
1486 pThis->cbWritten += cbToWrite;
1487 cbLeft -= cbRead;
1488 }
1489
1490 RTMemTmpFree(pvBufFree);
1491
1492 if (RT_SUCCESS(rc))
1493 return VINF_SUCCESS;
1494 }
1495 pThis->rcFatal = rc;
1496 }
1497 return rc;
1498}
1499
1500
1501/**
1502 * Adds a symbolic link to the stream.
1503 *
1504 * @returns IPRT status code.
1505 * @param pThis The TAR writer instance.
1506 * @param pszPath The path to the object.
1507 * @param hVfsSymlink The symbolic link object to add.
1508 * @param pObjInfo The object information.
1509 * @param pszOwnerNm The owner name.
1510 * @param pszGroupNm The group name.
1511 */
1512static int rtZipTarFssWriter_AddSymlink(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSSYMLINK hVfsSymlink,
1513 PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
1514{
1515 /*
1516 * Read the symlink target first and check that it's not too long.
1517 * Flip DOS slashes.
1518 */
1519 char szTarget[RTPATH_MAX];
1520 int rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
1521 if (RT_SUCCESS(rc))
1522 {
1523#if RTPATH_STYLE == RTPATH_STR_F_STYLE_UNIX
1524 char *pszDosSlash = strchr(szTarget, '\\');
1525 while (pszDosSlash)
1526 {
1527 *pszDosSlash = '/';
1528 pszDosSlash = strchr(pszDosSlash + 1, '\\');
1529 }
1530#endif
1531 size_t cchTarget = strlen(szTarget);
1532 if (cchTarget < sizeof(pThis->aHdrs[0].Common.linkname))
1533 {
1534 /*
1535 * Create a header, add the link target and push it out.
1536 */
1537 rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1538 if (RT_SUCCESS(rc))
1539 {
1540 memcpy(pThis->aHdrs[0].Common.linkname, szTarget, cchTarget + 1);
1541 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
1542 if (RT_SUCCESS(rc))
1543 {
1544 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]),
1545 true /*fBlocking*/, NULL);
1546 if (RT_SUCCESS(rc))
1547 {
1548 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1549 return VINF_SUCCESS;
1550 }
1551 pThis->rcFatal = rc;
1552 }
1553 }
1554 }
1555 else
1556 {
1557 /** @todo implement gnu and pax long name extensions. */
1558 rc = VERR_TAR_NAME_TOO_LONG;
1559 }
1560 }
1561 return rc;
1562}
1563
1564
1565/**
1566 * Adds a simple object to the stream.
1567 *
1568 * Simple objects only contains metadata, no actual data bits. Directories,
1569 * devices, fifos, sockets and such.
1570 *
1571 * @returns IPRT status code.
1572 * @param pThis The TAR writer instance.
1573 * @param pszPath The path to the object.
1574 * @param pObjInfo The object information.
1575 * @param pszOwnerNm The owner name.
1576 * @param pszGroupNm The group name.
1577 */
1578static int rtZipTarFssWriter_AddSimpleObject(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
1579 const char *pszOwnerNm, const char *pszGroupNm)
1580{
1581 int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
1582 if (RT_SUCCESS(rc))
1583 {
1584 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
1585 if (RT_SUCCESS(rc))
1586 {
1587 pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1588 return VINF_SUCCESS;
1589 }
1590 pThis->rcFatal = rc;
1591 }
1592 return rc;
1593}
1594
1595
1596/**
1597 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1598 */
1599static DECLCALLBACK(int) rtZipTarFssWriter_Close(void *pvThis)
1600{
1601 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1602
1603 rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1604
1605 RTVfsIoStrmRelease(pThis->hVfsIos);
1606 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
1607
1608 if (pThis->hVfsFile != NIL_RTVFSFILE)
1609 {
1610 RTVfsFileRelease(pThis->hVfsFile);
1611 pThis->hVfsFile = NIL_RTVFSFILE;
1612 }
1613
1614 return VINF_SUCCESS;
1615}
1616
1617
1618/**
1619 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1620 */
1621static DECLCALLBACK(int) rtZipTarFssWriter_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1622{
1623 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1624 /* Take the lazy approach here, with the sideffect of providing some info
1625 that is actually kind of useful. */
1626 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
1627}
1628
1629
1630/**
1631 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnAdd}
1632 */
1633static DECLCALLBACK(int) rtZipTarFssWriter_Add(void *pvThis, const char *pszPath, RTVFSOBJ hVfsObj, uint32_t fFlags)
1634{
1635 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1636
1637 /*
1638 * Before we continue we must complete any current push file and check rcFatal.
1639 */
1640 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1641 if (RT_FAILURE(rc))
1642 return rc;
1643
1644 /*
1645 * Query information about the object.
1646 */
1647 RTFSOBJINFO ObjInfo;
1648 rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX);
1649 AssertRCReturn(rc, rc);
1650
1651 RTFSOBJINFO ObjOwnerName;
1652 rc = RTVfsObjQueryInfo(hVfsObj, &ObjOwnerName, RTFSOBJATTRADD_UNIX_OWNER);
1653 if (RT_FAILURE(rc) || ObjOwnerName.Attr.u.UnixOwner.szName[0] == '\0')
1654 strcpy(ObjOwnerName.Attr.u.UnixOwner.szName, "someone");
1655
1656 RTFSOBJINFO ObjGrpName;
1657 rc = RTVfsObjQueryInfo(hVfsObj, &ObjGrpName, RTFSOBJATTRADD_UNIX_GROUP);
1658 if (RT_FAILURE(rc) || ObjGrpName.Attr.u.UnixGroup.szName[0] == '\0')
1659 strcpy(ObjGrpName.Attr.u.UnixGroup.szName, "somegroup");
1660
1661 /*
1662 * Do type specific handling. File have several options and variations to
1663 * take into account, thus the mess.
1664 */
1665 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
1666 {
1667 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
1668 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE);
1669
1670 if (fFlags & RTVFSFSSTRM_ADD_F_STREAM)
1671 rc = rtZipTarFssWriter_AddFileStream(pThis, pszPath, hVfsIos, &ObjInfo,
1672 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1673 else if ( !(pThis->fFlags & RTZIPTAR_C_SPARSE)
1674 || ObjInfo.cbObject < RTZIPTAR_MIN_SPARSE)
1675 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
1676 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1677 else
1678 {
1679 RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
1680 if (hVfsFile != NIL_RTVFSFILE)
1681 {
1682 rc = rtZipTarFssWriter_AddFileSparse(pThis, pszPath, hVfsFile, hVfsIos, &ObjInfo,
1683 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1684 RTVfsFileRelease(hVfsFile);
1685 }
1686 else
1687 rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
1688 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1689 }
1690 RTVfsIoStrmRelease(hVfsIos);
1691 }
1692 else if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
1693 {
1694 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
1695 AssertReturn(hVfsSymlink != NIL_RTVFSSYMLINK, VERR_WRONG_TYPE);
1696 rc = rtZipTarFssWriter_AddSymlink(pThis, pszPath, hVfsSymlink, &ObjInfo,
1697 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1698 RTVfsSymlinkRelease(hVfsSymlink);
1699 }
1700 else
1701 rc = rtZipTarFssWriter_AddSimpleObject(pThis, pszPath, &ObjInfo,
1702 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
1703
1704 return rc;
1705}
1706
1707
1708/**
1709 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnPushFile}
1710 */
1711static DECLCALLBACK(int) rtZipTarFssWriter_PushFile(void *pvThis, const char *pszPath, uint64_t cbFile, PCRTFSOBJINFO paObjInfo,
1712 uint32_t cObjInfo, uint32_t fFlags, PRTVFSIOSTREAM phVfsIos)
1713{
1714 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1715
1716 /*
1717 * We can only deal with output of indeterminate length if the output is
1718 * seekable (see also rtZipTarFssWriter_AddFileStream).
1719 */
1720 AssertReturn(cbFile != UINT64_MAX || pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
1721 AssertReturn(RT_BOOL(cbFile == UINT64_MAX) == RT_BOOL(fFlags & RTVFSFSSTRM_ADD_F_STREAM), VERR_INVALID_FLAGS);
1722
1723 /*
1724 * Before we continue we must complete any current push file and check rcFatal.
1725 */
1726 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1727 if (RT_FAILURE(rc))
1728 return rc;
1729
1730 /*
1731 * If no object info was provideded, fake up some.
1732 */
1733 const char *pszOwnerNm = "someone";
1734 const char *pszGroupNm = "somegroup";
1735 RTFSOBJINFO ObjInfo;
1736 if (cObjInfo == 0)
1737 {
1738 /* Fake up a info. */
1739 RT_ZERO(ObjInfo);
1740 ObjInfo.cbObject = cbFile != UINT64_MAX ? cbFile : 0;
1741 ObjInfo.cbAllocated = cbFile != UINT64_MAX ? RT_ALIGN_64(cbFile, RTZIPTAR_BLOCKSIZE) : UINT64_MAX;
1742 RTTimeNow(&ObjInfo.ModificationTime);
1743 ObjInfo.BirthTime = ObjInfo.ModificationTime;
1744 ObjInfo.ChangeTime = ObjInfo.ModificationTime;
1745 ObjInfo.AccessTime = ObjInfo.ModificationTime;
1746 ObjInfo.Attr.fMode = RTFS_TYPE_FILE | 0666;
1747 ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1748 ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
1749 ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
1750 ObjInfo.Attr.u.Unix.cHardlinks = 1;
1751 //ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
1752 //ObjInfo.Attr.u.Unix.INodeId = 0;
1753 //ObjInfo.Attr.u.Unix.fFlags = 0;
1754 //ObjInfo.Attr.u.Unix.GenerationId = 0;
1755 //ObjInfo.Attr.u.Unix.Device = 0;
1756 }
1757 else
1758 {
1759 /* Make a copy of the object info and adjust the size, if necessary. */
1760 ObjInfo = paObjInfo[0];
1761 Assert(ObjInfo.Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
1762 Assert(RTFS_IS_FILE(ObjInfo.Attr.fMode));
1763 if ((uint64_t)ObjInfo.cbObject != cbFile)
1764 {
1765 ObjInfo.cbObject = cbFile != UINT64_MAX ? cbFile : 0;
1766 ObjInfo.cbAllocated = cbFile != UINT64_MAX ? RT_ALIGN_64(cbFile, RTZIPTAR_BLOCKSIZE) : UINT64_MAX;
1767 }
1768
1769 /* Lookup the group and user names. */
1770 for (uint32_t i = 0; i < cObjInfo; i++)
1771 if ( paObjInfo[i].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_OWNER
1772 && paObjInfo[i].Attr.u.UnixOwner.szName[0] != '\0')
1773 pszOwnerNm = paObjInfo[i].Attr.u.UnixOwner.szName;
1774 else if ( paObjInfo[i].Attr.enmAdditional == RTFSOBJATTRADD_UNIX_GROUP
1775 && paObjInfo[i].Attr.u.UnixGroup.szName[0] != '\0')
1776 pszGroupNm = paObjInfo[i].Attr.u.UnixGroup.szName;
1777 }
1778
1779 /*
1780 * Create an I/O stream object for the caller to use.
1781 */
1782 RTFOFF const offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
1783 AssertReturn(offHdr >= 0, (int)offHdr);
1784
1785 PRTZIPTARFSSTREAMWRITERPUSH pPush;
1786 RTVFSIOSTREAM hVfsIos;
1787 if (pThis->hVfsFile == NIL_RTVFSFILE)
1788 {
1789 rc = RTVfsNewIoStream(&g_rtZipTarWriterIoStrmOps, sizeof(*pPush), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
1790 &hVfsIos, (void **)&pPush);
1791 if (RT_FAILURE(rc))
1792 return rc;
1793 }
1794 else
1795 {
1796 RTVFSFILE hVfsFile;
1797 rc = RTVfsNewFile(&g_rtZipTarWriterFileOps, sizeof(*pPush), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
1798 &hVfsFile, (void **)&pPush);
1799 if (RT_FAILURE(rc))
1800 return rc;
1801 hVfsIos = RTVfsFileToIoStream(hVfsFile);
1802 RTVfsFileRelease(hVfsFile);
1803 }
1804 pPush->pParent = NULL;
1805 pPush->cbExpected = cbFile;
1806 pPush->offHdr = (uint64_t)offHdr;
1807 pPush->offData = 0;
1808 pPush->offCurrent = 0;
1809 pPush->cbCurrent = 0;
1810 pPush->ObjInfo = ObjInfo;
1811 pPush->fOpenEnded = cbFile == UINT64_MAX;
1812
1813 /*
1814 * Produce and write file headers.
1815 */
1816 rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, &ObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_NORMAL);
1817 if (RT_SUCCESS(rc))
1818 {
1819 size_t cbHdrs = pThis->cHdrs * sizeof(pThis->aHdrs[0]);
1820 rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, cbHdrs, true /*fBlocking*/, NULL);
1821 if (RT_SUCCESS(rc))
1822 {
1823 pThis->cbWritten += cbHdrs;
1824
1825 /*
1826 * Complete the object and return.
1827 */
1828 pPush->offData = pPush->offHdr + cbHdrs;
1829 if (cbFile == UINT64_MAX)
1830 pPush->cbExpected = (uint64_t)(RTFOFF_MAX - _4K) - pPush->offData;
1831 pPush->pParent = pThis;
1832 pThis->pPush = pPush;
1833
1834 *phVfsIos = hVfsIos;
1835 return VINF_SUCCESS;
1836 }
1837 pThis->rcFatal = rc;
1838 }
1839
1840 RTVfsIoStrmRelease(hVfsIos);
1841 return rc;
1842}
1843
1844
1845/**
1846 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnEnd}
1847 */
1848static DECLCALLBACK(int) rtZipTarFssWriter_End(void *pvThis)
1849{
1850 PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
1851
1852 /*
1853 * Make sure to complete any pending push file and that rcFatal is fine.
1854 */
1855 int rc = rtZipTarFssWriter_CompleteCurrentPushFile(pThis);
1856 if (RT_SUCCESS(rc))
1857 {
1858 /*
1859 * There are supposed to be two zero headers at the end of the archive.
1860 * GNU tar may write more because of the way it does buffering,
1861 * libarchive OTOH writes exactly two.
1862 */
1863 rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, RTZIPTAR_BLOCKSIZE * 2, true /*fBlocking*/, NULL);
1864 if (RT_SUCCESS(rc))
1865 {
1866 pThis->cbWritten += RTZIPTAR_BLOCKSIZE * 2;
1867
1868 /*
1869 * Flush the output.
1870 */
1871 rc = RTVfsIoStrmFlush(pThis->hVfsIos);
1872 if (RT_SUCCESS(rc))
1873 return rc;
1874 }
1875 pThis->rcFatal = rc;
1876 }
1877 return rc;
1878}
1879
1880
1881/**
1882 * Tar filesystem stream operations.
1883 */
1884static const RTVFSFSSTREAMOPS rtZipTarFssOps =
1885{
1886 { /* Obj */
1887 RTVFSOBJOPS_VERSION,
1888 RTVFSOBJTYPE_FS_STREAM,
1889 "TarFsStreamWriter",
1890 rtZipTarFssWriter_Close,
1891 rtZipTarFssWriter_QueryInfo,
1892 RTVFSOBJOPS_VERSION
1893 },
1894 RTVFSFSSTREAMOPS_VERSION,
1895 0,
1896 NULL,
1897 rtZipTarFssWriter_Add,
1898 rtZipTarFssWriter_PushFile,
1899 rtZipTarFssWriter_End,
1900 RTVFSFSSTREAMOPS_VERSION
1901};
1902
1903
1904RTDECL(int) RTZipTarFsStreamToIoStream(RTVFSIOSTREAM hVfsIosOut, RTZIPTARFORMAT enmFormat,
1905 uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
1906{
1907 /*
1908 * Input validation.
1909 */
1910 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
1911 *phVfsFss = NIL_RTVFSFSSTREAM;
1912 AssertPtrReturn(hVfsIosOut, VERR_INVALID_HANDLE);
1913 AssertReturn(enmFormat > RTZIPTARFORMAT_INVALID && enmFormat < RTZIPTARFORMAT_END, VERR_INVALID_PARAMETER);
1914 AssertReturn(!(fFlags & ~RTZIPTAR_C_VALID_MASK), VERR_INVALID_FLAGS);
1915
1916 if (enmFormat == RTZIPTARFORMAT_DEFAULT)
1917 enmFormat = RTZIPTARFORMAT_GNU;
1918 AssertReturn( enmFormat == RTZIPTARFORMAT_GNU
1919 || enmFormat == RTZIPTARFORMAT_USTAR
1920 , VERR_NOT_IMPLEMENTED); /* Only implementing GNU and USTAR output at the moment. */
1921
1922 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosOut);
1923 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1924
1925 /*
1926 * Retain the input stream and create a new filesystem stream handle.
1927 */
1928 PRTZIPTARFSSTREAMWRITER pThis;
1929 RTVFSFSSTREAM hVfsFss;
1930 int rc = RTVfsNewFsStream(&rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, false /*fReadOnly*/,
1931 &hVfsFss, (void **)&pThis);
1932 if (RT_SUCCESS(rc))
1933 {
1934 pThis->hVfsIos = hVfsIosOut;
1935 pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIosOut);
1936
1937 pThis->enmFormat = enmFormat;
1938 pThis->fFlags = fFlags;
1939 pThis->rcFatal = VINF_SUCCESS;
1940
1941 *phVfsFss = hVfsFss;
1942 return VINF_SUCCESS;
1943 }
1944
1945 RTVfsIoStrmRelease(hVfsIosOut);
1946 return rc;
1947}
1948
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