VirtualBox

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

Last change on this file since 83743 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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