LCOV - code coverage report
Current view: top level - src - shred.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 228 382 59.7 %
Date: 2018-01-30 Functions: 12 14 85.7 %

          Line data    Source code
       1             : /* shred.c - overwrite files and devices to make it harder to recover data
       2             : 
       3             :    Copyright (C) 1999-2007 Free Software Foundation, Inc.
       4             :    Copyright (C) 1997, 1998, 1999 Colin Plumb.
       5             : 
       6             :    This program 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             :    Written by Colin Plumb.  */
      20             : 
      21             : /* TODO:
      22             :    - use consistent non-capitalization in error messages
      23             :    - add standard GNU copyleft comment
      24             : 
      25             :   - Add -r/-R/--recursive
      26             :   - Add -i/--interactive
      27             :   - Reserve -d
      28             :   - Add -L
      29             :   - Add an unlink-all option to emulate rm.
      30             :  */
      31             : 
      32             : /*
      33             :  * Do a more secure overwrite of given files or devices, to make it harder
      34             :  * for even very expensive hardware probing to recover the data.
      35             :  *
      36             :  * Although this process is also known as "wiping", I prefer the longer
      37             :  * name both because I think it is more evocative of what is happening and
      38             :  * because a longer name conveys a more appropriate sense of deliberateness.
      39             :  *
      40             :  * For the theory behind this, see "Secure Deletion of Data from Magnetic
      41             :  * and Solid-State Memory", on line at
      42             :  * http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html
      43             :  *
      44             :  * Just for the record, reversing one or two passes of disk overwrite
      45             :  * is not terribly difficult with hardware help.  Hook up a good-quality
      46             :  * digitizing oscilloscope to the output of the head preamplifier and copy
      47             :  * the high-res digitized data to a computer for some off-line analysis.
      48             :  * Read the "current" data and average all the pulses together to get an
      49             :  * "average" pulse on the disk.  Subtract this average pulse from all of
      50             :  * the actual pulses and you can clearly see the "echo" of the previous
      51             :  * data on the disk.
      52             :  *
      53             :  * Real hard drives have to balance the cost of the media, the head,
      54             :  * and the read circuitry.  They use better-quality media than absolutely
      55             :  * necessary to limit the cost of the read circuitry.  By throwing that
      56             :  * assumption out, and the assumption that you want the data processed
      57             :  * as fast as the hard drive can spin, you can do better.
      58             :  *
      59             :  * If asked to wipe a file, this also unlinks it, renaming it to in a
      60             :  * clever way to try to leave no trace of the original filename.
      61             :  *
      62             :  * This was inspired by a desire to improve on some code titled:
      63             :  * Wipe V1.0-- Overwrite and delete files.  S. 2/3/96
      64             :  * but I've rewritten everything here so completely that no trace of
      65             :  * the original remains.
      66             :  *
      67             :  * Thanks to:
      68             :  * Bob Jenkins, for his good RNG work and patience with the FSF copyright
      69             :  * paperwork.
      70             :  * Jim Meyering, for his work merging this into the GNU fileutils while
      71             :  * still letting me feel a sense of ownership and pride.  Getting me to
      72             :  * tolerate the GNU brace style was quite a feat of diplomacy.
      73             :  * Paul Eggert, for lots of useful discussion and code.  I disagree with
      74             :  * an awful lot of his suggestions, but they're disagreements worth having.
      75             :  *
      76             :  * Things to think about:
      77             :  * - Security: Is there any risk to the race
      78             :  *   between overwriting and unlinking a file?  Will it do anything
      79             :  *   drastically bad if told to attack a named pipe or socket?
      80             :  */
      81             : 
      82             : /* The official name of this program (e.g., no `g' prefix).  */
      83             : #define PROGRAM_NAME "shred"
      84             : 
      85             : #define AUTHORS "Colin Plumb"
      86             : 
      87             : #include <config.h>
      88             : 
      89             : #include <getopt.h>
      90             : #include <stdio.h>
      91             : #include <assert.h>
      92             : #include <setjmp.h>
      93             : #include <sys/types.h>
      94             : 
      95             : #include "system.h"
      96             : #include "xstrtol.h"
      97             : #include "error.h"
      98             : #include "fcntl--.h"
      99             : #include "human.h"
     100             : #include "inttostr.h"
     101             : #include "quotearg.h"         /* For quotearg_colon */
     102             : #include "randint.h"
     103             : #include "randread.h"
     104             : 
     105             : /* Default number of times to overwrite.  */
     106             : enum { DEFAULT_PASSES = 25 };
     107             : 
     108             : /* How many seconds to wait before checking whether to output another
     109             :    verbose output line.  */
     110             : enum { VERBOSE_UPDATE = 5 };
     111             : 
     112             : /* Sector size and corresponding mask, for recovering after write failures.
     113             :    The size must be a power of 2.  */
     114             : enum { SECTOR_SIZE = 512 };
     115             : enum { SECTOR_MASK = SECTOR_SIZE - 1 };
     116             : verify (0 < SECTOR_SIZE && (SECTOR_SIZE & SECTOR_MASK) == 0);
     117             : 
     118             : struct Options
     119             : {
     120             :   bool force;           /* -f flag: chmod files if necessary */
     121             :   size_t n_iterations;  /* -n flag: Number of iterations */
     122             :   off_t size;           /* -s flag: size of file */
     123             :   bool remove_file;     /* -u flag: remove file after shredding */
     124             :   bool verbose;         /* -v flag: Print progress */
     125             :   bool exact;           /* -x flag: Do not round up file size */
     126             :   bool zero_fill;       /* -z flag: Add a final zero pass */
     127             : };
     128             : 
     129             : /* For long options that have no equivalent short option, use a
     130             :    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
     131             : enum
     132             : {
     133             :   RANDOM_SOURCE_OPTION = CHAR_MAX + 1
     134             : };
     135             : 
     136             : static struct option const long_opts[] =
     137             : {
     138             :   {"exact", no_argument, NULL, 'x'},
     139             :   {"force", no_argument, NULL, 'f'},
     140             :   {"iterations", required_argument, NULL, 'n'},
     141             :   {"size", required_argument, NULL, 's'},
     142             :   {"random-source", required_argument, NULL, RANDOM_SOURCE_OPTION},
     143             :   {"remove", no_argument, NULL, 'u'},
     144             :   {"verbose", no_argument, NULL, 'v'},
     145             :   {"zero", no_argument, NULL, 'z'},
     146             :   {GETOPT_HELP_OPTION_DECL},
     147             :   {GETOPT_VERSION_OPTION_DECL},
     148             :   {NULL, 0, NULL, 0}
     149             : };
     150             : 
     151             : /* Global variable for error printing purposes */
     152             : char const *program_name; /* Initialized before any possible use */
     153             : 
     154             : void
     155          30 : usage (int status)
     156             : {
     157          30 :   if (status != EXIT_SUCCESS)
     158          29 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
     159             :              program_name);
     160             :   else
     161             :     {
     162           1 :       printf (_("Usage: %s [OPTIONS] FILE [...]\n"), program_name);
     163           1 :       fputs (_("\
     164             : Overwrite the specified FILE(s) repeatedly, in order to make it harder\n\
     165             : for even very expensive hardware probing to recover the data.\n\
     166             : \n\
     167             : "), stdout);
     168           1 :       fputs (_("\
     169             : Mandatory arguments to long options are mandatory for short options too.\n\
     170             : "), stdout);
     171           1 :       printf (_("\
     172             :   -f, --force    change permissions to allow writing if necessary\n\
     173             :   -n, --iterations=N  Overwrite N times instead of the default (%d)\n\
     174             :       --random-source=FILE  get random bytes from FILE (default /dev/urandom)\n\
     175             :   -s, --size=N   shred this many bytes (suffixes like K, M, G accepted)\n\
     176             : "), DEFAULT_PASSES);
     177           1 :       fputs (_("\
     178             :   -u, --remove   truncate and remove file after overwriting\n\
     179             :   -v, --verbose  show progress\n\
     180             :   -x, --exact    do not round file sizes up to the next full block;\n\
     181             :                    this is the default for non-regular files\n\
     182             :   -z, --zero     add a final overwrite with zeros to hide shredding\n\
     183             : "), stdout);
     184           1 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     185           1 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     186           1 :       fputs (_("\
     187             : \n\
     188             : If FILE is -, shred standard output.\n\
     189             : \n\
     190             : Delete FILE(s) if --remove (-u) is specified.  The default is not to remove\n\
     191             : the files because it is common to operate on device files like /dev/hda,\n\
     192             : and those files usually should not be removed.  When operating on regular\n\
     193             : files, most people use the --remove option.\n\
     194             : \n\
     195             : "), stdout);
     196           1 :       fputs (_("\
     197             : CAUTION: Note that shred relies on a very important assumption:\n\
     198             : that the file system overwrites data in place.  This is the traditional\n\
     199             : way to do things, but many modern file system designs do not satisfy this\n\
     200             : assumption.  The following are examples of file systems on which shred is\n\
     201             : not effective, or is not guaranteed to be effective in all file system modes:\n\
     202             : \n\
     203             : "), stdout);
     204           1 :       fputs (_("\
     205             : * log-structured or journaled file systems, such as those supplied with\n\
     206             : AIX and Solaris (and JFS, ReiserFS, XFS, Ext3, etc.)\n\
     207             : \n\
     208             : * file systems that write redundant data and carry on even if some writes\n\
     209             : fail, such as RAID-based file systems\n\
     210             : \n\
     211             : * file systems that make snapshots, such as Network Appliance's NFS server\n\
     212             : \n\
     213             : "), stdout);
     214           1 :       fputs (_("\
     215             : * file systems that cache in temporary locations, such as NFS\n\
     216             : version 3 clients\n\
     217             : \n\
     218             : * compressed file systems\n\
     219             : \n\
     220             : "), stdout);
     221           1 :       fputs (_("\
     222             : In the case of ext3 file systems, the above disclaimer applies\n\
     223             : (and shred is thus of limited effectiveness) only in data=journal mode,\n\
     224             : which journals file data in addition to just metadata.  In both the\n\
     225             : data=ordered (default) and data=writeback modes, shred works as usual.\n\
     226             : Ext3 journaling modes can be changed by adding the data=something option\n\
     227             : to the mount options for a particular file system in the /etc/fstab file,\n\
     228             : as documented in the mount man page (man mount).\n\
     229             : \n\
     230             : "), stdout);
     231           1 :       fputs (_("\
     232             : In addition, file system backups and remote mirrors may contain copies\n\
     233             : of the file that cannot be removed, and that will allow a shredded file\n\
     234             : to be recovered later.\n\
     235             : "), stdout);
     236           1 :       emit_bug_reporting_address ();
     237             :     }
     238          30 :   exit (status);
     239             : }
     240             : 
     241             : 
     242             : /*
     243             :  * Fill a buffer with a fixed pattern.
     244             :  *
     245             :  * The buffer must be at least 3 bytes long, even if
     246             :  * size is less.  Larger sizes are filled exactly.
     247             :  */
     248             : static void
     249         484 : fillpattern (int type, unsigned char *r, size_t size)
     250             : {
     251             :   size_t i;
     252         484 :   unsigned int bits = type & 0xfff;
     253             : 
     254         484 :   bits |= bits << 12;
     255         484 :   r[0] = (bits >> 4) & 255;
     256         484 :   r[1] = (bits >> 8) & 255;
     257         484 :   r[2] = bits & 255;
     258        5280 :   for (i = 3; i < size / 2; i *= 2)
     259        4796 :     memcpy (r + i, r, i);
     260         484 :   if (i < size)
     261         484 :     memcpy (r + i, r, size - i);
     262             : 
     263             :   /* Invert the first bit of every sector. */
     264         484 :   if (type & 0x1000)
     265           0 :     for (i = 0; i < size; i += SECTOR_SIZE)
     266           0 :       r[i] ^= 0x80;
     267         484 : }
     268             : 
     269             : /*
     270             :  * Generate a 6-character (+ nul) pass name string
     271             :  * FIXME: allow translation of "random".
     272             :  */
     273             : #define PASS_NAME_SIZE 7
     274             : static void
     275         550 : passname (unsigned char const *data, char name[PASS_NAME_SIZE])
     276             : {
     277         550 :   if (data)
     278         484 :     sprintf (name, "%02x%02x%02x", data[0], data[1], data[2]);
     279             :   else
     280          66 :     memcpy (name, "random", PASS_NAME_SIZE);
     281         550 : }
     282             : 
     283             : /* Request that all data for FD be transferred to the corresponding
     284             :    storage device.  QNAME is the file name (quoted for colons).
     285             :    Report any errors found.  Return 0 on success, -1
     286             :    (setting errno) on failure.  It is not an error if fdatasync and/or
     287             :    fsync is not supported for this file, or if the file is not a
     288             :    writable file descriptor.  */
     289             : static int
     290         550 : dosync (int fd, char const *qname)
     291             : {
     292             :   int err;
     293             : 
     294             : #if HAVE_FDATASYNC
     295         550 :   if (fdatasync (fd) == 0)
     296         550 :     return 0;
     297           0 :   err = errno;
     298           0 :   if (err != EINVAL && err != EBADF)
     299             :     {
     300           0 :       error (0, err, _("%s: fdatasync failed"), qname);
     301           0 :       errno = err;
     302           0 :       return -1;
     303             :     }
     304             : #endif
     305             : 
     306           0 :   if (fsync (fd) == 0)
     307           0 :     return 0;
     308           0 :   err = errno;
     309           0 :   if (err != EINVAL && err != EBADF)
     310             :     {
     311           0 :       error (0, err, _("%s: fsync failed"), qname);
     312           0 :       errno = err;
     313           0 :       return -1;
     314             :     }
     315             : 
     316           0 :   sync ();
     317           0 :   return 0;
     318             : }
     319             : 
     320             : /* Turn on or off direct I/O mode for file descriptor FD, if possible.
     321             :    Try to turn it on if ENABLE is true.  Otherwise, try to turn it off.  */
     322             : static void
     323          22 : direct_mode (int fd, bool enable)
     324             : {
     325             :   if (O_DIRECT)
     326             :     {
     327          22 :       int fd_flags = fcntl (fd, F_GETFL);
     328          22 :       if (0 < fd_flags)
     329             :         {
     330          22 :           int new_flags = (enable
     331             :                            ? (fd_flags | O_DIRECT)
     332          22 :                            : (fd_flags & ~O_DIRECT));
     333          22 :           if (new_flags != fd_flags)
     334          20 :             fcntl (fd, F_SETFL, new_flags);
     335             :         }
     336             :     }
     337             : 
     338             : #if HAVE_DIRECTIO && defined DIRECTIO_ON && defined DIRECTIO_OFF
     339             :   /* This is Solaris-specific.  See the following for details:
     340             :      http://docs.sun.com/db/doc/816-0213/6m6ne37so?q=directio&a=view  */
     341             :   directio (fd, enable ? DIRECTIO_ON : DIRECTIO_OFF);
     342             : #endif
     343          22 : }
     344             : 
     345             : /*
     346             :  * Do pass number k of n, writing "size" bytes of the given pattern "type"
     347             :  * to the file descriptor fd.   Qname, k and n are passed in only for verbose
     348             :  * progress message purposes.  If n == 0, no progress messages are printed.
     349             :  *
     350             :  * If *sizep == -1, the size is unknown, and it will be filled in as soon
     351             :  * as writing fails.
     352             :  *
     353             :  * Return 1 on write error, -1 on other error, 0 on success.
     354             :  */
     355             : static int
     356         550 : dopass (int fd, char const *qname, off_t *sizep, int type,
     357             :         struct randread_source *s, unsigned long int k, unsigned long int n)
     358             : {
     359         550 :   off_t size = *sizep;
     360             :   off_t offset;                 /* Current file posiiton */
     361             :   time_t thresh IF_LINT (= 0);  /* Time to maybe print next status update */
     362         550 :   time_t now = 0;               /* Current time */
     363             :   size_t lim;                   /* Amount of data to try writing */
     364             :   size_t soff;                  /* Offset into buffer for next write */
     365             :   ssize_t ssize;                /* Return value from write */
     366             : 
     367             :   /* Fill pattern buffer.  Aligning it to a 32-bit boundary speeds up randread
     368             :      in some cases.  */
     369             :   typedef uint32_t fill_pattern_buffer[3 * 1024];
     370             :   union
     371             :   {
     372             :     fill_pattern_buffer buffer;
     373             :     char c[sizeof (fill_pattern_buffer)];
     374             :     unsigned char u[sizeof (fill_pattern_buffer)];
     375             :   } r;
     376             : 
     377         550 :   off_t sizeof_r = sizeof r;
     378             :   char pass_string[PASS_NAME_SIZE];     /* Name of current pass */
     379         550 :   bool write_error = false;
     380         550 :   bool first_write = true;
     381             : 
     382             :   /* Printable previous offset into the file */
     383             :   char previous_offset_buf[LONGEST_HUMAN_READABLE + 1];
     384             :   char const *previous_human_offset IF_LINT (= 0);
     385             : 
     386         550 :   if (lseek (fd, 0, SEEK_SET) == -1)
     387             :     {
     388           0 :       error (0, errno, _("%s: cannot rewind"), qname);
     389           0 :       return -1;
     390             :     }
     391             : 
     392             :   /* Constant fill patterns need only be set up once. */
     393         550 :   if (type >= 0)
     394             :     {
     395         484 :       lim = (0 <= size && size < sizeof_r ? size : sizeof r);
     396         484 :       fillpattern (type, r.u, lim);
     397         484 :       passname (r.u, pass_string);
     398             :     }
     399             :   else
     400             :     {
     401          66 :       passname (0, pass_string);
     402             :     }
     403             : 
     404             :   /* Set position if first status update */
     405         550 :   if (n)
     406             :     {
     407         125 :       error (0, 0, _("%s: pass %lu/%lu (%s)..."), qname, k, n, pass_string);
     408         125 :       thresh = time (NULL) + VERBOSE_UPDATE;
     409         125 :       previous_human_offset = "";
     410             :     }
     411             : 
     412         550 :   offset = 0;
     413             :   for (;;)
     414             :     {
     415             :       /* How much to write this time? */
     416        1650 :       lim = sizeof r;
     417        1100 :       if (0 <= size && size - offset < sizeof_r)
     418             :         {
     419        1100 :           if (size < offset)
     420           0 :             break;
     421        1100 :           lim = size - offset;
     422        1100 :           if (!lim)
     423         550 :             break;
     424             :         }
     425         550 :       if (type < 0)
     426          66 :         randread (s, &r, lim);
     427             :       /* Loop to retry partial writes. */
     428        1100 :       for (soff = 0; soff < lim; soff += ssize, first_write = false)
     429             :         {
     430         550 :           ssize = write (fd, r.c + soff, lim - soff);
     431         550 :           if (ssize <= 0)
     432             :             {
     433           0 :               if (size < 0 && (ssize == 0 || errno == ENOSPC))
     434             :                 {
     435             :                   /* Ah, we have found the end of the file */
     436           0 :                   *sizep = size = offset + soff;
     437           0 :                   break;
     438             :                 }
     439             :               else
     440             :                 {
     441           0 :                   int errnum = errno;
     442             :                   char buf[INT_BUFSIZE_BOUND (uintmax_t)];
     443             : 
     444             :                   /* If the first write of the first pass for a given file
     445             :                      has just failed with EINVAL, turn off direct mode I/O
     446             :                      and try again.  This works around a bug in linux-2.4
     447             :                      whereby opening with O_DIRECT would succeed for some
     448             :                      file system types (e.g., ext3), but any attempt to
     449             :                      access a file through the resulting descriptor would
     450             :                      fail with EINVAL.  */
     451           0 :                   if (k == 1 && first_write && errno == EINVAL)
     452             :                     {
     453           0 :                       direct_mode (fd, false);
     454           0 :                       ssize = 0;
     455           0 :                       continue;
     456             :                     }
     457           0 :                   error (0, errnum, _("%s: error writing at offset %s"),
     458             :                          qname, umaxtostr (offset + soff, buf));
     459             : 
     460             :                   /* 'shred' is often used on bad media, before throwing it
     461             :                      out.  Thus, it shouldn't give up on bad blocks.  This
     462             :                      code works because lim is always a multiple of
     463             :                      SECTOR_SIZE, except at the end.  */
     464             :                   verify (sizeof r % SECTOR_SIZE == 0);
     465           0 :                   if (errnum == EIO && 0 <= size && (soff | SECTOR_MASK) < lim)
     466             :                     {
     467           0 :                       size_t soff1 = (soff | SECTOR_MASK) + 1;
     468           0 :                       if (lseek (fd, offset + soff1, SEEK_SET) != -1)
     469             :                         {
     470             :                           /* Arrange to skip this block. */
     471           0 :                           ssize = soff1 - soff;
     472           0 :                           write_error = true;
     473           0 :                           continue;
     474             :                         }
     475           0 :                       error (0, errno, _("%s: lseek failed"), qname);
     476             :                     }
     477           0 :                   return -1;
     478             :                 }
     479             :             }
     480             :         }
     481             : 
     482             :       /* Okay, we have written "soff" bytes. */
     483             : 
     484         550 :       if (offset + soff < offset)
     485             :         {
     486           0 :           error (0, 0, _("%s: file too large"), qname);
     487           0 :           return -1;
     488             :         }
     489             : 
     490         550 :       offset += soff;
     491             : 
     492             :       /* Time to print progress? */
     493         550 :       if (n
     494         125 :           && ((offset == size && *previous_human_offset)
     495         125 :               || thresh <= (now = time (NULL))))
     496             :         {
     497             :           char offset_buf[LONGEST_HUMAN_READABLE + 1];
     498             :           char size_buf[LONGEST_HUMAN_READABLE + 1];
     499           0 :           int human_progress_opts = (human_autoscale | human_SI
     500             :                                      | human_base_1024 | human_B);
     501           0 :           char const *human_offset
     502           0 :             = human_readable (offset, offset_buf,
     503             :                               human_floor | human_progress_opts, 1, 1);
     504             : 
     505           0 :           if (offset == size
     506           0 :               || !STREQ (previous_human_offset, human_offset))
     507             :             {
     508           0 :               if (size < 0)
     509           0 :                 error (0, 0, _("%s: pass %lu/%lu (%s)...%s"),
     510             :                        qname, k, n, pass_string, human_offset);
     511             :               else
     512             :                 {
     513           0 :                   uintmax_t off = offset;
     514           0 :                   int percent = (size == 0
     515             :                                  ? 100
     516             :                                  : (off <= TYPE_MAXIMUM (uintmax_t) / 100
     517           0 :                                     ? off * 100 / size
     518           0 :                                     : off / (size / 100)));
     519           0 :                   char const *human_size
     520           0 :                     = human_readable (size, size_buf,
     521             :                                       human_ceiling | human_progress_opts,
     522             :                                       1, 1);
     523           0 :                   if (offset == size)
     524           0 :                     human_offset = human_size;
     525           0 :                   error (0, 0, _("%s: pass %lu/%lu (%s)...%s/%s %d%%"),
     526             :                          qname, k, n, pass_string, human_offset, human_size,
     527             :                          percent);
     528             :                 }
     529             : 
     530           0 :               strcpy (previous_offset_buf, human_offset);
     531           0 :               previous_human_offset = previous_offset_buf;
     532           0 :               thresh = now + VERBOSE_UPDATE;
     533             : 
     534             :               /*
     535             :                * Force periodic syncs to keep displayed progress accurate
     536             :                * FIXME: Should these be present even if -v is not enabled,
     537             :                * to keep the buffer cache from filling with dirty pages?
     538             :                * It's a common problem with programs that do lots of writes,
     539             :                * like mkfs.
     540             :                */
     541           0 :               if (dosync (fd, qname) != 0)
     542             :                 {
     543           0 :                   if (errno != EIO)
     544           0 :                     return -1;
     545           0 :                   write_error = true;
     546             :                 }
     547             :             }
     548             :         }
     549             :     }
     550             : 
     551             :   /* Force what we just wrote to hit the media. */
     552         550 :   if (dosync (fd, qname) != 0)
     553             :     {
     554           0 :       if (errno != EIO)
     555           0 :         return -1;
     556           0 :       write_error = true;
     557             :     }
     558             : 
     559         550 :   return write_error;
     560             : }
     561             : 
     562             : /*
     563             :  * The passes start and end with a random pass, and the passes in between
     564             :  * are done in random order.  The idea is to deprive someone trying to
     565             :  * reverse the process of knowledge of the overwrite patterns, so they
     566             :  * have the additional step of figuring out what was done to the disk
     567             :  * before they can try to reverse or cancel it.
     568             :  *
     569             :  * First, all possible 1-bit patterns.  There are two of them.
     570             :  * Then, all possible 2-bit patterns.  There are four, but the two
     571             :  * which are also 1-bit patterns can be omitted.
     572             :  * Then, all possible 3-bit patterns.  Likewise, 8-2 = 6.
     573             :  * Then, all possible 4-bit patterns.  16-4 = 12.
     574             :  *
     575             :  * The basic passes are:
     576             :  * 1-bit: 0x000, 0xFFF
     577             :  * 2-bit: 0x555, 0xAAA
     578             :  * 3-bit: 0x249, 0x492, 0x924, 0x6DB, 0xB6D, 0xDB6 (+ 1-bit)
     579             :  *        100100100100         110110110110
     580             :  *           9   2   4            D   B   6
     581             :  * 4-bit: 0x111, 0x222, 0x333, 0x444, 0x666, 0x777,
     582             :  *        0x888, 0x999, 0xBBB, 0xCCC, 0xDDD, 0xEEE (+ 1-bit, 2-bit)
     583             :  * Adding three random passes at the beginning, middle and end
     584             :  * produces the default 25-pass structure.
     585             :  *
     586             :  * The next extension would be to 5-bit and 6-bit patterns.
     587             :  * There are 30 uncovered 5-bit patterns and 64-8-2 = 46 uncovered
     588             :  * 6-bit patterns, so they would increase the time required
     589             :  * significantly.  4-bit patterns are enough for most purposes.
     590             :  *
     591             :  * The main gotcha is that this would require a trickier encoding,
     592             :  * since lcm(2,3,4) = 12 bits is easy to fit into an int, but
     593             :  * lcm(2,3,4,5) = 60 bits is not.
     594             :  *
     595             :  * One extension that is included is to complement the first bit in each
     596             :  * 512-byte block, to alter the phase of the encoded data in the more
     597             :  * complex encodings.  This doesn't apply to MFM, so the 1-bit patterns
     598             :  * are considered part of the 3-bit ones and the 2-bit patterns are
     599             :  * considered part of the 4-bit patterns.
     600             :  *
     601             :  *
     602             :  * How does the generalization to variable numbers of passes work?
     603             :  *
     604             :  * Here's how...
     605             :  * Have an ordered list of groups of passes.  Each group is a set.
     606             :  * Take as many groups as will fit, plus a random subset of the
     607             :  * last partial group, and place them into the passes list.
     608             :  * Then shuffle the passes list into random order and use that.
     609             :  *
     610             :  * One extra detail: if we can't include a large enough fraction of the
     611             :  * last group to be interesting, then just substitute random passes.
     612             :  *
     613             :  * If you want more passes than the entire list of groups can
     614             :  * provide, just start repeating from the beginning of the list.
     615             :  */
     616             : static int const
     617             :   patterns[] =
     618             : {
     619             :   -2,                           /* 2 random passes */
     620             :   2, 0x000, 0xFFF,              /* 1-bit */
     621             :   2, 0x555, 0xAAA,              /* 2-bit */
     622             :   -1,                           /* 1 random pass */
     623             :   6, 0x249, 0x492, 0x6DB, 0x924, 0xB6D, 0xDB6,  /* 3-bit */
     624             :   12, 0x111, 0x222, 0x333, 0x444, 0x666, 0x777,
     625             :   0x888, 0x999, 0xBBB, 0xCCC, 0xDDD, 0xEEE,     /* 4-bit */
     626             :   -1,                           /* 1 random pass */
     627             :         /* The following patterns have the frst bit per block flipped */
     628             :   8, 0x1000, 0x1249, 0x1492, 0x16DB, 0x1924, 0x1B6D, 0x1DB6, 0x1FFF,
     629             :   14, 0x1111, 0x1222, 0x1333, 0x1444, 0x1555, 0x1666, 0x1777,
     630             :   0x1888, 0x1999, 0x1AAA, 0x1BBB, 0x1CCC, 0x1DDD, 0x1EEE,
     631             :   -1,                           /* 1 random pass */
     632             :   0                             /* End */
     633             : };
     634             : 
     635             : /*
     636             :  * Generate a random wiping pass pattern with num passes.
     637             :  * This is a two-stage process.  First, the passes to include
     638             :  * are chosen, and then they are shuffled into the desired
     639             :  * order.
     640             :  */
     641             : static void
     642          22 : genpattern (int *dest, size_t num, struct randint_source *s)
     643             : {
     644             :   size_t randpasses;
     645             :   int const *p;
     646             :   int *d;
     647             :   size_t n;
     648             :   size_t accum, top, swap;
     649             :   int k;
     650             : 
     651          22 :   if (!num)
     652           0 :     return;
     653             : 
     654             :   /* Stage 1: choose the passes to use */
     655          22 :   p = patterns;
     656          22 :   randpasses = 0;
     657          22 :   d = dest;                     /* Destination for generated pass list */
     658          22 :   n = num;                      /* Passes remaining to fill */
     659             : 
     660             :   for (;;)
     661             :     {
     662         286 :       k = *p++;                 /* Block descriptor word */
     663         154 :       if (!k)
     664             :         {                       /* Loop back to the beginning */
     665           0 :           p = patterns;
     666             :         }
     667         154 :       else if (k < 0)
     668             :         {                       /* -k random passes */
     669          66 :           k = -k;
     670          66 :           if ((size_t) k >= n)
     671             :             {
     672          22 :               randpasses += n;
     673          22 :               n = 0;
     674          22 :               break;
     675             :             }
     676          44 :           randpasses += k;
     677          44 :           n -= k;
     678             :         }
     679          88 :       else if ((size_t) k <= n)
     680             :         {                       /* Full block of patterns */
     681          88 :           memcpy (d, p, k * sizeof (int));
     682          88 :           p += k;
     683          88 :           d += k;
     684          88 :           n -= k;
     685             :         }
     686           0 :       else if (n < 2 || 3 * n < (size_t) k)
     687             :         {                       /* Finish with random */
     688           0 :           randpasses += n;
     689           0 :           break;
     690             :         }
     691             :       else
     692             :         {                       /* Pad out with k of the n available */
     693             :           do
     694             :             {
     695           0 :               if (n == (size_t) k || randint_choose (s, k) < n)
     696             :                 {
     697           0 :                   *d++ = *p;
     698           0 :                   n--;
     699             :                 }
     700           0 :               p++;
     701             :             }
     702           0 :           while (n);
     703           0 :           break;
     704             :         }
     705             :     }
     706          22 :   top = num - randpasses;       /* Top of initialized data */
     707             :   /* assert (d == dest+top); */
     708             : 
     709             :   /*
     710             :    * We now have fixed patterns in the dest buffer up to
     711             :    * "top", and we need to scramble them, with "randpasses"
     712             :    * random passes evenly spaced among them.
     713             :    *
     714             :    * We want one at the beginning, one at the end, and
     715             :    * evenly spaced in between.  To do this, we basically
     716             :    * use Bresenham's line draw (a.k.a DDA) algorithm
     717             :    * to draw a line with slope (randpasses-1)/(num-1).
     718             :    * (We use a positive accumulator and count down to
     719             :    * do this.)
     720             :    *
     721             :    * So for each desired output value, we do the following:
     722             :    * - If it should be a random pass, copy the pass type
     723             :    *   to top++, out of the way of the other passes, and
     724             :    *   set the current pass to -1 (random).
     725             :    * - If it should be a normal pattern pass, choose an
     726             :    *   entry at random between here and top-1 (inclusive)
     727             :    *   and swap the current entry with that one.
     728             :    */
     729          22 :   randpasses--;                 /* To speed up later math */
     730          22 :   accum = randpasses;           /* Bresenham DDA accumulator */
     731         572 :   for (n = 0; n < num; n++)
     732             :     {
     733         550 :       if (accum <= randpasses)
     734             :         {
     735          66 :           accum += num - 1;
     736          66 :           dest[top++] = dest[n];
     737          66 :           dest[n] = -1;
     738             :         }
     739             :       else
     740             :         {
     741         484 :           swap = n + randint_choose (s, top - n);
     742         484 :           k = dest[n];
     743         484 :           dest[n] = dest[swap];
     744         484 :           dest[swap] = k;
     745             :         }
     746         550 :       accum -= randpasses;
     747             :     }
     748             :   /* assert (top == num); */
     749             : }
     750             : 
     751             : /*
     752             :  * The core routine to actually do the work.  This overwrites the first
     753             :  * size bytes of the given fd.  Return true if successful.
     754             :  */
     755             : static bool
     756          63 : do_wipefd (int fd, char const *qname, struct randint_source *s,
     757             :            struct Options const *flags)
     758             : {
     759             :   size_t i;
     760             :   struct stat st;
     761             :   off_t size;                   /* Size to write, size to read */
     762             :   unsigned long int n;          /* Number of passes for printing purposes */
     763             :   int *passarray;
     764          63 :   bool ok = true;
     765             :   struct randread_source *rs;
     766             : 
     767          63 :   n = 0;                /* dopass takes n -- 0 to mean "don't print progress" */
     768          63 :   if (flags->verbose)
     769           7 :     n = flags->n_iterations + flags->zero_fill;
     770             : 
     771          63 :   if (fstat (fd, &st))
     772             :     {
     773           0 :       error (0, errno, _("%s: fstat failed"), qname);
     774           0 :       return false;
     775             :     }
     776             : 
     777             :   /* If we know that we can't possibly shred the file, give up now.
     778             :      Otherwise, we may go into a infinite loop writing data before we
     779             :      find that we can't rewind the device.  */
     780          63 :   if ((S_ISCHR (st.st_mode) && isatty (fd))
     781          25 :       || S_ISFIFO (st.st_mode)
     782          22 :       || S_ISSOCK (st.st_mode))
     783             :     {
     784          41 :       error (0, 0, _("%s: invalid file type"), qname);
     785          41 :       return false;
     786             :     }
     787             : 
     788          22 :   direct_mode (fd, true);
     789             : 
     790             :   /* Allocate pass array */
     791          22 :   passarray = xnmalloc (flags->n_iterations, sizeof *passarray);
     792             : 
     793          22 :   size = flags->size;
     794          22 :   if (size == -1)
     795             :     {
     796             :       /* Accept a length of zero only if it's a regular file.
     797             :          For any other type of file, try to get the size another way.  */
     798          22 :       if (S_ISREG (st.st_mode))
     799             :         {
     800          22 :           size = st.st_size;
     801          22 :           if (size < 0)
     802             :             {
     803           0 :               error (0, 0, _("%s: file has negative size"), qname);
     804           0 :               return false;
     805             :             }
     806             :         }
     807             :       else
     808             :         {
     809           0 :           size = lseek (fd, 0, SEEK_END);
     810           0 :           if (size <= 0)
     811             :             {
     812             :               /* We are unable to determine the length, up front.
     813             :                  Let dopass do that as part of its first iteration.  */
     814           0 :               size = -1;
     815             :             }
     816             :         }
     817             : 
     818             :       /* Allow `rounding up' only for regular files.  */
     819          22 :       if (0 <= size && !(flags->exact) && S_ISREG (st.st_mode))
     820             :         {
     821          21 :           size += ST_BLKSIZE (st) - 1 - (size - 1) % ST_BLKSIZE (st);
     822             : 
     823             :           /* If in rounding up, we've just overflowed, use the maximum.  */
     824          21 :           if (size < 0)
     825           0 :             size = TYPE_MAXIMUM (off_t);
     826             :         }
     827             :     }
     828             : 
     829             :   /* Schedule the passes in random order. */
     830          22 :   genpattern (passarray, flags->n_iterations, s);
     831             : 
     832          22 :   rs = randint_get_source (s);
     833             : 
     834             :   /* Do the work */
     835         572 :   for (i = 0; i < flags->n_iterations; i++)
     836             :     {
     837         550 :       int err = dopass (fd, qname, &size, passarray[i], rs, i + 1, n);
     838         550 :       if (err)
     839             :         {
     840           0 :           if (err < 0)
     841             :             {
     842           0 :               memset (passarray, 0, flags->n_iterations * sizeof (int));
     843           0 :               free (passarray);
     844           0 :               return false;
     845             :             }
     846           0 :           ok = false;
     847             :         }
     848             :     }
     849             : 
     850          22 :   memset (passarray, 0, flags->n_iterations * sizeof (int));
     851          22 :   free (passarray);
     852             : 
     853          22 :   if (flags->zero_fill)
     854             :     {
     855           0 :       int err = dopass (fd, qname, &size, 0, rs, flags->n_iterations + 1, n);
     856           0 :       if (err)
     857             :         {
     858           0 :           if (err < 0)
     859           0 :             return false;
     860           0 :           ok = false;
     861             :         }
     862             :     }
     863             : 
     864             :   /* Okay, now deallocate the data.  The effect of ftruncate on
     865             :      non-regular files is unspecified, so don't worry about any
     866             :      errors reported for them.  */
     867          22 :   if (flags->remove_file && ftruncate (fd, 0) != 0
     868           0 :       && S_ISREG (st.st_mode))
     869             :     {
     870           0 :       error (0, errno, _("%s: error truncating"), qname);
     871           0 :       return false;
     872             :     }
     873             : 
     874          22 :   return ok;
     875             : }
     876             : 
     877             : /* A wrapper with a little more checking for fds on the command line */
     878             : static bool
     879          56 : wipefd (int fd, char const *qname, struct randint_source *s,
     880             :         struct Options const *flags)
     881             : {
     882          56 :   int fd_flags = fcntl (fd, F_GETFL);
     883             : 
     884          56 :   if (fd_flags < 0)
     885             :     {
     886           0 :       error (0, errno, _("%s: fcntl failed"), qname);
     887           0 :       return false;
     888             :     }
     889          56 :   if (fd_flags & O_APPEND)
     890             :     {
     891           0 :       error (0, 0, _("%s: cannot shred append-only file descriptor"), qname);
     892           0 :       return false;
     893             :     }
     894          56 :   return do_wipefd (fd, qname, s, flags);
     895             : }
     896             : 
     897             : /* --- Name-wiping code --- */
     898             : 
     899             : /* Characters allowed in a file name - a safe universal set.  */
     900             : static char const nameset[] =
     901             : "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
     902             : 
     903             : /* Increment NAME (with LEN bytes).  NAME must be a big-endian base N
     904             :    number with the digits taken from nameset.  Return true if
     905             :    successful if not (because NAME already has the greatest possible
     906             :    value.  */
     907             : 
     908             : static bool
     909           0 : incname (char *name, size_t len)
     910             : {
     911           0 :   while (len--)
     912             :     {
     913           0 :       char const *p = strchr (nameset, name[len]);
     914             : 
     915             :       /* If this character has a successor, use it.  */
     916           0 :       if (p[1])
     917             :         {
     918           0 :           name[len] = p[1];
     919           0 :           return true;
     920             :         }
     921             : 
     922             :       /* Otherwise, set this digit to 0 and increment the prefix.  */
     923           0 :       name[len] = nameset[0];
     924             :     }
     925             : 
     926           0 :   return false;
     927             : }
     928             : 
     929             : /*
     930             :  * Repeatedly rename a file with shorter and shorter names,
     931             :  * to obliterate all traces of the file name on any system that
     932             :  * adds a trailing delimiter to on-disk file names and reuses
     933             :  * the same directory slot.  Finally, unlink it.
     934             :  * The passed-in filename is modified in place to the new filename.
     935             :  * (Which is unlinked if this function succeeds, but is still present if
     936             :  * it fails for some reason.)
     937             :  *
     938             :  * The main loop is written carefully to not get stuck if all possible
     939             :  * names of a given length are occupied.  It counts down the length from
     940             :  * the original to 0.  While the length is non-zero, it tries to find an
     941             :  * unused file name of the given length.  It continues until either the
     942             :  * name is available and the rename succeeds, or it runs out of names
     943             :  * to try (incname wraps and returns 1).  Finally, it unlinks the file.
     944             :  *
     945             :  * The unlink is Unix-specific, as ANSI-standard remove has more
     946             :  * portability problems with C libraries making it "safe".  rename
     947             :  * is ANSI-standard.
     948             :  *
     949             :  * To force the directory data out, we try to open the directory and
     950             :  * invoke fdatasync and/or fsync on it.  This is non-standard, so don't
     951             :  * insist that it works: just fall back to a global sync in that case.
     952             :  * This is fairly significantly Unix-specific.  Of course, on any
     953             :  * file system with synchronous metadata updates, this is unnecessary.
     954             :  */
     955             : static bool
     956           0 : wipename (char *oldname, char const *qoldname, struct Options const *flags)
     957             : {
     958           0 :   char *newname = xstrdup (oldname);
     959           0 :   char *base = last_component (newname);
     960           0 :   size_t len = base_len (base);
     961           0 :   char *dir = dir_name (newname);
     962           0 :   char *qdir = xstrdup (quotearg_colon (dir));
     963           0 :   bool first = true;
     964           0 :   bool ok = true;
     965             : 
     966           0 :   int dir_fd = open (dir, O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
     967             : 
     968           0 :   if (flags->verbose)
     969           0 :     error (0, 0, _("%s: removing"), qoldname);
     970             : 
     971           0 :   while (len)
     972             :     {
     973           0 :       memset (base, nameset[0], len);
     974           0 :       base[len] = 0;
     975             :       do
     976             :         {
     977             :           struct stat st;
     978           0 :           if (lstat (newname, &st) < 0)
     979             :             {
     980           0 :               if (rename (oldname, newname) == 0)
     981             :                 {
     982           0 :                   if (0 <= dir_fd && dosync (dir_fd, qdir) != 0)
     983           0 :                     ok = false;
     984           0 :                   if (flags->verbose)
     985             :                     {
     986             :                       /*
     987             :                        * People seem to understand this better than talking
     988             :                        * about renaming oldname.  newname doesn't need
     989             :                        * quoting because we picked it.  oldname needs to
     990             :                        * be quoted only the first time.
     991             :                        */
     992           0 :                       char const *old = (first ? qoldname : oldname);
     993           0 :                       error (0, 0, _("%s: renamed to %s"), old, newname);
     994           0 :                       first = false;
     995             :                     }
     996           0 :                   memcpy (oldname + (base - newname), base, len + 1);
     997           0 :                   break;
     998             :                 }
     999             :               else
    1000             :                 {
    1001             :                   /* The rename failed: give up on this length.  */
    1002           0 :                   break;
    1003             :                 }
    1004             :             }
    1005             :           else
    1006             :             {
    1007             :               /* newname exists, so increment BASE so we use another */
    1008             :             }
    1009             :         }
    1010           0 :       while (incname (base, len));
    1011           0 :       len--;
    1012             :     }
    1013           0 :   if (unlink (oldname) != 0)
    1014             :     {
    1015           0 :       error (0, errno, _("%s: failed to remove"), qoldname);
    1016           0 :       ok = false;
    1017             :     }
    1018           0 :   else if (flags->verbose)
    1019           0 :     error (0, 0, _("%s: removed"), qoldname);
    1020           0 :   if (0 <= dir_fd)
    1021             :     {
    1022           0 :       if (dosync (dir_fd, qdir) != 0)
    1023           0 :         ok = false;
    1024           0 :       if (close (dir_fd) != 0)
    1025             :         {
    1026           0 :           error (0, errno, _("%s: failed to close"), qdir);
    1027           0 :           ok = false;
    1028             :         }
    1029             :     }
    1030           0 :   free (newname);
    1031           0 :   free (dir);
    1032           0 :   free (qdir);
    1033           0 :   return ok;
    1034             : }
    1035             : 
    1036             : /*
    1037             :  * Finally, the function that actually takes a filename and grinds
    1038             :  * it into hamburger.
    1039             :  *
    1040             :  * FIXME
    1041             :  * Detail to note: since we do not restore errno to EACCES after
    1042             :  * a failed chmod, we end up printing the error code from the chmod.
    1043             :  * This is actually the error that stopped us from proceeding, so
    1044             :  * it's arguably the right one, and in practice it'll be either EACCES
    1045             :  * again or EPERM, which both give similar error messages.
    1046             :  * Does anyone disagree?
    1047             :  */
    1048             : static bool
    1049          34 : wipefile (char *name, char const *qname,
    1050             :           struct randint_source *s, struct Options const *flags)
    1051             : {
    1052             :   bool ok;
    1053             :   int fd;
    1054             : 
    1055          34 :   fd = open (name, O_WRONLY | O_NOCTTY | O_BINARY);
    1056          34 :   if (fd < 0
    1057          28 :       && (errno == EACCES && flags->force)
    1058           1 :       && chmod (name, S_IWUSR) == 0)
    1059           1 :     fd = open (name, O_WRONLY | O_NOCTTY | O_BINARY);
    1060          34 :   if (fd < 0)
    1061             :     {
    1062          27 :       error (0, errno, _("%s: failed to open for writing"), qname);
    1063          27 :       return false;
    1064             :     }
    1065             : 
    1066           7 :   ok = do_wipefd (fd, qname, s, flags);
    1067           7 :   if (close (fd) != 0)
    1068             :     {
    1069           0 :       error (0, errno, _("%s: failed to close"), qname);
    1070           0 :       ok = false;
    1071             :     }
    1072           7 :   if (ok && flags->remove_file)
    1073           0 :     ok = wipename (name, qname, flags);
    1074           7 :   return ok;
    1075             : }
    1076             : 
    1077             : 
    1078             : /* Buffers for random data.  */
    1079             : static struct randint_source *randint_source;
    1080             : 
    1081             : /* Just on general principles, wipe buffers containing information
    1082             :    that may be related to the possibly-pseudorandom values used during
    1083             :    shredding.  */
    1084             : static void
    1085          58 : clear_random_data (void)
    1086             : {
    1087          58 :   randint_all_free (randint_source);
    1088          58 : }
    1089             : 
    1090             : 
    1091             : int
    1092         102 : main (int argc, char **argv)
    1093             : {
    1094         102 :   bool ok = true;
    1095         102 :   struct Options flags = { 0, };
    1096             :   char **file;
    1097             :   int n_files;
    1098             :   int c;
    1099             :   int i;
    1100         102 :   char const *random_source = NULL;
    1101             : 
    1102             :   initialize_main (&argc, &argv);
    1103         102 :   program_name = argv[0];
    1104         102 :   setlocale (LC_ALL, "");
    1105             :   bindtextdomain (PACKAGE, LOCALEDIR);
    1106             :   textdomain (PACKAGE);
    1107             : 
    1108         102 :   atexit (close_stdout);
    1109             : 
    1110         102 :   flags.n_iterations = DEFAULT_PASSES;
    1111         102 :   flags.size = -1;
    1112             : 
    1113         249 :   while ((c = getopt_long (argc, argv, "fn:s:uvxz", long_opts, NULL)) != -1)
    1114             :     {
    1115          67 :       switch (c)
    1116             :         {
    1117          10 :         case 'f':
    1118          10 :           flags.force = true;
    1119          10 :           break;
    1120             : 
    1121          10 :         case 'n':
    1122             :           {
    1123             :             uintmax_t tmp;
    1124          10 :             if (xstrtoumax (optarg, NULL, 10, &tmp, NULL) != LONGINT_OK
    1125           4 :                 || MIN (UINT32_MAX, SIZE_MAX / sizeof (int)) < tmp)
    1126             :               {
    1127           6 :                 error (EXIT_FAILURE, 0, _("%s: invalid number of passes"),
    1128             :                        quotearg_colon (optarg));
    1129             :               }
    1130           4 :             flags.n_iterations = tmp;
    1131             :           }
    1132           4 :           break;
    1133             : 
    1134           1 :         case RANDOM_SOURCE_OPTION:
    1135           1 :           if (random_source && !STREQ (random_source, optarg))
    1136           0 :             error (EXIT_FAILURE, 0, _("multiple random sources specified"));
    1137           1 :           random_source = optarg;
    1138           1 :           break;
    1139             : 
    1140           3 :         case 'u':
    1141           3 :           flags.remove_file = true;
    1142           3 :           break;
    1143             : 
    1144          20 :         case 's':
    1145             :           {
    1146             :             uintmax_t tmp;
    1147          20 :             if (xstrtoumax (optarg, NULL, 0, &tmp, "cbBkKMGTPEZY0")
    1148             :                 != LONGINT_OK)
    1149             :               {
    1150           6 :                 error (EXIT_FAILURE, 0, _("%s: invalid file size"),
    1151             :                        quotearg_colon (optarg));
    1152             :               }
    1153          14 :             flags.size = tmp;
    1154             :           }
    1155          14 :           break;
    1156             : 
    1157           8 :         case 'v':
    1158           8 :           flags.verbose = true;
    1159           8 :           break;
    1160             : 
    1161           4 :         case 'x':
    1162           4 :           flags.exact = true;
    1163           4 :           break;
    1164             : 
    1165           1 :         case 'z':
    1166           1 :           flags.zero_fill = true;
    1167           1 :           break;
    1168             : 
    1169           1 :         case_GETOPT_HELP_CHAR;
    1170             : 
    1171           1 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    1172             : 
    1173           8 :         default:
    1174           8 :           usage (EXIT_FAILURE);
    1175             :         }
    1176             :     }
    1177             : 
    1178          80 :   file = argv + optind;
    1179          80 :   n_files = argc - optind;
    1180             : 
    1181          80 :   if (n_files == 0)
    1182             :     {
    1183          21 :       error (0, 0, _("missing file operand"));
    1184          21 :       usage (EXIT_FAILURE);
    1185             :     }
    1186             : 
    1187          59 :   randint_source = randint_all_new (random_source, SIZE_MAX);
    1188          59 :   if (! randint_source)
    1189           1 :     error (EXIT_FAILURE, errno, "%s", quotearg_colon (random_source));
    1190          58 :   atexit (clear_random_data);
    1191             : 
    1192         148 :   for (i = 0; i < n_files; i++)
    1193             :     {
    1194          90 :       char *qname = xstrdup (quotearg_colon (file[i]));
    1195          90 :       if (STREQ (file[i], "-"))
    1196             :         {
    1197          56 :           ok &= wipefd (STDOUT_FILENO, qname, randint_source, &flags);
    1198             :         }
    1199             :       else
    1200             :         {
    1201             :           /* Plain filename - Note that this overwrites *argv! */
    1202          34 :           ok &= wipefile (file[i], qname, randint_source, &flags);
    1203             :         }
    1204          90 :       free (qname);
    1205             :     }
    1206             : 
    1207          58 :   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
    1208             : }
    1209             : /*
    1210             :  * vim:sw=2:sts=2:
    1211             :  */

Generated by: LCOV version 1.10