VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp

Last change on this file was 102753, checked in by vboxsync, 4 months ago

Guest Properties/VBoxService: Deal with long(er) user name / domain name combinations on Windows guests. See comments for details. bugref:10575

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.2 KB
Line 
1/* $Id: VBoxServicePropCache.cpp 102753 2024-01-03 17:24:32Z vboxsync $ */
2/** @file
3 * VBoxServicePropCache - Guest property cache.
4 */
5
6/*
7 * Copyright (C) 2010-2023 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#include <iprt/assert.h>
33#include <iprt/list.h>
34#include <iprt/mem.h>
35#include <iprt/string.h>
36
37#include <VBox/VBoxGuestLib.h>
38#include <VBox/HostServices/GuestPropertySvc.h> /* For GUEST_PROP_MAX_VALUE_LEN */
39#include "VBoxServiceInternal.h"
40#include "VBoxServiceUtils.h"
41#include "VBoxServicePropCache.h"
42
43
44
45/** @todo Docs */
46static PVBOXSERVICEVEPROPCACHEENTRY vgsvcPropCacheFindInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName,
47 uint32_t fFlags)
48{
49 RT_NOREF1(fFlags);
50 AssertPtrReturn(pCache, NULL);
51 AssertPtrReturn(pszName, NULL);
52
53 /** @todo This is a O(n) lookup, maybe improve this later to O(1) using a
54 * map.
55 * r=bird: Use a string space (RTstrSpace*). That is O(log n) in its current
56 * implementation (AVL tree). However, this is not important at the
57 * moment. */
58 PVBOXSERVICEVEPROPCACHEENTRY pNode = NULL;
59 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
60 {
61 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
62 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
63 {
64 if (strcmp(pNodeIt->pszName, pszName) == 0)
65 {
66 pNode = pNodeIt;
67 break;
68 }
69 }
70 RTCritSectLeave(&pCache->CritSect);
71 }
72 return pNode;
73}
74
75
76/** @todo Docs */
77static PVBOXSERVICEVEPROPCACHEENTRY vgsvcPropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName)
78{
79 AssertPtrReturn(pCache, NULL);
80 AssertPtrReturn(pszName, NULL);
81
82 PVBOXSERVICEVEPROPCACHEENTRY pNode = (PVBOXSERVICEVEPROPCACHEENTRY)RTMemAlloc(sizeof(VBOXSERVICEVEPROPCACHEENTRY));
83 if (pNode)
84 {
85 pNode->pszName = RTStrDup(pszName);
86 if (!pNode->pszName)
87 {
88 RTMemFree(pNode);
89 return NULL;
90 }
91 pNode->pszValue = NULL;
92 pNode->fFlags = 0;
93 pNode->pszValueReset = NULL;
94
95 int rc = RTCritSectEnter(&pCache->CritSect);
96 if (RT_SUCCESS(rc))
97 {
98 RTListAppend(&pCache->NodeHead, &pNode->NodeSucc);
99 rc = RTCritSectLeave(&pCache->CritSect);
100 }
101 }
102 return pNode;
103}
104
105
106/** @todo Docs */
107static int vgsvcPropCacheWritePropF(uint32_t u32ClientId, const char *pszName, uint32_t fFlags, const char *pszValueFormat, ...)
108{
109 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
110
111 int rc;
112 if (pszValueFormat != NULL)
113 {
114 va_list va;
115 va_start(va, pszValueFormat);
116
117 char *pszValue;
118 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
119 {
120 if (fFlags & VGSVCPROPCACHE_FLAGS_TRANSIENT)
121 {
122 /*
123 * Because a value can be temporary we have to make sure it also
124 * gets deleted when the property cache did not have the chance to
125 * gracefully clean it up (due to a hard VM reset etc), so set this
126 * guest property using the TRANSRESET flag..
127 */
128 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, "TRANSRESET");
129 if (rc == VERR_PARSE_ERROR)
130 {
131 /* Host does not support the "TRANSRESET" flag, so only
132 * use the "TRANSIENT" flag -- better than nothing :-). */
133 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, "TRANSIENT");
134 /** @todo r=bird: Remember that the host doesn't support
135 * this. */
136 }
137 }
138 else
139 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue /* No transient flags set */);
140 RTStrFree(pszValue);
141 }
142 else
143 rc = VERR_NO_MEMORY;
144 va_end(va);
145 }
146 else
147 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
148 return rc;
149}
150
151
152/**
153 * Creates a property cache.
154 *
155 * @returns IPRT status code.
156 * @param pCache Pointer to the cache.
157 * @param uClientId The HGCM handle of to the guest property service.
158 */
159int VGSvcPropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId)
160{
161 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
162 /** @todo Prevent init the cache twice!
163 * r=bird: Use a magic. */
164 RTListInit(&pCache->NodeHead);
165 pCache->uClientID = uClientId;
166 return RTCritSectInit(&pCache->CritSect);
167}
168
169
170/**
171 * Updates a cache entry without submitting any changes to the host.
172 *
173 * This is handy for defining default values/flags.
174 *
175 * @returns VBox status code.
176 *
177 * @param pCache The property cache.
178 * @param pszName The property name.
179 * @param fFlags The property flags to set.
180 * @param pszValueReset The property reset value.
181 */
182int VGSvcPropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t fFlags, const char *pszValueReset)
183{
184 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
185 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
186 PVBOXSERVICEVEPROPCACHEENTRY pNode = vgsvcPropCacheFindInternal(pCache, pszName, 0);
187 if (pNode == NULL)
188 pNode = vgsvcPropCacheInsertEntryInternal(pCache, pszName);
189
190 int rc;
191 if (pNode != NULL)
192 {
193 rc = RTCritSectEnter(&pCache->CritSect);
194 if (RT_SUCCESS(rc))
195 {
196 pNode->fFlags = fFlags;
197 if (pszValueReset)
198 {
199 if (pNode->pszValueReset)
200 RTStrFree(pNode->pszValueReset);
201 pNode->pszValueReset = RTStrDup(pszValueReset);
202 AssertPtr(pNode->pszValueReset);
203 }
204 rc = RTCritSectLeave(&pCache->CritSect);
205 }
206 }
207 else
208 rc = VERR_NO_MEMORY;
209 return rc;
210}
211
212
213/**
214 * Updates the local guest property cache and writes it to HGCM if outdated.
215 *
216 * @returns VBox status code.
217 * @retval VERR_BUFFER_OVERFLOW if the property name or value exceeds the limit.
218 * @param pCache The property cache.
219 * @param pszName The property name.
220 * @param pszValueFormat The property format string. If this is NULL then
221 * the property will be deleted (if possible).
222 * @param ... Format arguments.
223 */
224int VGSvcPropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...)
225{
226 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
227 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
228
229 Assert(pCache->uClientID);
230
231 if (RTStrNLen(pszName, GUEST_PROP_MAX_NAME_LEN) > GUEST_PROP_MAX_NAME_LEN - 1 /* Terminator */)
232 return VERR_BUFFER_OVERFLOW;
233
234 /*
235 * Format the value first.
236 */
237 char *pszValue = NULL;
238 if (pszValueFormat)
239 {
240 va_list va;
241 va_start(va, pszValueFormat);
242 RTStrAPrintfV(&pszValue, pszValueFormat, va);
243 va_end(va);
244 if (!pszValue)
245 return VERR_NO_STR_MEMORY;
246 if (RTStrNLen(pszValue, GUEST_PROP_MAX_VALUE_LEN) > GUEST_PROP_MAX_VALUE_LEN - 1 /* Terminator */)
247 {
248 RTStrFree(pszValue);
249 return VERR_BUFFER_OVERFLOW;
250 }
251 }
252
253 PVBOXSERVICEVEPROPCACHEENTRY pNode = vgsvcPropCacheFindInternal(pCache, pszName, 0);
254
255 /* Lock the cache. */
256 int rc = RTCritSectEnter(&pCache->CritSect);
257 if (RT_SUCCESS(rc))
258 {
259 if (pNode == NULL)
260 pNode = vgsvcPropCacheInsertEntryInternal(pCache, pszName);
261
262 AssertPtr(pNode);
263 if (pszValue) /* Do we have a value to check for? */
264 {
265 bool fUpdate = false;
266 /* Always update this property, no matter what? */
267 if (pNode->fFlags & VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE)
268 fUpdate = true;
269 /* Did the value change so we have to update? */
270 else if (pNode->pszValue && strcmp(pNode->pszValue, pszValue) != 0)
271 fUpdate = true;
272 /* No value stored at the moment but we have a value now? */
273 else if (pNode->pszValue == NULL)
274 fUpdate = true;
275
276 if (fUpdate)
277 {
278 /* Write the update. */
279 rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pszValue);
280 VGSvcVerbose(4, "[PropCache %p]: Written '%s'='%s' (flags: %x), rc=%Rrc\n",
281 pCache, pNode->pszName, pszValue, pNode->fFlags, rc);
282 if (RT_SUCCESS(rc)) /* Only update the node's value on successful write. */
283 {
284 RTStrFree(pNode->pszValue);
285 pNode->pszValue = RTStrDup(pszValue);
286 if (!pNode->pszValue)
287 rc = VERR_NO_MEMORY;
288 }
289 }
290 else
291 rc = VINF_NO_CHANGE; /* No update needed. */
292 }
293 else
294 {
295 /* No value specified. Deletion (or no action required). */
296 if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */
297 {
298 rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName,
299 0, /* Flags */ NULL /* Value */);
300 VGSvcVerbose(4, "[PropCache %p]: Deleted '%s'='%s' (flags: %x), rc=%Rrc\n",
301 pCache, pNode->pszName, pNode->pszValue, pNode->fFlags, rc);
302 if (RT_SUCCESS(rc)) /* Only delete property value on successful Vbgl deletion. */
303 {
304 /* Delete property (but do not remove from cache) if not deleted yet. */
305 RTStrFree(pNode->pszValue);
306 pNode->pszValue = NULL;
307 }
308 }
309 else
310 rc = VINF_NO_CHANGE; /* No update needed. */
311 }
312
313 /* Release cache. */
314 RTCritSectLeave(&pCache->CritSect);
315 }
316
317 VGSvcVerbose(4, "[PropCache %p]: Updating '%s' resulted in rc=%Rrc\n", pCache, pszName, rc);
318
319 /* Delete temp stuff. */
320 RTStrFree(pszValue);
321 return rc;
322}
323
324
325/**
326 * Updates all cache values which are matching the specified path.
327 *
328 * @returns VBox status code.
329 *
330 * @param pCache The property cache.
331 * @param pszValue The value to set. A NULL will delete the value.
332 * @param fFlags Flags to set.
333 * @param pszPathFormat The path format string. May not be null and has
334 * to be an absolute path.
335 * @param ... Format arguments.
336 */
337int VGSvcPropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags,
338 const char *pszPathFormat, ...)
339{
340 RT_NOREF1(fFlags);
341 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
342 AssertPtrReturn(pszPathFormat, VERR_INVALID_POINTER);
343
344 int rc = VERR_NOT_FOUND;
345 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
346 {
347 /*
348 * Format the value first.
349 */
350 char *pszPath = NULL;
351 va_list va;
352 va_start(va, pszPathFormat);
353 RTStrAPrintfV(&pszPath, pszPathFormat, va);
354 va_end(va);
355 if (!pszPath)
356 {
357 rc = VERR_NO_STR_MEMORY;
358 }
359 else
360 {
361 /* Iterate through all nodes and compare their paths. */
362 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
363 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
364 {
365 if (RTStrStr(pNodeIt->pszName, pszPath) == pNodeIt->pszName)
366 {
367 /** @todo Use some internal function to update the node directly, this is slow atm. */
368 rc = VGSvcPropCacheUpdate(pCache, pNodeIt->pszName, pszValue);
369 }
370 if (RT_FAILURE(rc))
371 break;
372 }
373 RTStrFree(pszPath);
374 }
375 RTCritSectLeave(&pCache->CritSect);
376 }
377 return rc;
378}
379
380
381/**
382 * Flushes the cache by writing every item regardless of its state.
383 *
384 * @param pCache The property cache.
385 */
386int VGSvcPropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache)
387{
388 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
389
390 int rc = VINF_SUCCESS;
391 if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect)))
392 {
393 PVBOXSERVICEVEPROPCACHEENTRY pNodeIt;
394 RTListForEach(&pCache->NodeHead, pNodeIt, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc)
395 {
396 rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNodeIt->pszName, pNodeIt->fFlags, pNodeIt->pszValue);
397 if (RT_FAILURE(rc))
398 break;
399 }
400 RTCritSectLeave(&pCache->CritSect);
401 }
402 return rc;
403}
404
405
406/**
407 * Reset all temporary properties and destroy the cache.
408 *
409 * @param pCache The property cache.
410 */
411void VGSvcPropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache)
412{
413 AssertPtrReturnVoid(pCache);
414 Assert(pCache->uClientID);
415
416 /* Lock the cache. */
417 int rc = RTCritSectEnter(&pCache->CritSect);
418 if (RT_SUCCESS(rc))
419 {
420 PVBOXSERVICEVEPROPCACHEENTRY pNode = RTListGetFirst(&pCache->NodeHead, VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
421 while (pNode)
422 {
423 PVBOXSERVICEVEPROPCACHEENTRY pNext = RTListNodeIsLast(&pCache->NodeHead, &pNode->NodeSucc)
424 ? NULL :
425 RTListNodeGetNext(&pNode->NodeSucc,
426 VBOXSERVICEVEPROPCACHEENTRY, NodeSucc);
427 RTListNodeRemove(&pNode->NodeSucc);
428
429 if (pNode->fFlags & VGSVCPROPCACHE_FLAGS_TEMPORARY)
430 rc = vgsvcPropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pNode->pszValueReset);
431
432 AssertPtr(pNode->pszName);
433 RTStrFree(pNode->pszName);
434 RTStrFree(pNode->pszValue);
435 RTStrFree(pNode->pszValueReset);
436 pNode->fFlags = 0;
437
438 RTMemFree(pNode);
439
440 pNode = pNext;
441 }
442 RTCritSectLeave(&pCache->CritSect);
443 }
444
445 /* Destroy critical section. */
446 RTCritSectDelete(&pCache->CritSect);
447}
448
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use