[alt.sources] rm with hope part 1

waynet@tolerant.UUCP (08/25/87)

A request went out recently for on comp.sources.wanted asking for an rm
that gave a second chance.          Remember APRIL 1 ;-) 

This one came from Dave Taylor-Hewlett Packard Co. compliments of the net.
--
my review intercedes
--
rm/unrm - this pair of routines replaces rm so that when you do an rm
          the file(s) is actually mv'ed to /tmp/$user/. The full pathname
          is not remembered, only the tail. As such, if you rm to files
          with the same name the second will overwrite the first. Since
          directories in /tmp are not removed by the os, they will pile
          up until you /bin/rm them, and fill up the /tmp filesystem.
          unrm will mv them into the current directory regardless of their
          directory of origin.
--
#!/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