4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
32 /* Copyright (c) 1981 Regents of the University of California */
34 #pragma ident "%Z%%M% %I% %E% SMI"
37 #include <sys/types.h>
39 #include <sys/fcntl.h>
47 #define BUFSIZE (LINE_MAX*2) /* This should agree with what's in ex.h */
51 #define FTYPE(A) (A.st_mode)
52 #define FMODE(A) (A.st_mode)
53 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
54 #define ISBLK(A) ((A.st_mode & S_IFMT) == S_IFBLK)
55 #define ISCHR(A) ((A.st_mode & S_IFMT) == S_IFCHR)
56 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
57 #define ISFIFO(A) ((A.st_mode & S_IFMT) == S_IFIFO)
58 #define ISREG(A) ((A.st_mode & S_IFMT) == S_IFREG)
61 * Expreserve - preserve a file in usrpath(preserve)
63 * This routine is very naive - it doesn't remove anything from
64 * usrpath(preserve)... this may mean that we * stuff there...
65 * the danger in doing anything with usrpath(preserve)
66 * is that the clock may be messed up and we may get confused.
68 * We are called in two ways - first from the editor with no arguments
69 * and the standard input open on the temp file. Second with an argument
70 * to preserve the entire contents of /var/tmp (root only).
72 * BUG: should do something about preserving Rx... (register contents)
77 time_t Time
; /* Time temp file last updated */
78 int Uid
; /* This user's identity */
80 short Flines
; /* Number of lines in file */
84 unsigned char Savedfile
[FNSIZE
]; /* The current file name */
85 short Blocks
[LBLKS
]; /* Blocks where line pointers stashed */
86 short encrypted
; /* Encrypted temp file flag */
89 #define eq(a, b) strcmp(a, b) == 0
91 void notify(int, unsigned char *, int, int);
92 void mkdigits(unsigned char *);
99 struct dirent64
*direntry
;
100 unsigned char *filname
;
103 (void) setlocale(LC_ALL
, "");
104 #if !defined(TEXT_DOMAIN)
105 #define TEXT_DOMAIN "SYS_TEST"
107 (void) textdomain(TEXT_DOMAIN
);
109 * If only one argument, then preserve the standard input.
112 if (copyout((unsigned char *) 0))
118 * If not super user, then can only preserve standard input.
121 fprintf(stderr
, gettext("NOT super user\n"));
126 * ... else preserve all the stuff in /var/tmp, removing
129 if (chdir(TMPDIR
) < 0) {
134 if ((tf
= opendir(".")) == NULL
)
139 while ((direntry
= readdir64(tf
)) != NULL
)
141 filname
= (unsigned char *)direntry
->d_name
;
143 * Ex temporaries must begin with Ex;
144 * we check that the 12th character of the name is null
145 * so we won't have to worry about non-null terminated names
148 if (filname
[0] != 'E' || filname
[1] != 'x' || filname
[12])
150 if (stat64((char *)filname
, &stbuf
))
157 (void) copyout(filname
);
163 unsigned char mydir
[] = USRPRESERVE
;
164 unsigned char pattern
[] = "/Exaa`XXXXXXXXXX";
167 * Copy file name into usrpath(preserve)/...
168 * If name is (char *) 0, then do the standard input.
169 * We make some checks on the input to make sure it is
170 * really an editor temporary, generate a name for the
171 * file (this is the slowest thing since we must stat
172 * to find a unique name), and finally copy the file.
175 copyout(unsigned char *name
)
179 unsigned char buf
[BUFSIZE
];
180 unsigned char savdir
[PATH_MAX
+1];
181 unsigned char savfil
[PATH_MAX
+1];
187 * The first time we put in the digits of our
188 * process number at the end of the pattern.
196 * If a file name was given, make it the standard
202 * Need read/write access for arcane reasons
205 if (open(name
, O_RDWR
) < 0)
210 * Get the header block.
212 (void) lseek(0, 0l, 0);
213 if (read(0, (char *)&H
, sizeof (H
)) != sizeof (H
)) {
216 fprintf(stderr
, gettext("Buffer format error\t"));
219 * avoid having a bunch of NULL Ex* files
224 if (stat64((char *)name
, &stbuf
) == 0)
225 if (stbuf
.st_size
== 0)
226 (void) unlink((char *)name
);
232 * Consistency checks so we don't copy out garbage.
236 fprintf(stderr
, "Negative number of lines\n");
240 if (H
.Blocks
[0] != HBLKS
|| H
.Blocks
[1] != HBLKS
+1) {
242 fprintf(stderr
, "Blocks %d %d\n", H
.Blocks
[0], H
.Blocks
[1]);
246 if (name
== 0 && H
.Uid
!= getuid()) {
248 fprintf(stderr
, "Wrong user-id\n");
252 if (lseek(0, 0l, 0)) {
254 fprintf(stderr
, gettext("Negative number of lines\n"));
260 * If no name was assigned to the file, then give it the name
261 * LOST, by putting this in the header.
263 if (H
.Savedfile
[0] == 0) {
264 (void) strcpy(H
.Savedfile
, "LOST");
265 (void) write(0, (char *) &H
, sizeof (H
));
267 (void) lseek(0, 0l, 0);
271 * See if preservation directory for user exists.
274 strcpy(savdir
, mydir
);
275 pp
= getpwuid(H
.Uid
);
277 strcat(savdir
, pp
->pw_name
);
279 fprintf(stderr
, gettext("Unable to get uid for user.\n"));
282 if (lstat64((char *)savdir
, &stbuf
) < 0 || !S_ISDIR(stbuf
.st_mode
)) {
283 /* It doesn't exist or it isn't a directory, safe to unlink */
284 (void) unlink((char *)savdir
);
285 if (mkdir((char *)savdir
, 0700) < 0) {
287 gettext("Unable to create directory \"%s\"\n"),
292 (void) chmod((char *)savdir
, 0700);
293 (void) chown((char *)savdir
, H
.Uid
, 2);
297 * File is good. Get a name and create a file for the copy.
300 if ((savfild
= mknext(savdir
, pattern
)) < 0) {
302 perror((char *)savfil
);
305 strcpy(savfil
, savdir
);
306 strcat(savfil
, pattern
);
308 * Make target owned by user.
311 (void) fchown(savfild
, H
.Uid
, 2);
317 i
= read(0, buf
, BUFSIZE
);
320 perror(gettext("Buffer read error"));
321 (void) unlink((char *)savfil
);
326 (void) unlink((char *)name
);
327 notify(H
.Uid
, H
.Savedfile
, (int) name
, H
.encrypted
);
330 if (write(savfild
, buf
, i
) != i
) {
332 perror((char *)savfil
);
333 (void) unlink((char *)savfil
);
340 * Blast the last 5 characters of cp to be the process number.
343 mkdigits(unsigned char *cp
)
348 for (i
= getpid(), j
= 10, cp
+= strlen(cp
); j
> 0; i
/= 10, j
--)
349 *--cp
= i
% 10 | '0';
353 * Make the name in cp be unique by clobbering up to
354 * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
355 * Mktemp gets weird names too quickly to be useful here.
358 mknext(unsigned char *dir
, unsigned char *cp
)
362 unsigned char path
[PATH_MAX
+1];
367 dcp
= path
+ strlen(path
) - 1;
369 while (isdigit(*dcp
))
375 if (dcp
[-1] == 'z') {
377 if (dcp
[-2] == 'z') {
379 gettext("Can't find a name\t"));
388 } while (((fd
= open(path
, O_CREAT
|O_EXCL
|O_WRONLY
, 0600)) < 0) &&
390 /* copy out patern */
391 strcpy(cp
, path
+ strlen(dir
));
396 * Notify user uid that their file fname has been saved.
399 notify(int uid
, unsigned char *fname
, int flag
, int cryflag
)
402 #define MAXHOSTNAMELEN 256
404 struct passwd
*pp
= getpwuid(uid
);
406 unsigned char cmd
[BUFSIZE
];
408 char hostname
[MAXHOSTNAMELEN
];
409 int namelen
= MAXHOSTNAMELEN
;
411 if (gethostname((char *)hostname
, namelen
) == -1)
416 sprintf((char *)cmd
, "/usr/bin/mail %s", pp
->pw_name
);
417 mf
= popen((char *)cmd
, "w");
420 setbuf(mf
, (char *)cmd
);
423 "A copy of an editor buffer of yours was saved on %s when the system went down.\n" :
424 "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
);
426 "No name was associated with this buffer so it has been named \"LOST\".\n");
429 "A copy of an editor buffer of your file \"%s\"%s was saved on %s\nwhen the system \
431 "A copy of an editor buffer of your file \"%s\"%s was saved on %s\nwhen the editor \
432 was killed or was unable to save your changes.\n", fname
, (cryflag
) ? "[ENCRYPTED]" : "", hostname
);
434 * "the editor was killed" is perhaps still not an ideal
435 * error message. Usually, either it was forceably terminated
436 * or the phone was hung up, but we don't know which.
439 "This buffer can be retrieved using the \"recover\" command of the editor.\n");
441 "An easy way to do this is to give the command \"vi -r %s\".\n",
442 (fname
[0] == 0) ? "LOST" : (char *) fname
);
443 fprintf(mf
, "This works for \"edit\" and \"ex\" also.\n");