VirtualBox

source: vbox/trunk/src/VBox/GuestHost/DragAndDrop/DnDTransferList.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: 42.9 KB
Line 
1/* $Id: DnDTransferList.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * DnD - transfer list implemenation.
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 * This implementation is taylored to keeping track of a single DnD transfer by maintaining two separate entities,
30 * namely a list of root entries and a list of (recursive file system) transfer ojects to actually transfer.
31 *
32 * The list of root entries is sent to the target (guest/host) beforehand so that the OS has a data for the
33 * actual drag'n drop operation to work with. This also contains required header data like total number of
34 * objects or total bytes being received.
35 *
36 * The list of transfer objects only is needed in order to sending data from the source to the target.
37 * Currently there is no particular ordering implemented for the transfer object list; it depends on IPRT's RTDirRead().
38 *
39 * The target must not know anything about the actual (absolute) path the root entries are coming from
40 * due to security reasons. Those root entries then can be re-based on the target to desired location there.
41 *
42 * All data handling internally is done in the so-called "transport" format, that is, non-URI (regular) paths
43 * with the "/" as path separator. From/to URI conversion is provided for convenience only.
44 */
45
46
47/*********************************************************************************************************************************
48* Header Files *
49*********************************************************************************************************************************/
50#define LOG_GROUP LOG_GROUP_GUEST_DND
51#include <VBox/GuestHost/DragAndDrop.h>
52
53#include <iprt/dir.h>
54#include <iprt/err.h>
55#include <iprt/file.h>
56#include <iprt/fs.h>
57#include <iprt/mem.h>
58#include <iprt/path.h>
59#include <iprt/string.h>
60#include <iprt/symlink.h>
61#include <iprt/uri.h>
62
63#include <VBox/log.h>
64
65
66/*********************************************************************************************************************************
67* Prototypes *
68*********************************************************************************************************************************/
69static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs);
70
71static int dndTransferListRootEntryAdd(PDNDTRANSFERLIST pList, const char *pcszRoot);
72static void dndTransferListRootEntryFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj);
73
74static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags);
75static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pLstObj);
76
77
78/** The size of the directory entry buffer we're using. */
79#define DNDTRANSFERLIST_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
80
81
82/**
83 * Initializes a transfer list, internal version.
84 *
85 * @returns VBox status code.
86 * @param pList Transfer list to initialize.
87 * @param pcszRootPathAbs Absolute root path to use for this list. Optional and can be NULL.
88 */
89static int dndTransferListInitInternal(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
90{
91 AssertPtrReturn(pList, VERR_INVALID_POINTER);
92 /* pcszRootPathAbs is optional. */
93
94 if (pList->pszPathRootAbs) /* Already initialized? */
95 return VERR_WRONG_ORDER;
96
97 pList->pszPathRootAbs = NULL;
98
99 RTListInit(&pList->lstRoot);
100 pList->cRoots = 0;
101
102 RTListInit(&pList->lstObj);
103 pList->cObj = 0;
104 pList->cbObjTotal = 0;
105
106 if (pcszRootPathAbs)
107 return dndTransferListSetRootPath(pList, pcszRootPathAbs);
108
109 return VINF_SUCCESS;
110}
111
112/**
113 * Initializes a transfer list, extended version.
114 *
115 * @returns VBox status code.
116 * @param pList Transfer list to initialize.
117 * @param pcszRootPathAbs Absolute root path to use for this list.
118 * @param enmFmt Format of \a pcszRootPathAbs.
119 */
120int DnDTransferListInitEx(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs, DNDTRANSFERLISTFMT enmFmt)
121{
122 AssertPtrReturn(pList, VERR_INVALID_POINTER);
123 AssertPtrReturn(pcszRootPathAbs, VERR_INVALID_POINTER);
124 AssertReturn(*pcszRootPathAbs, VERR_INVALID_PARAMETER);
125
126 int rc;
127
128 if (enmFmt == DNDTRANSFERLISTFMT_URI)
129 {
130 char *pszPath;
131 rc = RTUriFilePathEx(pcszRootPathAbs, RTPATH_STR_F_STYLE_UNIX, &pszPath, 0 /*cbPath*/, NULL /*pcchPath*/);
132 if (RT_SUCCESS(rc))
133 {
134 rc = dndTransferListInitInternal(pList, pszPath);
135 RTStrFree(pszPath);
136 }
137 }
138 else
139 rc = dndTransferListInitInternal(pList, pcszRootPathAbs);
140
141 return rc;
142}
143
144/**
145 * Initializes a transfer list.
146 *
147 * @returns VBox status code.
148 * @param pList Transfer list to initialize.
149 */
150int DnDTransferListInit(PDNDTRANSFERLIST pList)
151{
152 return dndTransferListInitInternal(pList, NULL /* pcszRootPathAbs */);
153}
154
155/**
156 * Destroys a transfer list.
157 *
158 * @param pList Transfer list to destroy.
159 */
160void DnDTransferListDestroy(PDNDTRANSFERLIST pList)
161{
162 if (!pList)
163 return;
164
165 DnDTransferListReset(pList);
166
167 RTStrFree(pList->pszPathRootAbs);
168 pList->pszPathRootAbs = NULL;
169}
170
171/**
172 * Initializes a transfer list and sets the root path.
173 *
174 * Convenience function which calls dndTransferListInitInternal() if not initialized already.
175 *
176 * @returns VBox status code.
177 * @param pList List to determine root path for.
178 * @param pcszRootPathAbs Root path to use.
179 */
180static int dndTransferInitAndSetRoot(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
181{
182 int rc;
183
184 if (!pList->pszPathRootAbs)
185 {
186 rc = dndTransferListInitInternal(pList, pcszRootPathAbs);
187 AssertRCReturn(rc, rc);
188
189 LogRel2(("DnD: Determined root path is '%s'\n", pList->pszPathRootAbs));
190 }
191 else
192 rc = VINF_SUCCESS;
193
194 return rc;
195}
196
197/**
198 * Resets a transfer list to its initial state.
199 *
200 * @param pList Transfer list to reset.
201 */
202void DnDTransferListReset(PDNDTRANSFERLIST pList)
203{
204 AssertPtrReturnVoid(pList);
205
206 if (!pList->pszPathRootAbs)
207 return;
208
209 RTStrFree(pList->pszPathRootAbs);
210 pList->pszPathRootAbs = NULL;
211
212 PDNDTRANSFERLISTROOT pRootCur, pRootNext;
213 RTListForEachSafe(&pList->lstRoot, pRootCur, pRootNext, DNDTRANSFERLISTROOT, Node)
214 dndTransferListRootEntryFree(pList, pRootCur);
215 Assert(RTListIsEmpty(&pList->lstRoot));
216
217 PDNDTRANSFEROBJECT pObjCur, pObjNext;
218 RTListForEachSafe(&pList->lstObj, pObjCur, pObjNext, DNDTRANSFEROBJECT, Node)
219 dndTransferListObjFree(pList, pObjCur);
220 Assert(RTListIsEmpty(&pList->lstObj));
221
222 Assert(pList->cRoots == 0);
223 Assert(pList->cObj == 0);
224
225 pList->cbObjTotal = 0;
226}
227
228/**
229 * Adds a single transfer object entry to a transfer List.
230 *
231 * @returns VBox status code.
232 * @param pList Transfer list to add entry to.
233 * @param pcszSrcAbs Absolute source path (local) to use.
234 * @param fMode File mode of entry to add.
235 * @param fFlags Transfer list flags to use for appending.
236 */
237static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags)
238{
239 AssertPtrReturn(pList, VERR_INVALID_POINTER);
240 AssertPtrReturn(pcszSrcAbs, VERR_INVALID_POINTER);
241
242 LogFlowFunc(("pcszSrcAbs=%s, fMode=%#x, fFlags=0x%x\n", pcszSrcAbs, fMode, fFlags));
243
244 int rc = VINF_SUCCESS;
245
246 if ( !RTFS_IS_FILE(fMode)
247 && !RTFS_IS_DIRECTORY(fMode))
248 /** @todo Symlinks not allowed. */
249 {
250 rc = VERR_NOT_SUPPORTED;
251 }
252
253 if (RT_SUCCESS(rc))
254 {
255 /* Calculate the path to add as the destination path to our URI object. */
256 const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
257 AssertReturn(strlen(pcszSrcAbs) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
258
259 PDNDTRANSFEROBJECT pObj = (PDNDTRANSFEROBJECT)RTMemAllocZ(sizeof(DNDTRANSFEROBJECT));
260 if (pObj)
261 {
262 const bool fIsFile = RTFS_IS_FILE(fMode);
263
264 rc = DnDTransferObjectInitEx(pObj, fIsFile ? DNDTRANSFEROBJTYPE_FILE : DNDTRANSFEROBJTYPE_DIRECTORY,
265 pList->pszPathRootAbs, &pcszSrcAbs[idxPathToAdd]);
266 if (RT_SUCCESS(rc))
267 {
268 if (fIsFile)
269 rc = DnDTransferObjectOpen(pObj,
270 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, /** @todo Add a standard fOpen mode for this list. */
271 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
272 if (RT_SUCCESS(rc))
273 {
274 RTListAppend(&pList->lstObj, &pObj->Node);
275
276 pList->cObj++;
277 if (fIsFile)
278 pList->cbObjTotal += DnDTransferObjectGetSize(pObj);
279
280 if ( fIsFile
281 && !(fFlags & DNDTRANSFERLIST_FLAGS_KEEP_OPEN)) /* Shall we keep the file open while being added to this list? */
282 rc = DnDTransferObjectClose(pObj);
283 }
284
285 if (RT_FAILURE(rc))
286 DnDTransferObjectDestroy(pObj);
287 }
288
289 if (RT_FAILURE(rc))
290 RTMemFree(pObj);
291 }
292 else
293 rc = VERR_NO_MEMORY;
294 }
295
296 if (RT_FAILURE(rc))
297 LogRel(("DnD: Adding entry '%s' of type %#x failed with rc=%Rrc\n", pcszSrcAbs, fMode & RTFS_TYPE_MASK, rc));
298
299 LogFlowFuncLeaveRC(rc);
300 return rc;
301}
302
303/**
304 * Frees an internal DnD transfer list object.
305 *
306 * @param pList Transfer list to free object for.
307 * @param pLstObj transfer list object to free. The pointer will be invalid after calling.
308 */
309static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
310{
311 if (!pObj)
312 return;
313
314 DnDTransferObjectDestroy(pObj);
315 RTListNodeRemove(&pObj->Node);
316 RTMemFree(pObj);
317
318 AssertReturnVoid(pList->cObj);
319 pList->cObj--;
320}
321
322/**
323 * Helper routine for handling adding sub directories.
324 *
325 * @return IPRT status code.
326 * @param pList transfer list to add found entries to.
327 * @param pszDir Pointer to the directory buffer.
328 * @param cchDir The length of pszDir in pszDir.
329 * @param pDirEntry Pointer to the directory entry.
330 * @param fFlags Flags of type DNDTRANSFERLISTFLAGS.
331 */
332static int dndTransferListAppendPathRecursiveSub(PDNDTRANSFERLIST pList,
333 char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
334 DNDTRANSFERLISTFLAGS fFlags)
335
336{
337 Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
338
339 /* Make sure we've got some room in the path, to save us extra work further down. */
340 if (cchDir + 3 >= RTPATH_MAX)
341 return VERR_BUFFER_OVERFLOW;
342
343 /* Open directory. */
344 RTDIR hDir;
345 int rc = RTDirOpen(&hDir, pszDir);
346 if (RT_FAILURE(rc))
347 return rc;
348
349 /* Ensure we've got a trailing slash (there is space for it see above). */
350 if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
351 {
352 pszDir[cchDir++] = RTPATH_SLASH;
353 pszDir[cchDir] = '\0';
354 }
355
356 rc = dndTransferListObjAdd(pList, pszDir, pDirEntry->Info.Attr.fMode, fFlags);
357 AssertRCReturn(rc, rc);
358
359 LogFlowFunc(("pszDir=%s\n", pszDir));
360
361 /*
362 * Process the files and subdirs.
363 */
364 for (;;)
365 {
366 /* Get the next directory. */
367 size_t cbDirEntry = DNDTRANSFERLIST_DIRENTRY_BUF_SIZE;
368 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
369 if (RT_FAILURE(rc))
370 break;
371
372 /* Check length. */
373 if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
374 {
375 rc = VERR_BUFFER_OVERFLOW;
376 break;
377 }
378
379 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
380 {
381 case RTFS_TYPE_SYMLINK:
382 {
383 if (!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS))
384 break;
385 RT_FALL_THRU();
386 }
387 case RTFS_TYPE_DIRECTORY:
388 {
389 if (RTDirEntryExIsStdDotLink(pDirEntry))
390 continue;
391
392 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
393 int rc2 = dndTransferListAppendPathRecursiveSub(pList, pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags);
394 if (RT_SUCCESS(rc))
395 rc = rc2;
396 break;
397 }
398
399 case RTFS_TYPE_FILE:
400 {
401 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
402 rc = dndTransferListObjAdd(pList, pszDir, pDirEntry->Info.Attr.fMode, fFlags);
403 break;
404 }
405
406 default:
407 {
408
409 break;
410 }
411 }
412 }
413
414 if (rc == VERR_NO_MORE_FILES) /* Done reading current directory? */
415 {
416 rc = VINF_SUCCESS;
417 }
418 else if (RT_FAILURE(rc))
419 LogRel(("DnD: Error while adding files recursively, rc=%Rrc\n", rc));
420
421 int rc2 = RTDirClose(hDir);
422 if (RT_FAILURE(rc2))
423 {
424 if (RT_SUCCESS(rc))
425 rc = rc2;
426 }
427
428 return rc;
429}
430
431/**
432 * Appends a native system path recursively by adding these entries as transfer objects.
433 *
434 * @returns VBox status code.
435 * @param pList Transfer list to add found entries to.
436 * @param pcszPathAbs Absolute path to add.
437 * @param fFlags Flags of type DNDTRANSFERLISTFLAGS.
438 */
439static int dndTransferListAppendDirectoryRecursive(PDNDTRANSFERLIST pList,
440 const char *pcszPathAbs, DNDTRANSFERLISTFLAGS fFlags)
441{
442 char szPathAbs[RTPATH_MAX];
443 int rc = RTStrCopy(szPathAbs, sizeof(szPathAbs), pcszPathAbs);
444 if (RT_FAILURE(rc))
445 return rc;
446
447 union
448 {
449 uint8_t abPadding[DNDTRANSFERLIST_DIRENTRY_BUF_SIZE];
450 RTDIRENTRYEX DirEntry;
451 } uBuf;
452
453 const size_t cchPathAbs = RTStrNLen(szPathAbs, RTPATH_MAX);
454 AssertReturn(cchPathAbs, VERR_BUFFER_OVERFLOW);
455
456 /* Use the directory entry to hand-in the directorie's information. */
457 rc = RTPathQueryInfo(pcszPathAbs, &uBuf.DirEntry.Info, RTFSOBJATTRADD_NOTHING);
458 AssertRCReturn(rc, rc);
459
460 return dndTransferListAppendPathRecursiveSub(pList, szPathAbs, cchPathAbs, &uBuf.DirEntry, fFlags);
461}
462
463/**
464 * Helper function for appending a local directory to a DnD transfer list.
465 *
466 * @returns VBox status code.
467 * @param pList Transfer list to return total number of root entries for.
468 * @param pszPathAbs Absolute path of directory to append.
469 * @param cbPathAbs Size (in bytes) of absolute path of directory to append.
470 * @param pObjInfo Pointer to directory object info to append.
471 * @param fFlags Transfer list flags to use for appending.
472 */
473static int dndTransferListAppendDirectory(PDNDTRANSFERLIST pList, char* pszPathAbs, size_t cbPathAbs,
474 PRTFSOBJINFO pObjInfo, DNDTRANSFERLISTFLAGS fFlags)
475{
476 const size_t cchPathRoot = RTStrNLen(pList->pszPathRootAbs, RTPATH_MAX);
477 AssertReturn(cchPathRoot, VERR_INVALID_PARAMETER);
478
479 const size_t cchPathAbs = RTPathEnsureTrailingSeparator(pszPathAbs, sizeof(cbPathAbs));
480 AssertReturn(cchPathAbs, VERR_BUFFER_OVERFLOW);
481 AssertReturn(cchPathAbs >= cchPathRoot, VERR_BUFFER_UNDERFLOW);
482
483 const bool fPathIsRoot = cchPathAbs == cchPathRoot;
484
485 int rc;
486
487 if (!fPathIsRoot)
488 {
489 rc = dndTransferListObjAdd(pList, pszPathAbs, pObjInfo->Attr.fMode, fFlags);
490 AssertRCReturn(rc, rc);
491 }
492
493 RTDIR hDir;
494 rc = RTDirOpen(&hDir, pszPathAbs);
495 AssertRCReturn(rc, rc);
496
497 for (;;)
498 {
499 /* Get the next entry. */
500 RTDIRENTRYEX dirEntry;
501 rc = RTDirReadEx(hDir, &dirEntry, NULL, RTFSOBJATTRADD_UNIX,
502 RTPATH_F_ON_LINK /** @todo No symlinks yet. */);
503 if (RT_SUCCESS(rc))
504 {
505 if (RTDirEntryExIsStdDotLink(&dirEntry))
506 continue;
507
508 /* Check length. */
509 if (dirEntry.cbName + cchPathAbs + 3 >= cbPathAbs)
510 {
511 rc = VERR_BUFFER_OVERFLOW;
512 break;
513 }
514
515 /* Append the directory entry to our absolute path. */
516 memcpy(&pszPathAbs[cchPathAbs], dirEntry.szName, dirEntry.cbName + 1 /* Include terminator */);
517
518 LogFlowFunc(("szName=%s, pszPathAbs=%s\n", dirEntry.szName, pszPathAbs));
519
520 switch (dirEntry.Info.Attr.fMode & RTFS_TYPE_MASK)
521 {
522 case RTFS_TYPE_DIRECTORY:
523 {
524 if (fFlags & DNDTRANSFERLIST_FLAGS_RECURSIVE)
525 rc = dndTransferListAppendDirectoryRecursive(pList, pszPathAbs, fFlags);
526 break;
527 }
528
529 case RTFS_TYPE_FILE:
530 {
531 rc = dndTransferListObjAdd(pList, pszPathAbs, dirEntry.Info.Attr.fMode, fFlags);
532 break;
533 }
534
535 default:
536 /* Silently skip everything else. */
537 break;
538 }
539
540 if ( RT_SUCCESS(rc)
541 /* Make sure to add a root entry if we're processing the root path at the moment. */
542 && fPathIsRoot)
543 {
544 rc = dndTransferListRootEntryAdd(pList, pszPathAbs);
545 if (RT_FAILURE(rc))
546 break;
547 }
548 }
549 else if (rc == VERR_NO_MORE_FILES)
550 {
551 rc = VINF_SUCCESS;
552 break;
553 }
554 else
555 break;
556 }
557
558 RTDirClose(hDir);
559 return rc;
560}
561
562/**
563 * Appends a native path to a DnD transfer list.
564 *
565 * @returns VBox status code.
566 * @param pList Transfer list to append native path to.
567 * @param pcszPath Path (native) to append.
568 * @param fFlags Transfer list flags to use for appending.
569 */
570static int dndTransferListAppendPathNative(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
571{
572 /* We don't want to have a relative directory here. */
573 if (!RTPathStartsWithRoot(pcszPath))
574 return VERR_INVALID_PARAMETER;
575
576 int rc = DnDPathValidate(pcszPath, false /* fMustExist */);
577 AssertRCReturn(rc, rc);
578
579 char szPathAbs[RTPATH_MAX];
580 size_t cbPathAbs = sizeof(szPathAbs);
581 rc = RTStrCopy(szPathAbs, cbPathAbs, pcszPath);
582 AssertRCReturn(rc, rc);
583
584 size_t cchPathAbs = RTStrNLen(szPathAbs, cbPathAbs);
585 AssertReturn(cchPathAbs, VERR_INVALID_PARAMETER);
586
587 /* Convert path to transport style. */
588 rc = DnDPathConvert(szPathAbs, cbPathAbs, DNDPATHCONVERT_FLAGS_TRANSPORT);
589 if (RT_SUCCESS(rc))
590 {
591 /* Make sure the path has the same root path as our list. */
592 if (RTPathStartsWith(szPathAbs, pList->pszPathRootAbs))
593 {
594 RTFSOBJINFO objInfo;
595 rc = RTPathQueryInfo(szPathAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
596 if (RT_SUCCESS(rc))
597 {
598 const uint32_t fType = objInfo.Attr.fMode & RTFS_TYPE_MASK;
599
600 if ( RTFS_IS_DIRECTORY(fType)
601 || RTFS_IS_FILE(fType))
602 {
603 if (RTFS_IS_DIRECTORY(fType))
604 {
605 cchPathAbs = RTPathEnsureTrailingSeparator(szPathAbs, cbPathAbs);
606 AssertReturn(cchPathAbs, VERR_BUFFER_OVERFLOW);
607 }
608
609 const size_t cchPathRoot = RTStrNLen(pList->pszPathRootAbs, RTPATH_MAX);
610 AssertStmt(cchPathRoot, rc = VERR_INVALID_PARAMETER);
611
612 if ( RT_SUCCESS(rc)
613 && cchPathAbs > cchPathRoot)
614 rc = dndTransferListRootEntryAdd(pList, szPathAbs);
615 }
616 else
617 rc = VERR_NOT_SUPPORTED;
618
619 if (RT_SUCCESS(rc))
620 {
621 switch (fType)
622 {
623 case RTFS_TYPE_DIRECTORY:
624 {
625 rc = dndTransferListAppendDirectory(pList, szPathAbs, cbPathAbs, &objInfo, fFlags);
626 break;
627 }
628
629 case RTFS_TYPE_FILE:
630 {
631 rc = dndTransferListObjAdd(pList, szPathAbs, objInfo.Attr.fMode, fFlags);
632 break;
633 }
634
635 default:
636 AssertFailed();
637 break;
638 }
639 }
640 }
641 /* On UNIX-y OSes RTPathQueryInfo() returns VERR_FILE_NOT_FOUND in some cases
642 * so tweak this to make it uniform to Windows. */
643 else if (rc == VERR_FILE_NOT_FOUND)
644 rc = VERR_PATH_NOT_FOUND;
645 }
646 else
647 rc = VERR_INVALID_PARAMETER;
648 }
649
650 if (RT_FAILURE(rc))
651 LogRel(("DnD: Adding native path '%s' failed with rc=%Rrc\n", pcszPath, rc));
652
653 return rc;
654}
655
656/**
657 * Appends an URI path to a DnD transfer list.
658 *
659 * @returns VBox status code.
660 * @param pList Transfer list to append native path to.
661 * @param pcszPath URI path to append.
662 * @param fFlags Transfer list flags to use for appending.
663 */
664static int dndTransferListAppendPathURI(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
665{
666 RT_NOREF(fFlags);
667
668 /* Query the path component of a file URI. If this hasn't a
669 * file scheme, NULL is returned. */
670 char *pszPath;
671 int rc = RTUriFilePathEx(pcszPath, RTPATH_STR_F_STYLE_UNIX, &pszPath, 0 /*cbPath*/, NULL /*pcchPath*/);
672 if (RT_SUCCESS(rc))
673 {
674 rc = dndTransferListAppendPathNative(pList, pszPath, fFlags);
675 RTStrFree(pszPath);
676 }
677
678 if (RT_FAILURE(rc))
679 LogRel(("DnD: Adding URI path '%s' failed with rc=%Rrc\n", pcszPath, rc));
680
681 return rc;
682}
683
684/**
685 * Appends a single path to a transfer list.
686 *
687 * @returns VBox status code. VERR_NOT_SUPPORTED if the path is not supported.
688 * @param pList Transfer list to append to.
689 * @param enmFmt Format of \a pszPaths to append.
690 * @param pcszPath Path to append. Must be part of the list's set root path.
691 * @param fFlags Transfer list flags to use for appending.
692 */
693int DnDTransferListAppendPath(PDNDTRANSFERLIST pList,
694 DNDTRANSFERLISTFMT enmFmt, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
695{
696 AssertPtrReturn(pList, VERR_INVALID_POINTER);
697 AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
698 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
699 AssertReturn(!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS), VERR_NOT_SUPPORTED);
700
701 int rc;
702
703 switch (enmFmt)
704 {
705 case DNDTRANSFERLISTFMT_NATIVE:
706 rc = dndTransferListAppendPathNative(pList, pcszPath, fFlags);
707 break;
708
709 case DNDTRANSFERLISTFMT_URI:
710 rc = dndTransferListAppendPathURI(pList, pcszPath, fFlags);
711 break;
712
713 default:
714 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
715 break; /* Never reached */
716 }
717
718 return rc;
719}
720
721/**
722 * Appends native paths to a transfer list.
723 *
724 * @returns VBox status code.
725 * @param pList Transfer list to append to.
726 * @param enmFmt Format of \a pszPaths to append.
727 * @param pszPaths Buffer of paths to append.
728 * @param cbPaths Size (in bytes) of buffer of paths to append.
729 * @param pcszSeparator Separator string to use.
730 * @param fFlags Transfer list flags to use for appending.
731 */
732int DnDTransferListAppendPathsFromBuffer(PDNDTRANSFERLIST pList,
733 DNDTRANSFERLISTFMT enmFmt, const char *pszPaths, size_t cbPaths,
734 const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
735{
736 AssertPtrReturn(pList, VERR_INVALID_POINTER);
737 AssertPtrReturn(pszPaths, VERR_INVALID_POINTER);
738 AssertReturn(cbPaths, VERR_INVALID_PARAMETER);
739
740 char **papszPaths = NULL;
741 size_t cPaths = 0;
742 int rc = RTStrSplit(pszPaths, cbPaths, pcszSeparator, &papszPaths, &cPaths);
743 if (RT_SUCCESS(rc))
744 rc = DnDTransferListAppendPathsFromArray(pList, enmFmt, papszPaths, cPaths, fFlags);
745
746 for (size_t i = 0; i < cPaths; ++i)
747 RTStrFree(papszPaths[i]);
748 RTMemFree(papszPaths);
749
750 return rc;
751}
752
753/**
754 * Appends paths to a transfer list.
755 *
756 * @returns VBox status code. Will return VERR_INVALID_PARAMETER if a common root path could not be found.
757 * @param pList Transfer list to append path to.
758 * @param enmFmt Format of \a papcszPaths to append.
759 * @param papcszPaths Array of paths to append.
760 * @param cPaths Number of paths in \a papcszPaths to append.
761 * @param fFlags Transfer list flags to use for appending.
762 */
763int DnDTransferListAppendPathsFromArray(PDNDTRANSFERLIST pList,
764 DNDTRANSFERLISTFMT enmFmt,
765 const char * const *papcszPaths, size_t cPaths, DNDTRANSFERLISTFLAGS fFlags)
766{
767 AssertPtrReturn(pList, VERR_INVALID_POINTER);
768 AssertPtrReturn(papcszPaths, VERR_INVALID_POINTER);
769 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
770
771 int rc = VINF_SUCCESS;
772
773 if (!cPaths) /* Nothing to add? Bail out. */
774 return VINF_SUCCESS;
775
776 char **papszPathsTmp = NULL;
777
778 /* If URI data is being handed in, extract the paths first. */
779 if (enmFmt == DNDTRANSFERLISTFMT_URI)
780 {
781 papszPathsTmp = (char **)RTMemAlloc(sizeof(char *) * cPaths);
782 if (papszPathsTmp)
783 {
784 for (size_t i = 0; i < cPaths; i++)
785 papszPathsTmp[i] = RTUriFilePath(papcszPaths[i]);
786 }
787 else
788 rc = VERR_NO_MEMORY;
789 }
790
791 if (RT_FAILURE(rc))
792 return rc;
793
794 /* If we don't have a root path set, try to find the common path of all handed-in paths. */
795 if (!pList->pszPathRootAbs)
796 {
797 /* Can we work on the unmodified, handed-in data or do we need to use our temporary paths? */
798 const char * const *papszPathTmp = enmFmt == DNDTRANSFERLISTFMT_NATIVE
799 ? papcszPaths : papszPathsTmp;
800
801 size_t cchRootPath = 0; /* Length of root path in chars. */
802 if (cPaths > 1)
803 {
804 cchRootPath = RTPathFindCommon(cPaths, papszPathTmp);
805 }
806 else
807 cchRootPath = RTPathParentLength(papszPathTmp[0]);
808
809 if (cchRootPath)
810 {
811 /* Just use the first path in the array as the reference. */
812 char *pszRootPath = RTStrDupN(papszPathTmp[0], cchRootPath);
813 if (pszRootPath)
814 {
815 rc = dndTransferInitAndSetRoot(pList, pszRootPath);
816 RTStrFree(pszRootPath);
817 }
818 else
819 rc = VERR_NO_MEMORY;
820 }
821 else
822 rc = VERR_INVALID_PARAMETER;
823 }
824
825 if (RT_SUCCESS(rc))
826 {
827 /*
828 * Add all paths to the list.
829 */
830 for (size_t i = 0; i < cPaths; i++)
831 {
832 const char *pcszPath = enmFmt == DNDTRANSFERLISTFMT_NATIVE
833 ? papcszPaths[i] : papszPathsTmp[i];
834 rc = DnDTransferListAppendPath(pList, DNDTRANSFERLISTFMT_NATIVE, pcszPath, fFlags);
835 if (RT_FAILURE(rc))
836 {
837 LogRel(("DnD: Adding path '%s' (format %#x, root '%s') to transfer list failed with %Rrc\n",
838 pcszPath, enmFmt, pList->pszPathRootAbs ? pList->pszPathRootAbs : "<None>", rc));
839 break;
840 }
841 }
842 }
843
844 if (papszPathsTmp)
845 {
846 for (size_t i = 0; i < cPaths; i++)
847 RTStrFree(papszPathsTmp[i]);
848 RTMemFree(papszPathsTmp);
849 }
850
851 LogFlowFuncLeaveRC(rc);
852 return rc;
853}
854
855/**
856 * Appends the root entries for a transfer list.
857 *
858 * @returns VBox status code.
859 * @param pList Transfer list to append to.
860 * @param enmFmt Format of \a pszPaths to append.
861 * @param pszPaths Buffer of paths to append.
862 * @param cbPaths Size (in bytes) of buffer of paths to append.
863 * @param pcszSeparator Separator string to use.
864 * @param fFlags Transfer list flags to use for appending.
865 */
866int DnDTransferListAppendRootsFromBuffer(PDNDTRANSFERLIST pList,
867 DNDTRANSFERLISTFMT enmFmt, const char *pszPaths, size_t cbPaths,
868 const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
869{
870 AssertPtrReturn(pList, VERR_INVALID_POINTER);
871 AssertPtrReturn(pszPaths, VERR_INVALID_POINTER);
872 AssertReturn(cbPaths, VERR_INVALID_PARAMETER);
873
874 char **papszPaths = NULL;
875 size_t cPaths = 0;
876 int rc = RTStrSplit(pszPaths, cbPaths, pcszSeparator, &papszPaths, &cPaths);
877 if (RT_SUCCESS(rc))
878 rc = DnDTransferListAppendRootsFromArray(pList, enmFmt, papszPaths, cPaths, fFlags);
879
880 for (size_t i = 0; i < cPaths; ++i)
881 RTStrFree(papszPaths[i]);
882 RTMemFree(papszPaths);
883
884 return rc;
885}
886
887/**
888 * Appends root entries to a transfer list.
889 *
890 * @returns VBox status code.
891 * @param pList Transfer list to append root entries to.
892 * @param enmFmt Format of \a papcszPaths to append.
893 * @param papcszPaths Array of paths to append.
894 * @param cPaths Number of paths in \a papcszPaths to append.
895 * @param fFlags Transfer list flags to use for appending.
896 */
897int DnDTransferListAppendRootsFromArray(PDNDTRANSFERLIST pList,
898 DNDTRANSFERLISTFMT enmFmt,
899 const char * const *papcszPaths, size_t cPaths, DNDTRANSFERLISTFLAGS fFlags)
900{
901 AssertPtrReturn(pList, VERR_INVALID_POINTER);
902 AssertPtrReturn(papcszPaths, VERR_INVALID_POINTER);
903 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
904
905 AssertMsgReturn(pList->pszPathRootAbs, ("Root path not set yet\n"), VERR_WRONG_ORDER);
906
907 int rc = VINF_SUCCESS;
908
909 if (!cPaths) /* Nothing to add? Bail out. */
910 return VINF_SUCCESS;
911
912 char **papszPathsTmp = NULL;
913
914 /* If URI data is being handed in, extract the paths first. */
915 if (enmFmt == DNDTRANSFERLISTFMT_URI)
916 {
917 papszPathsTmp = (char **)RTMemAlloc(sizeof(char *) * cPaths);
918 if (papszPathsTmp)
919 {
920 for (size_t i = 0; i < cPaths; i++)
921 papszPathsTmp[i] = RTUriFilePath(papcszPaths[i]);
922 }
923 else
924 rc = VERR_NO_MEMORY;
925 }
926
927 if (RT_FAILURE(rc))
928 return rc;
929
930 char szPath[RTPATH_MAX];
931
932 /*
933 * Add all root entries to the root list.
934 */
935 for (size_t i = 0; i < cPaths; i++)
936 {
937 const char *pcszPath = enmFmt == DNDTRANSFERLISTFMT_NATIVE
938 ? papcszPaths[i] : papszPathsTmp[i];
939
940 rc = RTPathJoin(szPath, sizeof(szPath), pList->pszPathRootAbs, pcszPath);
941 AssertRCBreak(rc);
942
943 rc = DnDPathConvert(szPath, sizeof(szPath), DNDPATHCONVERT_FLAGS_TRANSPORT);
944 AssertRCBreak(rc);
945
946 rc = dndTransferListRootEntryAdd(pList, szPath);
947 if (RT_FAILURE(rc))
948 {
949 LogRel(("DnD: Adding root entry '%s' (format %#x, root '%s') to transfer list failed with %Rrc\n",
950 szPath, enmFmt, pList->pszPathRootAbs, rc));
951 break;
952 }
953 }
954
955 if (papszPathsTmp)
956 {
957 for (size_t i = 0; i < cPaths; i++)
958 RTStrFree(papszPathsTmp[i]);
959 RTMemFree(papszPathsTmp);
960 }
961
962 LogFlowFuncLeaveRC(rc);
963 return rc;
964}
965
966/**
967 * Returns the first transfer object in a list.
968 *
969 * @returns Pointer to transfer object if found, or NULL if not found.
970 * @param pList Transfer list to get first transfer object from.
971 */
972PDNDTRANSFEROBJECT DnDTransferListObjGetFirst(PDNDTRANSFERLIST pList)
973{
974 AssertPtrReturn(pList, NULL);
975
976 return RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
977}
978
979/**
980 * Removes an object from a transfer list, internal version.
981 *
982 * @param pList Transfer list to remove object from.
983 * @param pObj Object to remove. The object will be free'd and the pointer is invalid after calling.
984 */
985static void dndTransferListObjRemoveInternal(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
986{
987 AssertPtrReturnVoid(pList);
988 AssertPtrReturnVoid(pObj);
989
990 /** @todo Validate if \a pObj is part of \a pList. */
991
992 uint64_t cbSize = DnDTransferObjectGetSize(pObj);
993 Assert(pList->cbObjTotal >= cbSize);
994 pList->cbObjTotal -= cbSize; /* Adjust total size. */
995
996 dndTransferListObjFree(pList, pObj);
997}
998
999/**
1000 * Removes an object from a transfer list.
1001 *
1002 * @param pList Transfer list to remove object from.
1003 * @param pObj Object to remove. The object will be free'd and the pointer is invalid after calling.
1004 */
1005void DnDTransferListObjRemove(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
1006{
1007 return dndTransferListObjRemoveInternal(pList, pObj);
1008}
1009
1010/**
1011 * Removes the first DnD transfer object from a transfer list.
1012 *
1013 * @param pList Transfer list to remove first entry for.
1014 */
1015void DnDTransferListObjRemoveFirst(PDNDTRANSFERLIST pList)
1016{
1017 AssertPtrReturnVoid(pList);
1018
1019 if (!pList->cObj)
1020 return;
1021
1022 PDNDTRANSFEROBJECT pObj = RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
1023 AssertPtr(pObj);
1024
1025 dndTransferListObjRemoveInternal(pList, pObj);
1026}
1027
1028/**
1029 * Returns all root entries of a transfer list as a string.
1030 *
1031 * @returns VBox status code.
1032 * @param pList Transfer list to return root paths for.
1033 * @param pcszPathBase Root path to use as a base path. If NULL, the list's absolute root path will be used (if any).
1034 * @param pcszSeparator Separator to use for separating the root entries.
1035 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
1036 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
1037 */
1038int DnDTransferListGetRootsEx(PDNDTRANSFERLIST pList,
1039 DNDTRANSFERLISTFMT enmFmt, const char *pcszPathBase, const char *pcszSeparator,
1040 char **ppszBuffer, size_t *pcbBuffer)
1041{
1042 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1043 /* pcszPathBase can be NULL. */
1044 AssertPtrReturn(pcszSeparator, VERR_INVALID_POINTER);
1045 AssertPtrReturn(ppszBuffer, VERR_INVALID_POINTER);
1046 AssertPtrReturn(pcbBuffer, VERR_INVALID_POINTER);
1047
1048 char *pszString = NULL;
1049 size_t cchString = 0;
1050
1051 const size_t cchSep = RTStrNLen(pcszSeparator, RTPATH_MAX);
1052
1053 /* Find out which root path to use. */
1054 const char *pcszPathRootTmp = pcszPathBase ? pcszPathBase : pList->pszPathRootAbs;
1055 /* pcszPathRootTmp can be NULL */
1056
1057 LogFlowFunc(("Using root path '%s'\n", pcszPathRootTmp ? pcszPathRootTmp : "<None>"));
1058
1059 int rc = DnDPathValidate(pcszPathRootTmp, false /* fMustExist */);
1060 if (RT_FAILURE(rc))
1061 return rc;
1062
1063 char szPath[RTPATH_MAX];
1064
1065 PDNDTRANSFERLISTROOT pRoot;
1066 RTListForEach(&pList->lstRoot, pRoot, DNDTRANSFERLISTROOT, Node)
1067 {
1068 if (pcszPathRootTmp)
1069 {
1070 rc = RTStrCopy(szPath, sizeof(szPath), pcszPathRootTmp);
1071 AssertRCBreak(rc);
1072 }
1073
1074 rc = RTPathAppend(szPath, sizeof(szPath), pRoot->pszPathRoot);
1075 AssertRCBreak(rc);
1076
1077 if (enmFmt == DNDTRANSFERLISTFMT_URI)
1078 {
1079 char *pszPathURI = RTUriFileCreate(szPath);
1080 AssertPtrBreakStmt(pszPathURI, rc = VERR_NO_MEMORY);
1081 rc = RTStrAAppend(&pszString, pszPathURI);
1082 cchString += RTStrNLen(pszPathURI, RTPATH_MAX);
1083 RTStrFree(pszPathURI);
1084 AssertRCBreak(rc);
1085 }
1086 else /* Native */
1087 {
1088#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
1089 /* Convert paths to native path style. */
1090 rc = DnDPathConvert(szPath, sizeof(szPath), DNDPATHCONVERT_FLAGS_TO_DOS);
1091#endif
1092 if (RT_SUCCESS(rc))
1093 {
1094 rc = RTStrAAppend(&pszString, szPath);
1095 AssertRCBreak(rc);
1096
1097 cchString += RTStrNLen(szPath, RTPATH_MAX);
1098 }
1099 }
1100
1101 rc = RTStrAAppend(&pszString, pcszSeparator);
1102 AssertRCBreak(rc);
1103
1104 cchString += cchSep;
1105 }
1106
1107 if (RT_SUCCESS(rc))
1108 {
1109 *ppszBuffer = pszString;
1110 *pcbBuffer = pszString ? cchString + 1 /* Include termination */ : 0;
1111 }
1112 else
1113 RTStrFree(pszString);
1114 return rc;
1115}
1116
1117/**
1118 * Returns all root entries for a DnD transfer list.
1119 *
1120 * Note: Convenience function which uses the default DnD path separator.
1121 *
1122 * @returns VBox status code.
1123 * @param pList Transfer list to return root entries for.
1124 * @param enmFmt Which format to use for returning the entries.
1125 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
1126 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
1127 */
1128int DnDTransferListGetRoots(PDNDTRANSFERLIST pList,
1129 DNDTRANSFERLISTFMT enmFmt, char **ppszBuffer, size_t *pcbBuffer)
1130{
1131 return DnDTransferListGetRootsEx(pList, enmFmt, "" /* pcszPathRoot */, DND_PATH_SEPARATOR_STR,
1132 ppszBuffer, pcbBuffer);
1133}
1134
1135/**
1136 * Returns the total root entries count for a DnD transfer list.
1137 *
1138 * @returns Total number of root entries.
1139 * @param pList Transfer list to return total number of root entries for.
1140 */
1141uint64_t DnDTransferListGetRootCount(PDNDTRANSFERLIST pList)
1142{
1143 AssertPtrReturn(pList, 0);
1144 return pList->cRoots;
1145}
1146
1147/**
1148 * Returns the absolute root path for a DnD transfer list.
1149 *
1150 * @returns Pointer to the root path.
1151 * @param pList Transfer list to return root path for.
1152 */
1153const char *DnDTransferListGetRootPathAbs(PDNDTRANSFERLIST pList)
1154{
1155 AssertPtrReturn(pList, NULL);
1156 return pList->pszPathRootAbs;
1157}
1158
1159/**
1160 * Returns the total transfer object count for a DnD transfer list.
1161 *
1162 * @returns Total number of transfer objects.
1163 * @param pList Transfer list to return total number of transfer objects for.
1164 */
1165uint64_t DnDTransferListObjCount(PDNDTRANSFERLIST pList)
1166{
1167 AssertPtrReturn(pList, 0);
1168 return pList->cObj;
1169}
1170
1171/**
1172 * Returns the total bytes of all handled transfer objects for a DnD transfer list.
1173 *
1174 * @returns VBox status code.
1175 * @param pList Transfer list to return total bytes for.
1176 */
1177uint64_t DnDTransferListObjTotalBytes(PDNDTRANSFERLIST pList)
1178{
1179 AssertPtrReturn(pList, 0);
1180 return pList->cbObjTotal;
1181}
1182
1183/**
1184 * Sets the absolute root path of a transfer list.
1185 *
1186 * @returns VBox status code.
1187 * @param pList Transfer list to set root path for.
1188 * @param pcszRootPathAbs Absolute root path to set.
1189 */
1190static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
1191{
1192 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1193 AssertPtrReturn(pcszRootPathAbs, VERR_INVALID_POINTER);
1194 AssertReturn(pList->pszPathRootAbs == NULL, VERR_WRONG_ORDER); /* Already initialized? */
1195
1196 LogFlowFunc(("pcszRootPathAbs=%s\n", pcszRootPathAbs));
1197
1198 char szRootPath[RTPATH_MAX];
1199 int rc = RTStrCopy(szRootPath, sizeof(szRootPath), pcszRootPathAbs);
1200 if (RT_FAILURE(rc))
1201 return rc;
1202
1203 /* Note: The list's root path is kept in native style, so no conversion needed here. */
1204
1205 RTPathEnsureTrailingSeparatorEx(szRootPath, sizeof(szRootPath), RTPATH_STR_F_STYLE_HOST);
1206
1207 /* Make sure the root path is a directory (and no symlink or stuff). */
1208 RTFSOBJINFO objInfo;
1209 rc = RTPathQueryInfo(szRootPath, &objInfo, RTFSOBJATTRADD_NOTHING);
1210 if (RT_SUCCESS(rc))
1211 {
1212 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode & RTFS_TYPE_MASK))
1213 {
1214 pList->pszPathRootAbs = RTStrDup(szRootPath);
1215 if (pList->pszPathRootAbs)
1216 {
1217 LogFlowFunc(("Root path is '%s'\n", pList->pszPathRootAbs));
1218 }
1219 else
1220 rc = VERR_NO_MEMORY;
1221 }
1222 else
1223 rc = VERR_NOT_A_DIRECTORY;
1224 }
1225
1226 return rc;
1227}
1228
1229/**
1230 * Adds a root entry to a DnD transfer list.
1231 *
1232 * @returns VBox status code.
1233 * @param pList Transfer list to add root entry to.
1234 * @param pcszRoot Root entry to add.
1235 */
1236static int dndTransferListRootEntryAdd(PDNDTRANSFERLIST pList, const char *pcszRoot)
1237{
1238 AssertPtrReturn(pList->pszPathRootAbs, VERR_WRONG_ORDER); /* The list's root path must be set first. */
1239
1240 int rc;
1241
1242 /** @todo Handle / reject double entries. */
1243
1244 /* Get the index pointing to the relative path in relation to set the root path. */
1245 const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
1246 AssertReturn(strlen(pcszRoot) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
1247
1248 PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAllocZ(sizeof(DNDTRANSFERLISTROOT));
1249 if (pRoot)
1250 {
1251 const char *pcszRootIdx = &pcszRoot[idxPathToAdd];
1252
1253 LogFlowFunc(("pcszRoot=%s\n", pcszRootIdx));
1254
1255 pRoot->pszPathRoot = RTStrDup(pcszRootIdx);
1256 if (pRoot->pszPathRoot)
1257 {
1258 RTListAppend(&pList->lstRoot, &pRoot->Node);
1259 pList->cRoots++;
1260
1261 rc = VINF_SUCCESS;
1262 }
1263 else
1264 rc = VERR_NO_MEMORY;
1265
1266 if (RT_FAILURE(rc))
1267 {
1268 RTMemFree(pRoot);
1269 pRoot = NULL;
1270 }
1271 }
1272 else
1273 rc = VERR_NO_MEMORY;
1274
1275 return rc;
1276}
1277
1278/**
1279 * Removes (and destroys) a DnD transfer root entry.
1280 *
1281 * @param pList Transfer list to free root for.
1282 * @param pRootObj Transfer list root to free. The pointer will be invalid after calling.
1283 */
1284static void dndTransferListRootEntryFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj)
1285{
1286 if (!pRootObj)
1287 return;
1288
1289 RTStrFree(pRootObj->pszPathRoot);
1290
1291 RTListNodeRemove(&pRootObj->Node);
1292 RTMemFree(pRootObj);
1293
1294 AssertReturnVoid(pList->cRoots);
1295 pList->cRoots--;
1296}
1297
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