diff options
| -rw-r--r-- | src/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/file.c | 111 | ||||
| -rw-r--r-- | src/core/file.h | 79 | ||||
| -rw-r--r-- | src/core/nng_impl.h | 1 | ||||
| -rw-r--r-- | src/core/platform.h | 45 | ||||
| -rw-r--r-- | src/platform/posix/posix_file.c | 196 | ||||
| -rw-r--r-- | src/platform/windows/win_file.c | 309 | ||||
| -rw-r--r-- | src/supplemental/websocket/websocket.c | 2 | ||||
| -rw-r--r-- | src/transport/zerotier/zerotier.c | 56 | ||||
| -rw-r--r-- | tests/files.c | 207 | ||||
| -rw-r--r-- | tests/trantest.h | 1 |
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; |
