ali@403: /* ali@403: * Copyright (C) 2011 J. Ali Harlow ali@403: * ali@403: * This program is free software; you can redistribute it and/or modify ali@403: * it under the terms of the GNU General Public License as published by ali@403: * the Free Software Foundation; either version 2 of the License, or ali@403: * (at your option) any later version. ali@403: * ali@403: * This program is distributed in the hope that it will be useful, ali@403: * but WITHOUT ANY WARRANTY; without even the implied warranty of ali@403: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ali@403: * GNU General Public License for more details. ali@403: * ali@403: * You should have received a copy of the GNU General Public License along ali@403: * with this program; if not, write to the Free Software Foundation, Inc., ali@403: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ali@403: */ ali@403: ali@403: #include "config.h" ali@403: ali@403: #include ali@403: #ifdef MSWIN_API ali@403: #include ali@403: #endif ali@403: #include ali@403: #include ali@403: #include ali@403: #include ali@403: #include ali@403: #include ali@403: #include ali@403: #include ali@403: #if HAVE_WINDOWS_KTM ali@403: #include ali@403: #include ali@403: #endif ali@403: ali@403: #include "razor.h" ali@403: #include "razor-internal.h" ali@403: ali@403: /* ali@403: * Atomic transactions ali@403: */ ali@403: ali@403: #ifndef O_BINARY ali@403: #define O_BINARY 0 ali@403: #endif ali@403: ali@403: #define RAZOR_ASCII_ISALPHA(c) \ ali@403: ((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z') ali@403: ali@403: static int allow_all_root_names = 0; ali@403: ali@403: /* ali@403: * Primarily intended for testing named roots under UNIX platforms. ali@403: */ ali@403: RAZOR_EXPORT void ali@403: razor_disable_root_name_checks(int disable) ali@403: { ali@403: allow_all_root_names = disable; ali@403: } ali@403: ali@403: #ifdef MSWIN_API ali@403: ali@403: static char * ali@403: razor_utf16_to_utf8(const wchar_t *utf16, int len) ali@403: { ali@403: int n; ali@403: char *utf8; ali@403: ali@403: n = WideCharToMultiByte(CP_UTF8, 0, utf16, len, NULL, 0, NULL, NULL); ali@403: if (len >= 0 && utf16[len]) ali@403: n++; ali@403: utf8 = malloc(n); ali@403: (void)WideCharToMultiByte(CP_UTF8, 0, utf16, len, utf8, n, NULL, NULL); ali@403: if (len >= 0 && utf16[len]) ali@403: utf8[n - 1] = 0; ali@403: ali@403: return utf8; ali@403: } ali@403: ali@403: static wchar_t * ali@403: razor_utf8_to_utf16(const char *utf8, int len) ali@403: { ali@403: int n; ali@403: wchar_t *utf16; ali@403: ali@403: n = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); ali@403: if (len >= 0 && utf8[len]) ali@403: n++; ali@403: utf16 = malloc(n * sizeof(wchar_t)); ali@403: (void)MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, n); ali@403: if (len >= 0 && utf8[len]) ali@403: utf16[n - 1] = 0; ali@403: ali@403: return utf16; ali@403: } ali@403: ali@403: #endif /* MSWIN_API */ ali@403: ali@403: #if HAVE_WINDOWS_KTM ali@403: ali@403: static int ali@403: razor_valid_root_name(const wchar_t *name) ali@403: { ali@403: if (allow_all_root_names) ali@403: return !wcschr(name, '/'); ali@403: ali@403: return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' && ali@403: name[2] == '\0'; ali@403: } ali@403: ali@403: struct razor_atomic { ali@403: HANDLE transaction; ali@403: int n_files; ali@403: struct razor_atomic_file { ali@403: wchar_t *path; ali@403: HANDLE h; ali@403: } *files; ali@403: char *error_path; ali@403: char *error_str; ali@403: char *error_msg; ali@403: }; ali@403: ali@403: struct razor_wstr { ali@403: wchar_t *str; ali@403: int len, allocated; ali@403: }; ali@403: ali@403: static struct razor_wstr * ali@403: razor_wstr_create(const char *init, int len) ali@403: { ali@403: int n; ali@403: struct razor_wstr *wstr; ali@403: ali@403: wstr = malloc(sizeof(struct razor_wstr)); ali@403: ali@403: n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0); ali@403: if (len >= 0 && init[len]) ali@403: wstr->len = n++; ali@403: else ali@403: wstr->len = n - 1; ali@403: ali@403: wstr->allocated = n * 2; ali@403: wstr->str = malloc(wstr->allocated * sizeof(wchar_t)); ali@403: if (!wstr->str) { ali@403: free(wstr); ali@403: return NULL; ali@403: } ali@403: ali@403: (void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n); ali@403: if (len >= 0 && init[len]) ali@403: wstr->str[wstr->len] = 0; ali@403: ali@403: return wstr; ali@403: } ali@403: ali@403: static int ali@403: razor_wstr_append(struct razor_wstr *wstr, const char *s, int len) ali@403: { ali@403: int n, allocated; ali@403: wchar_t *str; ali@403: ali@403: n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0); ali@403: if (len < 0 || !s[len]) ali@403: n--; ali@403: ali@403: if (wstr->allocated <= wstr->len + n) { ali@403: allocated = (wstr->len + n + 1) * 2; ali@403: str = realloc(wstr->str, allocated * sizeof(wchar_t)); ali@403: if (!str) ali@403: return -1; ali@403: wstr->allocated = allocated; ali@403: wstr->str = str; ali@403: } ali@403: ali@403: (void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n); ali@403: wstr->len += n; ali@403: wstr->str[wstr->len] = 0; ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: static void ali@403: razor_wstr_destroy(struct razor_wstr *wstr) ali@403: { ali@403: free(wstr->str); ali@403: free(wstr); ali@403: } ali@403: ali@403: RAZOR_EXPORT struct razor_atomic * ali@403: razor_atomic_open(const char *description) ali@403: { ali@403: wchar_t *buf; ali@403: struct razor_atomic *atomic; ali@403: ali@403: atomic = zalloc(sizeof *atomic); ali@403: buf = razor_utf8_to_utf16(description, -1); ali@403: atomic->transaction = CreateTransaction(NULL, 0, ali@403: TRANSACTION_DO_NOT_PROMOTE, ali@403: 0, 0, 0, buf); ali@403: free(buf); ali@403: ali@403: return atomic; ali@403: } ali@403: ali@403: static void ali@403: razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path, ali@403: const char *str) ali@403: { ali@403: assert(!atomic->error_str); ali@403: ali@403: free(atomic->error_path); ali@403: ali@403: if (path) ali@403: atomic->error_path = razor_utf16_to_utf8(path, -1); ali@403: else ali@403: atomic->error_path = NULL; ali@403: ali@403: atomic->error_str = strdup(str); ali@403: } ali@403: ali@403: static void ali@403: razor_atomic_set_error(struct razor_atomic *atomic, const wchar_t *path, ali@403: DWORD error) ali@403: { ali@403: wchar_t *buf; ali@403: ali@403: assert(!atomic->error_str); ali@403: ali@403: free(atomic->error_path); ali@403: ali@403: if (path) ali@403: atomic->error_path = razor_utf16_to_utf8(path, -1); ali@403: else ali@403: atomic->error_path = NULL; ali@403: ali@403: FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER| ali@403: FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, ali@403: NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), ali@403: (LPWSTR)&buf, 0, NULL); ali@403: atomic->error_str = razor_utf16_to_utf8(buf, -1); ali@403: LocalFree(buf); ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_commit(struct razor_atomic *atomic) ali@403: { ali@403: int retval; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: retval = !CommitTransaction(atomic->transaction); ali@403: ali@403: if (retval) { ali@403: razor_atomic_set_error(atomic, NULL, GetLastError()); ali@403: RollbackTransaction(atomic->transaction); ali@403: } ali@403: ali@403: CloseHandle(atomic->transaction); ali@403: atomic->transaction = INVALID_HANDLE_VALUE; ali@403: ali@403: return retval; ali@403: } ali@403: ali@403: RAZOR_EXPORT void ali@403: razor_atomic_destroy(struct razor_atomic *atomic) ali@403: { ali@403: int i; ali@403: ali@403: for(i = 0; i < atomic->n_files; i++) { ali@403: if (atomic->files[i].h != INVALID_HANDLE_VALUE) { ali@403: CloseHandle(atomic->files[i].h); ali@403: free(atomic->files[i].path); ali@403: } ali@403: } ali@403: free(atomic->files); ali@403: if (atomic->transaction != INVALID_HANDLE_VALUE) { ali@403: RollbackTransaction(atomic->transaction); ali@403: CloseHandle(atomic->transaction); ali@403: } ali@403: free(atomic->error_path); ali@403: free(atomic->error_str); ali@403: free(atomic->error_msg); ali@403: free(atomic); ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, ali@403: const char *path) ali@403: { ali@403: struct razor_wstr *buffer; ali@403: const char *slash, *s, *next; ali@403: WIN32_FILE_ATTRIBUTE_DATA fa; ali@403: DWORD err; ali@403: int r, creating = 0; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: buffer = razor_wstr_create(root, -1); ali@403: slash = path; ali@403: ali@403: for (; *slash != '\0'; slash = next) { ali@403: next = strpbrk(slash + 1, "/\\"); ali@403: if (next == NULL) ali@403: break; ali@403: ali@403: razor_wstr_append(buffer, slash, next - slash); ali@403: ali@403: if (!creating) { ali@403: if (razor_valid_root_name(buffer->str)) ali@403: continue; ali@403: ali@403: r = GetFileAttributesTransactedW(buffer->str, ali@403: GetFileExInfoStandard, ali@403: &fa, ali@403: atomic->transaction); ali@403: ali@403: if (!r) { ali@403: err = GetLastError(); ali@403: if (err == ERROR_FILE_NOT_FOUND) { ali@403: creating = 1; ali@403: } else { ali@403: razor_atomic_set_error(atomic, ali@403: buffer->str, ali@403: err); ali@403: razor_wstr_destroy(buffer); ali@403: return -1; ali@403: } ali@403: } else if (!(fa.dwFileAttributes& ali@403: FILE_ATTRIBUTE_DIRECTORY)) { ali@403: razor_atomic_set_error_str(atomic, buffer->str, ali@403: "Not a directory"); ali@403: razor_wstr_destroy(buffer); ali@403: return -1; ali@403: } ali@403: } ali@403: if (creating) { ali@403: if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL, ali@403: atomic->transaction)) { ali@403: razor_atomic_set_error(atomic, buffer->str, ali@403: GetLastError()); ali@403: razor_wstr_destroy(buffer); ali@403: return -1; ali@403: } ali@403: ali@403: /* FIXME: What to do about permissions for dirs we ali@403: * have to create but are not in the cpio archive? */ ali@403: } ali@403: } ali@403: ali@403: razor_wstr_destroy(buffer); ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_remove(struct razor_atomic *atomic, const char *path) ali@403: { ali@403: wchar_t *buf; ali@403: DWORD err; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: buf = razor_utf8_to_utf16(path, -1); ali@403: ali@403: if (DeleteFileTransactedW(buf, atomic->transaction)) { ali@403: free(buf); ali@403: return 0; ali@403: } ali@403: ali@403: err = GetLastError(); ali@403: if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { ali@403: free(buf); ali@403: return 0; ali@403: } ali@403: ali@403: if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL, ali@403: atomic->transaction)) { ali@403: if (DeleteFileTransactedW(buf, atomic->transaction)) { ali@403: free(buf); ali@403: return 0; ali@403: } ali@403: err = GetLastError(); ali@403: } ali@403: ali@403: if (RemoveDirectoryTransactedW(buf, atomic->transaction) || ali@403: GetLastError() == ERROR_DIR_NOT_EMPTY) { ali@403: free(buf); ali@403: return 0; ali@403: } ali@403: ali@403: /* ali@403: * It would be tempting to use: ali@403: * MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) ali@403: * but unless we can guarantee that the system will be rebooted ali@403: * before we (or some other application) write another file with the ali@403: * same path, this is likely to cause more problems than it solves. ali@403: */ ali@403: ali@403: razor_atomic_set_error(atomic, buf, err); ali@403: free(buf); ali@403: return -1; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, ali@403: const char *newpath) ali@403: { ali@403: wchar_t *oldbuf, *newbuf; ali@403: const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: newbuf = razor_utf8_to_utf16(newpath, -1); ali@403: oldbuf = razor_utf8_to_utf16(oldpath, -1); ali@403: ali@403: /* ali@403: * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will ali@403: * cover every case we care about _except_ replacing an empty ali@403: * directory with a file. Calling RemoveDirectoryTransacted() will deal ali@403: * with this case while having no effect in all other cases. ali@403: */ ali@403: (void)RemoveDirectoryTransactedW(newbuf, atomic->transaction); ali@403: ali@403: if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags, ali@403: atomic->transaction)) ali@403: razor_atomic_set_error(atomic, newbuf, GetLastError()); ali@403: ali@403: free(newbuf); ali@403: free(oldbuf); ali@403: ali@403: return !!atomic->error_str; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, ali@403: mode_t mode) ali@403: { ali@403: wchar_t *buf; ali@403: DWORD err; ali@403: WIN32_FILE_ATTRIBUTE_DATA fa; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: buf = razor_utf8_to_utf16(dirname, -1); ali@403: ali@403: if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) { ali@403: err = GetLastError(); ali@403: if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) { ali@403: abort: ali@403: razor_atomic_set_error(atomic, buf, err); ali@403: free(buf); ali@403: return -1; ali@403: } ali@403: ali@403: if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard, ali@403: &fa, atomic->transaction)) ali@403: goto abort; ali@403: ali@403: if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) { ali@403: if (razor_atomic_remove(atomic, dirname)) { ali@403: free(buf); ali@403: return -1; ali@403: } ali@403: if (!CreateDirectoryTransactedW(NULL, buf, NULL, ali@403: atomic->transaction)) { ali@403: err = GetLastError(); ali@403: goto abort; ali@403: } ali@403: } ali@403: } ali@403: ali@403: free(buf); ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, ali@403: const char *path) ali@403: { ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: /* ali@403: * This isn't true, but symbolic links under Windows 7 ali@403: * need to know whether the target is a directory or not ali@403: * and we don't always know that at the time when the ali@403: * link is created, so it's a convienent lie for now. ali@403: */ ali@403: razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported " ali@403: "on this platform"); ali@403: ali@403: return -1; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, ali@403: mode_t mode) ali@403: { ali@403: DWORD attribs; ali@403: struct razor_atomic_file *files; ali@403: int i = atomic->n_files; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: files = realloc(atomic->files, ali@403: (atomic->n_files+1) * sizeof(struct razor_atomic_file)); ali@403: if (!files) { ali@403: razor_atomic_set_error_str(atomic, NULL, "Not enough memory"); ali@403: return -1; ali@403: } ali@403: atomic->n_files++; ali@403: atomic->files = files; ali@403: ali@403: files[i].path = razor_utf8_to_utf16(filename, -1); ali@403: ali@403: /* ali@403: * Passing CREATE_ALWAYS to CreateFileTransacted() will cover ali@403: * every case we care about _except_ replacing an empty directory ali@403: * with a file. Calling RemoveDirectoryTransacted() will deal ali@403: * with this case while having no effect in all other cases. ali@403: */ ali@403: (void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction); ali@403: ali@403: if (mode & S_IWUSR) ali@403: attribs = FILE_ATTRIBUTE_NORMAL; ali@403: else ali@403: attribs = FILE_ATTRIBUTE_READONLY; ali@403: ali@403: files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE, ali@403: 0, NULL, CREATE_ALWAYS, attribs, ali@403: NULL, atomic->transaction, NULL, ali@403: NULL); ali@403: ali@403: if (files[i].h == INVALID_HANDLE_VALUE) { ali@403: razor_atomic_set_error(atomic, files[i].path, GetLastError()); ali@403: free(files[i].path); ali@403: atomic->n_files--; ali@403: return -1; ali@403: } ali@403: ali@403: return i; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data, ali@403: size_t size) ali@403: { ali@403: DWORD written; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: assert(handle < atomic->n_files); ali@403: assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); ali@403: ali@403: while(size) { ali@403: if (!WriteFile(atomic->files[handle].h, data, size, &written, ali@403: NULL)) { ali@403: razor_atomic_set_error(atomic, ali@403: atomic->files[handle].path, ali@403: GetLastError()); ali@403: ali@403: (void)CloseHandle(atomic->files[handle].h); ali@403: free(atomic->files[handle].path); ali@403: atomic->files[handle].path = NULL; ali@403: atomic->files[handle].h = INVALID_HANDLE_VALUE; ali@403: ali@403: return -1; ali@403: } ali@403: ali@403: data += written; ali@403: size -= written; ali@403: } ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_sync(struct razor_atomic *atomic, int handle) ali@403: { ali@403: HANDLE h; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: assert(handle < atomic->n_files); ali@403: assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); ali@403: ali@403: if (!CloseHandle(atomic->files[handle].h)) { ali@403: razor_atomic_set_error(atomic, atomic->files[handle].path, ali@403: GetLastError()); ali@403: free(atomic->files[handle].path); ali@403: atomic->files[handle].path = NULL; ali@403: atomic->files[handle].h = INVALID_HANDLE_VALUE; ali@403: return -1; ali@403: } ali@403: ali@403: h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0, ali@403: NULL, OPEN_EXISTING, 0, NULL, ali@403: atomic->transaction, NULL, NULL); ali@403: atomic->files[handle].h = h; ali@403: ali@403: if (atomic->files[handle].h == INVALID_HANDLE_VALUE) { ali@403: razor_atomic_set_error(atomic, atomic->files[handle].path, ali@403: GetLastError()); ali@403: free(atomic->files[handle].path); ali@403: atomic->files[handle].path = NULL; ali@403: return -1; ali@403: } ali@403: ali@403: return !!atomic->error_str; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_close(struct razor_atomic *atomic, int handle) ali@403: { ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: assert(handle < atomic->n_files); ali@403: assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); ali@403: ali@403: if (!CloseHandle(atomic->files[handle].h)) ali@403: razor_atomic_set_error(atomic, atomic->files[handle].path, ali@403: GetLastError()); ali@403: ali@403: free(atomic->files[handle].path); ali@403: atomic->files[handle].path = NULL; ali@403: atomic->files[handle].h = INVALID_HANDLE_VALUE; ali@403: ali@403: while(atomic->n_files > 0 && ali@403: atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE) ali@403: atomic->n_files--; ali@403: ali@403: return !!atomic->error_str; ali@403: } ali@403: ali@403: #else /* HAVE_WINDOWS_KVM */ ali@403: ali@403: static int ali@403: razor_valid_root_name(const char *name) ali@403: { ali@403: if (allow_all_root_names) { ali@403: #ifdef MSWIN_API ali@403: return !strpbrk(name, "/\\"); ali@403: #else ali@403: return !strchr(name, '/'); ali@403: #endif ali@403: } ali@403: ali@403: #ifdef MSWIN_API ali@403: return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' && ali@403: name[2] == '\0'; ali@403: #else ali@403: return name[0] == '\0'; ali@403: #endif ali@403: } ali@403: ali@403: struct razor_atomic { ali@403: char *error_path; ali@403: char *error_str; ali@403: char *error_msg; ali@403: }; ali@403: ali@403: RAZOR_EXPORT struct razor_atomic * ali@403: razor_atomic_open(const char *description) ali@403: { ali@403: struct razor_atomic *atomic; ali@403: ali@403: atomic = zalloc(sizeof *atomic); ali@403: ali@403: return atomic; ali@403: } ali@403: ali@403: static void ali@403: razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path, ali@403: const char *str) ali@403: { ali@403: assert(!atomic->error_str); ali@403: ali@403: atomic->error_path = path ? strdup(path) : NULL; ali@403: atomic->error_str = strdup(str); ali@403: } ali@403: ali@403: #ifdef MSWIN_API ali@403: static void ali@403: razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path, ali@403: DWORD error) ali@403: { ali@403: wchar_t *buf; ali@403: ali@403: assert(!atomic->error_str); ali@403: ali@403: free(atomic->error_path); ali@403: ali@403: if (path) ali@403: atomic->error_path = razor_utf16_to_utf8(path, -1); ali@403: else ali@403: atomic->error_path = NULL; ali@403: ali@403: FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER| ali@403: FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, ali@403: NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), ali@403: (LPWSTR)&buf, 0, NULL); ali@403: atomic->error_str = razor_utf16_to_utf8(buf, -1); ali@403: LocalFree(buf); ali@403: } ali@403: #endif ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_commit(struct razor_atomic *atomic) ali@403: { ali@403: return !!atomic->error_str; ali@403: } ali@403: ali@403: RAZOR_EXPORT void ali@403: razor_atomic_destroy(struct razor_atomic *atomic) ali@403: { ali@403: free(atomic->error_path); ali@403: free(atomic->error_str); ali@403: free(atomic->error_msg); ali@403: free(atomic); ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, ali@403: const char *path) ali@403: { ali@403: char buffer[PATH_MAX], *p; ali@403: const char *slash, *next; ali@403: struct stat buf; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: strcpy(buffer, root); ali@403: p = buffer + strlen(buffer); ali@403: slash = path; ali@403: for (slash = path; *slash != '\0'; slash = next) { ali@403: #ifdef MSWIN_API ali@403: next = strpbrk(slash + 1, "/\\"); ali@403: #else ali@403: next = strchr(slash + 1, '/'); ali@403: #endif ali@403: if (next == NULL) ali@403: break; ali@403: ali@403: memcpy(p, slash, next - slash); ali@403: p += next - slash; ali@403: *p = '\0'; ali@403: ali@403: if (razor_valid_root_name(buffer)) ali@403: continue; ali@403: ali@403: if (stat(buffer, &buf) == 0) { ali@403: if (!S_ISDIR(buf.st_mode)) { ali@403: razor_atomic_set_error_str(atomic, buffer, ali@403: "Not a directory"); ali@403: return -1; ali@403: } ali@403: } else if (mkdir(buffer, 0777) < 0) { ali@403: razor_atomic_set_error_str(atomic, buffer, ali@403: strerror(errno)); ali@403: return -1; ali@403: } ali@403: } ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_remove(struct razor_atomic *atomic, const char *path) ali@403: { ali@403: #ifdef MSWIN_API ali@403: wchar_t *buf; ali@403: DWORD err; ali@403: #endif ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: #ifdef MSWIN_API ali@403: buf = razor_utf8_to_utf16(path, -1); ali@403: ali@403: if (!DeleteFileW(buf)) { ali@403: err = GetLastError(); ali@403: if (err != ERROR_FILE_NOT_FOUND && ali@403: err != ERROR_PATH_NOT_FOUND && ali@403: !(SetFileAttributesW(buf, FILE_ATTRIBUTE_NORMAL) && ali@403: DeleteFileW(buf)) && ali@403: !RemoveDirectoryW(buf) && ali@403: GetLastError() != ERROR_DIR_NOT_EMPTY) ali@403: razor_atomic_set_error_mswin(atomic, buf, err); ali@403: } ali@403: ali@403: free(buf); ali@403: #else ali@403: if (remove(path)) ali@403: razor_atomic_set_error_str(atomic, path, strerror(errno)); ali@403: #endif ali@403: ali@403: return !!atomic->error_str; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, ali@403: const char *newpath) ali@403: { ali@403: #ifdef MSWIN_API ali@403: wchar_t *oldbuf, *newbuf; ali@403: const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; ali@403: #endif ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: #ifdef MSWIN_API ali@403: newbuf = razor_utf8_to_utf16(newpath, -1); ali@403: oldbuf = razor_utf8_to_utf16(oldpath, -1); ali@403: ali@403: /* ali@403: * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will ali@403: * cover every case we care about _except_ replacing an empty ali@403: * directory with a file. Calling RemoveDirectory() will deal ali@403: * with this case while having no effect in all other cases. ali@403: */ ali@403: (void)RemoveDirectoryW(newbuf); ali@403: ali@403: if (!MoveFileExW(oldbuf, newbuf, flags)) ali@403: razor_atomic_set_error_mswin(atomic, newbuf, GetLastError()); ali@403: ali@403: free(newbuf); ali@403: free(oldbuf); ali@403: #else ali@403: if (rename(oldpath, newpath)) ali@403: razor_atomic_set_error_str(atomic, newpath, strerror(errno)); ali@403: #endif ali@403: ali@403: return !!atomic->error_str; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, ali@403: mode_t mode) ali@403: { ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: if (!mkdir(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO))) ali@403: return 0; ali@403: ali@403: if (errno != EEXIST) { ali@403: razor_atomic_set_error_str(atomic, dirname, strerror(errno)); ali@403: return -1; ali@403: } ali@403: ali@403: if (chmod(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) < 0) { ali@403: razor_atomic_set_error_str(atomic, dirname, strerror(errno)); ali@403: return -1; ali@403: } ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, ali@403: const char *path) ali@403: { ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: #if HAVE_SYMLINK ali@403: if (symlink(target, path) < 0) { ali@403: razor_atomic_set_error_str(atomic, NULL, strerror(errno)); ali@403: return -1; ali@403: } ali@403: #else ali@403: razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported " ali@403: "on this platform"); ali@403: #endif ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, ali@403: mode_t mode) ali@403: { ali@403: int fd; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: atomic->error_path = strdup(filename); ali@403: fd = open(atomic->error_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, ali@403: mode & (S_IRWXU | S_IRWXG | S_IRWXO)); ali@403: ali@403: if (fd == -1) ali@403: razor_atomic_set_error_str(atomic, NULL, strerror(errno)); ali@403: ali@403: return fd; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data, ali@403: size_t size) ali@403: { ali@403: int written; ali@403: ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: while(size) { ali@403: written = write(fd, data, size); ali@403: if (written < 0) { ali@403: razor_atomic_set_error_str(atomic, NULL, strerror(errno)); ali@403: ali@403: (void)close(fd); ali@403: ali@403: return -1; ali@403: } ali@403: ali@403: data += written; ali@403: size -= written; ali@403: } ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_sync(struct razor_atomic *atomic, int handle) ali@403: { ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: if (fsync(handle) < 0) { ali@403: razor_atomic_set_error_str(atomic, NULL, strerror(errno)); ali@403: return -1; ali@403: } ali@403: ali@403: free(atomic->error_path); ali@403: atomic->error_path = NULL; ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_close(struct razor_atomic *atomic, int fd) ali@403: { ali@403: if (atomic->error_str) ali@403: return -1; ali@403: ali@403: if (close(fd) < 0) { ali@403: razor_atomic_set_error_str(atomic, NULL, strerror(errno)); ali@403: return -1; ali@403: } ali@403: ali@403: free(atomic->error_path); ali@403: atomic->error_path = NULL; ali@403: ali@403: return 0; ali@403: } ali@403: ali@403: #endif /* HAVE_WINDOWS_KVM */ ali@403: ali@403: RAZOR_EXPORT const char * ali@403: razor_atomic_get_error_msg(struct razor_atomic *atomic) ali@403: { ali@403: if (!atomic->error_msg) { ali@403: if (atomic->error_path) ali@403: atomic->error_msg = razor_concat(atomic->error_path, ali@403: ": ", ali@403: atomic->error_str, ali@403: NULL); ali@403: else ali@403: atomic->error_msg = strdup(atomic->error_str); ali@403: } ali@403: ali@403: return atomic->error_msg; ali@403: } ali@403: ali@403: RAZOR_EXPORT void ali@403: razor_atomic_abort(struct razor_atomic *atomic, const char *error_msg) ali@403: { ali@403: if (!atomic->error_str) ali@403: razor_atomic_set_error_str(atomic, NULL, error_msg); ali@403: } ali@403: ali@403: RAZOR_EXPORT int ali@403: razor_atomic_in_error_state(struct razor_atomic *atomic) ali@403: { ali@403: return !!atomic->error_str; ali@403: }