summaryrefslogtreecommitdiff
path: root/usr.bin/vi/build/recover
blob: 41439cae9d7af5b6a7d98d995842b4ba61454b21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/perl -w
#
# $OpenBSD: recover,v 1.9 2001/11/06 23:31:08 millert Exp $
#
# Script to (safely) recover nvi edit sessions.
#

use Fcntl;
require 'sys/syscall.ph';

$recoverdir = $ARGV[0] || "/var/tmp/vi.recover";
$sendmail = "/usr/sbin/sendmail";

die "Sorry, $0 must be run as root\n" if $>;

# Make the recovery dir if it does not already exist.
if (!sysopen(DIR, $recoverdir, O_RDONLY|O_NOFOLLOW) || !stat(DIR)) {
	die "Warning! $recoverdir is a symbolic link! (ignoring)\n"
	    if -l $recoverdir;
	mkdir($recoverdir, 01777) || die "Unable to create $recoverdir: $!\n";
	chmod(01777, $recoverdir);
	exit(0);
}

#
# Sanity check the vi recovery dir
# Perl doesn't support fchdir, fchmod, or fchown so we call
# fchdir(2) via the syscall interface and then just modify ".".
#
die "Warning! $recoverdir is not a directory! (ignoring)\n"
    unless -d _;
die "$0: can't chdir to $recoverdir: $!\n"
    unless syscall(&SYS_fchdir, fileno(DIR)) == 0;
if (! -O _) {
	warn "Warning! $recoverdir is not owned by root! (fixing)\n";
	chown(0, 0, ".");
}
if (((stat(_))[2] & 07777) != 01777) {
	warn "Warning! $recoverdir is not mode 01777! (fixing)\n";
	chmod(01777, ".");
}

# Check editor backup files.
opendir(RECDIR, ".") || die "$0: can't open $recoverdir: $!\n";
foreach $file (readdir(RECDIR)) {
	next unless $file =~ /^vi\./;

	#
	# Unmodified vi editor backup files either have the
	# execute bit set or are zero length.  Delete them.
	# Anything that is not a normal file gets deleted too.
	#
	lstat($file) || die "$0: can't stat $file: $!\n";
	if (-x _ || ! -s _ || ! -f _) {
		unlink($file) unless -d _;
	}
}

#
# It is possible to get incomplete recovery files if the editor crashes
# at the right time.
#
rewinddir(RECDIR);
foreach $file (readdir(RECDIR)) {
	next unless $file =~ /^recover\./;

	if (!sysopen(RECFILE, $file, O_RDONLY|O_NOFOLLOW)) {
	    warn "$0: can't open $file: $!\n";
	    next;
	}

	#
	# Delete anything that is not a regular file as that is either
	# filesystem corruption from fsck or an exploit attempt.
	#
	if (!stat(RECFILE)) {
		warn "$0: can't stat $file: $!\n";
		close(RECFILE);
		next;
	}
	$owner = (stat(_))[4];
	if (! -f _ || ! -s _) {
		unlink($file) unless -d _;
		close(RECFILE);
		next;
	}

	#
	# Slurp in the recover.* file and search for X-vi-recover-path
	# (which should point to an existing vi.* file).
	#
	@recfile = <RECFILE>;
	close(RECFILE);

	#
	# Delete any recovery files that have no (or more than one)
	# corresponding backup file.
	#
	@backups = grep(m#^X-vi-recover-path:\s*\Q$recoverdir\E/+#, @recfile);
	if (@backups != 1) {
		unlink($file);
		next;
	}

	#
	# Make a copy of the backup file path.
	# We must not modify @backups directly since it contains
	# references to data in @recfile which we pipe to sendmail.
	#
	$backups[0] =~ m#^X-vi-recover-path:\s*\Q$recoverdir\E/+(.*)[\r\n]*$#;
	$backup = $1;

	#
	# If backup file is not rooted in the recover dir, ignore it.
	# If backup file owner doesn't match recovery file owner, ignore it.
	# If backup file is zero length or not a regular file, remove it.
	# Else send mail to the user.
	#
	if ($backup =~ m#/# || !lstat($backup)) {
		unlink($file);
	} elsif ($owner != 0 && (stat(_))[4] != $owner) {
		unlink($file);
	} elsif (! -f _ || ! -s _) {
		unlink($file, $backup);
	} else {
		open(SENDMAIL, "|$sendmail -t") ||
		    die "$0: can't run $sendmail -t: $!\n";
		print SENDMAIL @recfile;
		close(SENDMAIL);
	}
}
closedir(RECDIR);
close(DIR);

exit(0);