VirtualBox

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

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

DnD: Lots of documentation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1/* $Id: DnDDroppedFiles.cpp 85681 2020-08-11 09:36:37Z 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
42/**
43 * Initializes a DnD Dropped Files struct, internal version.
44 *
45 * @returns VBox status code.
46 * @param pDF DnD Dropped Files to initialize.
47 */
48static int dndDroppedFilesInitInternal(PDNDDROPPEDFILES pDF)
49{
50 pDF->m_fOpen = 0;
51 pDF->m_hDir = NIL_RTDIR;
52 pDF->pszPathAbs = NULL;
53
54 RTListInit(&pDF->m_lstDirs);
55 RTListInit(&pDF->m_lstFiles);
56
57 return VINF_SUCCESS;
58}
59
60/**
61 * Initializes a DnD Dropped Files struct, extended version.
62 *
63 * @returns VBox status code.
64 * @param pDF DnD Dropped Files to initialize.
65 * @param fFlags Dropped Files flags to use for initialization.
66 */
67int DnDDroppedFilesInitEx(PDNDDROPPEDFILES pDF,
68 const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
69{
70 int rc = dndDroppedFilesInitInternal(pDF);
71 if (RT_FAILURE(rc))
72 return rc;
73
74 return DnDDroppedFilesOpenEx(pDF, pszPath, fFlags);
75}
76
77/**
78 * Initializes a DnD Dropped Files struct.
79 *
80 * @returns VBox status code.
81 * @param pDF DnD Dropped Files to initialize.
82 */
83int DnDDroppedFilesInit(PDNDDROPPEDFILES pDF)
84{
85 return dndDroppedFilesInitInternal(pDF);
86}
87
88/**
89 * Destroys a DnD Dropped Files struct.
90 *
91 * Note: This does *not* (physically) delete any added content.
92 * Make sure to call DnDDroppedFilesReset() then.
93 *
94 * @param pDF DnD Dropped Files to destroy.
95 */
96void DnDDroppedFilesDestroy(PDNDDROPPEDFILES pDF)
97{
98 /* Only make sure to not leak any handles and stuff, don't delete any
99 * directories / files here. */
100 dndDroppedFilesCloseInternal(pDF);
101
102 RTStrFree(pDF->pszPathAbs);
103 pDF->pszPathAbs = NULL;
104}
105
106/**
107 * Adds a file reference to a Dropped Files directory.
108 *
109 * @returns VBox status code.
110 * @param pDF DnD Dropped Files to add file to.
111 * @param pszFile Path of file entry to add.
112 */
113int DnDDroppedFilesAddFile(PDNDDROPPEDFILES pDF, const char *pszFile)
114{
115 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
116
117 PDNDDROPPEDFILESENTRY pEntry = (PDNDDROPPEDFILESENTRY)RTMemAlloc(sizeof(DNDDROPPEDFILESENTRY));
118 if (!pEntry)
119 return VERR_NO_MEMORY;
120
121 pEntry->pszPath = RTStrDup(pszFile);
122 if (pEntry->pszPath)
123 {
124 RTListAppend(&pDF->m_lstFiles, &pEntry->Node);
125 return VINF_SUCCESS;
126 }
127
128 RTMemFree(pEntry);
129 return VERR_NO_MEMORY;
130}
131
132/**
133 * Adds a directory reference to a Dropped Files directory.
134 *
135 * Note: This does *not* (recursively) add sub entries.
136 *
137 * @returns VBox status code.
138 * @param pDF DnD Dropped Files to add directory to.
139 * @param pszDir Path of directory entry to add.
140 */
141int DnDDroppedFilesAddDir(PDNDDROPPEDFILES pDF, const char *pszDir)
142{
143 AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
144
145 PDNDDROPPEDFILESENTRY pEntry = (PDNDDROPPEDFILESENTRY)RTMemAlloc(sizeof(DNDDROPPEDFILESENTRY));
146 if (!pEntry)
147 return VERR_NO_MEMORY;
148
149 pEntry->pszPath = RTStrDup(pszDir);
150 if (pEntry->pszPath)
151 {
152 RTListAppend(&pDF->m_lstDirs, &pEntry->Node);
153 return VINF_SUCCESS;
154 }
155
156 RTMemFree(pEntry);
157 return VERR_NO_MEMORY;
158}
159
160/**
161 * Closes the dropped files directory handle, internal version.
162 *
163 * @returns VBox status code.
164 * @param pDF DnD Dropped Files to close.
165 */
166static int dndDroppedFilesCloseInternal(PDNDDROPPEDFILES pDF)
167{
168 int rc;
169 if (pDF->m_hDir != NULL)
170 {
171 rc = RTDirClose(pDF->m_hDir);
172 if (RT_SUCCESS(rc))
173 pDF->m_hDir = NULL;
174 }
175 else
176 rc = VINF_SUCCESS;
177
178 LogFlowFuncLeaveRC(rc);
179 return rc;
180}
181
182/**
183 * Closes the dropped files directory handle.
184 *
185 * @returns VBox status code.
186 * @param pDF DnD Dropped Files to close.
187 */
188int DnDDroppedFilesClose(PDNDDROPPEDFILES pDF)
189{
190 return dndDroppedFilesCloseInternal(pDF);
191}
192
193/**
194 * Returns the absolute path of the dropped files directory.
195 *
196 * @returns Pointer to absolute path of the dropped files directory.
197 * @param pDF DnD Dropped Files return absolute path of the dropped files directory for.
198 */
199const char *DnDDroppedFilesGetDirAbs(PDNDDROPPEDFILES pDF)
200{
201 return pDF->pszPathAbs;
202}
203
204/**
205 * Returns whether the dropped files directory has been opened or not.
206 *
207 * @returns \c true if open, \c false if not.
208 * @param pDF DnD Dropped Files to return open status for.
209 */
210bool DnDDroppedFilesIsOpen(PDNDDROPPEDFILES pDF)
211{
212 return (pDF->m_hDir != NULL);
213}
214
215/**
216 * Opens (creates) the dropped files directory.
217 *
218 * @returns VBox status code.
219 * @param pDF DnD Dropped Files to open.
220 * @param pszPath Absolute path where to create the dropped files directory.
221 * @param fFlags Dropped files flags to use for this directory.
222 */
223int DnDDroppedFilesOpenEx(PDNDDROPPEDFILES pDF,
224 const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
225{
226 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
227 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /* Flags not supported yet. */
228
229 int rc;
230
231 do
232 {
233 char szDropDir[RTPATH_MAX];
234 RTStrPrintf(szDropDir, sizeof(szDropDir), "%s", pszPath);
235
236 /** @todo On Windows we also could use the registry to override
237 * this path, on Posix a dotfile and/or a guest property
238 * can be used. */
239
240 /* Append our base drop directory. */
241 rc = RTPathAppend(szDropDir, sizeof(szDropDir), "VirtualBox Dropped Files"); /** @todo Make this tag configurable? */
242 if (RT_FAILURE(rc))
243 break;
244
245 /* Create it when necessary. */
246 if (!RTDirExists(szDropDir))
247 {
248 rc = RTDirCreateFullPath(szDropDir, RTFS_UNIX_IRWXU);
249 if (RT_FAILURE(rc))
250 break;
251 }
252
253 /* The actually drop directory consist of the current time stamp and a
254 * unique number when necessary. */
255 char szTime[64];
256 RTTIMESPEC time;
257 if (!RTTimeSpecToString(RTTimeNow(&time), szTime, sizeof(szTime)))
258 {
259 rc = VERR_BUFFER_OVERFLOW;
260 break;
261 }
262
263 rc = DnDPathSanitizeFileName(szTime, sizeof(szTime));
264 if (RT_FAILURE(rc))
265 break;
266
267 rc = RTPathAppend(szDropDir, sizeof(szDropDir), szTime);
268 if (RT_FAILURE(rc))
269 break;
270
271 /* Create it (only accessible by the current user) */
272 rc = RTDirCreateUniqueNumbered(szDropDir, sizeof(szDropDir), RTFS_UNIX_IRWXU, 3, '-');
273 if (RT_SUCCESS(rc))
274 {
275 RTDIR hDir;
276 rc = RTDirOpen(&hDir, szDropDir);
277 if (RT_SUCCESS(rc))
278 {
279 pDF->pszPathAbs = RTStrDup(szDropDir);
280 AssertPtrBreakStmt(pDF->pszPathAbs, rc = VERR_NO_MEMORY);
281 pDF->m_hDir = hDir;
282 pDF->m_fOpen = fFlags;
283 }
284 }
285
286 } while (0);
287
288 LogFlowFuncLeaveRC(rc);
289 return rc;
290}
291
292/**
293 * Opens (creates) the dropped files directory in the system's temp directory.
294 *
295 * @returns VBox status code.
296 * @param pDF DnD Dropped Files to open.
297 * @param fFlags Dropped files flags to use for this directory.
298 */
299int DnDDroppedFilesOpenTemp(PDNDDROPPEDFILES pDF, DNDURIDROPPEDFILEFLAGS fFlags)
300{
301 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /* Flags not supported yet. */
302
303 /*
304 * Get the user's temp directory. Don't use the user's root directory (or
305 * something inside it) because we don't know for how long/if the data will
306 * be kept after the guest OS used it.
307 */
308 char szTemp[RTPATH_MAX];
309 int rc = RTPathTemp(szTemp, sizeof(szTemp));
310 if (RT_SUCCESS(rc))
311 rc = DnDDroppedFilesOpenEx(pDF, szTemp, fFlags);
312
313 return rc;
314}
315
316/**
317 * Free's an internal DnD Dropped Files entry.
318 *
319 * @param pEntry Pointer to entry to free. The pointer will be invalid after calling.
320 */
321static void dndDroppedFilesEntryFree(PDNDDROPPEDFILESENTRY pEntry)
322{
323 if (!pEntry)
324 return;
325 RTStrFree(pEntry->pszPath);
326 RTListNodeRemove(&pEntry->Node);
327 RTMemFree(pEntry);
328}
329
330/**
331 * Resets an internal DnD Dropped Files list.
332 *
333 * @param pListAnchor Pointer to list (anchor) to reset.
334 */
335static void dndDroppedFilesResetList(PRTLISTANCHOR pListAnchor)
336{
337 PDNDDROPPEDFILESENTRY pEntryCur, pEntryNext;
338 RTListForEachSafe(pListAnchor, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
339 dndDroppedFilesEntryFree(pEntryCur);
340 Assert(RTListIsEmpty(pListAnchor));
341}
342
343/**
344 * Resets a droppped files directory.
345 *
346 * @returns VBox status code.
347 * @param pDF DnD Dropped Files to reset.
348 * @param fDelete Whether to physically delete the directory and its content
349 * or just clear the internal references.
350 */
351int DnDDroppedFilesReset(PDNDDROPPEDFILES pDF, bool fDelete)
352{
353 int rc = dndDroppedFilesCloseInternal(pDF);
354 if (RT_SUCCESS(rc))
355 {
356 if (fDelete)
357 {
358 rc = DnDDroppedFilesRollback(pDF);
359 }
360 else
361 {
362 dndDroppedFilesResetList(&pDF->m_lstDirs);
363 dndDroppedFilesResetList(&pDF->m_lstFiles);
364 }
365 }
366
367 LogFlowFuncLeaveRC(rc);
368 return rc;
369}
370
371/**
372 * Re-opens a droppes files directory.
373 *
374 * @returns VBox status code, or VERR_NOT_FOUND if the dropped files directory has not been opened before.
375 * @param pDF DnD Dropped Files to re-open.
376 */
377int DnDDroppedFilesReopen(PDNDDROPPEDFILES pDF)
378{
379 if (!pDF->pszPathAbs)
380 return VERR_NOT_FOUND;
381
382 return DnDDroppedFilesOpenEx(pDF, pDF->pszPathAbs, pDF->m_fOpen);
383}
384
385/**
386 * Performs a rollback of a dropped files directory.
387 * This cleans the directory by physically deleting all files / directories which have been added before.
388 *
389 * @returns VBox status code.
390 * @param pDF DnD Dropped Files to roll back.
391 */
392int DnDDroppedFilesRollback(PDNDDROPPEDFILES pDF)
393{
394 if (!pDF->pszPathAbs)
395 return VINF_SUCCESS;
396
397 int rc = VINF_SUCCESS;
398
399 /* Rollback by removing any stuff created.
400 * Note: Only remove empty directories, never ever delete
401 * anything recursive here! Steam (tm) knows best ... :-) */
402 int rc2;
403 PDNDDROPPEDFILESENTRY pEntryCur, pEntryNext;
404 RTListForEachSafe(&pDF->m_lstFiles, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
405 {
406 rc2 = RTFileDelete(pEntryCur->pszPath);
407 if (RT_SUCCESS(rc2))
408 dndDroppedFilesEntryFree(pEntryCur);
409 else if (RT_SUCCESS(rc))
410 rc = rc2;
411 /* Keep going. */
412 }
413
414 RTListForEachSafe(&pDF->m_lstDirs, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
415 {
416 rc2 = RTDirRemove(pEntryCur->pszPath);
417 if (RT_SUCCESS(rc2))
418 dndDroppedFilesEntryFree(pEntryCur);
419 else if (RT_SUCCESS(rc))
420 rc = rc2;
421 /* Keep going. */
422 }
423
424 if (RT_SUCCESS(rc))
425 {
426 rc2 = dndDroppedFilesCloseInternal(pDF);
427 if (RT_SUCCESS(rc2))
428 {
429 /* Try to remove the empty root dropped files directory as well.
430 * Might return VERR_DIR_NOT_EMPTY or similar. */
431 rc2 = RTDirRemove(pDF->pszPathAbs);
432 }
433 if (RT_SUCCESS(rc))
434 rc = rc2;
435 }
436
437 LogFlowFuncLeaveRC(rc);
438 return rc;
439}
440
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