VirtualBox

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

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