1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/librazor/atomic-ktm.c Sat Feb 11 09:30:23 2012 +0000
1.3 @@ -0,0 +1,574 @@
1.4 +/*
1.5 + * Copyright (C) 2011-2012 J. Ali Harlow <ali@juiblex.co.uk>
1.6 + *
1.7 + * This program is free software; you can redistribute it and/or modify
1.8 + * it under the terms of the GNU General Public License as published by
1.9 + * the Free Software Foundation; either version 2 of the License, or
1.10 + * (at your option) any later version.
1.11 + *
1.12 + * This program is distributed in the hope that it will be useful,
1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.15 + * GNU General Public License for more details.
1.16 + *
1.17 + * You should have received a copy of the GNU General Public License along
1.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
1.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.20 + */
1.21 +
1.22 +#include "config.h"
1.23 +
1.24 +#if HAVE_WINDOWS_KTM
1.25 +
1.26 +#include <stdlib.h>
1.27 +#include <windows.h>
1.28 +#include <stdio.h>
1.29 +#include <limits.h>
1.30 +#include <errno.h>
1.31 +#include <unistd.h>
1.32 +#include <fcntl.h>
1.33 +#include <sys/stat.h>
1.34 +#include <string.h>
1.35 +#include <assert.h>
1.36 +#include <wchar.h>
1.37 +#include <ktmw32.h>
1.38 +
1.39 +#include "razor.h"
1.40 +#include "razor-internal.h"
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
1.46 +razor_valid_root_name2(const wchar_t *name)
1.47 +{
1.48 + if (razor_allow_all_root_names())
1.49 + return !wcschr(name, '/');
1.50 +
1.51 + return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
1.52 + name[2] == '\0';
1.53 +}
1.54 +
1.55 +struct razor_wstr {
1.56 + wchar_t *str;
1.57 + int len, allocated;
1.58 +};
1.59 +
1.60 +static struct razor_wstr *
1.61 +razor_wstr_create(const char *init, int len)
1.62 +{
1.63 + int n;
1.64 + struct razor_wstr *wstr;
1.65 +
1.66 + wstr = malloc(sizeof(struct razor_wstr));
1.67 +
1.68 + n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
1.69 + if (len >= 0 && init[len])
1.70 + wstr->len = n++;
1.71 + else
1.72 + wstr->len = n - 1;
1.73 +
1.74 + wstr->allocated = n * 2;
1.75 + wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
1.76 + if (!wstr->str) {
1.77 + free(wstr);
1.78 + return NULL;
1.79 + }
1.80 +
1.81 + (void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
1.82 + if (len >= 0 && init[len])
1.83 + wstr->str[wstr->len] = 0;
1.84 +
1.85 + return wstr;
1.86 +}
1.87 +
1.88 +static int
1.89 +razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
1.90 +{
1.91 + int n, allocated;
1.92 + wchar_t *str;
1.93 +
1.94 + n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
1.95 + if (len < 0 || !s[len])
1.96 + n--;
1.97 +
1.98 + if (wstr->allocated <= wstr->len + n) {
1.99 + allocated = (wstr->len + n + 1) * 2;
1.100 + str = realloc(wstr->str, allocated * sizeof(wchar_t));
1.101 + if (!str)
1.102 + return -1;
1.103 + wstr->allocated = allocated;
1.104 + wstr->str = str;
1.105 + }
1.106 +
1.107 + (void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
1.108 + wstr->len += n;
1.109 + wstr->str[wstr->len] = 0;
1.110 +
1.111 + return 0;
1.112 +}
1.113 +
1.114 +static void
1.115 +razor_wstr_destroy(struct razor_wstr *wstr)
1.116 +{
1.117 + free(wstr->str);
1.118 + free(wstr);
1.119 +}
1.120 +
1.121 +RAZOR_EXPORT struct razor_atomic *
1.122 +razor_atomic_open(const char *description)
1.123 +{
1.124 + wchar_t *buf;
1.125 + struct razor_atomic *atomic;
1.126 +
1.127 + atomic = zalloc(sizeof *atomic);
1.128 + buf = razor_utf8_to_utf16(description, -1);
1.129 + atomic->transaction = CreateTransaction(NULL, 0,
1.130 + TRANSACTION_DO_NOT_PROMOTE,
1.131 + 0, 0, 0, buf);
1.132 + free(buf);
1.133 +
1.134 + return atomic;
1.135 +}
1.136 +
1.137 +void
1.138 +razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path,
1.139 + const char *str)
1.140 +{
1.141 + assert(!atomic->error_str);
1.142 +
1.143 + free(atomic->error_path);
1.144 +
1.145 + if (path)
1.146 + atomic->error_path = razor_utf16_to_utf8(path, -1);
1.147 + else
1.148 + atomic->error_path = NULL;
1.149 +
1.150 + atomic->error_str = strdup(str);
1.151 +}
1.152 +
1.153 +static void
1.154 +razor_atomic_set_error(struct razor_atomic *atomic, const wchar_t *path,
1.155 + DWORD error)
1.156 +{
1.157 + wchar_t *buf;
1.158 +
1.159 + assert(!atomic->error_str);
1.160 +
1.161 + free(atomic->error_path);
1.162 +
1.163 + if (path)
1.164 + atomic->error_path = razor_utf16_to_utf8(path, -1);
1.165 + else
1.166 + atomic->error_path = NULL;
1.167 +
1.168 + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
1.169 + FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
1.170 + NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
1.171 + (LPWSTR)&buf, 0, NULL);
1.172 + atomic->error_str = razor_utf16_to_utf8(buf, -1);
1.173 + LocalFree(buf);
1.174 +}
1.175 +
1.176 +RAZOR_EXPORT int
1.177 +razor_atomic_commit(struct razor_atomic *atomic)
1.178 +{
1.179 + int retval;
1.180 +
1.181 + if (razor_atomic_in_error_state(atomic))
1.182 + return -1;
1.183 +
1.184 + retval = !CommitTransaction(atomic->transaction);
1.185 +
1.186 + if (retval) {
1.187 + razor_atomic_set_error(atomic, NULL, GetLastError());
1.188 + RollbackTransaction(atomic->transaction);
1.189 + }
1.190 +
1.191 + CloseHandle(atomic->transaction);
1.192 + atomic->transaction = INVALID_HANDLE_VALUE;
1.193 +
1.194 + return retval;
1.195 +}
1.196 +
1.197 +RAZOR_EXPORT void
1.198 +razor_atomic_destroy(struct razor_atomic *atomic)
1.199 +{
1.200 + int i;
1.201 +
1.202 + for(i = 0; i < atomic->n_files; i++) {
1.203 + if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
1.204 + CloseHandle(atomic->files[i].h);
1.205 + free(atomic->files[i].path);
1.206 + }
1.207 + }
1.208 + free(atomic->files);
1.209 + if (atomic->transaction != INVALID_HANDLE_VALUE) {
1.210 + RollbackTransaction(atomic->transaction);
1.211 + CloseHandle(atomic->transaction);
1.212 + }
1.213 + free(atomic->error_path);
1.214 + free(atomic->error_str);
1.215 + free(atomic->error_msg);
1.216 + free(atomic);
1.217 +}
1.218 +
1.219 +RAZOR_EXPORT int
1.220 +razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
1.221 + const char *path)
1.222 +{
1.223 + struct razor_wstr *buffer;
1.224 + const char *slash, *next;
1.225 + WIN32_FILE_ATTRIBUTE_DATA fa;
1.226 + DWORD err;
1.227 + int r, creating = 0;
1.228 +
1.229 + if (razor_atomic_in_error_state(atomic))
1.230 + return -1;
1.231 +
1.232 + buffer = razor_wstr_create(root, -1);
1.233 + slash = path;
1.234 +
1.235 + for (; *slash != '\0'; slash = next) {
1.236 + next = strpbrk(slash + 1, "/\\");
1.237 + if (next == NULL)
1.238 + break;
1.239 +
1.240 + razor_wstr_append(buffer, slash, next - slash);
1.241 +
1.242 + if (!creating) {
1.243 + if (razor_valid_root_name2(buffer->str))
1.244 + continue;
1.245 +
1.246 + r = GetFileAttributesTransactedW(buffer->str,
1.247 + GetFileExInfoStandard,
1.248 + &fa,
1.249 + atomic->transaction);
1.250 +
1.251 + if (!r) {
1.252 + err = GetLastError();
1.253 + if (err == ERROR_FILE_NOT_FOUND) {
1.254 + creating = 1;
1.255 + } else {
1.256 + razor_atomic_set_error(atomic,
1.257 + buffer->str,
1.258 + err);
1.259 + razor_wstr_destroy(buffer);
1.260 + return -1;
1.261 + }
1.262 + } else if (!(fa.dwFileAttributes&
1.263 + FILE_ATTRIBUTE_DIRECTORY)) {
1.264 + razor_atomic_set_error_str(atomic, buffer->str,
1.265 + "Not a directory");
1.266 + razor_wstr_destroy(buffer);
1.267 + return -1;
1.268 + }
1.269 + }
1.270 + if (creating) {
1.271 + if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
1.272 + atomic->transaction)) {
1.273 + razor_atomic_set_error(atomic, buffer->str,
1.274 + GetLastError());
1.275 + razor_wstr_destroy(buffer);
1.276 + return -1;
1.277 + }
1.278 +
1.279 + /* FIXME: What to do about permissions for dirs we
1.280 + * have to create but are not in the cpio archive? */
1.281 + }
1.282 + }
1.283 +
1.284 + razor_wstr_destroy(buffer);
1.285 +
1.286 + return 0;
1.287 +}
1.288 +
1.289 +RAZOR_EXPORT int
1.290 +razor_atomic_remove(struct razor_atomic *atomic, const char *path)
1.291 +{
1.292 + wchar_t *buf;
1.293 + DWORD err;
1.294 +
1.295 + if (razor_atomic_in_error_state(atomic))
1.296 + return -1;
1.297 +
1.298 + buf = razor_utf8_to_utf16(path, -1);
1.299 +
1.300 + if (DeleteFileTransactedW(buf, atomic->transaction)) {
1.301 + free(buf);
1.302 + return 0;
1.303 + }
1.304 +
1.305 + err = GetLastError();
1.306 + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
1.307 + free(buf);
1.308 + return 0;
1.309 + }
1.310 +
1.311 + if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
1.312 + atomic->transaction)) {
1.313 + if (DeleteFileTransactedW(buf, atomic->transaction)) {
1.314 + free(buf);
1.315 + return 0;
1.316 + }
1.317 + err = GetLastError();
1.318 + }
1.319 +
1.320 + if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
1.321 + GetLastError() == ERROR_DIR_NOT_EMPTY) {
1.322 + free(buf);
1.323 + return 0;
1.324 + }
1.325 +
1.326 + /*
1.327 + * It would be tempting to use:
1.328 + * MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
1.329 + * but unless we can guarantee that the system will be rebooted
1.330 + * before we (or some other application) write another file with the
1.331 + * same path, this is likely to cause more problems than it solves.
1.332 + */
1.333 +
1.334 + razor_atomic_set_error(atomic, buf, err);
1.335 + free(buf);
1.336 + return -1;
1.337 +}
1.338 +
1.339 +RAZOR_EXPORT int
1.340 +razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
1.341 + const char *newpath)
1.342 +{
1.343 + wchar_t *oldbuf, *newbuf;
1.344 + const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
1.345 +
1.346 + if (razor_atomic_in_error_state(atomic))
1.347 + return -1;
1.348 +
1.349 + newbuf = razor_utf8_to_utf16(newpath, -1);
1.350 + oldbuf = razor_utf8_to_utf16(oldpath, -1);
1.351 +
1.352 + /*
1.353 + * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
1.354 + * cover every case we care about _except_ replacing an empty
1.355 + * directory with a file. Calling RemoveDirectoryTransacted() will deal
1.356 + * with this case while having no effect in all other cases.
1.357 + */
1.358 + (void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
1.359 +
1.360 + if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
1.361 + atomic->transaction))
1.362 + razor_atomic_set_error(atomic, newbuf, GetLastError());
1.363 +
1.364 + free(newbuf);
1.365 + free(oldbuf);
1.366 +
1.367 + return !!atomic->error_str;
1.368 +}
1.369 +
1.370 +RAZOR_EXPORT int
1.371 +razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
1.372 + mode_t mode)
1.373 +{
1.374 + wchar_t *buf;
1.375 + DWORD err;
1.376 + WIN32_FILE_ATTRIBUTE_DATA fa;
1.377 +
1.378 + if (razor_atomic_in_error_state(atomic))
1.379 + return -1;
1.380 +
1.381 + buf = razor_utf8_to_utf16(dirname, -1);
1.382 +
1.383 + if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
1.384 + err = GetLastError();
1.385 + if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
1.386 +abort:
1.387 + razor_atomic_set_error(atomic, buf, err);
1.388 + free(buf);
1.389 + return -1;
1.390 + }
1.391 +
1.392 + if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
1.393 + &fa, atomic->transaction))
1.394 + goto abort;
1.395 +
1.396 + if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
1.397 + if (razor_atomic_remove(atomic, dirname)) {
1.398 + free(buf);
1.399 + return -1;
1.400 + }
1.401 + if (!CreateDirectoryTransactedW(NULL, buf, NULL,
1.402 + atomic->transaction)) {
1.403 + err = GetLastError();
1.404 + goto abort;
1.405 + }
1.406 + }
1.407 + }
1.408 +
1.409 + free(buf);
1.410 +
1.411 + return 0;
1.412 +}
1.413 +
1.414 +RAZOR_EXPORT int
1.415 +razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
1.416 + const char *path)
1.417 +{
1.418 + if (razor_atomic_in_error_state(atomic))
1.419 + return -1;
1.420 +
1.421 + /*
1.422 + * This isn't true, but symbolic links under Windows 7
1.423 + * need to know whether the target is a directory or not
1.424 + * and we don't always know that at the time when the
1.425 + * link is created, so it's a convienent lie for now.
1.426 + */
1.427 + razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
1.428 + "on this platform");
1.429 +
1.430 + return -1;
1.431 +}
1.432 +
1.433 +RAZOR_EXPORT int
1.434 +razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
1.435 + mode_t mode)
1.436 +{
1.437 + DWORD attribs;
1.438 + struct razor_atomic_file *files;
1.439 + int i = atomic->n_files;
1.440 +
1.441 + if (razor_atomic_in_error_state(atomic))
1.442 + return -1;
1.443 +
1.444 + files = realloc(atomic->files,
1.445 + (atomic->n_files+1) * sizeof(struct razor_atomic_file));
1.446 + if (!files) {
1.447 + razor_atomic_set_error_str(atomic, NULL, "Not enough memory");
1.448 + return -1;
1.449 + }
1.450 + atomic->n_files++;
1.451 + atomic->files = files;
1.452 +
1.453 + files[i].path = razor_utf8_to_utf16(filename, -1);
1.454 +
1.455 + /*
1.456 + * Passing CREATE_ALWAYS to CreateFileTransacted() will cover
1.457 + * every case we care about _except_ replacing an empty directory
1.458 + * with a file. Calling RemoveDirectoryTransacted() will deal
1.459 + * with this case while having no effect in all other cases.
1.460 + */
1.461 + (void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction);
1.462 +
1.463 + if (mode & S_IWUSR)
1.464 + attribs = FILE_ATTRIBUTE_NORMAL;
1.465 + else
1.466 + attribs = FILE_ATTRIBUTE_READONLY;
1.467 +
1.468 + files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE,
1.469 + 0, NULL, CREATE_ALWAYS, attribs,
1.470 + NULL, atomic->transaction, NULL,
1.471 + NULL);
1.472 +
1.473 + if (files[i].h == INVALID_HANDLE_VALUE) {
1.474 + razor_atomic_set_error(atomic, files[i].path, GetLastError());
1.475 + free(files[i].path);
1.476 + atomic->n_files--;
1.477 + return -1;
1.478 + }
1.479 +
1.480 + return i;
1.481 +}
1.482 +
1.483 +RAZOR_EXPORT int
1.484 +razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data,
1.485 + size_t size)
1.486 +{
1.487 + DWORD written;
1.488 +
1.489 + if (razor_atomic_in_error_state(atomic))
1.490 + return -1;
1.491 +
1.492 + assert(handle < atomic->n_files);
1.493 + assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
1.494 +
1.495 + while(size) {
1.496 + if (!WriteFile(atomic->files[handle].h, data, size, &written,
1.497 + NULL)) {
1.498 + razor_atomic_set_error(atomic,
1.499 + atomic->files[handle].path,
1.500 + GetLastError());
1.501 +
1.502 + (void)CloseHandle(atomic->files[handle].h);
1.503 + free(atomic->files[handle].path);
1.504 + atomic->files[handle].path = NULL;
1.505 + atomic->files[handle].h = INVALID_HANDLE_VALUE;
1.506 +
1.507 + return -1;
1.508 + }
1.509 +
1.510 + data += written;
1.511 + size -= written;
1.512 + }
1.513 +
1.514 + return 0;
1.515 +}
1.516 +
1.517 +RAZOR_EXPORT int
1.518 +razor_atomic_sync(struct razor_atomic *atomic, int handle)
1.519 +{
1.520 + HANDLE h;
1.521 +
1.522 + if (razor_atomic_in_error_state(atomic))
1.523 + return -1;
1.524 +
1.525 + assert(handle < atomic->n_files);
1.526 + assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
1.527 +
1.528 + if (!CloseHandle(atomic->files[handle].h)) {
1.529 + razor_atomic_set_error(atomic, atomic->files[handle].path,
1.530 + GetLastError());
1.531 + free(atomic->files[handle].path);
1.532 + atomic->files[handle].path = NULL;
1.533 + atomic->files[handle].h = INVALID_HANDLE_VALUE;
1.534 + return -1;
1.535 + }
1.536 +
1.537 + h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0,
1.538 + NULL, OPEN_EXISTING, 0, NULL,
1.539 + atomic->transaction, NULL, NULL);
1.540 + atomic->files[handle].h = h;
1.541 +
1.542 + if (atomic->files[handle].h == INVALID_HANDLE_VALUE) {
1.543 + razor_atomic_set_error(atomic, atomic->files[handle].path,
1.544 + GetLastError());
1.545 + free(atomic->files[handle].path);
1.546 + atomic->files[handle].path = NULL;
1.547 + return -1;
1.548 + }
1.549 +
1.550 + return !!atomic->error_str;
1.551 +}
1.552 +
1.553 +RAZOR_EXPORT int
1.554 +razor_atomic_close(struct razor_atomic *atomic, int handle)
1.555 +{
1.556 + if (razor_atomic_in_error_state(atomic))
1.557 + return -1;
1.558 +
1.559 + assert(handle < atomic->n_files);
1.560 + assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
1.561 +
1.562 + if (!CloseHandle(atomic->files[handle].h))
1.563 + razor_atomic_set_error(atomic, atomic->files[handle].path,
1.564 + GetLastError());
1.565 +
1.566 + free(atomic->files[handle].path);
1.567 + atomic->files[handle].path = NULL;
1.568 + atomic->files[handle].h = INVALID_HANDLE_VALUE;
1.569 +
1.570 + while(atomic->n_files > 0 &&
1.571 + atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE)
1.572 + atomic->n_files--;
1.573 +
1.574 + return !!atomic->error_str;
1.575 +}
1.576 +
1.577 +#endif /* HAVE_WINDOWS_KTM */