ali@416: /* ali@441: * Copyright (C) 2011, 2012, 2014 J. Ali Harlow ali@416: * ali@416: * This program is free software; you can redistribute it and/or modify ali@416: * it under the terms of the GNU General Public License as published by ali@416: * the Free Software Foundation; either version 2 of the License, or ali@416: * (at your option) any later version. ali@416: * ali@416: * This program is distributed in the hope that it will be useful, ali@416: * but WITHOUT ANY WARRANTY; without even the implied warranty of ali@416: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ali@416: * GNU General Public License for more details. ali@416: * ali@416: * You should have received a copy of the GNU General Public License along ali@416: * with this program; if not, write to the Free Software Foundation, Inc., ali@416: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ali@416: */ ali@416: ali@416: #include "config.h" ali@416: ali@416: #if HAVE_WINDOWS_KTM ali@416: ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: ali@416: #include "razor.h" ali@416: #include "razor-internal.h" ali@416: ali@416: static int ali@416: razor_valid_root_name2(const wchar_t *name) ali@416: { ali@416: if (razor_allow_all_root_names()) ali@416: return !wcschr(name, '/'); ali@416: ali@416: return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' && ali@416: name[2] == '\0'; ali@416: } ali@416: ali@416: struct razor_wstr { ali@416: wchar_t *str; ali@416: int len, allocated; ali@416: }; ali@416: ali@416: static struct razor_wstr * ali@416: razor_wstr_create(const char *init, int len) ali@416: { ali@416: int n; ali@416: struct razor_wstr *wstr; ali@416: ali@416: wstr = malloc(sizeof(struct razor_wstr)); ali@416: ali@416: n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0); ali@416: if (len >= 0 && init[len]) ali@416: wstr->len = n++; ali@416: else ali@416: wstr->len = n - 1; ali@416: ali@416: wstr->allocated = n * 2; ali@416: wstr->str = malloc(wstr->allocated * sizeof(wchar_t)); ali@416: if (!wstr->str) { ali@416: free(wstr); ali@416: return NULL; ali@416: } ali@416: ali@416: (void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n); ali@416: if (len >= 0 && init[len]) ali@416: wstr->str[wstr->len] = 0; ali@416: ali@416: return wstr; ali@416: } ali@416: ali@416: static int ali@416: razor_wstr_append(struct razor_wstr *wstr, const char *s, int len) ali@416: { ali@416: int n, allocated; ali@416: wchar_t *str; ali@416: ali@416: n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0); ali@416: if (len < 0 || !s[len]) ali@416: n--; ali@416: ali@416: if (wstr->allocated <= wstr->len + n) { ali@416: allocated = (wstr->len + n + 1) * 2; ali@416: str = realloc(wstr->str, allocated * sizeof(wchar_t)); ali@416: if (!str) ali@416: return -1; ali@416: wstr->allocated = allocated; ali@416: wstr->str = str; ali@416: } ali@416: ali@416: (void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n); ali@416: wstr->len += n; ali@416: wstr->str[wstr->len] = 0; ali@416: ali@416: return 0; ali@416: } ali@416: ali@416: static void ali@416: razor_wstr_destroy(struct razor_wstr *wstr) ali@416: { ali@416: free(wstr->str); ali@416: free(wstr); ali@416: } ali@416: ali@416: RAZOR_EXPORT struct razor_atomic * ali@416: razor_atomic_open(const char *description) ali@416: { ali@416: wchar_t *buf; ali@416: struct razor_atomic *atomic; ali@416: ali@416: atomic = zalloc(sizeof *atomic); ali@416: buf = razor_utf8_to_utf16(description, -1); ali@416: atomic->transaction = CreateTransaction(NULL, 0, ali@416: TRANSACTION_DO_NOT_PROMOTE, ali@416: 0, 0, 0, buf); ali@416: free(buf); ali@416: ali@416: return atomic; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_commit(struct razor_atomic *atomic) ali@416: { ali@416: int retval; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: retval = !CommitTransaction(atomic->transaction); ali@416: ali@416: if (retval) { ali@424: razor_set_error_mswin(&atomic->error, NULL, GetLastError()); ali@416: RollbackTransaction(atomic->transaction); ali@416: } ali@416: ali@416: CloseHandle(atomic->transaction); ali@416: atomic->transaction = INVALID_HANDLE_VALUE; ali@416: ali@416: return retval; ali@416: } ali@416: ali@416: RAZOR_EXPORT void ali@416: razor_atomic_destroy(struct razor_atomic *atomic) ali@416: { ali@416: int i; ali@416: ali@416: for(i = 0; i < atomic->n_files; i++) { ali@416: if (atomic->files[i].h != INVALID_HANDLE_VALUE) { ali@416: CloseHandle(atomic->files[i].h); ali@416: free(atomic->files[i].path); ali@416: } ali@416: } ali@416: free(atomic->files); ali@416: if (atomic->transaction != INVALID_HANDLE_VALUE) { ali@416: RollbackTransaction(atomic->transaction); ali@416: CloseHandle(atomic->transaction); ali@416: } ali@423: if (atomic->error) ali@423: razor_error_free(atomic->error); ali@416: free(atomic); ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, ali@416: const char *path) ali@416: { ali@416: struct razor_wstr *buffer; ali@416: const char *slash, *next; ali@416: WIN32_FILE_ATTRIBUTE_DATA fa; ali@416: DWORD err; ali@416: int r, creating = 0; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: buffer = razor_wstr_create(root, -1); ali@441: slash = buffer->len ? SKIP_DRIVE_LETTER(path) : path; ali@416: ali@416: for (; *slash != '\0'; slash = next) { ali@416: next = strpbrk(slash + 1, "/\\"); ali@416: if (next == NULL) ali@416: break; ali@416: ali@416: razor_wstr_append(buffer, slash, next - slash); ali@416: ali@416: if (!creating) { ali@416: if (razor_valid_root_name2(buffer->str)) ali@416: continue; ali@416: ali@416: r = GetFileAttributesTransactedW(buffer->str, ali@416: GetFileExInfoStandard, ali@416: &fa, ali@416: atomic->transaction); ali@416: ali@416: if (!r) { ali@416: err = GetLastError(); ali@416: if (err == ERROR_FILE_NOT_FOUND) { ali@416: creating = 1; ali@416: } else { ali@424: razor_set_error_mswin(&atomic->error, ali@424: buffer->str, err); ali@416: razor_wstr_destroy(buffer); ali@416: return -1; ali@416: } ali@416: } else if (!(fa.dwFileAttributes& ali@416: FILE_ATTRIBUTE_DIRECTORY)) { ali@424: razor_set_error2(&atomic->error, buffer->str, ali@424: "Not a directory"); ali@416: razor_wstr_destroy(buffer); ali@416: return -1; ali@416: } ali@416: } ali@416: if (creating) { ali@416: if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL, ali@416: atomic->transaction)) { ali@424: razor_set_error_mswin(&atomic->error, ali@424: buffer->str, ali@424: GetLastError()); ali@416: razor_wstr_destroy(buffer); ali@416: return -1; ali@416: } ali@416: ali@416: /* FIXME: What to do about permissions for dirs we ali@416: * have to create but are not in the cpio archive? */ ali@416: } ali@416: } ali@416: ali@416: razor_wstr_destroy(buffer); ali@416: ali@416: return 0; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_remove(struct razor_atomic *atomic, const char *path) ali@416: { ali@416: wchar_t *buf; ali@416: DWORD err; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: buf = razor_utf8_to_utf16(path, -1); ali@416: ali@416: if (DeleteFileTransactedW(buf, atomic->transaction)) { ali@416: free(buf); ali@416: return 0; ali@416: } ali@416: ali@416: err = GetLastError(); ali@416: if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { ali@416: free(buf); ali@416: return 0; ali@416: } ali@416: ali@416: if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL, ali@416: atomic->transaction)) { ali@416: if (DeleteFileTransactedW(buf, atomic->transaction)) { ali@416: free(buf); ali@416: return 0; ali@416: } ali@416: err = GetLastError(); ali@416: } ali@416: ali@416: if (RemoveDirectoryTransactedW(buf, atomic->transaction) || ali@416: GetLastError() == ERROR_DIR_NOT_EMPTY) { ali@416: free(buf); ali@416: return 0; ali@416: } ali@416: ali@416: /* ali@416: * It would be tempting to use: ali@416: * MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) ali@416: * but unless we can guarantee that the system will be rebooted ali@416: * before we (or some other application) write another file with the ali@416: * same path, this is likely to cause more problems than it solves. ali@416: */ ali@416: ali@424: razor_set_error_mswin(&atomic->error, buf, err); ali@416: free(buf); ali@416: return -1; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, ali@416: const char *newpath) ali@416: { ali@416: wchar_t *oldbuf, *newbuf; ali@416: const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: newbuf = razor_utf8_to_utf16(newpath, -1); ali@416: oldbuf = razor_utf8_to_utf16(oldpath, -1); ali@416: ali@416: /* ali@416: * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will ali@416: * cover every case we care about _except_ replacing an empty ali@416: * directory with a file. Calling RemoveDirectoryTransacted() will deal ali@416: * with this case while having no effect in all other cases. ali@416: */ ali@416: (void)RemoveDirectoryTransactedW(newbuf, atomic->transaction); ali@416: ali@416: if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags, ali@416: atomic->transaction)) ali@424: razor_set_error_mswin(&atomic->error, newbuf, GetLastError()); ali@416: ali@416: free(newbuf); ali@416: free(oldbuf); ali@416: ali@423: return razor_atomic_in_error_state(atomic); ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, ali@416: mode_t mode) ali@416: { ali@416: wchar_t *buf; ali@416: DWORD err; ali@416: WIN32_FILE_ATTRIBUTE_DATA fa; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: buf = razor_utf8_to_utf16(dirname, -1); ali@416: ali@416: if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) { ali@416: err = GetLastError(); ali@416: if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) { ali@416: abort: ali@424: razor_set_error_mswin(&atomic->error, buf, err); ali@416: free(buf); ali@416: return -1; ali@416: } ali@416: ali@416: if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard, ali@416: &fa, atomic->transaction)) ali@416: goto abort; ali@416: ali@416: if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) { ali@416: if (razor_atomic_remove(atomic, dirname)) { ali@416: free(buf); ali@416: return -1; ali@416: } ali@416: if (!CreateDirectoryTransactedW(NULL, buf, NULL, ali@416: atomic->transaction)) { ali@416: err = GetLastError(); ali@416: goto abort; ali@416: } ali@416: } ali@416: } ali@416: ali@416: free(buf); ali@416: ali@416: return 0; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, ali@416: const char *path) ali@416: { ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: /* ali@416: * This isn't true, but symbolic links under Windows 7 ali@416: * need to know whether the target is a directory or not ali@416: * and we don't always know that at the time when the ali@416: * link is created, so it's a convienent lie for now. ali@416: */ ali@424: razor_set_error(&atomic->error, NULL, ali@424: "Symbolic links not supported on this platform"); ali@416: ali@416: return -1; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, ali@416: mode_t mode) ali@416: { ali@416: DWORD attribs; ali@416: struct razor_atomic_file *files; ali@416: int i = atomic->n_files; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: files = realloc(atomic->files, ali@416: (atomic->n_files+1) * sizeof(struct razor_atomic_file)); ali@416: if (!files) { ali@424: razor_set_error(&atomic->error, NULL, "Not enough memory"); ali@416: return -1; ali@416: } ali@416: atomic->n_files++; ali@416: atomic->files = files; ali@416: ali@416: files[i].path = razor_utf8_to_utf16(filename, -1); ali@416: ali@416: /* ali@416: * Passing CREATE_ALWAYS to CreateFileTransacted() will cover ali@416: * every case we care about _except_ replacing an empty directory ali@416: * with a file. Calling RemoveDirectoryTransacted() will deal ali@416: * with this case while having no effect in all other cases. ali@416: */ ali@416: (void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction); ali@416: ali@416: if (mode & S_IWUSR) ali@416: attribs = FILE_ATTRIBUTE_NORMAL; ali@416: else ali@416: attribs = FILE_ATTRIBUTE_READONLY; ali@416: ali@416: files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE, ali@416: 0, NULL, CREATE_ALWAYS, attribs, ali@416: NULL, atomic->transaction, NULL, ali@416: NULL); ali@416: ali@416: if (files[i].h == INVALID_HANDLE_VALUE) { ali@424: razor_set_error_mswin(&atomic->error, files[i].path, ali@424: GetLastError()); ali@416: free(files[i].path); ali@416: atomic->n_files--; ali@416: return -1; ali@416: } ali@416: ali@416: return i; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data, ali@416: size_t size) ali@416: { ali@416: DWORD written; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: assert(handle < atomic->n_files); ali@416: assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); ali@416: ali@416: while(size) { ali@416: if (!WriteFile(atomic->files[handle].h, data, size, &written, ali@416: NULL)) { ali@424: razor_set_error_mswin(&atomic->error, ali@424: atomic->files[handle].path, ali@424: GetLastError()); ali@416: ali@416: (void)CloseHandle(atomic->files[handle].h); ali@416: free(atomic->files[handle].path); ali@416: atomic->files[handle].path = NULL; ali@416: atomic->files[handle].h = INVALID_HANDLE_VALUE; ali@416: ali@416: return -1; ali@416: } ali@416: ali@416: data += written; ali@416: size -= written; ali@416: } ali@416: ali@416: return 0; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_sync(struct razor_atomic *atomic, int handle) ali@416: { ali@416: HANDLE h; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: assert(handle < atomic->n_files); ali@416: assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); ali@416: ali@416: if (!CloseHandle(atomic->files[handle].h)) { ali@424: razor_set_error_mswin(&atomic->error, ali@424: atomic->files[handle].path, ali@424: GetLastError()); ali@416: free(atomic->files[handle].path); ali@416: atomic->files[handle].path = NULL; ali@416: atomic->files[handle].h = INVALID_HANDLE_VALUE; ali@416: return -1; ali@416: } ali@416: ali@416: h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0, ali@416: NULL, OPEN_EXISTING, 0, NULL, ali@416: atomic->transaction, NULL, NULL); ali@416: atomic->files[handle].h = h; ali@416: ali@416: if (atomic->files[handle].h == INVALID_HANDLE_VALUE) { ali@424: razor_set_error_mswin(&atomic->error, ali@424: atomic->files[handle].path, ali@424: GetLastError()); ali@416: free(atomic->files[handle].path); ali@416: atomic->files[handle].path = NULL; ali@416: return -1; ali@416: } ali@416: ali@423: return razor_atomic_in_error_state(atomic); ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_close(struct razor_atomic *atomic, int handle) ali@416: { ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: assert(handle < atomic->n_files); ali@416: assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); ali@416: ali@416: if (!CloseHandle(atomic->files[handle].h)) ali@424: razor_set_error_mswin(&atomic->error, ali@424: atomic->files[handle].path, ali@424: GetLastError()); ali@416: ali@416: free(atomic->files[handle].path); ali@416: atomic->files[handle].path = NULL; ali@416: atomic->files[handle].h = INVALID_HANDLE_VALUE; ali@416: ali@416: while(atomic->n_files > 0 && ali@416: atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE) ali@416: atomic->n_files--; ali@416: ali@423: return razor_atomic_in_error_state(atomic); ali@416: } ali@416: ali@416: #endif /* HAVE_WINDOWS_KTM */