2 * Copyright (C) 2012 J. Ali Harlow <ali@juiblex.co.uk>
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.
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.
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.
21 #if ENABLE_ATOMIC && !HAVE_WINDOWS_KTM
29 #include <sys/types.h>
32 #include "razor-internal.h"
34 char *atomic_action_attic_tmpnam(struct razor_atomic *atomic)
37 sprintf(filename,"%03X",atomic->next_file_tag++);
38 return razor_concat(atomic->toplevel, "/", filename, NULL);
41 static struct atomic_action *
42 atomic_action_list_pop_head(struct atomic_action **list)
44 struct atomic_action *head;
55 struct atomic_action *
56 atomic_action_list_prepend(struct atomic_action *list,
57 struct atomic_action *action)
59 assert(action->next == NULL);
66 static struct atomic_action *
67 atomic_action_list_concat(struct atomic_action *list1,
68 struct atomic_action *list2)
70 struct atomic_action *action;
75 for(action=list1;action->next;action=action->next)
83 void atomic_action_free(struct atomic_action *action)
85 struct atomic_action *a;
88 a = atomic_action_list_pop_head(&action);
93 case ACTION_MAKE_DIRS:
94 free(a->args.u.make_dirs.root);
97 free(a->args.u.move.dest);
100 case ACTION_CREATE_SYMLINK:
101 free(a->args.u.create_symlink.target);
105 case ACTION_CREATE_DIR:
113 struct atomic_action *atomic_action_new(enum atomic_action_type type)
115 struct atomic_action *action;
117 action = zalloc(sizeof *action);
123 struct atomic_action *atomic_action_list_reverse(struct atomic_action *list)
125 struct atomic_action *prev = NULL, *next;
138 * All action_ functions take 1 action and return a list of primitive
139 * actions they took (such that the first action in the list is the
140 * last action they took). Primitive actions are always reversable.
142 * On failure, an error should be set on the atomic object (if it is
143 * not already set), the action freed and NULL returned.
145 * Whether they succeed or fail, they take ownership of the passed
146 * action and should thus either free it or return it back to their
147 * caller as appropriate.
149 * A NULL return means that nothing was done which may, or may not,
153 static struct atomic_action *
154 atomic_action_make_dirs(struct razor_atomic *atomic,
155 struct atomic_action *action)
157 char buffer[PATH_MAX], *p;
158 const char *slash, *next;
159 struct atomic_action *primitives = NULL;
160 struct atomic_action *prim;
162 if (razor_atomic_in_error_state(atomic)) {
163 atomic_action_free(action);
167 strcpy(buffer, action->args.u.make_dirs.root);
168 p = buffer + strlen(buffer);
169 slash = action->args.path;
170 for (; *slash != '\0'; slash = next) {
172 next = strpbrk(slash + 1, "/\\");
174 next = strchr(slash + 1, '/');
179 memcpy(p, slash, next - slash);
183 if (razor_valid_root_name(buffer))
186 prim = atomic_action_new(ACTION_CREATE_DIR);
187 prim->args.path = strdup(buffer);
188 prim->args.u.create_dir.mode = S_IRWXU | S_IRWXG | S_IRWXO;
189 primitives = atomic_action_list_prepend(primitives, prim);
191 primitives = atomic_action_list_reverse(primitives);
193 return atomic_action_do(atomic, primitives);
196 static struct atomic_action *
197 atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action)
205 struct atomic_action *prim;
207 if (razor_atomic_in_error_state(atomic)) {
208 atomic_action_free(action);
213 * Non-empty directories should NOT be removed
216 path = razor_utf8_to_utf16(action->args.path, -1);
218 dir = _wopendir(path);
219 if (dir && _wreaddir(dir)) {
220 atomic_action_free(action);
225 dir = opendir(action->args.path);
226 if (dir && readdir(dir)) {
227 atomic_action_free(action);
234 prim = atomic_action_new(ACTION_MOVE);
235 prim->args.path = strdup(action->args.path);
236 prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic);
238 atomic_action_free(action);
242 return prim ? atomic_action_do(atomic, prim) : NULL;
245 static struct atomic_action *
246 atomic_action_create_dir(struct razor_atomic *atomic,
247 struct atomic_action *action)
251 if (razor_atomic_in_error_state(atomic)) {
252 atomic_action_free(action);
256 mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
258 if (!mkdir(action->args.path, mode))
261 if (errno != EEXIST || chmod(action->args.path, mode) < 0) {
263 atomic->error = razor_error_new_posix(action->args.path);
264 atomic_action_free(action);
271 static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
272 struct atomic_action *action)
274 if (razor_atomic_in_error_state(atomic)) {
275 atomic_action_free(action);
279 if (rmdir(action->args.path) < 0) {
281 atomic->error = razor_error_new_posix(action->args.path);
282 atomic_action_free(action);
289 static struct atomic_action *
290 atomic_action_create_symlink(struct razor_atomic *atomic,
291 struct atomic_action *action)
295 if (razor_atomic_in_error_state(atomic)) {
296 atomic_action_free(action);
300 r = symlink(action->args.u.create_symlink.target, action->args.path);
303 atomic->error = razor_error_new_posix(action->args.path);
304 atomic_action_free(action);
311 static struct atomic_action *
312 atomic_action_remove_symlink(struct razor_atomic *atomic,
313 struct atomic_action *action)
315 if (razor_atomic_in_error_state(atomic)) {
316 atomic_action_free(action);
320 if (unlink(action->args.path) < 0) {
322 atomic->error = razor_error_new_posix(action->args.path);
323 atomic_action_free(action);
332 move_file(struct razor_atomic *atomic, const char *path, const char *dest)
335 wchar_t *oldbuf, *newbuf;
336 const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
338 newbuf = razor_utf8_to_utf16(dest, -1);
339 oldbuf = razor_utf8_to_utf16(path, -1);
342 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
343 * cover every case we care about _except_ replacing an empty
344 * directory with a file. Calling RemoveDirectory() will deal
345 * with this case while having no effect in all other cases.
347 (void)RemoveDirectoryW(newbuf);
349 if (!MoveFileExW(oldbuf, newbuf, flags)) {
351 atomic->error = razor_error_new_mswin(newbuf,
359 if (rename(path, dest)) {
361 atomic->error = razor_error_new_posix(dest);
369 static struct atomic_action *
370 atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
372 if (razor_atomic_in_error_state(atomic)) {
373 atomic_action_free(action);
377 if (move_file(atomic, action->args.path, action->args.u.move.dest)) {
378 atomic_action_free(action);
385 static struct atomic_action *
386 atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
388 if (razor_atomic_in_error_state(atomic)) {
389 atomic_action_free(action);
393 if (move_file(atomic, action->args.u.move.dest, action->args.path)) {
394 atomic_action_free(action);
401 static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
402 struct atomic_action *action)
404 switch(action->type) {
405 case ACTION_MAKE_DIRS:
406 action = atomic_action_make_dirs(atomic, action);
409 action = atomic_action_remove(atomic, action);
411 case ACTION_CREATE_DIR:
412 action = atomic_action_create_dir(atomic, action);
415 action = atomic_action_move(atomic, action);
418 case ACTION_CREATE_SYMLINK:
419 action = atomic_action_create_symlink(atomic, action);
426 static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
427 struct atomic_action *action)
429 switch(action->type) {
430 case ACTION_MAKE_DIRS:
432 /* Complex actions: should never happen */
434 case ACTION_CREATE_DIR:
435 action = atomic_action_rmdir(atomic, action);
438 action = atomic_action_unmove(atomic, action);
441 case ACTION_CREATE_SYMLINK:
442 action = atomic_action_remove_symlink(atomic, action);
450 * Note that undo has no error checking.
453 void atomic_action_undo(struct razor_atomic *atomic,
454 struct atomic_action *actions)
456 struct atomic_action *a;
461 a = atomic_action_list_pop_head(&actions);
462 a = atomic_action_reverse(atomic, a);
463 atomic_action_free(a);
469 struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
470 struct atomic_action *actions)
472 struct atomic_action *done = NULL, *a;
474 if (razor_atomic_in_error_state(atomic)) {
475 atomic_action_free(actions);
480 a = atomic_action_list_pop_head(&actions);
481 a = atomic_action_apply(atomic, a);
483 done = atomic_action_list_concat(a, done);
484 if (razor_atomic_in_error_state(atomic)) {
485 atomic_action_undo(atomic, done);
487 atomic_action_free(a);
494 #endif /* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */