VirtualBox

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

最後變更 在這個檔案從85371是 85371,由 vboxsync 提交於 5 年 前

DnD: Revamped code to simplify / untangle of internal data handling:

  • C-ifying and renaming classes DnDURIList / DnDURIObject -> DnDTransferList / DnDTransferObject
  • Added testcases for DnDTransferList / DnDTransferObject + DnDPath API
  • Reduced memory footprint
  • Greatly simplified / stripped down internal data flow of Main side
  • More (optional) release logging for further diagnosis

Work in progress.

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

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