VirtualBox

source: vbox/trunk/src/VBox/GuestHost/DragAndDrop/DnDURIObject.cpp@ 85243

Last change on this file since 85243 was 85031, checked in by vboxsync, 5 years ago

DnD/URIObject: Follow-up fixes for r138893 + r138894.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.8 KB
Line 
1/* $Id: DnDURIObject.cpp 85031 2020-07-01 15:21:23Z vboxsync $ */
2/** @file
3 * DnD - URI object class. 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.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/uri.h>
31
32#include <VBox/log.h>
33
34
35DnDURIObject::DnDURIObject(Type enmType /* = Type_Unknown */, const RTCString &strPathAbs /* = "" */)
36 : m_enmType(Type_Unknown)
37{
38 if ( enmType != Type_Unknown
39 && strPathAbs.isNotEmpty())
40 {
41 int rc2 = Init(enmType, strPathAbs);
42 AssertRC(rc2);
43 }
44}
45
46DnDURIObject::~DnDURIObject(void)
47{
48 closeInternal();
49}
50
51/**
52 * Closes the object's internal handles (to files / ...).
53 */
54void DnDURIObject::closeInternal(void)
55{
56 LogFlowThisFuncEnter();
57
58 int rc;
59
60 switch (m_enmType)
61 {
62 case Type_File:
63 {
64 if (RTFileIsValid(u.File.hFile))
65 {
66 LogRel2(("DnD: Closing file '%s'\n", m_strPathAbs.c_str()));
67
68 rc = RTFileClose(u.File.hFile);
69 if (RT_SUCCESS(rc))
70 {
71 u.File.hFile = NIL_RTFILE;
72 RT_ZERO(u.File.objInfo);
73 }
74 else
75 LogRel(("DnD: Closing file '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
76 }
77 break;
78 }
79
80 case Type_Directory:
81 {
82 if (RTDirIsValid(u.Dir.hDir))
83 {
84 LogRel2(("DnD: Closing directory '%s'\n", m_strPathAbs.c_str()));
85
86 rc = RTDirClose(u.Dir.hDir);
87 if (RT_SUCCESS(rc))
88 {
89 u.Dir.hDir = NIL_RTDIR;
90 RT_ZERO(u.Dir.objInfo);
91 }
92 else
93 LogRel(("DnD: Closing directory '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
94 }
95 break;
96 }
97
98 default:
99 break;
100 }
101
102 /** @todo Return rc. */
103}
104
105/**
106 * Closes the object.
107 * This also closes the internal handles associated with the object (to files / ...).
108 */
109void DnDURIObject::Close(void)
110{
111 closeInternal();
112}
113
114/**
115 * Returns the directory / file mode of the object.
116 *
117 * @return File / directory mode.
118 */
119RTFMODE DnDURIObject::GetMode(void) const
120{
121 switch (m_enmType)
122 {
123 case Type_File:
124 return u.File.objInfo.Attr.fMode;
125
126 case Type_Directory:
127 return u.Dir.objInfo.Attr.fMode;
128
129 default:
130 break;
131 }
132
133 AssertFailed();
134 return 0;
135}
136
137/**
138 * Returns the bytes already processed (read / written).
139 *
140 * Note: Only applies if the object is of type DnDURIObject::Type_File.
141 *
142 * @return Bytes already processed (read / written).
143 */
144uint64_t DnDURIObject::GetProcessed(void) const
145{
146 if (m_enmType == Type_File)
147 return u.File.cbProcessed;
148
149 return 0;
150}
151
152/**
153 * Returns the file's logical size (in bytes).
154 *
155 * Note: Only applies if the object is of type DnDURIObject::Type_File.
156 *
157 * @return The file's logical size (in bytes).
158 */
159uint64_t DnDURIObject::GetSize(void) const
160{
161 if (m_enmType == Type_File)
162 return u.File.cbToProcess;
163
164 return 0;
165}
166
167/**
168 * Initializes the object with an expected object type and file path.
169 *
170 * @returns VBox status code.
171 * @param enmType Type we expect this object to be.
172 * @param strPathAbs Absolute path of file this object represents. Optional.
173 */
174int DnDURIObject::Init(Type enmType, const RTCString &strPathAbs /* = */)
175{
176 AssertReturn(m_enmType == Type_Unknown, VERR_WRONG_ORDER);
177
178 int rc;
179
180 switch (enmType)
181 {
182 case Type_File:
183 {
184 u.File.hFile = NIL_RTFILE;
185 break;
186 }
187
188 case Type_Directory:
189 {
190 u.Dir.hDir = NIL_RTDIR;
191 break;
192 }
193
194 default:
195 break;
196 }
197
198 if (enmType != Type_Unknown)
199 {
200 AssertReturn(strPathAbs.isNotEmpty(), VERR_INVALID_PARAMETER);
201 RTCString strPathAbsCopy = strPathAbs;
202 rc = DnDPathConvert(strPathAbsCopy.mutableRaw(), strPathAbsCopy.capacity(), DNDPATHCONVERT_FLAGS_TO_NATIVE);
203 if (RT_SUCCESS(rc))
204 {
205 m_enmType = enmType;
206 m_strPathAbs = strPathAbsCopy;
207 }
208 else
209 LogRel2(("DnD: Absolute file path for guest file on the host is now '%s'\n", strPathAbs.c_str()));
210 }
211 else
212 rc = VERR_INVALID_PARAMETER;
213
214 return rc;
215}
216
217/**
218 * Returns whether the processing of the object is complete or not.
219 * For file objects this means that all bytes have been processed.
220 *
221 * @return True if complete, False if not.
222 */
223bool DnDURIObject::IsComplete(void) const
224{
225 bool fComplete;
226
227 switch (m_enmType)
228 {
229 case Type_File:
230 Assert(u.File.cbProcessed <= u.File.cbToProcess);
231 fComplete = u.File.cbProcessed == u.File.cbToProcess;
232 break;
233
234 case Type_Directory:
235 fComplete = true;
236 break;
237
238 default:
239 fComplete = true;
240 break;
241 }
242
243 return fComplete;
244}
245
246/**
247 * Returns whether the object is in an open state or not.
248 */
249bool DnDURIObject::IsOpen(void) const
250{
251 switch (m_enmType)
252 {
253 case Type_File: return RTFileIsValid(u.File.hFile);
254 case Type_Directory: return RTDirIsValid(u.Dir.hDir);
255 default: break;
256 }
257
258 return false;
259}
260
261/**
262 * Open the object with a specific file type, and, depending on the type, specifying additional parameters.
263 *
264 * @return IPRT status code.
265 * @param fOpen Open mode to use; only valid for file objects.
266 * @param fMode File mode to set; only valid for file objects. Depends on fOpen and and can be 0.
267 * @param fFlags Additional DnD URI object flags.
268 */
269int DnDURIObject::Open(uint64_t fOpen, RTFMODE fMode /* = 0 */,
270 DNDURIOBJECTFLAGS fFlags /* = DNDURIOBJECT_FLAGS_NONE */)
271{
272 AssertReturn(fOpen, VERR_INVALID_FLAGS);
273 /* fMode is optional. */
274 AssertReturn(!(fFlags & ~DNDURIOBJECT_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
275 RT_NOREF1(fFlags);
276
277 int rc = VINF_SUCCESS;
278
279 if (fOpen) /* Opening mode specified? */
280 {
281 LogFlowThisFunc(("strPath=%s, fOpen=0x%x, fMode=0x%x, fFlags=0x%x\n",
282 m_strPathAbs.c_str(), fOpen, fMode, fFlags));
283 switch (m_enmType)
284 {
285 case Type_File:
286 {
287 LogRel2(("DnD: Opening file '%s'\n", m_strPathAbs.c_str()));
288
289 /*
290 * Open files on the source with RTFILE_O_DENY_WRITE to prevent races
291 * where the OS writes to the file while the destination side transfers
292 * it over.
293 */
294 rc = RTFileOpen(&u.File.hFile, m_strPathAbs.c_str(), fOpen);
295 if (RT_SUCCESS(rc))
296 {
297 if ( (fOpen & RTFILE_O_WRITE) /* Only set the file mode on write. */
298 && fMode /* Some file mode to set specified? */)
299 {
300 rc = RTFileSetMode(u.File.hFile, fMode);
301 if (RT_FAILURE(rc))
302 LogRel(("DnD: Setting mode %#x for file '%s' failed with %Rrc\n", fMode, m_strPathAbs.c_str(), rc));
303 }
304 else if (fOpen & RTFILE_O_READ)
305 {
306 rc = queryInfoInternal();
307 }
308 }
309 else
310 LogRel(("DnD: Opening file '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
311
312 if (RT_SUCCESS(rc))
313 {
314 LogFlowThisFunc(("File cbObject=%RU64, fMode=0x%x\n",
315 u.File.objInfo.cbObject, u.File.objInfo.Attr.fMode));
316 u.File.cbToProcess = u.File.objInfo.cbObject;
317 u.File.cbProcessed = 0;
318 }
319
320 break;
321 }
322
323 case Type_Directory:
324 {
325 LogRel2(("DnD: Opening directory '%s'\n", m_strPathAbs.c_str()));
326
327 rc = RTDirOpen(&u.Dir.hDir, m_strPathAbs.c_str());
328 if (RT_SUCCESS(rc))
329 {
330 rc = queryInfoInternal();
331 }
332 else
333 LogRel(("DnD: Opening directory '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
334 break;
335 }
336
337 default:
338 rc = VERR_NOT_IMPLEMENTED;
339 break;
340 }
341 }
342
343 LogFlowFuncLeaveRC(rc);
344 return rc;
345}
346
347/**
348 * Queries information about the object using a specific view, internal version.
349 *
350 * @return IPRT status code.
351 */
352int DnDURIObject::queryInfoInternal(void)
353{
354 int rc;
355
356 switch (m_enmType)
357 {
358 case Type_File:
359 AssertMsgReturn(RTFileIsValid(u.File.hFile), ("Object has invalid file handle\n"), VERR_INVALID_STATE);
360 rc = RTFileQueryInfo(u.File.hFile, &u.File.objInfo, RTFSOBJATTRADD_NOTHING);
361 break;
362
363 case Type_Directory:
364 AssertMsgReturn(RTDirIsValid(u.Dir.hDir), ("Object has invalid directory handle\n"), VERR_INVALID_STATE);
365 rc = RTDirQueryInfo(u.Dir.hDir, &u.Dir.objInfo, RTFSOBJATTRADD_NOTHING);
366 break;
367
368 default:
369 rc = VERR_NOT_IMPLEMENTED;
370 break;
371 }
372
373 if (RT_FAILURE(rc))
374 LogRel(("DnD: Querying information for '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
375
376 return rc;
377}
378
379/**
380 * Queries information about the object using a specific view.
381 *
382 * @return IPRT status code.
383 */
384int DnDURIObject::QueryInfo(void)
385{
386 return queryInfoInternal();
387}
388
389/**
390 * Rebases an absolute URI path from an old path base to a new path base.
391 * This function is needed in order to transform path from the source side to the target side.
392 *
393 * @return IPRT status code.
394 * @param strPathAbs Absolute URI path to rebase.
395 * @param strBaseOld Old base path to rebase from.
396 * @param strBaseNew New base path to rebase to.
397 *
398 ** @todo Put this into an own class like DnDURIPath : public RTCString?
399 */
400/* static */
401int DnDURIObject::RebaseURIPath(RTCString &strPathAbs,
402 const RTCString &strBaseOld /* = "" */,
403 const RTCString &strBaseNew /* = "" */)
404{
405 char *pszPath = RTUriFilePath(strPathAbs.c_str());
406 if (!pszPath) /* No URI? */
407 pszPath = RTStrDup(strPathAbs.c_str());
408
409 int rc;
410
411 if (pszPath)
412 {
413 const char *pszPathStart = pszPath;
414 const char *pszBaseOld = strBaseOld.c_str();
415 if ( pszBaseOld
416 && RTPathStartsWith(pszPath, pszBaseOld))
417 {
418 pszPathStart += strlen(pszBaseOld);
419 }
420
421 rc = VINF_SUCCESS;
422
423 if (RT_SUCCESS(rc))
424 {
425 char *pszPathNew = RTPathJoinA(strBaseNew.c_str(), pszPathStart);
426 if (pszPathNew)
427 {
428 rc = DnDPathValidate(pszPathNew, false /* fMustExist */);
429 if (RT_SUCCESS(rc))
430 {
431 char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
432 pszPathNew /* pszPath */,
433 NULL /* pszQuery */, NULL /* pszFragment */);
434 if (pszPathURI)
435 {
436 LogFlowFunc(("Rebasing \"%s\" to \"%s\"\n", strPathAbs.c_str(), pszPathURI));
437
438 strPathAbs = RTCString(pszPathURI) + "\r\n";
439 RTStrFree(pszPathURI);
440 }
441 else
442 rc = VERR_INVALID_PARAMETER;
443 }
444 else
445 LogRel(("DnD: Path validation for '%s' failed with %Rrc\n", pszPathNew, rc));
446
447 RTStrFree(pszPathNew);
448 }
449 else
450 rc = VERR_NO_MEMORY;
451 }
452
453 RTStrFree(pszPath);
454 }
455 else
456 rc = VERR_NO_MEMORY;
457
458 if (RT_FAILURE(rc))
459 LogRel(("DnD: Rebasing absolute path '%s' (baseOld=%s, baseNew=%s) failed with %Rrc\n",
460 strPathAbs.c_str(), strBaseOld.c_str(), strBaseNew.c_str(), rc));
461
462 return rc;
463}
464
465/**
466 * Reads data from the object. Only applies to files objects.
467 *
468 * @return IPRT status code.
469 * @param pvBuf Buffer where to store the read data.
470 * @param cbBuf Size (in bytes) of the buffer.
471 * @param pcbRead Pointer where to store how many bytes were read. Optional.
472 */
473int DnDURIObject::Read(void *pvBuf, size_t cbBuf, uint32_t *pcbRead)
474{
475 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
476 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
477 /* pcbRead is optional. */
478
479 size_t cbRead = 0;
480
481 int rc;
482 switch (m_enmType)
483 {
484 case Type_File:
485 {
486 rc = RTFileRead(u.File.hFile, pvBuf, cbBuf, &cbRead);
487 if (RT_SUCCESS(rc))
488 {
489 u.File.cbProcessed += cbRead;
490 Assert(u.File.cbProcessed <= u.File.cbToProcess);
491
492 /* End of file reached or error occurred? */
493 if ( u.File.cbToProcess
494 && u.File.cbProcessed == u.File.cbToProcess)
495 {
496 rc = VINF_EOF;
497 }
498 }
499 else
500 LogRel(("DnD: Reading from file '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
501 break;
502 }
503
504 case Type_Directory:
505 {
506 rc = VINF_SUCCESS;
507 break;
508 }
509
510 default:
511 rc = VERR_NOT_IMPLEMENTED;
512 break;
513 }
514
515 if (RT_SUCCESS(rc))
516 {
517 if (pcbRead)
518 *pcbRead = (uint32_t)cbRead;
519 }
520
521 LogFlowFunc(("Returning strSourcePath=%s, cbRead=%zu, rc=%Rrc\n", m_strPathAbs.c_str(), cbRead, rc));
522 return rc;
523}
524
525/**
526 * Resets the object's state and closes all related handles.
527 */
528void DnDURIObject::Reset(void)
529{
530 LogFlowThisFuncEnter();
531
532 Close();
533
534 m_enmType = Type_Unknown;
535 m_strPathAbs = "";
536
537 RT_ZERO(u);
538}
539
540/**
541 * Sets the bytes to process by the object.
542 *
543 * Note: Only applies if the object is of type DnDURIObject::Type_File.
544 *
545 * @return IPRT return code.
546 * @param cbSize Size (in bytes) to process.
547 */
548int DnDURIObject::SetSize(uint64_t cbSize)
549{
550 AssertReturn(m_enmType == Type_File, VERR_INVALID_PARAMETER);
551
552 /** @todo Implement sparse file support here. */
553
554 u.File.cbToProcess = cbSize;
555 return VINF_SUCCESS;
556}
557
558/**
559 * Writes data to an object. Only applies to file objects.
560 *
561 * @return IPRT status code.
562 * @param pvBuf Buffer of data to write.
563 * @param cbBuf Size (in bytes) of data to write.
564 * @param pcbWritten Pointer where to store how many bytes were written. Optional.
565 */
566int DnDURIObject::Write(const void *pvBuf, size_t cbBuf, uint32_t *pcbWritten)
567{
568 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
569 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
570 /* pcbWritten is optional. */
571
572 size_t cbWritten = 0;
573
574 int rc;
575 switch (m_enmType)
576 {
577 case Type_File:
578 {
579 rc = RTFileWrite(u.File.hFile, pvBuf, cbBuf, &cbWritten);
580 if (RT_SUCCESS(rc))
581 {
582 u.File.cbProcessed += cbWritten;
583 }
584 else
585 LogRel(("DnD: Writing to file '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
586 break;
587 }
588
589 case Type_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 (pcbWritten)
603 *pcbWritten = (uint32_t)cbWritten;
604 }
605
606 LogFlowThisFunc(("Returning strSourcePathAbs=%s, cbWritten=%zu, rc=%Rrc\n", m_strPathAbs.c_str(), cbWritten, rc));
607 return rc;
608}
609
Note: See TracBrowser for help on using the repository browser.

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