diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/platform.h | 50 | ||||
| -rw-r--r-- | src/platform/posix/posix_file.c | 207 | ||||
| -rw-r--r-- | src/platform/windows/win_debug.c | 1 | ||||
| -rw-r--r-- | src/platform/windows/win_file.c | 270 | ||||
| -rw-r--r-- | src/transport/zerotier/zerotier.c | 53 |
6 files changed, 536 insertions, 47 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f2f54ff..c0080fe6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -113,6 +113,7 @@ if (NNG_PLATFORM_POSIX) platform/posix/posix_clock.c platform/posix/posix_debug.c platform/posix/posix_epdesc.c + platform/posix/posix_file.c platform/posix/posix_ipc.c platform/posix/posix_pipe.c platform/posix/posix_pipedesc.c @@ -131,6 +132,7 @@ if (NNG_PLATFORM_WINDOWS) platform/windows/win_impl.h platform/windows/win_clock.c platform/windows/win_debug.c + platform/windows/win_file.c platform/windows/win_iocp.c platform/windows/win_ipc.c platform/windows/win_pipe.c diff --git a/src/core/platform.h b/src/core/platform.h index 10074677..8b709e93 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -368,21 +368,65 @@ extern void nni_plat_pipe_close(int, int); // determined by using an environment variable (NNG_STATE_DIR), or // using some other application-specific method. // +// We also support listing keys, for the case where a key must be looked +// up -- for example to get a list of certificates, or some such. +// // nni_plat_file_put writes the named file, with the provided data, // and the given size. If the file already exists it is overwritten. // The permissions on the file should be limited to read and write // access by the entity running the application only. -extern int nni_plat_file_put(const char *, const void *, int); +extern int nni_plat_file_put(const char *, const void *, size_t); // nni_plat_file_get reads the entire named file, allocating storage // to receive the data and returning the data and the size in the -// reference arguments. -extern int nni_plat_file_get(const char *, void **, int *); +// reference arguments. The data pointer should be freed with nni_free +// using the supplied size when no longer needed. +extern int nni_plat_file_get(const char *, void **, size_t *); // nni_plat_file_delete deletes the named file. extern int nni_plat_file_delete(const char *); +// nni_plat_dir_open attempts to "open a directory" for listing. The +// handle for further operations is returned in the first argument, and +// the directory name is supplied in the second. +extern int nni_plat_dir_open(void **, const char *); + +// nni_plat_dir_next gets the next directory entry. Each call returns +// a new entry (arbitrary order). When no more entries exist, it returns +// NNG_ENOENT. The returned name is valid until the next call to this +// function, or until the directory is closed. Only files are returned, +// subdirectories are not reported. +extern int nni_plat_dir_next(void *, const char **); + +// nni_plat_dir_close closes the directory handle, freeing all +// resources associated with it. +extern void nni_plat_dir_close(void *); + +// nni_plat_dir_create creates a directory. Any parent directories must +// already exist. If the directory already exists, 0 is returned. +extern int nni_plat_dir_create(const char *); + +// nni_plat_dir_remove removes a directory, which must already be empty. +// If it does not exist, 0 is returned. +extern int nni_plat_dir_remove(const char *); + +// nni_plat_temp_dir returns a temporary/scratch directory for the platform +// The result should be freed with nni_strfree(). +extern char *nni_plat_temp_dir(void); + +// nni_plat_home_dir returns the "home" directory for the user running the +// application. This is a convenient place to store preferences, etc. +// Applications should append an application specific directory name. +// The result should be freed with nni_strfree(). +extern char *nni_plat_home_dir(void); + +// nni_plat_join_dir joins to path components to make a path name. +// For example. on UNIX systems nni_plat_join_dir("/tmp", "a") returns +// "/tmp/a". The pathname returned should be freed with nni_strfree(). +extern char *nni_plat_join_dir(const char *, const char *); + +// // Actual platforms we support. This is included up front so that we can // get the specific types that are supplied by the platform. #if defined(NNG_PLATFORM_POSIX) diff --git a/src/platform/posix/posix_file.c b/src/platform/posix/posix_file.c new file mode 100644 index 00000000..d7033095 --- /dev/null +++ b/src/platform/posix/posix_file.c @@ -0,0 +1,207 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// Copyright 2017 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "core/nng_impl.h" + +#ifdef NNG_PLATFORM_POSIX + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +// File support. + +// nni_plat_file_put writes the named file, with the provided data, +// and the given size. If the file already exists it is overwritten. +// The permissions on the file should be limited to read and write +// access by the entity running the application only. +int +nni_plat_file_put(const char *name, const void *data, size_t len) +{ + FILE *f; + int rv = 0; + + if ((f = fopen(name, "wb")) == NULL) { + return (nni_plat_errno(errno)); + } + if (fwrite(data, 1, len, f) != len) { + rv = nni_plat_errno(errno); + (void) unlink(name); + } + (void) fclose(f); + return (rv); +} + +// nni_plat_file_get reads the entire named file, allocating storage +// to receive the data and returning the data and the size in the +// reference arguments. +int +nni_plat_file_get(const char *name, void **datap, size_t *lenp) +{ + FILE * f; + struct stat st; + int rv = 0; + int len; + void * data; + + if ((f = fopen(name, "rb")) == NULL) { + return (nni_plat_errno(errno)); + } + + if (stat(name, &st) != 0) { + rv = nni_plat_errno(errno); + (void) fclose(f); + return (rv); + } + + len = st.st_size; + if ((data = nni_alloc(len)) == NULL) { + rv = NNG_ENOMEM; + goto done; + } + if (fread(data, 1, len, f) != len) { + rv = nni_plat_errno(errno); + nni_free(data, len); + goto done; + } + *datap = data; + *lenp = len; +done: + (void) fclose(f); + return (rv); +} + +// nni_plat_file_delete deletes the named file. +int +nni_plat_file_delete(const char *name) +{ + if (unlink(name) < 0) { + return (nni_plat_errno(errno)); + } + return (0); +} + +// nni_plat_dir_open attempts to "open a directory" for listing. The +// handle for further operations is returned in the first argument, and +// the directory name is supplied in the second. +int +nni_plat_dir_open(void **dirp, const char *name) +{ + DIR *dir; + + if ((dir = opendir(name)) == NULL) { + return (nni_plat_errno(errno)); + } + *dirp = dir; + return (0); +} + +int +nni_plat_dir_create(const char *name) +{ + if (mkdir(name, S_IRWXU) != 0) { + if (errno == EEXIST) { + return (0); + } + return (nni_plat_errno(errno)); + } + return (0); +} + +int +nni_plat_dir_remove(const char *name) +{ + if (rmdir(name) != 0) { + if (errno == ENOENT) { + return (0); + } + return (nni_plat_errno(errno)); + } + return (0); +} + +// nni_plat_dir_next gets the next directory entry. Each call returns +// a new entry (arbitrary order). When no more entries exist, it returns +// NNG_ENOENT. +int +nni_plat_dir_next(void *dir, const char **namep) +{ + for (;;) { + struct dirent *ent; + + if ((ent = readdir((DIR *) dir)) == NULL) { + return (NNG_ENOENT); + } + // Skip "." and ".." -- we would like to skip all + // directories, but that would require checking full + // paths. + if ((strcmp(ent->d_name, ".") == 0) || + (strcmp(ent->d_name, "..") == 0)) { + continue; + } + *namep = ent->d_name; + return (0); + } +} + +// nni_plat_dir_close closes the directory handle, freeing all +// resources associated with it. +void +nni_plat_dir_close(void *dir) +{ + (void) closedir((DIR *) dir); +} + +char * +nni_plat_temp_dir(void) +{ + char *temp; + + // POSIX says $TMPDIR is required. + if ((temp = getenv("TMPDIR")) != NULL) { + return (nni_strdup(temp)); + } + return (nni_strdup("/tmp")); +} + +char * +nni_plat_home_dir(void) +{ + char *home; + + // POSIX says that $HOME is *REQUIRED*. We could look in getpwuid, + // but realistically this is simply not required. + if ((home = getenv("HOME")) != NULL) { + return (nni_strdup(home)); + } + return (NULL); +} + +char * +nni_plat_join_dir(const char *prefix, const char *suffix) +{ + char * newdir; + size_t len; + + len = strlen(prefix) + strlen(suffix) + 2; + newdir = nni_alloc(strlen(prefix) + strlen(suffix) + 2); + if (newdir != NULL) { + (void) snprintf(newdir, len, "%s/%s", prefix, suffix); + } + return (newdir); +} + +#endif // NNG_PLATFORM_POSIX diff --git a/src/platform/windows/win_debug.c b/src/platform/windows/win_debug.c index 0113af58..00e327db 100644 --- a/src/platform/windows/win_debug.c +++ b/src/platform/windows/win_debug.c @@ -87,6 +87,7 @@ static struct { { // clang-format off { ERROR_FILE_NOT_FOUND, NNG_ENOENT }, + { ERROR_PATH_NOT_FOUND, NNG_ENOENT }, { ERROR_ACCESS_DENIED, NNG_EPERM }, { ERROR_INVALID_HANDLE, NNG_ECLOSED }, { ERROR_NOT_ENOUGH_MEMORY, NNG_ENOMEM }, diff --git a/src/platform/windows/win_file.c b/src/platform/windows/win_file.c new file mode 100644 index 00000000..adb57a75 --- /dev/null +++ b/src/platform/windows/win_file.c @@ -0,0 +1,270 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// Copyright 2017 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "core/nng_impl.h" + +#ifdef NNG_PLATFORM_WINDOWS + +#include <stdio.h> +#include <stdlib.h> + +// File support. + +// nni_plat_file_put writes the named file, with the provided data, +// and the given size. If the file already exists it is overwritten. +// The permissions on the file should be limited to read and write +// access by the entity running the application only. +int +nni_plat_file_put(const char *name, const void *data, size_t len) +{ + HANDLE h; + int rv = 0; + DWORD nwrite; + + h = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + return (nni_win_error(GetLastError())); + } + + if (!WriteFile(h, data, (DWORD) len, &nwrite, NULL)) { + rv = nni_win_error(GetLastError()); + (void) DeleteFile(name); + goto done; + } + // These are regular files, synchronous operations. If we got a + // short write, then we should have gotten an error! + NNI_ASSERT(nwrite == len); + +done: + (void) CloseHandle(h); + return (rv); +} + +// nni_plat_file_get reads the entire named file, allocating storage +// to receive the data and returning the data and the size in the +// reference arguments. +int +nni_plat_file_get(const char *name, void **datap, size_t *lenp) +{ + int rv = 0; + void * data; + DWORD sz; + DWORD nread; + HANDLE h; + + h = CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + return (nni_win_error(GetLastError())); + } + // We choose not to support extraordinarily large files (>4GB) + if ((sz = GetFileSize(h, NULL)) == INVALID_FILE_SIZE) { + rv = nni_win_error(GetLastError()); + goto done; + } + if ((data = nni_alloc((size_t) sz)) == NULL) { + rv = NNG_ENOMEM; + goto done; + } + if (!ReadFile(h, data, sz, &nread, NULL)) { + rv = nni_win_error(GetLastError()); + nni_free(data, sz); + goto done; + } + + // We can get a short read, indicating end of file. We return + // the actual number of bytes read. The fact that the data buffer + // is larger than this is ok, because our nni_free() routine just + // uses HeapFree(), which doesn't need a matching size. + + *datap = data; + *lenp = (size_t) nread; +done: + (void) CloseHandle(h); + return (rv); +} + +// nni_plat_file_delete deletes the named file. +int +nni_plat_file_delete(const char *name) +{ + if (!DeleteFile(name)) { + return (nni_win_error(GetLastError())); + } + return (0); +} + +// nni_plat_dir_open attempts to "open a directory" for listing. The +// handle for further operations is returned in the first argument, and +// the directory name is supplied in the second. +struct dirhandle { + HANDLE dirh; + WIN32_FIND_DATA data; + int cont; // zero on first read, 1 thereafter. +}; + +int +nni_plat_dir_open(void **dhp, const char *name) +{ + struct dirhandle *dh; + char fullpath[MAX_PATH + 1]; + + if ((dh = NNI_ALLOC_STRUCT(dh)) == NULL) { + return (NNG_ENOMEM); + } + + // Append wildcard to directory name + _snprintf(fullpath, sizeof(fullpath), "%s\\*", name); + + if ((dh->dirh = FindFirstFile(fullpath, &dh->data)) == + INVALID_HANDLE_VALUE) { + int rv; + rv = nni_win_error(GetLastError()); + NNI_FREE_STRUCT(dh); + return (rv); + } + dh->cont = 0; + *dhp = dh; + + return (0); +} + +// nni_plat_dir_next gets the next directory entry. Each call returns +// a new entry (arbitrary order). When no more entries exist, it returns +// NNG_ENOENT. +int +nni_plat_dir_next(void *dir, const char **namep) +{ + struct dirhandle *dh = dir; + int rv; + + if (dh->dirh == INVALID_HANDLE_VALUE) { + return (NNG_ENOENT); + } + if (dh->cont) { + // We need to read another entry + if (!FindNextFile(dh->dirh, &dh->data)) { + rv = GetLastError(); + FindClose(dh->dirh); + dh->dirh = INVALID_HANDLE_VALUE; + if (rv == ERROR_NO_MORE_FILES) { + return (NNG_ENOENT); + } + return (nni_win_error(rv)); + } + } + dh->cont = 1; + + // Skip over directories. + while (dh->data.dwFileAttributes & + (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN)) { + if (!FindNextFile(dh->dirh, &dh->data)) { + rv = GetLastError(); + FindClose(dh->dirh); + dh->dirh = INVALID_HANDLE_VALUE; + if (rv == ERROR_NO_MORE_FILES) { + return (NNG_ENOENT); + } + return (nni_win_error(rv)); + } + } + + // Got a good entry. + *namep = dh->data.cFileName; + return (0); +} + +// nni_plat_dir_close closes the directory handle, freeing all +// resources associated with it. +void +nni_plat_dir_close(void *dir) +{ + struct dirhandle *dh = dir; + if (dh->dirh != INVALID_HANDLE_VALUE) { + FindClose(dh->dirh); + } + NNI_FREE_STRUCT(dh); +} + +int +nni_plat_dir_create(const char *name) +{ + char parent[MAX_PATH + 1]; + int len; + + nni_strlcpy(parent, name, sizeof(parent)); + len = strlen(parent); + while (len > 0) { + if ((parent[len - 1] == '/') || (parent[len - 1] == '\\')) { + parent[len - 1] = '\0'; + break; + } + len--; + } + + if (!CreateDirectoryEx(parent, name, NULL)) { + int rv = GetLastError(); + if (rv == ERROR_ALREADY_EXISTS) { + return (0); + } + return (nni_win_error(rv)); + } + return (0); +} + +int +nni_plat_dir_remove(const char *name) +{ + if (!RemoveDirectory(name)) { + int rv = GetLastError(); + if (rv == ERROR_PATH_NOT_FOUND) { + return (0); + } + return (nni_win_error(rv)); + } + return (0); +} + +char * +nni_plat_temp_dir(void) +{ + char path[MAX_PATH + 1]; + + if (!GetTempPath(MAX_PATH + 1, path)) { + return (NULL); + } + return (nni_strdup(path)); +} + +char * +nni_plat_home_dir(void) +{ + char *homedrv; + char *homedir; + char stuff[MAX_PATH + 1]; + + if (((homedrv = getenv("HOMEDRIVE")) == NULL) || + ((homedir = getenv("HOMEPATH")) == NULL)) { + return (NULL); + } + _snprintf(stuff, sizeof(stuff), "%s%s", homedrv, homedir); + return (nni_strdup(stuff)); +} + +char * +nni_plat_join_dir(const char *prefix, const char *suffix) +{ + char stuff[MAX_PATH + 1]; + + _snprintf(stuff, sizeof(stuff), "%s\\%s", prefix, suffix); + return (nni_strdup(stuff)); +} +#endif // NNG_PLATFORM_WINDOWS
\ No newline at end of file diff --git a/src/transport/zerotier/zerotier.c b/src/transport/zerotier/zerotier.c index 56acce03..28b253d0 100644 --- a/src/transport/zerotier/zerotier.c +++ b/src/transport/zerotier/zerotier.c @@ -17,10 +17,6 @@ #include "core/nng_impl.h" #include "zerotier.h" -#ifndef _WIN32 -#include <unistd.h> -#endif - #include <ZeroTierOne.h> // These values are supplied to help folks checking status. They are the @@ -1187,7 +1183,6 @@ zt_state_put(ZT_Node *node, void *userptr, void *thr, enum ZT_StateObjectType objtype, const uint64_t objid[2], const void *data, int len) { - FILE * file; zt_node * ztn = userptr; char path[NNG_MAXADDRLEN + 1]; const char *fname; @@ -1224,27 +1219,12 @@ zt_state_put(ZT_Node *node, void *userptr, void *thr, return; } - // We assume that everyone can do standard C I/O. - // This may be a bad assumption. If that's the case, - // the platform should supply an alternative - // implementation. We are also assuming that we don't - // need to worry about atomic updates. As these items - // (keys, etc.) pretty much don't change, this should - // be fine. - if (len < 0) { - (void) unlink(path); - return; - } - - if ((file = fopen(path, "wb")) == NULL) { + nni_plat_file_delete(path); return; } - if (fwrite(data, 1, len, file) != len) { - (void) unlink(path); - } - (void) fclose(file); + (void) nni_plat_file_put(path, data, len); } static int @@ -1252,12 +1232,11 @@ zt_state_get(ZT_Node *node, void *userptr, void *thr, enum ZT_StateObjectType objtype, const uint64_t objid[2], void *data, unsigned int len) { - FILE * file; zt_node * ztn = userptr; char path[NNG_MAXADDRLEN + 1]; const char *fname; - int nread; size_t sz; + void * buf; NNI_ARG_UNUSED(objid); // we only use global files @@ -1285,30 +1264,16 @@ zt_state_get(ZT_Node *node, void *userptr, void *thr, return (-1); } - // We assume that everyone can do standard C I/O. - // This may be a bad assumption. If that's the case, - // the platform should supply an alternative - // implementation. We are also assuming that we don't - // need to worry about atomic updates. As these items - // (keys, etc.) pretty much don't change, this should - // be fine. - - if ((file = fopen(path, "rb")) == NULL) { + if (nni_plat_file_get(path, &buf, &sz) != 0) { return (-1); } - - // seek to end of file - (void) fseek(file, 0, SEEK_END); - if (ftell(file) > len) { - fclose(file); + if (sz > len) { + nni_free(buf, sz); return (-1); } - (void) fseek(file, 0, SEEK_SET); - - nread = (int) fread(data, 1, len, file); - (void) fclose(file); - - return (nread); + memcpy(data, buf, sz); + nni_free(buf, sz); + return ((int) sz); } typedef struct zt_send_hdr { |
