"Fossies" - the Fresh Open Source Software Archive 
Member "shake-1.0/executive.c" (15 Nov 2014, 13033 Bytes) of package /linux/privat/shake-1.0.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "executive.c" see the
Fossies "Dox" file reference documentation.
1 /***************************************************************************/
2 /* Copyright (C) 2006-2011 Brice Arnould. */
3 /* */
4 /* This file is part of ShaKe. */
5 /* */
6 /* ShaKe is free software; you can redistribute it and/or modify */
7 /* it under the terms of the GNU General Public License as published by */
8 /* the Free Software Foundation; either version 3 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* This program is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU General Public License */
17 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /***************************************************************************/
19
20 #define _GNU_SOURCE
21 #include "executive.h"
22 #include "linux.h" // is_lock_canceled()
23 #include "signals.h"
24 #include <alloca.h>
25 #include <stdlib.h>
26 #include <stdio.h> // asprintf()
27 #include <errno.h>
28 #include <assert.h>
29 #include <string.h>
30 #include <linux/fs.h> // FIGETBSZ
31 #include <limits.h> // SSIZE_MAX
32 #include <sys/stat.h> // stat()
33 #include <unistd.h> // stat()
34 #include <sys/ioctl.h> // ioctl()
35 #include <error.h> // error()
36 #include <sys/types.h> // opendir()
37 #include <dirent.h> // opendir()
38 #include <sys/time.h> // futimes()
39
40
41 int
42 fcopy (int in_fd, int out_fd, size_t gap, bool stop_if_input_unlocked)
43 {
44 assert (in_fd > -1), assert (out_fd > -1);
45 size_t buffsize = 65535; // Must fit in a integer
46 int *buffer;
47 /* Prepare files */
48 if (-1 == lseek (in_fd, (off_t) 0, SEEK_SET)
49 || -1 == lseek (out_fd, (off_t) 0, SEEK_SET)
50 || -1 == ftruncate (out_fd, (off_t) 0))
51 return -1;
52 /* Optimisation (on Linux it double the readahead window) */
53 posix_fadvise (in_fd, (off_t) 0, (off_t) 0, POSIX_FADV_SEQUENTIAL);
54 posix_fadvise (in_fd, (off_t) 0, (off_t) 0, POSIX_FADV_WILLNEED);
55 /* Get a buffer... */
56 {
57 if (gap)
58 {
59 int physbsize;
60 /* Convert the gap in a number of blocks
61 * The idea is that it would be useless to make only a part of a block
62 * sparse, so we use buffers of physical block size and make a hole
63 * only if there's enough consecutive empty buffers.
64 */
65 if (-1 == ioctl (out_fd, FIGETBSZ, &physbsize))
66 return -1;
67 else if (physbsize < 1)
68 {
69 error (0, 0, "Buggy FS: negative block size !");
70 return -1;
71 }
72 if (gap >= physbsize)
73 {
74 gap /= (size_t) physbsize;
75 // now gap is number of empty buffers required to make the file sparse
76 buffsize = (size_t) physbsize;
77 }
78 }
79 if (buffsize > SSIZE_MAX)
80 {
81 buffsize = SSIZE_MAX;
82 gap = 0;
83 }
84 buffer = alloca (buffsize); // better than "goto freeall"... or not ?
85 // Else would read uninitialised datas when filesize < buffsize
86 // and is_empty could be set uncorrectly.
87 memset (buffer, 0xFF, buffsize);
88 }
89 /* Let's go ! */
90 {
91 int len = 0;
92 uint empty_buffs = 0; // Number of consecutive empty buffers, for sparse files
93 bool is_empty = 0; // Tell if the buffer is empty, for sparse files
94 int *empty = NULL; // An empty buffer, for sparse files
95 if (gap)
96 {
97 empty = alloca (buffsize); // better than goto free()... or not ?
98 if (!empty)
99 return -1;
100 memset (empty, '\0', buffsize);
101 }
102 while (true)
103 {
104 bool eof; // tell if we are at end of file
105 bool cant_wait; // tell if we need to flush buffers
106 /* Check if we have to cancel the copy */
107 if (stop_if_input_unlocked && !is_locked (in_fd))
108 {
109 // The warning is shown by the signal handler
110 errno = 0;
111 return -2;
112 }
113 /* Read */
114 len = (int) read (in_fd, buffer, buffsize);
115 if (-1 == len)
116 return -1;
117 eof = (len != buffsize);
118 if (gap)
119 {
120 assert (0 == buffsize % sizeof (*buffer));
121 /* Is the buffer empty ? */
122 is_empty = 1;
123 // at EOF we will take in account previously read datas
124 // but that is not important
125 for (uint i = 0; i < buffsize / sizeof (*buffer); i++)
126 if (buffer[i])
127 {
128 is_empty = 0; // no
129 break;
130 }
131 if (eof)
132 is_empty = 0; // force write of the last buffer
133 if (is_empty)
134 empty_buffs++; // (can't overflow, see cant_wait two lines down)
135 }
136 /* if in sparse mode, we'll wait for eof or data, or int overflow before writing */
137 cant_wait = !is_empty || eof
138 || (empty_buffs + 1) * buffsize > INT_MAX;
139 if (gap && cant_wait)
140 {
141 /* Should we make a hole ? */
142 if (empty_buffs >= gap && len != 0) // Don't finish with a hole if len == 0
143 {
144 if (-1 ==
145 lseek (out_fd, (off_t) (empty_buffs * buffsize),
146 SEEK_CUR))
147 return -1;
148 empty_buffs = 0;
149 }
150 else
151 {
152 // Write empty space
153 for (; empty_buffs; empty_buffs--)
154 if (buffsize != write (out_fd, empty, buffsize))
155 return -1;
156 assert (0 == empty_buffs);
157 }
158 }
159 if (!gap || cant_wait)
160 {
161 if (len != write (out_fd, buffer, (uint) len))
162 return -1;
163 }
164 if (eof)
165 break;
166 }
167 }
168 /* Verify we didn't miss anything */
169 {
170 struct stat in_stats;
171 struct stat out_stats;
172 if (fstat (in_fd, &in_stats))
173 return -1;
174 if (fstat (out_fd, &out_stats))
175 return -1;
176 if (out_stats.st_size != in_stats.st_size)
177 {
178 errno = 0; // the error would be in the check and so meaningless
179 return -1;
180 }
181 }
182 return 1;
183 }
184
185
186 /* Marks a file as shaked
187 */
188 // The opposite function is "release"
189 static void
190 capture (struct accused *a, struct law *l)
191 {
192 assert (a), assert (l);
193 return;
194 }
195
196 /* Returns 1 if locking is enabled but we don't own a lock over a,
197 * else returns 0.
198 */
199 static int
200 has_been_unlocked (struct accused *a, struct law *l)
201 {
202 return l->locks && !is_locked (a->fd);
203 }
204
205
206 /* Restores the mtime of a
207 * and mark the file as shaked.
208 */
209 // The opposite function is "capture"
210 static void
211 release (struct accused *a, struct law *l)
212 {
213 assert (a), assert (l);
214 assert (a->fd >= 0);
215 /* Restores mtime */
216 {
217 struct timeval tv[2];
218 tv[0].tv_sec = a->atime;
219 tv[0].tv_usec = 0;
220 tv[1].tv_sec = a->mtime;
221 tv[1].tv_usec = 0;
222 futimes (a->fd, tv);
223 }
224 if (has_been_unlocked (a, l))
225 error (0, 0, "%s: concurent accesses", a->name);
226 return;
227 }
228
229
230 /* Backups a->fd over l->tmpfd. First halve of shake_reg() .
231 * Returns -1 if failed, else 0;
232 */
233 static int
234 shake_reg_backup_phase (struct accused *a, struct law *l)
235 {
236 const int res = fcopy (a->fd, l->tmpfd, MAGICLEAP, l->locks);
237 if (0 > res || has_been_unlocked (a, l))
238 return -1;
239 else
240 return 0;
241 }
242
243 /* Rewrites a->fd from l->tmpfd. Second halve of shake_reg() .
244 * This can be called only when a->fd is *write* locked.
245 * This can be called only when in NORMAL mode. It internally set the
246 * CRITICAL mode but goes back in NORMAL mode before returning.
247 * If it fails, it aborts the execution.
248 */
249 static void
250 shake_reg_rewrite_phase (struct accused *a, struct law *l)
251 {
252 const uint GAP = MAGICLEAP * 4;
253 char *msg;
254 if (-1 == asprintf (&msg,
255 "%s: unrecoverable internal error ! file has been saved at %s",
256 a->name, l->tmpname))
257 {
258 int errsv = errno;
259 unlink (l->tmpname); // could work
260 error (1, errsv, "%s: failed to initialize failure manager", a->name);
261 }
262 /* Disables most signals (except critical ones, see signals.h) */
263 enter_critical_mode (msg);
264 /* Ask the FS to put the file at a new place, without losing metadatas
265 * nor hard links. Works on ReiserFS and Ext4 but should be tested
266 * on other filesystems
267 */
268 if (0 > ftruncate (a->fd, (off_t) 0))
269 error (1, errno,
270 "%s: failed to ftruncate() ! file have been saved at %s",
271 a->name, l->tmpname);
272 /* Do the reverse copying */
273 if (0 > fcopy (l->tmpfd, a->fd, GAP, false))
274 error (1, errno, "%s: restore failed ! file have been saved at %s",
275 a->name, l->tmpname);
276 /* Restores most signals */
277 enter_normal_mode ();
278 free (msg);
279 }
280
281 int
282 shake_reg (struct accused *a, struct law *l)
283 {
284 assert (a), assert (l);
285 assert (S_ISREG (a->mode)), assert (a->guilty);
286
287 if (l->pretend)
288 return 0;
289
290 capture (a, l);
291
292 if (0 > shake_reg_backup_phase (a, l))
293 {
294 error (0, errno, "%s: temporary copy failed", a->name);
295 release (a, l);
296 return -1;
297 }
298
299 /* Tries acquiring a write lock and then to copy the backup over the
300 * original.
301 */
302 if (!(l->locks && 0 > readlock_to_writelock (a->fd)))
303 {
304 shake_reg_rewrite_phase (a, l);
305 /* Updates position time */
306 if (l->xattr && -1 == set_ptime (a->fd))
307 {
308 error (0, errno,
309 "%s: failed to set position time, check user_xattr",
310 a->name);
311 }
312 }
313
314 release (a, l);
315
316 return 0;
317 }
318
319
320 /* For use by qsort().
321 */
322 static int
323 atimesort (const void *a, const void *b)
324 {
325 assert (a && b);
326 /* Cast *a and *b */
327 char *aname = *((char *const *) a);
328 char *bname = *((char *const *) b);
329 assert (aname && bname);
330 /* Get atimes */
331 struct stat astat;
332 struct stat bstat;
333 if (-1 == stat (aname, &astat) || -1 == stat (bname, &bstat))
334 {
335 /* If we abort here, it will be a DoS caused by a race condition.
336 * So vomit a cryptic error message and go on.
337 */
338 #ifdef DEBUG
339 error (0, errno, "stat() of %s or %s failed", aname, bname);
340 #endif
341 return 0;
342 }
343 // Using modulo to prevent int overflow
344 return (int) ((bstat.st_atime - astat.st_atime) % 2);
345 }
346
347 char **
348 list_dir (char *name, int sort)
349 {
350 assert (name);
351 const size_t BUFFSTEP = 128;
352 size_t namel = strlen (name);
353 /* Read entries one by one and store their name in a buffer */
354 uint n = 0;
355 char **buff = malloc (sizeof (*buff) * BUFFSTEP);
356 if (!buff)
357 {
358 error (0, errno, "%s: malloc() failed", name);
359 return NULL;
360 }
361 buff[0] = NULL;
362 struct dirent *ent = NULL;
363 DIR *dir = opendir (name);
364 if (!dir)
365 {
366 error (0, errno, "%s: opendir() failed", name);
367 return NULL;
368 }
369 for (n = 0; (ent = readdir (dir)); n++)
370 {
371 char *dname = ent->d_name;
372 char *fname; // full name
373 if (n == INT_MAX)
374 {
375 error (0, 0, "%s: more than %u files", name, INT_MAX - 1);
376 break;
377 }
378 /* ignore "." and ".." */
379 if (0 == strcmp (dname, ".") || 0 == strcmp (dname, ".."))
380 {
381 n--;
382 continue;
383 }
384 /* Does the buffer need to be extended ? */
385 if (!((n + 2) % BUFFSTEP)) // + 1 for buff[n], + 1 for buff[n+1]=NULL
386 {
387 char **nbuff = realloc (buff, (n + 2 + BUFFSTEP) * sizeof (*buff));
388 if (!nbuff)
389 {
390 error (0, errno, "%s: realloc() failed", name);
391 break;
392 }
393 buff = nbuff;
394 }
395 /* get and store the complete path relative to cwd */
396 fname = malloc (namel + strlen (dname) + 2);
397 if (!fname)
398 {
399 error (0, errno, "%s: malloc() failed", name);
400 /* Try with the next file */
401 n--;
402 continue;
403 }
404 strcpy (fname, name);
405 fname[namel] = '/';
406 strcpy (fname + namel + 1, dname);
407 buff[n] = fname;
408 }
409 /* They are some breaks in the for loop */
410 if (sort)
411 qsort (buff, n, sizeof (*buff), &atimesort);
412 /* A NULL entry will mark the end of the buffer */
413 buff[n] = NULL;
414 closedir (dir);
415 return buff;
416 }
417
418 char **
419 list_stdin (void)
420 {
421 const size_t BUFFSTEP = 32;
422 uint n = 0;
423 size_t len = 0;
424 char *line = NULL;
425 char **buff = malloc (BUFFSTEP * sizeof (*buff));
426 if (!buff)
427 error (1, errno, "-: malloc() failed");
428 /* for each line */
429 for (; -1 != getline (&line, &len, stdin); line = NULL, len = 0)
430 {
431 if (n == INT_MAX)
432 {
433 error (0, 0, "-: more than %i files", INT_MAX - 1);
434 return NULL;
435 }
436 /* remove the end of line */
437 *strchrnul (line, '\n') = '\0';
438 /* Does the buffer need to be extended ? */
439 if (!((n + 2) % BUFFSTEP)) // + 1 for buff[n], + 1 for buff[n+1]=NULL
440 {
441 char **nbuff = realloc (buff, (n + 2 + BUFFSTEP) * sizeof (*buff));
442 if (!nbuff)
443 {
444 error (0, errno, "s: realloc() failed");
445 close_list (buff);
446 free (line);
447 return NULL;
448 }
449 buff = nbuff;
450 }
451 /* Ignore "" */
452 if ('\0' == *line)
453 {
454 free (line);
455 continue;
456 }
457 /* Add the line into the buffer */
458 buff[n] = line;
459 n++;
460 }
461 free (line);
462 /* A NULL entry will mark the end of the buffer */
463 buff[n] = NULL;
464 if (n)
465 qsort (buff, n - 1, sizeof (*buff), &atimesort);
466 return buff;
467 }
468
469 void
470 close_list (char **flist)
471 {
472 assert (flist);
473 for (char **oflist = flist; *oflist; oflist++)
474 free (*oflist);
475 free (flist);
476 }