VirtualBox

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

Last change on this file since 84519 was 84509, checked in by vboxsync, 4 years ago

iprt/cdefs.h,*: Introducing RT_FLEXIBLE_ARRAY_EXTENSION as a g++ hack that allows us to use RT_FLEXIBLE_ARRAY without the compiler going all pendantic on us. Only tested with 10.1.0. bugref:9746

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