1 /* this file is part of srm http://srm.sourceforge.net/
2    It is licensed under the MIT/X11 license */
3 
4 #include "config.h"
5 
6 #ifdef _MSC_VER
7 #include "AltStreams.h"
8 #endif
9 
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <time.h>
19 #include <unistd.h>
20 
21 #if defined(__unix__)
22 #include <sys/ioctl.h>
23 #include <stdint.h>
24 #endif
25 
26 #ifdef HAVE_SYS_VFS_H
27 #include <sys/vfs.h>
28 #endif
29 
30 #if defined(__APPLE__)
31 #include <sys/disk.h>
32 #include <sys/paths.h>
33 #endif
34 
35 #if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
36 #include <sys/param.h>
37 #include <sys/mount.h>
38 #endif
39 
40 #if defined(HAVE_LINUX_EXT3_FS_H)
41 #include <linux/fs.h>
42 #include <linux/ext3_fs.h>
43 
44 #define EXT2_IOC_GETFLAGS EXT3_IOC_GETFLAGS
45 #define EXT2_UNRM_FL EXT3_UNRM_FL
46 #define EXT2_IMMUTABLE_FL EXT3_IMMUTABLE_FL
47 #define EXT2_APPEND_FL EXT3_APPEND_FL
48 #define EXT2_IOC_SETFLAGS EXT3_IOC_SETFLAGS
49 #define EXT2_SECRM_FL EXT3_SECRM_FL
50 #define EXT2_IOC_SETFLAGS EXT3_IOC_SETFLAGS
51 #ifndef EXT2_SUPER_MAGIC
52 #define EXT2_SUPER_MAGIC EXT3_SUPER_MAGIC
53 #endif
54 
55 #elif defined(HAVE_LINUX_EXT2_FS_H)
56 #include <linux/fs.h>
57 #include <linux/ext2_fs.h>
58 #endif
59 
60 #if defined(HAVE_ATTR_XATTR_H)
61 #include <attr/xattr.h>
62 #undef HAVE_SYS_XATTR_H
63 #undef HAVE_SYS_EXTATTR_H
64 #elif defined(HAVE_SYS_XATTR_H)
65 #include <sys/xattr.h>
66 #undef HAVE_SYS_EXTATTR_H
67 #elif defined(HAVE_SYS_EXTATTR_H)
68 #include <sys/extattr.h>
69 #include <libutil.h>
70 #endif
71 
72 #include "srm.h"
73 #include "impl.h"
74 
75 #ifndef O_SYNC
76 #define O_SYNC 0
77 #endif
78 #ifndef _O_BINARY
79 #define _O_BINARY 0
80 #endif
81 
82 #define KiB 1024
83 #define MiB (KiB*KiB)
84 #define GiB (KiB*KiB*KiB)
85 
86 #ifdef _MSC_VER
87 typedef long long my_off_t;
88 #else
89 typedef off_t my_off_t;
90 #endif
91 
92 struct srm_target
93 {
94   int fd;
95   const char* file_name;
96   my_off_t file_size;
97   unsigned char *buffer;
98   unsigned buffer_size;
99   int options;
100 };
101 
102 static volatile int SIGINT_received = 0;
103 #if defined(__unix__)
104 #include <signal.h>
105 #if defined(__linux__) && !defined(__USE_GNU)
106 typedef __sighandler_t sighandler_t;
107 #endif
108 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
109 typedef sig_t sighandler_t;
110 #endif
111 
sigint_handler(int signo)112 static void sigint_handler(int signo)
113 {
114   SIGINT_received = signo;
115 }
116 int sunlink_impl(const char *path, const int options);
117 
sunlink(const char * path,const int options)118 int sunlink(const char *path, const int options)
119 {
120 #ifdef SIGUSR2
121   sighandler_t usr2=signal(SIGUSR2, sigint_handler);
122 #endif
123 #ifdef SIGINFO
124   sighandler_t info=signal(SIGINFO, sigint_handler);
125 #endif
126 #ifdef SIGPIPE
127   sighandler_t pipe=signal(SIGPIPE, SIG_IGN);
128 #endif
129 
130   int ret=sunlink_impl(path, options);
131 
132 #ifdef SIGPIPE
133   signal(SIGPIPE, pipe);
134 #endif
135 #ifdef SIGINFO
136   signal(SIGINFO, info);
137 #endif
138 #ifdef SIGUSR2
139   signal(SIGUSR2, usr2);
140 #endif
141   return ret;
142 }
143 
144 #else /* __unix__ */
145 #define sunlink_impl sunlink
146 #endif
147 
148 /**
149    writes a buffer to a file descriptor. Ensures that the complete
150    buffer is written.
151 
152    ripped from Advanced Programming in the Unix Environment by Richard Stevens
153 
154    @param fd file descriptor
155    @param buf pointer to a buffer
156    @param count size of buf in bytes
157 
158    @return upon success the number of bytes written, upon error the negative return code from write() (see the errno variable for details)
159 */
writen(const int fd,const void * buf,const size_t count)160 static ssize_t writen(const int fd, const void* buf, const size_t count)
161 {
162   const char *ptr=(const char*)buf;
163   size_t nleft=count;
164 
165   if(fd<0 || !buf) return -1;
166 
167   while(nleft > 0)
168     {
169       ssize_t nwritten;
170       if( (nwritten=write(fd, ptr, nleft)) < 0)
171 	return nwritten;
172       nleft -= nwritten;
173       ptr   += nwritten;
174     }
175 
176   return count;
177 }
178 
flush(int fd)179 static void flush(int fd)
180 {
181   /* force buffered writes to be flushed to disk */
182 #if defined F_FULLFSYNC
183   /* F_FULLFSYNC is equivalent to fsync plus device flush to media */
184   if (fcntl(fd, F_FULLFSYNC, NULL) != 0) {
185     /* we're not on a fs that supports this; fall back to plain fsync */
186     fsync(fd);
187   }
188 #elif HAVE_FDATASYNC
189   fdatasync(fd);
190 #else
191   fsync(fd);
192 #endif
193 }
194 
195 #if defined(HAVE_ATTR_XATTR_H) || defined(HAVE_SYS_XATTR_H) || defined(HAVE_SYS_EXTATTR_H)
extattr_overwrite(struct srm_target * srm,const int pass,const int attrnamespace)196 static int extattr_overwrite(struct srm_target *srm, const int pass, const int attrnamespace)
197 {
198   char *list = NULL;
199   unsigned char *value = NULL;
200   size_t list_size = 256, value_size = 0, key_len = 0;
201   ssize_t len = 0, i = 0;
202   /* get list of atrributes */
203   for(;;) {
204     list = alloca(list_size);
205     if (! list) {
206       errno = ENOMEM;
207       return -1;
208     }
209     errno = 0;
210 #if defined(HAVE_ATTR_XATTR_H) || defined(HAVE_SYS_XATTR_H)
211 #if defined(HAVE_ATTR_XATTR_H)
212     len = flistxattr(srm->fd, list, list_size);
213 #elif defined(HAVE_SYS_XATTR_H) && defined(__APPLE__)
214     len = flistxattr(srm->fd, list, list_size, 0);
215 #endif
216     if (len < 0 && errno == ERANGE) {
217       list_size *= 2;
218       if (list_size > 1024*1024) {
219 	error("file has very large extended attribute list, giving up.");
220 	break;
221       }
222       continue;
223     }
224 #elif defined(HAVE_SYS_EXTATTR_H)
225     len = extattr_list_fd(srm->fd, attrnamespace, NULL, 0);
226     if (len > list_size) {
227       list_size = len;
228       continue;
229     }
230     len = extattr_list_fd(srm->fd, attrnamespace, list, list_size);
231 #endif
232     if (len < 0) {
233       break;
234     }
235     break;
236   }
237 
238   /* iterate list of attributes */
239   for(i = 0; i < len; i += key_len + 1) {
240     int ret = 0;
241     ssize_t val_len = 0;
242     char *key = NULL;
243 #if defined(HAVE_ATTR_XATTR_H) || defined(HAVE_SYS_XATTR_H)
244     key = list + i;
245     key_len = strlen(key);
246 #if defined(HAVE_ATTR_XATTR_H)
247     val_len = fgetxattr(srm->fd, key, NULL, 0);
248 #elif defined(HAVE_SYS_XATTR_H) && defined(__APPLE__)
249     val_len = fgetxattr(srm->fd, key, NULL, 0, 0, 0);
250 #endif
251 #elif defined(HAVE_SYS_EXTATTR_H)
252     char keybuf[257];
253     key_len = *((unsigned char*)(list + i));
254     memcpy(keybuf, list+i+1, key_len);
255     keybuf[key_len] = 0;
256     key = keybuf;
257     val_len = extattr_get_fd(srm->fd, attrnamespace, key, NULL, 0);
258 #endif
259     if ( ((srm->options & SRM_OPT_V) > 1) && pass == 1) {
260       char *name_space="";
261 #if defined(HAVE_SYS_EXTATTR_H)
262       extattr_namespace_to_string(attrnamespace, &namespace);
263 #endif
264       error("found extended attribute %s %s of %i bytes", name_space, key, (int)val_len);
265     }
266     if (val_len > (ssize_t)value_size) {
267       value_size = val_len;
268       value = alloca(value_size);
269       if (! value) {
270 	errno = ENOMEM;
271 	return -1;
272       }
273       fill(value, value_size, srm->buffer, srm->buffer_size);
274     }
275 #if defined(HAVE_ATTR_XATTR_H)
276     ret = fsetxattr(srm->fd, key, value, val_len, XATTR_REPLACE);
277 #elif defined(HAVE_SYS_XATTR_H) && defined(__APPLE__)
278     ret = fsetxattr(srm->fd, key, value, val_len, 0, XATTR_REPLACE);
279 #elif defined(HAVE_SYS_EXTATTR_H)
280     ret = extattr_set_fd(srm->fd, attrnamespace, key, value, val_len);
281 #endif
282     if (ret < 0) {
283       errorp("could not overwrite extended attribute %s", key);
284     }
285   }
286 
287   (void)attrnamespace;
288   return 0;
289 }
290 #endif
291 
overwrite(struct srm_target * srm,const int pass)292 static int overwrite(struct srm_target *srm, const int pass)
293 {
294   unsigned last_val = ~0u;
295   my_off_t i = 0;
296   ssize_t w;
297 
298   if(!srm) return -1;
299   if(!srm->buffer) return -1;
300   if(srm->buffer_size < 1) return -1;
301 
302   /* check for extended attributes */
303 #if defined(HAVE_ATTR_XATTR_H) || defined(HAVE_SYS_XATTR_H)
304   if (extattr_overwrite(srm, pass, 0) < 0) {
305     return -1;
306   }
307 #elif defined(HAVE_SYS_EXTATTR_H)
308   if (extattr_overwrite(srm, pass, EXTATTR_NAMESPACE_USER) < 0) {
309     return -1;
310   }
311   if (extattr_overwrite(srm, pass, EXTATTR_NAMESPACE_SYSTEM) < 0) {
312     return -1;
313   }
314 #endif
315 
316   if(lseek(srm->fd, 0, SEEK_SET) != 0)
317     {
318       perror("could not seek");
319       return -1;
320     }
321 
322   if(srm->file_size < (my_off_t)(srm->buffer_size))
323     {
324       w=writen(srm->fd, srm->buffer, srm->file_size);
325       if(w != srm->file_size)
326 	return -1;
327     }
328   else
329     {
330       while (i < srm->file_size - (my_off_t)srm->buffer_size)
331 	{
332 	  w=writen(srm->fd, srm->buffer, srm->buffer_size);
333 	  if(w != (ssize_t)(srm->buffer_size))
334 	    return -1;
335 	  i += w;
336 
337 	  if ((srm->options & SRM_OPT_V) > 1 || SIGINT_received) {
338 	      unsigned val = 0, file_size = 0;
339 	      char c = '.';
340 	      if (srm->file_size < MiB) {
341 		  val = i / KiB;
342 		  file_size = (unsigned)(srm->file_size/KiB);
343 		  c = 'K';
344 	      } else if(srm->file_size < GiB) {
345 		  val = i / MiB;
346 		  file_size = (unsigned)(srm->file_size/MiB);
347 		  c = 'M';
348 	      } else {
349 		  val = i / GiB;
350 		  file_size = (unsigned)(srm->file_size/GiB);
351 		  c = 'G';
352 	      }
353 	      if (val != last_val) {
354 		  printf("\rpass %i %u%ciB/%u%ciB     ", pass, val, c, file_size, c);
355 		  fflush(stdout);
356 		  last_val = val;
357 	      }
358 
359 	      if(SIGINT_received)
360 		{
361 		  if(srm->file_name)
362 		    printf("%s\n", srm->file_name);
363 		  else
364 		    putchar('\n');
365 		  SIGINT_received=0;
366 		  fflush(stdout);
367 		}
368 	    }
369 	}
370       w=writen(srm->fd, srm->buffer, srm->file_size - i);
371       if(w != srm->file_size-i)
372 	return -1;
373     }
374 
375   if((srm->options & SRM_OPT_V) > 1)
376     {
377       printf("\rpass %i sync                        ", pass);
378       fflush(stdout);
379     }
380 
381   flush(srm->fd);
382 
383   if(lseek(srm->fd, 0, SEEK_SET) != 0)
384     {
385       perror("could not seek");
386       return -1;
387     }
388 
389   return 0;
390 }
391 
overwrite_random(struct srm_target * srm,const int pass,const int num_passes)392 static int overwrite_random(struct srm_target *srm, const int pass, const int num_passes)
393 {
394   int i;
395 
396   if(!srm) return -1;
397   if(!srm->buffer) return -1;
398   if(srm->buffer_size < 1) return -1;
399 
400   for (i = 0; i < num_passes; i++)
401     {
402       randomize_buffer(srm->buffer, srm->buffer_size);
403       if(overwrite(srm, pass+i) < 0)
404 	return -1;
405     }
406 
407   return 0;
408 }
409 
overwrite_byte(struct srm_target * srm,const int pass,const int byte)410 static int overwrite_byte(struct srm_target *srm, const int pass, const int byte)
411 {
412   if(!srm) return -1;
413   if(!srm->buffer) return -1;
414   if(srm->buffer_size < 1) return -1;
415   memset(srm->buffer, byte, srm->buffer_size);
416   return overwrite(srm, pass);
417 }
418 
overwrite_bytes(struct srm_target * srm,const int pass,const unsigned char byte1,const unsigned char byte2,const unsigned char byte3)419 static int overwrite_bytes(struct srm_target *srm, const int pass, const unsigned char byte1, const unsigned char byte2, const unsigned char byte3)
420 {
421   unsigned char buf[3];
422 
423   if(!srm) return -1;
424   if(!srm->buffer) return -1;
425   if(srm->buffer_size < 1) return -1;
426 
427   buf[0] = byte1;
428   buf[1] = byte2;
429   buf[2] = byte3;
430   fill(srm->buffer, srm->buffer_size, buf, sizeof(buf));
431   return overwrite(srm, pass);
432 }
433 
overwrite_string(struct srm_target * srm,const int pass,const char * str)434 static int overwrite_string(struct srm_target *srm, const int pass, const char *str)
435 {
436   if(!srm) return -1;
437   if(!srm->buffer) return -1;
438   if(srm->buffer_size < 1) return -1;
439   if (!str) return -1;
440 
441   fill(srm->buffer, srm->buffer_size, (const unsigned char*)str, strlen(str));
442   return overwrite(srm, pass);
443 }
444 
overwrite_selector(struct srm_target * srm)445 static int overwrite_selector(struct srm_target *srm)
446 {
447   if(!srm) return -1;
448 
449 #if defined(F_NOCACHE)
450   /* before performing file I/O, set F_NOCACHE to prevent caching */
451   (void)fcntl(srm->fd, F_NOCACHE, 1);
452 #endif
453 
454   if( (srm->buffer = (unsigned char *)alloca(srm->buffer_size)) == NULL )
455     {
456       errno = ENOMEM;
457       return -1;
458     }
459 
460   if(srm->options & SRM_MODE_DOD)
461     {
462       if((srm->options&SRM_OPT_V) > 1)
463 	error("US DoD mode");
464       if(overwrite_byte(srm, 1, 0xF6) < 0) return -1;
465       if(overwrite_byte(srm, 2, 0x00) < 0) return -1;
466       if(overwrite_byte(srm, 3, 0xFF) < 0) return -1;
467       if(overwrite_random(srm, 4, 1) < 0) return -1;
468       if(overwrite_byte(srm, 5, 0x00) < 0) return -1;
469       if(overwrite_byte(srm, 6, 0xFF) < 0) return -1;
470       if(overwrite_random(srm, 7, 1) < 0) return -1;
471     }
472   else if(srm->options & SRM_MODE_DOE)
473     {
474       if((srm->options&SRM_OPT_V) > 1)
475 	error("US DoE mode");
476       if(overwrite_random(srm, 1, 2) < 0) return -1;
477       if(overwrite_bytes(srm, 3, 'D', 'o', 'E') < 0) return -1;
478     }
479   else if(srm->options & SRM_MODE_OPENBSD)
480     {
481       if((srm->options&SRM_OPT_V) > 1)
482 	error("OpenBSD mode");
483       if(overwrite_byte(srm, 1, 0xFF) < 0) return -1;
484       if(overwrite_byte(srm, 2, 0x00) < 0) return -1;
485       if(overwrite_byte(srm, 3, 0xFF) < 0) return -1;
486     }
487   else if(srm->options & SRM_MODE_SIMPLE)
488     {
489       if((srm->options&SRM_OPT_V) > 1)
490 	error("Simple mode");
491       if(overwrite_byte(srm, 1, 0x00) < 0) return -1;
492     }
493   else if(srm->options & SRM_MODE_RCMP)
494     {
495       if((srm->options&SRM_OPT_V) > 1)
496 	error("RCMP mode");
497       if(overwrite_byte(srm, 1, 0x00) < 0) return -1;
498       if(overwrite_byte(srm, 2, 0xFF) < 0) return -1;
499       if(overwrite_string(srm, 3, "RCMP") < 0) return -1;
500     }
501   else
502     {
503       if(! (srm->options & SRM_MODE_35))
504 	error("something is strange, did not have mode_35 bit");
505       if((srm->options&SRM_OPT_V) > 1)
506 	error("Full 35-pass mode (Gutmann method)");
507       if(overwrite_random(srm, 1, 4) < 0) return -1;
508       if(overwrite_byte(srm, 5, 0x55) < 0) return -1;
509       if(overwrite_byte(srm, 6, 0xAA) < 0) return -1;
510       if(overwrite_bytes(srm, 7, 0x92, 0x49, 0x24) < 0) return -1;
511       if(overwrite_bytes(srm, 8, 0x49, 0x24, 0x92) < 0) return -1;
512       if(overwrite_bytes(srm, 9, 0x24, 0x92, 0x49) < 0) return -1;
513       if(overwrite_byte(srm, 10, 0x00) < 0) return -1;
514       if(overwrite_byte(srm, 11, 0x11) < 0) return -1;
515       if(overwrite_byte(srm, 12, 0x22) < 0) return -1;
516       if(overwrite_byte(srm, 13, 0x33) < 0) return -1;
517       if(overwrite_byte(srm, 14, 0x44) < 0) return -1;
518       if(overwrite_byte(srm, 15, 0x55) < 0) return -1;
519       if(overwrite_byte(srm, 16, 0x66) < 0) return -1;
520       if(overwrite_byte(srm, 17, 0x77) < 0) return -1;
521       if(overwrite_byte(srm, 18, 0x88) < 0) return -1;
522       if(overwrite_byte(srm, 19, 0x99) < 0) return -1;
523       if(overwrite_byte(srm, 20, 0xAA) < 0) return -1;
524       if(overwrite_byte(srm, 21, 0xBB) < 0) return -1;
525       if(overwrite_byte(srm, 22, 0xCC) < 0) return -1;
526       if(overwrite_byte(srm, 23, 0xDD) < 0) return -1;
527       if(overwrite_byte(srm, 24, 0xEE) < 0) return -1;
528       if(overwrite_byte(srm, 25, 0xFF) < 0) return -1;
529       if(overwrite_bytes(srm, 26, 0x92, 0x49, 0x24) < 0) return -1;
530       if(overwrite_bytes(srm, 27, 0x49, 0x24, 0x92) < 0) return -1;
531       if(overwrite_bytes(srm, 28, 0x24, 0x92, 0x49) < 0) return -1;
532       if(overwrite_bytes(srm, 29, 0x6D, 0xB6, 0xDB) < 0) return -1;
533       if(overwrite_bytes(srm, 30, 0xB6, 0xDB, 0x6D) < 0) return -1;
534       if(overwrite_bytes(srm, 31, 0xDB, 0x6D, 0xB6) < 0) return -1;
535       if(overwrite_random(srm, 32, 4) < 0) return -1;
536       /* if you want to backup your partition or shrink your vmware image having the file zero-ed gives best compression results. */
537       if(overwrite_byte(srm, 36, 0x00) < 0) return -1;
538     }
539 #if 0
540   if((srm->options & SRM_OPT_V) > 1)
541     printf("\n");
542 #endif
543   return 0;
544 }
545 
546 #ifdef _MSC_VER
getFileSize(WCHAR * fn)547 static my_off_t getFileSize(WCHAR *fn)
548 {
549     /* it's a pain, but it seems that the only way to get the size of an alternate data stream is to read it once. */
550 #if 1
551     my_off_t size = 0, r;
552     int fd = _wopen(fn, O_RDONLY);
553     if (fd < 0) return 0;
554     do {
555         char buf[4096];
556 	r = read(fd, buf, sizeof(buf));
557 	if (r > 0) size += r;
558     } while(r > 0);
559     close(fd);
560     return size;
561 #else
562     WCHAR fn2[32767];
563     WIN32_FILE_ATTRIBUTE_DATA w32_fad;
564 
565     wcscpy(fn2, L"\\\\?\\");
566     wcscat(fn2, fn);
567     if (! GetFileAttributesEx((char*)fn2, GetFileExInfoStandard, &w32_fad)) {
568 	error("could not get file attributes of %S", fn2);
569 	return 0;
570     }
571     return (((long long)w32_fad.nFileSizeHigh) << 32) | ((long long)w32_fad.nFileSizeLow);
572 #endif
573 }
574 
ntfsHardLinks(const char * fn)575 int ntfsHardLinks(const char *fn)
576 {
577     int ret = -1;
578     BY_HANDLE_FILE_INFORMATION hfi;
579     HANDLE hFile = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
580     if (hFile == INVALID_HANDLE_VALUE) {
581 	return ret;
582     }
583 
584     if (GetFileInformationByHandle(hFile, &hfi)) {
585 	ret = hfi.nNumberOfLinks;
586     }
587 
588     CloseHandle(hFile);
589     return ret;
590 }
591 #endif
592 
sunlink_impl(const char * path,const int options)593 int sunlink_impl(const char *path, const int options)
594 {
595   const int oflags = O_WRONLY|O_SYNC|_O_BINARY;
596   struct srm_target srm;
597 #if defined(_MSC_VER)
598   struct __stat64 statbuf;
599 #else
600   struct stat statbuf;
601 #endif
602 #if defined(__unix__) || defined(__APPLE__)
603   struct flock flock;
604 #endif
605 
606   /* check function arguments */
607   if(!path) return -1;
608 
609   memset(&srm, 0, sizeof(srm));
610   srm.file_name = path;
611   srm.options = options;
612 
613   /* check if path exists */
614 #if defined(_MSC_VER)
615   if (_stat64(path, &statbuf) < 0) {
616     return -1;
617   }
618 #else
619   if (lstat(path, &statbuf) < 0) {
620     return -1;
621   }
622 #endif
623 
624   srm.file_size = statbuf.st_size;
625   if (srm.file_size < 0) {
626     error("%s : file size: %lli, can not work with negative values", path, (long long)srm.file_size);
627     return -1;
628   }
629 #ifdef _MSC_VER
630   srm.buffer_size = 4096;
631 #else
632   srm.buffer_size = statbuf.st_blksize;
633 #endif
634   if(srm.buffer_size < 16)
635     srm.buffer_size = 512;
636   if((srm.options & SRM_OPT_V) > 2)
637     error("file size: %lli, buffer_size=%u", (long long)srm.file_size, srm.buffer_size);
638 
639 #if defined(__linux__)
640   if(S_ISBLK(statbuf.st_mode))
641     {
642       int secsize=512;
643       long blocks=0;
644       uint64_t u=0, u_;
645 
646       if( (srm.fd = open(srm.file_name, O_WRONLY)) < 0)
647 	return -1;
648 
649       if(ioctl(srm.fd, BLKSSZGET, &secsize) < 0)
650 	{
651 	  perror("could not ioctl(BLKSSZGET)");
652 	  return -1;
653 	}
654       if((options&SRM_OPT_V) > 2)
655 	error("sector size %i bytes", secsize);
656 
657       if(ioctl(srm.fd, BLKGETSIZE, &blocks) < 0)
658 	{
659 	  perror("could not ioctl(BLKGETSIZE)");
660 	  return -1;
661 	}
662       if((options&SRM_OPT_V) > 2)
663 	error("BLKGETSIZE %i blocks", (int)blocks);
664 
665       if(ioctl(srm.fd, BLKGETSIZE64, &u) < 0)
666 	{
667 	  perror("could not ioctl(BLKGETSIZE64)");
668 	  return -1;
669 	}
670       if((options&SRM_OPT_V) > 2)
671 	error("BLKGETSIZE64 %llu bytes", (unsigned long long)u);
672 
673       u_=((uint64_t)blocks)*secsize;
674       if(u_ != u)
675 	  error("!Warning! sectorsize*blocks:%llu != bytes:%llu", (long long unsigned) u_, (long long unsigned) u);
676 
677       srm.file_size = u;
678       srm.buffer_size = secsize;
679 
680       if(srm.file_size == 0)
681 	{
682 	  close(srm.fd);
683 	  if (srm.options & SRM_OPT_V)
684 	    error("could not determine block device %s filesize", srm.file_name);
685 	  errno = EIO;
686 	  return -1;
687 	}
688 
689       if((options&SRM_OPT_V) > 1)
690 	error("block device %s size: %llu bytes", srm.file_name, (unsigned long long)u);
691 
692       if(overwrite_selector(&srm) < 0)
693 	{
694 	  int e=errno;
695 	  if (srm.options & SRM_OPT_V)
696 	    errorp("could not overwrite device %s", srm.file_name);
697 	  close(srm.fd);
698 	  errno=e;
699 	  return -1;
700 	}
701       close(srm.fd);
702       return 0;
703     }
704 #endif
705 
706     if (!S_ISREG(statbuf.st_mode)) {
707 	return rename_unlink(srm.file_name);
708     }
709 
710 #if defined(_MSC_VER)
711   /* check for alternate NTFS data streams */
712   /* code taken from http://www.flexhex.com/docs/articles/alternate-streams.phtml */
713   {
714     static NTQUERYINFORMATIONFILE NtQueryInformationFile = 0;
715     char InfoBlock[64 * 1024];
716     PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)InfoBlock;
717 
718     if (! NtQueryInformationFile) {
719 	NtQueryInformationFile = (NTQUERYINFORMATIONFILE) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationFile");
720     }
721 
722     /* get information on current file */
723     if (NtQueryInformationFile) {
724 	IO_STATUS_BLOCK ioStatus;
725 	HANDLE hFile = CreateFile(srm.file_name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
726 	NtQueryInformationFile(hFile, &ioStatus, InfoBlock, sizeof(InfoBlock), FileStreamInformation);
727 	CloseHandle(hFile);
728 
729     /* iterate over information looking from streams */
730     for (;;) {
731 	WCHAR wszStreamName[MAX_PATH];
732 	int i;
733 	char ads_fn[MAX_PATH], buf[MAX_PATH];
734 	WCHAR ads_fn_w[MAX_PATH];
735 	struct srm_target ads = srm;
736 
737 	/* Check if stream info block is empty (directory may have no stream) */
738 	if (pStreamInfo->StreamNameLength == 0) break;
739 
740 	/* Get null-terminated stream name */
741 	memcpy(wszStreamName, pStreamInfo->StreamName, pStreamInfo->StreamNameLength);
742 	wszStreamName[pStreamInfo->StreamNameLength / sizeof(WCHAR)] = L'\0';
743 
744 	/* skip the default data stream */
745 	if (wcscmp(wszStreamName, L"::$DATA") == 0) {
746 	    goto next_ads;
747 	}
748 
749 	i = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszStreamName, -1, buf, sizeof(buf)-1, NULL, NULL);
750 	if (i > 0) {
751 	    buf[i] = 0;
752 	    snprintf(ads_fn, sizeof(ads_fn), "%s%s", srm.file_name, buf);
753 	    ads.file_name = ads_fn;
754 	}
755 
756 	i = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, srm.file_name, -1, ads_fn_w, MAX_PATH);
757 	if (i <= 0) {
758 	    error("could not convert %S to multi byte string", wszStreamName);
759 	    goto next_ads;
760 	}
761 	ads_fn_w[i] = 0;
762 	wcscat(ads_fn_w, wszStreamName);
763 
764 	ads.file_size = getFileSize(ads_fn_w);
765 	if (ads.file_size == 0) {
766 	    if ((srm.options & SRM_OPT_V) > 1) {
767 		error("skipping alternate data stream %S of %lli bytes %i %i", ads_fn_w, ads.file_size);
768 	    }
769 	    goto next_ads;
770 	}
771 
772 	ads.fd = _wopen(ads_fn_w, oflags);
773 	if (ads.fd < 0) {
774 	    errorp("could not open alternate data stream %S", wszStreamName);
775 	    goto next_ads;
776 	}
777 
778 	if (srm.options & SRM_OPT_V) {
779 	    error("removing alternate data stream %S of %lli bytes", ads_fn_w, ads.file_size);
780 	}
781 	if (ads.file_size > 0) {
782 	    if(overwrite_selector(&ads) < 0)
783 		{
784 		if (ads.options & SRM_OPT_V)
785 		    errorp("could not overwrite alternate data stream %S", ads_fn_w);
786 		}
787 	    ftruncate(ads.fd, 0);
788 	}
789 	close(ads.fd);
790 
791 next_ads:
792 	if (pStreamInfo->NextEntryOffset == 0) break;
793 	pStreamInfo = (PFILE_STREAM_INFORMATION) ((LPBYTE)pStreamInfo + pStreamInfo->NextEntryOffset);
794     }
795     }
796   }
797 #endif
798 
799 #ifdef _MSC_VER
800   if (ntfsHardLinks(srm.file_name) > 1)
801 #else
802   if (statbuf.st_nlink > 1)
803 #endif
804   {
805     rename_unlink(srm.file_name);
806     errno = EMLINK;
807     return -1;
808   }
809 
810   if (srm.file_size==0) {
811     return rename_unlink(srm.file_name);
812   }
813 
814   if ( (srm.fd = open(srm.file_name, oflags)) < 0)
815     return -1;
816 
817 #if defined(__unix__) || defined(__APPLE__)
818   flock.l_type = F_WRLCK;
819   flock.l_whence = SEEK_SET;
820   flock.l_start = 0;
821   flock.l_len = 0;
822   if (fcntl(srm.fd, F_SETLK, &flock) < 0) {
823     int e=errno;
824     flock.l_type = F_WRLCK;
825     flock.l_whence = SEEK_SET;
826     flock.l_start = 0;
827     flock.l_len = 0;
828     flock.l_pid = 0;
829     if (fcntl(srm.fd, F_GETLK, &flock) == 0 && flock.l_pid > 0) {
830       error("can't unlink %s, locked by process %i", srm.file_name, flock.l_pid);
831     }
832     close(srm.fd);
833     errno=e;
834     return -1;
835   }
836 #endif
837 
838 #if defined(HAVE_SYS_VFS_H) || (defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H))
839   {
840     struct statfs fs_stats;
841     if (fstatfs(srm.fd, &fs_stats) < 0 && errno != ENOSYS)
842       {
843 	int e=errno;
844 	close(srm.fd);
845 	errno=e;
846 	return -1;
847       }
848 
849 #if defined(__linux__)
850     srm.buffer_size = fs_stats.f_bsize;
851 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__APPLE__)
852     srm.buffer_size = fs_stats.f_iosize;
853 #else
854 #error Please define your platform.
855 #endif
856     if((srm.options & SRM_OPT_V) > 2)
857       error("buffer_size=%u", srm.buffer_size);
858 
859 #if defined(HAVE_LINUX_EXT2_FS_H) || defined(HAVE_LINUX_EXT3_FS_H)
860     if (fs_stats.f_type == EXT2_SUPER_MAGIC ) /* EXT2_SUPER_MAGIC and EXT3_SUPER_MAGIC are the same */
861       {
862 	int flags = 0;
863 
864 	  if (ioctl(srm.fd, EXT2_IOC_GETFLAGS, &flags) < 0)
865 	    {
866 	      int e=errno;
867 	      close(srm.fd);
868 	      errno=e;
869 	      return -1;
870 	    }
871 
872 	  if ( (flags & EXT2_UNRM_FL) ||
873 	       (flags & EXT2_IMMUTABLE_FL) ||
874 	       (flags & EXT2_APPEND_FL) )
875 	    {
876               if((srm.options & SRM_OPT_V) > 2) {
877                   error("%s has ext2 undelete, immutable or append-only flag", srm.file_name);
878               }
879 	      close(srm.fd);
880 	      errno = EPERM;
881 	      return -1;
882 	    }
883 
884 #ifdef HAVE_LINUX_EXT3_FS_H
885 	  /* if we have the required capabilities we can disable data journaling on ext3 */
886 	  if(fs_stats.f_type == EXT3_SUPER_MAGIC) /* superflous check again, just to make it clear again */
887 	    {
888 	      flags &= ~EXT3_JOURNAL_DATA_FL;
889 	      if (ioctl(srm.fd, EXT3_IOC_SETFLAGS, flags) < 0) {
890                   if (srm.options & SRM_OPT_V)
891                       errorp("could not clear journal data flag for ext3 on %s", srm.file_name);
892 	      }
893 	    }
894 #endif
895 	}
896 #endif /* HAVE_LINUX_EXT2_FS_H */
897   }
898 #endif /* HAVE_SYS_VFS_H */
899 
900 /* chflags(2) turns out to be a different system call in every BSD
901    derivative. The important thing is to make sure we'll be able to
902    unlink it after we're through messing around. Unlinking it first
903    would remove the need for any of these checks, but would leave the
904    user with no way to overwrite the file if the process was
905    interupted during the overwriting. So, instead we assume that the
906    open() above will fail on immutable and append-only files and try
907    and catch only platforms supporting NOUNLINK here.
908 
909    OpenBSD - doesn't support nounlink (As of 3.1)
910    FreeBSD - supports nounlink (from 4.4 on?)
911    Tru64   - unknown
912    MacOS X - doesn't support NOUNLINK (as of 10.3.5)
913 */
914 
915 #if defined(HAVE_CHFLAGS) && defined(__FreeBSD__)
916   if ((statbuf.st_flags & UF_IMMUTABLE) ||
917       (statbuf.st_flags & UF_APPEND) ||
918       (statbuf.st_flags & UF_NOUNLINK) ||
919       (statbuf.st_flags & SF_IMMUTABLE) ||
920       (statbuf.st_flags & SF_APPEND) ||
921       (statbuf.st_flags & SF_NOUNLINK))
922     {
923       if((srm.options & SRM_OPT_V) > 2) {
924           error("%s has ext2 nounlink, immutable or append-only flag", srm.file_name);
925       }
926       close(srm.fd);
927       errno = EPERM;
928       return -1;
929     }
930 #endif /* HAVE_CHFLAGS */
931 
932   /* check that the srm struct contains useful values */
933   if (srm.file_name == 0) {
934     error("internal error: srm.file_name is NULL");
935     close(srm.fd);
936     errno = ENOSYS;
937     return -1;
938   }
939   if (srm.file_size == 0) {
940     error("internal error: srm.file_size is 0");
941     close(srm.fd);
942     errno = ENOSYS;
943     return -1;
944   }
945   if (srm.buffer_size == 0) {
946     error("internal error: srm.buffer_size is 0");
947     close(srm.fd);
948     errno = ENOSYS;
949     return -1;
950   }
951 
952   if(overwrite_selector(&srm) < 0)
953     {
954       int e=errno;
955       if (srm.options & SRM_OPT_V)
956 	errorp("could not overwrite file %s", srm.file_name);
957       close(srm.fd);
958       errno=e;
959       return -1;
960     }
961 
962 #if defined(HAVE_LINUX_EXT2_FS_H) || defined(HAVE_LINUX_EXT3_FS_H)
963   ioctl(srm.fd, EXT2_IOC_SETFLAGS, EXT2_SECRM_FL);
964 #endif
965 
966   if (ftruncate(srm.fd, 0) < 0) {
967     int e=errno;
968     close(srm.fd);
969     errno=e;
970     return -1;
971   }
972 
973   close(srm.fd);
974   srm.fd = -1;
975 
976 #ifdef __APPLE__
977   /* Also overwrite the file's resource fork, if present. */
978   {
979     struct srm_target rsrc = srm;
980     rsrc.file_name = (char *)alloca(strlen(srm.file_name) + sizeof(_PATH_RSRCFORKSPEC) + 1);
981     if (rsrc.file_name == NULL)
982       {
983 	errno = ENOMEM;
984 	goto rsrc_fork_failed;
985       }
986 
987     if (snprintf((char*)rsrc.file_name, MAXPATHLEN, "%s" _PATH_RSRCFORKSPEC, srm.file_name) > MAXPATHLEN - 1)
988       {
989 	errno = ENAMETOOLONG;
990 	goto rsrc_fork_failed;
991       }
992 
993     if (lstat(rsrc.file_name, &statbuf) != 0)
994       {
995 	if (errno == ENOENT || errno == ENOTDIR) {
996 	  rsrc.file_size = 0;
997 	} else {
998 	  goto rsrc_fork_failed;
999 	}
1000       }
1001     else
1002       {
1003 	rsrc.file_size = statbuf.st_size;
1004       }
1005 
1006     if (rsrc.file_size > 0)
1007       {
1008 	if ((rsrc.fd = open(rsrc.file_name, oflags)) < 0) {
1009 	  goto rsrc_fork_failed;
1010 	}
1011 
1012 	flock.l_type = F_WRLCK;
1013 	flock.l_whence = SEEK_SET;
1014 	flock.l_start = 0;
1015 	flock.l_len = 0;
1016 	if (fcntl(rsrc.fd, F_SETLK, &flock) == -1)
1017 	  {
1018 	    close(rsrc.fd);
1019 	    goto rsrc_fork_failed;
1020 	  }
1021 
1022 	if (rsrc.options & SRM_OPT_V) {
1023 	  error("removing %s", rsrc.file_name);
1024 	}
1025 
1026 	if(overwrite_selector(&rsrc) < 0)
1027 	  {
1028 	    if (rsrc.options & SRM_OPT_V) {
1029 	      errorp("could not overwrite resource fork %s", rsrc.file_name);
1030 	    }
1031 	  }
1032 
1033 	ftruncate(rsrc.fd, 0);
1034 	close(rsrc.fd);
1035       }
1036     goto rsrc_fork_done;
1037 
1038   rsrc_fork_failed:
1039     if (rsrc.options & SRM_OPT_V) {
1040       errorp("could not access resource fork %s", srm.file_name);
1041     }
1042 
1043   rsrc_fork_done: ;
1044   }
1045 #endif /* __APPLE__ */
1046 
1047   return rename_unlink(srm.file_name);
1048 }
1049