From 0ad42af17b3e7745a4be07cde8ad5a0259b40d15 Mon Sep 17 00:00:00 2001 From: Timo Boettcher Date: Tue, 15 Oct 2013 10:57:35 +0200 Subject: [PATCH 1/3] Properly overwrite filenames This patch is based on the source of shred from coreutils by Colin Plumb. While shred is now licensed under GPLv3, the parts of this patch that were derived from shred could easily be changed to a version of shred (git commit cad884a) that was licensed under GPLv2 or later. Note that this patch only keeps the filename from being readable in the filesystem structure. For journaling fileystems, such as ext{3,4}, the filename is still retained in the journal. --- wipe.c | 195 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 151 insertions(+), 44 deletions(-) diff --git a/wipe.c b/wipe.c index 96d4300..3ee3b5e 100644 --- a/wipe.c +++ b/wipe.c @@ -78,6 +78,7 @@ #ifdef HAVE_GETOPT #include #endif +#include #include #include #include @@ -243,6 +244,9 @@ int o_pass_order[MAX_PASSES] = { -1 }; /* End of Options ***/ +static int ignorable_sync_errno (int errno_val); +static int dosync (int fd, char const *qname); +static int incname (char *name, size_t len); static int wipe_filename_and_remove (char *fn); /*** do_remove */ @@ -521,73 +525,176 @@ inline static int directory_name_length (char *fn) static char valid_filename_chars[64] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-."; +static int +ignorable_sync_errno (int errno_val) +{ + return (errno_val == EINVAL + || errno_val == EBADF + /* HP-UX does this */ + || errno_val == EISDIR); +} + + +#define HAVE_FDATASYNC 1 +static int +dosync (int fd, char const *qname) +{ + int err; + +#if HAVE_FDATASYNC + if (fdatasync (fd) == 0) + return 0; + err = errno; + if ( ! ignorable_sync_errno (err)) { + fprintf (stderr, "%s: fdatasync failed", qname); + errno = err; + return -1; + } +#endif + + if (fsync (fd) == 0) + return 0; + err = errno; + if ( ! ignorable_sync_errno (err)) { + fprintf (stderr, "%s: fsync failed", qname); + errno = err; + return -1; + } + + sync (); + return 0; +} + +static int +incname (char *name, size_t len) +{ + while (len--) { + char const *p = strchr (valid_filename_chars, name[len]); + + /* Given that NAME is composed of bytes from NAMESET, + P will never be NULL here. */ + assert (p); + + /* 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] = valid_filename_chars[0]; + } + + return -1; +} + +#ifndef ISSLASH +# define ISSLASH(C) ((C) == '/') +#endif + +char * +last_component (char const *name) +{ + char const *base = name; + char const *p; + int saw_slash = -1; + + while (ISSLASH (*base)) + base++; + + for (p = base; *p; p++) { + if (ISSLASH (*p)) + saw_slash = -1; + else if (saw_slash) { + base = p; + saw_slash = 0; + } + } + + return (char *) base; +} + + /*** wipe_filename_and_remove */ /* actually, after renaming a file, the only way to make sure that the * name change is physically carried out is to call sync (), which flushes * out ALL the disk caches of the system, whereas for - * reading and writing one can use the O_SYNC bit to get syncrhonous + * reading and writing one can use the O_SYNC bit to get synchronous * I/O for one file. as sync () is very slow, calling sync () after * every rename () makes wipe extremely slow. */ static int wipe_filename_and_remove (char *fn) { - int i, j, k, l; + int len; int r = -1; int fn_l, dn_l; - /* char *dn; */ - char *buf[2]; + char *oldname, *newname; + char *dir, *dirc; + dirc = strdup(fn); + dir = dirname(dirc); struct stat st; - int t_l; /* target length */ - /* dn = directory_name (fn); */ fn_l = strlen (fn); dn_l = directory_name_length (fn); - buf[0] = malloc (fn_l + NAME_MAX + 1); - buf[1] = malloc (fn_l + NAME_MAX + 1); + oldname = malloc (fn_l + NAME_MAX + 1); + newname = malloc (fn_l + NAME_MAX + 1); r = 0; - t_l = fn_l - dn_l; /* first target length */ - - if (buf[0] && buf[1]) { - strcpy (buf[0], fn); - strcpy (buf[1], fn); - for (j = 1, i = 0; i < o_name_max_passes; j ^= 1, i++) { - for (k = o_name_max_tries; k; k--) { - l = t_l; - fill_random_from_table (buf[j] + dn_l, l, - valid_filename_chars, 0x3f); - buf[j][dn_l + l] = 0; - if (stat (buf[j], &st)) break; - } + if (oldname && newname) { + strcpy (oldname, fn); + strcpy (newname, fn); - if (k) { - if (!o_silent) { - fprintf (stderr, "\rRenaming %32.32s -> %32.32s", buf[j^1], buf[j]); - middle_of_line = 1; - fflush (stderr); - } - if (rename (buf[j^1], buf[j])) { - FLUSH_MIDDLE - fprintf (stderr, "%.32s: could not rename '%s' to '%s': %s (%d)\n", - fn, buf[j^1], buf[j], strerror (errno), errno); - r = -1; - break; + int dir_fd = open (dir, O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK); + + + char *base = last_component(newname); + len = strlen(base); + fprintf (stderr, "\n"); + while (len) { + memset (base, valid_filename_chars[0], len); + base[len] = 0; + do { + if (lstat (newname, &st) < 0) { + if (!o_silent) { + fprintf (stderr, "\rRenaming %32.32s -> %32.32s", oldname, newname); + middle_of_line = 1; + fflush (stderr); + } + if (rename (oldname, newname) == 0) { + if (0 <= dir_fd && dosync (dir_fd, dir) != 0) + r = -1; + memcpy (oldname + (base - newname), base, len + 1); + break; + } else { + /* The rename failed: give up on this length. */ + fprintf (stderr, "%.32s: could not rename '%s' to '%s': %s (%d)\n", fn, oldname, newname, strerror (errno), errno); + break; + } + } else { + //fprintf (stderr, "%.32s: rename target '%s' exists\n", fn, newname); } - (void) sync (); - } else { - /* we could not find a target name of desired length, so - * increase target length until we find one. */ - t_l ++; - j ^= 1; + } while (incname (base, len)); + len--; + } + + + if (remove (oldname)) { + fprintf (stderr, "%.32s: failed to unlink '%s'\n", fn, oldname); + r = -1; + } + if (0 <= dir_fd) { + dosync (dir_fd, dir); + if (close (dir_fd) != 0) { + fprintf (stderr, "%s: failed to close\n", dir); + r = -1; } } - if (remove (buf[j^1])) r = -1; } - free (buf[0]); free (buf[1]); + free (oldname); free (newname); free(dirc); return r; } @@ -1060,7 +1167,7 @@ static int dothejob (char *fn) } #ifndef HAVE_OSYNC - if (fsync (fd)) { + if (dosync (fd,fn)) { fnerror ("fsync error [1]"); close (fd); return -1; @@ -1068,7 +1175,7 @@ static int dothejob (char *fn) #endif } - if (fsync (fd)) { + if (dosync (fd,fn)) { fnerror ("fsync error [2]"); close (fd); return -1; @@ -1270,7 +1377,7 @@ void banner () "Web site: http://lambda-diode.com/software/wipe/\n" "Release date: " WIPE_DATE "\n" "Compiled: " __DATE__ "\n" - "Git version: " WIPE_GIT "\n" + "Git version: TEST\n" "\n" "Based on data from \"Secure Deletion of Data from Magnetic and Solid-State\n" "Memory\" by Peter Gutmann .\n"); From 7130c0c14924e20f385ad1e3b32606dc73154a2d Mon Sep 17 00:00:00 2001 From: Till Maas Date: Sun, 10 Nov 2013 08:41:45 +0100 Subject: [PATCH 2/3] wipe: Restore correct banner Use WIPE_GIT instead of TEST as version again. --- wipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wipe.c b/wipe.c index 3ee3b5e..73f9f9e 100644 --- a/wipe.c +++ b/wipe.c @@ -1377,7 +1377,7 @@ void banner () "Web site: http://lambda-diode.com/software/wipe/\n" "Release date: " WIPE_DATE "\n" "Compiled: " __DATE__ "\n" - "Git version: TEST\n" + "Git version: " WIPE_GIT "\n" "\n" "Based on data from \"Secure Deletion of Data from Magnetic and Solid-State\n" "Memory\" by Peter Gutmann .\n"); From 485aff23014630b8cfc15671336f2c28c9c8a8ed Mon Sep 17 00:00:00 2001 From: Till Maas Date: Sun, 10 Nov 2013 08:44:46 +0100 Subject: [PATCH 3/3] Define HAVE_DATASYNC in Makefile --- Makefile | 2 +- wipe.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 17d9758..e4404e4 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ # CC_LINUX=gcc -CCO_LINUX=-Wall -DHAVE_DEV_URANDOM -DHAVE_OSYNC -DHAVE_STRCASECMP -DHAVE_RANDOM -DWEAK_RC6 -DSYNC_WAITS_FOR_SYNC -DFIND_DEVICE_SIZE_BY_BLKGETSIZE -DSIXTYFOUR -D__USE_LARGEFILE -D_FILE_OFFSET_BITS=64 +CCO_LINUX=-Wall -DHAVE_DEV_URANDOM -DHAVE_OSYNC -DHAVE_DATASYNC -DHAVE_STRCASECMP -DHAVE_RANDOM -DWEAK_RC6 -DSYNC_WAITS_FOR_SYNC -DFIND_DEVICE_SIZE_BY_BLKGETSIZE -DSIXTYFOUR -D__USE_LARGEFILE -D_FILE_OFFSET_BITS=64 # default should be to turn off debugging and to turn on optimization. #CCO_LINUX+=-O9 -pipe -fomit-frame-pointer -finline-functions -funroll-loops -fstrength-reduce CCO_LINUX+=$(CFLAGS) diff --git a/wipe.c b/wipe.c index 73f9f9e..6294ee4 100644 --- a/wipe.c +++ b/wipe.c @@ -535,7 +535,6 @@ ignorable_sync_errno (int errno_val) } -#define HAVE_FDATASYNC 1 static int dosync (int fd, char const *qname) {