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