VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp@ 93967

Last change on this file since 93967 was 93453, checked in by vboxsync, 3 years ago

Additions: Guest Library: VBoxGuestR3LibGuestProp: Fix NULL pointer dereference, bugref:10134.

Prevent NULL pointer dereference in VbglR3GuestPropWait() when
pcbBufActual parameter is omitted.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 37.0 KB
Line 
1/* $Id: VBoxGuestR3LibGuestProp.cpp 93453 2022-01-26 21:10:57Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest properties.
4 */
5
6/*
7 * Copyright (C) 2007-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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27#if defined(VBOX_VBGLR3_XFREE86) || defined(VBOX_VBGLR3_XORG)
28# define VBOX_VBGLR3_XSERVER
29#endif
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#include <iprt/string.h>
36#ifndef VBOX_VBGLR3_XSERVER
37# include <iprt/mem.h>
38#endif
39#include <iprt/assert.h>
40#include <iprt/mem.h>
41#include <iprt/stdarg.h>
42#include <VBox/err.h>
43#include <VBox/log.h>
44#include <VBox/HostServices/GuestPropertySvc.h>
45
46#include "VBoxGuestR3LibInternal.h"
47
48#ifdef VBOX_VBGLR3_XFREE86
49/* Rather than try to resolve all the header file conflicts, I will just
50 prototype what we need here. */
51extern "C" char* xf86strcpy(char*,const char*);
52# undef strcpy
53# define strcpy xf86strcpy
54extern "C" void* xf86memchr(const void*,int,xf86size_t);
55# undef memchr
56# define memchr xf86memchr
57extern "C" void* xf86memset(const void*,int,xf86size_t);
58# undef memset
59# define memset xf86memset
60
61#endif /* VBOX_VBGLR3_XFREE86 */
62
63#ifdef VBOX_VBGLR3_XSERVER
64
65# undef RTStrEnd
66# define RTStrEnd xf86RTStrEnd
67
68DECLINLINE(char const *) RTStrEnd(char const *pszString, size_t cchMax)
69{
70 /* Avoid potential issues with memchr seen in glibc.
71 * See sysdeps/x86_64/memchr.S in glibc versions older than 2.11 */
72 while (cchMax > RTSTR_MEMCHR_MAX)
73 {
74 char const *pszRet = (char const *)memchr(pszString, '\0', RTSTR_MEMCHR_MAX);
75 if (RT_LIKELY(pszRet))
76 return pszRet;
77 pszString += RTSTR_MEMCHR_MAX;
78 cchMax -= RTSTR_MEMCHR_MAX;
79 }
80 return (char const *)memchr(pszString, '\0', cchMax);
81}
82
83DECLINLINE(char *) RTStrEnd(char *pszString, size_t cchMax)
84{
85 /* Avoid potential issues with memchr seen in glibc.
86 * See sysdeps/x86_64/memchr.S in glibc versions older than 2.11 */
87 while (cchMax > RTSTR_MEMCHR_MAX)
88 {
89 char *pszRet = (char *)memchr(pszString, '\0', RTSTR_MEMCHR_MAX);
90 if (RT_LIKELY(pszRet))
91 return pszRet;
92 pszString += RTSTR_MEMCHR_MAX;
93 cchMax -= RTSTR_MEMCHR_MAX;
94 }
95 return (char *)memchr(pszString, '\0', cchMax);
96}
97
98#endif /* VBOX_VBGLR3_XSERVER */
99
100
101/*********************************************************************************************************************************
102* Structures and Typedefs *
103*********************************************************************************************************************************/
104/**
105 * Structure containing information needed to enumerate through guest
106 * properties.
107 *
108 * @remarks typedef in VBoxGuestLib.h.
109 */
110struct VBGLR3GUESTPROPENUM
111{
112 /** @todo add a magic and validate the handle. */
113 /** The buffer containing the raw enumeration data */
114 char *pchBuf;
115 /** The end of the buffer */
116 char *pchBufEnd;
117 /** Pointer to the next entry to enumerate inside the buffer */
118 char *pchNext;
119};
120
121
122
123/**
124 * Connects to the guest property service.
125 *
126 * @returns VBox status code
127 * @returns VERR_NOT_SUPPORTED if guest properties are not available on the host.
128 * @param pidClient Where to put the client ID on success. The client ID
129 * must be passed to all the other calls to the service.
130 */
131VBGLR3DECL(int) VbglR3GuestPropConnect(HGCMCLIENTID *pidClient)
132{
133 int rc = VbglR3HGCMConnect("VBoxGuestPropSvc", pidClient);
134 if (rc == VERR_NOT_IMPLEMENTED || rc == VERR_HGCM_SERVICE_NOT_FOUND)
135 rc = VERR_NOT_SUPPORTED;
136 return rc;
137}
138
139
140/**
141 * Disconnect from the guest property service.
142 *
143 * @returns VBox status code.
144 * @param idClient The client id returned by VbglR3InfoSvcConnect().
145 */
146VBGLR3DECL(int) VbglR3GuestPropDisconnect(HGCMCLIENTID idClient)
147{
148 return VbglR3HGCMDisconnect(idClient);
149}
150
151
152/**
153 * Checks if @a pszPropName exists.
154 *
155 * @returns \c true if the guest property exists, \c false if not.
156 * @param idClient The HGCM client ID for the guest property session.
157 * @param pszPropName The property name.
158 */
159VBGLR3DECL(bool) VbglR3GuestPropExist(uint32_t idClient, const char *pszPropName)
160{
161 return RT_SUCCESS(VbglR3GuestPropReadEx(idClient, pszPropName, NULL /*ppszValue*/, NULL /* ppszFlags */, NULL /* puTimestamp */));
162}
163
164
165/**
166 * Write a property value.
167 *
168 * @returns VBox status code.
169 * @param idClient The client id returned by VbglR3InvsSvcConnect().
170 * @param pszName The property to save to. Utf8
171 * @param pszValue The value to store. Utf8. If this is NULL then
172 * the property will be removed.
173 * @param pszFlags The flags for the property
174 */
175VBGLR3DECL(int) VbglR3GuestPropWrite(HGCMCLIENTID idClient, const char *pszName, const char *pszValue, const char *pszFlags)
176{
177 int rc;
178
179 if (pszValue != NULL)
180 {
181 GuestPropMsgSetProperty Msg;
182 VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_SET_PROP_VALUE, 3);
183 VbglHGCMParmPtrSetString(&Msg.name, pszName);
184 VbglHGCMParmPtrSetString(&Msg.value, pszValue);
185 VbglHGCMParmPtrSetString(&Msg.flags, pszFlags);
186 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
187 }
188 else
189 {
190 GuestPropMsgDelProperty Msg;
191 VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1);
192 VbglHGCMParmPtrSetString(&Msg.name, pszName);
193 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
194 }
195 return rc;
196}
197
198
199/**
200 * Write a property value.
201 *
202 * @returns VBox status code.
203 *
204 * @param idClient The client id returned by VbglR3InvsSvcConnect().
205 * @param pszName The property to save to. Must be valid UTF-8.
206 * @param pszValue The value to store. Must be valid UTF-8.
207 * If this is NULL then the property will be removed.
208 *
209 * @note if the property already exists and pszValue is not NULL then the
210 * property's flags field will be left unchanged
211 */
212VBGLR3DECL(int) VbglR3GuestPropWriteValue(HGCMCLIENTID idClient, const char *pszName, const char *pszValue)
213{
214 int rc;
215
216 if (pszValue != NULL)
217 {
218 GuestPropMsgSetPropertyValue Msg;
219 VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_SET_PROP_VALUE, 2);
220 VbglHGCMParmPtrSetString(&Msg.name, pszName);
221 VbglHGCMParmPtrSetString(&Msg.value, pszValue);
222 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
223 }
224 else
225 {
226 GuestPropMsgDelProperty Msg;
227 VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1);
228 VbglHGCMParmPtrSetString(&Msg.name, pszName);
229 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
230 }
231 return rc;
232}
233
234#ifndef VBOX_VBGLR3_XSERVER
235/**
236 * Write a property value where the value is formatted in RTStrPrintfV fashion.
237 *
238 * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY.
239 *
240 * @param idClient The client ID returned by VbglR3InvsSvcConnect().
241 * @param pszName The property to save to. Must be valid UTF-8.
242 * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted.
243 * @param va The format arguments.
244 */
245VBGLR3DECL(int) VbglR3GuestPropWriteValueV(HGCMCLIENTID idClient, const char *pszName, const char *pszValueFormat, va_list va)
246{
247 /*
248 * Format the value and pass it on to the setter.
249 */
250 int rc = VERR_NO_STR_MEMORY;
251 char *pszValue;
252 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
253 {
254 rc = VbglR3GuestPropWriteValue(idClient, pszName, pszValue);
255 RTStrFree(pszValue);
256 }
257 return rc;
258}
259
260
261/**
262 * Write a property value where the value is formatted in RTStrPrintf fashion.
263 *
264 * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY.
265 *
266 * @param idClient The client ID returned by VbglR3InvsSvcConnect().
267 * @param pszName The property to save to. Must be valid UTF-8.
268 * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted.
269 * @param ... The format arguments.
270 */
271VBGLR3DECL(int) VbglR3GuestPropWriteValueF(HGCMCLIENTID idClient, const char *pszName, const char *pszValueFormat, ...)
272{
273 va_list va;
274 va_start(va, pszValueFormat);
275 int rc = VbglR3GuestPropWriteValueV(idClient, pszName, pszValueFormat, va);
276 va_end(va);
277 return rc;
278}
279#endif /* VBOX_VBGLR3_XSERVER */
280
281/**
282 * Retrieve a property.
283 *
284 * @returns VBox status code.
285 * @retval VINF_SUCCESS on success, pszValue, pu64Timestamp and pszFlags
286 * containing valid data.
287 * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pcBuf is not large
288 * enough. In this case the size needed will be placed in
289 * @a pcbBufActual if it is not NULL.
290 * @retval VERR_NOT_FOUND if the key wasn't found.
291 *
292 * @param idClient The client id returned by VbglR3GuestPropConnect().
293 * @param pszName The value to read. Utf8
294 * @param pvBuf A scratch buffer to store the data retrieved into.
295 * The returned data is only valid for it's lifetime.
296 * @a ppszValue will point to the start of this buffer.
297 * @param cbBuf The size of @a pcBuf
298 * @param ppszValue Where to store the pointer to the value retrieved.
299 * Optional.
300 * @param pu64Timestamp Where to store the timestamp. Optional.
301 * @param ppszFlags Where to store the pointer to the flags. Optional.
302 * @param pcbBufActual If @a pcBuf is not large enough, the size needed.
303 * Optional.
304 */
305VBGLR3DECL(int) VbglR3GuestPropRead(HGCMCLIENTID idClient, const char *pszName,
306 void *pvBuf, uint32_t cbBuf,
307 char **ppszValue, uint64_t *pu64Timestamp,
308 char **ppszFlags,
309 uint32_t *pcbBufActual)
310{
311 /*
312 * Create the GET_PROP message and call the host.
313 */
314 GuestPropMsgGetProperty Msg;
315 VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_GET_PROP, 4);
316 VbglHGCMParmPtrSetString(&Msg.name, pszName);
317 VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf);
318 VbglHGCMParmUInt64Set(&Msg.timestamp, 0);
319 VbglHGCMParmUInt32Set(&Msg.size, 0);
320
321 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
322
323 /*
324 * The cbBufActual parameter is also returned on overflow so the call can
325 * adjust his/her buffer.
326 */
327 if ( rc == VERR_BUFFER_OVERFLOW
328 || pcbBufActual != NULL)
329 {
330 int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual);
331 AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2);
332 }
333 if (RT_FAILURE(rc))
334 return rc;
335
336 /*
337 * Buffer layout: Value\0Flags\0.
338 *
339 * If the caller cares about any of these strings, make sure things are
340 * properly terminated (paranoia).
341 */
342 if ( RT_SUCCESS(rc)
343 && (ppszValue != NULL || ppszFlags != NULL))
344 {
345 /* Validate / skip 'Name'. */
346 char *pszFlags = RTStrEnd((char *)pvBuf, cbBuf) + 1;
347 AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA);
348 if (ppszValue)
349 *ppszValue = (char *)pvBuf;
350
351 if (ppszFlags)
352 {
353 /* Validate 'Flags'. */
354 char *pszEos = RTStrEnd(pszFlags, cbBuf - (pszFlags - (char *)pvBuf));
355 AssertPtrReturn(pszEos, VERR_TOO_MUCH_DATA);
356 *ppszFlags = pszFlags;
357 }
358 }
359
360 /* And the timestamp, if requested. */
361 if (pu64Timestamp != NULL)
362 {
363 rc = VbglHGCMParmUInt64Get(&Msg.timestamp, pu64Timestamp);
364 AssertRCReturn(rc, rc);
365 }
366
367 return VINF_SUCCESS;
368}
369
370/**
371 * Reads a guest property by returning allocated values.
372 *
373 * @returns VBox status code, fully bitched.
374 *
375 * @param idClient The HGCM client ID for the guest property session.
376 * @param pszPropName The property name.
377 * @param ppszValue Where to return the value. This is always set
378 * to NULL. Needs to be free'd using RTStrFree(). Optional.
379 * @param ppszFlags Where to return the value flags.
380 * Needs to be free'd using RTStrFree(). Optional.
381 * @param puTimestamp Where to return the timestamp. This is only set
382 * on success. Optional.
383 */
384VBGLR3DECL(int) VbglR3GuestPropReadEx(uint32_t idClient,
385 const char *pszPropName, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp)
386{
387 AssertPtrReturn(pszPropName, VERR_INVALID_POINTER);
388
389 uint32_t cbBuf = _1K;
390 void *pvBuf = NULL;
391 int rc = VINF_SUCCESS; /* MSC can't figure out the loop */
392
393 if (ppszValue)
394 *ppszValue = NULL;
395
396 for (unsigned cTries = 0; cTries < 10; cTries++)
397 {
398 /*
399 * (Re-)Allocate the buffer and try read the property.
400 */
401 RTMemFree(pvBuf);
402 pvBuf = RTMemAlloc(cbBuf);
403 if (!pvBuf)
404 {
405 rc = VERR_NO_MEMORY;
406 break;
407 }
408 char *pszValue;
409 char *pszFlags;
410 uint64_t uTimestamp;
411 rc = VbglR3GuestPropRead(idClient, pszPropName, pvBuf, cbBuf, &pszValue, &uTimestamp, &pszFlags, NULL);
412 if (RT_FAILURE(rc))
413 {
414 if (rc == VERR_BUFFER_OVERFLOW)
415 {
416 /* try again with a bigger buffer. */
417 cbBuf *= 2;
418 continue;
419 }
420 break;
421 }
422
423 if (ppszValue)
424 {
425 *ppszValue = RTStrDup(pszValue);
426 if (!*ppszValue)
427 {
428 rc = VERR_NO_MEMORY;
429 break;
430 }
431 }
432
433 if (puTimestamp)
434 *puTimestamp = uTimestamp;
435 if (ppszFlags)
436 *ppszFlags = RTStrDup(pszFlags);
437 break; /* done */
438 }
439
440 if (pvBuf)
441 RTMemFree(pvBuf);
442 return rc;
443}
444
445#ifndef VBOX_VBGLR3_XSERVER
446/**
447 * Retrieve a property value, allocating space for it.
448 *
449 * @returns VBox status code.
450 * @retval VINF_SUCCESS on success, *ppszValue containing valid data.
451 * @retval VERR_NOT_FOUND if the key wasn't found.
452 * @retval VERR_TOO_MUCH_DATA if we were unable to determine the right size
453 * to allocate for the buffer. This can happen as the result of a
454 * race between our allocating space and the host changing the
455 * property value.
456 *
457 * @param idClient The client id returned by VbglR3GuestPropConnect().
458 * @param pszName The value to read. Must be valid UTF-8.
459 * @param ppszValue Where to store the pointer to the value returned.
460 * This is always set to NULL or to the result, even
461 * on failure.
462 */
463VBGLR3DECL(int) VbglR3GuestPropReadValueAlloc(HGCMCLIENTID idClient, const char *pszName, char **ppszValue)
464{
465 /*
466 * Quick input validation.
467 */
468 AssertPtr(ppszValue);
469 *ppszValue = NULL;
470 AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
471
472 /*
473 * There is a race here between our reading the property size and the
474 * host changing the value before we read it. Try up to ten times and
475 * report the problem if that fails.
476 */
477 char *pszValue = NULL;
478 void *pvBuf = NULL;
479 uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN;
480 int rc = VERR_BUFFER_OVERFLOW;
481 for (unsigned i = 0; i < 10 && rc == VERR_BUFFER_OVERFLOW; ++i)
482 {
483 /* We leave a bit of space here in case the maximum value is raised. */
484 cbBuf += 1024;
485 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
486 if (pvTmpBuf)
487 {
488 pvBuf = pvTmpBuf;
489 rc = VbglR3GuestPropRead(idClient, pszName, pvBuf, cbBuf, &pszValue, NULL, NULL, &cbBuf);
490 }
491 else
492 rc = VERR_NO_MEMORY;
493 }
494 if (RT_SUCCESS(rc))
495 {
496 Assert(pszValue == (char *)pvBuf);
497 *ppszValue = pszValue;
498 }
499 else
500 {
501 RTMemFree(pvBuf);
502 if (rc == VERR_BUFFER_OVERFLOW)
503 /* VERR_BUFFER_OVERFLOW has a different meaning here as a
504 * return code, but we need to report the race. */
505 rc = VERR_TOO_MUCH_DATA;
506 }
507
508 return rc;
509}
510
511
512/**
513 * Free the memory used by VbglR3GuestPropReadValueAlloc for returning a
514 * value.
515 *
516 * @param pszValue the memory to be freed. NULL pointers will be ignored.
517 */
518VBGLR3DECL(void) VbglR3GuestPropReadValueFree(char *pszValue)
519{
520 RTMemFree(pszValue);
521}
522#endif /* VBOX_VBGLR3_XSERVER */
523
524/**
525 * Retrieve a property value, using a user-provided buffer to store it.
526 *
527 * @returns VBox status code.
528 * @retval VINF_SUCCESS on success, pszValue containing valid data.
529 * @retval VERR_BUFFER_OVERFLOW and the size needed in pcchValueActual if the
530 * buffer provided was too small
531 * @retval VERR_NOT_FOUND if the key wasn't found.
532 *
533 * @note There is a race here between obtaining the size of the buffer
534 * needed to hold the value and the value being updated.
535 *
536 * @param idClient The client id returned by VbglR3GuestPropConnect().
537 * @param pszName The value to read. Utf8
538 * @param pszValue Where to store the value retrieved.
539 * @param cchValue The size of the buffer pointed to by @a pszValue
540 * @param pcchValueActual Where to store the size of the buffer needed if
541 * the buffer supplied is too small. Optional.
542 */
543VBGLR3DECL(int) VbglR3GuestPropReadValue(HGCMCLIENTID idClient, const char *pszName,
544 char *pszValue, uint32_t cchValue,
545 uint32_t *pcchValueActual)
546{
547 void *pvBuf = pszValue;
548 uint32_t cchValueActual;
549 int rc = VbglR3GuestPropRead(idClient, pszName, pvBuf, cchValue, &pszValue, NULL, NULL, &cchValueActual);
550 if (pcchValueActual != NULL)
551 *pcchValueActual = cchValueActual;
552 return rc;
553}
554
555
556#ifndef VBOX_VBGLR3_XSERVER
557/**
558 * Raw API for enumerating guest properties which match a given pattern.
559 *
560 * @returns VBox status code.
561 * @retval VINF_SUCCESS on success and pcBuf points to a packed array
562 * of the form \<name\>, \<value\>, \<timestamp string\>, \<flags\>,
563 * terminated by four empty strings. pcbBufActual will contain the
564 * total size of the array.
565 * @retval VERR_BUFFER_OVERFLOW if the buffer provided was too small. In
566 * this case pcbBufActual will contain the size of the buffer needed.
567 * @returns IPRT error code in other cases, and pchBufActual is undefined.
568 *
569 * @param idClient The client ID returned by VbglR3GuestPropConnect
570 * @param pszzPatterns A packed array of zero terminated strings, terminated
571 * by an empty string.
572 * @param pcBuf The buffer to store the results to.
573 * @param cbBuf The size of the buffer
574 * @param pcbBufActual Where to store the size of the returned data on
575 * success or the buffer size needed if @a pcBuf is too
576 * small.
577 */
578VBGLR3DECL(int) VbglR3GuestPropEnumRaw(HGCMCLIENTID idClient,
579 const char *pszzPatterns,
580 char *pcBuf,
581 uint32_t cbBuf,
582 uint32_t *pcbBufActual)
583{
584 GuestPropMsgEnumProperties Msg;
585 VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_ENUM_PROPS, 3);
586
587 /* Get the length of the patterns array... */
588 size_t cchPatterns = 0;
589 for (size_t cchCurrent = strlen(pszzPatterns); cchCurrent != 0;
590 cchCurrent = strlen(pszzPatterns + cchPatterns))
591 cchPatterns += cchCurrent + 1;
592 /* ...including the terminator. */
593 ++cchPatterns;
594 VbglHGCMParmPtrSet(&Msg.patterns, (char *)pszzPatterns, (uint32_t)cchPatterns);
595 VbglHGCMParmPtrSet(&Msg.strings, pcBuf, cbBuf);
596 VbglHGCMParmUInt32Set(&Msg.size, 0);
597
598 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
599 if ( pcbBufActual
600 && ( RT_SUCCESS(rc)
601 || rc == VERR_BUFFER_OVERFLOW))
602 {
603 int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual);
604 if (RT_FAILURE(rc2))
605 rc = rc2;
606 }
607 return rc;
608}
609
610
611/**
612 * Start enumerating guest properties which match a given pattern.
613 *
614 * This function creates a handle which can be used to continue enumerating.
615 *
616 * @returns VBox status code.
617 * @retval VINF_SUCCESS on success, *ppHandle points to a handle for continuing
618 * the enumeration and *ppszName, *ppszValue, *pu64Timestamp and
619 * *ppszFlags are set.
620 * @retval VERR_TOO_MUCH_DATA if it was not possible to determine the amount
621 * of local space needed to store all the enumeration data. This is
622 * due to a race between allocating space and the host adding new
623 * data, so retrying may help here. Other parameters are left
624 * uninitialised
625 *
626 * @param idClient The client id returned by VbglR3InfoSvcConnect().
627 * @param papszPatterns The patterns against which the properties are
628 * matched. Pass NULL if everything should be matched.
629 * @param cPatterns The number of patterns in @a papszPatterns. 0 means
630 * match everything.
631 * @param ppHandle where the handle for continued enumeration is stored
632 * on success. This must be freed with
633 * VbglR3GuestPropEnumFree when it is no longer needed.
634 * @param ppszName Where to store the next property name. This will be
635 * set to NULL if there are no more properties to
636 * enumerate. This pointer should not be freed. Optional.
637 * @param ppszValue Where to store the next property value. This will be
638 * set to NULL if there are no more properties to
639 * enumerate. This pointer should not be freed. Optional.
640 * @param pu64Timestamp Where to store the next property timestamp. This
641 * will be set to zero if there are no more properties
642 * to enumerate. Optional.
643 * @param ppszFlags Where to store the next property flags. This will be
644 * set to NULL if there are no more properties to
645 * enumerate. This pointer should not be freed. Optional.
646 *
647 * @remarks While all output parameters are optional, you need at least one to
648 * figure out when to stop.
649 */
650VBGLR3DECL(int) VbglR3GuestPropEnum(HGCMCLIENTID idClient,
651 char const * const *papszPatterns,
652 uint32_t cPatterns,
653 PVBGLR3GUESTPROPENUM *ppHandle,
654 char const **ppszName,
655 char const **ppszValue,
656 uint64_t *pu64Timestamp,
657 char const **ppszFlags)
658{
659 /* Create the handle. */
660 PVBGLR3GUESTPROPENUM pHandle = (PVBGLR3GUESTPROPENUM)RTMemAllocZ(sizeof(VBGLR3GUESTPROPENUM));
661 if (RT_LIKELY(pHandle))
662 {/* likely */}
663 else
664 return VERR_NO_MEMORY;
665
666 /* Get the length of the pattern string, including the final terminator. */
667 size_t cbPatterns = 1;
668 for (uint32_t i = 0; i < cPatterns; ++i)
669 cbPatterns += strlen(papszPatterns[i]) + 1;
670
671 /* Pack the pattern array. */
672 char *pszzPatterns = (char *)RTMemAlloc(cbPatterns);
673 size_t off = 0;
674 for (uint32_t i = 0; i < cPatterns; ++i)
675 {
676 size_t cb = strlen(papszPatterns[i]) + 1;
677 memcpy(&pszzPatterns[off], papszPatterns[i], cb);
678 off += cb;
679 }
680 pszzPatterns[off] = '\0';
681
682 /* In reading the guest property data we are racing against the host
683 * adding more of it, so loop a few times and retry on overflow. */
684 uint32_t cbBuf = 4096; /* picked out of thin air */
685 char *pchBuf = NULL;
686 int rc = VINF_SUCCESS;
687 for (int i = 0; i < 10; ++i)
688 {
689 void *pvNew = RTMemRealloc(pchBuf, cbBuf);
690 if (pvNew)
691 pchBuf = (char *)pvNew;
692 else
693 {
694 rc = VERR_NO_MEMORY;
695 break;
696 }
697 rc = VbglR3GuestPropEnumRaw(idClient, pszzPatterns, pchBuf, cbBuf, &cbBuf);
698 if (rc != VERR_BUFFER_OVERFLOW)
699 break;
700 cbBuf += 4096; /* Just to increase our chances */
701 }
702 RTMemFree(pszzPatterns);
703 if (RT_SUCCESS(rc))
704 {
705 /*
706 * Complete the handle and call VbglR3GuestPropEnumNext to retrieve the first entry.
707 */
708 pHandle->pchNext = pchBuf;
709 pHandle->pchBuf = pchBuf;
710 pHandle->pchBufEnd = pchBuf + cbBuf;
711
712 const char *pszNameTmp;
713 if (!ppszName)
714 ppszName = &pszNameTmp;
715 rc = VbglR3GuestPropEnumNext(pHandle, ppszName, ppszValue, pu64Timestamp, ppszFlags);
716 if (RT_SUCCESS(rc))
717 {
718 *ppHandle = pHandle;
719 return rc;
720 }
721 }
722 else if (rc == VERR_BUFFER_OVERFLOW)
723 rc = VERR_TOO_MUCH_DATA;
724 RTMemFree(pchBuf);
725 RTMemFree(pHandle);
726 return rc;
727}
728
729
730/**
731 * Get the next guest property.
732 *
733 * See @a VbglR3GuestPropEnum.
734 *
735 * @returns VBox status code.
736 *
737 * @param pHandle Handle obtained from @a VbglR3GuestPropEnum.
738 * @param ppszName Where to store the next property name. This will be
739 * set to NULL if there are no more properties to
740 * enumerate. This pointer should not be freed. Optional.
741 * @param ppszValue Where to store the next property value. This will be
742 * set to NULL if there are no more properties to
743 * enumerate. This pointer should not be freed. Optional.
744 * @param pu64Timestamp Where to store the next property timestamp. This
745 * will be set to zero if there are no more properties
746 * to enumerate. Optional.
747 * @param ppszFlags Where to store the next property flags. This will be
748 * set to NULL if there are no more properties to
749 * enumerate. This pointer should not be freed. Optional.
750 *
751 * @remarks While all output parameters are optional, you need at least one to
752 * figure out when to stop.
753 */
754VBGLR3DECL(int) VbglR3GuestPropEnumNext(PVBGLR3GUESTPROPENUM pHandle,
755 char const **ppszName,
756 char const **ppszValue,
757 uint64_t *pu64Timestamp,
758 char const **ppszFlags)
759{
760 /*
761 * The VBGLR3GUESTPROPENUM structure contains a buffer containing the raw
762 * properties data and a pointer into the buffer which tracks how far we
763 * have parsed so far. The buffer contains packed strings in groups of
764 * four - name, value, timestamp (as a decimal string) and flags. It is
765 * terminated by four empty strings. We can rely on this layout unless
766 * the caller has been poking about in the structure internals, in which
767 * case they must take responsibility for the results.
768 *
769 * Layout:
770 * Name\0Value\0Timestamp\0Flags\0
771 */
772 char *pchNext = pHandle->pchNext; /* The cursor. */
773 char *pchEnd = pHandle->pchBufEnd; /* End of buffer, for size calculations. */
774
775 char *pszName = pchNext;
776 char *pszValue = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1;
777 AssertPtrReturn(pchNext, VERR_PARSE_ERROR); /* 0x1 is also an invalid pointer :) */
778
779 char *pszTimestamp = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1;
780 AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
781
782 char *pszFlags = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1;
783 AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
784
785 /*
786 * Don't move the index pointer if we found the terminating "\0\0\0\0" entry.
787 * Don't try convert the timestamp either.
788 */
789 uint64_t u64Timestamp;
790 if (*pszName != '\0')
791 {
792 pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1;
793 AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
794
795 /* Convert the timestamp string into a number. */
796 int rc = RTStrToUInt64Full(pszTimestamp, 0, &u64Timestamp);
797 AssertRCSuccessReturn(rc, VERR_PARSE_ERROR);
798
799 pHandle->pchNext = pchNext;
800 AssertPtr(pchNext);
801 }
802 else
803 {
804 u64Timestamp = 0;
805 AssertMsgReturn(!*pszValue && !*pszTimestamp && !*pszFlags,
806 ("'%s' '%s' '%s'\n", pszValue, pszTimestamp, pszFlags),
807 VERR_PARSE_ERROR);
808 }
809
810 /*
811 * Everything is fine, set the return values.
812 */
813 if (ppszName)
814 *ppszName = *pszName != '\0' ? pszName : NULL;
815 if (ppszValue)
816 *ppszValue = *pszValue != '\0' ? pszValue : NULL;
817 if (pu64Timestamp)
818 *pu64Timestamp = u64Timestamp;
819 if (ppszFlags)
820 *ppszFlags = *pszFlags != '\0' ? pszFlags : NULL;
821 return VINF_SUCCESS;
822}
823
824
825/**
826 * Free an enumeration handle returned by @a VbglR3GuestPropEnum.
827 * @param pHandle the handle to free
828 */
829VBGLR3DECL(void) VbglR3GuestPropEnumFree(PVBGLR3GUESTPROPENUM pHandle)
830{
831 if (!pHandle)
832 return;
833 RTMemFree(pHandle->pchBuf);
834 RTMemFree(pHandle);
835}
836
837
838/**
839 * Deletes a guest property.
840 *
841 * @returns VBox status code.
842 * @param idClient The client id returned by VbglR3InvsSvcConnect().
843 * @param pszName The property to delete. Utf8
844 */
845VBGLR3DECL(int) VbglR3GuestPropDelete(HGCMCLIENTID idClient, const char *pszName)
846{
847 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
848
849 GuestPropMsgDelProperty Msg;
850 VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1);
851 VbglHGCMParmPtrSetString(&Msg.name, pszName);
852 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
853}
854
855
856/**
857 * Deletes a set of keys.
858 *
859 * The set is specified in the same way as for VbglR3GuestPropEnum.
860 *
861 * @returns VBox status code. Stops on first failure.
862 * See also VbglR3GuestPropEnum.
863 *
864 * @param idClient The client id returned by VbglR3InfoSvcConnect().
865 * @param papszPatterns The patterns against which the properties are
866 * matched. Pass NULL if everything should be matched.
867 * @param cPatterns The number of patterns in @a papszPatterns. 0 means
868 * match everything.
869 */
870VBGLR3DECL(int) VbglR3GuestPropDelSet(HGCMCLIENTID idClient,
871 const char * const *papszPatterns,
872 uint32_t cPatterns)
873{
874 PVBGLR3GUESTPROPENUM pHandle;
875 char const *pszName, *pszValue, *pszFlags;
876 uint64_t pu64Timestamp;
877 int rc = VbglR3GuestPropEnum(idClient,
878 (char **)papszPatterns, /** @todo fix this cast. */
879 cPatterns,
880 &pHandle,
881 &pszName,
882 &pszValue,
883 &pu64Timestamp,
884 &pszFlags);
885
886 while (RT_SUCCESS(rc) && pszName)
887 {
888 rc = VbglR3GuestPropWriteValue(idClient, pszName, NULL);
889 if (RT_FAILURE(rc))
890 break;
891
892 rc = VbglR3GuestPropEnumNext(pHandle,
893 &pszName,
894 &pszValue,
895 &pu64Timestamp,
896 &pszFlags);
897 }
898
899 VbglR3GuestPropEnumFree(pHandle);
900 return rc;
901}
902
903
904/**
905 * Wait for notification of changes to a guest property. If this is called in
906 * a loop, the timestamp of the last notification seen can be passed as a
907 * parameter to be sure that no notifications are missed.
908 *
909 * @returns VBox status code.
910 * @retval VINF_SUCCESS on success, @a ppszName, @a ppszValue,
911 * @a pu64Timestamp and @a ppszFlags containing valid data.
912 * @retval VINF_NOT_FOUND if no previous notification could be found with the
913 * timestamp supplied. This will normally mean that a large number
914 * of notifications occurred in between.
915 * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pvBuf is not large
916 * enough. In this case the size needed will be placed in
917 * @a pcbBufActual if it is not NULL.
918 * @retval VERR_TIMEOUT if a timeout occurred before a notification was seen.
919 *
920 * @param idClient The client id returned by VbglR3GuestPropConnect().
921 * @param pszPatterns The patterns that the property names must matchfor
922 * the change to be reported.
923 * @param pvBuf A scratch buffer to store the data retrieved into.
924 * The returned data is only valid for it's lifetime.
925 * @a ppszValue will point to the start of this buffer.
926 * @param cbBuf The size of @a pvBuf
927 * @param u64Timestamp The timestamp of the last event seen. Pass zero
928 * to wait for the next event.
929 * @param cMillies Timeout in milliseconds. Use RT_INDEFINITE_WAIT
930 * to wait indefinitely.
931 * @param ppszName Where to store the pointer to the name retrieved.
932 * Optional.
933 * @param ppszValue Where to store the pointer to the value retrieved.
934 * Optional.
935 * @param pu64Timestamp Where to store the timestamp. Optional.
936 * @param ppszFlags Where to store the pointer to the flags. Optional.
937 * @param pcbBufActual If @a pcBuf is not large enough, the size needed.
938 * Optional.
939 */
940VBGLR3DECL(int) VbglR3GuestPropWait(HGCMCLIENTID idClient,
941 const char *pszPatterns,
942 void *pvBuf, uint32_t cbBuf,
943 uint64_t u64Timestamp, uint32_t cMillies,
944 char ** ppszName, char **ppszValue,
945 uint64_t *pu64Timestamp, char **ppszFlags,
946 uint32_t *pcbBufActual)
947{
948 /*
949 * Create the GET_NOTIFICATION message and call the host.
950 */
951 GuestPropMsgGetNotification Msg;
952 VBGL_HGCM_HDR_INIT_TIMED(&Msg.hdr, idClient, GUEST_PROP_FN_GET_NOTIFICATION, 4, cMillies);
953
954 VbglHGCMParmPtrSetString(&Msg.patterns, pszPatterns);
955 VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf);
956 VbglHGCMParmUInt64Set(&Msg.timestamp, u64Timestamp);
957 VbglHGCMParmUInt32Set(&Msg.size, 0);
958
959 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
960
961 /*
962 * The cbBufActual parameter is also returned on overflow so the caller can
963 * adjust their buffer.
964 */
965 if ( rc == VERR_BUFFER_OVERFLOW
966 && pcbBufActual != NULL)
967 {
968 int rc2 = Msg.size.GetUInt32(pcbBufActual);
969 AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2);
970 }
971 if (RT_FAILURE(rc))
972 return rc;
973
974 /*
975 * Buffer layout: Name\0Value\0Flags\0.
976 *
977 * If the caller cares about any of these strings, make sure things are
978 * properly terminated (paranoia).
979 */
980 if ( RT_SUCCESS(rc)
981 && (ppszName != NULL || ppszValue != NULL || ppszFlags != NULL))
982 {
983 /* Validate / skip 'Name'. */
984 char *pszValue = RTStrEnd((char *)pvBuf, cbBuf) + 1;
985 AssertPtrReturn(pszValue, VERR_TOO_MUCH_DATA);
986 if (ppszName)
987 *ppszName = (char *)pvBuf;
988
989 /* Validate / skip 'Value'. */
990 char *pszFlags = RTStrEnd(pszValue, cbBuf - (pszValue - (char *)pvBuf)) + 1;
991 AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA);
992 if (ppszValue)
993 *ppszValue = pszValue;
994
995 if (ppszFlags)
996 {
997 /* Validate 'Flags'. */
998 char *pszEos = RTStrEnd(pszFlags, cbBuf - (pszFlags - (char *)pvBuf));
999 AssertPtrReturn(pszEos, VERR_TOO_MUCH_DATA);
1000 *ppszFlags = pszFlags;
1001 }
1002 }
1003
1004 /* And the timestamp, if requested. */
1005 if (pu64Timestamp != NULL)
1006 {
1007 rc = Msg.timestamp.GetUInt64(pu64Timestamp);
1008 AssertRCReturn(rc, rc);
1009 }
1010
1011 return VINF_SUCCESS;
1012}
1013#endif /* VBOX_VBGLR3_XSERVER */
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