sources-request@mirror.UUCP (06/26/86)
Submitted by: Dave Taylor <pyramid!hplabs!hpldat!taylor> Mod.sources: Volume 6, Issue 25 Archive-name: unrm.rm [ I wrote a brief manpage and a Makefile; I hope Dave forgives the (humorous) editorial comment in the latter. Some sites will want to change the "LOGNAME" strings to "USER" or similar, in the code below. --r$] The following `shar' file contains two programs that, when installed, will allow you to use "rm" as usual but also have the ability to change your mind and use "unrm". Another useful program from.... -- Dave Taylor taylor/HP1900/UX, taylor@HPLABS.{CSNET, ARPA} or ..hplabs!taylor --------------------CUT HERE-------------------- #!/bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # Contents: rm.c unrm.1 unrm.c Makefile echo x - rm.c sed 's/^XX//' > "rm.c" <<'@//E*O*F rm.c//' XX/** rm.c **/ XX/** This program replaces 'rm' (it's assumed that this program is BEFORE XX /bin/rm in the path) and will ferret away copies of the files being XX removed to the directory /tmp/rm/<login>. Files that are copied into XX /tmp/rm/* can be recalled by 'unrm filename'. Every so often (probably XX midnight every night) a daemon should clear out the old files in the XX /tmp/rm directory... XX (C) Copyright 1986, Dave Taylor, Hewlett-Packard Company XX**/ XX#include <stdio.h> XX#include <errno.h> XX#define real_rm "/bin/rm" XX#define RM_DIR "/tmp/rm" XX#define ACCESS_MODE 04 & 02 XX#define DIR_ACCESS 04 & 01 XX#define SLEN 80 XX#define USERS_NAME "LOGNAME" /* "USER" in BSD */ XXextern int errno; XXchar *basename(), *getlogin(), *getenv(); XXmain(argc, argv) XXint argc; XXchar **argv; XX{ XX extern int optind; /* for getopt */ XX char buffer[SLEN], login_name[SLEN], dirname[SLEN], *cp; XX int c, oldumask; XX while ((c = getopt(argc, argv, "rfi")) != EOF) { XX switch (c) { XX case 'r' : XX case 'f' : XX case 'i' : break; /* just accept 'em... */ XX case '?' : exit(fprintf(stderr,"Usage: rm [-rfi] files\n")); XX } XX } XX if (strlen(argv[optind]) == 0) XX exit(0); XX /* is the top level /tmp directory available??? */ XX if (access(RM_DIR, DIR_ACCESS)) { XX sprintf(buffer,"mkdir %s; chmod 777 %s", RM_DIR, RM_DIR); XX if (system(buffer) != 0) { XX printf("'%s' failed!!\n", buffer); XX exit(1); XX } XX } XX /* now get the users login name... */ XX if ((cp = getenv(USERS_NAME)) == NULL) XX strcpy(login_name, getlogin()); XX else XX strcpy(login_name, cp); XX /* let's see if THAT directory is hangin' around... */ XX sprintf(dirname, "%s/%s", RM_DIR, login_name); XX if (access(dirname, DIR_ACCESS)) { XX sprintf(buffer,"mkdir %s; chmod 700 %s", dirname, dirname); XX if (system(buffer) != 0) { XX printf("'%s' failed!!\n", buffer); XX exit(1); XX } XX } XX oldumask = umask(077); XX while (strlen(argv[optind]) > 0) { XX if (access(basename(argv[optind]), ACCESS_MODE) == 0) XX save_copy_of(dirname, argv[optind]); XX optind++; XX } XX (void) umask(oldumask); XX execv(real_rm, argv); XX XX fprintf(stderr,"rm: error %d exec'ing!\n", errno); XX} XXchar *basename(string) XXchar *string; XX{ XX /** returns the basename of the file specified in string **/ XX static char *buff; XX buff = string + strlen(string); /* start at last char */ XX XX while (*buff != '/' && buff > string) buff--; XX return( (char *) (*buff == '/'? ++buff : buff)); XX} XXsave_copy_of(dirname, filename) XXchar *dirname, *filename; XX{ XX /** Try to link filename to dirname, if that fails, copy it XX bit by bit... **/ XX char newfname[80]; XX sprintf(newfname,"%s/%s", dirname, basename(filename)); XX (void) unlink(newfname); /* blow it away if already there! */ XX if (link(filename, newfname) != 0) { XX FILE *infile, *outfile; XX int c; XX XX if ((infile = fopen(filename, "r")) == NULL) XX exit(fprintf(stderr, "rm: can't read file '%s' to save a copy!\n", XX filename)); XX if ((outfile = fopen(newfname, "w")) == NULL) XX exit(fprintf(stderr, "rm: can't write to file '%s'!\n", XX newfname)); XX while ((c = getc(infile)) != EOF) XX putc(c, outfile); XX XX fclose(infile); XX fclose(outfile); XX } XX} @//E*O*F rm.c// chmod u=rw,g=r,o=r rm.c echo x - unrm.1 sed 's/^XX//' > "unrm.1" <<'@//E*O*F unrm.1//' XX.TH UNRM 1 LOCAL XX.SH NAME XXunrm, rm \- remove files and bring them back XX.SH SYNOPSIS XX.B unrm XX[ XX.B \-f XX] file ... XX.br XX.B rm XXfile ... XX.SH DESCRIPTION XX.I Rm XXis a local replacement for the standard XX.IR rm (1) XXcommand. Prior to removing a file it ferrets away a copy of it in XXthe directory XX.RI /tmp/rm/ login_id , XXcreating each component along the way as necessary. XXThe standard ``\-r,'' ``\-f,'' and ``\-i'' options may be specified, XXbut they are ignored. XX.PP XXFiles that have been deleted with this version of XX.I rm XXcan than be retrieved with the XX.I unrm XXcommand. XX.I Unrm XXchecks to see that you are indeed the owner of a file before you XXcan copy it. It will not overwrite a file of the same name in the XXcurrent directory, unless the ``\-f'' flag is specified, which XXcauses the file to be replaced regardless. XX.PP XXIf these programs are available, the system administror will probably want XXto add a line like the following to /usr/lib/crontab: XX.RS XX40 4 * * * find /tmp -mtime +2 -exec rm -f {} ; XX.RE XXThis removes all files from temp that haven't been touched within XXtwo days. XX.SH BUGS XXStrange things can happen if you try to specify something other than XXa regular file. @//E*O*F unrm.1// chmod u=rw,g=rw,o=rw unrm.1 echo x - unrm.c sed 's/^XX//' > "unrm.c" <<'@//E*O*F unrm.c//' XX/** unrm.c **/ XX/** This is the companion program to the rm.c program, and will extract XX files from the RM_DIR/login directory if they exist. It checks to see XX that you are indeed the owner of the file before it'll let you copy XX it AND it ensures that the file doesn't already exist in the current XX directory (makes sense, eh?). XX This will not allow unrm'ing files that aren't owned by you, nor XX will it allow restores that replace a file of the same name in the XX current directory (unless '-f' is specified, which will cause the XX file to be replaced regardless). XX (C) Copyright 1986, Dave Taylor, Hewlett-Packard XX**/ XX#include <stdio.h> XX#include <errno.h> XX#include <sys/types.h> XX#include <sys/stat.h> XX#define RM_DIR "/tmp/rm" XX/** access modes for calls to 'access()' **/ XX#define DIRACCESS 02 & 04 XX#define TOCOPY 04 XX#define TOREPLACE 02 & 04 XX#define SLEN 80 XXint force_overwrite = 0; /* replace current regardless! */ XXchar *getenv(), *getlogin(); XXmain(argc, argv) XXint argc; XXchar **argv; XX{ XX extern int optind; /* for getopt */ XX char buffer[SLEN], login_name[SLEN], dirname[SLEN], *cp; XX int c; XX while ((c = getopt(argc, argv, "f")) != EOF) { XX switch (c) { XX case 'f' : force_overwrite++; break; XX case '?' : exit(fprintf(stderr,"Usage: unrm [-f] files\n")); XX } XX } XX if (argv[optind] == 0 || strlen(argv[optind]) == 0) XX exit(0); XX if (access(RM_DIR, DIRACCESS)) { XX fprintf(stderr,"Error: Directory %s doesn't exist!\n", RM_DIR); XX exit(0); XX } XX if ((cp = getenv("LOGNAME")) == NULL) XX strcpy(login_name, getlogin()); XX else XX strcpy(login_name, cp); XX sprintf(dirname, "%s/%s", RM_DIR, login_name); XX if (access(dirname, DIRACCESS)) { XX fprintf(stderr,"Error: Directory %s doesn't exist!\n", dirname); XX exit(0); XX } XX while (argv[optind] && strlen(argv[optind]) > 0) { XX restore(dirname, argv[optind]); XX optind++; XX } XX exit(0); XX} XXrestore(directory, filename) XXchar *directory, *filename; XX{ XX /** Try to link RM_DIR/filename to current directory. If that XX fails, try to copy it byte by byte... **/ XX struct stat buffer; XX char newfname[80], answer[80]; XX sprintf(newfname,"%s/%s", directory, filename); XX if (access(newfname,TOCOPY) != 0) XX return(fprintf(stderr,"Error: Can't find old copy of '%s'!\n", XX filename)); XX XX if (stat(newfname, &buffer) != 0) XX return(fprintf(stderr,"Error: Can't stat old copy of '%s'!\n", XX filename)); XX if (buffer.st_uid != getuid()) XX return(fprintf(stderr,"Error: File '%s' isn't yours to restore!\n", XX filename)); XX /** now we're ready to start some REAL work... **/ XX if (access(filename,TOREPLACE) == 0) { /* it exists! */ XX if (! force_overwrite) XX printf( XX "File %s already exists in this directory! Replace it? (y/n) ", XX filename); XX gets(answer, 1); XX if (tolower(answer[0]) != 'y') { XX fprintf(stderr,"Restore of file %s cancelled\n", filename); XX return; XX } XX } XX (void) unlink(filename); /* blow it away, if it's here! */ XX if (link(newfname, filename) != 0) { XX FILE *infile, *outfile; XX int c; XX XX if ((infile = fopen(newfname, "r")) == NULL) XX exit(fprintf(stderr, XX "Error: Can't read file '%s' to restore from!\n", XX newfname)); XX if ((outfile = fopen(filename, "w")) == NULL) XX exit(fprintf(stderr, "Error: Can't write to file '%s'!\n", XX filename)); XX while ((c = getc(infile)) != EOF) XX putc(c, outfile); XX XX fclose(infile); XX fclose(outfile); XX } XX unlink(newfname); XX fprintf(stderr,"Restored file '%s'\n", filename); XX} @//E*O*F unrm.c// chmod u=rw,g=r,o=r unrm.c echo x - Makefile sed 's/^XX//' > "Makefile" <<'@//E*O*F Makefile//' XXall: unrm rm XXunrm: unrm.c ; cc $(CFLAGS) -o $@ $? XXrm: rm.c ; cc $(CFLAGS) -o $@ $? XXinstall: XX @echo surely you jest @//E*O*F Makefile// chmod u=rw,g=rw,o=rw Makefile echo Inspecting for damage in transit... temp=/tmp/sharin$$; dtemp=/tmp/sharout$$ trap "rm -f $temp $dtemp; exit" 0 1 2 3 15 cat > $temp <<\!!! 136 428 3194 rm.c 43 221 1170 unrm.1 142 454 3554 unrm.c 7 24 117 Makefile 328 1127 8035 total !!! wc rm.c unrm.1 unrm.c Makefile | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp if test -s $dtemp then echo "Ouch [diff of wc output]:" ; cat $dtemp else echo "No problems found." fi exit 0