2 * Copyright (C) 2012, 2016 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 "razor-internal.h"
31 char *atomic_action_attic_tmpnam(struct razor_atomic *atomic)
34 sprintf(filename,"%03X",atomic->next_file_tag++);
35 return razor_concat(atomic->toplevel, "/", filename, NULL);
38 static struct atomic_action *
39 atomic_action_list_pop_head(struct atomic_action **list)
41 struct atomic_action *head;
52 struct atomic_action *
53 atomic_action_list_prepend(struct atomic_action *list,
54 struct atomic_action *action)
56 assert(action->next == NULL);
63 static struct atomic_action *
64 atomic_action_list_concat(struct atomic_action *list1,
65 struct atomic_action *list2)
67 struct atomic_action *action;
72 for(action=list1;action->next;action=action->next)
80 void atomic_action_free(struct atomic_action *action)
82 struct atomic_action *a;
85 a = atomic_action_list_pop_head(&action);
90 case ACTION_MAKE_DIRS:
91 free(a->args.u.make_dirs.root);
94 free(a->args.u.move.dest);
97 case ACTION_CREATE_SYMLINK:
98 free(a->args.u.create_symlink.target);
102 case ACTION_CREATE_DIR:
110 struct atomic_action *atomic_action_new(enum atomic_action_type type)
112 struct atomic_action *action;
114 action = zalloc(sizeof *action);
120 struct atomic_action *atomic_action_list_reverse(struct atomic_action *list)
122 struct atomic_action *prev = NULL, *next;
135 * All action_ functions take 1 action and return a list of primitive
136 * actions they took (such that the first action in the list is the
137 * last action they took). Primitive actions are always reversable.
139 * On failure, an error should be set on the atomic object (if it is
140 * not already set), the action freed and NULL returned.
142 * Whether they succeed or fail, they take ownership of the passed
143 * action and should thus either free it or return it back to their
144 * caller as appropriate.
146 * A NULL return means that nothing was done which may, or may not,
150 static struct atomic_action *
151 atomic_action_make_dirs(struct razor_atomic *atomic,
152 struct atomic_action *action)
154 char buffer[PATH_MAX], *p;
155 const char *slash, *next;
156 struct atomic_action *primitives = NULL;
157 struct atomic_action *prim;
159 if (razor_atomic_in_error_state(atomic)) {
160 atomic_action_free(action);
164 strcpy(buffer, action->args.u.make_dirs.root);
165 p = buffer + strlen(buffer);
166 slash = action->args.uri;
167 if (p > buffer && p[-1] != ':' && p[-1] != '/' && *slash != '/')
169 for (; *slash != '\0'; slash = next) {
170 next = strchr(slash + 1, '/');
174 memcpy(p, slash, next - slash);
178 if (razor_valid_root_name(buffer))
181 prim = atomic_action_new(ACTION_CREATE_DIR);
182 prim->args.uri = strdup(buffer);
183 prim->args.u.create_dir.mode = S_IRWXU | S_IRWXG | S_IRWXO;
184 primitives = atomic_action_list_prepend(primitives, prim);
186 primitives = atomic_action_list_reverse(primitives);
188 atomic_action_free(action);
190 return atomic_action_do(atomic, primitives);
193 static struct atomic_action *
194 atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action)
198 struct atomic_action *prim;
200 if (razor_atomic_in_error_state(atomic)) {
201 atomic_action_free(action);
206 * Non-empty directories should NOT be removed
208 dir = razor_uri_opendir(action->args.uri, NULL);
210 name = razor_uri_readdir(dir, NULL);
211 razor_uri_closedir(dir, NULL);
214 atomic_action_free(action);
220 prim = atomic_action_new(ACTION_MOVE);
221 prim->args.uri = strdup(action->args.uri);
222 prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic);
224 atomic_action_free(action);
228 return prim ? atomic_action_do(atomic, prim) : NULL;
231 static struct atomic_action *
232 atomic_action_create_dir(struct razor_atomic *atomic,
233 struct atomic_action *action)
236 struct razor_error **error;
238 if (razor_atomic_in_error_state(atomic)) {
239 atomic_action_free(action);
243 mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
248 error = &atomic->error;
250 if (!razor_uri_mkdir(action->args.uri, mode, error))
253 atomic_action_free(action);
257 static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
258 struct atomic_action *action)
260 if (razor_atomic_in_error_state(atomic)) {
261 atomic_action_free(action);
265 if (rmdir(action->args.uri) < 0) {
267 atomic->error = razor_error_new_posix(action->args.uri);
268 atomic_action_free(action);
275 static struct atomic_action *
276 atomic_action_create_symlink(struct razor_atomic *atomic,
277 struct atomic_action *action)
281 if (razor_atomic_in_error_state(atomic)) {
282 atomic_action_free(action);
286 r = symlink(action->args.u.create_symlink.target, action->args.uri);
289 atomic->error = razor_error_new_posix(action->args.uri);
290 atomic_action_free(action);
297 static struct atomic_action *
298 atomic_action_remove_symlink(struct razor_atomic *atomic,
299 struct atomic_action *action)
301 struct razor_error **error;
303 if (razor_atomic_in_error_state(atomic)) {
304 atomic_action_free(action);
311 error = &atomic->error;
313 if (razor_uri_unlink(action->args.uri, error)) {
314 atomic_action_free(action);
323 move_file(struct razor_atomic *atomic, const char *uri, const char *dest)
325 struct razor_error **error;
330 error = &atomic->error;
332 return razor_uri_move(uri, dest, error);
335 static struct atomic_action *
336 atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
338 if (razor_atomic_in_error_state(atomic)) {
339 atomic_action_free(action);
343 if (move_file(atomic, action->args.uri, action->args.u.move.dest)) {
344 atomic_action_free(action);
351 static struct atomic_action *
352 atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
354 if (razor_atomic_in_error_state(atomic)) {
355 atomic_action_free(action);
359 if (move_file(atomic, action->args.u.move.dest, action->args.uri)) {
360 atomic_action_free(action);
367 static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
368 struct atomic_action *action)
370 switch(action->type) {
371 case ACTION_MAKE_DIRS:
372 action = atomic_action_make_dirs(atomic, action);
375 action = atomic_action_remove(atomic, action);
377 case ACTION_CREATE_DIR:
378 action = atomic_action_create_dir(atomic, action);
381 action = atomic_action_move(atomic, action);
384 case ACTION_CREATE_SYMLINK:
385 action = atomic_action_create_symlink(atomic, action);
392 static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
393 struct atomic_action *action)
395 switch(action->type) {
396 case ACTION_MAKE_DIRS:
398 /* Complex actions: should never happen */
400 case ACTION_CREATE_DIR:
401 action = atomic_action_rmdir(atomic, action);
404 action = atomic_action_unmove(atomic, action);
407 case ACTION_CREATE_SYMLINK:
408 action = atomic_action_remove_symlink(atomic, action);
416 * Note that undo has no error checking.
419 void atomic_action_undo(struct razor_atomic *atomic,
420 struct atomic_action *actions)
422 struct atomic_action *a;
427 a = atomic_action_list_pop_head(&actions);
428 a = atomic_action_reverse(atomic, a);
429 atomic_action_free(a);
435 struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
436 struct atomic_action *actions)
438 struct atomic_action *done = NULL, *a;
440 if (razor_atomic_in_error_state(atomic)) {
441 atomic_action_free(actions);
446 a = atomic_action_list_pop_head(&actions);
447 a = atomic_action_apply(atomic, a);
449 done = atomic_action_list_concat(a, done);
450 if (razor_atomic_in_error_state(atomic)) {
451 atomic_action_undo(atomic, done);
453 atomic_action_free(a);
460 #endif /* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */