removefile_sunlink.c [plain text]
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <time.h>
#include <unistd.h>
#if HAVE_LINUX_EXT2_FS_H
#include <linux/ext2_fs.h>
#endif
#if HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#if HAVE_SYS_PARAM_H && HAVE_SYS_MOUNT_H
#include <sys/param.h>
#include <sys/mount.h>
#endif
#if __APPLE__
#include <sys/disk.h>
#endif
#if HAVE_CHFLAGS
# if !defined UF_IMMUTABLE
# define UF_IMMUTABLE 0
# endif
# if !defined UF_APPEND
# define UF_APPEND 0
# endif
# if !defined UF_NOUNLINK
# define UF_NOUNLINK 0
# endif
# if !defined SF_IMMUTABLE
# define SF_IMMUTABLE 0
# endif
# if !defined SF_APPEND
# define SF_APPEND 0
# endif
# if !defined SF_NOUNLINK
# define SF_NOUNLINK 0
# endif
#endif
#include "removefile.h"
#include "removefile_priv.h"
static int
init_write_buffer(struct stat *statbuf, struct statfs *fs_stats, removefile_state_t state) {
u_int32_t tmp_buffsize;
state->file_size = statbuf->st_size;
state->buffsize = statbuf->st_blksize;
#if HAVE_SYS_PARAM_H
state->buffsize = (u_int32_t)(statbuf->st_size / statbuf->st_blksize) * statbuf->st_blksize;
if ((statbuf->st_size % statbuf->st_blksize) != 0) {
state->buffsize += statbuf->st_blksize;
} else if (state->buffsize < statbuf->st_blksize) {
state->buffsize = statbuf->st_blksize;
}
tmp_buffsize = MAXBSIZE;
if (state->buffsize > tmp_buffsize) {
state->buffsize = tmp_buffsize;
}
#endif
tmp_buffsize = state->buffsize + 4;
if (state->buffer) {
if (tmp_buffsize > state->allocated_buffsize) {
free(state->buffer);
state->buffer = NULL;
} else {
return 0;
}
}
if ((state->buffer = (unsigned char *)malloc(tmp_buffsize)) == NULL) {
errno = ENOMEM;
return -1;
}
state->allocated_buffsize = tmp_buffsize;
return 0;
}
static void
flush(int fd) {
#if defined F_FULLFSYNC
if (fcntl(fd, F_FULLFSYNC, NULL) != 0) {
fsync(fd);
}
#elif HAVE_FDATASYNC
fdatasync(fd);
#else
fsync(fd);
#endif
}
static unsigned char *align_buffer(unsigned char *buf, off_t pos) {
return (unsigned char *)((uintptr_t)buf + (unsigned int)(pos % 3));
}
#if 0
void verification_failure(off_t count) {
if (sizeof(off_t) == 4)
printf("warning: failed to verify write at offset %d\n", count);
else if (sizeof(off_t) == 8)
printf("warning: failed to verify write at offset %lld\n", count);
else
printf("warning: previous write failed to verify!\n");
fflush(stdout);
}
#endif
static void
overwrite(int stage, removefile_state_t state) {
ssize_t i;
off_t count = 0;
unsigned char *buffptr = state->buffer;
if (__removefile_state_test_cancel(state)) return;
lseek(state->file, 0, SEEK_SET);
while (count < state->file_size - state->buffsize) {
if (stage == 1 ) {
__removefile_randomize_buffer(state->buffer, state->buffsize, state);
} else if (stage == 2 ) {
buffptr = align_buffer(state->buffer, count);
}
i = write(state->file, buffptr, state->buffsize);
if (i > 0)
count += i;
if (__removefile_state_test_cancel(state)) return;
}
if (stage == 1 ) {
__removefile_randomize_buffer(state->buffer, (size_t)(state->file_size - count), state);
} else if (stage == 2 ) {
buffptr = align_buffer(state->buffer, count);
}
i = write(state->file, buffptr, (size_t)(state->file_size - count));
if ((state->unlink_flags & (REMOVEFILE_SECURE_7_PASS | REMOVEFILE_SECURE_35_PASS | REMOVEFILE_SECURE_3_PASS)) != 0)
flush(state->file);
lseek(state->file, 0, SEEK_SET);
}
static void
overwrite_random(int num_passes, removefile_state_t state) {
int i;
for (i = 0; i < num_passes; i++) {
overwrite(1 , state);
}
}
static void
overwrite_byte(int byte, removefile_state_t state) {
memset(state->buffer, byte, state->buffsize);
overwrite(0 , state);
}
static void
overwrite_bytes(unsigned int byte1, unsigned int byte2, unsigned int byte3, removefile_state_t state) {
u_int32_t val[3], *p = (u_int32_t *)state->buffer;
unsigned int i, mod12buffsize = state->allocated_buffsize - (state->allocated_buffsize % 12);
val[0] = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte1;
val[1] = (byte2 << 24) | (byte3 << 16) | (byte1 << 8) | byte2;
val[2] = (byte3 << 24) | (byte1 << 16) | (byte2 << 8) | byte3;
for (i = 0; i < mod12buffsize; i += 12) {
*p++ = val[0];
*p++ = val[1];
*p++ = val[2];
}
while (i < state->allocated_buffsize) {
state->buffer[i] = ((unsigned char *)&val[0])[i % 3];
i++;
}
overwrite(2 , state);
}
static void
overwrite_file(removefile_state_t state) {
if (state->unlink_flags & REMOVEFILE_SECURE_35_PASS) {
overwrite_random(4, state);
overwrite_byte(0x55, state);
overwrite_byte(0xAA, state);
overwrite_bytes(0x92, 0x49, 0x24, state);
overwrite_bytes(0x49, 0x24, 0x92, state);
overwrite_bytes(0x24, 0x92, 0x49, state);
overwrite_byte(0x00, state);
overwrite_byte(0x11, state);
overwrite_byte(0x22, state);
overwrite_byte(0x33, state);
overwrite_byte(0x44, state);
overwrite_byte(0x55, state);
overwrite_byte(0x66, state);
overwrite_byte(0x77, state);
overwrite_byte(0x88, state);
overwrite_byte(0x99, state);
overwrite_byte(0xAA, state);
overwrite_byte(0xBB, state);
overwrite_byte(0xCC, state);
overwrite_byte(0xDD, state);
overwrite_byte(0xEE, state);
overwrite_byte(0xFF, state);
overwrite_bytes(0x92, 0x49, 0x24, state);
overwrite_bytes(0x49, 0x24, 0x92, state);
overwrite_bytes(0x24, 0x92, 0x49, state);
overwrite_bytes(0x6D, 0xB6, 0xDB, state);
overwrite_bytes(0xB6, 0xDB, 0x6D, state);
overwrite_bytes(0xDB, 0x6D, 0xB6, state);
overwrite_random(4, state);
} else if (state->unlink_flags & REMOVEFILE_SECURE_7_PASS) {
overwrite_byte(0xF6, state);
overwrite_byte(0x00, state);
overwrite_byte(0xFF, state);
overwrite_random(1, state);
overwrite_byte(0x00, state);
overwrite_byte(0xFF, state);
overwrite_random(1, state);
} else if (state->unlink_flags & REMOVEFILE_SECURE_3_PASS) {
overwrite_random(2, state);
overwrite_byte(0xAA, state);
} else if (state->unlink_flags & REMOVEFILE_SECURE_1_PASS) {
overwrite_random(1, state);
} else if (state->unlink_flags & REMOVEFILE_SECURE_1_PASS_ZERO) {
overwrite_byte(0, state);
}
}
int
__removefile_sunlink(const char *path, removefile_state_t state) {
struct stat statbuf;
struct statfs fs_stats;
#if HAVE_LINUX_EXT2_FS_H
int flags = 0;
#endif
int fmode = O_WRONLY;
struct flock flock;
if (lstat(path, &statbuf) == -1)
return -1;
if (!S_ISREG(statbuf.st_mode))
return __removefile_rename_unlink(path, state);
if (statbuf.st_nlink > 1) {
return __removefile_rename_unlink(path, state);
}
if ( (state->file = open(path, fmode)) == -1)
return -1;
if (fcntl(state->file, F_WRLCK, &flock) == -1) {
close(state->file);
state->file = -1;
return -1;
}
if (fstatfs(state->file, &fs_stats) == -1 && errno != ENOSYS) {
close(state->file);
state->file = -1;
return -1;
}
#if HAVE_LINUX_EXT2_FS_H
if (fs_stats.f_type == EXT2_SUPER_MAGIC)
if (ioctl(state->file, EXT2_IOC_GETFLAGS, &flags) == -1) {
close(state->file);
state->file = -1;
return -1;
}
if ( (flags & EXT2_UNRM_FL) || (flags & EXT2_IMMUTABLE_FL) ||
(flags & EXT2_APPEND_FL) )
{
close(state->file);
state->file = -1;
errno = EPERM;
return -1;
}
#endif
#if HAVE_CHFLAGS
if ((statbuf.st_flags & UF_IMMUTABLE) ||
(statbuf.st_flags & UF_APPEND) ||
(statbuf.st_flags & UF_NOUNLINK) ||
(statbuf.st_flags & SF_IMMUTABLE) ||
(statbuf.st_flags & SF_APPEND) ||
(statbuf.st_flags & SF_NOUNLINK))
{
close(state->file);
state->file = -1;
errno = EPERM;
return -1;
}
#endif
if (init_write_buffer(&statbuf, &fs_stats, state) == -1) {
close(state->file);
state->file = -1;
return -1;
}
#if defined F_NOCACHE
(void)fcntl(state->file, F_NOCACHE, 1);
#endif
overwrite_file(state);
#if HAVE_LINUX_EXT2_FS_H
ioctl(state->file, EXT2_IOC_SETFLAGS, EXT2_SECRM_FL);
#endif
close(state->file);
state->file = -1;
#if __APPLE__
{
static const char *RSRCFORKSPEC = "/..namedfork/rsrc";
off_t rsrc_fork_size;
size_t rsrc_path_size = strlen(path) + strlen(RSRCFORKSPEC) + 1;
char *rsrc_path = (char *)alloca(rsrc_path_size);
if (rsrc_path == NULL) {
errno = ENOMEM;
return -1;
}
if (snprintf(rsrc_path, MAXPATHLEN,
"%s%s", path, RSRCFORKSPEC ) > MAXPATHLEN - 1) {
errno = ENAMETOOLONG;
return -1;
}
if (lstat(rsrc_path, &statbuf) != 0) {
int err = errno;
if (err == ENOENT || err == ENOTDIR) {
rsrc_fork_size = 0;
} else {
return -1;
}
} else {
rsrc_fork_size = statbuf.st_size;
}
if (rsrc_fork_size > 0) {
if ((state->file = open(rsrc_path, O_WRONLY)) == -1) {
return -1;
}
if (fcntl(state->file, F_WRLCK, &flock) == -1) {
close(state->file);
state->file = -1;
return -1;
}
if (init_write_buffer(&statbuf, &fs_stats, state) == -1) {
close(state->file);
state->file = -1;
return -1;
}
#if defined F_NOCACHE
(void)fcntl(state->file, F_NOCACHE, 1);
#endif
overwrite_file(state);
close(state->file);
state->file = -1;
}
}
#endif
if (__removefile_state_test_cancel(state)) {
errno = ECANCELED;
return -1;
}
return __removefile_rename_unlink(path, state);
}