1#include "config.h" 2 3#include <errno.h> 4#include <fcntl.h> 5#include <stdarg.h> 6#include <stdio.h> 7#include <stdlib.h> 8#include <string.h> 9#include <sys/ioctl.h> 10#include <sys/stat.h> 11#include <sys/types.h> 12#include <time.h> 13#include <unistd.h> 14 15#if HAVE_LINUX_EXT2_FS_H 16#include <linux/ext2_fs.h> 17#endif 18 19#if HAVE_SYS_VFS_H 20#include <sys/vfs.h> 21#endif 22 23#if HAVE_SYS_PARAM_H && HAVE_SYS_MOUNT_H 24#include <sys/param.h> 25#include <sys/mount.h> 26#endif 27 28#if __APPLE__ 29#include <sys/disk.h> 30#endif 31 32#if HAVE_CHFLAGS 33/* define unsupported flags as 0 */ 34# if !defined UF_IMMUTABLE 35# define UF_IMMUTABLE 0 36# endif 37# if !defined UF_APPEND 38# define UF_APPEND 0 39# endif 40# if !defined UF_NOUNLINK 41# define UF_NOUNLINK 0 42# endif 43# if !defined SF_IMMUTABLE 44# define SF_IMMUTABLE 0 45# endif 46# if !defined SF_APPEND 47# define SF_APPEND 0 48# endif 49# if !defined SF_NOUNLINK 50# define SF_NOUNLINK 0 51# endif 52#endif 53 54#include "srm.h" 55 56enum { 57 W_SINGLE = 0, 58 W_RANDOM = 1, 59 W_TRIPLE = 2 60}; 61 62static int file; 63static off_t file_size; 64static unsigned char *buffer = NULL; 65static unsigned char *verify_buffer = NULL; 66static u_int32_t buffsize, allocated_buffsize = 0; 67static off_t bytes_total; 68static off_t bytes_completed; 69 70int init_write_buffer(struct stat *statbuf, struct statfs *fs_stats) { 71 u_int64_t maxbytecount; 72 u_int32_t tmp_buffsize; 73 74 file_size = statbuf->st_size; 75 buffsize = statbuf->st_blksize; 76 77#if HAVE_SYS_PARAM_H 78 /* try to determine an optimal write buffer size */ 79 buffsize = (u_int32_t)(statbuf->st_size / statbuf->st_blksize) * statbuf->st_blksize; 80 if ((statbuf->st_size % statbuf->st_blksize) != 0) { 81 /* add full size of last block */ 82 buffsize += statbuf->st_blksize; 83 } else if (buffsize < statbuf->st_blksize) { 84 /* no smaller than one device block */ 85 buffsize = statbuf->st_blksize; 86 } 87 tmp_buffsize = MAXBSIZE; 88 if (buffsize > tmp_buffsize) { 89 /* no larger than the largest file system buffer size */ 90 buffsize = tmp_buffsize; 91 } 92#endif 93 94 if (opt_buffsize) { 95 /* allow command-line override */ 96 buffsize = opt_buffsize; 97 } 98 99 /* Allocated buffer must be at least 2 bytes larger than logical buffsize. 100 This lets us align repeating 3-byte patterns across multiple buffer 101 writes by using a variable offset (0..2) from the start of the buffer. */ 102 103 tmp_buffsize = buffsize + 4; 104 105 if (buffer) { 106 if (tmp_buffsize > allocated_buffsize) { 107 free(buffer); 108 buffer = NULL; 109 } else { 110 return 0; /* use existing buffer */ 111 } 112 } 113 if ((buffer = (unsigned char *)malloc(tmp_buffsize)) == NULL) { 114 errno = ENOMEM; 115 return -1; 116 } 117 if (options & OPT_VERIFY) { 118 if ((verify_buffer = (unsigned char *)malloc(buffsize)) == NULL) { 119 errno = ENOMEM; 120 return -1; 121 } 122 } 123 allocated_buffsize = tmp_buffsize; 124 return 0; 125} 126 127void flush(int fd) { 128 /* force buffered writes to be flushed to disk */ 129#if defined F_FULLFSYNC 130 /* F_FULLFSYNC is equivalent to fsync plus device flush to media */ 131 if (fcntl(fd, F_FULLFSYNC, NULL) != 0) { 132 /* we're not on a fs that supports this; fall back to plain fsync */ 133 fsync(fd); 134 } 135#elif HAVE_FDATASYNC 136 fdatasync(fd); 137#else 138 fsync(fd); 139#endif 140} 141 142void update_progress(u_int32_t bytes_written) { 143 u_int32_t cur_percent, new_percent; 144 145 if (!bytes_total) 146 return; 147 148 cur_percent = (u_int32_t)((off_t)(bytes_completed*100)/bytes_total); 149 bytes_completed += bytes_written; 150 new_percent = (u_int32_t)((off_t)(bytes_completed*100)/bytes_total); 151 152 if (cur_percent != new_percent) { 153 printf("\r%d%%", new_percent); 154 if (bytes_completed == bytes_total) 155 printf("\rdone\n"); 156 fflush(stdout); 157 } 158} 159 160unsigned char *align_buffer(unsigned char *buf, off_t pos) { 161 /* return a pointer to the start of the buffer which should be written, 162 offset from the given buffer by 0, 1, or 2 bytes, so that the 3-byte 163 pattern which the buffer contains is aligned with the previous write. */ 164 return (unsigned char *)((uintptr_t)buf + (unsigned int)(pos % 3)); 165} 166 167void verification_failure(off_t count) { 168 if (sizeof(off_t) == 4) 169 printf("warning: failed to verify write at offset %d\n", count); 170 else if (sizeof(off_t) == 8) 171 printf("warning: failed to verify write at offset %lld\n", count); 172 else 173 printf("warning: previous write failed to verify!\n"); 174 fflush(stdout); 175} 176 177void overwrite(int stage) { 178 u_int32_t i, j; 179 off_t count = 0; 180 unsigned char *buffptr = buffer; 181 182 lseek(file, 0, SEEK_SET); 183 while (count < file_size - buffsize) { 184 if (stage == W_RANDOM) { 185 randomize_buffer(buffer, buffsize); 186 } else if (stage == W_TRIPLE) { 187 buffptr = align_buffer(buffer, count); 188 } 189 i = write(file, buffptr, buffsize); 190 if (options & OPT_VERIFY) { 191 /* verify the write */ 192 lseek(file, count, SEEK_SET); 193 j = read(file, verify_buffer, buffsize); 194 if (!(i == j && !memcmp(verify_buffer, buffptr, buffsize))) { 195 verification_failure(count); 196 } 197 } 198 if (options & OPT_V) { 199 update_progress(i); 200 } 201 count += i; 202 } 203 if (stage == W_RANDOM) { 204 randomize_buffer(buffer, file_size - count); 205 } else if (stage == W_TRIPLE) { 206 buffptr = align_buffer(buffer, count); 207 } 208 i = write(file, buffptr, file_size - count); 209 if (options & OPT_VERIFY) { 210 /* verify the write */ 211 lseek(file, count, SEEK_SET); 212 j = read(file, verify_buffer, file_size - count); 213 if (!(i == j && !memcmp(verify_buffer, buffptr, file_size - count))) { 214 verification_failure(count); 215 } 216 } 217 if (options & OPT_V) { 218 update_progress(i); 219 } 220 flush(file); 221 lseek(file, 0, SEEK_SET); 222} 223 224void overwrite_random(int num_passes) { 225 int i; 226 227 for (i = 0; i < num_passes; i++) { 228 overwrite(W_RANDOM); 229 } 230} 231 232void overwrite_byte(int byte) { 233 memset(buffer, byte, buffsize); 234 overwrite(W_SINGLE); 235} 236 237void overwrite_bytes(unsigned int byte1, unsigned int byte2, unsigned int byte3) { 238 u_int32_t val[3], *p = (u_int32_t *)buffer; 239 unsigned int i, mod12buffsize = allocated_buffsize - (allocated_buffsize % 12); 240 241 val[0] = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte1; 242 val[1] = (byte2 << 24) | (byte3 << 16) | (byte1 << 8) | byte2; 243 val[2] = (byte3 << 24) | (byte1 << 16) | (byte2 << 8) | byte3; 244 245 /* fill buffer 12 bytes at a time, optimized for 4-byte alignment */ 246 for (i = 0; i < mod12buffsize; i += 12) { 247 *p++ = val[0]; 248 *p++ = val[1]; 249 *p++ = val[2]; 250 } 251 while (i < allocated_buffsize) { 252 buffer[i] = ((unsigned char *)&val[0])[i % 3]; 253 i++; 254 } 255 overwrite(W_TRIPLE); 256} 257 258void overwrite_file() { 259 bytes_completed = 0; 260 bytes_total = 0; 261 262 if (!file_size) { 263 /* nothing to overwrite in a zero-length file */ 264 if (options & OPT_V) { 265 printf("\rdone\n"); 266 fflush(stdout); 267 } 268 return; 269 } 270 271 if (options & OPT_ZERO) { 272 bytes_total = file_size; 273 } 274 if (seclevel==1) { 275 /* simple one-pass overwrite */ 276 bytes_total += file_size*1; 277 overwrite_random(1); 278 } else if (seclevel==7) { 279 /* DoD-compliant 7-pass overwrite */ 280 bytes_total += file_size*7; 281 overwrite_byte(0xF6); 282 overwrite_byte(0x00); 283 overwrite_byte(0xFF); 284 overwrite_random(1); 285 overwrite_byte(0x00); 286 overwrite_byte(0xFF); 287 overwrite_random(1); 288 } else { 289 /* Gutmann 35-pass overwrite */ 290 bytes_total += file_size*35; 291 overwrite_random(4); 292 overwrite_byte(0x55); 293 overwrite_byte(0xAA); 294 overwrite_bytes(0x92, 0x49, 0x24); 295 overwrite_bytes(0x49, 0x24, 0x92); 296 overwrite_bytes(0x24, 0x92, 0x49); 297 overwrite_byte(0x00); 298 overwrite_byte(0x11); 299 overwrite_byte(0x22); 300 overwrite_byte(0x33); 301 overwrite_byte(0x44); 302 overwrite_byte(0x55); 303 overwrite_byte(0x66); 304 overwrite_byte(0x77); 305 overwrite_byte(0x88); 306 overwrite_byte(0x99); 307 overwrite_byte(0xAA); 308 overwrite_byte(0xBB); 309 overwrite_byte(0xCC); 310 overwrite_byte(0xDD); 311 overwrite_byte(0xEE); 312 overwrite_byte(0xFF); 313 overwrite_bytes(0x92, 0x49, 0x24); 314 overwrite_bytes(0x49, 0x24, 0x92); 315 overwrite_bytes(0x24, 0x92, 0x49); 316 overwrite_bytes(0x6D, 0xB6, 0xDB); 317 overwrite_bytes(0xB6, 0xDB, 0x6D); 318 overwrite_bytes(0xDB, 0x6D, 0xB6); 319 overwrite_random(4); 320 } 321 if (options & OPT_ZERO) { 322 overwrite_byte(0x00); 323 } 324} 325 326int sunlink(const char *path) { 327 struct stat statbuf; 328 struct statfs fs_stats; 329#if HAVE_LINUX_EXT2_FS_H 330 int flags = 0; 331#endif 332 int fmode = (options & OPT_VERIFY) ? O_RDWR : O_WRONLY; 333 struct flock flock; 334 335 if (lstat(path, &statbuf) == -1) 336 return -1; 337 if (!S_ISREG(statbuf.st_mode)) 338 return rename_unlink(path); 339 340 if (statbuf.st_nlink > 1) { 341 rename_unlink(path); 342 errno = EMLINK; 343 return -1; 344 } 345 346 if ( (file = open(path, fmode)) == -1) /* BSD doesn't support O_SYNC */ 347 return -1; 348 349 if (fcntl(file, F_WRLCK, &flock) == -1) { 350 close(file); 351 return -1; 352 } 353 354 if (fstatfs(file, &fs_stats) == -1 && errno != ENOSYS) { 355 close(file); 356 return -1; 357 } 358 359 /* warn when trying to overwrite files on a non-local fs, 360 since there are no guarantees that writes will not be 361 buffered on the server, or will overwrite the same spot. */ 362 if (options & OPT_V) { 363 if ((fs_stats.f_flags & MNT_LOCAL) == 0) { 364 printf("warning: %s is not on a local filesystem!\n", path); 365 fflush(stdout); 366 } 367 } 368 369#if HAVE_LINUX_EXT2_FS_H 370 if (fs_stats.f_type == EXT2_SUPER_MAGIC) 371 if (ioctl(file, EXT2_IOC_GETFLAGS, &flags) == -1) { 372 close(file); 373 return -1; 374 } 375 376 if ( (flags & EXT2_UNRM_FL) || (flags & EXT2_IMMUTABLE_FL) || 377 (flags & EXT2_APPEND_FL) ) 378 { 379 close(file); 380 errno = EPERM; 381 return -1; 382 } 383 384#endif /* HAVE_LINUX_EXT2_FS_H */ 385 386/* chflags(2) turns out to be a different system call in every BSD 387 derivative. The important thing is to make sure we'll be able to 388 unlink it after we're through messing around. Unlinking it first 389 would remove the need for any of these checks, but would leave the 390 user with no way to overwrite the file if the process was 391 interrupted during the overwriting. So, instead we assume that the 392 open() above will fail on immutable and append-only files and try 393 and catch only platforms supporting NOUNLINK here. 394 395 FreeBSD - supports NOUNLINK (from 4.4 on?) 396 MacOS X - doesn't support NOUNLINK (as of 10.3.5) 397 OpenBSD - doesn't support NOUNLINK (as of 3.1) 398 Tru64 - unknown 399 400 Note: unsupported flags are defined as 0 at the top of this file, 401 so a specific platform check is not required here. 402*/ 403 404#if HAVE_CHFLAGS 405 if ((statbuf.st_flags & UF_IMMUTABLE) || 406 (statbuf.st_flags & UF_APPEND) || 407 (statbuf.st_flags & UF_NOUNLINK) || 408 (statbuf.st_flags & SF_IMMUTABLE) || 409 (statbuf.st_flags & SF_APPEND) || 410 (statbuf.st_flags & SF_NOUNLINK)) 411 { 412 close(file); 413 errno = EPERM; 414 return -1; 415 } 416#endif /* HAVE_CHFLAGS */ 417 418 if (init_write_buffer(&statbuf, &fs_stats) == -1) { 419 close(file); 420 return -1; 421 } 422#if defined F_NOCACHE 423 /* before performing file I/O, set F_NOCACHE to prevent caching */ 424 (void)fcntl(file, F_NOCACHE, 1); 425#endif 426 427 overwrite_file(); 428 429#if HAVE_LINUX_EXT2_FS_H 430 ioctl(file, EXT2_IOC_SETFLAGS, EXT2_SECRM_FL); 431#endif 432 433 if ((options & OPT_N) == 0) { 434 if (ftruncate(file, 0) == -1) { 435 close(file); 436 return -1; 437 } 438 } 439 440 close(file); 441 442#if __APPLE__ 443 /* Also overwrite the file's resource fork, if present. */ 444 { 445 static const char *RSRCFORKSPEC = "/..namedfork/rsrc"; 446 size_t rsrc_fork_size; 447 size_t rsrc_path_size = strlen(path) + strlen(RSRCFORKSPEC) + 1; 448 char *rsrc_path = (char *)alloca(rsrc_path_size); 449 if (rsrc_path == NULL) { 450 errno = ENOMEM; 451 return -1; 452 } 453 if (snprintf(rsrc_path, MAXPATHLEN, 454 "%s%s", path, RSRCFORKSPEC ) > MAXPATHLEN - 1) { 455 errno = ENAMETOOLONG; 456 return -1; 457 } 458 459 if (lstat(rsrc_path, &statbuf) != 0) { 460 int err = errno; 461 if (err == ENOENT || err == ENOTDIR) { 462 rsrc_fork_size = 0; 463 } else { 464 return -1; 465 } 466 } else { 467 rsrc_fork_size = statbuf.st_size; 468 } 469 470 if (rsrc_fork_size > 0) { 471 472 if ((file = open(rsrc_path, O_WRONLY)) == -1) { 473 return -1; 474 } 475 if (fcntl(file, F_WRLCK, &flock) == -1) { 476 close(file); 477 return -1; 478 } 479 480 if (options & OPT_V) { 481 printf("removing %s\n", rsrc_path); 482 fflush(stdout); 483 } 484 485 if (init_write_buffer(&statbuf, &fs_stats) == -1) { 486 close(file); 487 return -1; 488 } 489 #if defined F_NOCACHE 490 /* before performing file I/O, set F_NOCACHE to prevent caching */ 491 (void)fcntl(file, F_NOCACHE, 1); 492 #endif 493 494 overwrite_file(); 495 496 if ((options & OPT_N) == 0) { 497 if (ftruncate(file, 0) == -1) { 498 close(file); 499 return -1; 500 } 501 } 502 close(file); 503 } 504 } 505#endif /* __APPLE__ */ 506 507 if (options & OPT_N) 508 return 0; 509 510 return rename_unlink(path); 511} 512