diff options
Diffstat (limited to 'Radio/HW/BladeRF/src/helpers/file.c')
-rw-r--r-- | Radio/HW/BladeRF/src/helpers/file.c | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/helpers/file.c b/Radio/HW/BladeRF/src/helpers/file.c new file mode 100644 index 0000000..0977137 --- /dev/null +++ b/Radio/HW/BladeRF/src/helpers/file.c @@ -0,0 +1,511 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2013-2014 Nuand LLC + * Copyright (C) 2013 Daniel Gröber <dxld ÄT darkboxed DOT org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +#include <libbladeRF.h> + +#include "host_config.h" +#include "minmax.h" +#include "log.h" +#include "rel_assert.h" + +#include "helpers/file.h" + +/* Paths to search for bladeRF files */ +struct search_path_entries { + bool prepend_home; + const char *path; +}; + +int file_write(FILE *f, uint8_t *buf, size_t len) +{ + size_t rv; + + rv = fwrite(buf, 1, len, f); + if(rv < len) { + log_debug("File write failed: %s\n", strerror(errno)); + return BLADERF_ERR_IO; + } + + return 0; +} + +int file_read(FILE *f, char *buf, size_t len) +{ + size_t rv; + + rv = fread(buf, 1, len, f); + if(rv < len) { + if(feof(f)) + log_debug("Unexpected end of file: %s\n", strerror(errno)); + else + log_debug("Error reading file: %s\n", strerror(errno)); + + return BLADERF_ERR_IO; + } + + return 0; +} + +ssize_t file_size(FILE *f) +{ + ssize_t rv = BLADERF_ERR_IO; + long int fpos = ftell(f); + long len; + + if(fpos < 0) { + log_verbose("ftell failed: %s\n", strerror(errno)); + goto out; + } + + if(fseek(f, 0, SEEK_END)) { + log_verbose("fseek failed: %s\n", strerror(errno)); + goto out; + } + + len = ftell(f); + if(len < 0) { + log_verbose("ftell failed: %s\n", strerror(errno)); + goto out; + } else if (len == LONG_MAX) { + log_debug("ftell called with a directory?\n"); + goto out; + } + + if(fseek(f, fpos, SEEK_SET)) { + log_debug("fseek failed: %s\n", strerror(errno)); + goto out; + } + + rv = (ssize_t) len; + assert(rv == len); + +out: + return rv; +} + +int file_read_buffer(const char *filename, uint8_t **buf_ret, size_t *size_ret) +{ + int status = BLADERF_ERR_UNEXPECTED; + FILE *f; + uint8_t *buf = NULL; + ssize_t len; + + f = fopen(filename, "rb"); + if (!f) { + log_error("%s: could not open %s: %s\n", __FUNCTION__, filename, + strerror(errno)); + switch (errno) { + case ENOENT: + return BLADERF_ERR_NO_FILE; + + case EACCES: + return BLADERF_ERR_PERMISSION; + + default: + return BLADERF_ERR_IO; + } + } + + len = file_size(f); + if (len < 0) { + status = BLADERF_ERR_IO; + goto out; + } + + buf = (uint8_t *)malloc(len); + if (!buf) { + status = BLADERF_ERR_MEM; + goto out; + } + + status = file_read(f, (char *)buf, len); + if (status < 0) { + goto out; + } + + *buf_ret = buf; + *size_ret = len; + fclose(f); + return 0; + +out: + free(buf); + if (f) { + fclose(f); + } + + return status; +} + +/* Remove the last entry in a path. This is used to strip the executable name +* from a path to get the directory that the executable resides in. */ +static size_t strip_last_path_entry(char *buf, char dir_delim) +{ + size_t len = strlen(buf); + + while (len > 0 && buf[len - 1] != dir_delim) { + buf[len - 1] = '\0'; + len--; + } + + return len; +} + +#if BLADERF_OS_LINUX || BLADERF_OS_OSX || BLADERF_OS_FREEBSD +#define ACCESS_FILE_EXISTS F_OK +#define DIR_DELIMETER '/' + +static const struct search_path_entries search_paths[] = { + { false, "" }, + { true, "/.config/Nuand/bladeRF/" }, + { true, "/.Nuand/bladeRF/" }, + + /* LIBBLADERF_SEARCH_PATH_PREFIX is defined in the libbladeRF + * CMakeLists.txt file. It defaults to ${CMAKE_INSTALL_PREFIX}, but + * can be overridden via -DLIBBLADERF_SEARCH_PATH_OVERRIDE + */ + //{ false, LIBBLADERF_SEARCH_PREFIX "/etc/Nuand/bladeRF/" }, + //{ false, LIBBLADERF_SEARCH_PREFIX "/share/Nuand/bladeRF/" }, + + /* These two entries are here for reverse compatibility. + * + * We failed to prefix ${CMAKE_INSTALL_PREFIX} on these from the beginning, + * forcing package maintainers to hard-code one of these locations, + * despite having a different ${CMAKE_INSTALL_PREFIX}. + * + * We'll keep these around for some time as fall-backs, as not to break + * existing packaging scripts. + */ + { false, "/etc/Nuand/bladeRF/" }, + { false, "/usr/share/Nuand/bladeRF/" }, +}; + +static inline size_t get_home_dir(char *buf, size_t max_len) +{ + const char *home; + + home = getenv("HOME"); + if (home != NULL && strlen(home) > 0 && strlen(home) < max_len) { + strncat(buf, home, max_len); + } else { + const struct passwd *passwd; + const uid_t uid = getuid(); + passwd = getpwuid(uid); + strncat(buf, passwd->pw_dir, max_len); + } + + return strlen(buf); +} + +static inline size_t get_install_dir(char *buf, size_t max_len) +{ + return 0; +} + +#if BLADERF_OS_LINUX +static inline size_t get_binary_dir(char *buf, size_t max_len) +{ + ssize_t result = readlink("/proc/self/exe", buf, max_len); + + if (result > 0) { + return strip_last_path_entry(buf, DIR_DELIMETER); + } else { + return 0; + } +} +#elif BLADERF_OS_FREEBSD +static inline size_t get_binary_dir(char *buf, size_t max_len) +{ + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + ssize_t result = sysctl(mib, 4, buf, &max_len, NULL, 0); + + if (result > 0) { + return strip_last_path_entry(buf, DIR_DELIMETER); + } else { + return 0; + } +} +#elif BLADERF_OS_OSX +#include <mach-o/dyld.h> +static inline size_t get_binary_dir(char *buf, size_t max_len) +{ + uint32_t buf_size = max_len; + int status = _NSGetExecutablePath(buf, &buf_size); + + if (status == 0) { + return strip_last_path_entry(buf, DIR_DELIMETER); + } else { + return 0; + } + +} +#endif + +#elif BLADERF_OS_WINDOWS +#define ACCESS_FILE_EXISTS 0 +#define DIR_DELIMETER '\\' +#include <shlobj.h> + +static const struct search_path_entries search_paths[] = { + { false, "" }, + { true, "/Nuand/bladeRF/" }, +}; + +static inline size_t get_home_dir(char *buf, size_t max_len) +{ + /* Explicitly link to a runtime DLL to get SHGetKnownFolderPath. + * This deals with the case where we might not be able to staticly + * link it at build time, e.g. mingw32. + * + * http://msdn.microsoft.com/en-us/library/784bt7z7.aspx + */ + typedef HRESULT (CALLBACK* LPFNSHGKFP_T)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR*); + HINSTANCE hDLL; // Handle to DLL + LPFNSHGKFP_T lpfnSHGetKnownFolderPath; // Function pointer + + const KNOWNFOLDERID folder_id = FOLDERID_RoamingAppData; + PWSTR path; + HRESULT status; + + assert(max_len < INT_MAX); + + hDLL = LoadLibrary("Shell32"); + if (hDLL == NULL) { + // DLL couldn't be loaded, bail out. + return 0; + } + + lpfnSHGetKnownFolderPath = (LPFNSHGKFP_T)GetProcAddress(hDLL, "SHGetKnownFolderPath"); + + if (!lpfnSHGetKnownFolderPath) { + // Can't find the procedure we want. Free and bail. + FreeLibrary(hDLL); + return 0; + } + + status = lpfnSHGetKnownFolderPath(&folder_id, 0, NULL, &path); + if (status == S_OK) { + WideCharToMultiByte(CP_ACP, 0, path, -1, buf, (int)max_len, NULL, NULL); + CoTaskMemFree(path); + } + + FreeLibrary(hDLL); + + return strlen(buf); +} + +static inline size_t get_binary_dir(char *buf, size_t max_len) +{ + DWORD status; + + assert(max_len <= MAXDWORD); + status = GetModuleFileName(NULL, buf, (DWORD) max_len); + + if (status > 0) { + return strip_last_path_entry(buf, DIR_DELIMETER); + } else { + return 0; + } +} + +static inline size_t get_install_dir(char *buf, size_t max_len) +{ + typedef LONG (CALLBACK* LPFNREGOPEN_T)(HKEY, LPCTSTR, DWORD, REGSAM, PHKEY); + typedef LONG (CALLBACK* LPFNREGQUERY_T)(HKEY, LPCTSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD); + typedef LONG (CALLBACK* LPFNREGCLOSE_T)(HKEY); + + HINSTANCE hDLL; // Handle to DLL + LPFNREGOPEN_T lpfnRegOpenKeyEx; // Function pointer + LPFNREGQUERY_T lpfnRegQueryValueEx; // Function pointer + LPFNREGCLOSE_T lpfnRegCloseKey; // Function pointer + HKEY hk; + DWORD len; + + assert(max_len < INT_MAX); + + memset(buf, 0, max_len); + hDLL = LoadLibrary("Advapi32"); + if (hDLL == NULL) { + // DLL couldn't be loaded, bail out. + return 0; + } + + lpfnRegOpenKeyEx = (LPFNREGOPEN_T)GetProcAddress(hDLL, "RegOpenKeyExA"); + if (!lpfnRegOpenKeyEx) { + // Can't find the procedure we want. Free and bail. + FreeLibrary(hDLL); + return 0; + } + + lpfnRegQueryValueEx = (LPFNREGQUERY_T)GetProcAddress(hDLL, "RegQueryValueExA"); + if (!lpfnRegQueryValueEx) { + // Can't find the procedure we want. Free and bail. + FreeLibrary(hDLL); + return 0; + } + + lpfnRegCloseKey = (LPFNREGCLOSE_T)GetProcAddress(hDLL, "RegCloseKey"); + if (!lpfnRegCloseKey) { + // Can't find the procedure we want. Free and bail. + FreeLibrary(hDLL); + return 0; + } + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Nuand LLC", 0, KEY_READ, &hk)) { + FreeLibrary(hDLL); + return 0; + } + + len = (DWORD)max_len; + if (RegQueryValueEx(hk, "Path", 0, NULL, (LPBYTE) buf, &len) == ERROR_SUCCESS) { + if (len > 0 && len < max_len && buf[len - 1] != '\\') + strcat(buf, "\\"); + } else + len = 0; + + if (len) { + lpfnRegCloseKey(hk); + } + + FreeLibrary(hDLL); + + return len; +} + +#else +#error "Unknown OS or missing BLADERF_OS_* definition" +#endif + +/* We're not using functions that use the *nix PATH_MAX (which is known to be + * problematic), or the WIN32 MAX_PATH. Therefore, we'll just use this + * arbitrary, but "sufficiently" large max buffer size for paths */ +#define PATH_MAX_LEN 4096 + +char *file_find(const char *filename) +{ + size_t i, max_len; + char *full_path; + const char *env_var; + + full_path = calloc(PATH_MAX_LEN+1, 1); + if (full_path == NULL) { + return NULL; + } + + /* Check directory specified by environment variable */ + env_var = getenv("BLADERF_SEARCH_DIR"); + if (env_var != NULL) { + strncat(full_path, env_var, PATH_MAX_LEN - 1); + full_path[strlen(full_path)] = DIR_DELIMETER; + + max_len = PATH_MAX_LEN - strlen(full_path); + + if (max_len >= strlen(filename)) { + strncat(full_path, filename, max_len); + if (access(full_path, ACCESS_FILE_EXISTS) != -1) { + return full_path; + } + } + } + + /* Check the directory containing the currently running binary */ + memset(full_path, 0, PATH_MAX_LEN); + max_len = PATH_MAX_LEN - 1; + + if (get_binary_dir(full_path, max_len) != 0) { + max_len -= strlen(full_path); + + if (max_len >= strlen(filename)) { + strncat(full_path, filename, max_len); + if (access(full_path, ACCESS_FILE_EXISTS) != -1) { + return full_path; + } + } else { + log_debug("Skipping search for %s in %s. " + "Path would be truncated.\n", + filename, full_path); + } + } + + /* Search our list of pre-determined paths */ + for (i = 0; i < ARRAY_SIZE(search_paths); i++) { + memset(full_path, 0, PATH_MAX_LEN); + max_len = PATH_MAX_LEN; + + if (search_paths[i].prepend_home) { + const size_t len = get_home_dir(full_path, max_len); + + if (len != 0) { + max_len -= len; + } else { + continue; + } + + } + + strncat(full_path, search_paths[i].path, max_len); + max_len = PATH_MAX_LEN - strlen(full_path); + + if (max_len >= strlen(filename)) { + strncat(full_path, filename, max_len); + + if (access(full_path, ACCESS_FILE_EXISTS) != -1) { + return full_path; + } + } else { + log_debug("Skipping search for %s in %s. " + "Path would be truncated.\n", + filename, full_path); + } + } + + /* Search the installation directory, if applicable */ + if (get_install_dir(full_path, PATH_MAX_LEN)) { + max_len = PATH_MAX_LEN - strlen(full_path); + + if (max_len >= strlen(filename)) { + strncat(full_path, filename, max_len); + if (access(full_path, ACCESS_FILE_EXISTS) != -1) { + return full_path; + } + } else { + log_debug("Skipping search for %s in %s. " + "Path would be truncated.\n", + filename, full_path); + } + } + + free(full_path); + return NULL; +} |