VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedInfoServices/service.cpp@ 10031

Last change on this file since 10031 was 10017, checked in by vboxsync, 17 years ago

HostServices/SharedInfoService and Additions/common: deal with overflow and missing keys better when querying guest properties

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.9 KB
Line 
1/** @file
2 *
3 * Shared Information Services:
4 * Host service entry points.
5 */
6
7/*
8 * Copyright (C) 2008 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23/**
24 * An HGCM service for passing requests which do not need any persistant state
25 * to handle. We currently only support three types of request - set guest
26 * property (SET_CONFIG_KEY and SET_CONFIG_KEY_HOST), get guest property
27 * (GET_CONFIG_KEY and GET_CONFIG_KEY_HOST) and remove guest property
28 * (DEL_CONFIG_KEY and DEL_CONFIG_KEY_HOST). These may be used to read, to
29 * write and to remove configuration information which is available to
30 * both guest and host. This configuration information is stored in a CFGM
31 * node using the CFGM APIs. It is the responsibility of whoever creates the
32 * service to create this node and to tell the service about it using the
33 * SET_CFGM_NODE host call. It is also the responsibility of the service
34 * creator to save this information to disk and to retrieve it when needed.
35 * Since the CFGM APIs are single threaded, the creator must also ensure that
36 * no-one else accesses the configuration node while the service is running.
37 *
38 * If this service is extended to deal with new requests it would probably be a
39 * good idea to split it up into several files.
40 */
41
42#define LOG_GROUP LOG_GROUP_HGCM
43
44/*******************************************************************************
45* Header Files *
46*******************************************************************************/
47#include <VBox/HostServices/VBoxInfoSvc.h>
48
49#include <memory> /* for auto_ptr */
50
51#include <iprt/err.h>
52#include <iprt/assert.h>
53#include <VBox/log.h>
54
55#include <VBox/cfgm.h>
56
57#include "noncopyable.h"
58
59/*******************************************************************************
60* Internal functions *
61*******************************************************************************/
62/** Extract a pointer value from an HGCM parameter structure */
63static int VBoxHGCMParmPtrGet (VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
64{
65 if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
66 {
67 *ppv = pParm->u.pointer.addr;
68 *pcb = pParm->u.pointer.size;
69 return VINF_SUCCESS;
70 }
71
72 return VERR_INVALID_PARAMETER;
73}
74
75/** Set a uint32_t value to an HGCM parameter structure */
76static void VBoxHGCMParmUInt32Set (VBOXHGCMSVCPARM *pParm, uint32_t u32)
77{
78 pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
79 pParm->u.uint32 = u32;
80}
81
82
83namespace svcInfo {
84
85/**
86 * Class containing the shared information service functionality.
87 */
88class Service : public noncopyable
89{
90private:
91 /** Type definition for use in callback functions */
92 typedef Service SELF;
93 /** HGCM helper functions. */
94 PVBOXHGCMSVCHELPERS mpHelpers;
95 /** Pointer to our configuration node. */
96 PCFGMNODE mpNode;
97
98public:
99 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
100 : mpHelpers(pHelpers), mpNode(NULL) {}
101
102 /**
103 * @copydoc VBOXHGCMSVCHELPERS::pfnUnload
104 * Simply deletes the service object
105 */
106 static DECLCALLBACK(int) svcUnload (void *pvService)
107 {
108 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
109 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
110 delete pSelf;
111 return VINF_SUCCESS;
112 }
113
114 /**
115 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
116 * Stub implementation of pfnConnect and pfnDisconnect.
117 */
118 static DECLCALLBACK(int) svcConnectDisconnect (void * /* pvService */,
119 uint32_t /* u32ClientID */,
120 void * /* pvClient */)
121 {
122 return VINF_SUCCESS;
123 }
124
125 /**
126 * @copydoc VBOXHGCMSVCHELPERS::pfnCall
127 * Wraps to the call member function
128 */
129 static DECLCALLBACK(void) svcCall (void * pvService,
130 VBOXHGCMCALLHANDLE callHandle,
131 uint32_t u32ClientID,
132 void *pvClient,
133 uint32_t u32Function,
134 uint32_t cParms,
135 VBOXHGCMSVCPARM paParms[])
136 {
137 AssertLogRelReturnVoid(VALID_PTR(pvService));
138 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
139 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
140 }
141
142 /**
143 * @copydoc VBOXHGCMSVCHELPERS::pfnHostCall
144 * Wraps to the hostCall member function
145 */
146 static DECLCALLBACK(int) svcHostCall (void *pvService,
147 uint32_t u32Function,
148 uint32_t cParms,
149 VBOXHGCMSVCPARM paParms[])
150 {
151 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
152 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
153 return pSelf->hostCall(u32Function, cParms, paParms);
154 }
155private:
156 int validateKey(const char *pszKey, uint32_t cbKey);
157 int validateValue(char *pszValue, uint32_t cbValue);
158 int getKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
159 int setKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
160 int delKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
161 void call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
162 void *pvClient, uint32_t eFunction, uint32_t cParms,
163 VBOXHGCMSVCPARM paParms[]);
164 int hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
165};
166
167
168/**
169 * Checking that the key passed by the guest fits our criteria for a
170 * configuration key
171 *
172 * @returns IPRT status code
173 * @param pszKey the key passed by the guest
174 * @param cbKey the number of bytes pszKey points to, including the
175 * terminating '\0'
176 * @thread HGCM
177 */
178int Service::validateKey(const char *pszKey, uint32_t cbKey)
179{
180 LogFlowFunc(("cbKey=%d\n", cbKey));
181
182 unsigned count;
183 int rc = VINF_SUCCESS;
184
185 /* Validate the format of the key. */
186 /* Only accept names in printable ASCII without spaces */
187 for (count = 0; (count < cbKey) && (pszKey[count] != '\0'); ++count)
188 if ((pszKey[count] < 33) || (pszKey[count] > 126))
189 rc = VERR_INVALID_PARAMETER;
190 if (RT_SUCCESS(rc) && (count == cbKey))
191 /* This would mean that no null terminator was found */
192 rc = VERR_INVALID_PARAMETER;
193 if (RT_SUCCESS(rc) && (count > KEY_MAX_LEN))
194 rc = VERR_INVALID_PARAMETER;
195
196 LogFlowFunc(("returning %Rrc\n", rc));
197 return rc;
198}
199
200
201/**
202 * Check that the data passed by the guest fits our criteria for the value of
203 * a configuration key
204 *
205 * @returns IPRT status code
206 * @param pszValue the value to store in the key
207 * @param cbValue the number of bytes in the buffer pszValue points to
208 * @thread HGCM
209 */
210int Service::validateValue(char *pszValue, uint32_t cbValue)
211{
212 LogFlowFunc(("cbValue=%d\n", cbValue));
213
214 uint32_t count;
215 int rc = VINF_SUCCESS;
216
217 if (cbValue != 0)
218 {
219 /* Validate the format of the value. */
220 /* Only accept values in printable ASCII without spaces */
221 for (count = 0; (count < cbValue) && (pszValue[count] != '\0'); ++count)
222 if ((pszValue[count] < 33) || (pszValue[count] > 126))
223 rc = VERR_INVALID_PARAMETER;
224 if (RT_SUCCESS(rc) && (count == cbValue))
225 /* This would mean that no null terminator was found */
226 rc = VERR_INVALID_PARAMETER;
227 if (RT_SUCCESS(rc) && (count > KEY_MAX_VALUE_LEN))
228 rc = VERR_INVALID_PARAMETER;
229 }
230
231 if (RT_SUCCESS(rc))
232 LogFlow((" pszValue=%s\n", cbValue > 0 ? pszValue : NULL));
233 LogFlowFunc(("returning %Rrc\n", rc));
234 return rc;
235}
236
237
238/**
239 * Retrieve a value from the guest registry by key, checking the validity
240 * of the arguments passed. If the guest has not allocated enough buffer
241 * space for the value then we return VERR_OVERFLOW and set the size of the
242 * buffer needed in the "size" HGCM parameter. If the key was not found at
243 * all, we return VERR_NOT_FOUND.
244 *
245 * @returns iprt status value
246 * @param cParms the number of HGCM parameters supplied
247 * @param paParms the array of HGCM parameters
248 * @thread HGCM
249 */
250int Service::getKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
251{
252 int rc = VINF_SUCCESS;
253 char *pszKey, *pszValue;
254 uint32_t cbKey, cbValue;
255 size_t cbValueActual;
256
257 LogFlowThisFunc(("\n"));
258 if ( (cParms != 3) /* Hardcoded value as the next lines depend on it. */
259 || (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) /* key */
260 || (paParms[1].type != VBOX_HGCM_SVC_PARM_PTR) /* value */
261 )
262 rc = VERR_INVALID_PARAMETER;
263 if (RT_SUCCESS(rc))
264 rc = VBoxHGCMParmPtrGet(&paParms[0], (void **) &pszKey, &cbKey);
265 if (RT_SUCCESS(rc))
266 rc = VBoxHGCMParmPtrGet(&paParms[1], (void **) &pszValue, &cbValue);
267 if (RT_SUCCESS(rc))
268 rc = validateKey(pszKey, cbKey);
269 if (RT_SUCCESS(rc))
270 rc = CFGMR3QuerySize(mpNode, pszKey, &cbValueActual);
271 if (RT_SUCCESS(rc))
272 VBoxHGCMParmUInt32Set(&paParms[2], cbValueActual);
273 if (RT_SUCCESS(rc) && (cbValueActual > cbValue))
274 rc = VERR_BUFFER_OVERFLOW;
275 if (RT_SUCCESS(rc) && (rc != VINF_BUFFER_OVERFLOW))
276 rc = CFGMR3QueryString(mpNode, pszKey, pszValue, cbValue);
277 if (RT_SUCCESS(rc) && (rc != VINF_BUFFER_OVERFLOW))
278 Log2(("Queried string %s, rc=%Rrc, value=%.*s\n", pszKey, rc, cbValue, pszValue));
279 else if (VERR_CFGM_VALUE_NOT_FOUND == rc)
280 rc = VERR_NOT_FOUND;
281 LogFlowThisFunc(("rc = %Rrc\n", rc));
282 return rc;
283}
284
285
286/**
287 * Set a value in the guest registry by key, checking the validity
288 * of the arguments passed.
289 *
290 * @returns iprt status value
291 * @param cParms the number of HGCM parameters supplied
292 * @param paParms the array of HGCM parameters
293 * @thread HGCM
294 */
295int Service::setKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
296{
297 int rc = VINF_SUCCESS;
298 char *pszKey, *pszValue;
299 uint32_t cbKey, cbValue;
300
301 LogFlowThisFunc(("\n"));
302 if ( (cParms != 2) /* Hardcoded value as the next lines depend on it. */
303 || (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) /* key */
304 || (paParms[1].type != VBOX_HGCM_SVC_PARM_PTR) /* value */
305 )
306 rc = VERR_INVALID_PARAMETER;
307 if (RT_SUCCESS(rc))
308 rc = VBoxHGCMParmPtrGet(&paParms[0], (void **) &pszKey, &cbKey);
309 if (RT_SUCCESS(rc))
310 rc = VBoxHGCMParmPtrGet(&paParms[1], (void **) &pszValue, &cbValue);
311 if (RT_SUCCESS(rc))
312 rc = validateKey(pszKey, cbKey);
313 if (RT_SUCCESS(rc))
314 rc = validateValue(pszValue, cbValue);
315 if (RT_SUCCESS(rc))
316 {
317 /* Limit the number of keys that we can set. */
318 unsigned cChildren = 0;
319 for (PCFGMNODE pChild = CFGMR3GetFirstChild(mpNode); pChild != 0; pChild = CFGMR3GetNextChild(pChild))
320 ++cChildren;
321 if (cChildren >= KEY_MAX_KEYS)
322 rc = VERR_TOO_MUCH_DATA;
323 }
324 if (RT_SUCCESS(rc))
325 {
326 CFGMR3RemoveValue(mpNode, pszKey);
327 if (pszValue > 0)
328 rc = CFGMR3InsertString(mpNode, pszKey, pszValue);
329 }
330 if (RT_SUCCESS(rc))
331 Log2(("Set string %s, rc=%Rrc, value=%s\n", pszKey, rc, pszValue));
332 LogFlowThisFunc(("rc = %Rrc\n", rc));
333 return rc;
334}
335
336
337/**
338 * Remove a value in the guest registry by key, checking the validity
339 * of the arguments passed.
340 *
341 * @returns iprt status value
342 * @param cParms the number of HGCM parameters supplied
343 * @param paParms the array of HGCM parameters
344 * @thread HGCM
345 */
346int Service::delKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
347{
348 int rc = VINF_SUCCESS;
349 char *pszKey, *pszValue;
350 uint32_t cbKey, cbValue;
351
352 LogFlowThisFunc(("\n"));
353 if ( (cParms != 1) /* Hardcoded value as the next lines depend on it. */
354 || (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) /* key */
355 )
356 rc = VERR_INVALID_PARAMETER;
357 if (RT_SUCCESS(rc))
358 rc = VBoxHGCMParmPtrGet(&paParms[0], (void **) &pszKey, &cbKey);
359 if (RT_SUCCESS(rc))
360 rc = validateKey(pszKey, cbKey);
361 if (RT_SUCCESS(rc))
362 CFGMR3RemoveValue(mpNode, pszKey);
363 LogFlowThisFunc(("rc = %Rrc\n", rc));
364 return rc;
365}
366
367
368/**
369 * Handle an HGCM service call.
370 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
371 * @note All functions which do not involve an unreasonable delay will be
372 * handled synchronously. If needed, we will add a request handler
373 * thread in future for those which do.
374 *
375 * @thread HGCM
376 */
377void Service::call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
378 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
379 VBOXHGCMSVCPARM paParms[])
380{
381 int rc = VINF_SUCCESS;
382 bool fCallSync = true;
383
384 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
385 u32ClientID, eFunction, cParms, paParms));
386
387 switch (eFunction)
388 {
389 /* The guest wishes to read a configuration value */
390 case GET_CONFIG_KEY:
391 LogFlowFunc(("GET_CONFIG_KEY\n"));
392 rc = getKey(cParms, paParms);
393 break;
394
395 /* The guest wishes to set a configuration value */
396 case SET_CONFIG_KEY:
397 LogFlowFunc(("SET_CONFIG_KEY\n"));
398 rc = setKey(cParms, paParms);
399 break;
400
401 /* The guest wishes to remove a configuration value */
402 case DEL_CONFIG_KEY:
403 LogFlowFunc(("DEL_CONFIG_KEY\n"));
404 rc = delKey(cParms, paParms);
405 break;
406
407 default:
408 rc = VERR_NOT_IMPLEMENTED;
409 }
410 if (fCallSync)
411 {
412 LogFlowFunc(("rc = %Rrc\n", rc));
413 mpHelpers->pfnCallComplete (callHandle, rc);
414 }
415}
416
417
418/**
419 * Service call handler for the host.
420 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
421 * @thread hgcm
422 */
423int Service::hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
424{
425 int rc = VINF_SUCCESS;
426
427 LogFlowFunc(("fn = %d, cParms = %d, pparms = %d\n",
428 eFunction, cParms, paParms));
429
430 switch (eFunction)
431 {
432 /* Set the root CFGM node used. This should be called when instantiating
433 * the service. */
434 case SET_CFGM_NODE:
435 {
436 LogFlowFunc(("SET_CFGM_NODE\n"));
437
438 if (cParms != 1)
439 {
440 rc = VERR_INVALID_PARAMETER;
441 }
442 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pNode */
443 )
444 {
445 rc = VERR_INVALID_PARAMETER;
446 }
447 else
448 {
449 PCFGMNODE pNode = NULL;
450 uint32_t cbDummy;
451
452 rc = VBoxHGCMParmPtrGet (&paParms[0], (void **) &pNode, &cbDummy);
453 mpNode = pNode;
454 }
455 } break;
456
457 /* The host wishes to read a configuration value */
458 case GET_CONFIG_KEY_HOST:
459 LogFlowFunc(("GET_CONFIG_KEY_HOST\n"));
460 rc = getKey(cParms, paParms);
461 break;
462
463 /* The host wishes to set a configuration value */
464 case SET_CONFIG_KEY_HOST:
465 LogFlowFunc(("SET_CONFIG_KEY_HOST\n"));
466 rc = setKey(cParms, paParms);
467 break;
468
469 /* The host wishes to remove a configuration value */
470 case DEL_CONFIG_KEY_HOST:
471 LogFlowFunc(("DEL_CONFIG_KEY_HOST\n"));
472 rc = delKey(cParms, paParms);
473 break;
474
475 default:
476 rc = VERR_NOT_SUPPORTED;
477 break;
478 }
479
480 LogFlowFunc(("rc = %Vrc\n", rc));
481 return rc;
482}
483
484} /* namespace svcInfo */
485
486using svcInfo::Service;
487
488/**
489 * @copydoc VBOXHGCMSVCLOAD
490 */
491extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
492{
493 int rc = VINF_SUCCESS;
494
495 LogFlowFunc(("ptable = %p\n", ptable));
496
497 if (!VALID_PTR(ptable))
498 {
499 rc = VERR_INVALID_PARAMETER;
500 }
501 else
502 {
503 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
504
505 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
506 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
507 {
508 rc = VERR_VERSION_MISMATCH;
509 }
510 else
511 {
512 std::auto_ptr<Service> apService;
513 try {
514 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
515 } catch (...) {
516 /* No exceptions may propogate outside. */
517 rc = VERR_UNRESOLVED_ERROR;
518 }
519
520 if (RT_SUCCESS(rc))
521 {
522 /* We do not maintain connections, so no client data is needed. */
523 ptable->cbClient = 0;
524
525 ptable->pfnUnload = Service::svcUnload;
526 ptable->pfnConnect = Service::svcConnectDisconnect;
527 ptable->pfnDisconnect = Service::svcConnectDisconnect;
528 ptable->pfnCall = Service::svcCall;
529 ptable->pfnHostCall = Service::svcHostCall;
530 ptable->pfnSaveState = NULL; /* The service is stateless by definition, so the */
531 ptable->pfnLoadState = NULL; /* normal construction done before restoring suffices */
532 ptable->pfnRegisterExtension = NULL;
533
534 /* Service specific initialization. */
535 ptable->pvService = apService.release();
536 }
537 }
538 }
539
540 LogFlowFunc(("returning %Rrc\n", rc));
541 return rc;
542}
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