librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Aug 23 16:28:31 2014 +0100 (2014-08-23)
changeset 442 c4bcba8023a9
parent 423 6112bcc5d1cf
child 444 a2908416b7cc
permissions -rw-r--r--
Fix compiler warnings
     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 */