1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1991-1995, 2002-2006, 2008, 2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, see
17 // <http://www.gnu.org/licenses/>.
18 //
19
20 #include <common/mem.h>
21 #include <common/trace.h>
22 #include <libaegis/commit.h>
23 #include <libaegis/dir.h>
24 #include <libaegis/dir/functor/rm_dir_tree.h>
25 #include <libaegis/interrupt.h>
26 #include <libaegis/os.h>
27 #include <libaegis/undo.h>
28
29
30 //
31 // This file contains "commit" functions.
32 //
33 // The idea is that aegis can be interrupted, or fail from errors, and
34 // behave as if "nothing" had happened. That is, no user discernable
35 // difference to their environment, and certainly no changes to aegis'
36 // data base.
37 //
38 // To do this, many database functions write updates to temporary files
39 // "near" where they are to go, eventually. A commit and an abort
40 // function are both issued, one to put the new file where it really
41 // goes, and one to remove it. Exactly one option will be exercised.
42 //
43 // Commit actions will be performed with the user-id set the same as was
44 // set at the time the commit call was issued.
45 //
46
47 enum what_ty
48 {
49 what_rename,
50 what_unlink_errok,
51 what_rmdir_errok,
52 what_rmdir_tree_bg,
53 what_rmdir_tree_errok,
54 what_hard_link,
55 what_symlink
56 };
57
58 struct action_ty
59 {
60 what_ty what;
61 nstring path1;
62 nstring path2;
63 action_ty *next;
64 int uid;
65 int gid;
66 int umask;
67
action_tyaction_ty68 action_ty() :
69 what((what_ty)-1),
70 next(0),
71 uid(0),
72 gid(0),
73 umask(0)
74 {
75 os_become_query(&uid, &gid, &umask);
76 }
77 };
78
79 static action_ty *head1;
80 static action_ty *tail1;
81 static action_ty *head2;
82
83
84 /**
85 * The link1 function is used to add an new item to the head of chain1.
86 *
87 * @param action
88 * what to do
89 * @param path1
90 * mandatory argument
91 * @param path2
92 * optional argument
93 */
94
95 static void
link1(what_ty what,const nstring & path1,const nstring & path2)96 link1(what_ty what, const nstring &path1, const nstring &path2)
97 {
98 trace(("commit::link1(what = %d, path1 = \"%s\", path2 = \"%s\")\n{\n",
99 what, path1.c_str(), path2.c_str()));
100 action_ty *new_thing = new action_ty;
101 new_thing->what = what;
102 new_thing->path1 = path1;
103 new_thing->path2 = path2;
104 if (head1)
105 {
106 tail1->next = new_thing;
107 tail1 = new_thing;
108 }
109 else
110 head1 = tail1 = new_thing;
111 trace(("}\n"));
112 }
113
114
115 /**
116 * The link2 function is used to add an new item to the head of chain2
117 *
118 * @param action
119 * what to do
120 * @param path1
121 * mandatory argument
122 * @param path2
123 * optional argument
124 */
125
126 static void
link2(what_ty what,const nstring & path1,const nstring & path2)127 link2(what_ty what, const nstring &path1, const nstring &path2)
128 {
129 trace(("commit::link2(what = %d, path1 = \"%s\", path2 = \"%s\")\n{\n",
130 what, path1.c_str(), path2.c_str()));
131 action_ty *new_thing = new action_ty;
132 new_thing->what = what;
133 new_thing->path1 = path1;
134 new_thing->path2 = path2;
135 new_thing->next = head2;
136 head2 = new_thing;
137 trace(("}\n"));
138 }
139
140
141 void
commit_rename(string_ty * from,string_ty * to)142 commit_rename(string_ty *from, string_ty *to)
143 {
144 commit_rename(nstring(from), nstring(to));
145 }
146
147
148 void
commit_rename(const nstring & from,const nstring & to)149 commit_rename(const nstring &from, const nstring &to)
150 {
151 trace(("commit_rename(from = \"%s\", to = \"%s\")\n{\n", from.c_str(),
152 to.c_str()));
153 link1(what_rename, from, to);
154 trace(("}\n"));
155 }
156
157
158 void
commit_symlink(string_ty * from,string_ty * to)159 commit_symlink(string_ty *from, string_ty *to)
160 {
161 commit_symlink(nstring(from), nstring(to));
162 }
163
164
165 void
commit_symlink(const nstring & from,const nstring & to)166 commit_symlink(const nstring &from, const nstring &to)
167 {
168 trace(("commit_symlink(from = \"%s\", to = \"%s\")\n{\n", from.c_str(),
169 to.c_str()));
170 link1(what_symlink, from, to);
171 trace(("}\n"));
172 }
173
174
175 void
commit_hard_link(string_ty * from,string_ty * to)176 commit_hard_link(string_ty *from, string_ty *to)
177 {
178 commit_hard_link(nstring(from), nstring(to));
179 }
180
181
182 void
commit_hard_link(const nstring & from,const nstring & to)183 commit_hard_link(const nstring &from, const nstring &to)
184 {
185 trace(("commit_hard_link(from = \"%s\", to = \"%s\")\n{\n", from.c_str(),
186 to.c_str()));
187 link1(what_hard_link, from, to);
188 trace(("}\n"));
189 }
190
191
192 void
commit_unlink_errok(string_ty * path)193 commit_unlink_errok(string_ty *path)
194 {
195 commit_unlink_errok(nstring(path));
196 }
197
198
199 void
commit_unlink_errok(const nstring & path)200 commit_unlink_errok(const nstring &path)
201 {
202 trace(("commit_unlink_errok(path = \"%s\")\n{\n", path.c_str()));
203 link2(what_unlink_errok, path, "");
204 trace(("}\n"));
205 }
206
207
208 void
commit_rmdir_errok(string_ty * path)209 commit_rmdir_errok(string_ty *path)
210 {
211 commit_rmdir_errok(nstring(path));
212 }
213
214
215 void
commit_rmdir_errok(const nstring & path)216 commit_rmdir_errok(const nstring &path)
217 {
218 trace(("commit_rmdir_errok(path = \"%s\")\n{\n", path.c_str()));
219 link2(what_rmdir_errok, path, "");
220 trace(("}\n"));
221 }
222
223
224 void
commit_rmdir_tree_bg(string_ty * path)225 commit_rmdir_tree_bg(string_ty *path)
226 {
227 commit_rmdir_tree_bg(nstring(path));
228 }
229
230
231 void
commit_rmdir_tree_bg(const nstring & path)232 commit_rmdir_tree_bg(const nstring &path)
233 {
234 trace(("commit_rmdir_tree_bg(path = \"%s\")\n{\n", path.c_str()));
235 link2(what_rmdir_tree_bg, path, "");
236 trace(("}\n"));
237 }
238
239
240 void
commit_rmdir_tree_errok(string_ty * path)241 commit_rmdir_tree_errok(string_ty *path)
242 {
243 commit_rmdir_tree_errok(nstring(path));
244 }
245
246
247 void
commit_rmdir_tree_errok(const nstring & path)248 commit_rmdir_tree_errok(const nstring &path)
249 {
250 trace(("commit_rmdir_tree_errok(path = \"%s\")\n{\n", path.c_str()));
251 link2(what_rmdir_tree_errok, path, "");
252 trace(("}\n"));
253 }
254
255
256 void
commit(void)257 commit(void)
258 {
259 //
260 // Disable interrupts (such as ^C) for the duration. Note that
261 // commit consists solely of file renames and removes. No long
262 // writes are performed at this time. Sometimes there is a lot
263 // to do.
264 //
265 trace(("commit()\n{\n"));
266 interrupt_disable();
267
268 //
269 // Perform the queued actions.
270 //
271 while (head1 || head2)
272 {
273 //
274 // Take the first item off the list.
275 // Note that actions may append more items to the list.
276 //
277 action_ty *action = 0;
278 if (head1)
279 {
280 action = head1;
281 head1 = action->next;
282 if (!head1)
283 tail1 = 0;
284 }
285 else
286 {
287 action = head2;
288 head2 = action->next;
289 }
290
291 //
292 // Do the action.
293 //
294 os_become(action->uid, action->gid, action->umask);
295 switch (action->what)
296 {
297 case what_rename:
298 os_rename(action->path1, action->path2);
299 undo_rename(action->path2, action->path1);
300 break;
301
302 case what_symlink:
303 os_symlink(action->path1, action->path2);
304 undo_unlink_errok(action->path2);
305 break;
306
307 case what_hard_link:
308 os_link(action->path1, action->path2);
309 undo_unlink_errok(action->path2);
310 break;
311
312 case what_unlink_errok:
313 os_unlink_errok(action->path1);
314 break;
315
316 case what_rmdir_errok:
317 os_rmdir_errok(action->path1);
318 break;
319
320 case what_rmdir_tree_bg:
321 os_rmdir_bg(action->path1);
322 break;
323
324 case what_rmdir_tree_errok:
325 if (os_exists(action->path1))
326 {
327 dir_functor_rm_dir_tree eraser;
328 dir_walk(action->path1, eraser);
329 }
330 break;
331 }
332 os_become_undo();
333
334 //
335 // Delete the list element.
336 //
337 delete action;
338 }
339
340 //
341 // it's all committed, nothing left to undo.
342 //
343 undo_cancel();
344
345 //
346 // Enable interrupts once more.
347 //
348 interrupt_enable();
349 trace(("}\n"));
350 }
351
352
353 // vim: set ts=8 sw=4 et :
354