⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shred.c

📁 Linux下文件工具。
💻 C
📖 第 1 页 / 共 3 页
字号:
	      n = 0;	      break;	    }	  randpasses += k;	  n -= k;	}      else if ((size_t) k <= n)	{			/* Full block of patterns */	  memcpy (d, p, k * sizeof (int));	  p += k;	  d += k;	  n -= k;	}      else if (n < 2 || 3 * n < (size_t) k)	{			/* Finish with random */	  randpasses += n;	  break;	}      else	{			/* Pad out with k of the n available */	  do	    {	      if (n == (size_t) k-- || irand_mod (&r, k) < n)		{		  *d++ = *p;		  n--;		}	      p++;	    }	  while (n);	  break;	}    }  top = num - randpasses;	/* Top of initialized data */  /* assert (d == dest+top); */  /*   * We now have fixed patterns in the dest buffer up to   * "top", and we need to scramble them, with "randpasses"   * random passes evenly spaced among them.   *   * We want one at the beginning, one at the end, and   * evenly spaced in between.  To do this, we basically   * use Bresenham's line draw (a.k.a DDA) algorithm   * to draw a line with slope (randpasses-1)/(num-1).   * (We use a positive accumulator and count down to   * do this.)   *   * So for each desired output value, we do the following:   * - If it should be a random pass, copy the pass type   *   to top++, out of the way of the other passes, and   *   set the current pass to -1 (random).   * - If it should be a normal pattern pass, choose an   *   entry at random between here and top-1 (inclusive)   *   and swap the current entry with that one.   */  randpasses--;			/* To speed up later math */  accum = randpasses;		/* Bresenham DDA accumulator */  for (n = 0; n < num; n++)    {      if (accum <= randpasses)	{	  accum += num - 1;	  dest[top++] = dest[n];	  dest[n] = -1;	}      else	{	  swap = n + irand_mod (&r, top - n - 1);	  k = dest[n];	  dest[n] = dest[swap];	  dest[swap] = k;	}      accum -= randpasses;    }  /* assert (top == num); */  memset (&r, 0, sizeof r);	/* Wipe this on general principles */}/* * The core routine to actually do the work.  This overwrites the first * size bytes of the given fd.  Returns -1 on error, 0 on success. */static intdo_wipefd (int fd, char const *qname, struct isaac_state *s,	   struct Options const *flags){  size_t i;  struct stat st;  off_t size;			/* Size to write, size to read */  unsigned long n;		/* Number of passes for printing purposes */  int *passarray;  n = 0;		/* dopass takes n -- 0 to mean "don't print progress" */  if (flags->verbose)    n = flags->n_iterations + ((flags->zero_fill) != 0);  if (fstat (fd, &st))    {      error (0, errno, "%s: fstat", qname);      return -1;    }  /* If we know that we can't possibly shred the file, give up now.     Otherwise, we may go into a infinite loop writing data before we     find that we can't rewind the device.  */  if ((S_ISCHR (st.st_mode) && isatty (fd))      || S_ISFIFO (st.st_mode)      || S_ISSOCK (st.st_mode))    {      error (0, 0, _("%s: invalid file type"), qname);      return -1;    }  /* Allocate pass array */  passarray = xmalloc (flags->n_iterations * sizeof (int));  size = flags->size;  if (size == -1)    {      /* Accept a length of zero only if it's a regular file.	 For any other type of file, try to get the size another way.  */      if (S_ISREG (st.st_mode))	{	  size = st.st_size;	  if (size < 0)	    {	      error (0, 0, _("%s: file has negative size"), qname);	      return -1;	    }	}      else	{	  size = lseek (fd, (off_t) 0, SEEK_END);	  if (size <= 0)	    {	      /* We are unable to determine the length, up front.		 Let dopass do that as part of its first iteration.  */	      size = -1;	    }	}      if (0 <= size && !(flags->exact))	{	  size += ST_BLKSIZE (st) - 1 - (size - 1) % ST_BLKSIZE (st);	  /* If in rounding up, we've just overflowed, use the maximum.  */	  if (size < 0)	    size = TYPE_MAXIMUM (off_t);	}    }  /* Schedule the passes in random order. */  genpattern (passarray, flags->n_iterations, s);  /* Do the work */  for (i = 0; i < flags->n_iterations; i++)    {      if (dopass (fd, qname, &size, passarray[i], s, i + 1, n) < 0)	{	  memset (passarray, 0, flags->n_iterations * sizeof (int));	  free (passarray);	  return -1;	}    }  memset (passarray, 0, flags->n_iterations * sizeof (int));  free (passarray);  if (flags->zero_fill)    if (dopass (fd, qname, &size, 0, s, flags->n_iterations + 1, n) < 0)      return -1;  /* Okay, now deallocate the data.  The effect of ftruncate on     non-regular files is unspecified, so don't worry about any     errors reported for them.  */  if (flags->remove_file && ftruncate (fd, (off_t) 0) != 0      && S_ISREG (st.st_mode))    {      error (0, errno, _("%s: error truncating"), qname);      return -1;    }  return 0;}/* A wrapper with a little more checking for fds on the command line */static intwipefd (int fd, char const *qname, struct isaac_state *s,	struct Options const *flags){  int fd_flags = fcntl (fd, F_GETFL);  if (fd_flags < 0)    {      error (0, errno, "%s: fcntl", qname);      return -1;    }  if (fd_flags & O_APPEND)    {      error (0, 0, _("%s: cannot shred append-only file descriptor"), qname);      return -1;    }  return do_wipefd (fd, qname, s, flags);}/* --- Name-wiping code --- *//* Characters allowed in a file name - a safe universal set. */static char const nameset[] ="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_+=%@#.";/* * This increments the name, considering it as a big-endian base-N number * with the digits taken from nameset.  Characters not in the nameset * are considered to come before nameset[0]. * * It's not obvious, but this will explode if name[0..len-1] contains * any 0 bytes. * * This returns the carry (1 on overflow). */static intincname (char *name, unsigned len){  char const *p;  if (!len)    return 1;  p = strchr (nameset, name[--len]);  /* If the character is not found, replace it with a 0 digit */  if (!p)    {      name[len] = nameset[0];      return 0;    }  /* If this character has a successor, use it */  if (p[1])    {      name[len] = p[1];      return 0;    }  /* Otherwise, set this digit to 0 and increment the prefix */  name[len] = nameset[0];  return incname (name, len);}/* * Repeatedly rename a file with shorter and shorter names, * to obliterate all traces of the file name on any system that * adds a trailing delimiter to on-disk file names and reuses * the same directory slot.  Finally, unlink it. * The passed-in filename is modified in place to the new filename. * (Which is unlinked if this function succeeds, but is still present if * it fails for some reason.) * * The main loop is written carefully to not get stuck if all possible * names of a given length are occupied.  It counts down the length from * the original to 0.  While the length is non-zero, it tries to find an * unused file name of the given length.  It continues until either the * name is available and the rename succeeds, or it runs out of names * to try (incname wraps and returns 1).  Finally, it unlinks the file. * * The unlink is Unix-specific, as ANSI-standard remove has more * portability problems with C libraries making it "safe".  rename * is ANSI-standard. * * To force the directory data out, we try to open the directory and * invoke fdatasync on it.  This is rather non-standard, so we don't * insist that it works, just fall back to a global sync in that case. * This is fairly significantly Unix-specific.  Of course, on any * filesystem with synchronous metadata updates, this is unnecessary. */static intwipename (char *oldname, char const *qoldname, struct Options const *flags){  char *newname, *base;	  /* Base points to filename part of newname */  unsigned len;  int first = 1;  int err;  int dir_fd;			/* Try to open directory to sync *it* */  newname = xstrdup (oldname);  if (flags->verbose)    error (0, 0, _("%s: removing"), qoldname);  /* Find the file name portion */  base = strrchr (newname, '/');  /* Temporary hackery to get a directory fd */  if (base)    {      *base = '\0';      dir_fd = open (newname, O_RDONLY | O_NOCTTY);      *base = '/';    }  else    {      dir_fd = open (".", O_RDONLY | O_NOCTTY);    }  base = base ? base + 1 : newname;  len = strlen (base);  while (len)    {      memset (base, nameset[0], len);      base[len] = 0;      do	{	  struct stat st;	  if (lstat (newname, &st) < 0)	    {	      if (rename (oldname, newname) == 0)		{		  if (dir_fd < 0		      || (fdatasync (dir_fd) < 0 && fsync (dir_fd) < 0))		    sync ();	/* Force directory out */		  if (flags->verbose)		    {		      /*		       * People seem to understand this better than talking		       * about renaming oldname.  newname doesn't need		       * quoting because we picked it.  oldname needs to		       * be quoted only the first time.		       */		      char const *old = (first ? qoldname : oldname);		      error (0, 0, _("%s: renamed to %s"), old, newname);		      first = 0;		    }		  memcpy (oldname + (base - newname), base, len + 1);		  break;		}	      else		{		  /* The rename failed: give up on this length.  */		  break;		}	    }	  else	    {	      /* newname exists, so increment BASE so we use another */	    }	}      while (!incname (base, len));      len--;    }  free (newname);  err = unlink (oldname);  if (dir_fd < 0 || (fdatasync (dir_fd) < 0 && fsync (dir_fd) < 0))    sync ();  close (dir_fd);  if (!err && flags->verbose)    error (0, 0, _("%s: removed"), qoldname);  return err;}/* * Finally, the function that actually takes a filename and grinds * it into hamburger. * * FIXME * Detail to note: since we do not restore errno to EACCES after * a failed chmod, we end up printing the error code from the chmod. * This is actually the error that stopped us from proceeding, so * it's arguably the right one, and in practice it'll be either EACCES * again or EPERM, which both give similar error messages. * Does anyone disagree? */static intwipefile (char *name, char const *qname,	  struct isaac_state *s, struct Options const *flags){  int err, fd;  fd = open (name, O_WRONLY | O_NOCTTY);  if (fd < 0)    {      if (errno == EACCES && flags->force)	{	  if (chmod (name, S_IWUSR) >= 0) /* 0200, user-write-only */	    fd = open (name, O_WRONLY | O_NOCTTY);	}      else if ((errno == ENOENT || errno == ENOTDIR)	       && strncmp (name, "/dev/fd/", 8) == 0)	{	  /* We accept /dev/fd/# even if the OS doesn't support it */	  int errnum = errno;	  unsigned long num;	  char *p;	  errno = 0;	  num = strtoul (name + 8, &p, 10);	  /* If it's completely decimal with no leading zeros... */	  if (errno == 0 && !*p && num <= INT_MAX &&	      (('1' <= name[8] && name[8] <= '9')	       || (name[8] == '0' && !name[9])))	    {	      return wipefd ((int) num, qname, s, flags);	    }	  errno = errnum;	}    }  if (fd < 0)    {      error (0, errno, "%s", qname);      return -1;    }  err = do_wipefd (fd, qname, s, flags);  if (close (fd) != 0)    {      error (0, 0, "%s: close", qname);      err = -1;    }  if (err == 0 && flags->remove_file)    {      err = wipename (name, qname, flags);      if (err < 0)	error (0, 0, _("%s: cannot remove"), qname);    }  return err;}intmain (int argc, char **argv){  struct isaac_state s;  int err = 0;  struct Options flags;  char **file;  int n_files;  int c;  int i;  program_name = argv[0];  setlocale (LC_ALL, "");  bindtextdomain (PACKAGE, LOCALEDIR);  textdomain (PACKAGE);  atexit (close_stdout);  isaac_seed (&s);  memset (&flags, 0, sizeof flags);  flags.n_iterations = DEFAULT_PASSES;  flags.size = -1;  while ((c = getopt_long (argc, argv, "fn:s:uvxz", long_opts, NULL)) != -1)    {      switch (c)	{	case 0:	  break;	case 'f':	  flags.force = 1;	  break;	case 'n':	  {	    uintmax_t tmp;	    if (xstrtoumax (optarg, NULL, 10, &tmp, NULL) != LONGINT_OK		|| (word32) tmp != tmp		|| ((size_t) (tmp * sizeof (int)) / sizeof (int) != tmp))	      {		error (1, 0, _("%s: invalid number of passes"),		       quotearg_colon (optarg));	      }	    flags.n_iterations = (size_t) tmp;	  }	  break;	case 'u':	  flags.remove_file = 1;	  break;	case 's':	  {	    uintmax_t tmp;	    if (xstrtoumax (optarg, NULL, 0, &tmp, "cbBkKMGTPEZY0")		!= LONGINT_OK)	      {		error (1, 0, _("%s: invalid file size"),		       quotearg_colon (optarg));	      }	    flags.size = tmp;	  }	  break;	case 'v':	  flags.verbose = 1;	  break;	case 'x':	  flags.exact = 1;	  break;	case 'z':	  flags.zero_fill = 1;	  break;	case_GETOPT_HELP_CHAR;	case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);	default:	  usage (1);	}    }  file = argv + optind;  n_files = argc - optind;  if (n_files == 0)    {      error (0, 0, _("missing file argument"));      usage (1);    }  for (i = 0; i < n_files; i++)    {      char *qname = xstrdup (quotearg_colon (file[i]));      if (STREQ (file[i], "-"))	{	  if (wipefd (STDOUT_FILENO, qname, &s, &flags) < 0)	    err = 1;	}      else	{	  /* Plain filename - Note that this overwrites *argv! */	  if (wipefile (file[i], qname, &s, &flags) < 0)	    err = 1;	}      free (qname);    }  /* Just on general principles, wipe s. */  memset (&s, 0, sizeof s);  exit (err);}/* * vim:sw=2:sts=2: */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -