aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/files.c196
-rw-r--r--tests/zt.c2
9 files changed, 734 insertions, 48 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 {
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b34c5275..1042e48a 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -106,6 +106,7 @@ endif ()
add_nng_test(bus 5)
add_nng_test(event 5)
+add_nng_test(files 5)
add_nng_test(idhash 5)
add_nng_test(inproc 5)
add_nng_test(ipc 5)
diff --git a/tests/files.c b/tests/files.c
new file mode 100644
index 00000000..c7e6a1dd
--- /dev/null
+++ b/tests/files.c
@@ -0,0 +1,196 @@
+//
+// 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 "convey.h"
+#include "core/nng_impl.h"
+#include "stubs.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef NNG_PLATFORM_POSIX
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+void
+test_permissions(void)
+{
+#ifdef NNG_PLATFORM_POSIX
+ Convey("Permissions work", {
+ int rv;
+ char * temp;
+ char * file;
+ void * data;
+ size_t n;
+ temp = nni_plat_temp_dir();
+ So(temp != NULL);
+ file = nni_plat_join_dir(temp, "nng_files_perms_test");
+ So(nni_plat_file_put(file, "abc", 4) == 0);
+ Reset({
+ nni_plat_file_delete(file);
+ nni_strfree(file);
+ nni_strfree(temp);
+ });
+ chmod(file, 0);
+ So((rv = nni_plat_file_get(file, &data, &n)) != 0);
+ So(rv == NNG_EPERM);
+ So(nni_plat_file_put(file, "def", 4) == NNG_EPERM);
+ });
+#endif
+}
+
+TestMain("Platform File Support", {
+ Convey("Directory names work", {
+ char *d;
+
+ So((d = nni_plat_temp_dir()) != NULL);
+ nni_strfree(d);
+
+ So((d = nni_plat_home_dir()) != NULL);
+ nni_strfree(d);
+
+ So((d = nni_plat_join_dir("a", "b")) != NULL);
+ So(d[0] == 'a');
+ So(d[2] == 'b');
+ So(d[3] == '\0');
+ So((d[1] == '/') || (d[1] == '\\'));
+ nni_strfree(d);
+ });
+
+ Convey("Cannot create file in non-extant directory", {
+ int rv;
+ So((rv = nni_plat_file_put("/bogus/dir/a", "", 0)) != 0);
+ So(rv == NNG_ENOENT);
+ });
+ Convey("Cannot read missing file", {
+ int rv;
+ void * data;
+ size_t n;
+ So((rv = nni_plat_file_get("/bogus/dir/a", &data, &n)) != 0);
+ So(rv == NNG_ENOENT);
+ });
+ Convey("Cannot delete missing file", {
+ int rv;
+ So((rv = nni_plat_file_delete("/bogus/dir/a")) != 0);
+ So(rv == NNG_ENOENT);
+ });
+ Convey("Cannot open missing directory", {
+ int rv;
+ void *dir;
+ So((rv = nni_plat_dir_open(
+ &dir, "/bogus/nng_does_not_exist")) != 0);
+ So(rv == NNG_ENOENT);
+ });
+ Convey("Cannot create directory in non-existing subdir", {
+ int rv;
+ So((rv = nni_plat_dir_create(
+ "/bogus/nng_does_not_exist/subdir")) != 0);
+ So(rv == NNG_ENOENT);
+ });
+ Convey("Remove missing directory works",
+ { So(nni_plat_dir_remove("/bogus/nng_does_not_exist") == 0); });
+
+ Convey("Create existing directory works", {
+ char *tmp;
+ tmp = nni_plat_temp_dir();
+ So(nni_plat_dir_create(tmp) == 0);
+ nni_strfree(tmp);
+ });
+ Convey("We can create a pair of files", {
+
+ char *temp;
+ char *mydir;
+ char *a;
+ char *b;
+ char *c;
+ temp = nni_plat_temp_dir();
+ So(temp != NULL);
+ mydir = nni_plat_join_dir(temp, "nng_files_test");
+ So(mydir != NULL);
+ a = nni_plat_join_dir(mydir, "a");
+ So(a != NULL);
+ b = nni_plat_join_dir(mydir, "b");
+ So(b != NULL);
+ c = nni_plat_join_dir(mydir, "c");
+ So(c != NULL);
+
+ So(nni_plat_dir_create(mydir) == 0);
+ So(nni_plat_file_put(a, "alpha", 6) == 0);
+ So(nni_plat_file_put(b, "bravo", 6) == 0);
+
+ Reset({
+ nni_strfree(temp);
+ nni_plat_file_delete(a);
+ nni_plat_file_delete(b);
+ nni_strfree(a);
+ nni_strfree(b);
+ nni_strfree(c);
+ nni_plat_dir_remove(mydir);
+ nni_strfree(mydir);
+ });
+
+ Convey("Directory list works", {
+ int seen_a = 0;
+ int seen_b = 0;
+ int seen_what = 0;
+ int rv;
+ void * dirh;
+ const char *name;
+
+ So(nni_plat_dir_open(&dirh, mydir) == 0);
+ while ((rv = nni_plat_dir_next(dirh, &name)) == 0) {
+ if (strcmp(name, "a") == 0) {
+ seen_a++;
+ } else if (strcmp(name, "b") == 0) {
+ seen_b++;
+ } else {
+ seen_what++;
+ }
+ }
+ So(rv == NNG_ENOENT);
+ So(seen_a == 1);
+ So(seen_b == 1);
+ So(seen_what == 0);
+ nni_plat_dir_close(dirh);
+ });
+
+ Convey("Contents work", {
+ void * data;
+ size_t len;
+
+ So(nni_plat_file_get(a, &data, &len) == 0);
+ So(len == 6);
+ So(strcmp(data, "alpha") == 0);
+ nni_free(data, len);
+ });
+ });
+
+ Convey("Zero length files work", {
+ char * temp;
+ char * empty;
+ void * data;
+ size_t n;
+ temp = nni_plat_temp_dir();
+ So(temp != NULL);
+ empty = nni_plat_join_dir(temp, "nng_files_test1");
+ So(empty != NULL);
+ So(nni_plat_file_put(empty, "", 0) == 0);
+ Reset({
+ nni_plat_file_delete(empty);
+ nni_strfree(empty);
+ nni_strfree(temp);
+ });
+ So(nni_plat_file_get(empty, &data, &n) == 0);
+ So(n == 0);
+ });
+
+ test_permissions();
+});
diff --git a/tests/zt.c b/tests/zt.c
index 1e6992b4..ee6bf9cc 100644
--- a/tests/zt.c
+++ b/tests/zt.c
@@ -318,7 +318,7 @@ TestMain("ZeroTier Transport", {
So(nng_dialer_setopt(
d, NNG_OPT_ZT_HOME, path2, strlen(path2) + 1) == 0);
So(nng_dialer_start(d, 0) == 0);
-
+ nng_usleep(2000000);
});
trantest_test_extended("zt://" NWID "/*:%u", check_props);