// // Copyright 2018 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // Copyright 2018 QXSoftware // // 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 #include #include // 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 // 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; 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) { 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 (sz > 0) { 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; } } else { data = NULL; nread = 0; } // 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) { int rv; if (RemoveDirectory(name)) { return (0); } if (DeleteFile(name)) { return (0); } if ((rv = nni_win_error(GetLastError())) == NNG_ENOENT) { return (0); } return (rv); } 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; _snprintf(path, sizeof(path), "%s\\%s", name, "*"); if ((dirh = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) { rv = nni_win_error(GetLastError()); return (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 (((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); } } } 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); } next_file: if (!FindNextFile(dirh, &data)) { rv = GetLastError(); FindClose(dirh); if (rv == ERROR_NO_MORE_FILES) { break; } return (nni_win_error(rv)); } } 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)); } char * nni_plat_temp_dir(void) { char path[MAX_PATH + 1]; if (!GetTempPath(MAX_PATH + 1, path)) { return (NULL); } return (nni_strdup(path)); } int nni_plat_file_type(const char *name, int *typep) { DWORD attrs; if ((attrs = GetFileAttributes(name)) == INVALID_FILE_ATTRIBUTES) { return (nni_win_error(GetLastError())); } if (attrs & FILE_ATTRIBUTE_DIRECTORY) { *typep = NNI_PLAT_FILE_TYPE_DIR; } else if (attrs & FILE_ATTRIBUTE_DEVICE) { *typep = NNI_PLAT_FILE_TYPE_OTHER; } else if (attrs & 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 *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; // 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); } int nni_plat_file_lock(const char *path, nni_plat_flock *lk) { HANDLE h; // On Windows we do not have to explicitly lock the file, the // dwShareMode being set to zeor effectively prevents it. h = CreateFile(path, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { return (nni_win_error(GetLastError())); } lk->h = h; return (0); } void nni_plat_file_unlock(nni_plat_flock *lk) { HANDLE h = lk->h; (void) CloseHandle(h); lk->h = INVALID_HANDLE_VALUE; } #endif // NNG_PLATFORM_WINDOWS