"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 }