VirtualBox

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

Last change on this file was 98709, checked in by vboxsync, 14 months ago

Guest Control: Implemented directory handling / walking as non-toolbox variants. bugref:9783

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.9 KB
Line 
1/* $Id: VBoxServiceUtils.cpp 98709 2023-02-24 08:49:40Z vboxsync $ */
2/** @file
3 * VBoxServiceUtils - Some utility functions.
4 */
5
6/*
7 * Copyright (C) 2009-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#ifdef RT_OS_WINDOWS
33# include <iprt/win/windows.h>
34# include <iprt/param.h>
35# include <iprt/path.h>
36#endif
37#include <iprt/assert.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40
41#include <VBox/VBoxGuestLib.h>
42#include "VBoxServiceInternal.h"
43#include "VBoxServiceUtils.h"
44
45
46#ifdef VBOX_WITH_GUEST_PROPS
47/**
48 * Reads a guest property as a 32-bit value.
49 *
50 * @returns VBox status code, fully bitched.
51 *
52 * @param u32ClientId The HGCM client ID for the guest property session.
53 * @param pszPropName The property name.
54 * @param pu32 Where to store the 32-bit value.
55 *
56 */
57int VGSvcReadPropUInt32(uint32_t u32ClientId, const char *pszPropName, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
58{
59 char *pszValue;
60 int rc = VbglR3GuestPropReadEx(u32ClientId, pszPropName, &pszValue, NULL /* ppszFlags */, NULL /* puTimestamp */);
61 if (RT_SUCCESS(rc))
62 {
63 char *pszNext;
64 rc = RTStrToUInt32Ex(pszValue, &pszNext, 0, pu32);
65 if ( RT_SUCCESS(rc)
66 && (*pu32 < u32Min || *pu32 > u32Max))
67 rc = VGSvcError("The guest property value %s = %RU32 is out of range [%RU32..%RU32].\n",
68 pszPropName, *pu32, u32Min, u32Max);
69 RTStrFree(pszValue);
70 }
71 return rc;
72}
73
74/**
75 * Reads a guest property from the host side.
76 *
77 * @returns IPRT status code, fully bitched.
78 * @param u32ClientId The HGCM client ID for the guest property session.
79 * @param pszPropName The property name.
80 * @param fReadOnly Whether or not this property needs to be read only
81 * by the guest side. Otherwise VERR_ACCESS_DENIED will
82 * be returned.
83 * @param ppszValue Where to return the value. This is always set
84 * to NULL. Free it using RTStrFree().
85 * @param ppszFlags Where to return the value flags. Free it
86 * using RTStrFree(). Optional.
87 * @param puTimestamp Where to return the timestamp. This is only set
88 * on success. Optional.
89 */
90int VGSvcReadHostProp(uint32_t u32ClientId, const char *pszPropName, bool fReadOnly,
91 char **ppszValue, char **ppszFlags, uint64_t *puTimestamp)
92{
93 AssertPtrReturn(ppszValue, VERR_INVALID_PARAMETER);
94
95 char *pszValue = NULL;
96 char *pszFlags = NULL;
97 int rc = VbglR3GuestPropReadEx(u32ClientId, pszPropName, &pszValue, &pszFlags, puTimestamp);
98 if (RT_SUCCESS(rc))
99 {
100 /* Check security bits. */
101 if ( fReadOnly /* Do we except a guest read-only property */
102 && !RTStrStr(pszFlags, "RDONLYGUEST"))
103 {
104 /* If we want a property which is read-only on the guest
105 * and it is *not* marked as such, deny access! */
106 rc = VERR_ACCESS_DENIED;
107 }
108
109 if (RT_SUCCESS(rc))
110 {
111 *ppszValue = pszValue;
112
113 if (ppszFlags)
114 *ppszFlags = pszFlags;
115 else if (pszFlags)
116 RTStrFree(pszFlags);
117 }
118 else
119 {
120 if (pszValue)
121 RTStrFree(pszValue);
122 if (pszFlags)
123 RTStrFree(pszFlags);
124 }
125 }
126
127 return rc;
128}
129
130
131/**
132 * Wrapper around VbglR3GuestPropWriteValue that does value formatting and
133 * logging.
134 *
135 * @returns VBox status code. Errors will be logged.
136 *
137 * @param u32ClientId The HGCM client ID for the guest property session.
138 * @param pszName The property name.
139 * @param pszValueFormat The property format string. If this is NULL then
140 * the property will be deleted (if possible).
141 * @param ... Format arguments.
142 */
143int VGSvcWritePropF(uint32_t u32ClientId, const char *pszName, const char *pszValueFormat, ...)
144{
145 AssertPtr(pszName);
146 int rc;
147 if (pszValueFormat != NULL)
148 {
149 va_list va;
150 va_start(va, pszValueFormat);
151 VGSvcVerbose(3, "Writing guest property '%s' = '%N'\n", pszName, pszValueFormat, &va);
152 va_end(va);
153
154 va_start(va, pszValueFormat);
155 rc = VbglR3GuestPropWriteValueV(u32ClientId, pszName, pszValueFormat, va);
156 va_end(va);
157
158 if (RT_FAILURE(rc))
159 VGSvcError("Error writing guest property '%s' (rc=%Rrc)\n", pszName, rc);
160 }
161 else
162 {
163 VGSvcVerbose(3, "Deleting guest property '%s'\n", pszName);
164 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, NULL);
165 if (RT_FAILURE(rc))
166 VGSvcError("Error deleting guest property '%s' (rc=%Rrc)\n", pszName, rc);
167 }
168 return rc;
169}
170
171#endif /* VBOX_WITH_GUEST_PROPS */
172#ifdef RT_OS_WINDOWS
173
174/**
175 * Helper for vgsvcUtilGetFileVersion and attempts to read and parse
176 * FileVersion.
177 *
178 * @returns Success indicator.
179 */
180static bool vgsvcUtilGetFileVersionOwn(LPSTR pVerData, uint32_t *puMajor, uint32_t *puMinor,
181 uint32_t *puBuildNumber, uint32_t *puRevisionNumber)
182{
183 UINT cchStrValue = 0;
184 LPTSTR pStrValue = NULL;
185 if (!VerQueryValueA(pVerData, "\\StringFileInfo\\040904b0\\FileVersion", (LPVOID *)&pStrValue, &cchStrValue))
186 return false;
187
188 char *pszNext = pStrValue;
189 int rc = RTStrToUInt32Ex(pszNext, &pszNext, 0, puMajor);
190 AssertReturn(rc == VWRN_TRAILING_CHARS, false);
191 AssertReturn(*pszNext == '.', false);
192
193 rc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 0, puMinor);
194 AssertReturn(rc == VWRN_TRAILING_CHARS, false);
195 AssertReturn(*pszNext == '.', false);
196
197 rc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 0, puBuildNumber);
198 AssertReturn(rc == VWRN_TRAILING_CHARS, false);
199 AssertReturn(*pszNext == '.', false);
200
201 rc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 0, puRevisionNumber);
202 AssertReturn(rc == VINF_SUCCESS || rc == VWRN_TRAILING_CHARS /*??*/, false);
203
204 return true;
205}
206
207
208/**
209 * Worker for VGSvcUtilWinGetFileVersionString.
210 *
211 * @returns VBox status code.
212 * @param pszFilename ASCII & ANSI & UTF-8 compliant name.
213 * @param puMajor Where to return the major version number.
214 * @param puMinor Where to return the minor version number.
215 * @param puBuildNumber Where to return the build number.
216 * @param puRevisionNumber Where to return the revision number.
217 */
218static int vgsvcUtilGetFileVersion(const char *pszFilename, uint32_t *puMajor, uint32_t *puMinor, uint32_t *puBuildNumber,
219 uint32_t *puRevisionNumber)
220{
221 int rc;
222
223 *puMajor = *puMinor = *puBuildNumber = *puRevisionNumber = 0;
224
225 /*
226 * Get the file version info.
227 */
228 DWORD dwHandleIgnored;
229 DWORD cbVerData = GetFileVersionInfoSizeA(pszFilename, &dwHandleIgnored);
230 if (cbVerData)
231 {
232 LPTSTR pVerData = (LPTSTR)RTMemTmpAllocZ(cbVerData);
233 if (pVerData)
234 {
235 if (GetFileVersionInfoA(pszFilename, dwHandleIgnored, cbVerData, pVerData))
236 {
237 /*
238 * Try query and parse the FileVersion string our selves first
239 * since this will give us the correct revision number when
240 * it goes beyond the range of an uint16_t / WORD.
241 */
242 if (vgsvcUtilGetFileVersionOwn(pVerData, puMajor, puMinor, puBuildNumber, puRevisionNumber))
243 rc = VINF_SUCCESS;
244 else
245 {
246 /* Fall back on VS_FIXEDFILEINFO */
247 UINT cbFileInfoIgnored = 0;
248 VS_FIXEDFILEINFO *pFileInfo = NULL;
249 if (VerQueryValue(pVerData, "\\", (LPVOID *)&pFileInfo, &cbFileInfoIgnored))
250 {
251 *puMajor = HIWORD(pFileInfo->dwFileVersionMS);
252 *puMinor = LOWORD(pFileInfo->dwFileVersionMS);
253 *puBuildNumber = HIWORD(pFileInfo->dwFileVersionLS);
254 *puRevisionNumber = LOWORD(pFileInfo->dwFileVersionLS);
255 rc = VINF_SUCCESS;
256 }
257 else
258 {
259 rc = RTErrConvertFromWin32(GetLastError());
260 VGSvcVerbose(3, "No file version value for file '%s' available! (%d / rc=%Rrc)\n",
261 pszFilename, GetLastError(), rc);
262 }
263 }
264 }
265 else
266 {
267 rc = RTErrConvertFromWin32(GetLastError());
268 VGSvcVerbose(0, "GetFileVersionInfo(%s) -> %u / %Rrc\n", pszFilename, GetLastError(), rc);
269 }
270
271 RTMemTmpFree(pVerData);
272 }
273 else
274 {
275 VGSvcVerbose(0, "Failed to allocate %u byte for file version info for '%s'\n", cbVerData, pszFilename);
276 rc = VERR_NO_TMP_MEMORY;
277 }
278 }
279 else
280 {
281 rc = RTErrConvertFromWin32(GetLastError());
282 VGSvcVerbose(3, "GetFileVersionInfoSize(%s) -> %u / %Rrc\n", pszFilename, GetLastError(), rc);
283 }
284 return rc;
285}
286
287
288/**
289 * Gets a re-formatted version string from the VS_FIXEDFILEINFO table.
290 *
291 * @returns VBox status code. The output buffer is always valid and the status
292 * code can safely be ignored.
293 *
294 * @param pszPath The base path.
295 * @param pszFilename The filename.
296 * @param pszVersion Where to return the version string.
297 * @param cbVersion The size of the version string buffer. This MUST be
298 * at least 2 bytes!
299 */
300int VGSvcUtilWinGetFileVersionString(const char *pszPath, const char *pszFilename, char *pszVersion, size_t cbVersion)
301{
302 /*
303 * We will ALWAYS return with a valid output buffer.
304 */
305 AssertReturn(cbVersion >= 2, VERR_BUFFER_OVERFLOW);
306 pszVersion[0] = '-';
307 pszVersion[1] = '\0';
308
309 /*
310 * Create the path and query the bits.
311 */
312 char szFullPath[RTPATH_MAX];
313 int rc = RTPathJoin(szFullPath, sizeof(szFullPath), pszPath, pszFilename);
314 if (RT_SUCCESS(rc))
315 {
316 uint32_t uMajor, uMinor, uBuild, uRev;
317 rc = vgsvcUtilGetFileVersion(szFullPath, &uMajor, &uMinor, &uBuild, &uRev);
318 if (RT_SUCCESS(rc))
319 RTStrPrintf(pszVersion, cbVersion, "%u.%u.%ur%u", uMajor, uMinor, uBuild, uRev);
320 }
321 return rc;
322}
323
324#endif /* RT_OS_WINDOWS */
325
326
327/**
328 * Resolves the UID to a name as best as we can.
329 *
330 * @returns Read-only name string. Only valid till the next cache call.
331 * @param pIdCache The ID cache.
332 * @param uid The UID to resolve.
333 * @param pszEntry The filename of the UID.
334 * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
335 */
336const char *VGSvcIdCacheGetUidName(PVGSVCIDCACHE pIdCache, RTUID uid, const char *pszEntry, const char *pszRelativeTo)
337{
338 /* Check cached entries. */
339 for (uint32_t i = 0; i < pIdCache->cEntries; i++)
340 if ( pIdCache->aEntries[i].id == uid
341 && pIdCache->aEntries[i].fIsUid)
342 return pIdCache->aEntries[i].szName;
343
344 /* Miss. */
345 RTFSOBJINFO ObjInfo;
346 RT_ZERO(ObjInfo); /* shut up msc */
347 int rc;
348 if (!pszRelativeTo)
349 rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
350 else
351 {
352 char szPath[RTPATH_MAX];
353 rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
354 if (RT_SUCCESS(rc))
355 rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
356 }
357
358 if ( RT_SUCCESS(rc)
359 && ObjInfo.Attr.u.UnixOwner.uid == uid)
360 {
361 uint32_t i = pIdCache->cEntries;
362 if (i < RT_ELEMENTS(pIdCache->aEntries))
363 pIdCache->cEntries = i + 1;
364 else
365 i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
366 pIdCache->aEntries[i].id = uid;
367 pIdCache->aEntries[i].fIsUid = true;
368 RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixOwner.szName);
369 return pIdCache->aEntries[i].szName;
370 }
371 return "";
372}
373
374
375/**
376 * Resolves the GID to a name as best as we can.
377 *
378 * @returns Read-only name string. Only valid till the next cache call.
379 * @param pIdCache The ID cache.
380 * @param gid The GID to resolve.
381 * @param pszEntry The filename of the GID.
382 * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
383 */
384const char *VGSvcIdCacheGetGidName(PVGSVCIDCACHE pIdCache, RTGID gid, const char *pszEntry, const char *pszRelativeTo)
385{
386 /* Check cached entries. */
387 for (uint32_t i = 0; i < pIdCache->cEntries; i++)
388 if ( pIdCache->aEntries[i].id == gid
389 && !pIdCache->aEntries[i].fIsUid)
390 return pIdCache->aEntries[i].szName;
391
392 /* Miss. */
393 RTFSOBJINFO ObjInfo;
394 RT_ZERO(ObjInfo); /* shut up msc */
395 int rc;
396 if (!pszRelativeTo)
397 rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
398 else
399 {
400 char szPath[RTPATH_MAX];
401 rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
402 if (RT_SUCCESS(rc))
403 rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
404 }
405
406 if ( RT_SUCCESS(rc)
407 && ObjInfo.Attr.u.UnixGroup.gid == gid)
408 {
409 uint32_t i = pIdCache->cEntries;
410 if (i < RT_ELEMENTS(pIdCache->aEntries))
411 pIdCache->cEntries = i + 1;
412 else
413 i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
414 pIdCache->aEntries[i].id = gid;
415 pIdCache->aEntries[i].fIsUid = false;
416 RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixGroup.szName);
417 return pIdCache->aEntries[i].szName;
418 }
419 return "";
420}
421
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use