/* please see 'please_read_before_using_this_program' and 'license.txt' for important * information */ /* this application was written by folkert@vanheusden.com * check http://vanheusden.com/Linux/phantom.php3 for information/new versions */ #include #include #include #include #include #include #include #include #include #include char debug=0; char seeksleft=0; off_t cur_pos=0; off_t phantomblocks=0; /* function that creates a file and returns an fd. nothing fancy */ int create_file(char *fnam, int mode) { int fdout = creat(fnam, mode); if (fdout == -1) { perror("phantom"); if (debug) fprintf(stderr, "error in create_file %s\n", fnam); exit(1); } if (fchmod(fdout, mode) == -1) /* creat() doesn't set suid etc. */ { perror("phantom"); if (debug) fprintf(stderr, "error in fchmod on %s\n", fnam); exit(1); } if (debug) fprintf(stderr, "create_file: %s => %d\n", fnam, fdout); return fdout; } /* function that allocates a buffer, nothing fancy */ char * allocate_buffer(int size) { /* alloc mem */ char *dummy = (char *)malloc(size); if (!dummy) { fprintf(stderr, "phantom: cannot allocate %d bytes of memory", size); exit(1); } if (debug) fprintf(stderr, "allocate_buffer: %d bytes (ok)\n", size); return dummy; } /* this read takes care of EINTR, partial reads, etc. */ int MY_READ(int fd, char *buffer, int nreq) { int nread=0; for (;nread != nreq;) { int ncur = read(fd, buffer + nread, nreq-nread); if (ncur > 0) { nread += ncur; } else if (ncur == 0) { /* just an end-of-file */ if (debug) fprintf(stderr, "MY_READ: EOF\n"); break; } else if (ncur == -1 && errno != EINTR) { /* error! */ perror("phantom"); if (debug) fprintf(stderr, "MY_READ: %d, %d\n", fd, nreq); exit(1); } } if (debug) fprintf(stderr, "MY_READ: got %d\n", nread); return nread; } /* this write takes care of EINTR, partial writes, etc. */ int MY_WRITE(int fd, char *buffer, int nreq) { int nwritten=0; do { int ncur = write(fd, buffer + nwritten, nreq-nwritten); if (ncur > 0) { nwritten += ncur; } else if (ncur == 0) { /* ehr..., ok */ break; } else if (ncur == -1 && errno != EINTR) { /* error! */ perror("phantom"); if (debug) fprintf(stderr, "MY_WRITE: %d, %d (%d)\n", fd, nreq, errno); exit(1); } } while(nreq != nwritten); return nwritten; } int do_write(int fd, char *buffer, int nreq) { if (seeksleft) { if (ftruncate(fd, cur_pos) == -1) { perror("phantom: "); if (debug) fprintf(stderr, "do_write: ftruncate error!\n"); exit(1); } if (lseek(fd, cur_pos, SEEK_SET) == -1) { perror("phantom: "); if (debug) fprintf(stderr, "do_write: lseek error!\n"); exit(1); } if (debug) fprintf(stderr, "do_write, curpos: %ld, actual pos: %ld\n", (long)cur_pos, (long)lseek(fd, 0, SEEK_CUR)); seeksleft=0; } return MY_WRITE(fd, buffer, nreq); } /* see if this block is all 0x00's */ void do_block(int fdout, char *buf) { long *pnt = (long *)buf; int loop; const int nelements=512/sizeof(long); long zero=0; /* all 0x00? */ for(loop=0; loop= 512) { do_block(fdout, pnt); pnt += 512; curnread -= 512; } /* partial block, is EOF */ if (curnread) { if (debug) fprintf(stderr, "do_copy_normal: <512b; EOF\n"); do_write(fdout, pnt, curnread); /* is also EOF */ break; } } if (debug) fprintf(stderr, "do_copy_normal: out of loop\n"); /* force write of any phantom blocks left */ do_write(fdout, buf, 0); close(fdout); free(buf); /* all done */ if (debug) fprintf(stderr, "do_copy_normal done\n"); } /* return filesize of inputfile */ int file_size(int fd_in) { struct stat sb; if (fstat(fd_in, &sb)) { perror("phantom"); exit(1); } return sb.st_size; } /* this one does *the* trick with mmap'ed input file */ void do_copy_mmap(int fd_in, char *fnam_out, int mode) { int fdout=-1; off_t fin_size = file_size(fd_in); char *memmap; if (debug) fprintf(stderr, "do_copy_mmap(%d) start\n", (int)fin_size); /* create memory-map */ memmap = (char *)mmap(NULL, fin_size, PROT_READ, MAP_SHARED, fd_in, 0); /* mmap failed? then do slowcopy */ if (memmap == NULL) { if (debug) fprintf(stderr, "phantom: mmap failed (size: %ld), errno: %d\n", (long)fin_size, errno); do_copy_normal(fd_in, fnam_out, mode); } else { char *pnt = memmap; int len = fin_size; /* yes! memory mapped! */ /* Linux kernel 2.2 doesn't fully support (or at all) the madvise system-call * it seems 'madvise' is specified in the headerfiles, but MADV_SEQUENTIAL * is not, so I do a check for that one to see wether it is supported or not */ #ifdef MADV_SEQUENTIAL /* advise kernel on what we're gonna do with the input-file */ if (madvise(memmap, fin_size, MADV_SEQUENTIAL) == -1) { perror("phantom"); if (debug) fprintf(stderr, "do_copy_mmap error while madvise\n"); exit(1); } #endif /* create outputfile */ fdout = create_file(fnam_out, mode); /* go trough file */ while(len >= 512) { do_block(fdout, pnt); pnt += 512; len -= 512; } /* anything smaller then half a kb left? */ if (len) { if (debug) fprintf(stderr, "do_copy_mmap: copy partial file (%d bytes)\n", len); do_write(fdout, pnt, len); } else { /* force write of any phantom blocks left */ do_write(fdout, memmap, 0); } /* unmap file */ if (munmap(memmap, fin_size) == -1) { perror("phantom"); if (debug) fprintf(stderr, "do_copy_mmap error while munmap\n"); exit(1); } close(fdout); } /* all done */ if (debug) fprintf(stderr, "do_copy_mmap end\n"); } void print_usage(void) { if (debug) fprintf(stderr, "print_usage\n"); fprintf(stderr, "phantom v1.1, (c) 2000 by F.J.J. van Heusden \n\n"); fprintf(stderr, "-o\toutput-file - You *must* supply an output-file for obvious reasons\n"); fprintf(stderr, "-r\t...or use '-r' to replace the original file (only when an -i is given!)\n"); fprintf(stderr, "-i\tinput file - File to read from. Omit to have this program read from stdin\n"); fprintf(stderr, "-d\tSwitch on debugging\n"); fprintf(stderr, "-s\tBe quiet\n"); fprintf(stderr, "-n\tShow current filename\n"); fprintf(stderr, "-h\tThis list\n\n"); fprintf(stderr, "example: find /mnt/bv-space -type f -exec phantom -r -i \"{}\" \\;\n\n"); /* find /mnt/bv-space -type f -exec phantom -r -i "{}" \; */ } int main(int argc, char *argv[]) { int loop; char *f_out = NULL, *f_in=NULL, silent=0, showName=0; char replace_org=0; int mode = S_IREAD|S_IWRITE; uid_t uid = geteuid(); /* no error checking: geteuid & getegid */ gid_t gid = getegid(); /* are not supposed to fail */ if (argc == 1) { print_usage(); return 1; } for(loop=1;loop