aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/core/file.c111
-rw-r--r--src/core/file.h79
-rw-r--r--src/core/nng_impl.h1
-rw-r--r--src/core/platform.h45
-rw-r--r--src/platform/posix/posix_file.c196
-rw-r--r--src/platform/windows/win_file.c309
-rw-r--r--src/supplemental/websocket/websocket.c2
-rw-r--r--src/transport/zerotier/zerotier.c56
-rw-r--r--tests/files.c207
-rw-r--r--tests/trantest.h1
11 files changed, 733 insertions, 276 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a341a8c5..a7bf3937 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -40,6 +40,8 @@ set (NNG_SOURCES
core/device.h
core/endpt.c
core/endpt.h
+ core/file.c
+ core/file.h
core/idhash.c
core/idhash.h
core/init.c
diff --git a/src/core/file.c b/src/core/file.c
new file mode 100644
index 00000000..d7000a6a
--- /dev/null
+++ b/src/core/file.c
@@ -0,0 +1,111 @@
+//
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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"
+
+int
+nni_file_put(const char *name, const void *data, size_t sz)
+{
+ return (nni_plat_file_put(name, data, sz));
+}
+
+int
+nni_file_get(const char *name, void **datap, size_t *szp)
+{
+ return (nni_plat_file_get(name, datap, szp));
+}
+
+int
+nni_file_delete(const char *name)
+{
+ return (nni_plat_file_delete(name));
+}
+
+struct walkdata {
+ nni_file_walker fn;
+ void * arg;
+};
+
+static int
+plat_walker(const char *name, void *arg)
+{
+ struct walkdata *w = arg;
+ int rv;
+
+ rv = w->fn(name, w->arg);
+ switch (rv) {
+ case NNI_FILE_WALK_CONTINUE:
+ return (NNI_PLAT_FILE_WALK_CONTINUE);
+ case NNI_FILE_WALK_STOP:
+ return (NNI_PLAT_FILE_WALK_STOP);
+ case NNI_FILE_WALK_PRUNE_CHILD:
+ return (NNI_PLAT_FILE_WALK_PRUNE_CHILD);
+ case NNI_FILE_WALK_PRUNE_SIB:
+ return (NNI_PLAT_FILE_WALK_PRUNE_SIB);
+ }
+ // We treat any other value as a stop condition. The program
+ // is returning something invalid.
+ return (NNI_PLAT_FILE_WALK_STOP);
+}
+
+int
+nni_file_walk(const char *name, nni_file_walker walker, void *arg, int flags)
+{
+ struct walkdata w;
+ int wflags = 0;
+
+ w.fn = walker;
+ w.arg = arg;
+
+ if (flags & NNI_FILE_WALK_FILES_ONLY) {
+ wflags |= NNI_PLAT_FILE_WALK_FILES_ONLY;
+ }
+ if (flags & NNI_FILE_WALK_SHALLOW) {
+ wflags |= NNI_PLAT_FILE_WALK_SHALLOW;
+ }
+
+ return (nni_plat_file_walk(name, plat_walker, &w, wflags));
+}
+
+int
+nni_file_type(const char *name, int *ftype)
+{
+ int rv;
+ int t;
+
+ if ((rv = nni_plat_file_type(name, &t)) != 0) {
+ return (rv);
+ }
+
+ switch (t) {
+ case NNI_PLAT_FILE_TYPE_FILE:
+ *ftype = NNI_FILE_TYPE_FILE;
+ break;
+ case NNI_PLAT_FILE_TYPE_DIR:
+ *ftype = NNI_FILE_TYPE_DIR;
+ break;
+ default:
+ *ftype = NNI_FILE_TYPE_OTHER;
+ break;
+ }
+ return (0);
+}
+
+char *
+nni_file_join(const char *dir, const char *file)
+{
+ return (nni_plat_join_dir(dir, file));
+}
+
+const char *
+nni_file_basename(const char *path)
+{
+ return (nni_plat_file_basename(path));
+} \ No newline at end of file
diff --git a/src/core/file.h b/src/core/file.h
new file mode 100644
index 00000000..b45aae61
--- /dev/null
+++ b/src/core/file.h
@@ -0,0 +1,79 @@
+//
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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.
+//
+
+#ifndef CORE_FILE_H
+#define CORE_FILE_H
+
+// File/Store Support
+//
+// Some transports require a persistent storage for things like configs,
+// key material, etc. Generally, these are all going to be relatively
+// small objects (such as certificates), so we only require a synchronous
+// implementation from platforms. We provide a very limited and simple
+// file API for these purposes; basic CRUD operations only, plus a way
+// to iterate over names. These are adequate for NNG's internal uses;
+// applications should use normal platform-specific APIs or those in the
+// standard C library.
+
+// nni_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 will allow the application to read and
+// write the file, but may (should) restrict anything else beyond that
+// where they can. If the name contains platform specific directory
+// separators, then any missing parent directories will be created if
+// possible.
+extern int nni_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. The data pointer should be freed with nni_free
+// using the supplied size when no longer needed.
+extern int nni_file_get(const char *, void **, size_t *);
+
+// nni_file_delete deletes the named file.
+extern int nni_file_delete(const char *);
+
+enum nni_file_type_val {
+ NNI_FILE_TYPE_FILE,
+ NNI_FILE_TYPE_DIR,
+ NNI_FILE_TYPE_OTHER,
+};
+
+// nni_file_exists checks if the named file exists.
+extern int nni_file_type(const char *, int *);
+
+// nni_file_walk walks a list of files.
+enum nni_file_walk_result {
+ NNI_FILE_WALK_CONTINUE,
+ NNI_FILE_WALK_STOP,
+ NNI_FILE_WALK_PRUNE_SIB,
+ NNI_FILE_WALK_PRUNE_CHILD,
+};
+
+enum nni_file_walk_flags {
+ NNI_FILE_WALK_DEPTH_FIRST = 0, // get children first
+ NNI_FILE_WALK_BREADTH_FIRST = 1, // get siblings first (later)
+ NNI_FILE_WALK_SHALLOW = 2, // do not descend into subdirectories
+ NNI_FILE_WALK_FILES_ONLY = 4, // directory names are not reported
+};
+
+typedef int (*nni_file_walker)(const char *, void *);
+extern int nni_file_walk(const char *, nni_file_walker, void *, int);
+
+// nni_file_join joins two path components to make a path name.
+// For example. on UNIX systems nni_file_join("/tmp", "a") returns
+// "/tmp/a". The pathname returned should be freed with nni_strfree().
+extern char *nni_file_join(const char *, const char *);
+
+// nni_file_basename returns the "file" name, without the parent directory.
+// The returned value generally is within the supplied path name.
+extern const char *nni_file_basename(const char *);
+
+#endif // CORE_FILE_H
diff --git a/src/core/nng_impl.h b/src/core/nng_impl.h
index bee6ae41..ea8512bb 100644
--- a/src/core/nng_impl.h
+++ b/src/core/nng_impl.h
@@ -29,6 +29,7 @@
#include "core/aio.h"
#include "core/clock.h"
#include "core/device.h"
+#include "core/file.h"
#include "core/idhash.h"
#include "core/init.h"
#include "core/list.h"
diff --git a/src/core/platform.h b/src/core/platform.h
index 9e193175..73b6785b 100644
--- a/src/core/platform.h
+++ b/src/core/platform.h
@@ -1,6 +1,6 @@
//
-// Copyright 2017 Garrett D'Amore <garrett@damore.org>
-// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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
@@ -370,7 +370,7 @@ extern void nni_plat_pipe_close(int, int);
//
// Some transports require a persistent storage for things like
// key material, etc. Generally, these are all going to be relatively
-// small objects (such as certificates), so we ony require a synchronous
+// small objects (such as certificates), so we only require a synchronous
// implementation from platforms. This Key-Value API is intended to
// to support using the Key's as filenames, and keys will consist of
// only these characters: [0-9a-zA-Z._-]. The directory used should be
@@ -393,9 +393,41 @@ extern int nni_plat_file_put(const char *, const void *, size_t);
// 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.
+// nni_plat_file_delete deletes the named file. If the name refers to
+// a directory, then that will be removed only if empty.
extern int nni_plat_file_delete(const char *);
+// nni_plat_file_check checks the file path to determine its type.
+// If the path does not exist, then NNG_ENOENT is returned.
+enum nni_plat_file_type_val {
+ NNI_PLAT_FILE_TYPE_FILE, // normal file
+ NNI_PLAT_FILE_TYPE_DIR, // normal directory
+ NNI_PLAT_FILE_TYPE_OTHER, // something else (pipe, device node, etc.)
+};
+extern int nni_plat_file_type(const char *, int *);
+
+enum nni_plat_file_walk_result {
+ NNI_PLAT_FILE_WALK_CONTINUE,
+ NNI_PLAT_FILE_WALK_STOP, // stop walking (all done)
+ NNI_PLAT_FILE_WALK_PRUNE_SIB, // skip siblings and their children
+ NNI_PLAT_FILE_WALK_PRUNE_CHILD, // skip children
+};
+
+enum nni_plat_file_walk_flags {
+ NNI_PLAT_FILE_WALK_DEPTH_FIRST = 0, // get children first
+ NNI_PLAT_FILE_WALK_BREADTH_FIRST = 1, // get siblings first (later)
+ NNI_PLAT_FILE_WALK_SHALLOW = 2, // do not descend into subdirectories
+ NNI_PLAT_FILE_WALK_FILES_ONLY = 4, // directory names are not reported
+};
+
+// nni_plat_file_walker is called for each pathname found by walking a
+// directory tree. It returns one of the nni_plat_file_walk_result values.
+typedef int (*nni_plat_file_walker)(const char *, void *);
+
+// nni_plat_file_walk walks a directory tree, calling the walker function
+// with the path name, and the supplied void * argument.
+extern int nni_plat_file_walk(const char *, nni_plat_file_walker, void *, int);
+
// 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.
@@ -435,6 +467,11 @@ extern char *nni_plat_home_dir(void);
// "/tmp/a". The pathname returned should be freed with nni_strfree().
extern char *nni_plat_join_dir(const char *, const char *);
+// nni_plat_file_basename returns the "file" part of the file name.
+// The returned pointer will usually reference the end of the supplied
+// string, and may not be altered.
+extern const char *nni_plat_file_basename(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.
diff --git a/src/platform/posix/posix_file.c b/src/platform/posix/posix_file.c
index d7033095..23a714cd 100644
--- a/src/platform/posix/posix_file.c
+++ b/src/platform/posix/posix_file.c
@@ -1,6 +1,6 @@
//
-// Copyright 2017 Garrett D'Amore <garrett@damore.org>
-// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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
@@ -24,6 +24,39 @@
// File support.
+static int
+nni_plat_make_parent_dirs(const char *path)
+{
+ char *dup;
+ char *p;
+ int rv;
+
+ // creates everything up until the last component.
+ if ((dup = nni_strdup(path)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ p = dup;
+ while ((p = strchr(p, '/')) != NULL) {
+ if (p != dup) {
+ *p = '\0';
+ rv = mkdir(dup, S_IRWXU);
+ *p = '/';
+ if ((rv != 0) && (errno != EEXIST)) {
+ rv = nni_plat_errno(errno);
+ nni_strfree(dup);
+ return (rv);
+ }
+ }
+
+ // collapse grouped "/" characters
+ while (*p == '/') {
+ p++;
+ }
+ }
+ nni_strfree(dup);
+ return (0);
+}
+
// 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
@@ -34,6 +67,15 @@ nni_plat_file_put(const char *name, const void *data, size_t len)
FILE *f;
int rv = 0;
+ // It is possible that the name contains a directory path
+ // that does not exist. In this case we try to create the
+ // entire tree.
+ if (strchr(name, '/') != NULL) {
+ if ((rv = nni_plat_make_parent_dirs(name)) != 0) {
+ return (rv);
+ }
+ }
+
if ((f = fopen(name, "wb")) == NULL) {
return (nni_plat_errno(errno));
}
@@ -84,66 +126,64 @@ done:
return (rv);
}
-// nni_plat_file_delete deletes the named file.
+// nni_plat_file_delete deletes the named file or directory.
int
nni_plat_file_delete(const char *name)
{
- if (unlink(name) < 0) {
- return (nni_plat_errno(errno));
+ if (rmdir(name) == 0) {
+ return (0);
}
- return (0);
+ if ((errno == ENOTDIR) && (unlink(name) == 0)) {
+ return (0);
+ }
+ if (errno == ENOENT) {
+ return (0);
+ }
+ return (nni_plat_errno(errno));
}
-// 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)
+nni_plat_file_type(const char *name, int *typep)
{
- DIR *dir;
+ struct stat sbuf;
+ int rv;
- if ((dir = opendir(name)) == NULL) {
+ if (stat(name, &sbuf) != 0) {
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));
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG:
+ *typep = NNI_PLAT_FILE_TYPE_FILE;
+ break;
+ case S_IFDIR:
+ *typep = NNI_PLAT_FILE_TYPE_DIR;
+ break;
+ default:
+ *typep = NNI_PLAT_FILE_TYPE_OTHER;
+ break;
}
return (0);
}
-int
-nni_plat_dir_remove(const char *name)
+static int
+nni_plat_file_walk_inner(const char *name, nni_plat_file_walker walkfn,
+ void *arg, int flags, bool *stop)
{
- if (rmdir(name) != 0) {
- if (errno == ENOENT) {
- return (0);
- }
+ DIR *dir;
+
+ if ((dir = opendir(name)) == NULL) {
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 (;;) {
+ int rv;
struct dirent *ent;
+ struct stat sbuf;
+ char * path;
+ int walkrv;
- if ((ent = readdir((DIR *) dir)) == NULL) {
- return (NNG_ENOENT);
+ if ((ent = readdir(dir)) == NULL) {
+ closedir(dir);
+ return (0);
}
// Skip "." and ".." -- we would like to skip all
// directories, but that would require checking full
@@ -152,17 +192,73 @@ nni_plat_dir_next(void *dir, const char **namep)
(strcmp(ent->d_name, "..") == 0)) {
continue;
}
- *namep = ent->d_name;
- return (0);
+ if ((rv = nni_asprintf(&path, "%s/%s", name, ent->d_name)) !=
+ 0) {
+ closedir(dir);
+ return (rv);
+ }
+ if (stat(path, &sbuf) != 0) {
+ if (errno == ENOENT) { // deleted while walking
+ continue;
+ }
+ rv = nni_plat_errno(errno);
+ nni_strfree(path);
+ closedir(dir);
+ return (rv);
+ }
+ if (flags & NNI_PLAT_FILE_WALK_FILES_ONLY) {
+ if ((sbuf.st_mode & S_IFMT) == S_IFREG) {
+ walkrv = walkfn(path, arg);
+ } else {
+ walkrv = NNI_PLAT_FILE_WALK_CONTINUE;
+ }
+ } else {
+ walkrv = walkfn(path, arg);
+ }
+
+ if (walkrv == NNI_PLAT_FILE_WALK_STOP) {
+ *stop = true;
+ }
+
+ if ((!*stop) && (rv != NNI_PLAT_FILE_WALK_PRUNE_CHILD) &&
+ ((flags & NNI_PLAT_FILE_WALK_SHALLOW) == 0) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ rv = nni_plat_file_walk_inner(
+ path, walkfn, arg, flags, stop);
+ if (rv != 0) {
+ nni_strfree(path);
+ closedir(dir);
+ return (rv);
+ }
+ }
+
+ nni_strfree(path);
+
+ if ((walkrv == NNI_PLAT_FILE_WALK_PRUNE_SIB) || (*stop)) {
+ break;
+ }
}
+ closedir(dir);
+ return (0);
}
-// nni_plat_dir_close closes the directory handle, freeing all
-// resources associated with it.
-void
-nni_plat_dir_close(void *dir)
+int
+nni_plat_file_walk(
+ const char *name, nni_plat_file_walker walkfn, void *arg, int flags)
{
- (void) closedir((DIR *) dir);
+ bool stop = false;
+
+ return (nni_plat_file_walk_inner(name, walkfn, arg, flags, &stop));
+}
+
+const char *
+nni_plat_file_basename(const char *path)
+{
+ const char *end;
+ if ((end = strrchr(path, '/')) != NULL) {
+ return (end + 1);
+ }
+ return (path);
}
char *
@@ -182,8 +278,8 @@ 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.
+ // 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));
}
diff --git a/src/platform/windows/win_file.c b/src/platform/windows/win_file.c
index e10f2848..2a9504aa 100644
--- a/src/platform/windows/win_file.c
+++ b/src/platform/windows/win_file.c
@@ -1,6 +1,6 @@
//
-// Copyright 2017 Garrett D'Amore <garrett@damore.org>
-// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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
@@ -12,11 +12,73 @@
#ifdef NNG_PLATFORM_WINDOWS
+#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
// File support.
+static char *
+nni_plat_find_pathsep(char *path)
+{
+ char *p;
+ // Legal path separators are "\\" and "/" under Windows.
+ // This is sort of a poormans strchr, but with the two specific
+ // separator characters instead.
+ for (p = path; *p != '\0'; p++) {
+ if ((*p == '/') || (*p == '\\')) {
+ return (p);
+ }
+ }
+ return (NULL);
+}
+
+static int
+nni_plat_make_parent_dirs(const char *path)
+{
+ char *dup;
+ char *p;
+
+ // creates everything up until the last component.
+ if ((dup = nni_strdup(path)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+
+ // Skip past C:, C:\, \\ and \ style prefixes, because we cannot
+ // create those things as directories -- they should already exist.
+ p = dup;
+ if (isalpha(p[0]) && (p[1] == ':')) {
+ p += 2;
+ if ((p[0] == '\\') || (p[0] == '/')) {
+ p++;
+ }
+ } else if ((p[0] == '\\') && (p[1] == '\\')) {
+ p += 2;
+ } else if ((p[0] == '\\') || (p[0] == '/')) {
+ p++;
+ }
+
+ while ((p = nni_plat_find_pathsep(p)) != NULL) {
+ *p = '\0';
+
+ if (!CreateDirectory(dup, NULL)) {
+ int rv = GetLastError();
+ if (rv != ERROR_ALREADY_EXISTS) {
+ nni_strfree(dup);
+ return (nni_win_error(rv));
+ }
+ }
+ *p = '\\'; // Windows prefers this though.
+
+ // collapse grouped pathsep characters
+ while ((*p == '/') || (*p == '\\')) {
+ p++;
+ }
+ }
+ nni_strfree(dup);
+ return (0);
+}
+
// 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
@@ -28,6 +90,10 @@ nni_plat_file_put(const char *name, const void *data, size_t len)
int rv = 0;
DWORD nwrite;
+ if ((rv = nni_plat_make_parent_dirs(name)) != 0) {
+ return (rv);
+ }
+
h = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
@@ -96,141 +162,104 @@ done:
int
nni_plat_file_delete(const char *name)
{
- if (!DeleteFile(name)) {
- return (nni_win_error(GetLastError()));
+ int rv;
+ if (RemoveDirectory(name)) {
+ return (0);
}
- return (0);
+ if (DeleteFile(name)) {
+ return (0);
+ }
+ if ((rv = nni_win_error(GetLastError())) == NNG_ENOENT) {
+ return (0);
+ }
+ return (rv);
}
-// 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 {
+static int
+nni_plat_file_walk_inner(const char *name, nni_plat_file_walker walkfn,
+ void *arg, int flags, bool *stop)
+{
+ char path[MAX_PATH + 1];
+ int rv;
+ int walkrv;
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;
+ _snprintf(path, sizeof(path), "%s\\%s", name, "*");
+ if ((dirh = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) {
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;
+ for (;;) {
+ // We never return hidden files.
+ if ((data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
+ (strcmp(data.cFileName, ".") == 0) ||
+ (strcmp(data.cFileName, "..") == 0)) {
+ goto next_file;
+ }
+ _snprintf(path, sizeof(path), "%s\\%s", name, data.cFileName);
+ walkrv = NNI_PLAT_FILE_WALK_CONTINUE;
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if ((flags & NNI_PLAT_FILE_WALK_FILES_ONLY) == 0) {
+ walkrv = walkfn(path, arg);
+ }
- 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);
+ if (((flags & NNI_PLAT_FILE_WALK_SHALLOW) == 0) &&
+ (walkrv != NNI_PLAT_FILE_WALK_STOP) &&
+ (walkrv != NNI_PLAT_FILE_WALK_PRUNE_CHILD)) {
+ rv = nni_plat_file_walk_inner(
+ path, walkfn, arg, flags, stop);
+ if (rv != 0) {
+ if (rv == NNG_ENOENT) {
+ rv = 0; // File deleted.
+ }
+ FindClose(dirh);
+ return (rv);
+ }
}
- return (nni_win_error(rv));
+ } else if (data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
+ if ((flags & NNI_PLAT_FILE_WALK_FILES_ONLY) == 0) {
+ walkrv = walkfn(path, arg);
+ }
+ } else {
+ walkrv = walkfn(path, arg);
+ }
+
+ if (*stop) {
+ walkrv = NNI_PLAT_FILE_WALK_STOP;
+ }
+
+ switch (walkrv) {
+ case NNI_PLAT_FILE_WALK_STOP:
+ *stop = true;
+ FindClose(dirh);
+ return (0);
+ case NNI_PLAT_FILE_WALK_PRUNE_SIB:
+ FindClose(dirh);
+ return (0);
}
- }
- dh->cont = 1;
- // Skip over directories.
- while (dh->data.dwFileAttributes &
- (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN)) {
- if (!FindNextFile(dh->dirh, &dh->data)) {
+ next_file:
+ if (!FindNextFile(dirh, &data)) {
rv = GetLastError();
- FindClose(dh->dirh);
- dh->dirh = INVALID_HANDLE_VALUE;
+ FindClose(dirh);
if (rv == ERROR_NO_MORE_FILES) {
- return (NNG_ENOENT);
+ break;
}
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];
- size_t 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)
+nni_plat_file_walk(
+ const char *name, nni_plat_file_walker walkfn, void *arg, int flags)
{
- if (!RemoveDirectory(name)) {
- int rv = GetLastError();
- if (rv == ERROR_PATH_NOT_FOUND) {
- return (0);
- }
- return (nni_win_error(rv));
- }
- return (0);
+ bool stop = false;
+ return (nni_plat_file_walk_inner(name, walkfn, arg, flags, &stop));
}
char *
@@ -249,22 +278,68 @@ nni_plat_home_dir(void)
{
char *homedrv;
char *homedir;
- char stuff[MAX_PATH + 1];
+ char *result;
if (((homedrv = getenv("HOMEDRIVE")) == NULL) ||
((homedir = getenv("HOMEPATH")) == NULL)) {
return (NULL);
}
- _snprintf(stuff, sizeof(stuff), "%s%s", homedrv, homedir);
- return (nni_strdup(stuff));
+ if (nni_asprintf(&result, "%s%s", homedrv, homedir) == 0) {
+ return (result);
+ }
+ return (NULL);
+}
+
+int
+nni_plat_file_type(const char *name, int *typep)
+{
+ HANDLE dirh;
+ WIN32_FIND_DATA data;
+
+ if ((dirh = FindFirstFile(name, &data)) == INVALID_HANDLE_VALUE) {
+ return (nni_win_error(GetLastError()));
+ }
+ (void) FindClose(dirh);
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ *typep = NNI_PLAT_FILE_TYPE_DIR;
+ } else if (data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
+ *typep = NNI_PLAT_FILE_TYPE_OTHER;
+ } else if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
+ *typep = NNI_PLAT_FILE_TYPE_OTHER;
+ } else {
+ *typep = NNI_PLAT_FILE_TYPE_FILE;
+ }
+ return (0);
}
char *
nni_plat_join_dir(const char *prefix, const char *suffix)
{
- char stuff[MAX_PATH + 1];
+ char *result;
+
+ if (nni_asprintf(&result, "%s\\%s", prefix, suffix) == 0) {
+ return (result);
+ }
+ return (NULL);
+}
+
+const char *
+nni_plat_file_basename(const char *name)
+{
+ const char *s;
- _snprintf(stuff, sizeof(stuff), "%s\\%s", prefix, suffix);
- return (nni_strdup(stuff));
+ // skip over drive designator if present
+ if (isalpha(name[0]) && (name[1] == ':')) {
+ name += 2;
+ }
+ s = name + strlen(name);
+ while (s > name) {
+ if ((*s == '\\') || (*s == '/')) {
+ return (s + 1);
+ }
+ s--;
+ }
+ return (name);
}
+
#endif // NNG_PLATFORM_WINDOWS \ No newline at end of file
diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c
index c350a3c7..aca09749 100644
--- a/src/supplemental/websocket/websocket.c
+++ b/src/supplemental/websocket/websocket.c
@@ -1807,8 +1807,6 @@ nni_ws_dialer_init(nni_ws_dialer **dp, const char *addr)
nni_ws_dialer *d;
int rv;
nni_aio * aio;
- nni_url * url;
- char * host;
char * serv;
if ((d = NNI_ALLOC_STRUCT(d)) == NULL) {
diff --git a/src/transport/zerotier/zerotier.c b/src/transport/zerotier/zerotier.c
index f6594eb3..cef31a29 100644
--- a/src/transport/zerotier/zerotier.c
+++ b/src/transport/zerotier/zerotier.c
@@ -1,6 +1,6 @@
//
-// Copyright 2017 Garrett D'Amore <garrett@damore.org>
-// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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
@@ -1212,17 +1212,14 @@ zt_state_put(ZT_Node *node, void *userptr, void *thr,
return;
}
- if ((path = nni_plat_join_dir(ztn->zn_path, fname)) == NULL) {
+ if ((path = nni_file_join(ztn->zn_path, fname)) == NULL) {
return;
}
if (len < 0) {
- (void) nni_plat_file_delete(path);
+ (void) nni_file_delete(path);
} else {
- if (strlen(ztn->zn_path) > 0) {
- (void) nni_plat_dir_create(ztn->zn_path);
- }
- (void) nni_plat_file_put(path, data, len);
+ (void) nni_file_put(path, data, len);
}
nni_strfree(path);
}
@@ -1260,11 +1257,11 @@ zt_state_get(ZT_Node *node, void *userptr, void *thr,
return (len);
}
- if ((path = nni_plat_join_dir(ztn->zn_path, fname)) == NULL) {
+ if ((path = nni_file_join(ztn->zn_path, fname)) == NULL) {
return (-1);
}
- if (nni_plat_file_get(path, &buf, &sz) != 0) {
+ if (nni_file_get(path, &buf, &sz) != 0) {
nni_strfree(path);
return (-1);
}
@@ -1475,9 +1472,6 @@ zt_node_create(zt_node **ztnp, const char *path)
(nni_random() % (zt_max_port - zt_ephemeral)) + zt_ephemeral);
nni_strlcpy(ztn->zn_path, path, sizeof(ztn->zn_path));
- if (strlen(ztn->zn_path) > 0) {
- (void) nni_plat_dir_create(ztn->zn_path);
- }
zrv = ZT_Node_new(&ztn->zn_znode, ztn, NULL, &zt_callbacks, zt_now());
if (zrv != ZT_RESULT_OK) {
zt_node_destroy(ztn);
@@ -1513,13 +1507,30 @@ zt_node_create(zt_node **ztnp, const char *path)
}
static int
+zt_walk_moons(const char *path, void *arg)
+{
+ zt_node * ztn = arg;
+ const char *bn = nni_file_basename(path);
+ char * ep;
+ uint64_t moonid;
+
+ if (strncmp(bn, "moon.", 5) != 0) {
+ return (NNI_FILE_WALK_CONTINUE);
+ }
+ moonid = strtoull(bn + 5, &ep, 16);
+ if (*ep == '\0') {
+ ZT_Node_orbit(ztn->zn_znode, NULL, moonid, 0);
+ }
+ return (NNI_FILE_WALK_CONTINUE);
+}
+
+static int
zt_node_find(zt_ep *ep)
{
zt_node * ztn;
int rv;
nng_sockaddr sa;
ZT_VirtualNetworkConfig *cf;
- void * dir;
NNI_LIST_FOREACH (&zt_nodes, ztn) {
if (strcmp(ep->ze_home, ztn->zn_path) == 0) {
@@ -1534,20 +1545,9 @@ zt_node_find(zt_ep *ep)
}
// Load moons
- if (nni_plat_dir_open(&dir, ep->ze_home) == 0) {
- uint64_t moonid;
- const char *fname;
- char * ep;
- while (nni_plat_dir_next(dir, &fname) == 0) {
- if (strncmp(fname, "moon.", 5) != 0) {
- continue;
- }
- moonid = strtoull(fname + 5, &ep, 16);
- if (*ep == '\0') {
- ZT_Node_orbit(ztn->zn_znode, NULL, moonid, 0);
- }
- }
- nni_plat_dir_close(dir);
+ if (strlen(ep->ze_home) != 0) {
+ (void) nni_file_walk(ep->ze_home, zt_walk_moons, ztn,
+ NNI_FILE_WALK_FILES_ONLY | NNI_FILE_WALK_SHALLOW);
}
done:
diff --git a/tests/files.c b/tests/files.c
index 6e9e5564..3b21ac65 100644
--- a/tests/files.c
+++ b/tests/files.c
@@ -1,6 +1,6 @@
//
-// Copyright 2017 Garrett D'Amore <garrett@damore.org>
-// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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
@@ -32,24 +32,63 @@ test_permissions(void)
size_t n;
temp = nni_plat_temp_dir();
So(temp != NULL);
- file = nni_plat_join_dir(temp, "nng_files_perms_test");
+ file = nni_file_join(temp, "nng_files_perms_test");
if (geteuid() == 0) {
ConveySkip("Cannot test permissions as root");
}
- So(nni_plat_file_put(file, "abc", 4) == 0);
+ So(nni_file_put(file, "abc", 4) == 0);
Reset({
- nni_plat_file_delete(file);
+ nni_file_delete(file);
nni_strfree(file);
nni_strfree(temp);
});
chmod(file, 0);
- So((rv = nni_plat_file_get(file, &data, &n)) != 0);
+ So((rv = nni_file_get(file, &data, &n)) != 0);
So(rv == NNG_EPERM);
- So(nni_plat_file_put(file, "def", 4) == NNG_EPERM);
+ So(nni_file_put(file, "def", 4) == NNG_EPERM);
});
#endif
}
+struct walkarg {
+ int a;
+ int b;
+ int c;
+ int d;
+ int seen;
+};
+
+static int
+walker(const char *name, void *arg)
+{
+ struct walkarg *wa = arg;
+ const char * bn;
+
+ bn = nni_file_basename(name);
+ if (wa != NULL) {
+ wa->seen++;
+ if (strcmp(bn, "a") == 0) {
+ wa->a++;
+ } else if (strcmp(bn, "b") == 0) {
+ wa->b++;
+ } else if (strcmp(bn, "c") == 0) {
+ wa->c++;
+ } else if (strcmp(bn, "d") == 0) {
+ wa->d++;
+ }
+ }
+ if (strcmp(bn, "stop") == 0) {
+ return (NNI_FILE_WALK_STOP);
+ }
+ if (strcmp(bn, "prunechild") == 0) {
+ return (NNI_FILE_WALK_PRUNE_CHILD);
+ }
+ if (strcmp(bn, "prunesib") == 0) {
+ return (NNI_FILE_WALK_PRUNE_SIB);
+ }
+ return (NNI_FILE_WALK_CONTINUE);
+}
+
TestMain("Platform File Support", {
Convey("Directory names work", {
char *d;
@@ -60,7 +99,7 @@ TestMain("Platform File Support", {
So((d = nni_plat_home_dir()) != NULL);
nni_strfree(d);
- So((d = nni_plat_join_dir("a", "b")) != NULL);
+ So((d = nni_file_join("a", "b")) != NULL);
So(d[0] == 'a');
So(d[2] == 'b');
So(d[3] == '\0');
@@ -68,45 +107,40 @@ TestMain("Platform File Support", {
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("Can create file in non-existent directory", {
+ int rv;
+ char *tmp;
+ char *d1;
+ char *d2;
+ So((tmp = nni_plat_temp_dir()) != NULL);
+ So((d1 = nni_file_join(tmp, "bogusdir")) != NULL);
+ So((d2 = nni_file_join(d1, "a")) != NULL);
+ So((rv = nni_plat_file_put(d2, "", 0)) == 0);
+ So(nni_file_delete(d2) == 0);
+ So(nni_file_delete(d1) == 0);
+ nni_strfree(d2);
+ nni_strfree(d1);
+ nni_strfree(tmp);
});
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 = nni_file_get("/bogus/dir/a", &data, &n)) != 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);
+
+ Convey("Delete of missing file passes",
+ { So(nni_file_delete("/bogus/dir/a") == 0); });
+
+ Convey("Walk of missing directory fails", {
+ int rv = nni_file_walk("/bogus/dir/a", walker, NULL, 0);
So(rv == NNG_ENOENT);
});
+
Convey("Remove missing directory works",
- { So(nni_plat_dir_remove("/bogus/nng_does_not_exist") == 0); });
+ { So(nni_file_delete("/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;
@@ -114,62 +148,86 @@ TestMain("Platform File Support", {
char *a;
char *b;
char *c;
+ char *d;
temp = nni_plat_temp_dir();
So(temp != NULL);
- mydir = nni_plat_join_dir(temp, "nng_files_test");
+ mydir = nni_file_join(temp, "nng_files_test");
So(mydir != NULL);
- a = nni_plat_join_dir(mydir, "a");
+ a = nni_file_join(mydir, "a");
So(a != NULL);
- b = nni_plat_join_dir(mydir, "b");
+ b = nni_file_join(mydir, "b");
So(b != NULL);
- c = nni_plat_join_dir(mydir, "c");
+ c = nni_file_join(mydir, "c");
So(c != NULL);
+ d = nni_file_join(c, "d");
+ So(d != 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);
+ So(nni_file_put(a, "alpha", 6) == 0);
+ So(nni_file_put(b, "bravo", 6) == 0);
+ So(nni_file_put(d, "delta", 6) == 0);
Reset({
nni_strfree(temp);
- nni_plat_file_delete(a);
- nni_plat_file_delete(b);
+ nni_file_delete(a);
+ nni_file_delete(b);
+ nni_file_delete(d);
+ nni_file_delete(c);
+ nni_file_delete(mydir);
nni_strfree(a);
nni_strfree(b);
nni_strfree(c);
- nni_plat_dir_remove(mydir);
+ nni_strfree(d);
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("Directory walk works", {
+ struct walkarg wa = { 0 };
+ int rv;
+
+ rv = nni_file_walk(mydir, walker, &wa, 0);
+ So(rv == 0);
+ So(wa.a == 1);
+ So(wa.b == 1);
+ So(wa.c == 1);
+ So(wa.d == 1);
+ So(wa.seen == 4);
+
+ memset(&wa, 0, sizeof(wa));
+ rv = nni_file_walk(
+ mydir, walker, &wa, NNI_FILE_WALK_FILES_ONLY);
+ So(rv == 0);
+ So(wa.a == 1);
+ So(wa.b == 1);
+ So(wa.c == 0);
+ So(wa.d == 1);
+ So(wa.seen == 3);
+
+ memset(&wa, 0, sizeof(wa));
+ rv = nni_file_walk(
+ mydir, walker, &wa, NNI_FILE_WALK_SHALLOW);
+ So(rv == 0);
+ So(wa.a == 1);
+ So(wa.b == 1);
+ So(wa.c == 1);
+ So(wa.d == 0);
+ So(wa.seen == 3);
+
+ memset(&wa, 0, sizeof(wa));
+ rv = nni_file_walk(mydir, walker, &wa,
+ NNI_FILE_WALK_SHALLOW | NNI_FILE_WALK_FILES_ONLY);
+ So(rv == 0);
+ So(wa.a == 1);
+ So(wa.b == 1);
+ So(wa.c == 0);
+ So(wa.d == 0);
+ So(wa.seen == 2);
});
Convey("Contents work", {
void * data;
size_t len;
- So(nni_plat_file_get(a, &data, &len) == 0);
+ So(nni_file_get(a, &data, &len) == 0);
So(len == 6);
So(strcmp(data, "alpha") == 0);
nni_free(data, len);
@@ -183,15 +241,16 @@ TestMain("Platform File Support", {
size_t n;
temp = nni_plat_temp_dir();
So(temp != NULL);
- empty = nni_plat_join_dir(temp, "nng_files_test1");
+ empty = nni_file_join(temp, "nng_files_test1");
So(empty != NULL);
- So(nni_plat_file_put(empty, "", 0) == 0);
+ So(nni_file_put(empty, "", 0) == 0);
Reset({
- nni_plat_file_delete(empty);
+ nni_file_delete(empty);
nni_strfree(empty);
nni_strfree(temp);
});
- So(nni_plat_file_get(empty, &data, &n) == 0);
+ So(nni_file_get(empty, &data, &n) == 0);
+ nni_free(data, n);
So(n == 0);
});
diff --git a/tests/trantest.h b/tests/trantest.h
index 46eaa7b1..205a54b1 100644
--- a/tests/trantest.h
+++ b/tests/trantest.h
@@ -313,7 +313,6 @@ trantest_send_recv_multi(trantest *tt)
nng_dialer d;
nng_msg * send;
nng_msg * recv;
- size_t len;
nng_pipe p;
char url[NNG_MAXADDRLEN];
size_t sz;