VirtualBox

source: vbox/trunk/src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp@ 85371

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

DnD: Revamped code to simplify / untangle of internal data handling:

  • C-ifying and renaming classes DnDURIList / DnDURIObject -> DnDTransferList / DnDTransferObject
  • Added testcases for DnDTransferList / DnDTransferObject + DnDPath API
  • Reduced memory footprint
  • Greatly simplified / stripped down internal data flow of Main side
  • More (optional) release logging for further diagnosis

Work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: DnDDroppedFiles.cpp 85371 2020-07-17 10:02:58Z vboxsync $ */
2/** @file
3 * DnD - Directory handling.
4 */
5
6/*
7 * Copyright (C) 2014-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_GUEST_DND
23#include <VBox/GuestHost/DragAndDrop.h>
24
25#include <iprt/assert.h>
26#include <iprt/dir.h>
27#include <iprt/err.h>
28#include <iprt/file.h>
29#include <iprt/mem.h>
30#include <iprt/path.h>
31#include <iprt/string.h>
32
33#include <VBox/log.h>
34
35
36/*********************************************************************************************************************************
37* Prototypes *
38*********************************************************************************************************************************/
39static int dndDroppedFilesCloseInternal(PDNDDROPPEDFILES pDF);
40
41
42int DnDDroppedFilesInit(PDNDDROPPEDFILES pDF,
43 const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
44{
45 pDF->m_fOpen = 0;
46 pDF->m_hDir = NIL_RTDIR;
47
48 return DnDDroppedFilesOpenEx(pDF, pszPath, fFlags);
49}
50
51void DnDDroppedFilesDestroy(PDNDDROPPEDFILES pDF)
52{
53 /* Only make sure to not leak any handles and stuff, don't delete any
54 * directories / files here. */
55 dndDroppedFilesCloseInternal(pDF);
56}
57
58/**
59 * Adds a file reference to a dropped files directory.
60 *
61 * @returns VBox status code.
62 * @param pszFile Path of file entry to add.
63 */
64int DnDDroppedFilesAddFile(PDNDDROPPEDFILES pDF, const char *pszFile)
65{
66 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
67
68 PDNDDROPPEDFILESENTRY pEntry = (PDNDDROPPEDFILESENTRY)RTMemAlloc(sizeof(DNDDROPPEDFILESENTRY));
69 if (!pEntry)
70 return VERR_NO_MEMORY;
71
72 pEntry->pszPath = RTStrDup(pszFile);
73 if (pEntry->pszPath)
74 {
75 RTListAppend(&pDF->m_lstFiles, &pEntry->Node);
76 return VINF_SUCCESS;
77 }
78
79 RTMemFree(pEntry);
80 return VERR_NO_MEMORY;
81}
82
83/**
84 * Adds a directory reference to a dropped files directory.
85 * Note: This does *not* (recursively) add sub entries.
86 *
87 * @returns VBox status code.
88 * @param pszDir Path of directory entry to add.
89 */
90int DnDDroppedFilesAddDir(PDNDDROPPEDFILES pDF, const char *pszDir)
91{
92 AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
93
94 PDNDDROPPEDFILESENTRY pEntry = (PDNDDROPPEDFILESENTRY)RTMemAlloc(sizeof(DNDDROPPEDFILESENTRY));
95 if (!pEntry)
96 return VERR_NO_MEMORY;
97
98 pEntry->pszPath = RTStrDup(pszDir);
99 if (pEntry->pszPath)
100 {
101 RTListAppend(&pDF->m_lstDirs, &pEntry->Node);
102 return VINF_SUCCESS;
103 }
104
105 RTMemFree(pEntry);
106 return VERR_NO_MEMORY;
107}
108
109/**
110 * Closes the dropped files directory handle, internal version.
111 *
112 * @returns VBox status code.
113 */
114static int dndDroppedFilesCloseInternal(PDNDDROPPEDFILES pDF)
115{
116 int rc;
117 if (pDF->m_hDir != NULL)
118 {
119 rc = RTDirClose(pDF->m_hDir);
120 if (RT_SUCCESS(rc))
121 pDF->m_hDir = NULL;
122 }
123 else
124 rc = VINF_SUCCESS;
125
126 LogFlowFuncLeaveRC(rc);
127 return rc;
128}
129
130/**
131 * Closes the dropped files directory handle.
132 *
133 * @returns VBox status code.
134 */
135int DnDDroppedFilesClose(PDNDDROPPEDFILES pDF)
136{
137 return dndDroppedFilesCloseInternal(pDF);
138}
139
140/**
141 * Returns the absolute path of the dropped files directory.
142 *
143 * @returns Pointer to absolute path of the dropped files directory.
144 */
145const char *DnDDroppedFilesGetDirAbs(PDNDDROPPEDFILES pDF)
146{
147 return pDF->pszPathAbs;
148}
149
150/**
151 * Returns whether the dropped files directory has been opened or not.
152 *
153 * @returns \c true if open, \c false if not.
154 */
155bool DnDDroppedFilesIsOpen(PDNDDROPPEDFILES pDF)
156{
157 return (pDF->m_hDir != NULL);
158}
159
160/**
161 * Opens (creates) the dropped files directory.
162 *
163 * @returns VBox status code.
164 * @param pszPath Absolute path where to create the dropped files directory.
165 * @param fFlags Dropped files flags to use for this directory.
166 */
167int DnDDroppedFilesOpenEx(PDNDDROPPEDFILES pDF,
168 const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
169{
170 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
171 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /* Flags not supported yet. */
172
173 int rc;
174
175 do
176 {
177 char szDropDir[RTPATH_MAX];
178 RTStrPrintf(szDropDir, sizeof(szDropDir), "%s", pszPath);
179
180 /** @todo On Windows we also could use the registry to override
181 * this path, on Posix a dotfile and/or a guest property
182 * can be used. */
183
184 /* Append our base drop directory. */
185 rc = RTPathAppend(szDropDir, sizeof(szDropDir), "VirtualBox Dropped Files"); /** @todo Make this tag configurable? */
186 if (RT_FAILURE(rc))
187 break;
188
189 /* Create it when necessary. */
190 if (!RTDirExists(szDropDir))
191 {
192 rc = RTDirCreateFullPath(szDropDir, RTFS_UNIX_IRWXU);
193 if (RT_FAILURE(rc))
194 break;
195 }
196
197 /* The actually drop directory consist of the current time stamp and a
198 * unique number when necessary. */
199 char szTime[64];
200 RTTIMESPEC time;
201 if (!RTTimeSpecToString(RTTimeNow(&time), szTime, sizeof(szTime)))
202 {
203 rc = VERR_BUFFER_OVERFLOW;
204 break;
205 }
206
207 rc = DnDPathSanitizeFileName(szTime, sizeof(szTime));
208 if (RT_FAILURE(rc))
209 break;
210
211 rc = RTPathAppend(szDropDir, sizeof(szDropDir), szTime);
212 if (RT_FAILURE(rc))
213 break;
214
215 /* Create it (only accessible by the current user) */
216 rc = RTDirCreateUniqueNumbered(szDropDir, sizeof(szDropDir), RTFS_UNIX_IRWXU, 3, '-');
217 if (RT_SUCCESS(rc))
218 {
219 RTDIR hDir;
220 rc = RTDirOpen(&hDir, szDropDir);
221 if (RT_SUCCESS(rc))
222 {
223 pDF->m_hDir = hDir;
224 pDF->pszPathAbs = szDropDir;
225 pDF->m_fOpen = fFlags;
226 }
227 }
228
229 } while (0);
230
231 LogFlowFuncLeaveRC(rc);
232 return rc;
233}
234
235/**
236 * Opens (creates) the dropped files directory in the system's temp directory.
237 *
238 * @returns VBox status code.
239 * @param fFlags Dropped files flags to use for this directory.
240 */
241int DnDDroppedFilesOpenTemp(PDNDDROPPEDFILES pDF, DNDURIDROPPEDFILEFLAGS fFlags)
242{
243 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /* Flags not supported yet. */
244
245 /*
246 * Get the user's temp directory. Don't use the user's root directory (or
247 * something inside it) because we don't know for how long/if the data will
248 * be kept after the guest OS used it.
249 */
250 char szTemp[RTPATH_MAX];
251 int rc = RTPathTemp(szTemp, sizeof(szTemp));
252 if (RT_SUCCESS(rc))
253 rc = DnDDroppedFilesOpenEx(pDF, szTemp, fFlags);
254
255 return rc;
256}
257
258static void dndDroppedFilesEntryFree(PDNDDROPPEDFILESENTRY pEntry)
259{
260 if (!pEntry)
261 return;
262 RTStrFree(pEntry->pszPath);
263 RTListNodeRemove(&pEntry->Node);
264 RTMemFree(pEntry);
265}
266
267static void dndDroppedFilesResetList(PRTLISTANCHOR pListAnchor)
268{
269 PDNDDROPPEDFILESENTRY pEntryCur, pEntryNext;
270 RTListForEachSafe(pListAnchor, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
271 dndDroppedFilesEntryFree(pEntryCur);
272 Assert(RTListIsEmpty(pListAnchor));
273}
274
275/**
276 * Resets a droppped files directory.
277 *
278 * @returns VBox status code.
279 * @param fDelete Whether to physically delete the directory and its content
280 * or just clear the internal references.
281 */
282int DnDDroppedFilesReset(PDNDDROPPEDFILES pDF, bool fDelete)
283{
284 int rc = dndDroppedFilesCloseInternal(pDF);
285 if (RT_SUCCESS(rc))
286 {
287 if (fDelete)
288 {
289 rc = DnDDroppedFilesRollback(pDF);
290 }
291 else
292 {
293 dndDroppedFilesResetList(&pDF->m_lstDirs);
294 dndDroppedFilesResetList(&pDF->m_lstFiles);
295 }
296 }
297
298 LogFlowFuncLeaveRC(rc);
299 return rc;
300}
301
302/**
303 * Re-opens a droppes files directory.
304 *
305 * @returns VBox status code, or VERR_NOT_FOUND if the dropped files directory has not been opened before.
306 */
307int DnDDroppedFilesReopen(PDNDDROPPEDFILES pDF)
308{
309 if (!pDF->pszPathAbs)
310 return VERR_NOT_FOUND;
311
312 return DnDDroppedFilesOpenEx(pDF, pDF->pszPathAbs, pDF->m_fOpen);
313}
314
315/**
316 * Performs a rollback of a dropped files directory.
317 * This cleans the directory by physically deleting all files / directories which have been added before.
318 *
319 * @returns VBox status code.
320 */
321int DnDDroppedFilesRollback(PDNDDROPPEDFILES pDF)
322{
323 if (!pDF->pszPathAbs)
324 return VINF_SUCCESS;
325
326 int rc = VINF_SUCCESS;
327
328 /* Rollback by removing any stuff created.
329 * Note: Only remove empty directories, never ever delete
330 * anything recursive here! Steam (tm) knows best ... :-) */
331 int rc2;
332 PDNDDROPPEDFILESENTRY pEntryCur, pEntryNext;
333 RTListForEachSafe(&pDF->m_lstFiles, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
334 {
335 rc2 = RTFileDelete(pEntryCur->pszPath);
336 if (RT_SUCCESS(rc2))
337 dndDroppedFilesEntryFree(pEntryCur);
338 else if (RT_SUCCESS(rc))
339 rc = rc2;
340 /* Keep going. */
341 }
342
343 RTListForEachSafe(&pDF->m_lstDirs, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
344 {
345 rc2 = RTDirRemove(pEntryCur->pszPath);
346 if (RT_SUCCESS(rc2))
347 dndDroppedFilesEntryFree(pEntryCur);
348 else if (RT_SUCCESS(rc))
349 rc = rc2;
350 /* Keep going. */
351 }
352
353 if (RT_SUCCESS(rc))
354 {
355 rc2 = dndDroppedFilesCloseInternal(pDF);
356 if (RT_SUCCESS(rc2))
357 {
358 /* Try to remove the empty root dropped files directory as well.
359 * Might return VERR_DIR_NOT_EMPTY or similar. */
360 rc2 = RTDirRemove(pDF->pszPathAbs);
361 }
362 if (RT_SUCCESS(rc))
363 rc = rc2;
364 }
365
366 LogFlowFuncLeaveRC(rc);
367 return rc;
368}
369
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