1#!/usr/bin/perl -w 2# 3# $OpenBSD: recover,v 1.9 2001/11/06 23:31:08 millert Exp $ 4# 5# Script to (safely) recover nvi edit sessions. 6# 7 8use Fcntl; 9require 'sys/syscall.ph'; 10 11$recoverdir = $ARGV[0] || "/var/tmp/vi.recover"; 12$sendmail = "/usr/sbin/sendmail"; 13 14die "Sorry, $0 must be run as root\n" if $>; 15 16# Make the recovery dir if it does not already exist. 17if (!sysopen(DIR, $recoverdir, O_RDONLY|O_NOFOLLOW) || !stat(DIR)) { 18 die "Warning! $recoverdir is a symbolic link! (ignoring)\n" 19 if -l $recoverdir; 20 mkdir($recoverdir, 01777) || die "Unable to create $recoverdir: $!\n"; 21 chmod(01777, $recoverdir); 22 exit(0); 23} 24 25# 26# Sanity check the vi recovery dir 27# Perl doesn't support fchdir, fchmod, or fchown so we call 28# fchdir(2) via the syscall interface and then just modify ".". 29# 30die "Warning! $recoverdir is not a directory! (ignoring)\n" 31 unless -d _; 32die "$0: can't chdir to $recoverdir: $!\n" 33 unless syscall(&SYS_fchdir, fileno(DIR)) == 0; 34if (! -O _) { 35 warn "Warning! $recoverdir is not owned by root! (fixing)\n"; 36 chown(0, 0, "."); 37} 38if (((stat(_))[2] & 07777) != 01777) { 39 warn "Warning! $recoverdir is not mode 01777! (fixing)\n"; 40 chmod(01777, "."); 41} 42 43# Check editor backup files. 44opendir(RECDIR, ".") || die "$0: can't open $recoverdir: $!\n"; 45foreach $file (readdir(RECDIR)) { 46 next unless $file =~ /^vi\./; 47 48 # 49 # Unmodified vi editor backup files either have the 50 # execute bit set or are zero length. Delete them. 51 # Anything that is not a normal file gets deleted too. 52 # 53 lstat($file) || die "$0: can't stat $file: $!\n"; 54 if (-x _ || ! -s _ || ! -f _) { 55 unlink($file) unless -d _; 56 } 57} 58 59# 60# It is possible to get incomplete recovery files if the editor crashes 61# at the right time. 62# 63rewinddir(RECDIR); 64foreach $file (readdir(RECDIR)) { 65 next unless $file =~ /^recover\./; 66 67 if (!sysopen(RECFILE, $file, O_RDONLY|O_NOFOLLOW)) { 68 warn "$0: can't open $file: $!\n"; 69 next; 70 } 71 72 # 73 # Delete anything that is not a regular file as that is either 74 # filesystem corruption from fsck or an exploit attempt. 75 # 76 if (!stat(RECFILE)) { 77 warn "$0: can't stat $file: $!\n"; 78 close(RECFILE); 79 next; 80 } 81 $owner = (stat(_))[4]; 82 if (! -f _ || ! -s _) { 83 unlink($file) unless -d _; 84 close(RECFILE); 85 next; 86 } 87 88 # 89 # Slurp in the recover.* file and search for X-vi-recover-path 90 # (which should point to an existing vi.* file). 91 # 92 @recfile = <RECFILE>; 93 close(RECFILE); 94 95 # 96 # Delete any recovery files that have no (or more than one) 97 # corresponding backup file. 98 # 99 @backups = grep(m#^X-vi-recover-path:\s*\Q$recoverdir\E/+#, @recfile); 100 if (@backups != 1) { 101 unlink($file); 102 next; 103 } 104 105 # 106 # Make a copy of the backup file path. 107 # We must not modify @backups directly since it contains 108 # references to data in @recfile which we pipe to sendmail. 109 # 110 $backups[0] =~ m#^X-vi-recover-path:\s*\Q$recoverdir\E/+(.*)[\r\n]*$#; 111 $backup = $1; 112 113 # 114 # If backup file is not rooted in the recover dir, ignore it. 115 # If backup file owner doesn't match recovery file owner, ignore it. 116 # If backup file is zero length or not a regular file, remove it. 117 # Else send mail to the user. 118 # 119 if ($backup =~ m#/# || !lstat($backup)) { 120 unlink($file); 121 } elsif ($owner != 0 && (stat(_))[4] != $owner) { 122 unlink($file); 123 } elsif (! -f _ || ! -s _) { 124 unlink($file, $backup); 125 } else { 126 open(SENDMAIL, "|$sendmail -t") || 127 die "$0: can't run $sendmail -t: $!\n"; 128 print SENDMAIL @recfile; 129 close(SENDMAIL); 130 } 131} 132closedir(RECDIR); 133close(DIR); 134 135exit(0); 136