"Fossies" - the Fresh Open Source Software Archive 
Member "shake-1.0/judge.c" (15 Nov 2014, 9428 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 "judge.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 <stdlib.h>
22 #include <errno.h>
23 #include <assert.h>
24
25 #include <string.h> // strdup(), memset()
26 #include <fcntl.h> // open()
27 #include <sys/types.h> // open(), umask()
28 #include <dirent.h> // scandir()
29 #include <sys/stat.h> // stat(), umask()
30 #include <unistd.h> // stat()
31 #include <stdio.h> // printf(), tmpfile()
32 #include <error.h> // error()
33 #include <limits.h> // SSIZE_MAX
34 #include "executive.h" // fcopy()
35 #include "judge.h"
36 #include "linux.h"
37 #include "msg.h"
38
39
40 struct accused *
41 investigate (char *name, struct law *l)
42 {
43 assert (name);
44 struct accused *a;
45 ino_t inode; // used to check against race between open and stat
46 /* malloc() */
47 {
48 a = malloc (sizeof (*a));
49 if (NULL == a)
50 error (1, errno, "%s: malloc() failed", name);
51 a->name = strdup (name);
52 if (NULL == a->name)
53 error (1, errno, "%s: strdup() failed", name);
54 }
55 /* Set default value */
56 {
57 a->fd = -1;
58 a->blocks = 0;
59 a->fragc = 0;
60 a->crumbc = 0;
61 a->start = 0;
62 a->end = 0;
63 a->ideal = 0;
64 a->atime = 0;
65 a->mtime = 0;
66 a->age = 0;
67 a->poslog = NULL;
68 a->sizelog = NULL;
69 a->guilty = 0;
70 }
71 /* this stat() will be applied on all accused, including directory */
72 {
73 struct stat st;
74 if (-1 == lstat (a->name, &st))
75 {
76 error (0, errno, "%s: lstat() failed", name);
77 goto freeall;
78 }
79 a->mode = st.st_mode;
80 a->fs = st.st_dev;
81 a->size = st.st_blocks * 512;
82 inode = st.st_ino;
83 }
84 if (!S_ISREG (a->mode) || 0 == a->size)
85 return a; // a->fd is not opened or locked
86 /* open() */
87 if (-1 == (a->fd = open (name, O_NOATIME | O_RDWR)))
88 {
89 error (0, errno, "%s: open() failed", name);
90 goto freeall;
91 }
92 /* Puts the lock */
93 // it will be released just before returning
94 if (l->locks && -1 == readlock_file (a->fd, a->name))
95 {
96 error (0, errno, "%s: failed to acquire a lock", a->name);
97 goto freeall;
98 }
99 /* This stat() will be applied only on regular files */
100 {
101 struct stat st;
102 if (-1 == fstat (a->fd, &st))
103 {
104 error (0, errno, "%s: fstat() failed", name);
105 goto freeall;
106 }
107 /* Check against race condition */
108 if (st.st_ino != inode || st.st_dev != a->fs)
109 {
110 error (0, errno, "%s: file have moved", name);
111 goto freeall;
112 }
113 a->size = st.st_blocks * 512;
114 a->atime = st.st_atime;
115 a->mtime = st.st_mtime;
116 a->age = time (NULL) - st.st_ctime;
117 }
118 /* Read ptime - placement time */
119 if (l->xattr)
120 {
121 time_t ptime = get_ptime (a->fd);
122 if (ptime != (time_t) - 1)
123 a->age = time (NULL) - ptime;
124 }
125 if (-1 == get_testimony (a, l))
126 goto freeall;
127 unlock_file (a->fd);
128 return a;
129 freeall:
130 {
131 if (a->fd != -1)
132 {
133 unlock_file (a->fd);
134 close (a->fd);
135 }
136 free (a->name);
137 free (a);
138 }
139 return NULL;
140 }
141
142 void
143 close_case (struct accused *a, struct law *l)
144 {
145 if (!a)
146 return;
147 if (a->fd >= 0)
148 {
149 // We ignore the case where the file is already unlocked
150 // because it is legitimate when eg. there were concurent
151 // accesses
152 if (l->locks)
153 unlock_file (a->fd);
154 close (a->fd);
155 }
156 free (a->name);
157 a->name = NULL;
158 a->fd = -1;
159 a->mode = 0x42;
160 if (a->poslog)
161 free (a->poslog);
162 if (a->sizelog)
163 free (a->sizelog);
164 free (a);
165 }
166
167
168 /* This function tell return the tolerance, that is a number
169 * corresponding to the cost of shake()ing the accused.
170 * It is used by judge().
171 */
172 static double
173 tol_reg (struct accused *a, struct law *l)
174 {
175 assert (a && l);
176 assert (S_ISREG (a->mode));
177 double tol = 1.0;
178 if (a->size < l->smallsize && l->smallsize)
179 tol = l->smallsize_tol;
180 else if (a->size > l->bigsize && l->bigsize)
181 tol = l->bigsize_tol;
182 return tol;
183 }
184
185 /* Return true if the file is fragmented, else false.
186 */
187 static bool
188 judge_reg (struct accused *a, struct law *l)
189 {
190 assert (a && l);
191 assert (S_ISREG (a->mode));
192 double tol = tol_reg (a, l);
193 if (MAX_TOL == tol)
194 return false;
195 if (a->age < (double) l->new * tol)
196 return false;
197 if (a->age > (double) l->old * tol)
198 return true;
199 if (a->fragc > l->maxfragc * tol)
200 return true;
201 if (a->crumbc > l->maxcrumbc * tol)
202 return true;
203 if ((l->maxdeviance) && (a->start) && (a->ideal)
204 && abs ((int) (a->start - a->ideal)) > (uint) l->maxdeviance * tol)
205 return true;
206 return false;
207 }
208
209 /* This function call judge on the list content
210 */
211 static int
212 judge_list (char *restrict * flist, struct law *restrict l)
213 {
214 assert (flist && l);
215 int res = 0; // value returned
216 /* We want "y", to be the file examined, "x" the previous one, and
217 * eventually "z" the next one (to take neighboors in account).
218 */
219 struct accused *x = NULL, *y = NULL, *z = NULL;
220 /* check if list is empty */
221 if (!flist[0])
222 return 0;
223 /* Main loop, read every file and their neighboor
224 * Typically, x:flist[n-1], y: flist[n], z: flist[n+1]
225 */
226 z = investigate (flist[0], l);
227 for (uint n = 0; flist[n]; n++)
228 {
229 /* Do we have a file after y ? */
230 if (z)
231 {
232 close_case (x, l);
233 x = y;
234 y = z;
235 }
236 /* Try to add a file from the list */
237 if (flist[n + 1])
238 {
239 z = investigate (flist[n + 1], l);
240 if (!z)
241 continue; // Try the next file.
242 }
243 else
244 z = NULL;
245 /* Do we actually have a file ? */
246 if (!y)
247 continue;
248 /* Do we know where the file should be ? */
249 {
250 y->ideal = 0;
251 if (y->start)
252 {
253 if (x && x->end && labs (x->atime - y->atime) < MAGICTIME)
254 {
255 if (z && z->start && labs (z->atime - y->atime) < MAGICTIME)
256 y->ideal = (x->end + z->start) / 2;
257 else
258 y->ideal = (x->end + MAGICLEAP);
259 }
260 else if (z && z->start && labs (z->atime - y->atime) < MAGICTIME)
261 y->ideal = (z->start - z->blocks - MAGICLEAP);
262 }
263 }
264 /* judge */
265 if (-1 == judge (y, l))
266 {
267 res = -1;
268 break;
269 }
270 }
271 close_case (x, l);
272 close_case (y, l);
273 close_case (z, l);
274 return res;
275 }
276
277 /* This function call judge on the directory content
278 */
279 static int
280 judge_dir (struct accused *a, struct law *l)
281 {
282 assert (a && a->name && l);
283 assert ((dev_t) - 1 != a->fs);
284 /* check against --one-file-system */
285 if ((dev_t) - 1 != l->kingdom && a->fs != l->kingdom)
286 return 0;
287 else
288 {
289 int res;
290 char **flist = list_dir (a->name, true);
291 if (!flist)
292 {
293 error (0, 0, "%s: list_dir() failed", a->name);
294 return -1;
295 }
296 res = judge_list (flist, l);
297 close_list (flist);
298 return res;
299 }
300 }
301
302 int
303 judge_stdin (struct accused *a, struct law *l)
304 {
305 assert (!a && l);
306 int res;
307 char **flist = list_stdin ();
308 if (!flist)
309 {
310 error (0, 0, "-: list_stdin() failed");
311 return -1;
312 }
313 res = judge_list (flist, l);
314 close_list (flist);
315 return res;
316 }
317
318
319 int
320 judge (struct accused *a, struct law *l)
321 {
322 assert (a && l);
323 if (S_ISLNK (a->mode))
324 return 0;
325 else if (S_ISDIR (a->mode))
326 return judge_dir (a, l);
327 else if (S_ISREG (a->mode) && a->size)
328 {
329 /* Take the lock, it will be released just before returning */
330 if (l->locks && -1 == readlock_file (a->fd, a->name))
331 {
332 error (0, errno, "%s: failed to acquire a lock", a->name);
333 return 0;
334 }
335 /* Check against modification */
336 {
337 struct stat st;
338 if (-1 == fstat (a->fd, &st))
339 {
340 error (0, errno, "%s: lstat() failed", a->name);
341 goto freeall;
342 }
343 if (st.st_blocks * 512 != a->size
344 || st.st_mtime != a->mtime || st.st_mode != a->mode)
345 {
346 error (0, 0, "%s: concurrent access", a->name);
347 goto freeall;
348 }
349 }
350 /* Judge and maybe shake */
351 a->guilty = judge_reg (a, l);
352 if (a->guilty)
353 shake_reg (a, l);
354 /* Unlock */
355 unlock_file (a->fd);
356 /* Show result of investigation, if the file is guilty or if
357 * level of verbosity is greater than 2
358 */
359 if ((a->guilty && l->verbosity) || l->verbosity >= 2)
360 show_reg (a, l);
361 }
362 return a->guilty;
363 freeall:
364 unlock_file (a->fd);
365 return 0;
366 }