VirtualBox

source: vbox/trunk/src/VBox/GuestHost/DragAndDrop/DnDTransferList.cpp@ 108813

Last change on this file since 108813 was 108813, checked in by vboxsync, 5 weeks ago

DnD: Added some more format checks and simplified DnDTransferListAppendPathsFromArray().

  • 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 108813 2025-04-01 15:16:47Z 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 if (!cPaths) /* Nothing to add? Bail out. */
772 return VINF_SUCCESS;
773
774 char **papszPathsConverted = NULL;
775
776 int rc = VINF_SUCCESS;
777
778 switch (enmFmt)
779 {
780 /* If URI data is being handed in, extract the paths first. */
781 case DNDTRANSFERLISTFMT_URI:
782 {
783 papszPathsConverted = (char **)RTMemAllocZ(sizeof(char *) * cPaths);
784 AssertPtrReturn(papszPathsConverted, VERR_NO_MEMORY);
785 for (size_t i = 0; i < cPaths; i++)
786 {
787 papszPathsConverted[i] = RTUriFilePath(papcszPaths[i]);
788 AssertPtrBreakStmt(papszPathsConverted[i], rc = VERR_INVALID_PARAMETER);
789 }
790
791 papcszPaths = papszPathsConverted;
792 break;
793 }
794
795 case DNDTRANSFERLISTFMT_NATIVE:
796 /* Use original paths handed-in. */
797 break;
798
799 default:
800 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
801 break;
802 }
803
804 /* If we don't have a root path set, try to find the common path of all handed-in paths. */
805 if ( RT_SUCCESS(rc)
806 && !pList->pszPathRootAbs)
807 {
808 size_t cchRootPath = 0; /* Length of root path in chars. */
809 if (cPaths > 1)
810 cchRootPath = RTPathFindCommon(cPaths, papcszPaths);
811 else
812 cchRootPath = RTPathParentLength(papcszPaths[0]);
813 if (cchRootPath)
814 {
815 /* Just use the first path in the array as the reference. */
816 char *pszRootPath = RTStrDupN(papcszPaths[0], cchRootPath);
817 if (pszRootPath)
818 {
819 rc = dndTransferInitAndSetRoot(pList, pszRootPath);
820 RTStrFree(pszRootPath);
821 }
822 else
823 rc = VERR_NO_MEMORY;
824 }
825 else
826 rc = VERR_INVALID_PARAMETER;
827 }
828
829 if (RT_SUCCESS(rc))
830 {
831 /*
832 * Add all paths (as native paths) to the list.
833 */
834 for (size_t i = 0; i < cPaths; i++)
835 {
836 rc = DnDTransferListAppendPath(pList, DNDTRANSFERLISTFMT_NATIVE, papcszPaths[i], fFlags);
837 if (RT_FAILURE(rc))
838 {
839 LogRel(("DnD: Adding path '%s' (format %#x, root '%s') to transfer list failed with %Rrc\n",
840 papcszPaths[i], enmFmt, pList->pszPathRootAbs ? pList->pszPathRootAbs : "<None>", rc));
841 break;
842 }
843 }
844 }
845
846 if (papszPathsConverted)
847 {
848 for (size_t i = 0; i < cPaths; i++)
849 RTStrFree(papszPathsConverted[i]);
850 RTMemFree(papszPathsConverted);
851 }
852
853 LogFlowFuncLeaveRC(rc);
854 return rc;
855}
856
857/**
858 * Appends the root entries for a transfer list.
859 *
860 * @returns VBox status code.
861 * @param pList Transfer list to append to.
862 * @param enmFmt Format of \a pszPaths to append.
863 * @param pszPaths Buffer of paths to append.
864 * @param cbPaths Size (in bytes) of buffer of paths to append.
865 * @param pcszSeparator Separator string to use.
866 * @param fFlags Transfer list flags to use for appending.
867 */
868int DnDTransferListAppendRootsFromBuffer(PDNDTRANSFERLIST pList,
869 DNDTRANSFERLISTFMT enmFmt, const char *pszPaths, size_t cbPaths,
870 const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
871{
872 AssertPtrReturn(pList, VERR_INVALID_POINTER);
873 AssertPtrReturn(pszPaths, VERR_INVALID_POINTER);
874 AssertReturn(cbPaths, VERR_INVALID_PARAMETER);
875
876 char **papszPaths = NULL;
877 size_t cPaths = 0;
878 int rc = RTStrSplit(pszPaths, cbPaths, pcszSeparator, &papszPaths, &cPaths);
879 if (RT_SUCCESS(rc))
880 rc = DnDTransferListAppendRootsFromArray(pList, enmFmt, papszPaths, cPaths, fFlags);
881
882 for (size_t i = 0; i < cPaths; ++i)
883 RTStrFree(papszPaths[i]);
884 RTMemFree(papszPaths);
885
886 return rc;
887}
888
889/**
890 * Appends root entries to a transfer list.
891 *
892 * @returns VBox status code.
893 * @param pList Transfer list to append root entries to.
894 * @param enmFmt Format of \a papcszPaths to append.
895 * @param papcszPaths Array of paths to append.
896 * @param cPaths Number of paths in \a papcszPaths to append.
897 * @param fFlags Transfer list flags to use for appending.
898 */
899int DnDTransferListAppendRootsFromArray(PDNDTRANSFERLIST pList,
900 DNDTRANSFERLISTFMT enmFmt,
901 const char * const *papcszPaths, size_t cPaths, DNDTRANSFERLISTFLAGS fFlags)
902{
903 AssertPtrReturn(pList, VERR_INVALID_POINTER);
904 AssertPtrReturn(papcszPaths, VERR_INVALID_POINTER);
905 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
906
907 AssertMsgReturn(pList->pszPathRootAbs, ("Root path not set yet\n"), VERR_WRONG_ORDER);
908
909 int rc = VINF_SUCCESS;
910
911 if (!cPaths) /* Nothing to add? Bail out. */
912 return VINF_SUCCESS;
913
914 char **papszPathsTmp = NULL;
915
916 /* If URI data is being handed in, extract the paths first. */
917 if (enmFmt == DNDTRANSFERLISTFMT_URI)
918 {
919 papszPathsTmp = (char **)RTMemAlloc(sizeof(char *) * cPaths);
920 if (papszPathsTmp)
921 {
922 for (size_t i = 0; i < cPaths; i++)
923 papszPathsTmp[i] = RTUriFilePath(papcszPaths[i]);
924 }
925 else
926 rc = VERR_NO_MEMORY;
927 }
928
929 if (RT_FAILURE(rc))
930 return rc;
931
932 char szPath[RTPATH_MAX];
933
934 /*
935 * Add all root entries to the root list.
936 */
937 for (size_t i = 0; i < cPaths; i++)
938 {
939 const char *pcszPath = enmFmt == DNDTRANSFERLISTFMT_NATIVE
940 ? papcszPaths[i] : papszPathsTmp[i];
941
942 rc = RTPathJoin(szPath, sizeof(szPath), pList->pszPathRootAbs, pcszPath);
943 AssertRCBreak(rc);
944
945 rc = DnDPathConvert(szPath, sizeof(szPath), DNDPATHCONVERT_FLAGS_TRANSPORT);
946 AssertRCBreak(rc);
947
948 rc = dndTransferListRootEntryAdd(pList, szPath);
949 if (RT_FAILURE(rc))
950 {
951 LogRel(("DnD: Adding root entry '%s' (format %#x, root '%s') to transfer list failed with %Rrc\n",
952 szPath, enmFmt, pList->pszPathRootAbs, rc));
953 break;
954 }
955 }
956
957 if (papszPathsTmp)
958 {
959 for (size_t i = 0; i < cPaths; i++)
960 RTStrFree(papszPathsTmp[i]);
961 RTMemFree(papszPathsTmp);
962 }
963
964 LogFlowFuncLeaveRC(rc);
965 return rc;
966}
967
968/**
969 * Returns the first transfer object in a list.
970 *
971 * @returns Pointer to transfer object if found, or NULL if not found.
972 * @param pList Transfer list to get first transfer object from.
973 */
974PDNDTRANSFEROBJECT DnDTransferListObjGetFirst(PDNDTRANSFERLIST pList)
975{
976 AssertPtrReturn(pList, NULL);
977
978 return RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
979}
980
981/**
982 * Removes an object from a transfer list, internal version.
983 *
984 * @param pList Transfer list to remove object from.
985 * @param pObj Object to remove. The object will be free'd and the pointer is invalid after calling.
986 */
987static void dndTransferListObjRemoveInternal(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
988{
989 AssertPtrReturnVoid(pList);
990 AssertPtrReturnVoid(pObj);
991
992 /** @todo Validate if \a pObj is part of \a pList. */
993
994 uint64_t cbSize = DnDTransferObjectGetSize(pObj);
995 Assert(pList->cbObjTotal >= cbSize);
996 pList->cbObjTotal -= cbSize; /* Adjust total size. */
997
998 dndTransferListObjFree(pList, pObj);
999}
1000
1001/**
1002 * Removes an object from a transfer list.
1003 *
1004 * @param pList Transfer list to remove object from.
1005 * @param pObj Object to remove. The object will be free'd and the pointer is invalid after calling.
1006 */
1007void DnDTransferListObjRemove(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
1008{
1009 return dndTransferListObjRemoveInternal(pList, pObj);
1010}
1011
1012/**
1013 * Removes the first DnD transfer object from a transfer list.
1014 *
1015 * @param pList Transfer list to remove first entry for.
1016 */
1017void DnDTransferListObjRemoveFirst(PDNDTRANSFERLIST pList)
1018{
1019 AssertPtrReturnVoid(pList);
1020
1021 if (!pList->cObj)
1022 return;
1023
1024 PDNDTRANSFEROBJECT pObj = RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
1025 AssertPtr(pObj);
1026
1027 dndTransferListObjRemoveInternal(pList, pObj);
1028}
1029
1030/**
1031 * Returns all root entries of a transfer list as a string.
1032 *
1033 * @returns VBox status code.
1034 * @param pList Transfer list to return root paths for.
1035 * @param pcszPathBase Root path to use as a base path. If NULL, the list's absolute root path will be used (if any).
1036 * @param pcszSeparator Separator to use for separating the root entries.
1037 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
1038 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
1039 */
1040int DnDTransferListGetRootsEx(PDNDTRANSFERLIST pList,
1041 DNDTRANSFERLISTFMT enmFmt, const char *pcszPathBase, const char *pcszSeparator,
1042 char **ppszBuffer, size_t *pcbBuffer)
1043{
1044 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1045 /* pcszPathBase can be NULL. */
1046 AssertPtrReturn(pcszSeparator, VERR_INVALID_POINTER);
1047 AssertPtrReturn(ppszBuffer, VERR_INVALID_POINTER);
1048 AssertPtrReturn(pcbBuffer, VERR_INVALID_POINTER);
1049
1050 char *pszString = NULL;
1051 size_t cchString = 0;
1052
1053 const size_t cchSep = RTStrNLen(pcszSeparator, RTPATH_MAX);
1054
1055 /* Find out which root path to use. */
1056 const char *pcszPathRootTmp = pcszPathBase ? pcszPathBase : pList->pszPathRootAbs;
1057 /* pcszPathRootTmp can be NULL */
1058
1059 LogFlowFunc(("Using root path '%s'\n", pcszPathRootTmp ? pcszPathRootTmp : "<None>"));
1060
1061 int rc = DnDPathValidate(pcszPathRootTmp, false /* fMustExist */);
1062 if (RT_FAILURE(rc))
1063 return rc;
1064
1065 char szPath[RTPATH_MAX];
1066
1067 PDNDTRANSFERLISTROOT pRoot;
1068 RTListForEach(&pList->lstRoot, pRoot, DNDTRANSFERLISTROOT, Node)
1069 {
1070 if (pcszPathRootTmp)
1071 {
1072 rc = RTStrCopy(szPath, sizeof(szPath), pcszPathRootTmp);
1073 AssertRCBreak(rc);
1074 }
1075
1076 rc = RTPathAppend(szPath, sizeof(szPath), pRoot->pszPathRoot);
1077 AssertRCBreak(rc);
1078
1079 if (enmFmt == DNDTRANSFERLISTFMT_URI)
1080 {
1081 char *pszPathURI = RTUriFileCreate(szPath);
1082 AssertPtrBreakStmt(pszPathURI, rc = VERR_NO_MEMORY);
1083 rc = RTStrAAppend(&pszString, pszPathURI);
1084 cchString += RTStrNLen(pszPathURI, RTPATH_MAX);
1085 RTStrFree(pszPathURI);
1086 AssertRCBreak(rc);
1087 }
1088 else /* Native */
1089 {
1090#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
1091 /* Convert paths to native path style. */
1092 rc = DnDPathConvert(szPath, sizeof(szPath), DNDPATHCONVERT_FLAGS_TO_DOS);
1093#endif
1094 if (RT_SUCCESS(rc))
1095 {
1096 rc = RTStrAAppend(&pszString, szPath);
1097 AssertRCBreak(rc);
1098
1099 cchString += RTStrNLen(szPath, RTPATH_MAX);
1100 }
1101 }
1102
1103 rc = RTStrAAppend(&pszString, pcszSeparator);
1104 AssertRCBreak(rc);
1105
1106 cchString += cchSep;
1107 }
1108
1109 if (RT_SUCCESS(rc))
1110 {
1111 *ppszBuffer = pszString;
1112 *pcbBuffer = pszString ? cchString + 1 /* Include termination */ : 0;
1113 }
1114 else
1115 RTStrFree(pszString);
1116 return rc;
1117}
1118
1119/**
1120 * Returns all root entries for a DnD transfer list.
1121 *
1122 * Note: Convenience function which uses the default DnD path separator.
1123 *
1124 * @returns VBox status code.
1125 * @param pList Transfer list to return root entries for.
1126 * @param enmFmt Which format to use for returning the entries.
1127 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
1128 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
1129 */
1130int DnDTransferListGetRoots(PDNDTRANSFERLIST pList,
1131 DNDTRANSFERLISTFMT enmFmt, char **ppszBuffer, size_t *pcbBuffer)
1132{
1133 return DnDTransferListGetRootsEx(pList, enmFmt, "" /* pcszPathRoot */, DND_PATH_SEPARATOR_STR,
1134 ppszBuffer, pcbBuffer);
1135}
1136
1137/**
1138 * Returns the total root entries count for a DnD transfer list.
1139 *
1140 * @returns Total number of root entries.
1141 * @param pList Transfer list to return total number of root entries for.
1142 */
1143uint64_t DnDTransferListGetRootCount(PDNDTRANSFERLIST pList)
1144{
1145 AssertPtrReturn(pList, 0);
1146 return pList->cRoots;
1147}
1148
1149/**
1150 * Returns the absolute root path for a DnD transfer list.
1151 *
1152 * @returns Pointer to the root path.
1153 * @param pList Transfer list to return root path for.
1154 */
1155const char *DnDTransferListGetRootPathAbs(PDNDTRANSFERLIST pList)
1156{
1157 AssertPtrReturn(pList, NULL);
1158 return pList->pszPathRootAbs;
1159}
1160
1161/**
1162 * Returns the total transfer object count for a DnD transfer list.
1163 *
1164 * @returns Total number of transfer objects.
1165 * @param pList Transfer list to return total number of transfer objects for.
1166 */
1167uint64_t DnDTransferListObjCount(PDNDTRANSFERLIST pList)
1168{
1169 AssertPtrReturn(pList, 0);
1170 return pList->cObj;
1171}
1172
1173/**
1174 * Returns the total bytes of all handled transfer objects for a DnD transfer list.
1175 *
1176 * @returns VBox status code.
1177 * @param pList Transfer list to return total bytes for.
1178 */
1179uint64_t DnDTransferListObjTotalBytes(PDNDTRANSFERLIST pList)
1180{
1181 AssertPtrReturn(pList, 0);
1182 return pList->cbObjTotal;
1183}
1184
1185/**
1186 * Sets the absolute root path of a transfer list.
1187 *
1188 * @returns VBox status code.
1189 * @param pList Transfer list to set root path for.
1190 * @param pcszRootPathAbs Absolute root path to set.
1191 */
1192static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
1193{
1194 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1195 AssertPtrReturn(pcszRootPathAbs, VERR_INVALID_POINTER);
1196 AssertReturn(pList->pszPathRootAbs == NULL, VERR_WRONG_ORDER); /* Already initialized? */
1197
1198 LogFlowFunc(("pcszRootPathAbs=%s\n", pcszRootPathAbs));
1199
1200 char szRootPath[RTPATH_MAX];
1201 int rc = RTStrCopy(szRootPath, sizeof(szRootPath), pcszRootPathAbs);
1202 if (RT_FAILURE(rc))
1203 return rc;
1204
1205 /* Note: The list's root path is kept in native style, so no conversion needed here. */
1206
1207 RTPathEnsureTrailingSeparatorEx(szRootPath, sizeof(szRootPath), RTPATH_STR_F_STYLE_HOST);
1208
1209 /* Make sure the root path is a directory (and no symlink or stuff). */
1210 RTFSOBJINFO objInfo;
1211 rc = RTPathQueryInfo(szRootPath, &objInfo, RTFSOBJATTRADD_NOTHING);
1212 if (RT_SUCCESS(rc))
1213 {
1214 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode & RTFS_TYPE_MASK))
1215 {
1216 pList->pszPathRootAbs = RTStrDup(szRootPath);
1217 if (pList->pszPathRootAbs)
1218 {
1219 LogFlowFunc(("Root path is '%s'\n", pList->pszPathRootAbs));
1220 }
1221 else
1222 rc = VERR_NO_MEMORY;
1223 }
1224 else
1225 rc = VERR_NOT_A_DIRECTORY;
1226 }
1227
1228 return rc;
1229}
1230
1231/**
1232 * Adds a root entry to a DnD transfer list.
1233 *
1234 * @returns VBox status code.
1235 * @param pList Transfer list to add root entry to.
1236 * @param pcszRoot Root entry to add.
1237 */
1238static int dndTransferListRootEntryAdd(PDNDTRANSFERLIST pList, const char *pcszRoot)
1239{
1240 AssertPtrReturn(pList->pszPathRootAbs, VERR_WRONG_ORDER); /* The list's root path must be set first. */
1241
1242 int rc;
1243
1244 /** @todo Handle / reject double entries. */
1245
1246 /* Get the index pointing to the relative path in relation to set the root path. */
1247 const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
1248 AssertReturn(strlen(pcszRoot) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
1249
1250 PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAllocZ(sizeof(DNDTRANSFERLISTROOT));
1251 if (pRoot)
1252 {
1253 const char *pcszRootIdx = &pcszRoot[idxPathToAdd];
1254
1255 LogFlowFunc(("pcszRoot=%s\n", pcszRootIdx));
1256
1257 pRoot->pszPathRoot = RTStrDup(pcszRootIdx);
1258 if (pRoot->pszPathRoot)
1259 {
1260 RTListAppend(&pList->lstRoot, &pRoot->Node);
1261 pList->cRoots++;
1262
1263 rc = VINF_SUCCESS;
1264 }
1265 else
1266 rc = VERR_NO_MEMORY;
1267
1268 if (RT_FAILURE(rc))
1269 {
1270 RTMemFree(pRoot);
1271 pRoot = NULL;
1272 }
1273 }
1274 else
1275 rc = VERR_NO_MEMORY;
1276
1277 return rc;
1278}
1279
1280/**
1281 * Removes (and destroys) a DnD transfer root entry.
1282 *
1283 * @param pList Transfer list to free root for.
1284 * @param pRootObj Transfer list root to free. The pointer will be invalid after calling.
1285 */
1286static void dndTransferListRootEntryFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj)
1287{
1288 if (!pRootObj)
1289 return;
1290
1291 RTStrFree(pRootObj->pszPathRoot);
1292
1293 RTListNodeRemove(&pRootObj->Node);
1294 RTMemFree(pRootObj);
1295
1296 AssertReturnVoid(pList->cRoots);
1297 pList->cRoots--;
1298}
1299
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette