VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/sysfs.cpp@ 25659

Last change on this file since 25659 was 25292, checked in by vboxsync, 15 years ago

RTDirReadEx parameter to resolve symlinks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.4 KB
Line 
1/* $Id: sysfs.cpp 25292 2009-12-10 10:29:57Z vboxsync $ */
2/** @file
3 * IPRT - Linux sysfs access.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#define LOG_GROUP RTLOGGROUP_SYSTEM
36#include <iprt/linux/sysfs.h>
37#include <iprt/assert.h>
38#include <iprt/dir.h>
39#include <iprt/err.h>
40#include <iprt/fs.h>
41#include <iprt/param.h>
42#include <iprt/path.h>
43#include <iprt/string.h>
44
45#include <unistd.h>
46#include <stdio.h>
47#include <sys/sysctl.h>
48#include <sys/stat.h>
49#include <sys/fcntl.h>
50#include <errno.h>
51
52/** @todo r=bird: This whole API should be rewritten to use IPRT status codes.
53 * using errno was a mistake and only results in horrible code. */
54
55
56/**
57 * Constructs the path of a sysfs file from the format paramaters passed,
58 * prepending a prefix if the path is relative.
59 *
60 * @returns The number of characters returned, or -1 and errno set to ERANGE on
61 * failure.
62 *
63 * @param pszPrefix The prefix to prepend if the path is relative. Must end
64 * in '/'.
65 * @param pszBuf Where to write the path. Must be at least
66 * sizeof(@a pszPrefix) characters long
67 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
68 * @param pszFormat The name format, either absolute or relative to the
69 * prefix specified by @a pszPrefix.
70 * @param va The format args.
71 */
72static ssize_t rtLinuxConstructPathV(char *pszBuf, size_t cchBuf,
73 const char *pszPrefix,
74 const char *pszFormat, va_list va)
75{
76 size_t cchPrefix = strlen(pszPrefix);
77 AssertReturnStmt(pszPrefix[cchPrefix - 1] == '/', errno = ERANGE, -1);
78 AssertReturnStmt(cchBuf > cchPrefix + 1, errno = ERANGE, -1);
79
80 /** @todo While RTStrPrintfV prevents overflows, it doesn't make it easy to
81 * check for truncations. RTPath should provide some formatters and
82 * joiners which can take over this rather common task that is
83 * performed here. */
84 size_t cch = RTStrPrintfV(pszBuf, cchBuf, pszFormat, va);
85 if (*pszBuf != '/')
86 {
87 AssertReturnStmt(cchBuf >= cch + cchPrefix + 1, errno = ERANGE, -1);
88 memmove(pszBuf + cchPrefix, pszBuf, cch + 1);
89 memcpy(pszBuf, pszPrefix, cchPrefix);
90 cch += cchPrefix;
91 }
92 return cch;
93}
94
95
96/**
97 * Constructs the path of a sysfs file from the format paramaters passed,
98 * prepending a prefix if the path is relative.
99 *
100 * @returns The number of characters returned, or -1 and errno set to ERANGE on
101 * failure.
102 *
103 * @param pszPrefix The prefix to prepend if the path is relative. Must end
104 * in '/'.
105 * @param pszBuf Where to write the path. Must be at least
106 * sizeof(@a pszPrefix) characters long
107 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
108 * @param pszFormat The name format, either absolute or relative to "/sys/".
109 * @param ... The format args.
110 */
111static ssize_t rtLinuxConstructPath(char *pszBuf, size_t cchBuf,
112 const char *pszPrefix,
113 const char *pszFormat, ...)
114{
115 va_list va;
116 va_start(va, pszFormat);
117 int rc = rtLinuxConstructPathV(pszBuf, cchBuf, pszPrefix, pszFormat, va);
118 va_end(va);
119 return rc;
120}
121
122
123/**
124 * Constructs the path of a sysfs file from the format paramaters passed,
125 * prepending "/sys/" if the path is relative.
126 *
127 * @returns The number of characters returned, or -1 and errno set to ERANGE on
128 * failure.
129 *
130 * @param pszBuf Where to write the path. Must be at least
131 * sizeof("/sys/") characters long
132 * @param cchBuf The size of the buffer pointed to by @a pszBuf.
133 * @param pszFormat The name format, either absolute or relative to "/sys/".
134 * @param va The format args.
135 */
136static ssize_t rtLinuxSysFsConstructPath(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
137{
138 return rtLinuxConstructPathV(pszBuf, cchBuf, "/sys/", pszFormat, va);
139}
140
141
142RTDECL(bool) RTLinuxSysFsExistsV(const char *pszFormat, va_list va)
143{
144 int iSavedErrno = errno;
145
146 /*
147 * Construct the filename and call stat.
148 */
149 bool fRet = false;
150 char szFilename[RTPATH_MAX];
151 ssize_t rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
152 if (rc != -1)
153 {
154 struct stat st;
155 fRet = stat(szFilename, &st) == 0;
156 }
157
158 errno = iSavedErrno;
159 return fRet;
160}
161
162
163RTDECL(bool) RTLinuxSysFsExists(const char *pszFormat, ...)
164{
165 va_list va;
166 va_start(va, pszFormat);
167 bool fRet = RTLinuxSysFsExistsV(pszFormat, va);
168 va_end(va);
169 return fRet;
170}
171
172
173RTDECL(int) RTLinuxSysFsOpenV(const char *pszFormat, va_list va)
174{
175 /*
176 * Construct the filename and call open.
177 */
178 char szFilename[RTPATH_MAX];
179 ssize_t rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
180 if (rc != -1)
181 rc = open(szFilename, O_RDONLY, 0);
182 return rc;
183}
184
185
186RTDECL(int) RTLinuxSysFsOpen(const char *pszFormat, ...)
187{
188 va_list va;
189 va_start(va, pszFormat);
190 int fd = RTLinuxSysFsOpenV(pszFormat, va);
191 va_end(va);
192 return fd;
193}
194
195
196RTDECL(void) RTLinuxSysFsClose(int fd)
197{
198 int iSavedErrno = errno;
199 close(fd);
200 errno = iSavedErrno;
201}
202
203
204RTDECL(ssize_t) RTLinuxSysFsReadStr(int fd, char *pszBuf, size_t cchBuf)
205{
206 Assert(cchBuf > 1);
207 ssize_t cchRead = read(fd, pszBuf, cchBuf - 1);
208 pszBuf[cchRead >= 0 ? cchRead : 0] = '\0';
209 return cchRead;
210}
211
212
213RTDECL(int64_t) RTLinuxSysFsReadIntFileV(unsigned uBase, const char *pszFormat, va_list va)
214{
215 int fd = RTLinuxSysFsOpenV(pszFormat, va);
216 if (fd == -1)
217 return -1;
218
219 int64_t i64Ret = -1;
220 char szNum[128];
221 ssize_t cchNum = RTLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
222 if (cchNum > 0)
223 {
224 int rc = RTStrToInt64Ex(szNum, NULL, uBase, &i64Ret);
225 if (RT_FAILURE(rc))
226 {
227 i64Ret = -1;
228 errno = -ETXTBSY; /* just something that won't happen at read / open. */
229 }
230 }
231 else if (cchNum == 0)
232 errno = -ETXTBSY; /* just something that won't happen at read / open. */
233
234 RTLinuxSysFsClose(fd);
235 return i64Ret;
236}
237
238
239RTDECL(int64_t) RTLinuxSysFsReadIntFile(unsigned uBase, const char *pszFormat, ...)
240{
241 va_list va;
242 va_start(va, pszFormat);
243 int64_t i64Ret = RTLinuxSysFsReadIntFileV(uBase, pszFormat, va);
244 va_end(va);
245 return i64Ret;
246}
247
248
249RTDECL(dev_t) RTLinuxSysFsReadDevNumFileV(const char *pszFormat, va_list va)
250{
251 int fd = RTLinuxSysFsOpenV(pszFormat, va);
252 if (fd == -1)
253 return 0;
254
255 dev_t DevNum = 0;
256 char szNum[128];
257 ssize_t cchNum = RTLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
258 if (cchNum > 0)
259 {
260 uint32_t u32Maj = 0;
261 uint32_t u32Min = 0;
262 char *pszNext = NULL;
263 int rc = RTStrToUInt32Ex(szNum, &pszNext, 10, &u32Maj);
264 if (RT_FAILURE(rc) || (rc != VWRN_TRAILING_CHARS) || (*pszNext != ':'))
265 errno = EINVAL;
266 else
267 {
268 rc = RTStrToUInt32Ex(pszNext + 1, NULL, 10, &u32Min);
269 if ( rc != VINF_SUCCESS
270 && rc != VWRN_TRAILING_CHARS
271 && rc != VWRN_TRAILING_SPACES)
272 errno = EINVAL;
273 else
274 {
275 errno = 0;
276 DevNum = makedev(u32Maj, u32Min);
277 }
278 }
279 }
280 else if (cchNum == 0)
281 errno = EINVAL;
282
283 RTLinuxSysFsClose(fd);
284 return DevNum;
285}
286
287
288RTDECL(dev_t) RTLinuxSysFsReadDevNumFile(const char *pszFormat, ...)
289{
290 va_list va;
291 va_start(va, pszFormat);
292 dev_t DevNum = RTLinuxSysFsReadDevNumFileV(pszFormat, va);
293 va_end(va);
294 return DevNum;
295}
296
297
298RTDECL(ssize_t) RTLinuxSysFsReadStrFileV(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
299{
300 int fd = RTLinuxSysFsOpenV(pszFormat, va);
301 if (fd == -1)
302 return -1;
303
304 ssize_t cchRet = RTLinuxSysFsReadStr(fd, pszBuf, cchBuf);
305 RTLinuxSysFsClose(fd);
306 if (cchRet > 0)
307 {
308 char *pchNewLine = (char *)memchr(pszBuf, '\n', cchRet);
309 if (pchNewLine)
310 *pchNewLine = '\0';
311 }
312 return cchRet;
313}
314
315
316RTDECL(ssize_t) RTLinuxSysFsReadStrFile(char *pszBuf, size_t cchBuf, const char *pszFormat, ...)
317{
318 va_list va;
319 va_start(va, pszFormat);
320 ssize_t cchRet = RTLinuxSysFsReadStrFileV(pszBuf, cchBuf, pszFormat, va);
321 va_end(va);
322 return cchRet;
323}
324
325
326RTDECL(ssize_t) RTLinuxSysFsGetLinkDestV(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
327{
328 AssertReturnStmt(cchBuf >= 2, errno = EINVAL, -1);
329
330 /*
331 * Construct the filename and read the link.
332 */
333 char szFilename[RTPATH_MAX];
334 int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
335 if (rc == -1)
336 return -1;
337
338 char szLink[RTPATH_MAX];
339 rc = readlink(szFilename, szLink, sizeof(szLink));
340 if (rc == -1)
341 return -1;
342 if ((size_t)rc > sizeof(szLink) - 1)
343 {
344 errno = ERANGE;
345 return -1;
346 }
347 szLink[rc] = '\0'; /* readlink fun. */
348
349 /*
350 * Extract the file name component and copy it into the return buffer.
351 */
352 size_t cchName;
353 const char *pszName = RTPathFilename(szLink);
354 if (pszName)
355 {
356 cchName = strlen(pszName); /* = &szLink[rc] - pszName; */
357 if (cchName >= cchBuf)
358 {
359 errno = ERANGE;
360 return -1;
361 }
362 memcpy(pszBuf, pszName, cchName + 1);
363 }
364 else
365 {
366 *pszBuf = '\0';
367 cchName = 0;
368 }
369 return cchName;
370}
371
372
373RTDECL(ssize_t) RTLinuxSysFsGetLinkDest(char *pszBuf, size_t cchBuf, const char *pszFormat, ...)
374{
375 va_list va;
376 va_start(va, pszFormat);
377 int rc = RTLinuxSysFsGetLinkDestV(pszBuf, cchBuf, pszFormat, va);
378 va_end(va);
379 return rc;
380}
381
382
383static ssize_t rtLinuxFindDevicePathRecursive(dev_t DevNum, RTFMODE fMode, const char *pszBasePath,
384 char *pszBuf, size_t cchBuf)
385{
386 /*
387 * Check assumptions made by the code below.
388 */
389 size_t const cchBasePath = strlen(pszBasePath);
390 AssertReturnStmt(cchBasePath < RTPATH_MAX - 10U, errno = ENAMETOOLONG, -1);
391
392 ssize_t rcRet;
393 PRTDIR pDir;
394 int rc = RTDirOpen(&pDir, pszBasePath);
395 if (RT_SUCCESS(rc))
396 {
397 char szPath[RTPATH_MAX]; /** @todo 4K per recursion - can easily be optimized away by passing it along pszBasePath
398 and only remember the length. */
399 memcpy(szPath, pszBasePath, cchBasePath + 1);
400
401 for (;;)
402 {
403 RTDIRENTRYEX Entry;
404 rc = RTDirReadEx(pDir, &Entry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
405 if (RT_FAILURE(rc))
406 {
407 errno = rc == VERR_NO_MORE_FILES
408 ? ENOENT
409 : rc == VERR_BUFFER_OVERFLOW
410 ? EOVERFLOW
411 : EIO;
412 rcRet = -1;
413 break;
414 }
415 if (RTFS_IS_SYMLINK(Entry.Info.Attr.fMode)) /* paranoia. @todo RTDirReadEx now returns symlinks, see also #if 1 below. */
416 continue;
417
418 /* Do the matching. */
419 if ( Entry.Info.Attr.u.Unix.Device == DevNum
420 && (Entry.Info.Attr.fMode & RTFS_TYPE_MASK) == fMode)
421 {
422 rcRet = rtLinuxConstructPath(pszBuf, cchBuf, pszBasePath, "%s", Entry.szName);
423 break;
424 }
425
426 /* Recurse into subdirectories. */
427 if (!RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode))
428 continue;
429 if (Entry.szName[0] == '.')
430 continue;
431
432 szPath[cchBasePath] = '\0';
433 rc = RTPathAppend(szPath, sizeof(szPath) - 1, Entry.szName); /* -1: for slash */
434 if (RT_FAILURE(rc))
435 {
436 errno = ENAMETOOLONG;
437 rcRet = -1;
438 break;
439 }
440#if 1 /** @todo This is a temporary hack, as RTDirReadEx in 3.0 doesn't know about symbolic links. */
441 struct stat Stat = { 0 };
442 if ( lstat(szPath, &Stat) < 0
443 || S_ISLNK(Stat.st_mode))
444 continue;
445#endif
446 strcat(&szPath[cchBasePath], "/");
447 rcRet = rtLinuxFindDevicePathRecursive(DevNum, fMode, szPath, pszBuf, cchBuf);
448 if (rcRet >= 0 || errno != ENOENT)
449 break;
450 }
451 RTDirClose(pDir);
452 }
453 else
454 {
455 rcRet = -1;
456 errno = RTErrConvertToErrno(rc);
457 }
458 return rcRet;
459}
460
461
462RTDECL(ssize_t) RTLinuxFindDevicePathV(dev_t DevNum, RTFMODE fMode, char *pszBuf, size_t cchBuf,
463 const char *pszSuggestion, va_list va)
464{
465 AssertReturnStmt(cchBuf >= 2, errno = EINVAL, -1);
466 AssertReturnStmt( fMode == RTFS_TYPE_DEV_CHAR
467 || fMode == RTFS_TYPE_DEV_BLOCK,
468 errno = EINVAL, -1);
469
470 if (pszSuggestion)
471 {
472 /*
473 * Construct the filename and read the link.
474 */
475 char szFilename[RTPATH_MAX];
476 int rc = rtLinuxConstructPathV(szFilename, sizeof(szFilename), "/dev/", pszSuggestion, va);
477 if (rc == -1)
478 return -1;
479
480 /*
481 * Check whether the caller's suggestion was right.
482 */
483 RTFSOBJINFO Info;
484 rc = RTPathQueryInfo(szFilename, &Info, RTFSOBJATTRADD_UNIX);
485 if ( RT_SUCCESS(rc)
486 && Info.Attr.u.Unix.Device == DevNum
487 && (Info.Attr.fMode & RTFS_TYPE_MASK) == fMode)
488 {
489 size_t cchPath = strlen(szFilename);
490 if (cchPath >= cchBuf)
491 {
492 errno = EOVERFLOW;
493 return -1;
494 }
495 memcpy(pszBuf, szFilename, cchPath + 1);
496 return cchPath;
497 }
498
499 /* The suggestion was wrong, fall back on the brute force attack. */
500 }
501
502 return rtLinuxFindDevicePathRecursive(DevNum, fMode, "/dev/", pszBuf, cchBuf);
503}
504
505
506RTDECL(ssize_t) RTLinuxFindDevicePath(dev_t DevNum, RTFMODE fMode, char *pszBuf, size_t cchBuf,
507 const char *pszSuggestion, ...)
508{
509 va_list va;
510 va_start(va, pszSuggestion);
511 int rc = RTLinuxFindDevicePathV(DevNum, fMode, pszBuf, cchBuf, pszSuggestion, va);
512 va_end(va);
513 return rc;
514}
515
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