VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/vfs/vfsfss2dir.cpp@ 106579

Last change on this file since 106579 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: vfsfss2dir.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, FS write stream dumping in a normal directory.
4 *
5 * This is just a simple mechanism to provide a drop in for the TAR creator
6 * that writes files individually to the disk instead of a TAR archive. It
7 * has an additional feature for removing the files to help bail out on error.
8 */
9
10/*
11 * Copyright (C) 2010-2024 Oracle and/or its affiliates.
12 *
13 * This file is part of VirtualBox base platform packages, as
14 * available from https://www.virtualbox.org.
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation, in version 3 of the
19 * License.
20 *
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, see <https://www.gnu.org/licenses>.
28 *
29 * The contents of this file may alternatively be used under the terms
30 * of the Common Development and Distribution License Version 1.0
31 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
32 * in the VirtualBox distribution, in which case the provisions of the
33 * CDDL are applicable instead of those of the GPL.
34 *
35 * You may elect to license modified versions of this file under the
36 * terms and conditions of either the GPL or the CDDL or both.
37 *
38 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
39 */
40
41
42/*********************************************************************************************************************************
43* Header Files *
44*********************************************************************************************************************************/
45/// @todo #define RTVFSFSS2DIR_USE_DIR
46#include "internal/iprt.h"
47#include <iprt/vfs.h>
48
49#include <iprt/assert.h>
50#include <iprt/err.h>
51#include <iprt/file.h>
52#include <iprt/mem.h>
53#ifndef RTVFSFSS2DIR_USE_DIR
54# include <iprt/path.h>
55#endif
56#include <iprt/string.h>
57#include <iprt/vfslowlevel.h>
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63/**
64 * Undo entry for RTVFSFSSWRITE2DIR.
65 */
66typedef struct RTVFSFSSWRITE2DIRENTRY
67{
68 /** The list entry (head is RTVFSFSSWRITE2DIR::Entries). */
69 RTLISTNODE Entry;
70 /** The file mode mask. */
71 RTFMODE fMode;
72#ifdef RTVFSFSS2DIR_USE_DIR
73 /** The name (relative to RTVFSFSSWRITE2DIR::hVfsBaseDir). */
74#else
75 /** The name (relative to RTVFSFSSWRITE2DIR::szBaseDir). */
76#endif
77 RT_FLEXIBLE_ARRAY_EXTENSION
78 char szName[RT_FLEXIBLE_ARRAY];
79} RTVFSFSSWRITE2DIRENTRY;
80/** Pointer to a RTVFSFSSWRITE2DIR undo entry. */
81typedef RTVFSFSSWRITE2DIRENTRY *PRTVFSFSSWRITE2DIRENTRY;
82
83/**
84 * FSS write to directory instance.
85 */
86typedef struct RTVFSFSSWRITE2DIR
87{
88 /** Flags (RTVFSFSS2DIR_F_XXX). */
89 uint32_t fFlags;
90 /** Number of files and stuff we've created. */
91 uint32_t cEntries;
92 /** Files and stuff we've created (RTVFSFSSWRITE2DIRENTRY).
93 * This is used for reverting changes on failure. */
94 RTLISTANCHOR Entries;
95#ifdef RTVFSFSS2DIR_USE_DIR
96 /** The handle of the base directory. */
97 RTVFSDIR hVfsBaseDir;
98#else
99 /** Path to the directory that all operations are relative to. */
100 RT_FLEXIBLE_ARRAY_EXTENSION
101 char szBaseDir[RT_FLEXIBLE_ARRAY];
102#endif
103} RTVFSFSSWRITE2DIR;
104/** Pointer to a write-to-directory FSS instance. */
105typedef RTVFSFSSWRITE2DIR *PRTVFSFSSWRITE2DIR;
106
107
108/*********************************************************************************************************************************
109* Internal Functions *
110*********************************************************************************************************************************/
111static DECLCALLBACK(int) rtVfsFssToDir_PushFile(void *pvThis, const char *pszPath, uint64_t cbFile, PCRTFSOBJINFO paObjInfo,
112 uint32_t cObjInfo, uint32_t fFlags, PRTVFSIOSTREAM phVfsIos);
113
114
115/**
116 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
117 */
118static DECLCALLBACK(int) rtVfsFssToDir_Close(void *pvThis)
119{
120 PRTVFSFSSWRITE2DIR pThis = (PRTVFSFSSWRITE2DIR)pvThis;
121
122#ifdef RTVFSFSS2DIR_USE_DIR
123 RTVfsDirRelease(pThis->hVfsBaseDir);
124 pThis->hVfsBaseDir = NIL_RTVFSDIR;
125#endif
126
127 PRTVFSFSSWRITE2DIRENTRY pCur;
128 PRTVFSFSSWRITE2DIRENTRY pNext;
129 RTListForEachSafe(&pThis->Entries, pCur, pNext, RTVFSFSSWRITE2DIRENTRY, Entry)
130 {
131 RTMemFree(pCur);
132 }
133 return VINF_SUCCESS;
134}
135
136
137/**
138 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
139 */
140static DECLCALLBACK(int) rtVfsFssToDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
141{
142 RT_NOREF(pvThis);
143
144 /* no info here, sorry. */
145 RT_ZERO(*pObjInfo);
146 pObjInfo->Attr.enmAdditional = enmAddAttr;
147
148 return VINF_SUCCESS;
149}
150
151
152/**
153 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnAdd}
154 */
155static DECLCALLBACK(int) rtVfsFssToDir_Add(void *pvThis, const char *pszPath, RTVFSOBJ hVfsObj, uint32_t fFlags)
156{
157 PRTVFSFSSWRITE2DIR pThis = (PRTVFSFSSWRITE2DIR)pvThis;
158 RT_NOREF(fFlags);
159
160 /*
161 * Query information about the object.
162 */
163 RTFSOBJINFO ObjInfo;
164 int rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX);
165 AssertRCReturn(rc, rc);
166
167 /*
168 * Deal with files.
169 */
170 if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
171 {
172 RTVFSIOSTREAM hVfsIosSrc = RTVfsObjToIoStream(hVfsObj);
173 AssertReturn(hVfsIosSrc != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE);
174
175 RTVFSIOSTREAM hVfsIosDst;
176 rc = rtVfsFssToDir_PushFile(pvThis, pszPath, ObjInfo.cbObject, &ObjInfo, 1, 0 /*fFlags*/, &hVfsIosDst);
177 if (RT_SUCCESS(rc))
178 {
179 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (size_t)RT_ALIGN(ObjInfo.cbObject, _4K));
180 RTVfsIoStrmRelease(hVfsIosDst);
181 }
182 RTVfsIoStrmRelease(hVfsIosSrc);
183 }
184 /*
185 * Symbolic links.
186 */
187 else if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
188 {
189 RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
190 AssertReturn(hVfsSymlink != NIL_RTVFSSYMLINK, VERR_WRONG_TYPE);
191
192 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
193 RT_NOREF(pThis);
194
195 RTVfsSymlinkRelease(hVfsSymlink);
196 }
197 /*
198 * Directories.
199 */
200 else if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
201 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
202 /*
203 * And whatever else we need when we need it...
204 */
205 else
206 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
207
208 return rc;
209}
210
211
212/**
213 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnPushFile}
214 */
215static DECLCALLBACK(int) rtVfsFssToDir_PushFile(void *pvThis, const char *pszPath, uint64_t cbFile, PCRTFSOBJINFO paObjInfo,
216 uint32_t cObjInfo, uint32_t fFlags, PRTVFSIOSTREAM phVfsIos)
217{
218 PRTVFSFSSWRITE2DIR pThis = (PRTVFSFSSWRITE2DIR)pvThis;
219 RT_NOREF(cbFile, fFlags);
220 int rc;
221
222#ifndef RTVFSFSS2DIR_USE_DIR
223 /*
224 * Join up the path with the base dir and make sure it fits.
225 */
226 char szFullPath[RTPATH_MAX];
227 rc = RTPathJoin(szFullPath, sizeof(szFullPath), pThis->szBaseDir, pszPath);
228 if (RT_SUCCESS(rc))
229 {
230#endif
231 /*
232 * Create an undo entry for it.
233 */
234 size_t const cbRelativePath = strlen(pszPath);
235 PRTVFSFSSWRITE2DIRENTRY pEntry;
236 pEntry = (PRTVFSFSSWRITE2DIRENTRY)RTMemAllocVar(RT_UOFFSETOF_DYN(RTVFSFSSWRITE2DIRENTRY, szName[cbRelativePath]));
237 if (pEntry)
238 {
239 if (cObjInfo)
240 pEntry->fMode = (paObjInfo[0].Attr.fMode & ~RTFS_TYPE_MASK) | RTFS_TYPE_FILE;
241 else
242 pEntry->fMode = RTFS_TYPE_FILE | 0664;
243 memcpy(pEntry->szName, pszPath, cbRelativePath);
244
245 /*
246 * Create the file.
247 */
248 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
249 fOpen |= ((pEntry->fMode & RTFS_UNIX_ALL_ACCESS_PERMS) << RTFILE_O_CREATE_MODE_SHIFT);
250 if (!(pThis->fFlags & RTVFSFSS2DIR_F_OVERWRITE_FILES))
251 fOpen |= RTFILE_O_CREATE;
252 else
253 fOpen |= RTFILE_O_CREATE_REPLACE;
254#ifdef RTVFSFSS2DIR_USE_DIR
255 rc = RTVfsDirOpenFileAsIoStream(pThis->hVfsBaseDir, pszPath, fOpen, phVfsIos);
256#else
257 rc = RTVfsIoStrmOpenNormal(szFullPath, fOpen, phVfsIos);
258#endif
259 if (RT_SUCCESS(rc))
260 RTListAppend(&pThis->Entries, &pEntry->Entry);
261 else
262 RTMemFree(pEntry);
263 }
264 else
265 rc = VERR_NO_MEMORY;
266#ifndef RTVFSFSS2DIR_USE_DIR
267 }
268 else if (rc == VERR_BUFFER_OVERFLOW)
269 rc = VERR_FILENAME_TOO_LONG;
270#endif
271 return rc;
272}
273
274
275/**
276 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnEnd}
277 */
278static DECLCALLBACK(int) rtVfsFssToDir_End(void *pvThis)
279{
280 RT_NOREF(pvThis);
281 return VINF_SUCCESS;
282}
283
284
285/**
286 * The write-to-directory FSS operations.
287 */
288static const RTVFSFSSTREAMOPS g_rtVfsFssToDirOps =
289{
290 { /* Obj */
291 RTVFSOBJOPS_VERSION,
292 RTVFSOBJTYPE_FS_STREAM,
293 "TarFsStreamWriter",
294 rtVfsFssToDir_Close,
295 rtVfsFssToDir_QueryInfo,
296 NULL,
297 RTVFSOBJOPS_VERSION
298 },
299 RTVFSFSSTREAMOPS_VERSION,
300 0,
301 NULL,
302 rtVfsFssToDir_Add,
303 rtVfsFssToDir_PushFile,
304 rtVfsFssToDir_End,
305 RTVFSFSSTREAMOPS_VERSION
306};
307
308
309#ifdef RTVFSFSS2DIR_USE_DIR
310RTDECL(int) RTVfsFsStrmToDir(RTVFSDIR hVfsBaseDir, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
311{
312 /*
313 * Input validation.
314 */
315 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
316 *phVfsFss = NIL_RTVFSFSSTREAM;
317 AssertReturn(!(fFlags & ~RTVFSFSS2DIR_F_VALID_MASK), VERR_INVALID_FLAGS);
318 uint32_t cRefs = RTVfsDirRetain(hVfsBaseDir);
319 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
320
321 /*
322 * Create the file system stream handle and init our data.
323 */
324 PRTVFSFSSWRITE2DIR pThis;
325 RTVFSFSSTREAM hVfsFss;
326 int rc = RTVfsNewFsStream(&g_rtVfsFssToDirOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, RTFILE_O_WRITE,
327 &hVfsFss, (void **)&pThis);
328 if (RT_SUCCESS(rc))
329 {
330 pThis->fFlags = fFlags;
331 pThis->cEntries = 0;
332 pThis->hVfsBaseDir = hVfsBaseDir;
333 RTListInit(&pThis->Entries);
334
335 *phVfsFss = hVfsFss;
336 return VINF_SUCCESS;
337 }
338 RTVfsDirRelease(hVfsBaseDir);
339
340}
341#endif /* RTVFSFSS2DIR_USE_DIR */
342
343
344RTDECL(int) RTVfsFsStrmToNormalDir(const char *pszBaseDir, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
345{
346#ifdef RTVFSFSS2DIR_USE_DIR
347 RTVFSDIR hVfsBaseDir;
348 int rc = RTVfsDirOpenNormal(pszBaseDir, 0 /*fFlags*/, &hVfsBaseDir);
349 if (RT_SUCCESS(rc))
350 {
351 rc = RTVfsFsStrmToDir(hVfsBaseDir, fFlags, phVfsFss);
352 RTVfsDirRelease(hVfsBaseDir);
353 }
354#else
355
356 /*
357 * Input validation.
358 */
359 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
360 *phVfsFss = NIL_RTVFSFSSTREAM;
361 AssertReturn(!(fFlags & ~RTVFSFSS2DIR_F_VALID_MASK), VERR_INVALID_FLAGS);
362 AssertPtrReturn(pszBaseDir, VERR_INVALID_POINTER);
363 AssertReturn(*pszBaseDir != '\0', VERR_INVALID_NAME);
364
365 /*
366 * Straighten the path and make sure it's an existing directory.
367 */
368 char szAbsPath[RTPATH_MAX];
369 int rc = RTPathAbs(pszBaseDir, szAbsPath, sizeof(szAbsPath));
370 if (RT_SUCCESS(rc))
371 {
372 RTFSOBJINFO ObjInfo;
373 rc = RTPathQueryInfo(szAbsPath, &ObjInfo, RTFSOBJATTRADD_NOTHING);
374 if (RT_SUCCESS(rc))
375 {
376 if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
377 {
378 /*
379 * Create the file system stream handle and init our data.
380 */
381 size_t const cbBaseDir = strlen(szAbsPath) + 1;
382 PRTVFSFSSWRITE2DIR pThis;
383 RTVFSFSSTREAM hVfsFss;
384 rc = RTVfsNewFsStream(&g_rtVfsFssToDirOps, RT_UOFFSETOF_DYN(RTVFSFSSWRITE2DIR, szBaseDir[cbBaseDir]),
385 NIL_RTVFS, NIL_RTVFSLOCK, RTFILE_O_WRITE, &hVfsFss, (void **)&pThis);
386 if (RT_SUCCESS(rc))
387 {
388 pThis->fFlags = fFlags;
389 pThis->cEntries = 0;
390 RTListInit(&pThis->Entries);
391 memcpy(pThis->szBaseDir, szAbsPath, cbBaseDir);
392
393 *phVfsFss = hVfsFss;
394 return VINF_SUCCESS;
395 }
396 }
397 else
398 rc = VERR_NOT_A_DIRECTORY;
399 }
400 }
401#endif
402 return rc;
403}
404
405
406RTDECL(int) RTVfsFsStrmToDirUndo(RTVFSFSSTREAM hVfsFss)
407{
408 /*
409 * Validate input.
410 */
411 PRTVFSFSSWRITE2DIR pThis = (PRTVFSFSSWRITE2DIR)RTVfsFsStreamToPrivate(hVfsFss, &g_rtVfsFssToDirOps);
412 AssertReturn(pThis, VERR_WRONG_TYPE);
413
414 /*
415 * Do the job, in reverse order. Dropping stuff we
416 * successfully remove from the list.
417 */
418 int rc = VINF_SUCCESS;
419 PRTVFSFSSWRITE2DIRENTRY pCur;
420 PRTVFSFSSWRITE2DIRENTRY pPrev;
421 RTListForEachReverseSafe(&pThis->Entries, pCur, pPrev, RTVFSFSSWRITE2DIRENTRY, Entry)
422 {
423#ifdef RTVFSFSS2DIR_USE_DIR
424 int rc2 = RTVfsDirUnlinkEntry(pThis->hVfsBaseDir, pCur->szName);
425#else
426 char szFullPath[RTPATH_MAX];
427 int rc2 = RTPathJoin(szFullPath, sizeof(szFullPath), pThis->szBaseDir, pCur->szName);
428 AssertRC(rc2);
429 if (RT_SUCCESS(rc2))
430 rc2 = RTPathUnlink(szFullPath, 0 /*fUnlink*/);
431#endif
432 if ( RT_SUCCESS(rc2)
433 || rc2 == VERR_PATH_NOT_FOUND
434 || rc2 == VERR_FILE_NOT_FOUND
435 || rc2 == VERR_NOT_FOUND)
436 {
437 RTListNodeRemove(&pCur->Entry);
438 RTMemFree(pCur);
439 }
440 else if (RT_SUCCESS(rc))
441 rc = rc2;
442 }
443 return rc;
444}
445
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