VirtualBox

source: vbox/trunk/src/VBox/GuestHost/DragAndDrop/DnDTransferObject.cpp@ 94615

Last change on this file since 94615 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.8 KB
Line 
1/* $Id: DnDTransferObject.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * DnD - Transfer object implemenation for handling creation/reading/writing to files and directories on host or guest side.
4 */
5
6/*
7 * Copyright (C) 2014-2022 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/path.h>
30#include <iprt/string.h>
31#include <iprt/uri.h>
32
33#include <VBox/log.h>
34
35
36/*********************************************************************************************************************************
37* Prototypes *
38*********************************************************************************************************************************/
39static void dndTransferObjectCloseInternal(PDNDTRANSFEROBJECT pObj);
40static int dndTransferObjectQueryInfoInternal(PDNDTRANSFEROBJECT pObj);
41
42
43/**
44 * Initializes the object, internal version.
45 *
46 * @returns VBox status code.
47 * @param pObj DnD transfer object to initialize.
48 */
49static int dndTransferObjectInitInternal(PDNDTRANSFEROBJECT pObj)
50{
51 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
52
53 pObj->enmType = DNDTRANSFEROBJTYPE_UNKNOWN;
54 pObj->idxDst = 0;
55 pObj->pszPath = NULL;
56
57 RT_ZERO(pObj->u);
58
59 return VINF_SUCCESS;
60}
61
62/**
63 * Initializes the object.
64 *
65 * @returns VBox status code.
66 * @param pObj DnD transfer object to initialize.
67 */
68int DnDTransferObjectInit(PDNDTRANSFEROBJECT pObj)
69{
70 return dndTransferObjectInitInternal(pObj);
71}
72
73/**
74 * Initializes the object with an expected object type and file path.
75 *
76 * @returns VBox status code.
77 * @param pObj DnD transfer object to initialize.
78 * @param enmType Type we expect this object to be.
79 * @param pcszPathSrcAbs Absolute source (local) path of file this object represents. Can be empty (e.g. for root stuff).
80 * @param pcszPathDst Relative path of file this object represents at the destination.
81 * Together with \a pcszPathSrcAbs this represents the complete absolute local path.
82 */
83int DnDTransferObjectInitEx(PDNDTRANSFEROBJECT pObj,
84 DNDTRANSFEROBJTYPE enmType, const char *pcszPathSrcAbs, const char *pcszPathDst)
85{
86 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
87 AssertReturn(pObj->enmType == DNDTRANSFEROBJTYPE_UNKNOWN, VERR_WRONG_ORDER); /* Already initialized? */
88 /* pcszPathSrcAbs can be empty. */
89 AssertPtrReturn(pcszPathDst, VERR_INVALID_POINTER);
90
91 int rc = dndTransferObjectInitInternal(pObj);
92 AssertRCReturn(rc, rc);
93
94 rc = DnDPathValidate(pcszPathDst, false /* Does not need to exist */);
95 AssertRCReturn(rc, rc);
96
97 char szPath[RTPATH_MAX + 1];
98
99 /* Save the index (in characters) where the first destination segment starts. */
100 if ( pcszPathSrcAbs
101 && RTStrNLen(pcszPathSrcAbs, RTSTR_MAX))
102 {
103 rc = DnDPathValidate(pcszPathSrcAbs, false /* Does not need to exist */);
104 if (RT_FAILURE(rc))
105 return rc;
106
107 rc = RTStrCopy(szPath, sizeof(szPath), pcszPathSrcAbs);
108 if (RT_SUCCESS(rc))
109 rc = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath)) == 0 ? VERR_BUFFER_OVERFLOW : VINF_SUCCESS;
110
111 /* Save the index (in characters) where the destination part starts. */
112 pObj->idxDst = (uint16_t)RTStrNLen(szPath, RTPATH_MAX);
113 AssertReturn(pObj->idxDst <= RTPATH_MAX, VERR_INVALID_PARAMETER);
114 }
115 else
116 {
117 szPath[0] = '\0'; /* Init empty string. */
118 pObj->idxDst = 0;
119 }
120
121 if (RT_FAILURE(rc))
122 return rc;
123
124 /* Append the destination part. */
125 rc = RTPathAppend(szPath, sizeof(szPath), pcszPathDst);
126 if ( RT_SUCCESS(rc)
127 && enmType == DNDTRANSFEROBJTYPE_DIRECTORY)
128 rc = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath)) == 0 ? VERR_BUFFER_OVERFLOW : VINF_SUCCESS;
129
130 if (RT_FAILURE(rc))
131 return rc;
132
133 pObj->pszPath = RTStrDup(szPath);
134 if (!pObj->pszPath)
135 return VERR_NO_MEMORY;
136
137 /* Convert paths into transport format. */
138 rc = DnDPathConvert(pObj->pszPath, strlen(pObj->pszPath), DNDPATHCONVERT_FLAGS_TRANSPORT);
139 if (RT_FAILURE(rc))
140 {
141 RTStrFree(pObj->pszPath);
142 pObj->pszPath = NULL;
143 return rc;
144 }
145
146 LogFlowFunc(("enmType=%RU32, pcszPathSrcAbs=%s, pcszPathDst=%s -> pszPath=%s\n",
147 enmType, pcszPathSrcAbs, pcszPathDst, pObj->pszPath));
148
149 pObj->enmType = enmType;
150
151 return VINF_SUCCESS;
152}
153
154/**
155 * Destroys a DnD transfer object.
156 *
157 * @param pObj DnD transfer object to destroy.
158 */
159void DnDTransferObjectDestroy(PDNDTRANSFEROBJECT pObj)
160{
161 if (!pObj)
162 return;
163
164 DnDTransferObjectReset(pObj);
165}
166
167/**
168 * Closes the object's internal handles (to files / ...).
169 *
170 * @param pObj DnD transfer object to close internally.
171 */
172static void dndTransferObjectCloseInternal(PDNDTRANSFEROBJECT pObj)
173{
174 AssertPtrReturnVoid(pObj);
175
176 int rc;
177
178 LogRel2(("DnD: Closing '%s'\n", pObj->pszPath));
179
180 switch (pObj->enmType)
181 {
182 case DNDTRANSFEROBJTYPE_FILE:
183 {
184 if (RTFileIsValid(pObj->u.File.hFile))
185 {
186 rc = RTFileClose(pObj->u.File.hFile);
187 if (RT_SUCCESS(rc))
188 {
189 pObj->u.File.hFile = NIL_RTFILE;
190 RT_ZERO(pObj->u.File.objInfo);
191 }
192 else
193 LogRel(("DnD: Closing file '%s' failed with %Rrc\n", pObj->pszPath, rc));
194 }
195 break;
196 }
197
198 case DNDTRANSFEROBJTYPE_DIRECTORY:
199 {
200 if (RTDirIsValid(pObj->u.Dir.hDir))
201 {
202 rc = RTDirClose(pObj->u.Dir.hDir);
203 if (RT_SUCCESS(rc))
204 {
205 pObj->u.Dir.hDir = NIL_RTDIR;
206 RT_ZERO(pObj->u.Dir.objInfo);
207 }
208 else
209 LogRel(("DnD: Closing directory '%s' failed with %Rrc\n", pObj->pszPath, rc));
210 }
211 break;
212 }
213
214 default:
215 break;
216 }
217
218 /** @todo Return rc. */
219}
220
221/**
222 * Closes the object.
223 * This also closes the internal handles associated with the object (to files / ...).
224 *
225 * @param pObj DnD transfer object to close.
226 */
227void DnDTransferObjectClose(PDNDTRANSFEROBJECT pObj)
228{
229 AssertPtrReturnVoid(pObj);
230
231 dndTransferObjectCloseInternal(pObj);
232}
233
234/**
235 * Returns the absolute source path of the object.
236 *
237 * @return Absolute source path of the object.
238 * @param pObj DnD transfer object to get source path for.
239 */
240const char *DnDTransferObjectGetSourcePath(PDNDTRANSFEROBJECT pObj)
241{
242 AssertPtrReturn(pObj, NULL);
243 return pObj->pszPath;
244}
245
246/**
247 * Returns the (relative) destination path of the object, in transport style.
248 *
249 * @return Relative destination path of the object, or NULL if not set.
250 * @param pObj DnD transfer object to get destination path for.
251 */
252const char *DnDTransferObjectGetDestPath(PDNDTRANSFEROBJECT pObj)
253{
254 AssertPtrReturn(pObj, NULL);
255
256 if (!pObj->pszPath)
257 return NULL;
258
259 AssertReturn(strlen(pObj->pszPath) >= pObj->idxDst, NULL);
260
261 return &pObj->pszPath[pObj->idxDst];
262}
263
264/**
265 * Returns the (relative) destination path of the object, extended version.
266 *
267 * @return VBox status code, or VERR_NOT_FOUND if not initialized yet.
268 * @param pObj DnD transfer object to get destination path for.
269 * @param enmStyle Which path style to return.
270 * @param pszBuf Where to store the path.
271 * @param cbBuf Size (in bytes) where to store the path.
272 */
273int DnDTransferObjectGetDestPathEx(PDNDTRANSFEROBJECT pObj, DNDTRANSFEROBJPATHSTYLE enmStyle, char *pszBuf, size_t cbBuf)
274{
275 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
276 AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
277 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
278
279 if (!pObj->pszPath)
280 return VERR_NOT_FOUND;
281
282 AssertReturn(strlen(pObj->pszPath) >= pObj->idxDst, VERR_INTERNAL_ERROR);
283
284 int rc = RTStrCopy(pszBuf, cbBuf, &pObj->pszPath[pObj->idxDst]);
285 if ( RT_SUCCESS(rc)
286 && enmStyle == DNDTRANSFEROBJPATHSTYLE_DOS)
287 rc = DnDPathConvert(pszBuf, cbBuf, DNDPATHCONVERT_FLAGS_TO_DOS);
288
289 return rc;
290}
291
292/**
293 * Returns the directory / file mode of the object.
294 *
295 * @return File / directory mode.
296 * @param pObj DnD transfer object to get directory / file mode for.
297 */
298RTFMODE DnDTransferObjectGetMode(PDNDTRANSFEROBJECT pObj)
299{
300 AssertPtrReturn(pObj, 0);
301
302 switch (pObj->enmType)
303 {
304 case DNDTRANSFEROBJTYPE_FILE:
305 return pObj->u.File.objInfo.Attr.fMode;
306
307 case DNDTRANSFEROBJTYPE_DIRECTORY:
308 return pObj->u.Dir.objInfo.Attr.fMode;
309
310 default:
311 break;
312 }
313
314 return 0;
315}
316
317/**
318 * Returns the bytes already processed (read / written).
319 *
320 * Note: Only applies if the object is of type DnDTransferObjectType_File.
321 *
322 * @return Bytes already processed (read / written).
323 * @param pObj DnD transfer object to get processed bytes for.
324 */
325uint64_t DnDTransferObjectGetProcessed(PDNDTRANSFEROBJECT pObj)
326{
327 if (pObj->enmType == DNDTRANSFEROBJTYPE_FILE)
328 return pObj->u.File.cbProcessed;
329
330 return 0;
331}
332
333/**
334 * Returns the file's logical size (in bytes).
335 *
336 * Note: Only applies if the object is of type DnDTransferObjectType_File.
337 *
338 * @return The file's logical size (in bytes).
339 * @param pObj DnD transfer object to get size for.
340 */
341uint64_t DnDTransferObjectGetSize(PDNDTRANSFEROBJECT pObj)
342{
343 if (pObj->enmType == DNDTRANSFEROBJTYPE_FILE)
344 return pObj->u.File.cbToProcess;
345
346 return 0;
347}
348
349/**
350 * Returns the object's type.
351 *
352 * @return The object's type.
353 * @param pObj DnD transfer object to get type for.
354 */
355DNDTRANSFEROBJTYPE DnDTransferObjectGetType(PDNDTRANSFEROBJECT pObj)
356{
357 return pObj->enmType;
358}
359
360/**
361 * Returns whether the processing of the object is complete or not.
362 * For file objects this means that all bytes have been processed.
363 *
364 * @return True if complete, False if not.
365 * @param pObj DnD transfer object to get completion status for.
366 */
367bool DnDTransferObjectIsComplete(PDNDTRANSFEROBJECT pObj)
368{
369 bool fComplete;
370
371 switch (pObj->enmType)
372 {
373 case DNDTRANSFEROBJTYPE_FILE:
374 Assert(pObj->u.File.cbProcessed <= pObj->u.File.cbToProcess);
375 fComplete = pObj->u.File.cbProcessed == pObj->u.File.cbToProcess;
376 break;
377
378 case DNDTRANSFEROBJTYPE_DIRECTORY:
379 fComplete = true;
380 break;
381
382 default:
383 fComplete = true;
384 break;
385 }
386
387 return fComplete;
388}
389
390/**
391 * Returns whether the object is in an open state or not.
392 * @param pObj DnD transfer object to get open status for.
393 */
394bool DnDTransferObjectIsOpen(PDNDTRANSFEROBJECT pObj)
395{
396 switch (pObj->enmType)
397 {
398 case DNDTRANSFEROBJTYPE_FILE: return RTFileIsValid(pObj->u.File.hFile);
399 case DNDTRANSFEROBJTYPE_DIRECTORY: return RTDirIsValid(pObj->u.Dir.hDir);
400 default: break;
401 }
402
403 return false;
404}
405
406/**
407 * Open the object with a specific file type, and, depending on the type, specifying additional parameters.
408 *
409 * @return IPRT status code.
410 * @param pObj DnD transfer object to open.
411 * @param fOpen Open mode to use; only valid for file objects.
412 * @param fMode File mode to set; only valid for file objects. Depends on fOpen and and can be 0.
413 * @param fFlags Additional DnD transfer object flags.
414 */
415int DnDTransferObjectOpen(PDNDTRANSFEROBJECT pObj, uint64_t fOpen, RTFMODE fMode, DNDTRANSFEROBJECTFLAGS fFlags)
416{
417 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
418 AssertReturn(fOpen, VERR_INVALID_FLAGS);
419 /* fMode is optional. */
420 AssertReturn(!(fFlags & ~DNDTRANSFEROBJECT_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
421 RT_NOREF1(fFlags);
422
423 int rc = VINF_SUCCESS;
424
425 LogFlowFunc(("pszPath=%s, fOpen=0x%x, fMode=0x%x, fFlags=0x%x\n", pObj->pszPath, fOpen, fMode, fFlags));
426
427 switch (pObj->enmType)
428 {
429 case DNDTRANSFEROBJTYPE_FILE:
430 {
431 LogRel2(("DnD: Opening file '%s'\n", pObj->pszPath));
432
433 /*
434 * Open files on the source with RTFILE_O_DENY_WRITE to prevent races
435 * where the OS writes to the file while the destination side transfers
436 * it over.
437 */
438 rc = RTFileOpen(&pObj->u.File.hFile, pObj->pszPath, fOpen);
439 if (RT_SUCCESS(rc))
440 {
441 if ( (fOpen & RTFILE_O_WRITE) /* Only set the file mode on write. */
442 && fMode /* Some file mode to set specified? */)
443 {
444 rc = RTFileSetMode(pObj->u.File.hFile, fMode);
445 if (RT_FAILURE(rc))
446 LogRel(("DnD: Setting mode %#x for file '%s' failed with %Rrc\n", fMode, pObj->pszPath, rc));
447 }
448 else if (fOpen & RTFILE_O_READ)
449 {
450 rc = dndTransferObjectQueryInfoInternal(pObj);
451 }
452 }
453 else
454 LogRel(("DnD: Opening file '%s' failed with %Rrc\n", pObj->pszPath, rc));
455
456 if (RT_SUCCESS(rc))
457 {
458 LogFlowFunc(("File cbObject=%RU64, fMode=0x%x\n",
459 pObj->u.File.objInfo.cbObject, pObj->u.File.objInfo.Attr.fMode));
460 pObj->u.File.cbToProcess = pObj->u.File.objInfo.cbObject;
461 pObj->u.File.cbProcessed = 0;
462 }
463
464 break;
465 }
466
467 case DNDTRANSFEROBJTYPE_DIRECTORY:
468 {
469 LogRel2(("DnD: Opening directory '%s'\n", pObj->pszPath));
470
471 rc = RTDirOpen(&pObj->u.Dir.hDir, pObj->pszPath);
472 if (RT_SUCCESS(rc))
473 {
474 rc = dndTransferObjectQueryInfoInternal(pObj);
475 }
476 else
477 LogRel(("DnD: Opening directory '%s' failed with %Rrc\n", pObj->pszPath, rc));
478 break;
479 }
480
481 default:
482 rc = VERR_NOT_IMPLEMENTED;
483 break;
484 }
485
486 LogFlowFuncLeaveRC(rc);
487 return rc;
488}
489
490/**
491 * Queries information about the object using a specific view, internal version.
492 *
493 * @return IPRT status code.
494 * @param pObj DnD transfer object to query info for.
495 */
496static int dndTransferObjectQueryInfoInternal(PDNDTRANSFEROBJECT pObj)
497{
498 int rc;
499
500 switch (pObj->enmType)
501 {
502 case DNDTRANSFEROBJTYPE_FILE:
503 AssertMsgReturn(RTFileIsValid(pObj->u.File.hFile), ("Object has invalid file handle\n"), VERR_INVALID_STATE);
504 rc = RTFileQueryInfo(pObj->u.File.hFile, &pObj->u.File.objInfo, RTFSOBJATTRADD_NOTHING);
505 break;
506
507 case DNDTRANSFEROBJTYPE_DIRECTORY:
508 AssertMsgReturn(RTDirIsValid(pObj->u.Dir.hDir), ("Object has invalid directory handle\n"), VERR_INVALID_STATE);
509 rc = RTDirQueryInfo(pObj->u.Dir.hDir, &pObj->u.Dir.objInfo, RTFSOBJATTRADD_NOTHING);
510 break;
511
512 default:
513 rc = VERR_NOT_IMPLEMENTED;
514 break;
515 }
516
517 if (RT_FAILURE(rc))
518 LogRel(("DnD: Querying information for '%s' failed with %Rrc\n", pObj->pszPath, rc));
519
520 return rc;
521}
522
523/**
524 * Queries information about the object using a specific view.
525 *
526 * @return IPRT status code.
527 * @param pObj DnD transfer object to query info for.
528 */
529int DnDTransferObjectQueryInfo(PDNDTRANSFEROBJECT pObj)
530{
531 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
532 return dndTransferObjectQueryInfoInternal(pObj);
533}
534
535/**
536 * Reads data from the object. Only applies to files objects.
537 *
538 * @return IPRT status code.
539 * @param pObj DnD transfer object to read data from.
540 * @param pvBuf Buffer where to store the read data.
541 * @param cbBuf Size (in bytes) of the buffer.
542 * @param pcbRead Pointer where to store how many bytes were read. Optional.
543 */
544int DnDTransferObjectRead(PDNDTRANSFEROBJECT pObj, void *pvBuf, size_t cbBuf, uint32_t *pcbRead)
545{
546 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
547 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
548 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
549 /* pcbRead is optional. */
550
551 size_t cbRead = 0;
552
553 int rc;
554 switch (pObj->enmType)
555 {
556 case DNDTRANSFEROBJTYPE_FILE:
557 {
558 rc = RTFileRead(pObj->u.File.hFile, pvBuf, cbBuf, &cbRead);
559 if (RT_SUCCESS(rc))
560 {
561 pObj->u.File.cbProcessed += cbRead;
562 Assert(pObj->u.File.cbProcessed <= pObj->u.File.cbToProcess);
563
564 /* End of file reached or error occurred? */
565 if ( pObj->u.File.cbToProcess
566 && pObj->u.File.cbProcessed == pObj->u.File.cbToProcess)
567 {
568 rc = VINF_EOF;
569 }
570 }
571 else
572 LogRel(("DnD: Reading from file '%s' failed with %Rrc\n", pObj->pszPath, rc));
573 break;
574 }
575
576 case DNDTRANSFEROBJTYPE_DIRECTORY:
577 {
578 rc = VINF_SUCCESS;
579 break;
580 }
581
582 default:
583 rc = VERR_NOT_IMPLEMENTED;
584 break;
585 }
586
587 if (RT_SUCCESS(rc))
588 {
589 if (pcbRead)
590 *pcbRead = (uint32_t)cbRead;
591 }
592
593 LogFlowFunc(("Returning cbRead=%zu, rc=%Rrc\n", cbRead, rc));
594 return rc;
595}
596
597/**
598 * Resets the object's state and closes all related handles.
599 *
600 * @param pObj DnD transfer object to reset.
601 */
602void DnDTransferObjectReset(PDNDTRANSFEROBJECT pObj)
603{
604 AssertPtrReturnVoid(pObj);
605
606 LogFlowFuncEnter();
607
608 dndTransferObjectCloseInternal(pObj);
609
610 pObj->enmType = DNDTRANSFEROBJTYPE_UNKNOWN;
611 pObj->idxDst = 0;
612
613 RTStrFree(pObj->pszPath);
614 pObj->pszPath = NULL;
615
616 RT_ZERO(pObj->u);
617}
618
619/**
620 * Sets the bytes to process by the object.
621 *
622 * Note: Only applies if the object is of type DnDTransferObjectType_File.
623 *
624 * @return IPRT return code.
625 * @param pObj DnD transfer object to set size for.
626 * @param cbSize Size (in bytes) to process.
627 */
628int DnDTransferObjectSetSize(PDNDTRANSFEROBJECT pObj, uint64_t cbSize)
629{
630 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
631 AssertReturn(pObj->enmType == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER);
632
633 /** @todo Implement sparse file support here. */
634
635 pObj->u.File.cbToProcess = cbSize;
636 return VINF_SUCCESS;
637}
638
639/**
640 * Writes data to an object. Only applies to file objects.
641 *
642 * @return IPRT status code.
643 * @param pObj DnD transfer object to write to.
644 * @param pvBuf Buffer of data to write.
645 * @param cbBuf Size (in bytes) of data to write.
646 * @param pcbWritten Pointer where to store how many bytes were written. Optional.
647 */
648int DnDTransferObjectWrite(PDNDTRANSFEROBJECT pObj, const void *pvBuf, size_t cbBuf, uint32_t *pcbWritten)
649{
650 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
651 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
652 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
653 /* pcbWritten is optional. */
654
655 size_t cbWritten = 0;
656
657 int rc;
658 switch (pObj->enmType)
659 {
660 case DNDTRANSFEROBJTYPE_FILE:
661 {
662 rc = RTFileWrite(pObj->u.File.hFile, pvBuf, cbBuf, &cbWritten);
663 if (RT_SUCCESS(rc))
664 {
665 pObj->u.File.cbProcessed += cbWritten;
666 }
667 else
668 LogRel(("DnD: Writing to file '%s' failed with %Rrc\n", pObj->pszPath, rc));
669 break;
670 }
671
672 case DNDTRANSFEROBJTYPE_DIRECTORY:
673 {
674 rc = VINF_SUCCESS;
675 break;
676 }
677
678 default:
679 rc = VERR_NOT_IMPLEMENTED;
680 break;
681 }
682
683 if (RT_SUCCESS(rc))
684 {
685 if (pcbWritten)
686 *pcbWritten = (uint32_t)cbWritten;
687 }
688
689 LogFlowFunc(("Returning cbWritten=%zu, rc=%Rrc\n", cbWritten, rc));
690 return rc;
691}
692
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