summaryrefslogtreecommitdiff
path: root/usr/src/cmd/vi/port/expreserve.c
blob: 11d0c3e36feb71df05190655b1edddc7fe1cdfea (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright (c) 2016 by Delphix. All rights reserved.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/* Copyright (c) 1981 Regents of the University of California */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <pwd.h>
#include <locale.h>
#include <limits.h>
#include <unistd.h>

#define	BUFSIZE	(LINE_MAX*2)	/* This should agree with what's in ex.h */

#include "ex_tune.h"

#define	FTYPE(A)	(A.st_mode)
#define	FMODE(A)	(A.st_mode)
#define	IDENTICAL(A, B)	(A.st_dev == B.st_dev && A.st_ino == B.st_ino)
#define	ISBLK(A)	((A.st_mode & S_IFMT) == S_IFBLK)
#define	ISCHR(A)	((A.st_mode & S_IFMT) == S_IFCHR)
#define	ISDIR(A)	((A.st_mode & S_IFMT) == S_IFDIR)
#define	ISFIFO(A)	((A.st_mode & S_IFMT) == S_IFIFO)
#define	ISREG(A)	((A.st_mode & S_IFMT) == S_IFREG)

/*
 * Expreserve - preserve a file in usrpath(preserve)
 *
 * This routine is very naive - it doesn't remove anything from
 * usrpath(preserve)... this may mean that we  * stuff there...
 * the danger in doing anything with usrpath(preserve)
 * is that the clock may be messed up and we may get confused.
 *
 * We are called in two ways - first from the editor with no arguments
 * and the standard input open on the temp file. Second with an argument
 * to preserve the entire contents of /var/tmp (root only).
 *
 * BUG: should do something about preserving Rx... (register contents)
 *      temporaries.
 */

struct 	header {
	time_t	Time;			/* Time temp file last updated */
	int	Uid;			/* This user's identity */
#ifndef VMUNIX
	short	Flines;			/* Number of lines in file */
#else
	int	Flines;
#endif
	unsigned char	Savedfile[FNSIZE];	/* The current file name */
	short	Blocks[LBLKS];		/* Blocks where line pointers stashed */
	short	encrypted;		/* Encrypted temp file flag */
} H;

#define	eq(a, b) strcmp(a, b) == 0

void notify(int, unsigned char *, int, int);
void mkdigits(unsigned char *);

int
main(argc)
	int argc;
{
	DIR *tf;
	struct dirent64 *direntry;
	unsigned char *filname;
	struct stat64 stbuf;

	(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif
	(void) textdomain(TEXT_DOMAIN);
	/*
	 * If only one argument, then preserve the standard input.
	 */
	if (argc == 1) {
		if (copyout((unsigned char *) 0))
			return (1);
		return (0);
	}

	/*
	 * If not super user, then can only preserve standard input.
	 */
	if (getuid()) {
		fprintf(stderr, gettext("NOT super user\n"));
		return (1);
	}

	/*
	 * ... else preserve all the stuff in /var/tmp, removing
	 * it as we go.
	 */
	if (chdir(TMPDIR) < 0) {
		perror(TMPDIR);
		return (1);
	}

	if ((tf = opendir(".")) == NULL)
	{
		perror(TMPDIR);
		return (1);
	}
	while ((direntry = readdir64(tf)) != NULL)
	{
		filname = (unsigned char *)direntry->d_name;
		/*
		 * Ex temporaries must begin with Ex;
		 * we check that the 12th character of the name is null
		 * so we won't have to worry about non-null terminated names
		 * later on.
		 */
		if (filname[0] != 'E' || filname[1] != 'x' || filname[12])
			continue;
		if (stat64((char *)filname, &stbuf))
			continue;
		if (!ISREG(stbuf))
			continue;
		/*
		 * Save the file.
		 */
		(void) copyout(filname);
	}
	closedir(tf);
	return (0);
}

unsigned char	mydir[] =	USRPRESERVE;
unsigned char	pattern[] =	"/Exaa`XXXXXXXXXX";

/*
 * Copy file name into usrpath(preserve)/...
 * If name is (char *) 0, then do the standard input.
 * We make some checks on the input to make sure it is
 * really an editor temporary, generate a name for the
 * file (this is the slowest thing since we must stat
 * to find a unique name), and finally copy the file.
 */
int
copyout(unsigned char *name)
{
	int i;
	static int reenter;
	unsigned char buf[BUFSIZE];
	unsigned char	savdir[PATH_MAX+1];
	unsigned char	savfil[PATH_MAX+1];
	struct passwd *pp;
	struct stat64	stbuf;
	int savfild;

	/*
	 * The first time we put in the digits of our
	 * process number at the end of the pattern.
	 */
	if (reenter == 0) {
		mkdigits(pattern);
		reenter++;
	}

	/*
	 * If a file name was given, make it the standard
	 * input if possible.
	 */
	if (name != 0) {
		(void) close(0);
		/*
		 * Need read/write access for arcane reasons
		 * (see below).
		 */
		if (open(name, O_RDWR) < 0)
			return (-1);
	}

	/*
	 * Get the header block.
	 */
	(void) lseek(0, 0l, 0);
	if (read(0, (char *)&H, sizeof (H)) != sizeof (H)) {
format:
		if (name == 0)
			fprintf(stderr, gettext("Buffer format error\t"));
		else {
			/*
			 * avoid having a bunch of NULL Ex* files
			 * hanging around
			 */
			struct stat64 stbuf;

			if (stat64((char *)name, &stbuf) == 0)
			if (stbuf.st_size == 0)
				(void) unlink((char *)name);
		}
		return (-1);
	}

	/*
	 * Consistency checks so we don't copy out garbage.
	 */
	if (H.Flines < 0) {
#ifdef DEBUG
		fprintf(stderr, "Negative number of lines\n");
#endif
		goto format;
	}
	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
#ifdef DEBUG
		fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
#endif
		goto format;
	}
	if (name == 0 && H.Uid != getuid()) {
#ifdef DEBUG
		fprintf(stderr, "Wrong user-id\n");
#endif
		goto format;
	}
	if (lseek(0, 0l, 0)) {
#ifdef DEBUG
		fprintf(stderr, gettext("Negative number of lines\n"));
#endif
		goto format;
	}

	/*
	 * If no name was assigned to the file, then give it the name
	 * LOST, by putting this in the header.
	 */
	if (H.Savedfile[0] == 0) {
		(void) strcpy(H.Savedfile, "LOST");
		(void) write(0, (char *) &H, sizeof (H));
		H.Savedfile[0] = 0;
		(void) lseek(0, 0l, 0);
	}

	/*
	 * See if preservation directory for user exists.
	 */

	strcpy(savdir, mydir);
	pp = getpwuid(H.Uid);
	if (pp)
		strcat(savdir, pp->pw_name);
	else {
		fprintf(stderr, gettext("Unable to get uid for user.\n"));
		return (-1);
	}
	if (lstat64((char *)savdir, &stbuf) < 0 || !S_ISDIR(stbuf.st_mode)) {
		/* It doesn't exist or it isn't a directory, safe to unlink */
		(void) unlink((char *)savdir);
		if (mkdir((char *)savdir, 0700) < 0) {
			fprintf(stderr,
				gettext("Unable to create directory \"%s\"\n"),
				savdir);
			perror("");
			return (-1);
		}
		(void) chmod((char *)savdir, 0700);
		(void) chown((char *)savdir, H.Uid, 2);
	}

	/*
	 * File is good.  Get a name and create a file for the copy.
	 */
	(void) close(1);
	if ((savfild = mknext(savdir, pattern)) < 0) {
		if (name == 0)
			perror((char *)savfil);
		return	(1);
	}
	strcpy(savfil, savdir);
	strcat(savfil, pattern);
	/*
	 * Make target owned by user.
	 */

	(void) fchown(savfild, H.Uid, 2);

	/*
	 * Copy the file.
	 */
	for (;;) {
		i = read(0, buf, BUFSIZE);
		if (i < 0) {
			if (name)
				perror(gettext("Buffer read error"));
			(void) unlink((char *)savfil);
			return (-1);
		}
		if (i == 0) {
			if (name)
				(void) unlink((char *)name);
			notify(H.Uid, H.Savedfile, (int) name, H.encrypted);
			return (0);
		}
		if (write(savfild, buf, i) != i) {
			if (name == 0)
				perror((char *)savfil);
			(void) unlink((char *)savfil);
			return (-1);
		}
	}
}

/*
 * Blast the last 5 characters of cp to be the process number.
 */
void
mkdigits(unsigned char *cp)
{
	pid_t i;
	int j;

	for (i = getpid(), j = 10, cp += strlen(cp); j > 0; i /= 10, j--)
		*--cp = i % 10 | '0';
}

/*
 * Make the name in cp be unique by clobbering up to
 * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
 * Mktemp gets weird names too quickly to be useful here.
 */
int
mknext(unsigned char *dir, unsigned char *cp)
{
	unsigned char *dcp;
	struct stat stb;
	unsigned char path[PATH_MAX+1];
	int fd;

	strcpy(path, dir);
	strcat(path, cp);
	dcp = path + strlen(path) - 1;

	while (isdigit(*dcp))
		dcp--;

	do {
		if (dcp[0] == 'z') {
			dcp[0] = 'a';
			if (dcp[-1] == 'z') {
				dcp[-1] = 'a';
				if (dcp[-2] == 'z') {
					fprintf(stderr,
						gettext("Can't find a name\t"));
						return (-1);
				}
				dcp[-2]++;
			} else
				dcp[-1]++;
		} else
			dcp[0]++;

	} while (((fd = open(path, O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0) &&
		errno == EEXIST);
	/* copy out patern */
	strcpy(cp, path + strlen(dir));
	return (fd);
}

/*
 * Notify user uid that their file fname has been saved.
 */
void
notify(int uid, unsigned char *fname, int flag, int cryflag)
{

#define MAXHOSTNAMELEN 256

	struct passwd *pp = getpwuid(uid);
	FILE *mf;
	unsigned char cmd[BUFSIZE];

	char hostname[MAXHOSTNAMELEN];
	int namelen = MAXHOSTNAMELEN ;

	if (gethostname((char *)hostname, namelen) == -1)
	  return;

	if (pp == NULL)
		return;
	sprintf((char *)cmd, "/usr/bin/mail %s", pp->pw_name);
	mf = popen((char *)cmd, "w");
	if (mf == NULL)
		return;
	setbuf(mf, (char *)cmd);
	if (fname[0] == 0) {
		fprintf(mf, flag ?
"A copy of an editor buffer of yours was saved on %s when the system went down.\n" :
"A copy of an editor buffer of yours was saved on %s when the editor was killed\nor was unable to save your changes.\n", hostname);
		fprintf(mf,
"No name was associated with this buffer so it has been named \"LOST\".\n");
	} else
		fprintf(mf, flag ?
"A copy of an editor buffer of your file \"%s\"%s was saved on %s\nwhen the system \
went down.\n" :
"A copy of an editor buffer of your file \"%s\"%s was saved on %s\nwhen the editor \
was killed or was unable to save your changes.\n", fname, (cryflag) ? "[ENCRYPTED]" : "", hostname);
		/*
		 * "the editor was killed" is perhaps still not an ideal
		 * error message.  Usually, either it was forceably terminated
		 * or the phone was hung up, but we don't know which.
		 */
	fprintf(mf,
"This buffer can be retrieved using the \"recover\" command of the editor.\n");
	fprintf(mf,
"An easy way to do this is to give the command \"vi -r %s\".\n",
		(fname[0] == 0) ? "LOST" : (char *) fname);
	fprintf(mf, "This works for \"edit\" and \"ex\" also.\n");
	(void) pclose(mf);
}