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) {
262 if (!atomic->error_str)
263 razor_atomic_set_error_str(atomic, action->args.path,
265 atomic_action_free(action);
272 static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
273 struct atomic_action *action)
275 if (razor_atomic_in_error_state(atomic)) {
276 atomic_action_free(action);
280 if (rmdir(action->args.path) < 0) {
281 if (!atomic->error_str)
282 razor_atomic_set_error_str(atomic, action->args.path,
284 atomic_action_free(action);
291 static struct atomic_action *
292 atomic_action_create_symlink(struct razor_atomic *atomic,
293 struct atomic_action *action)
297 if (razor_atomic_in_error_state(atomic)) {
298 atomic_action_free(action);
302 r = symlink(action->args.u.create_symlink.target, action->args.path);
304 if (!atomic->error_str)
305 razor_atomic_set_error_str(atomic, NULL,
307 atomic_action_free(action);
314 static struct atomic_action *
315 atomic_action_remove_symlink(struct razor_atomic *atomic,
316 struct atomic_action *action)
318 if (razor_atomic_in_error_state(atomic)) {
319 atomic_action_free(action);
323 if (unlink(action->args.path) < 0) {
324 if (!atomic->error_str)
325 razor_atomic_set_error_str(atomic, NULL,
327 atomic_action_free(action);
336 move_file(struct razor_atomic *atomic, const char *path, const char *dest)
339 wchar_t *oldbuf, *newbuf;
340 const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
342 newbuf = razor_utf8_to_utf16(dest, -1);
343 oldbuf = razor_utf8_to_utf16(path, -1);
346 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
347 * cover every case we care about _except_ replacing an empty
348 * directory with a file. Calling RemoveDirectory() will deal
349 * with this case while having no effect in all other cases.
351 (void)RemoveDirectoryW(newbuf);
353 if (!MoveFileExW(oldbuf, newbuf, flags)) {
354 if (!atomic->error_str)
355 razor_atomic_set_error_mswin(atomic, newbuf,
363 if (rename(path, dest)) {
364 if (!atomic->error_str)
365 razor_atomic_set_error_str(atomic, dest,
374 static struct atomic_action *
375 atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
377 if (razor_atomic_in_error_state(atomic)) {
378 atomic_action_free(action);
382 if (move_file(atomic, action->args.path, action->args.u.move.dest)) {
383 atomic_action_free(action);
390 static struct atomic_action *
391 atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
393 if (razor_atomic_in_error_state(atomic)) {
394 atomic_action_free(action);
398 if (move_file(atomic, action->args.u.move.dest, action->args.path)) {
399 atomic_action_free(action);
406 static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
407 struct atomic_action *action)
409 switch(action->type) {
410 case ACTION_MAKE_DIRS:
411 action = atomic_action_make_dirs(atomic, action);
414 action = atomic_action_remove(atomic, action);
416 case ACTION_CREATE_DIR:
417 action = atomic_action_create_dir(atomic, action);
420 action = atomic_action_move(atomic, action);
423 case ACTION_CREATE_SYMLINK:
424 action = atomic_action_create_symlink(atomic, action);
431 static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
432 struct atomic_action *action)
434 switch(action->type) {
435 case ACTION_MAKE_DIRS:
437 /* Complex actions: should never happen */
439 case ACTION_CREATE_DIR:
440 action = atomic_action_rmdir(atomic, action);
443 action = atomic_action_unmove(atomic, action);
446 case ACTION_CREATE_SYMLINK:
447 action = atomic_action_remove_symlink(atomic, action);
455 * Note that undo has no error checking.
458 void atomic_action_undo(struct razor_atomic *atomic,
459 struct atomic_action *actions)
461 struct atomic_action *a;
466 a = atomic_action_list_pop_head(&actions);
467 a = atomic_action_reverse(atomic, a);
468 atomic_action_free(a);
474 struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
475 struct atomic_action *actions)
477 struct atomic_action *done = NULL, *a;
479 if (razor_atomic_in_error_state(atomic)) {
480 atomic_action_free(actions);
485 a = atomic_action_list_pop_head(&actions);
486 a = atomic_action_apply(atomic, a);
488 done = atomic_action_list_concat(a, done);
489 if (razor_atomic_in_error_state(atomic)) {
490 atomic_action_undo(atomic, done);
492 atomic_action_free(actions);
499 #endif /* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */