diff options
Diffstat (limited to 'src/VBox/Runtime/r3/linux/sysfs.cpp')
-rw-r--r-- | src/VBox/Runtime/r3/linux/sysfs.cpp | 714 |
1 files changed, 714 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/linux/sysfs.cpp b/src/VBox/Runtime/r3/linux/sysfs.cpp new file mode 100644 index 00000000..aeaa2726 --- /dev/null +++ b/src/VBox/Runtime/r3/linux/sysfs.cpp @@ -0,0 +1,714 @@ +/* $Id: sysfs.cpp $ */ +/** @file + * IPRT - Linux sysfs access. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_SYSTEM +#include <iprt/assert.h> +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/fs.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/symlink.h> + +#include <iprt/linux/sysfs.h> + +#include <unistd.h> +#include <stdio.h> +#include <sys/sysctl.h> +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <sys/sysmacros.h> +#include <errno.h> + + + +/** + * Constructs the path of a sysfs file from the format parameters passed, + * prepending a prefix if the path is relative. + * + * @returns IPRT status code. + * @param pszPrefix The prefix to prepend if the path is relative. Must end + * in '/'. + * @param pszBuf Where to write the path. Must be at least + * sizeof(@a pszPrefix) characters long + * @param cchBuf The size of the buffer pointed to by @a pszBuf. + * @param pszFormat The name format, either absolute or relative to the + * prefix specified by @a pszPrefix. + * @param va The format args. + */ +static int rtLinuxConstructPathV(char *pszBuf, size_t cchBuf, + const char *pszPrefix, + const char *pszFormat, va_list va) +{ + size_t cchPrefix = strlen(pszPrefix); + AssertReturn(pszPrefix[cchPrefix - 1] == '/', VERR_INVALID_PARAMETER); + AssertReturn(cchBuf > cchPrefix + 1, VERR_INVALID_PARAMETER); + + /** @todo While RTStrPrintfV prevents overflows, it doesn't make it easy to + * check for truncations. RTPath should provide some formatters and + * joiners which can take over this rather common task that is + * performed here. */ + size_t cch = RTStrPrintfV(pszBuf, cchBuf, pszFormat, va); + if (*pszBuf != '/') + { + AssertReturn(cchBuf >= cch + cchPrefix + 1, VERR_BUFFER_OVERFLOW); + memmove(pszBuf + cchPrefix, pszBuf, cch + 1); + memcpy(pszBuf, pszPrefix, cchPrefix); + cch += cchPrefix; + } + return VINF_SUCCESS; +} + + +/** + * Constructs the path of a sysfs file from the format parameters passed, + * prepending a prefix if the path is relative. + * + * @returns IPRT status code. + * @param pszPrefix The prefix to prepend if the path is relative. Must end + * in '/'. + * @param pszBuf Where to write the path. Must be at least + * sizeof(@a pszPrefix) characters long + * @param cchBuf The size of the buffer pointed to by @a pszBuf. + * @param pszFormat The name format, either absolute or relative to "/sys/". + * @param ... The format args. + */ +DECLINLINE(int) rtLinuxConstructPath(char *pszBuf, size_t cchBuf, + const char *pszPrefix, + const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = rtLinuxConstructPathV(pszBuf, cchBuf, pszPrefix, pszFormat, va); + va_end(va); + return rc; +} + + +/** + * Constructs the path of a sysfs file from the format parameters passed, + * prepending "/sys/" if the path is relative. + * + * @returns IPRT status code. + * @param pszBuf Where to write the path. Must be at least + * sizeof("/sys/") characters long + * @param cchBuf The size of the buffer pointed to by @a pszBuf. + * @param pszFormat The name format, either absolute or relative to "/sys/". + * @param va The format args. + */ +DECLINLINE(int) rtLinuxSysFsConstructPath(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va) +{ + return rtLinuxConstructPathV(pszBuf, cchBuf, "/sys/", pszFormat, va); +} + + +RTDECL(int) RTLinuxSysFsExistsExV(const char *pszFormat, va_list va) +{ + int iSavedErrno = errno; + + /* + * Construct the filename and call stat. + */ + char szFilename[RTPATH_MAX]; + int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va); + if (RT_SUCCESS(rc)) + { + struct stat st; + int rcStat = stat(szFilename, &st); + if (rcStat != 0) + rc = RTErrConvertFromErrno(errno); + } + + errno = iSavedErrno; + return rc; +} + + +RTDECL(bool) RTLinuxSysFsExistsV(const char *pszFormat, va_list va) +{ + return RT_SUCCESS(RTLinuxSysFsExistsExV(pszFormat, va)); +} + + +RTDECL(int) RTLinuxSysFsExistsEx(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsExistsExV(pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(bool) RTLinuxSysFsExists(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + bool fRet = RTLinuxSysFsExistsV(pszFormat, va); + va_end(va); + return fRet; +} + + +RTDECL(int) RTLinuxSysFsOpenV(PRTFILE phFile, const char *pszFormat, va_list va) +{ + /* + * Construct the filename and call open. + */ + char szFilename[RTPATH_MAX]; + int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va); + if (RT_SUCCESS(rc)) + rc = RTFileOpen(phFile, szFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); + return rc; +} + + +RTDECL(int) RTLinuxSysFsOpenExV(PRTFILE phFile, uint64_t fOpen, const char *pszFormat, va_list va) +{ + /* + * Construct the filename and call open. + */ + char szFilename[RTPATH_MAX]; + int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va); + if (RT_SUCCESS(rc)) + rc = RTFileOpen(phFile, szFilename, fOpen); + return rc; +} + + +RTDECL(int) RTLinuxSysFsOpen(PRTFILE phFile, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsOpenV(phFile, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsOpenEx(PRTFILE phFile, uint64_t fOpen, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsOpenExV(phFile, fOpen, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsReadStr(RTFILE hFile, char *pszBuf, size_t cchBuf, size_t *pcchRead) +{ + Assert(cchBuf > 1); /* not mandatory */ + + int rc; + size_t cchRead; + rc = RTFileRead(hFile, pszBuf, cchBuf, &cchRead); + if (RT_SUCCESS(rc)) + { + /* + * ASSUME that if we've read less than we asked for, we've reached the + * end of the file. Otherwise, we've been given a buffer too small for + * the entire remainder of the file. + */ + if (cchRead < cchBuf) + pszBuf[cchRead] = '\0'; + else if (cchBuf) + { + rc = RTFileSeek(hFile, -1, RTFILE_SEEK_CURRENT, NULL); + if (RT_SUCCESS(rc)) + rc = VERR_BUFFER_OVERFLOW; + cchRead = cchBuf - 1; + pszBuf[cchRead] = '\0'; + } + else + rc = VERR_BUFFER_OVERFLOW; + } + else + { + if (cchBuf > 0) + *pszBuf = '\0'; + cchRead = 0; + } + + if (pcchRead) + *pcchRead = cchRead; + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteStr(RTFILE hFile, const char *pszBuf, size_t cchBuf, size_t *pcchWritten) +{ + if (!cchBuf) + cchBuf = strlen(pszBuf) + 1; /* Include the terminator */ + return RTFileWrite(hFile, pszBuf, cchBuf, pcchWritten); +} + + +RTDECL(int) RTLinuxSysFsReadFile(RTFILE hFile, void *pvBuf, size_t cbBuf, size_t *pcbRead) +{ + int rc; + size_t cbRead = 0; + + rc = RTFileRead(hFile, pvBuf, cbBuf, &cbRead); + if (RT_SUCCESS(rc)) + { + if (pcbRead) + *pcbRead = cbRead; + if (cbRead < cbBuf) + rc = VINF_SUCCESS; + else + { + /* Check for EOF */ + uint64_t offCur = 0; + uint8_t bRead; + rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &offCur); + if (RT_SUCCESS(rc)) + { + int rc2 = RTFileRead(hFile, &bRead, 1, NULL); + if (RT_SUCCESS(rc2)) + { + rc = VERR_BUFFER_OVERFLOW; + + rc2 = RTFileSeek(hFile, offCur, RTFILE_SEEK_BEGIN, NULL); + if (RT_FAILURE(rc2)) + rc = rc2; + } + else if (rc2 != VERR_EOF) + rc = rc2; + } + } + } + + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteFile(RTFILE hFile, void *pvBuf, size_t cbBuf, size_t *pcbWritten) +{ + return RTFileWrite(hFile, pvBuf, cbBuf, pcbWritten); +} + + +RTDECL(int) RTLinuxSysFsReadIntFileV(unsigned uBase, int64_t *pi64, const char *pszFormat, va_list va) +{ + RTFILE hFile; + + AssertPtrReturn(pi64, VERR_INVALID_POINTER); + + int rc = RTLinuxSysFsOpenV(&hFile, pszFormat, va); + if (RT_SUCCESS(rc)) + { + char szNum[128]; + size_t cchNum; + rc = RTLinuxSysFsReadStr(hFile, szNum, sizeof(szNum), &cchNum); + if (RT_SUCCESS(rc)) + { + if (cchNum > 0) + { + int64_t i64Ret = -1; + rc = RTStrToInt64Ex(szNum, NULL, uBase, &i64Ret); + if (RT_SUCCESS(rc)) + *pi64 = i64Ret; + } + else + rc = VERR_INVALID_PARAMETER; + } + + RTFileClose(hFile); + } + + return rc; +} + + +RTDECL(int) RTLinuxSysFsReadIntFile(unsigned uBase, int64_t *pi64, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsReadIntFileV(uBase, pi64, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteU8FileV(unsigned uBase, uint8_t u8, const char *pszFormat, va_list va) +{ + return RTLinuxSysFsWriteU64FileV(uBase, u8, pszFormat, va); +} + + +RTDECL(int) RTLinuxSysFsWriteU8File(unsigned uBase, uint8_t u8, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsWriteU64FileV(uBase, u8, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteU16FileV(unsigned uBase, uint16_t u16, const char *pszFormat, va_list va) +{ + return RTLinuxSysFsWriteU64FileV(uBase, u16, pszFormat, va); +} + + +RTDECL(int) RTLinuxSysFsWriteU16File(unsigned uBase, uint16_t u16, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsWriteU64FileV(uBase, u16, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteU32FileV(unsigned uBase, uint32_t u32, const char *pszFormat, va_list va) +{ + return RTLinuxSysFsWriteU64FileV(uBase, u32, pszFormat, va); +} + + +RTDECL(int) RTLinuxSysFsWriteU32File(unsigned uBase, uint32_t u32, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsWriteU64FileV(uBase, u32, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteU64FileV(unsigned uBase, uint64_t u64, const char *pszFormat, va_list va) +{ + RTFILE hFile; + + const char *pszFmt = NULL; + switch (uBase) + { + case 8: + pszFmt = "%#llo"; + break; + case 10: + pszFmt = "%llu"; + break; + case 16: + pszFmt = "%#llx"; + break; + default: + return VERR_INVALID_PARAMETER; + } + + int rc = RTLinuxSysFsOpenExV(&hFile, RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_DENY_NONE, pszFormat, va); + if (RT_SUCCESS(rc)) + { + char szNum[128]; + size_t cchNum = RTStrPrintf(szNum, sizeof(szNum), pszFmt, u64); + if (cchNum > 0) + { + size_t cbWritten = 0; + rc = RTLinuxSysFsWriteStr(hFile, &szNum[0], cchNum, &cbWritten); + if ( RT_SUCCESS(rc) + && cbWritten != cchNum) + rc = VERR_BUFFER_OVERFLOW; + } + else + rc = VERR_INVALID_PARAMETER; + + RTFileClose(hFile); + } + + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteU64File(unsigned uBase, uint32_t u64, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsWriteU64FileV(uBase, u64, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsReadDevNumFileV(dev_t *pDevNum, const char *pszFormat, va_list va) +{ + RTFILE hFile; + + AssertPtrReturn(pDevNum, VERR_INVALID_POINTER); + + int rc = RTLinuxSysFsOpenV(&hFile, pszFormat, va); + if (RT_SUCCESS(rc)) + { + size_t cchNum = 0; + char szNum[128]; + rc = RTLinuxSysFsReadStr(hFile, szNum, sizeof(szNum), &cchNum); + if (RT_SUCCESS(rc)) + { + if (cchNum > 0) + { + uint32_t u32Maj = 0; + uint32_t u32Min = 0; + char *pszNext = NULL; + rc = RTStrToUInt32Ex(szNum, &pszNext, 10, &u32Maj); + if (RT_FAILURE(rc) || (rc != VWRN_TRAILING_CHARS) || (*pszNext != ':')) + rc = VERR_INVALID_PARAMETER; + else + { + rc = RTStrToUInt32Ex(pszNext + 1, NULL, 10, &u32Min); + if ( rc != VINF_SUCCESS + && rc != VWRN_TRAILING_CHARS + && rc != VWRN_TRAILING_SPACES) + rc = VERR_INVALID_PARAMETER; + else + *pDevNum = makedev(u32Maj, u32Min); + } + } + else + rc = VERR_INVALID_PARAMETER; + } + + RTFileClose(hFile); + } + + return rc; +} + + +RTDECL(int) RTLinuxSysFsReadDevNumFile(dev_t *pDevNum, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsReadDevNumFileV(pDevNum, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsReadStrFileV(char *pszBuf, size_t cchBuf, size_t *pcchRead, const char *pszFormat, va_list va) +{ + RTFILE hFile; + + AssertPtrReturn(pszBuf, VERR_INVALID_POINTER); + + int rc = RTLinuxSysFsOpenV(&hFile, pszFormat, va); + if (RT_SUCCESS(rc)) + { + /* + * Note! We cannot use RTLinuxSysFsReadStr here as it has different + * semantics wrt to newline characters. It is not known why + * the semantics has to differ... Michael, any clues? + */ + size_t cchRead; + rc = RTFileRead(hFile, pszBuf, cchBuf, &cchRead); + if (RT_SUCCESS(rc)) + { + char *pchNewLine = (char *)memchr(pszBuf, '\n', cchRead); + if (pchNewLine) + { + *pchNewLine = '\0'; + cchRead = pchNewLine - pszBuf; + } + else if (cchRead < cchBuf) + pszBuf[cchRead] = '\0'; + else + { + if (cchBuf) + { + cchRead = cchBuf - 1; + pszBuf[cchRead] = '\0'; + } + else + cchRead = 0; + rc = VERR_BUFFER_OVERFLOW; + } + } + else + cchRead = 0; + + RTFileClose(hFile); + + if (pcchRead) + *pcchRead = cchRead; + } + else + { + if (cchBuf) + *pszBuf = '\0'; + if (pcchRead) + *pcchRead = 0; + } + return rc; +} + + +RTDECL(int) RTLinuxSysFsReadStrFile(char *pszBuf, size_t cchBuf, size_t *pcchRead, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsReadStrFileV(pszBuf, cchBuf, pcchRead, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteStrFileV(const char *pszBuf, size_t cchBuf, size_t *pcchWritten, const char *pszFormat, va_list va) +{ + RTFILE hFile; + + AssertPtrReturn(pszBuf, VERR_INVALID_POINTER); + + int rc = RTLinuxSysFsOpenExV(&hFile, RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_DENY_NONE, pszFormat, va); + if (RT_SUCCESS(rc)) + { + rc = RTLinuxSysFsWriteStr(hFile, pszBuf, cchBuf, pcchWritten); + RTFileClose(hFile); + } + return rc; +} + + +RTDECL(int) RTLinuxSysFsWriteStrFile(const char *pszBuf, size_t cchBuf, size_t *pcchWritten, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsWriteStrFileV(pszBuf, cchBuf, pcchWritten, pszFormat, va); + va_end(va); + return rc; +} + +RTDECL(int) RTLinuxSysFsGetLinkDestV(char *pszBuf, size_t cchBuf, size_t *pchBuf, const char *pszFormat, va_list va) +{ + AssertReturn(cchBuf >= 2, VERR_INVALID_PARAMETER); + + /* + * Construct the filename and read the link. + */ + char szFilename[RTPATH_MAX]; + int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va); + if (RT_SUCCESS(rc)) + { + char szLink[RTPATH_MAX]; + rc = RTSymlinkRead(szFilename, szLink, sizeof(szLink), 0); + if (RT_SUCCESS(rc)) + { + /* + * Extract the file name component and copy it into the return buffer. + */ + size_t cchName; + const char *pszName = RTPathFilename(szLink); + if (pszName) + { + cchName = strlen(pszName); + if (cchName < cchBuf) + memcpy(pszBuf, pszName, cchName + 1); + else + rc = VERR_BUFFER_OVERFLOW; + } + else + { + *pszBuf = '\0'; + cchName = 0; + } + + if (pchBuf) + *pchBuf = cchName; + } + } + + return rc; +} + + +RTDECL(int) RTLinuxSysFsGetLinkDest(char *pszBuf, size_t cchBuf, size_t *pchBuf, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTLinuxSysFsGetLinkDestV(pszBuf, cchBuf, pchBuf, pszFormat, va); + va_end(va); + return rc; +} + + +RTDECL(int) RTLinuxCheckDevicePathV(dev_t DevNum, RTFMODE fMode, char *pszBuf, + size_t cchBuf, const char *pszPattern, + va_list va) +{ + AssertReturn(cchBuf >= 2, VERR_INVALID_PARAMETER); + AssertReturn( fMode == RTFS_TYPE_DEV_CHAR + || fMode == RTFS_TYPE_DEV_BLOCK, + VERR_INVALID_PARAMETER); + AssertPtrReturn(pszPattern, VERR_INVALID_PARAMETER); + + /* + * Construct the filename and read the link. + */ + char szFilename[RTPATH_MAX]; + int rc = rtLinuxConstructPathV(szFilename, sizeof(szFilename), "/dev/", + pszPattern, va); + if (RT_SUCCESS(rc)) + { + RTFSOBJINFO Info; + rc = RTPathQueryInfo(szFilename, &Info, RTFSOBJATTRADD_UNIX); + if ( rc == VERR_PATH_NOT_FOUND + || ( RT_SUCCESS(rc) + && ( Info.Attr.u.Unix.Device != DevNum + || (Info.Attr.fMode & RTFS_TYPE_MASK) != fMode))) + rc = VERR_FILE_NOT_FOUND; + + if (RT_SUCCESS(rc)) + { + size_t cchPath = strlen(szFilename); + if (cchPath < cchBuf) + memcpy(pszBuf, szFilename, cchPath + 1); + else + rc = VERR_BUFFER_OVERFLOW; + } + } + + return rc; +} + + +RTDECL(int) RTLinuxCheckDevicePath(dev_t DevNum, RTFMODE fMode, char *pszBuf, + size_t cchBuf, const char *pszPattern, + ...) +{ + va_list va; + va_start(va, pszPattern); + int rc = RTLinuxCheckDevicePathV(DevNum, fMode, pszBuf, cchBuf, + pszPattern, va); + va_end(va); + return rc; +} + |