VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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