VirtualBox

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

最後變更 在這個檔案從67253是 67253,由 vboxsync 提交於 8 年 前

IPRT/tarvfswriter.cpp: Added RTZipTarFsStreamSetOwner, RTZipTarFsStreamSetGroup, RTZipTarFsStreamSetPrefix, RTZipTarFsStreamSetFileMode, RTZipTarFsStreamSetDirMode, and RTZipTarFsStreamSetModTime for overriding attributes in the output archive.

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

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