librazor/atomic-ktm.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Aug 23 16:07:09 2014 +0100 (2014-08-23)
changeset 441 cf499fd51df7
parent 424 8cbc438cc298
child 447 0a5e583393e1
permissions -rw-r--r--
Drop drive letter from path to razor root when RAZOR_ROOT set.

If the RAZOR_ROOT environment variable was set to eg., /root then on
Microsoft Windows we were trying to use paths such as /rootC:/Programs
which is obviously wrong. Instead we should drop the drive letter
giving paths of the form /root/Programs. Note that the drive letter is
_not_ migrated to C:/root/Programs: If a root of C:/root was desired
then RAZOR_ROOT would have been set to C:/root.
     1 /*
     2  * Copyright (C) 2011, 2012, 2014  J. Ali Harlow <ali@juiblex.co.uk>
     3  *
     4  * This program is free software; you can redistribute it and/or modify
     5  * it under the terms of the GNU General Public License as published by
     6  * the Free Software Foundation; either version 2 of the License, or
     7  * (at your option) any later version.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License along
    15  * with this program; if not, write to the Free Software Foundation, Inc.,
    16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    17  */
    18 
    19 #include "config.h"
    20 
    21 #if HAVE_WINDOWS_KTM
    22 
    23 #include <stdlib.h>
    24 #include <windows.h>
    25 #include <stdio.h>
    26 #include <limits.h>
    27 #include <errno.h>
    28 #include <unistd.h>
    29 #include <fcntl.h>
    30 #include <sys/stat.h>
    31 #include <string.h>
    32 #include <assert.h>
    33 #include <wchar.h>
    34 #include <ktmw32.h>
    35 
    36 #include "razor.h"
    37 #include "razor-internal.h"
    38 
    39 static int
    40 razor_valid_root_name2(const wchar_t *name)
    41 {
    42 	if (razor_allow_all_root_names())
    43 		return !wcschr(name, '/');
    44 
    45 	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
    46 	       name[2] == '\0';
    47 }
    48 
    49 struct razor_wstr {
    50 	wchar_t *str;
    51 	int len, allocated;
    52 };
    53 
    54 static struct razor_wstr *
    55 razor_wstr_create(const char *init, int len)
    56 {
    57 	int n;
    58 	struct razor_wstr *wstr;
    59 
    60 	wstr = malloc(sizeof(struct razor_wstr));
    61 
    62 	n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
    63 	if (len >= 0 && init[len])
    64 		wstr->len = n++;
    65 	else
    66 		wstr->len = n - 1;
    67 
    68 	wstr->allocated = n * 2;
    69 	wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
    70 	if (!wstr->str) {
    71 		free(wstr);
    72 		return NULL;
    73 	}
    74 
    75 	(void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
    76 	if (len >= 0 && init[len])
    77 		wstr->str[wstr->len] = 0;
    78 
    79 	return wstr;
    80 }
    81 
    82 static int
    83 razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
    84 {
    85 	int n, allocated;
    86 	wchar_t *str;
    87 
    88 	n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
    89 	if (len < 0 || !s[len])
    90 		n--;
    91 
    92 	if (wstr->allocated <= wstr->len + n) {
    93 		allocated = (wstr->len + n + 1) * 2;
    94 		str = realloc(wstr->str, allocated * sizeof(wchar_t));
    95 		if (!str)
    96 			return -1;
    97 		wstr->allocated = allocated;
    98 		wstr->str = str;
    99 	}
   100 
   101 	(void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
   102 	wstr->len += n;
   103 	wstr->str[wstr->len] = 0;
   104 
   105 	return 0;
   106 }
   107 
   108 static void
   109 razor_wstr_destroy(struct razor_wstr *wstr)
   110 {
   111 	free(wstr->str);
   112 	free(wstr);
   113 }
   114 
   115 RAZOR_EXPORT struct razor_atomic *
   116 razor_atomic_open(const char *description)
   117 {
   118 	wchar_t *buf;
   119 	struct razor_atomic *atomic;
   120 
   121 	atomic = zalloc(sizeof *atomic);
   122 	buf = razor_utf8_to_utf16(description, -1);
   123 	atomic->transaction = CreateTransaction(NULL, 0,
   124 						TRANSACTION_DO_NOT_PROMOTE,
   125 						0, 0, 0, buf);
   126 	free(buf);
   127 
   128 	return atomic;
   129 }
   130 
   131 RAZOR_EXPORT int
   132 razor_atomic_commit(struct razor_atomic *atomic)
   133 {
   134 	int retval;
   135 
   136 	if (razor_atomic_in_error_state(atomic))
   137 		return -1;
   138 
   139 	retval = !CommitTransaction(atomic->transaction);
   140 
   141 	if (retval) {
   142 		razor_set_error_mswin(&atomic->error, NULL, GetLastError());
   143 		RollbackTransaction(atomic->transaction);
   144 	}
   145 
   146 	CloseHandle(atomic->transaction);
   147 	atomic->transaction = INVALID_HANDLE_VALUE;
   148 
   149 	return retval;
   150 }
   151 
   152 RAZOR_EXPORT void
   153 razor_atomic_destroy(struct razor_atomic *atomic)
   154 {
   155 	int i;
   156 
   157 	for(i = 0; i < atomic->n_files; i++) {
   158 		if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
   159 			CloseHandle(atomic->files[i].h);
   160 			free(atomic->files[i].path);
   161 		}
   162 	}
   163 	free(atomic->files);
   164 	if (atomic->transaction != INVALID_HANDLE_VALUE) {
   165 		RollbackTransaction(atomic->transaction);
   166 		CloseHandle(atomic->transaction);
   167 	}
   168 	if (atomic->error)
   169 		razor_error_free(atomic->error);
   170 	free(atomic);
   171 }
   172 
   173 RAZOR_EXPORT int
   174 razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   175 		       const char *path)
   176 {
   177 	struct razor_wstr *buffer;
   178 	const char *slash, *next;
   179 	WIN32_FILE_ATTRIBUTE_DATA fa;
   180 	DWORD err;
   181 	int r, creating = 0;
   182 
   183 	if (razor_atomic_in_error_state(atomic))
   184 		return -1;
   185 
   186 	buffer = razor_wstr_create(root, -1);
   187 	slash = buffer->len ? SKIP_DRIVE_LETTER(path) : path;
   188 
   189 	for (; *slash != '\0'; slash = next) {
   190 		next = strpbrk(slash + 1, "/\\");
   191 		if (next == NULL)
   192 			break;
   193 
   194 		razor_wstr_append(buffer, slash, next - slash);
   195 
   196 		if (!creating) {
   197 			if (razor_valid_root_name2(buffer->str))
   198 				continue;
   199 
   200 			r = GetFileAttributesTransactedW(buffer->str,
   201 							 GetFileExInfoStandard,
   202 							 &fa,
   203 							 atomic->transaction);
   204 
   205 			if (!r) {
   206 				err = GetLastError();
   207 				if (err == ERROR_FILE_NOT_FOUND) {
   208 					creating = 1;
   209 				} else {
   210 					razor_set_error_mswin(&atomic->error,
   211 							      buffer->str, err);
   212 					razor_wstr_destroy(buffer);
   213 					return -1;
   214 				}
   215 			} else if (!(fa.dwFileAttributes&
   216 				     FILE_ATTRIBUTE_DIRECTORY)) {
   217 				razor_set_error2(&atomic->error, buffer->str,
   218 						 "Not a directory");
   219 				razor_wstr_destroy(buffer);
   220 				return -1;
   221 			}
   222 		}
   223 		if (creating) {
   224 			if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
   225 							atomic->transaction)) {
   226 				razor_set_error_mswin(&atomic->error,
   227 						      buffer->str,
   228 						      GetLastError());
   229 				razor_wstr_destroy(buffer);
   230 				return -1;
   231 			}
   232 
   233 			/* FIXME: What to do about permissions for dirs we
   234 			 * have to create but are not in the cpio archive? */
   235 		}
   236 	}
   237 
   238 	razor_wstr_destroy(buffer);
   239 
   240 	return 0;
   241 }
   242 
   243 RAZOR_EXPORT int
   244 razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   245 {
   246 	wchar_t *buf;
   247 	DWORD err;
   248 
   249 	if (razor_atomic_in_error_state(atomic))
   250 		return -1;
   251 
   252 	buf = razor_utf8_to_utf16(path, -1);
   253 
   254 	if (DeleteFileTransactedW(buf, atomic->transaction)) {
   255 		free(buf);
   256 		return 0;
   257 	}
   258 
   259 	err = GetLastError();
   260 	if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
   261 		free(buf);
   262 		return 0;
   263 	}
   264 
   265 	if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
   266 					 atomic->transaction)) {
   267 		if (DeleteFileTransactedW(buf, atomic->transaction)) {
   268 			free(buf);
   269 			return 0;
   270 		}
   271 		err = GetLastError();
   272 	}
   273 
   274 	if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
   275 	    GetLastError() == ERROR_DIR_NOT_EMPTY) {
   276 		free(buf);
   277 		return 0;
   278 	}
   279 
   280 	/*
   281 	 * It would be tempting to use:
   282 	 * 	MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
   283 	 * but unless we can guarantee that the system will be rebooted
   284 	 * before we (or some other application) write another file with the
   285 	 * same path, this is likely to cause more problems than it solves.
   286 	 */
   287 
   288 	razor_set_error_mswin(&atomic->error, buf, err);
   289 	free(buf);
   290 	return -1;
   291 }
   292 
   293 RAZOR_EXPORT int
   294 razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   295 			 const char *newpath)
   296 {
   297 	wchar_t *oldbuf, *newbuf;
   298 	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   299 
   300 	if (razor_atomic_in_error_state(atomic))
   301 		return -1;
   302 
   303 	newbuf = razor_utf8_to_utf16(newpath, -1);
   304 	oldbuf = razor_utf8_to_utf16(oldpath, -1);
   305 
   306 	/*
   307 	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
   308 	 * cover every case we care about _except_ replacing an empty
   309 	 * directory with a file. Calling RemoveDirectoryTransacted() will deal
   310 	 * with this case while having no effect in all other cases.
   311 	 */
   312 	(void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
   313 
   314 	if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
   315 			         atomic->transaction))
   316 		razor_set_error_mswin(&atomic->error, newbuf, GetLastError());
   317 
   318 	free(newbuf);
   319 	free(oldbuf);
   320 
   321 	return razor_atomic_in_error_state(atomic);
   322 }
   323 
   324 RAZOR_EXPORT int
   325 razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   326 			mode_t mode)
   327 {
   328 	wchar_t *buf;
   329 	DWORD err;
   330 	WIN32_FILE_ATTRIBUTE_DATA fa;
   331 
   332 	if (razor_atomic_in_error_state(atomic))
   333 		return -1;
   334 
   335 	buf = razor_utf8_to_utf16(dirname, -1);
   336 
   337 	if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
   338 		err = GetLastError();
   339 		if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
   340 abort:
   341 			razor_set_error_mswin(&atomic->error, buf, err);
   342 			free(buf);
   343 			return -1;
   344 		}
   345 
   346 		if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
   347 						  &fa, atomic->transaction))
   348 			goto abort;
   349 
   350 		if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
   351 			if (razor_atomic_remove(atomic, dirname)) {
   352 				free(buf);
   353 				return -1;
   354 			}
   355 			if (!CreateDirectoryTransactedW(NULL, buf, NULL,
   356 							atomic->transaction)) {
   357 				err = GetLastError();
   358 				goto abort;
   359 			}
   360 		}
   361 	}
   362 
   363 	free(buf);
   364 
   365 	return 0;
   366 }
   367 
   368 RAZOR_EXPORT int
   369 razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   370 			    const char *path)
   371 {
   372 	if (razor_atomic_in_error_state(atomic))
   373 		return -1;
   374 
   375 	/*
   376 	 * This isn't true, but symbolic links under Windows 7
   377 	 * need to know whether the target is a directory or not
   378 	 * and we don't always know that at the time when the
   379 	 * link is created, so it's a convienent lie for now.
   380 	 */
   381 	razor_set_error(&atomic->error, NULL,
   382 			"Symbolic links not supported on this platform");
   383 
   384 	return -1;
   385 }
   386 
   387 RAZOR_EXPORT int
   388 razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   389 			 mode_t mode)
   390 {
   391 	DWORD attribs;
   392 	struct razor_atomic_file *files;
   393 	int i = atomic->n_files;
   394 
   395 	if (razor_atomic_in_error_state(atomic))
   396 		return -1;
   397 
   398 	files = realloc(atomic->files,
   399 			(atomic->n_files+1) * sizeof(struct razor_atomic_file));
   400 	if (!files) {
   401 		razor_set_error(&atomic->error, NULL, "Not enough memory");
   402 		return -1;
   403 	}
   404 	atomic->n_files++;
   405 	atomic->files = files;
   406 
   407 	files[i].path = razor_utf8_to_utf16(filename, -1);
   408 
   409 	/*
   410 	 * Passing CREATE_ALWAYS to CreateFileTransacted() will cover
   411 	 * every case we care about _except_ replacing an empty directory
   412 	 * with a file. Calling RemoveDirectoryTransacted() will deal
   413 	 * with this case while having no effect in all other cases.
   414 	 */
   415 	(void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction);
   416 
   417 	if (mode & S_IWUSR)
   418 		attribs = FILE_ATTRIBUTE_NORMAL;
   419 	else
   420 		attribs = FILE_ATTRIBUTE_READONLY;
   421 
   422 	files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE,
   423 					   0, NULL, CREATE_ALWAYS, attribs,
   424 					   NULL, atomic->transaction, NULL,
   425 					   NULL);
   426 
   427 	if (files[i].h == INVALID_HANDLE_VALUE) {
   428 		razor_set_error_mswin(&atomic->error, files[i].path,
   429 				      GetLastError());
   430 		free(files[i].path);
   431 		atomic->n_files--;
   432 		return -1;
   433 	}
   434 
   435 	return i;
   436 }
   437 
   438 RAZOR_EXPORT int
   439 razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data,
   440 		   size_t size)
   441 {
   442 	DWORD written;
   443 
   444 	if (razor_atomic_in_error_state(atomic))
   445 		return -1;
   446 
   447 	assert(handle < atomic->n_files);
   448 	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   449 
   450 	while(size) {
   451 		if (!WriteFile(atomic->files[handle].h, data, size, &written,
   452 			       NULL)) {
   453 			razor_set_error_mswin(&atomic->error,
   454 					      atomic->files[handle].path,
   455 					      GetLastError());
   456 
   457 			(void)CloseHandle(atomic->files[handle].h);
   458 			free(atomic->files[handle].path);
   459 			atomic->files[handle].path = NULL;
   460 			atomic->files[handle].h = INVALID_HANDLE_VALUE;
   461 
   462 			return -1;
   463 		}
   464 
   465 		data += written;
   466 		size -= written;
   467 	}
   468 
   469 	return 0;
   470 }
   471 
   472 RAZOR_EXPORT int
   473 razor_atomic_sync(struct razor_atomic *atomic, int handle)
   474 {
   475 	HANDLE h;
   476 
   477 	if (razor_atomic_in_error_state(atomic))
   478 		return -1;
   479 
   480 	assert(handle < atomic->n_files);
   481 	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   482 
   483 	if (!CloseHandle(atomic->files[handle].h)) {
   484 		razor_set_error_mswin(&atomic->error,
   485 				      atomic->files[handle].path,
   486 				      GetLastError());
   487 		free(atomic->files[handle].path);
   488 		atomic->files[handle].path = NULL;
   489 		atomic->files[handle].h = INVALID_HANDLE_VALUE;
   490 		return -1;
   491 	}
   492 
   493 	h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0,
   494 				  NULL, OPEN_EXISTING, 0, NULL,
   495 				  atomic->transaction, NULL, NULL);
   496 	atomic->files[handle].h = h;
   497 
   498 	if (atomic->files[handle].h == INVALID_HANDLE_VALUE) {
   499 		razor_set_error_mswin(&atomic->error,
   500 				      atomic->files[handle].path,
   501 				      GetLastError());
   502 		free(atomic->files[handle].path);
   503 		atomic->files[handle].path = NULL;
   504 		return -1;
   505 	}
   506 
   507 	return razor_atomic_in_error_state(atomic);
   508 }
   509 
   510 RAZOR_EXPORT int
   511 razor_atomic_close(struct razor_atomic *atomic, int handle)
   512 {
   513 	if (razor_atomic_in_error_state(atomic))
   514 		return -1;
   515 
   516 	assert(handle < atomic->n_files);
   517 	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   518 
   519 	if (!CloseHandle(atomic->files[handle].h))
   520 		razor_set_error_mswin(&atomic->error,
   521 				      atomic->files[handle].path,
   522 				      GetLastError());
   523 
   524 	free(atomic->files[handle].path);
   525 	atomic->files[handle].path = NULL;
   526 	atomic->files[handle].h = INVALID_HANDLE_VALUE;
   527 
   528 	while(atomic->n_files > 0 &&
   529 	      atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE)
   530 		atomic->n_files--;
   531 
   532 	return razor_atomic_in_error_state(atomic);
   533 }
   534 
   535 #endif		/* HAVE_WINDOWS_KTM */