VirtualBox

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

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

DnD: Renaming -- DND_FORMATS_SEPARATOR -> DND_FORMATS_SEPARATOR_STR and DND_PATH_SEPARATOR -> DND_PATH_SEPARATOR_STR.

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