1.1 --- a/librazor/atomic.c Thu Nov 10 10:35:21 2011 +0000
1.2 +++ b/librazor/atomic.c Thu Feb 09 20:45:27 2012 +0000
1.3 @@ -1,5 +1,5 @@
1.4 /*
1.5 - * Copyright (C) 2011 J. Ali Harlow <ali@juiblex.co.uk>
1.6 + * Copyright (C) 2011-2012 J. Ali Harlow <ali@juiblex.co.uk>
1.7 *
1.8 * This program is free software; you can redistribute it and/or modify
1.9 * it under the terms of the GNU General Public License as published by
1.10 @@ -19,21 +19,15 @@
1.11 #include "config.h"
1.12
1.13 #include <stdlib.h>
1.14 -#ifdef MSWIN_API
1.15 -#include <windows.h>
1.16 -#endif
1.17 #include <stdio.h>
1.18 #include <limits.h>
1.19 #include <errno.h>
1.20 #include <unistd.h>
1.21 +#include <sys/types.h>
1.22 +#include <sys/stat.h>
1.23 #include <fcntl.h>
1.24 -#include <sys/stat.h>
1.25 #include <string.h>
1.26 #include <assert.h>
1.27 -#if HAVE_WINDOWS_KTM
1.28 -#include <wchar.h>
1.29 -#include <ktmw32.h>
1.30 -#endif
1.31
1.32 #include "razor.h"
1.33 #include "razor-internal.h"
1.34 @@ -42,13 +36,6 @@
1.35 * Atomic transactions
1.36 */
1.37
1.38 -#ifndef O_BINARY
1.39 -#define O_BINARY 0
1.40 -#endif
1.41 -
1.42 -#define RAZOR_ASCII_ISALPHA(c) \
1.43 - ((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
1.44 -
1.45 static int allow_all_root_names = 0;
1.46
1.47 /*
1.48 @@ -60,918 +47,12 @@
1.49 allow_all_root_names = disable;
1.50 }
1.51
1.52 -#ifdef MSWIN_API
1.53 -
1.54 -static char *
1.55 -razor_utf16_to_utf8(const wchar_t *utf16, int len)
1.56 +int
1.57 +razor_allow_all_root_names(void)
1.58 {
1.59 - int n;
1.60 - char *utf8;
1.61 -
1.62 - n = WideCharToMultiByte(CP_UTF8, 0, utf16, len, NULL, 0, NULL, NULL);
1.63 - if (len >= 0 && utf16[len])
1.64 - n++;
1.65 - utf8 = malloc(n);
1.66 - (void)WideCharToMultiByte(CP_UTF8, 0, utf16, len, utf8, n, NULL, NULL);
1.67 - if (len >= 0 && utf16[len])
1.68 - utf8[n - 1] = 0;
1.69 -
1.70 - return utf8;
1.71 + return allow_all_root_names;
1.72 }
1.73
1.74 -static wchar_t *
1.75 -razor_utf8_to_utf16(const char *utf8, int len)
1.76 -{
1.77 - int n;
1.78 - wchar_t *utf16;
1.79 -
1.80 - n = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0);
1.81 - if (len >= 0 && utf8[len])
1.82 - n++;
1.83 - utf16 = malloc(n * sizeof(wchar_t));
1.84 - (void)MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, n);
1.85 - if (len >= 0 && utf8[len])
1.86 - utf16[n - 1] = 0;
1.87 -
1.88 - return utf16;
1.89 -}
1.90 -
1.91 -#endif /* MSWIN_API */
1.92 -
1.93 -#if HAVE_WINDOWS_KTM
1.94 -
1.95 -static int
1.96 -razor_valid_root_name(const wchar_t *name)
1.97 -{
1.98 - if (allow_all_root_names)
1.99 - return !wcschr(name, '/');
1.100 -
1.101 - return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
1.102 - name[2] == '\0';
1.103 -}
1.104 -
1.105 -struct razor_atomic {
1.106 - HANDLE transaction;
1.107 - int n_files;
1.108 - struct razor_atomic_file {
1.109 - wchar_t *path;
1.110 - HANDLE h;
1.111 - } *files;
1.112 - char *error_path;
1.113 - char *error_str;
1.114 - char *error_msg;
1.115 -};
1.116 -
1.117 -struct razor_wstr {
1.118 - wchar_t *str;
1.119 - int len, allocated;
1.120 -};
1.121 -
1.122 -static struct razor_wstr *
1.123 -razor_wstr_create(const char *init, int len)
1.124 -{
1.125 - int n;
1.126 - struct razor_wstr *wstr;
1.127 -
1.128 - wstr = malloc(sizeof(struct razor_wstr));
1.129 -
1.130 - n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
1.131 - if (len >= 0 && init[len])
1.132 - wstr->len = n++;
1.133 - else
1.134 - wstr->len = n - 1;
1.135 -
1.136 - wstr->allocated = n * 2;
1.137 - wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
1.138 - if (!wstr->str) {
1.139 - free(wstr);
1.140 - return NULL;
1.141 - }
1.142 -
1.143 - (void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
1.144 - if (len >= 0 && init[len])
1.145 - wstr->str[wstr->len] = 0;
1.146 -
1.147 - return wstr;
1.148 -}
1.149 -
1.150 -static int
1.151 -razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
1.152 -{
1.153 - int n, allocated;
1.154 - wchar_t *str;
1.155 -
1.156 - n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
1.157 - if (len < 0 || !s[len])
1.158 - n--;
1.159 -
1.160 - if (wstr->allocated <= wstr->len + n) {
1.161 - allocated = (wstr->len + n + 1) * 2;
1.162 - str = realloc(wstr->str, allocated * sizeof(wchar_t));
1.163 - if (!str)
1.164 - return -1;
1.165 - wstr->allocated = allocated;
1.166 - wstr->str = str;
1.167 - }
1.168 -
1.169 - (void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
1.170 - wstr->len += n;
1.171 - wstr->str[wstr->len] = 0;
1.172 -
1.173 - return 0;
1.174 -}
1.175 -
1.176 -static void
1.177 -razor_wstr_destroy(struct razor_wstr *wstr)
1.178 -{
1.179 - free(wstr->str);
1.180 - free(wstr);
1.181 -}
1.182 -
1.183 -RAZOR_EXPORT struct razor_atomic *
1.184 -razor_atomic_open(const char *description)
1.185 -{
1.186 - wchar_t *buf;
1.187 - struct razor_atomic *atomic;
1.188 -
1.189 - atomic = zalloc(sizeof *atomic);
1.190 - buf = razor_utf8_to_utf16(description, -1);
1.191 - atomic->transaction = CreateTransaction(NULL, 0,
1.192 - TRANSACTION_DO_NOT_PROMOTE,
1.193 - 0, 0, 0, buf);
1.194 - free(buf);
1.195 -
1.196 - return atomic;
1.197 -}
1.198 -
1.199 -static void
1.200 -razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path,
1.201 - const char *str)
1.202 -{
1.203 - assert(!atomic->error_str);
1.204 -
1.205 - free(atomic->error_path);
1.206 -
1.207 - if (path)
1.208 - atomic->error_path = razor_utf16_to_utf8(path, -1);
1.209 - else
1.210 - atomic->error_path = NULL;
1.211 -
1.212 - atomic->error_str = strdup(str);
1.213 -}
1.214 -
1.215 -static void
1.216 -razor_atomic_set_error(struct razor_atomic *atomic, const wchar_t *path,
1.217 - DWORD error)
1.218 -{
1.219 - wchar_t *buf;
1.220 -
1.221 - assert(!atomic->error_str);
1.222 -
1.223 - free(atomic->error_path);
1.224 -
1.225 - if (path)
1.226 - atomic->error_path = razor_utf16_to_utf8(path, -1);
1.227 - else
1.228 - atomic->error_path = NULL;
1.229 -
1.230 - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
1.231 - FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
1.232 - NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
1.233 - (LPWSTR)&buf, 0, NULL);
1.234 - atomic->error_str = razor_utf16_to_utf8(buf, -1);
1.235 - LocalFree(buf);
1.236 -}
1.237 -
1.238 -RAZOR_EXPORT int
1.239 -razor_atomic_commit(struct razor_atomic *atomic)
1.240 -{
1.241 - int retval;
1.242 -
1.243 - if (atomic->error_str)
1.244 - return -1;
1.245 -
1.246 - retval = !CommitTransaction(atomic->transaction);
1.247 -
1.248 - if (retval) {
1.249 - razor_atomic_set_error(atomic, NULL, GetLastError());
1.250 - RollbackTransaction(atomic->transaction);
1.251 - }
1.252 -
1.253 - CloseHandle(atomic->transaction);
1.254 - atomic->transaction = INVALID_HANDLE_VALUE;
1.255 -
1.256 - return retval;
1.257 -}
1.258 -
1.259 -RAZOR_EXPORT void
1.260 -razor_atomic_destroy(struct razor_atomic *atomic)
1.261 -{
1.262 - int i;
1.263 -
1.264 - for(i = 0; i < atomic->n_files; i++) {
1.265 - if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
1.266 - CloseHandle(atomic->files[i].h);
1.267 - free(atomic->files[i].path);
1.268 - }
1.269 - }
1.270 - free(atomic->files);
1.271 - if (atomic->transaction != INVALID_HANDLE_VALUE) {
1.272 - RollbackTransaction(atomic->transaction);
1.273 - CloseHandle(atomic->transaction);
1.274 - }
1.275 - free(atomic->error_path);
1.276 - free(atomic->error_str);
1.277 - free(atomic->error_msg);
1.278 - free(atomic);
1.279 -}
1.280 -
1.281 -RAZOR_EXPORT int
1.282 -razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
1.283 - const char *path)
1.284 -{
1.285 - struct razor_wstr *buffer;
1.286 - const char *slash, *s, *next;
1.287 - WIN32_FILE_ATTRIBUTE_DATA fa;
1.288 - DWORD err;
1.289 - int r, creating = 0;
1.290 -
1.291 - if (atomic->error_str)
1.292 - return -1;
1.293 -
1.294 - buffer = razor_wstr_create(root, -1);
1.295 - slash = path;
1.296 -
1.297 - for (; *slash != '\0'; slash = next) {
1.298 - next = strpbrk(slash + 1, "/\\");
1.299 - if (next == NULL)
1.300 - break;
1.301 -
1.302 - razor_wstr_append(buffer, slash, next - slash);
1.303 -
1.304 - if (!creating) {
1.305 - if (razor_valid_root_name(buffer->str))
1.306 - continue;
1.307 -
1.308 - r = GetFileAttributesTransactedW(buffer->str,
1.309 - GetFileExInfoStandard,
1.310 - &fa,
1.311 - atomic->transaction);
1.312 -
1.313 - if (!r) {
1.314 - err = GetLastError();
1.315 - if (err == ERROR_FILE_NOT_FOUND) {
1.316 - creating = 1;
1.317 - } else {
1.318 - razor_atomic_set_error(atomic,
1.319 - buffer->str,
1.320 - err);
1.321 - razor_wstr_destroy(buffer);
1.322 - return -1;
1.323 - }
1.324 - } else if (!(fa.dwFileAttributes&
1.325 - FILE_ATTRIBUTE_DIRECTORY)) {
1.326 - razor_atomic_set_error_str(atomic, buffer->str,
1.327 - "Not a directory");
1.328 - razor_wstr_destroy(buffer);
1.329 - return -1;
1.330 - }
1.331 - }
1.332 - if (creating) {
1.333 - if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
1.334 - atomic->transaction)) {
1.335 - razor_atomic_set_error(atomic, buffer->str,
1.336 - GetLastError());
1.337 - razor_wstr_destroy(buffer);
1.338 - return -1;
1.339 - }
1.340 -
1.341 - /* FIXME: What to do about permissions for dirs we
1.342 - * have to create but are not in the cpio archive? */
1.343 - }
1.344 - }
1.345 -
1.346 - razor_wstr_destroy(buffer);
1.347 -
1.348 - return 0;
1.349 -}
1.350 -
1.351 -RAZOR_EXPORT int
1.352 -razor_atomic_remove(struct razor_atomic *atomic, const char *path)
1.353 -{
1.354 - wchar_t *buf;
1.355 - DWORD err;
1.356 -
1.357 - if (atomic->error_str)
1.358 - return -1;
1.359 -
1.360 - buf = razor_utf8_to_utf16(path, -1);
1.361 -
1.362 - if (DeleteFileTransactedW(buf, atomic->transaction)) {
1.363 - free(buf);
1.364 - return 0;
1.365 - }
1.366 -
1.367 - err = GetLastError();
1.368 - if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
1.369 - free(buf);
1.370 - return 0;
1.371 - }
1.372 -
1.373 - if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
1.374 - atomic->transaction)) {
1.375 - if (DeleteFileTransactedW(buf, atomic->transaction)) {
1.376 - free(buf);
1.377 - return 0;
1.378 - }
1.379 - err = GetLastError();
1.380 - }
1.381 -
1.382 - if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
1.383 - GetLastError() == ERROR_DIR_NOT_EMPTY) {
1.384 - free(buf);
1.385 - return 0;
1.386 - }
1.387 -
1.388 - /*
1.389 - * It would be tempting to use:
1.390 - * MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
1.391 - * but unless we can guarantee that the system will be rebooted
1.392 - * before we (or some other application) write another file with the
1.393 - * same path, this is likely to cause more problems than it solves.
1.394 - */
1.395 -
1.396 - razor_atomic_set_error(atomic, buf, err);
1.397 - free(buf);
1.398 - return -1;
1.399 -}
1.400 -
1.401 -RAZOR_EXPORT int
1.402 -razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
1.403 - const char *newpath)
1.404 -{
1.405 - wchar_t *oldbuf, *newbuf;
1.406 - const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
1.407 -
1.408 - if (atomic->error_str)
1.409 - return -1;
1.410 -
1.411 - newbuf = razor_utf8_to_utf16(newpath, -1);
1.412 - oldbuf = razor_utf8_to_utf16(oldpath, -1);
1.413 -
1.414 - /*
1.415 - * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
1.416 - * cover every case we care about _except_ replacing an empty
1.417 - * directory with a file. Calling RemoveDirectoryTransacted() will deal
1.418 - * with this case while having no effect in all other cases.
1.419 - */
1.420 - (void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
1.421 -
1.422 - if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
1.423 - atomic->transaction))
1.424 - razor_atomic_set_error(atomic, newbuf, GetLastError());
1.425 -
1.426 - free(newbuf);
1.427 - free(oldbuf);
1.428 -
1.429 - return !!atomic->error_str;
1.430 -}
1.431 -
1.432 -RAZOR_EXPORT int
1.433 -razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
1.434 - mode_t mode)
1.435 -{
1.436 - wchar_t *buf;
1.437 - DWORD err;
1.438 - WIN32_FILE_ATTRIBUTE_DATA fa;
1.439 -
1.440 - if (atomic->error_str)
1.441 - return -1;
1.442 -
1.443 - buf = razor_utf8_to_utf16(dirname, -1);
1.444 -
1.445 - if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
1.446 - err = GetLastError();
1.447 - if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
1.448 -abort:
1.449 - razor_atomic_set_error(atomic, buf, err);
1.450 - free(buf);
1.451 - return -1;
1.452 - }
1.453 -
1.454 - if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
1.455 - &fa, atomic->transaction))
1.456 - goto abort;
1.457 -
1.458 - if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
1.459 - if (razor_atomic_remove(atomic, dirname)) {
1.460 - free(buf);
1.461 - return -1;
1.462 - }
1.463 - if (!CreateDirectoryTransactedW(NULL, buf, NULL,
1.464 - atomic->transaction)) {
1.465 - err = GetLastError();
1.466 - goto abort;
1.467 - }
1.468 - }
1.469 - }
1.470 -
1.471 - free(buf);
1.472 -
1.473 - return 0;
1.474 -}
1.475 -
1.476 -RAZOR_EXPORT int
1.477 -razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
1.478 - const char *path)
1.479 -{
1.480 - if (atomic->error_str)
1.481 - return -1;
1.482 -
1.483 - /*
1.484 - * This isn't true, but symbolic links under Windows 7
1.485 - * need to know whether the target is a directory or not
1.486 - * and we don't always know that at the time when the
1.487 - * link is created, so it's a convienent lie for now.
1.488 - */
1.489 - razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
1.490 - "on this platform");
1.491 -
1.492 - return -1;
1.493 -}
1.494 -
1.495 -RAZOR_EXPORT int
1.496 -razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
1.497 - mode_t mode)
1.498 -{
1.499 - DWORD attribs;
1.500 - struct razor_atomic_file *files;
1.501 - int i = atomic->n_files;
1.502 -
1.503 - if (atomic->error_str)
1.504 - return -1;
1.505 -
1.506 - files = realloc(atomic->files,
1.507 - (atomic->n_files+1) * sizeof(struct razor_atomic_file));
1.508 - if (!files) {
1.509 - razor_atomic_set_error_str(atomic, NULL, "Not enough memory");
1.510 - return -1;
1.511 - }
1.512 - atomic->n_files++;
1.513 - atomic->files = files;
1.514 -
1.515 - files[i].path = razor_utf8_to_utf16(filename, -1);
1.516 -
1.517 - /*
1.518 - * Passing CREATE_ALWAYS to CreateFileTransacted() will cover
1.519 - * every case we care about _except_ replacing an empty directory
1.520 - * with a file. Calling RemoveDirectoryTransacted() will deal
1.521 - * with this case while having no effect in all other cases.
1.522 - */
1.523 - (void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction);
1.524 -
1.525 - if (mode & S_IWUSR)
1.526 - attribs = FILE_ATTRIBUTE_NORMAL;
1.527 - else
1.528 - attribs = FILE_ATTRIBUTE_READONLY;
1.529 -
1.530 - files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE,
1.531 - 0, NULL, CREATE_ALWAYS, attribs,
1.532 - NULL, atomic->transaction, NULL,
1.533 - NULL);
1.534 -
1.535 - if (files[i].h == INVALID_HANDLE_VALUE) {
1.536 - razor_atomic_set_error(atomic, files[i].path, GetLastError());
1.537 - free(files[i].path);
1.538 - atomic->n_files--;
1.539 - return -1;
1.540 - }
1.541 -
1.542 - return i;
1.543 -}
1.544 -
1.545 -RAZOR_EXPORT int
1.546 -razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data,
1.547 - size_t size)
1.548 -{
1.549 - DWORD written;
1.550 -
1.551 - if (atomic->error_str)
1.552 - return -1;
1.553 -
1.554 - assert(handle < atomic->n_files);
1.555 - assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
1.556 -
1.557 - while(size) {
1.558 - if (!WriteFile(atomic->files[handle].h, data, size, &written,
1.559 - NULL)) {
1.560 - razor_atomic_set_error(atomic,
1.561 - atomic->files[handle].path,
1.562 - GetLastError());
1.563 -
1.564 - (void)CloseHandle(atomic->files[handle].h);
1.565 - free(atomic->files[handle].path);
1.566 - atomic->files[handle].path = NULL;
1.567 - atomic->files[handle].h = INVALID_HANDLE_VALUE;
1.568 -
1.569 - return -1;
1.570 - }
1.571 -
1.572 - data += written;
1.573 - size -= written;
1.574 - }
1.575 -
1.576 - return 0;
1.577 -}
1.578 -
1.579 -RAZOR_EXPORT int
1.580 -razor_atomic_sync(struct razor_atomic *atomic, int handle)
1.581 -{
1.582 - HANDLE h;
1.583 -
1.584 - if (atomic->error_str)
1.585 - return -1;
1.586 -
1.587 - assert(handle < atomic->n_files);
1.588 - assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
1.589 -
1.590 - if (!CloseHandle(atomic->files[handle].h)) {
1.591 - razor_atomic_set_error(atomic, atomic->files[handle].path,
1.592 - GetLastError());
1.593 - free(atomic->files[handle].path);
1.594 - atomic->files[handle].path = NULL;
1.595 - atomic->files[handle].h = INVALID_HANDLE_VALUE;
1.596 - return -1;
1.597 - }
1.598 -
1.599 - h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0,
1.600 - NULL, OPEN_EXISTING, 0, NULL,
1.601 - atomic->transaction, NULL, NULL);
1.602 - atomic->files[handle].h = h;
1.603 -
1.604 - if (atomic->files[handle].h == INVALID_HANDLE_VALUE) {
1.605 - razor_atomic_set_error(atomic, atomic->files[handle].path,
1.606 - GetLastError());
1.607 - free(atomic->files[handle].path);
1.608 - atomic->files[handle].path = NULL;
1.609 - return -1;
1.610 - }
1.611 -
1.612 - return !!atomic->error_str;
1.613 -}
1.614 -
1.615 -RAZOR_EXPORT int
1.616 -razor_atomic_close(struct razor_atomic *atomic, int handle)
1.617 -{
1.618 - if (atomic->error_str)
1.619 - return -1;
1.620 -
1.621 - assert(handle < atomic->n_files);
1.622 - assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
1.623 -
1.624 - if (!CloseHandle(atomic->files[handle].h))
1.625 - razor_atomic_set_error(atomic, atomic->files[handle].path,
1.626 - GetLastError());
1.627 -
1.628 - free(atomic->files[handle].path);
1.629 - atomic->files[handle].path = NULL;
1.630 - atomic->files[handle].h = INVALID_HANDLE_VALUE;
1.631 -
1.632 - while(atomic->n_files > 0 &&
1.633 - atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE)
1.634 - atomic->n_files--;
1.635 -
1.636 - return !!atomic->error_str;
1.637 -}
1.638 -
1.639 -#else /* HAVE_WINDOWS_KVM */
1.640 -
1.641 -static int
1.642 -razor_valid_root_name(const char *name)
1.643 -{
1.644 - if (allow_all_root_names) {
1.645 -#ifdef MSWIN_API
1.646 - return !strpbrk(name, "/\\");
1.647 -#else
1.648 - return !strchr(name, '/');
1.649 -#endif
1.650 - }
1.651 -
1.652 -#ifdef MSWIN_API
1.653 - return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
1.654 - name[2] == '\0';
1.655 -#else
1.656 - return name[0] == '\0';
1.657 -#endif
1.658 -}
1.659 -
1.660 -struct razor_atomic {
1.661 - char *error_path;
1.662 - char *error_str;
1.663 - char *error_msg;
1.664 -};
1.665 -
1.666 -RAZOR_EXPORT struct razor_atomic *
1.667 -razor_atomic_open(const char *description)
1.668 -{
1.669 - struct razor_atomic *atomic;
1.670 -
1.671 - atomic = zalloc(sizeof *atomic);
1.672 -
1.673 - return atomic;
1.674 -}
1.675 -
1.676 -static void
1.677 -razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path,
1.678 - const char *str)
1.679 -{
1.680 - assert(!atomic->error_str);
1.681 -
1.682 - atomic->error_path = path ? strdup(path) : NULL;
1.683 - atomic->error_str = strdup(str);
1.684 -}
1.685 -
1.686 -#ifdef MSWIN_API
1.687 -static void
1.688 -razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path,
1.689 - DWORD error)
1.690 -{
1.691 - wchar_t *buf;
1.692 -
1.693 - assert(!atomic->error_str);
1.694 -
1.695 - free(atomic->error_path);
1.696 -
1.697 - if (path)
1.698 - atomic->error_path = razor_utf16_to_utf8(path, -1);
1.699 - else
1.700 - atomic->error_path = NULL;
1.701 -
1.702 - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
1.703 - FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
1.704 - NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
1.705 - (LPWSTR)&buf, 0, NULL);
1.706 - atomic->error_str = razor_utf16_to_utf8(buf, -1);
1.707 - LocalFree(buf);
1.708 -}
1.709 -#endif
1.710 -
1.711 -RAZOR_EXPORT int
1.712 -razor_atomic_commit(struct razor_atomic *atomic)
1.713 -{
1.714 - return !!atomic->error_str;
1.715 -}
1.716 -
1.717 -RAZOR_EXPORT void
1.718 -razor_atomic_destroy(struct razor_atomic *atomic)
1.719 -{
1.720 - free(atomic->error_path);
1.721 - free(atomic->error_str);
1.722 - free(atomic->error_msg);
1.723 - free(atomic);
1.724 -}
1.725 -
1.726 -RAZOR_EXPORT int
1.727 -razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
1.728 - const char *path)
1.729 -{
1.730 - char buffer[PATH_MAX], *p;
1.731 - const char *slash, *next;
1.732 - struct stat buf;
1.733 -
1.734 - if (atomic->error_str)
1.735 - return -1;
1.736 -
1.737 - strcpy(buffer, root);
1.738 - p = buffer + strlen(buffer);
1.739 - slash = path;
1.740 - for (slash = path; *slash != '\0'; slash = next) {
1.741 -#ifdef MSWIN_API
1.742 - next = strpbrk(slash + 1, "/\\");
1.743 -#else
1.744 - next = strchr(slash + 1, '/');
1.745 -#endif
1.746 - if (next == NULL)
1.747 - break;
1.748 -
1.749 - memcpy(p, slash, next - slash);
1.750 - p += next - slash;
1.751 - *p = '\0';
1.752 -
1.753 - if (razor_valid_root_name(buffer))
1.754 - continue;
1.755 -
1.756 - if (stat(buffer, &buf) == 0) {
1.757 - if (!S_ISDIR(buf.st_mode)) {
1.758 - razor_atomic_set_error_str(atomic, buffer,
1.759 - "Not a directory");
1.760 - return -1;
1.761 - }
1.762 - } else if (mkdir(buffer, 0777) < 0) {
1.763 - razor_atomic_set_error_str(atomic, buffer,
1.764 - strerror(errno));
1.765 - return -1;
1.766 - }
1.767 - }
1.768 -
1.769 - return 0;
1.770 -}
1.771 -
1.772 -RAZOR_EXPORT int
1.773 -razor_atomic_remove(struct razor_atomic *atomic, const char *path)
1.774 -{
1.775 -#ifdef MSWIN_API
1.776 - wchar_t *buf;
1.777 - DWORD err;
1.778 -#endif
1.779 -
1.780 - if (atomic->error_str)
1.781 - return -1;
1.782 -
1.783 -#ifdef MSWIN_API
1.784 - buf = razor_utf8_to_utf16(path, -1);
1.785 -
1.786 - if (!DeleteFileW(buf)) {
1.787 - err = GetLastError();
1.788 - if (err != ERROR_FILE_NOT_FOUND &&
1.789 - err != ERROR_PATH_NOT_FOUND &&
1.790 - !(SetFileAttributesW(buf, FILE_ATTRIBUTE_NORMAL) &&
1.791 - DeleteFileW(buf)) &&
1.792 - !RemoveDirectoryW(buf) &&
1.793 - GetLastError() != ERROR_DIR_NOT_EMPTY)
1.794 - razor_atomic_set_error_mswin(atomic, buf, err);
1.795 - }
1.796 -
1.797 - free(buf);
1.798 -#else
1.799 - if (remove(path))
1.800 - razor_atomic_set_error_str(atomic, path, strerror(errno));
1.801 -#endif
1.802 -
1.803 - return !!atomic->error_str;
1.804 -}
1.805 -
1.806 -RAZOR_EXPORT int
1.807 -razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
1.808 - const char *newpath)
1.809 -{
1.810 -#ifdef MSWIN_API
1.811 - wchar_t *oldbuf, *newbuf;
1.812 - const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
1.813 -#endif
1.814 -
1.815 - if (atomic->error_str)
1.816 - return -1;
1.817 -
1.818 -#ifdef MSWIN_API
1.819 - newbuf = razor_utf8_to_utf16(newpath, -1);
1.820 - oldbuf = razor_utf8_to_utf16(oldpath, -1);
1.821 -
1.822 - /*
1.823 - * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
1.824 - * cover every case we care about _except_ replacing an empty
1.825 - * directory with a file. Calling RemoveDirectory() will deal
1.826 - * with this case while having no effect in all other cases.
1.827 - */
1.828 - (void)RemoveDirectoryW(newbuf);
1.829 -
1.830 - if (!MoveFileExW(oldbuf, newbuf, flags))
1.831 - razor_atomic_set_error_mswin(atomic, newbuf, GetLastError());
1.832 -
1.833 - free(newbuf);
1.834 - free(oldbuf);
1.835 -#else
1.836 - if (rename(oldpath, newpath))
1.837 - razor_atomic_set_error_str(atomic, newpath, strerror(errno));
1.838 -#endif
1.839 -
1.840 - return !!atomic->error_str;
1.841 -}
1.842 -
1.843 -RAZOR_EXPORT int
1.844 -razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
1.845 - mode_t mode)
1.846 -{
1.847 - if (atomic->error_str)
1.848 - return -1;
1.849 -
1.850 - if (!mkdir(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
1.851 - return 0;
1.852 -
1.853 - if (errno != EEXIST) {
1.854 - razor_atomic_set_error_str(atomic, dirname, strerror(errno));
1.855 - return -1;
1.856 - }
1.857 -
1.858 - if (chmod(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) < 0) {
1.859 - razor_atomic_set_error_str(atomic, dirname, strerror(errno));
1.860 - return -1;
1.861 - }
1.862 -
1.863 - return 0;
1.864 -}
1.865 -
1.866 -RAZOR_EXPORT int
1.867 -razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
1.868 - const char *path)
1.869 -{
1.870 - if (atomic->error_str)
1.871 - return -1;
1.872 -
1.873 -#if HAVE_SYMLINK
1.874 - if (symlink(target, path) < 0) {
1.875 - razor_atomic_set_error_str(atomic, NULL, strerror(errno));
1.876 - return -1;
1.877 - }
1.878 -#else
1.879 - razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
1.880 - "on this platform");
1.881 -#endif
1.882 -
1.883 - return 0;
1.884 -}
1.885 -
1.886 -RAZOR_EXPORT int
1.887 -razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
1.888 - mode_t mode)
1.889 -{
1.890 - int fd;
1.891 -
1.892 - if (atomic->error_str)
1.893 - return -1;
1.894 -
1.895 - atomic->error_path = strdup(filename);
1.896 - fd = open(atomic->error_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
1.897 - mode & (S_IRWXU | S_IRWXG | S_IRWXO));
1.898 -
1.899 - if (fd == -1)
1.900 - razor_atomic_set_error_str(atomic, NULL, strerror(errno));
1.901 -
1.902 - return fd;
1.903 -}
1.904 -
1.905 -RAZOR_EXPORT int
1.906 -razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data,
1.907 - size_t size)
1.908 -{
1.909 - int written;
1.910 -
1.911 - if (atomic->error_str)
1.912 - return -1;
1.913 -
1.914 - while(size) {
1.915 - written = write(fd, data, size);
1.916 - if (written < 0) {
1.917 - razor_atomic_set_error_str(atomic, NULL, strerror(errno));
1.918 -
1.919 - (void)close(fd);
1.920 -
1.921 - return -1;
1.922 - }
1.923 -
1.924 - data += written;
1.925 - size -= written;
1.926 - }
1.927 -
1.928 - return 0;
1.929 -}
1.930 -
1.931 -RAZOR_EXPORT int
1.932 -razor_atomic_sync(struct razor_atomic *atomic, int handle)
1.933 -{
1.934 - if (atomic->error_str)
1.935 - return -1;
1.936 -
1.937 - if (fsync(handle) < 0) {
1.938 - razor_atomic_set_error_str(atomic, NULL, strerror(errno));
1.939 - return -1;
1.940 - }
1.941 -
1.942 - free(atomic->error_path);
1.943 - atomic->error_path = NULL;
1.944 -
1.945 - return 0;
1.946 -}
1.947 -
1.948 -RAZOR_EXPORT int
1.949 -razor_atomic_close(struct razor_atomic *atomic, int fd)
1.950 -{
1.951 - if (atomic->error_str)
1.952 - return -1;
1.953 -
1.954 - if (close(fd) < 0) {
1.955 - razor_atomic_set_error_str(atomic, NULL, strerror(errno));
1.956 - return -1;
1.957 - }
1.958 -
1.959 - free(atomic->error_path);
1.960 - atomic->error_path = NULL;
1.961 -
1.962 - return 0;
1.963 -}
1.964 -
1.965 -#endif /* HAVE_WINDOWS_KVM */
1.966 -
1.967 RAZOR_EXPORT const char *
1.968 razor_atomic_get_error_msg(struct razor_atomic *atomic)
1.969 {
1.970 @@ -998,5 +79,131 @@
1.971 RAZOR_EXPORT int
1.972 razor_atomic_in_error_state(struct razor_atomic *atomic)
1.973 {
1.974 - return !!atomic->error_str;
1.975 + return atomic->error_str && !atomic->in_undo;
1.976 }
1.977 +
1.978 +#if !HAVE_WINDOWS_KTM
1.979 +
1.980 +/*
1.981 + * Common code with atomic-none and atomic-emulate
1.982 + */
1.983 +
1.984 +#define RAZOR_ASCII_ISALPHA(c) \
1.985 + ((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
1.986 +
1.987 +int
1.988 +razor_valid_root_name(const char *name)
1.989 +{
1.990 + if (razor_allow_all_root_names()) {
1.991 +#ifdef MSWIN_API
1.992 + return !strpbrk(name, "/\\");
1.993 +#else
1.994 + return !strchr(name, '/');
1.995 +#endif
1.996 + }
1.997 +
1.998 +#ifdef MSWIN_API
1.999 + return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
1.1000 + name[2] == '\0';
1.1001 +#else
1.1002 + return name[0] == '\0';
1.1003 +#endif
1.1004 +}
1.1005 +
1.1006 +#ifdef MSWIN_API
1.1007 +void
1.1008 +razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path,
1.1009 + DWORD error)
1.1010 +{
1.1011 + wchar_t *buf;
1.1012 +
1.1013 + assert(!atomic->error_str);
1.1014 +
1.1015 + free(atomic->error_path);
1.1016 +
1.1017 + if (path)
1.1018 + atomic->error_path = razor_utf16_to_utf8(path, -1);
1.1019 + else
1.1020 + atomic->error_path = NULL;
1.1021 +
1.1022 + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
1.1023 + FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
1.1024 + NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
1.1025 + (LPWSTR)&buf, 0, NULL);
1.1026 + atomic->error_str = razor_utf16_to_utf8(buf, -1);
1.1027 + LocalFree(buf);
1.1028 +}
1.1029 +#endif
1.1030 +
1.1031 +void
1.1032 +razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path,
1.1033 + const char *str)
1.1034 +{
1.1035 + assert(!atomic->error_str);
1.1036 +
1.1037 + atomic->error_path = path ? strdup(path) : NULL;
1.1038 + atomic->error_str = strdup(str);
1.1039 +}
1.1040 +
1.1041 +RAZOR_EXPORT int
1.1042 +razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data,
1.1043 + size_t size)
1.1044 +{
1.1045 + int written;
1.1046 +
1.1047 + if (razor_atomic_in_error_state(atomic))
1.1048 + return -1;
1.1049 +
1.1050 + while(size) {
1.1051 + written = write(fd, data, size);
1.1052 + if (written < 0) {
1.1053 + razor_atomic_set_error_str(atomic, NULL,
1.1054 + strerror(errno));
1.1055 +
1.1056 + (void)close(fd);
1.1057 +
1.1058 + return -1;
1.1059 + }
1.1060 +
1.1061 + data += written;
1.1062 + size -= written;
1.1063 + }
1.1064 +
1.1065 + return 0;
1.1066 +}
1.1067 +
1.1068 +RAZOR_EXPORT int
1.1069 +razor_atomic_sync(struct razor_atomic *atomic, int handle)
1.1070 +{
1.1071 + if (razor_atomic_in_error_state(atomic))
1.1072 + return -1;
1.1073 +
1.1074 + if (fsync(handle) < 0) {
1.1075 + razor_atomic_set_error_str(atomic, NULL, strerror(errno));
1.1076 + return -1;
1.1077 + }
1.1078 +
1.1079 + free(atomic->error_path);
1.1080 + atomic->error_path = NULL;
1.1081 +
1.1082 + return 0;
1.1083 +}
1.1084 +
1.1085 +RAZOR_EXPORT int
1.1086 +razor_atomic_close(struct razor_atomic *atomic, int fd)
1.1087 +{
1.1088 + if (razor_atomic_in_error_state(atomic))
1.1089 + return -1;
1.1090 +
1.1091 + if (close(fd) < 0) {
1.1092 + razor_atomic_set_error_str(atomic, NULL, strerror(errno));
1.1093 + return -1;
1.1094 + }
1.1095 +
1.1096 + free(atomic->error_path);
1.1097 + atomic->error_path = NULL;
1.1098 +
1.1099 + return 0;
1.1100 +}
1.1101 +
1.1102 +#endif /* !HAVE_WINDOWS_KTM */