// // Copyright 2020 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // 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 #include #include #include #include #include #include #include #include // Some systems -- Android -- have BSD flock but not POSIX lockf. #if defined(NNG_HAVE_FLOCK) && !defined(NNG_HAVE_LOCKF) #include #endif // 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 // 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; // 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)); } 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; size_t 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 (len > 0) { 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; } } else { data = NULL; } *datap = data; *lenp = len; done: (void) fclose(f); return (rv); } // nni_plat_file_delete deletes the named file or directory. int nni_plat_file_delete(const char *name) { if (rmdir(name) == 0) { return (0); } if ((errno == ENOTDIR) && (unlink(name) == 0)) { return (0); } if (errno == ENOENT) { return (0); } return (nni_plat_errno(errno)); } int nni_plat_file_type(const char *name, int *typep) { struct stat sbuf; if (stat(name, &sbuf) != 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); } static int nni_plat_file_walk_inner(const char *name, nni_plat_file_walker walkfn, void *arg, int flags, bool *stop) { DIR *dir; if ((dir = opendir(name)) == NULL) { return (nni_plat_errno(errno)); } for (;;) { int rv; struct dirent *ent; struct stat sbuf; char * path; int walkrv; if ((ent = readdir(dir)) == NULL) { closedir(dir); return (0); } // 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; } 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); } int nni_plat_file_walk( const char *name, nni_plat_file_walker walkfn, void *arg, int flags) { 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); } int nni_plat_file_lock(const char *path, nni_plat_flock *lk) { int fd; int rv; if ((fd = open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { return (nni_plat_errno(errno)); } #ifdef NNG_HAVE_LOCKF rv = lockf(fd, F_TLOCK, 0); #elif defined NNG_HAVE_FLOCK rv = flock(fd, LOCK_EX | LOCK_NB); #else // We don't have locking support. This means you live dangerously. // For example, ZeroTier cannot be sure that nothing else is using // the same configuration file. If you're here, its probably an // embedded scenario, and we can live with it. rv = 0; #endif if (rv < 0) { int rv = errno; close(fd); if (rv == EAGAIN) { return (NNG_EBUSY); } return (nni_plat_errno(rv)); } lk->fd = fd; return (0); } void nni_plat_file_unlock(nni_plat_flock *lk) { int fd = lk->fd; lk->fd = -1; (void) close(fd); } 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_join_dir(const char *prefix, const char *suffix) { char *result; if (nni_asprintf(&result, "%s/%s", prefix, suffix) == 0) { return (result); } return (NULL); } #endif // NNG_PLATFORM_POSIX