aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/core/platform.h50
-rw-r--r--src/platform/posix/posix_file.c207
-rw-r--r--src/platform/windows/win_debug.c1
-rw-r--r--src/platform/windows/win_file.c270
-rw-r--r--src/transport/zerotier/zerotier.c53
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 {