VirtualBox

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

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

DnD/TransferList: Fixed double free in dndTransferListObjAdd().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.8 KB
Line 
1/* $Id: DnDTransferList.cpp 85416 2020-07-22 14:48:57Z 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_GUEST_DND
23#include <VBox/GuestHost/DragAndDrop.h>
24
25#include <iprt/dir.h>
26#include <iprt/err.h>
27#include <iprt/file.h>
28#include <iprt/fs.h>
29#include <iprt/mem.h>
30#include <iprt/path.h>
31#include <iprt/string.h>
32#include <iprt/symlink.h>
33#include <iprt/uri.h>
34
35#include <VBox/log.h>
36
37
38/*********************************************************************************************************************************
39* Prototypes *
40*********************************************************************************************************************************/
41static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs);
42
43static int dndTransferListRootAdd(PDNDTRANSFERLIST pList, const char *pcszRoot);
44static void dndTransferListRootFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj);
45
46static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags);
47static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pLstObj);
48
49
50/** The size of the directory entry buffer we're using. */
51#define DNDTRANSFERLIST_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
52
53
54/**
55 * Initializes a transfer list, internal version.
56 *
57 * @returns VBox status code.
58 * @param pList Transfer list to initialize.
59 * @param pcszRootPathAbs Absolute root path to use for this list. Optional and can be NULL.
60 */
61static int dndTransferListInitInternal(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
62{
63 AssertPtrReturn(pList, VERR_INVALID_POINTER);
64 /* pcszRootPathAbs is optional. */
65
66 if (!strlen(pcszRootPathAbs))
67 return VERR_INVALID_PARAMETER;
68
69 if (pList->pszPathRootAbs)
70 return VERR_WRONG_ORDER;
71
72 pList->pszPathRootAbs = NULL;
73
74 RTListInit(&pList->lstRoot);
75 pList->cRoots = 0;
76
77 RTListInit(&pList->lstObj);
78 pList->cObj = 0;
79 pList->cbObjTotal = 0;
80
81 if (pcszRootPathAbs)
82 return dndTransferListSetRootPath(pList, pcszRootPathAbs);
83
84 return VINF_SUCCESS;
85}
86
87/**
88 * Initializes a transfer list.
89 *
90 * @returns VBox status code.
91 * @param pList Transfer list to initialize.
92 * @param pcszRootPathAbs Absolute root path to use for this list. Optional and can be NULL.
93 */
94int DnDTransferListInit(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
95{
96 return dndTransferListInitInternal(pList, pcszRootPathAbs);
97}
98
99/**
100 * Destroys a transfer list.
101 *
102 * @param pList Transfer list to destroy.
103 */
104void DnDTransferListDestroy(PDNDTRANSFERLIST pList)
105{
106 if (!pList)
107 return;
108
109 DnDTransferListReset(pList);
110
111 RTStrFree(pList->pszPathRootAbs);
112 pList->pszPathRootAbs = NULL;
113}
114
115/**
116 * Resets a transfer list.
117 *
118 * Note: Does *not* clear the root path!
119 *
120 * @param pList Transfer list to clear.
121 */
122void DnDTransferListReset(PDNDTRANSFERLIST pList)
123{
124 AssertPtrReturnVoid(pList);
125
126 /* Note: This does not clear the root path! */
127
128 if (!pList->pszPathRootAbs)
129 return;
130
131 PDNDTRANSFERLISTROOT pRootCur, pRootNext;
132 RTListForEachSafe(&pList->lstRoot, pRootCur, pRootNext, DNDTRANSFERLISTROOT, Node)
133 dndTransferListRootFree(pList, pRootCur);
134 Assert(RTListIsEmpty(&pList->lstRoot));
135
136 PDNDTRANSFEROBJECT pObjCur, pObjNext;
137 RTListForEachSafe(&pList->lstObj, pObjCur, pObjNext, DNDTRANSFEROBJECT, Node)
138 dndTransferListObjFree(pList, pObjCur);
139 Assert(RTListIsEmpty(&pList->lstObj));
140
141 Assert(pList->cRoots == 0);
142 Assert(pList->cObj == 0);
143
144 pList->cbObjTotal = 0;
145}
146
147/**
148 * Adds a single transfer object entry to a transfer List.
149 *
150 * @returns VBox status code.
151 * @param pList Transfer list to add entry to.
152 * @param pcszSrcAbs Absolute source path (local) to use.
153 * @param fMode File mode of entry to add.
154 * @param fFlags Transfer list flags to use for appending.
155 */
156static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags)
157{
158 AssertPtrReturn(pList, VERR_INVALID_POINTER);
159 AssertPtrReturn(pcszSrcAbs, VERR_INVALID_POINTER);
160
161 LogFlowFunc(("pcszSrcAbs=%s, fMode=%#x, fFlags=0x%x\n", pcszSrcAbs, fMode, fFlags));
162
163 int rc = VINF_SUCCESS;
164
165 if ( !RTFS_IS_FILE(fMode)
166 && !RTFS_IS_DIRECTORY(fMode))
167 /** @todo Symlinks not allowed. */
168 {
169 rc = VERR_NOT_SUPPORTED;
170 }
171
172 if (RT_SUCCESS(rc))
173 {
174 /* Calculate the path to add as the destination path to our URI object. */
175 const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
176 AssertReturn(strlen(pcszSrcAbs) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
177
178 PDNDTRANSFEROBJECT pObj = (PDNDTRANSFEROBJECT)RTMemAllocZ(sizeof(DNDTRANSFEROBJECT));
179 if (pObj)
180 {
181 pObj = (PDNDTRANSFEROBJECT)RTMemAllocZ(sizeof(DNDTRANSFEROBJECT));
182 if (pObj)
183 {
184 const bool fIsFile = RTFS_IS_FILE(fMode);
185
186 rc = DnDTransferObjectInit(pObj, fIsFile ? DNDTRANSFEROBJTYPE_FILE : DNDTRANSFEROBJTYPE_DIRECTORY,
187 pList->pszPathRootAbs, &pcszSrcAbs[idxPathToAdd]);
188 if (RT_SUCCESS(rc))
189 {
190 if (fIsFile)
191 rc = DnDTransferObjectOpen(pObj,
192 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, /** @todo Add a standard fOpen mode for this list. */
193 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
194 if (RT_SUCCESS(rc))
195 {
196 RTListAppend(&pList->lstObj, &pObj->Node);
197
198 pList->cObj++;
199 if (fIsFile)
200 pList->cbObjTotal += DnDTransferObjectGetSize(pObj);
201
202 if ( fIsFile
203 && !(fFlags & DNDTRANSFERLIST_FLAGS_KEEP_OPEN)) /* Shall we keep the file open while being added to this list? */
204 DnDTransferObjectClose(pObj);
205 }
206
207 if (RT_FAILURE(rc))
208 DnDTransferObjectDestroy(pObj);
209 }
210 }
211 else
212 rc = VERR_NO_MEMORY;
213
214 if (RT_FAILURE(rc))
215 RTMemFree(pObj);
216 }
217 else
218 rc = VERR_NO_MEMORY;
219 }
220
221 if (RT_FAILURE(rc))
222 LogRel(("DnD: Adding entry '%s' of type %#x failed with rc=%Rrc\n", pcszSrcAbs, fMode & RTFS_TYPE_MASK, rc));
223
224 LogFlowFuncLeaveRC(rc);
225 return rc;
226}
227
228/**
229 * Frees an internal DnD transfer list object.
230 *
231 * @param pList Transfer list to free object for.
232 * @param pLstObj transfer list object to free. The pointer will be invalid after calling.
233 */
234static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
235{
236 if (!pObj)
237 return;
238
239 DnDTransferObjectDestroy(pObj);
240 RTListNodeRemove(&pObj->Node);
241 RTMemFree(pObj);
242
243 AssertReturnVoid(pList->cObj);
244 pList->cObj--;
245}
246
247/**
248 * Helper routine for handling adding sub directories.
249 *
250 * @return IPRT status code.
251 * @param pList transfer list to add found entries to.
252 * @param pszDir Pointer to the directory buffer.
253 * @param cchDir The length of pszDir in pszDir.
254 * @param pDirEntry Pointer to the directory entry.
255 * @param fFlags Flags of type DNDTRANSFERLISTFLAGS.
256 */
257static int dndTransferListAppendPathRecursiveSub(PDNDTRANSFERLIST pList,
258 char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
259 DNDTRANSFERLISTFLAGS fFlags)
260
261{
262 Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
263
264 /* Make sure we've got some room in the path, to save us extra work further down. */
265 if (cchDir + 3 >= RTPATH_MAX)
266 return VERR_BUFFER_OVERFLOW;
267
268 /* Open directory. */
269 RTDIR hDir;
270 int rc = RTDirOpen(&hDir, pszDir);
271 if (RT_FAILURE(rc))
272 return rc;
273
274 /* Ensure we've got a trailing slash (there is space for it see above). */
275 if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
276 {
277 pszDir[cchDir++] = RTPATH_SLASH;
278 pszDir[cchDir] = '\0';
279 }
280
281 LogFlowFunc(("pszDir=%s\n", pszDir));
282
283 /*
284 * Process the files and subdirs.
285 */
286 for (;;)
287 {
288 /* Get the next directory. */
289 size_t cbDirEntry = DNDTRANSFERLIST_DIRENTRY_BUF_SIZE;
290 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
291 if (RT_FAILURE(rc))
292 break;
293
294 /* Check length. */
295 if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
296 {
297 rc = VERR_BUFFER_OVERFLOW;
298 break;
299 }
300
301 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
302 {
303 case RTFS_TYPE_SYMLINK:
304 {
305 if (!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS))
306 break;
307 RT_FALL_THRU();
308 }
309 case RTFS_TYPE_DIRECTORY:
310 {
311 if (RTDirEntryExIsStdDotLink(pDirEntry))
312 continue;
313
314 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
315 int rc2 = dndTransferListAppendPathRecursiveSub(pList, pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags);
316 if (RT_SUCCESS(rc))
317 rc = rc2;
318 break;
319 }
320
321 case RTFS_TYPE_FILE:
322 {
323 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
324 rc = dndTransferListObjAdd(pList, pszDir, pDirEntry->Info.Attr.fMode, fFlags);
325 break;
326 }
327
328 default:
329 {
330
331 break;
332 }
333 }
334 }
335
336 if (rc == VERR_NO_MORE_FILES) /* Done reading current directory? */
337 {
338 rc = VINF_SUCCESS;
339 }
340 else if (RT_FAILURE(rc))
341 LogRel(("DnD: Error while adding files recursively, rc=%Rrc\n", rc));
342
343 int rc2 = RTDirClose(hDir);
344 if (RT_FAILURE(rc2))
345 {
346 if (RT_SUCCESS(rc))
347 rc = rc2;
348 }
349
350 return rc;
351}
352
353/**
354 * Appends a native system path recursively by adding these entries as transfer objects.
355 *
356 * @returns VBox status code.
357 * @param pList Transfer list to add found entries to.
358 * @param pcszPathAbs Absolute path to add.
359 * @param fFlags Flags of type DNDTRANSFERLISTFLAGS.
360 */
361static int dndTransferListAppendPathNativeRecursive(PDNDTRANSFERLIST pList,
362 const char *pcszPathAbs, DNDTRANSFERLISTFLAGS fFlags)
363{
364 char szPathAbs[RTPATH_MAX];
365 int rc = RTStrCopy(szPathAbs, sizeof(szPathAbs), pcszPathAbs);
366 if (RT_FAILURE(rc))
367 return rc;
368
369 union
370 {
371 uint8_t abPadding[DNDTRANSFERLIST_DIRENTRY_BUF_SIZE];
372 RTDIRENTRYEX DirEntry;
373 } uBuf;
374 const size_t cchPathAbs = strlen(szPathAbs);
375 if (!cchPathAbs)
376 return VINF_SUCCESS;
377 return dndTransferListAppendPathRecursiveSub(pList, szPathAbs, cchPathAbs, &uBuf.DirEntry, fFlags);
378}
379
380/**
381 * Helper function for appending a local directory to a DnD transfer list.
382 *
383 * @returns VBox status code.
384 * @param pList Transfer list to return total number of root entries for.
385 * @param pszPathAbs Absolute path of directory to append.
386 * @param cbPathAbs Size (in bytes) of absolute path of directory to append.
387 * @param fFlags Transfer list flags to use for appending.
388 */
389static int dndTransferListAppenDirectory(PDNDTRANSFERLIST pList, char* pszPathAbs, size_t cbPathAbs,
390 DNDTRANSFERLISTFLAGS fFlags)
391{
392 RTDIR hDir;
393 int rc = RTDirOpen(&hDir, pszPathAbs);
394 if (RT_FAILURE(rc))
395 return rc;
396
397 const size_t cchPathAbs = RTStrNLen(pszPathAbs, cbPathAbs);
398
399 for (;;)
400 {
401 /* Get the next directory. */
402 RTDIRENTRYEX dirEntry;
403 rc = RTDirReadEx(hDir, &dirEntry, NULL, RTFSOBJATTRADD_UNIX,
404 RTPATH_F_ON_LINK /** @todo No symlinks yet. */);
405 if (RT_SUCCESS(rc))
406 {
407 if (RTDirEntryExIsStdDotLink(&dirEntry))
408 continue;
409
410 /* Check length. */
411 if (dirEntry.cbName + cchPathAbs + 3 >= cbPathAbs)
412 {
413 rc = VERR_BUFFER_OVERFLOW;
414 break;
415 }
416
417 /* Append the directory entry to our absolute path. */
418 memcpy(&pszPathAbs[cchPathAbs], dirEntry.szName, dirEntry.cbName + 1);
419
420 LogFlowFunc(("szName=%s, pszPathAbs=%s\n", dirEntry.szName, pszPathAbs));
421
422 switch (dirEntry.Info.Attr.fMode & RTFS_TYPE_MASK)
423 {
424 case RTFS_TYPE_DIRECTORY:
425 {
426 rc = dndTransferListAppendPathNativeRecursive(pList, pszPathAbs, fFlags);
427 break;
428 }
429
430 case RTFS_TYPE_FILE:
431 {
432 rc = dndTransferListObjAdd(pList, pszPathAbs, dirEntry.Info.Attr.fMode, fFlags);
433 break;
434 }
435
436 default:
437 /* Silently skip everything else. */
438 break;
439 }
440 }
441 else if (rc == VERR_NO_MORE_FILES)
442 {
443 rc = VINF_SUCCESS;
444 break;
445 }
446 else
447 break;
448 }
449
450 return rc;
451}
452
453/**
454 * Appends a native path to a DnD transfer list.
455 *
456 * @returns VBox status code.
457 * @param pList Transfer list to append native path to.
458 * @param pcszPath Path (native) to append.
459 * @param fFlags Transfer list flags to use for appending.
460 */
461static int dndTransferListAppendPathNative(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
462{
463 /* We don't want to have a relative directory here. */
464 if (!RTPathStartsWithRoot(pcszPath))
465 return VERR_INVALID_PARAMETER;
466
467 int rc = DnDPathValidate(pcszPath, false /* fMustExist */);
468 if (RT_FAILURE(rc))
469 return rc;
470
471 char szPathAbs[RTPATH_MAX];
472 rc = RTStrCopy(szPathAbs, sizeof(szPathAbs), pcszPath);
473 if (RT_FAILURE(rc))
474 return rc;
475
476 size_t cchPathAbs = RTStrNLen(szPathAbs, sizeof(szPathAbs));
477 AssertReturn(cchPathAbs, VERR_INVALID_PARAMETER);
478
479 /* Convert path to transport style. */
480 rc = DnDPathConvert(szPathAbs, sizeof(szPathAbs), DNDPATHCONVERT_FLAGS_TRANSPORT);
481 if (RT_SUCCESS(rc))
482 {
483 /* Make sure the path has the same root path as our list. */
484 if (RTPathStartsWith(szPathAbs, pList->pszPathRootAbs))
485 {
486 RTFSOBJINFO objInfo;
487 rc = RTPathQueryInfo(szPathAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
488 if (RT_SUCCESS(rc))
489 {
490 switch (objInfo.Attr.fMode & RTFS_TYPE_MASK)
491 {
492 case RTFS_TYPE_DIRECTORY:
493 if (fFlags & DNDTRANSFERLIST_FLAGS_RECURSIVE)
494 rc = dndTransferListAppenDirectory(pList, szPathAbs, sizeof(szPathAbs), fFlags);
495 break;
496
497 case RTFS_TYPE_FILE:
498 rc = dndTransferListObjAdd(pList, szPathAbs, objInfo.Attr.fMode, fFlags);
499 break;
500
501 default:
502 /* Silently skip everything else. */
503 break;
504 }
505
506 if (RT_SUCCESS(rc))
507 rc = dndTransferListRootAdd(pList, szPathAbs);
508 }
509 }
510 else
511 rc = VERR_INVALID_PARAMETER;
512 }
513
514 if (RT_FAILURE(rc))
515 LogRel(("DnD: Adding native path '%s' failed with rc=%Rrc\n", pcszPath, rc));
516
517 return rc;
518}
519
520/**
521 * Appends an URI path to a DnD transfer list.
522 *
523 * @returns VBox status code.
524 * @param pList Transfer list to append native path to.
525 * @param pcszPath URI path to append.
526 * @param fFlags Transfer list flags to use for appending.
527 */
528static int dndTransferListAppendPathURI(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
529{
530 RT_NOREF(fFlags);
531
532 /* Query the path component of a file URI. If this hasn't a
533 * file scheme, NULL is returned. */
534 char *pszFilePath;
535 int rc = RTUriFilePathEx(pcszPath, RTPATH_STR_F_STYLE_UNIX, &pszFilePath, 0 /*cbPath*/, NULL /*pcchPath*/);
536 if (RT_SUCCESS(rc))
537 {
538 LogFlowFunc(("pcszPath=%s -> pszFilePath=%s\n", pcszPath, pszFilePath));
539 rc = dndTransferListRootAdd(pList, pszFilePath);
540 RTStrFree(pszFilePath);
541
542 }
543
544 if (RT_FAILURE(rc))
545 LogRel(("DnD: Adding URI path '%s' failed with rc=%Rrc\n", pcszPath, rc));
546
547 return rc;
548}
549
550/**
551 * Appends a single path to a transfer list.
552 *
553 * @returns VBox status code. VERR_NOT_SUPPORTED if the path is not supported.
554 * @param pList Transfer list to append to.
555 * @param enmFmt Format of \a pszPaths to append.
556 * @param pcszPath Path to append. Must be part of the list's set root path.
557 * @param fFlags Transfer list flags to use for appending.
558 */
559int DnDTransferListAppendPath(PDNDTRANSFERLIST pList,
560 DNDTRANSFERLISTFMT enmFmt, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
561{
562 AssertPtrReturn(pList, VERR_INVALID_POINTER);
563 AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
564 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
565 AssertReturn(!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS), VERR_NOT_SUPPORTED);
566
567 int rc;
568
569 switch (enmFmt)
570 {
571 case DNDTRANSFERLISTFMT_NATIVE:
572 rc = dndTransferListAppendPathNative(pList, pcszPath, fFlags);
573 break;
574
575 case DNDTRANSFERLISTFMT_URI:
576 rc = dndTransferListAppendPathURI(pList, pcszPath, fFlags);
577 break;
578
579 default:
580 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
581 break; /* Never reached */
582 }
583
584 return rc;
585}
586
587/**
588 * Appends native paths to a transfer list.
589 *
590 * @returns VBox status code.
591 * @param pList Transfer list to append to.
592 * @param enmFmt Format of \a pszPaths to append.
593 * @param pszPaths Buffer of paths to append.
594 * @param cbPaths Size (in bytes) of buffer of paths to append.
595 * @param pcszSeparator Separator string to use.
596 * @param fFlags Transfer list flags to use for appending.
597 */
598int DnDTransferListAppendPathsFromBuffer(PDNDTRANSFERLIST pList,
599 DNDTRANSFERLISTFMT enmFmt, const char *pszPaths, size_t cbPaths,
600 const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
601{
602 AssertPtrReturn(pList, VERR_INVALID_POINTER);
603 AssertPtrReturn(pszPaths, VERR_INVALID_POINTER);
604 AssertReturn(cbPaths, VERR_INVALID_PARAMETER);
605
606 char **papszPaths = NULL;
607 size_t cPaths = 0;
608 int rc = RTStrSplit(pszPaths, cbPaths, pcszSeparator, &papszPaths, &cPaths);
609 if (RT_SUCCESS(rc))
610 rc = DnDTransferListAppendPathsFromArray(pList, enmFmt, papszPaths, cPaths, fFlags);
611
612 for (size_t i = 0; i < cPaths; ++i)
613 RTStrFree(papszPaths[i]);
614 RTMemFree(papszPaths);
615
616 return rc;
617}
618
619/**
620 * Appends paths to a transfer list.
621 *
622 * @returns VBox status code. Will return VERR_INVALID_PARAMETER if a common root path could not be found.
623 * @param pList Transfer list to append path to.
624 * @param enmFmt Format of \a papcszPaths to append.
625 * @param papcszPaths Array of paths to append.
626 * @param cPaths Number of paths in \a papcszPaths to append.
627 * @param fFlags Transfer list flags to use for appending.
628 */
629int DnDTransferListAppendPathsFromArray(PDNDTRANSFERLIST pList,
630 DNDTRANSFERLISTFMT enmFmt,
631 const char * const *papcszPaths, size_t cPaths, DNDTRANSFERLISTFLAGS fFlags)
632{
633 AssertPtrReturn(pList, VERR_INVALID_POINTER);
634 AssertPtrReturn(papcszPaths, VERR_INVALID_POINTER);
635 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
636
637 int rc = VINF_SUCCESS;
638
639 if (!cPaths) /* Nothing to add? Bail out. */
640 return VINF_SUCCESS;
641
642 char **papszPathsTmp = NULL;
643
644 /* If URI data is being handed in, extract the paths first. */
645 if (enmFmt == DNDTRANSFERLISTFMT_URI)
646 {
647 papszPathsTmp = (char **)RTMemAlloc(sizeof(char *) * cPaths);
648 if (papszPathsTmp)
649 {
650 for (size_t i = 0; i < cPaths; i++)
651 papszPathsTmp[i] = RTUriFilePath(papcszPaths[i]);
652 }
653 else
654 rc = VERR_NO_MEMORY;
655 }
656
657 if (RT_FAILURE(rc))
658 return rc;
659
660 /* If we don't have a root path set, try to find the common path of all handed-in paths. */
661 if (!pList->pszPathRootAbs)
662 {
663 /* Can we work on the unmodified, handed-in data or do we need to use our temporary paths? */
664 const char * const *papszPathTmp = enmFmt == DNDTRANSFERLISTFMT_NATIVE
665 ? papcszPaths : papszPathsTmp;
666
667 size_t cchRootPath = 0; /* Length of root path in chars. */
668 if (cPaths > 1)
669 {
670 cchRootPath = RTPathFindCommon(cPaths, papszPathTmp);
671 }
672 else
673 cchRootPath = RTPathParentLength(papszPathTmp[0]);
674
675 if (cchRootPath)
676 {
677 /* Just use the first path in the array as the reference. */
678 char *pszRootPath = RTStrDupN(papszPathTmp[0], cchRootPath);
679 if (pszRootPath)
680 {
681 LogRel2(("DnD: Determined root path is '%s'\n", pszRootPath));
682 rc = dndTransferListInitInternal(pList, pszRootPath);
683 RTStrFree(pszRootPath);
684 }
685 else
686 rc = VERR_NO_MEMORY;
687 }
688 else
689 rc = VERR_INVALID_PARAMETER;
690 }
691
692 if (RT_SUCCESS(rc))
693 {
694 /*
695 * Add all paths to the list.
696 */
697 for (size_t i = 0; i < cPaths; i++)
698 {
699 const char *pcszPath = enmFmt == DNDTRANSFERLISTFMT_NATIVE
700 ? papcszPaths[i] : papszPathsTmp[i];
701 rc = DnDTransferListAppendPath(pList, DNDTRANSFERLISTFMT_NATIVE, pcszPath, fFlags);
702 if (RT_FAILURE(rc))
703 {
704 LogRel(("DnD: Adding path '%s' (format %#x, root '%s') to transfer list failed with %Rrc\n",
705 pcszPath, enmFmt, pList->pszPathRootAbs ? pList->pszPathRootAbs : "<None>", rc));
706 break;
707 }
708 }
709 }
710
711 if (papszPathsTmp)
712 {
713 for (size_t i = 0; i < cPaths; i++)
714 RTStrFree(papszPathsTmp[i]);
715 RTMemFree(papszPathsTmp);
716 }
717
718 LogFlowFuncLeaveRC(rc);
719 return rc;
720}
721
722/**
723 * Returns the first transfer object in a list.
724 *
725 * @returns Pointer to transfer object if found, or NULL if not found.
726 * @param pList Transfer list to get first transfer object from.
727 */
728PDNDTRANSFEROBJECT DnDTransferListObjGetFirst(PDNDTRANSFERLIST pList)
729{
730 AssertPtrReturn(pList, NULL);
731
732 return RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
733}
734
735/**
736 * Removes an object from a transfer list, internal version.
737 *
738 * @param pList Transfer list to remove object from.
739 * @param pObj Object to remove. The object will be free'd and the pointer is invalid after calling.
740 */
741static void dndTransferListObjRemoveInternal(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
742{
743 AssertPtrReturnVoid(pList);
744 AssertPtrReturnVoid(pObj);
745
746 /** @todo Validate if \a pObj is part of \a pList. */
747
748 uint64_t cbSize = DnDTransferObjectGetSize(pObj);
749 Assert(pList->cbObjTotal >= cbSize);
750 pList->cbObjTotal -= cbSize; /* Adjust total size. */
751
752 dndTransferListObjFree(pList, pObj);
753}
754
755/**
756 * Removes an object from a transfer list.
757 *
758 * @param pList Transfer list to remove object from.
759 * @param pObj Object to remove. The object will be free'd and the pointer is invalid after calling.
760 */
761void DnDTransferListObjRemove(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
762{
763 return dndTransferListObjRemoveInternal(pList, pObj);
764}
765
766/**
767 * Removes the first DnD transfer object from a transfer list.
768 *
769 * @param pList Transfer list to remove first entry for.
770 */
771void DnDTransferListObjRemoveFirst(PDNDTRANSFERLIST pList)
772{
773 AssertPtrReturnVoid(pList);
774
775 if (!pList->cObj)
776 return;
777
778 PDNDTRANSFEROBJECT pObj = RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
779 AssertPtr(pObj);
780
781 dndTransferListObjRemoveInternal(pList, pObj);
782}
783
784/**
785 * Returns all root entries of a transfer list as a string.
786 *
787 * @returns VBox status code.
788 * @param pList Transfer list to return root paths for.
789 * @param pcszPathBase Root path to use as a base path. If NULL, the list's absolute root path will be used (if any).
790 * @param pcszSeparator Separator to use for separating the root entries.
791 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
792 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
793 */
794int DnDTransferListGetRootsEx(PDNDTRANSFERLIST pList,
795 DNDTRANSFERLISTFMT enmFmt, const char *pcszPathBase, const char *pcszSeparator,
796 char **ppszBuffer, size_t *pcbBuffer)
797{
798 AssertPtrReturn(pList, VERR_INVALID_POINTER);
799 /* pcszPathBase can be NULL. */
800 AssertPtrReturn(pcszSeparator, VERR_INVALID_POINTER);
801 AssertPtrReturn(ppszBuffer, VERR_INVALID_POINTER);
802 AssertPtrReturn(pcbBuffer, VERR_INVALID_POINTER);
803
804 char *pszString = NULL;
805 size_t cchString = 0;
806
807 const size_t cchSep = RTStrNLen(pcszSeparator, RTPATH_MAX);
808
809 /* Find out which root path to use. */
810 const char *pcszPathRootTmp = pcszPathBase ? pcszPathBase : pList->pszPathRootAbs;
811 /* pcszPathRootTmp can be NULL */
812
813 LogFlowFunc(("Using root path '%s'\n", pcszPathRootTmp ? pcszPathRootTmp : "<None>"));
814
815 int rc = DnDPathValidate(pcszPathRootTmp, false /* fMustExist */);
816 if (RT_FAILURE(rc))
817 return rc;
818
819 char szPath[RTPATH_MAX];
820
821 PDNDTRANSFERLISTROOT pRoot;
822 RTListForEach(&pList->lstRoot, pRoot, DNDTRANSFERLISTROOT, Node)
823 {
824 if (pcszPathRootTmp)
825 {
826 rc = RTStrCopy(szPath, sizeof(szPath), pcszPathRootTmp);
827 AssertRCBreak(rc);
828 cchString += RTStrNLen(pcszPathRootTmp, RTPATH_MAX);
829 }
830
831 rc = RTPathAppend(szPath, sizeof(szPath), pRoot->pszPathRoot);
832 AssertRCBreak(rc);
833
834 if (enmFmt == DNDTRANSFERLISTFMT_URI)
835 {
836 char *pszPathURI = RTUriFileCreate(szPath);
837 AssertPtrBreakStmt(pszPathURI, rc = VERR_NO_MEMORY);
838 rc = RTStrAAppend(&pszString, pszPathURI);
839 cchString += RTStrNLen(pszPathURI, RTPATH_MAX);
840 RTStrFree(pszPathURI);
841 AssertRCBreak(rc);
842 }
843 else /* Native */
844 {
845#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
846 /* Convert paths to native path style. */
847 rc = DnDPathConvert(szPath, sizeof(szPath), DNDPATHCONVERT_FLAGS_TO_DOS);
848#endif
849 if (RT_SUCCESS(rc))
850 {
851 rc = RTStrAAppend(&pszString, szPath);
852 AssertRCBreak(rc);
853
854 cchString += RTStrNLen(szPath, RTPATH_MAX);
855 }
856 }
857
858 rc = RTStrAAppend(&pszString, pcszSeparator);
859 AssertRCBreak(rc);
860
861 cchString += cchSep;
862 }
863
864 if (RT_SUCCESS(rc))
865 {
866 *ppszBuffer = pszString;
867 *pcbBuffer = pszString ? cchString + 1 /* Include termination */ : 0;
868 }
869 else
870 RTStrFree(pszString);
871 return rc;
872}
873
874/**
875 * Returns all root entries for a DnD transfer list.
876 *
877 * Note: Convenience function which uses the default DnD path separator.
878 *
879 * @returns VBox status code.
880 * @param pList Transfer list to return root entries for.
881 * @param enmFmt Which format to use for returning the entries.
882 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
883 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
884 */
885int DnDTransferListGetRoots(PDNDTRANSFERLIST pList,
886 DNDTRANSFERLISTFMT enmFmt, char **ppszBuffer, size_t *pcbBuffer)
887{
888 return DnDTransferListGetRootsEx(pList, enmFmt, "" /* pcszPathRoot */, DND_PATH_SEPARATOR,
889 ppszBuffer, pcbBuffer);
890}
891
892/**
893 * Returns the total root entries count for a DnD transfer list.
894 *
895 * @returns Total number of root entries.
896 * @param pList Transfer list to return total number of root entries for.
897 */
898uint64_t DnDTransferListGetRootCount(PDNDTRANSFERLIST pList)
899{
900 AssertPtrReturn(pList, 0);
901 return pList->cRoots;
902}
903
904/**
905 * Returns the absolute root path for a DnD transfer list.
906 *
907 * @returns Pointer to the root path.
908 * @param pList Transfer list to return root path for.
909 */
910const char *DnDTransferListGetRootPathAbs(PDNDTRANSFERLIST pList)
911{
912 AssertPtrReturn(pList, NULL);
913 return pList->pszPathRootAbs;
914}
915
916/**
917 * Returns the total transfer object count for a DnD transfer list.
918 *
919 * @returns Total number of transfer objects.
920 * @param pList Transfer list to return total number of transfer objects for.
921 */
922uint64_t DnDTransferListObjCount(PDNDTRANSFERLIST pList)
923{
924 AssertPtrReturn(pList, 0);
925 return pList->cObj;
926}
927
928/**
929 * Returns the total bytes of all handled transfer objects for a DnD transfer list.
930 *
931 * @returns VBox status code.
932 * @param pList Transfer list to return total bytes for.
933 */
934uint64_t DnDTransferListObjTotalBytes(PDNDTRANSFERLIST pList)
935{
936 AssertPtrReturn(pList, 0);
937 return pList->cbObjTotal;
938}
939
940/**
941 * Sets the absolute root path of a transfer list.
942 *
943 * @returns VBox status code.
944 * @param pList Transfer list to set root path for.
945 * @param pcszRootPathAbs Absolute root path to set.
946 */
947static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
948{
949 AssertPtrReturn(pList, VERR_INVALID_POINTER);
950 AssertPtrReturn(pcszRootPathAbs, VERR_INVALID_POINTER);
951 AssertReturn(pList->pszPathRootAbs == NULL, VERR_WRONG_ORDER); /* Already initialized? */
952
953 LogFlowFunc(("pcszRootPathAbs=%s\n", pcszRootPathAbs));
954
955 char szRootPath[RTPATH_MAX];
956 int rc = RTStrCopy(szRootPath, sizeof(szRootPath), pcszRootPathAbs);
957 if (RT_FAILURE(rc))
958 return rc;
959
960 /* Note: The list's root path is kept in native style, so no conversion needed here. */
961
962 RTPathEnsureTrailingSeparatorEx(szRootPath, sizeof(szRootPath), RTPATH_STR_F_STYLE_HOST);
963
964 pList->pszPathRootAbs = RTStrDup(szRootPath);
965 if (pList->pszPathRootAbs)
966 {
967 LogFlowFunc(("Root path is '%s'\n", pList->pszPathRootAbs));
968 }
969 else
970 rc = VERR_NO_MEMORY;
971
972 return rc;
973}
974
975/**
976 * Adds a root entry to a DnD transfer list.
977 *
978 * @returns VBox status code.
979 * @param pList Transfer list to add root entry to.
980 * @param pcszRoot Root entry to add.
981 */
982static int dndTransferListRootAdd(PDNDTRANSFERLIST pList, const char *pcszRoot)
983{
984 int rc;
985
986 /** @todo Handle / reject double entries. */
987
988 /* Calculate the path to add as the destination path to our URI object. */
989 const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
990 AssertReturn(strlen(pcszRoot) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
991
992 PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAllocZ(sizeof(DNDTRANSFERLISTROOT));
993 if (pRoot)
994 {
995 const char *pcszRootIdx = &pcszRoot[idxPathToAdd];
996
997 LogFlowFunc(("pcszRoot=%s\n", pcszRootIdx));
998
999 pRoot->pszPathRoot = RTStrDup(pcszRootIdx);
1000 if (pRoot->pszPathRoot)
1001 {
1002 RTListAppend(&pList->lstRoot, &pRoot->Node);
1003 pList->cRoots++;
1004
1005 rc = VINF_SUCCESS;
1006 }
1007 else
1008 rc = VERR_NO_MEMORY;
1009
1010 if (RT_FAILURE(rc))
1011 {
1012 RTMemFree(pRoot);
1013 pRoot = NULL;
1014 }
1015 }
1016 else
1017 rc = VERR_NO_MEMORY;
1018
1019 return rc;
1020}
1021
1022/**
1023 * Removes (and destroys) a DnD transfer root entry.
1024 *
1025 * @param pList Transfer list to free root for.
1026 * @param pRootObj Transfer list root to free. The pointer will be invalid after calling.
1027 */
1028static void dndTransferListRootFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj)
1029{
1030 if (!pRootObj)
1031 return;
1032
1033 RTStrFree(pRootObj->pszPathRoot);
1034
1035 RTListNodeRemove(&pRootObj->Node);
1036 RTMemFree(pRootObj);
1037
1038 AssertReturnVoid(pList->cRoots);
1039 pList->cRoots--;
1040}
1041
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