librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Aug 23 11:13:48 2014 +0100 (2014-08-23)
changeset 440 48204dea0b9f
parent 423 6112bcc5d1cf
child 444 a2908416b7cc
permissions -rw-r--r--
Remove INTLLIBS from librazor_la_LIBADD.

This partially reverts 611c84a3f4b4538a65d186050608c17adbf17770.
It's not clear what motivated the initial inclusion of INTLLIBS
here since the net effect is only seen in librazor.la and not
in razor.pc and librazor.la is not normally packaged. Certainly
neither the static nor the dynamic versions of librazor currently
use libintl. At best this would cause the linker to search a
static libintl for undefined symbols without finding any; at worse
it causes a static build of plover using librazor.la to fail if
no static version of libintl is installed.
     1 /*
     2  * Copyright (C) 2012  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 ENABLE_ATOMIC && !HAVE_WINDOWS_KTM
    22 
    23 #include <stdlib.h>
    24 #include <stdio.h>
    25 #include <string.h>
    26 #include <unistd.h>
    27 #include <sys/types.h>
    28 #include <sys/stat.h>
    29 #include <fcntl.h>
    30 #include <dirent.h>
    31 #include <errno.h>
    32 #include "razor-internal.h"
    33 
    34 /*
    35  * Emulated atomic support
    36  *
    37  * This implementation is better than nothing, but is certainly not atomic.
    38  * It does have a couple of advantages over atomic-none:
    39  *	- If a file operation fails while a package is being installed we
    40  *	  have a good chance of being able to rollback the transaction to
    41  *	  a well-known state.
    42  *	- We behave similarly to atomic-ktm in that changes are not visible
    43  *	  on disk to non-atomic operations (eg., scripts) until the atomic
    44  *	  is committed. This makes the testsuite more likely to pick up
    45  *	  problems that would otherwise only be found when using razor on
    46  *	  an MS-Windows system which supports KTM.
    47  */
    48 
    49 #ifndef O_BINARY
    50 #define O_BINARY	0
    51 #endif
    52 
    53 static void recursive_remove(const char *directory)
    54 {
    55 	DIR *dp;
    56 	struct dirent *dirp;
    57 	char *buf;
    58 
    59 	dp = opendir(directory);
    60 	while((dirp = readdir(dp))) {
    61 		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
    62 			buf = malloc(strlen(directory) + strlen(dirp->d_name)
    63 				     + 2);
    64 			sprintf(buf, "%s/%s", directory, dirp->d_name);
    65 			if (remove(buf) < 0)
    66 				recursive_remove(buf);
    67 			free(buf);
    68 		}
    69 	}
    70 
    71 	rmdir(directory);
    72 }
    73 
    74 RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description)
    75 {
    76 	struct razor_atomic *atomic;
    77 
    78 	atomic = zalloc(sizeof *atomic);
    79 
    80 	atomic->description = strdup(description);
    81 
    82 	return atomic;
    83 }
    84 
    85 RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic)
    86 {
    87 	struct atomic_action *actions;
    88 
    89 	if (razor_atomic_in_error_state(atomic))
    90 		return -1;
    91 
    92 	if (atomic->actions) {
    93 		actions = atomic_action_list_reverse(atomic->actions);
    94 		atomic->actions = NULL;
    95 		actions = atomic_action_do(atomic, actions);
    96 		atomic_action_free(actions);
    97 	}
    98 
    99 	if (atomic->toplevel) {
   100 		recursive_remove(atomic->toplevel);
   101 		free(atomic->toplevel);
   102 		atomic->toplevel = NULL;
   103 	}
   104 
   105 	return razor_atomic_in_error_state(atomic);
   106 }
   107 
   108 RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic)
   109 {
   110 	if (atomic->toplevel) {
   111 		recursive_remove(atomic->toplevel);
   112 		free(atomic->toplevel);
   113 		atomic->toplevel = NULL;
   114 	}
   115 
   116 	if (atomic->error)
   117 		razor_error_free(atomic->error);
   118 
   119 	free(atomic);
   120 }
   121 
   122 /*
   123  * We need a toplevel directory in which to hold temporary files
   124  * before they are committed. Since we can generally assume that
   125  * we have write permissions anywhere on the disk in question,
   126  * the best location is in the relevant root directory. The most
   127  * common case where this assumption fails is when testing, when
   128  * the current directory is a good choice.
   129  * It might be even better to find a mount point above path instead
   130  * but this is hard to do, and probably not worth the effort.
   131  */
   132 
   133 static int
   134 razor_atomic_set_toplevel_from_path(struct razor_atomic *atomic,
   135 				    const char *path)
   136 {
   137 	if (razor_atomic_in_error_state(atomic))
   138 		return -1;
   139 
   140 	if (atomic->toplevel)
   141 		return 0;
   142 
   143 #ifdef MSWIN_API
   144 	if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
   145 	    && strchr(path+3,'\\')) {
   146 		/* We have a UNC path: \\servername\sharename... */
   147 		const char *sharename, *root;
   148 		int disklen;
   149 
   150 		sharename = strchr(path+3,'\\')+1;
   151 		root = strchr(sharename,'\\');
   152 		if (root)
   153 		    disklen = root - path;
   154 		else
   155 		    disklen = strlen(path);
   156 
   157 		atomic->toplevel =
   158 		  malloc(disklen + strlen("\\atomic-XXXXXX") + 1);
   159 		memcpy(atomic->toplevel, path, disklen);
   160 		strcpy(atomic->toplevel + disklen, "\\atomic-XXXXXX");
   161 	} else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
   162 		    path[1]==':') {
   163 		atomic->toplevel = strdup("X:\\atomic-XXXXXX");
   164 		*atomic->toplevel = *path;
   165 	} else {
   166 		DWORD n;
   167 		wchar_t *buf;
   168 		char *dir;
   169 
   170 		n = GetCurrentDirectoryW(0, NULL);
   171 		buf = malloc(n * sizeof(wchar_t));
   172 
   173 		if (GetCurrentDirectoryW(n, buf)) {
   174 			dir = razor_utf16_to_utf8(buf, n - 1);
   175 			razor_atomic_set_toplevel_from_path(atomic, dir);
   176 
   177 			free(dir);
   178 			free(buf);
   179 			return;
   180 		} else
   181 			atomic->toplevel = strdup("C:\\atomic-XXXXXX");
   182 
   183 		free(buf);
   184 	}
   185 #else
   186 	atomic->toplevel = strdup("/.atomic-XXXXXX");
   187 #endif
   188 
   189 	if (!mkdtemp(atomic->toplevel)) {
   190 		int err = errno;
   191 
   192 #ifdef EACCES
   193 		if (err == EACCES) {
   194 			char *s = strdup("atomic-XXXXXX");
   195 
   196 			if (mkdtemp(s)) {
   197 				free(atomic->toplevel);
   198 				atomic->toplevel = s;
   199 				return 0;
   200 			} else
   201 				free(s);
   202 		}
   203 #endif
   204 
   205 		atomic->error = razor_error_new_str(atomic->toplevel,
   206 						    strerror(err));
   207 
   208 		free(atomic->toplevel);
   209 		atomic->toplevel = NULL;
   210 	}
   211 
   212 	return !atomic->toplevel;
   213 }
   214 
   215 RAZOR_EXPORT int
   216 razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   217 		       const char *path)
   218 {
   219 	struct atomic_action *a;
   220 
   221 	razor_atomic_set_toplevel_from_path(atomic, *root ? root : path);
   222 
   223 	if (razor_atomic_in_error_state(atomic))
   224 		return -1;
   225 
   226 	a = atomic_action_new(ACTION_MAKE_DIRS);
   227 	a->args.path = strdup(path);
   228 	a->args.u.make_dirs.root = strdup(root);
   229 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   230 
   231 	return 0;
   232 }
   233 
   234 RAZOR_EXPORT int
   235 razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   236 {
   237 	struct atomic_action *a;
   238 
   239 	razor_atomic_set_toplevel_from_path(atomic, path);
   240 
   241 	if (razor_atomic_in_error_state(atomic))
   242 		return -1;
   243 
   244 	a = atomic_action_new(ACTION_REMOVE);
   245 	a->args.path = strdup(path);
   246 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   247 
   248 	return 0;
   249 }
   250 
   251 RAZOR_EXPORT int
   252 razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   253 			 const char *newpath)
   254 {
   255 	struct atomic_action *a;
   256 
   257 	razor_atomic_set_toplevel_from_path(atomic, newpath);
   258 
   259 	if (razor_atomic_in_error_state(atomic))
   260 		return -1;
   261 
   262 	a = atomic_action_new(ACTION_MOVE);
   263 	a->args.path = strdup(oldpath);
   264 	a->args.u.move.dest = strdup(newpath);
   265 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   266 
   267 	return 0;
   268 }
   269 
   270 RAZOR_EXPORT int
   271 razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   272 			mode_t mode)
   273 {
   274 	struct atomic_action *a;
   275 
   276 	razor_atomic_set_toplevel_from_path(atomic, dirname);
   277 
   278 	if (razor_atomic_in_error_state(atomic))
   279 		return -1;
   280 
   281 	a = atomic_action_new(ACTION_CREATE_DIR);
   282 	a->args.path = strdup(dirname);
   283 	a->args.u.create_dir.mode = mode;
   284 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   285 
   286 	return 0;
   287 }
   288 
   289 RAZOR_EXPORT int
   290 razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   291 			    const char *path)
   292 {
   293 #if HAVE_SYMLINK
   294 	struct atomic_action *a;
   295 
   296 	razor_atomic_set_toplevel_from_path(atomic, path);
   297 #endif
   298 
   299 	if (razor_atomic_in_error_state(atomic))
   300 		return -1;
   301 
   302 #if HAVE_SYMLINK
   303 	a = atomic_action_new(ACTION_CREATE_SYMLINK);
   304 	a->args.path = strdup(path);
   305 	a->args.u.create_symlink.target = strdup(target);
   306 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   307 
   308 	return 0;
   309 #else
   310 	atomic->error = razor_error_new_str(NULL,
   311 					    "Symbolic links not supported "
   312 					    "on this platform");
   313 
   314 	return -1;
   315 #endif
   316 }
   317 
   318 RAZOR_EXPORT int
   319 razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   320                          mode_t mode)
   321 {
   322 	int fd;
   323 	struct atomic_action *a;
   324 	char *tmpnam;
   325 
   326 	razor_atomic_set_toplevel_from_path(atomic, filename);
   327 
   328 	if (razor_atomic_in_error_state(atomic))
   329 		return -1;
   330 
   331 	tmpnam = atomic_action_attic_tmpnam(atomic);
   332 	fd = open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
   333 		  mode & (S_IRWXU | S_IRWXG | S_IRWXO));
   334 
   335 	if (fd == -1)
   336 		atomic->error = razor_error_new_str(filename, strerror(errno));
   337 	else {
   338 		a = atomic_action_new(ACTION_MOVE);
   339 		a->args.path = tmpnam;
   340 		a->args.u.move.dest = strdup(filename);
   341 		atomic->actions = atomic_action_list_prepend(atomic->actions,
   342 							     a);
   343 	}
   344 
   345 	return fd;
   346 }
   347 
   348 #endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */