xref: /openbsd/usr.bin/vi/build/recover (revision 404b540a)
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