1 /* Copyright (c) 1981 Regents of the University of California */ 2 static char *sccsid = "@(#)ex3.7preserve.c 7.5 08/02/81"; 3 #include <stdio.h> 4 #include <ctype.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <sys/dir.h> 8 #include <pwd.h> 9 #include "local/uparm.h" 10 /* mjm: "/tmp" --> TMP */ 11 #define TMP "/tmp" 12 13 #ifdef VMUNIX 14 #define HBLKS 2 15 #else 16 #define HBLKS 1 17 #endif 18 19 char xstr[1]; /* make loader happy */ 20 21 /* 22 * Expreserve - preserve a file in usrpath(preserve) 23 * Bill Joy UCB November 13, 1977 24 * 25 * This routine is very naive - it doesn't remove anything from 26 * usrpath(preserve)... this may mean that we * stuff there... the danger in doing anything with usrpath(preserve) 27 * is that the clock may be screwed up and we may get confused. 28 * 29 * We are called in two ways - first from the editor with no argumentss 30 * and the standard input open on the temp file. Second with an argument 31 * to preserve the entire contents of /tmp (root only). 32 * 33 * BUG: should do something about preserving Rx... (register contents) 34 * temporaries. 35 */ 36 37 #ifndef VMUNIX 38 #define LBLKS 125 39 #else 40 #define LBLKS 900 41 #endif 42 #define FNSIZE 128 43 44 struct header { 45 time_t Time; /* Time temp file last updated */ 46 int Uid; /* This users identity */ 47 #ifndef VMUNIX 48 short Flines; /* Number of lines in file */ 49 #else 50 int Flines; 51 #endif 52 char Savedfile[FNSIZE]; /* The current file name */ 53 short Blocks[LBLKS]; /* Blocks where line pointers stashed */ 54 } H; 55 56 #ifdef lint 57 #define ignore(a) Ignore(a) 58 #define ignorl(a) Ignorl(a) 59 #else 60 #define ignore(a) a 61 #define ignorl(a) a 62 #endif 63 64 struct passwd *getpwuid(); 65 off_t lseek(); 66 FILE *popen(); 67 68 #define eq(a, b) strcmp(a, b) == 0 69 70 main(argc) 71 int argc; 72 { 73 register FILE *tf; 74 struct direct dirent; 75 struct stat stbuf; 76 77 /* 78 * If only one argument, then preserve the standard input. 79 */ 80 if (argc == 1) { 81 if (copyout((char *) 0)) 82 exit(1); 83 exit(0); 84 } 85 86 /* 87 * If not super user, then can only preserve standard input. 88 */ 89 if (getuid()) { 90 fprintf(stderr, "NOT super user\n"); 91 exit(1); 92 } 93 94 /* 95 * ... else preserve all the stuff in /tmp, removing 96 * it as we go. 97 */ 98 if (chdir(TMP) < 0) { 99 perror(TMP); 100 exit(1); 101 } 102 103 tf = fopen(".", "r"); 104 if (tf == NULL) { 105 perror(TMP); 106 exit(1); 107 } 108 while (fread((char *) &dirent, sizeof dirent, 1, tf) == 1) { 109 if (dirent.d_ino == 0) 110 continue; 111 /* 112 * Ex temporaries must begin with Ex; 113 * we check that the 10th character of the name is null 114 * so we won't have to worry about non-null terminated names 115 * later on. 116 */ 117 if (dirent.d_name[0] != 'E' || dirent.d_name[1] != 'x' || dirent.d_name[10]) 118 continue; 119 if (stat(dirent.d_name, &stbuf)) 120 continue; 121 if ((stbuf.st_mode & S_IFMT) != S_IFREG) 122 continue; 123 /* 124 * Save the bastard. 125 */ 126 ignore(copyout(dirent.d_name)); 127 } 128 exit(0); 129 } 130 131 char pattern[] = usrpath(preserve/Exaa`XXXXX); 132 133 /* 134 * Copy file name into usrpath(preserve)/... 135 * If name is (char *) 0, then do the standard input. 136 * We make some checks on the input to make sure it is 137 * really an editor temporary, generate a name for the 138 * file (this is the slowest thing since we must stat 139 * to find a unique name), and finally copy the file. 140 */ 141 copyout(name) 142 char *name; 143 { 144 int i; 145 static int reenter; 146 char buf[BUFSIZ]; 147 148 /* 149 * The first time we put in the digits of our 150 * process number at the end of the pattern. 151 */ 152 if (reenter == 0) { 153 mkdigits(pattern); 154 reenter++; 155 } 156 157 /* 158 * If a file name was given, make it the standard 159 * input if possible. 160 */ 161 if (name != 0) { 162 ignore(close(0)); 163 /* 164 * Need read/write access for arcane reasons 165 * (see below). 166 */ 167 if (open(name, 2) < 0) 168 return (-1); 169 } 170 171 /* 172 * Get the header block. 173 */ 174 ignorl(lseek(0, 0l, 0)); 175 if (read(0, (char *) &H, sizeof H) != sizeof H) { 176 format: 177 if (name == 0) 178 fprintf(stderr, "Buffer format error\t"); 179 return (-1); 180 } 181 182 /* 183 * Consistency checsks so we don't copy out garbage. 184 */ 185 if (H.Flines < 0) { 186 #ifdef DEBUG 187 fprintf(stderr, "Negative number of lines\n"); 188 #endif 189 goto format; 190 } 191 if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) { 192 #ifdef DEBUG 193 fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]); 194 #endif 195 goto format; 196 } 197 if (name == 0 && H.Uid != getuid()) { 198 #ifdef DEBUG 199 fprintf(stderr, "Wrong user-id\n"); 200 #endif 201 goto format; 202 } 203 if (lseek(0, 0l, 0)) { 204 #ifdef DEBUG 205 fprintf(stderr, "Negative number of lines\n"); 206 #endif 207 goto format; 208 } 209 210 /* 211 * If no name was assigned to the file, then give it the name 212 * LOST, by putting this in the header. 213 */ 214 if (H.Savedfile[0] == 0) { 215 strcpy(H.Savedfile, "LOST"); 216 ignore(write(0, (char *) &H, sizeof H)); 217 H.Savedfile[0] = 0; 218 lseek(0, 0l, 0); 219 } 220 221 /* 222 * File is good. Get a name and create a file for the copy. 223 */ 224 mknext(pattern); 225 ignore(close(1)); 226 if (creat(pattern, 0600) < 0) { 227 if (name == 0) 228 perror(pattern); 229 return (1); 230 } 231 232 /* 233 * Make the target be owned by the owner of the file. 234 */ 235 ignore(chown(pattern, H.Uid, 0)); 236 237 /* 238 * Copy the file. 239 */ 240 for (;;) { 241 i = read(0, buf, BUFSIZ); 242 if (i < 0) { 243 if (name) 244 perror("Buffer read error"); 245 ignore(unlink(pattern)); 246 return (-1); 247 } 248 if (i == 0) { 249 if (name) 250 ignore(unlink(name)); 251 notify(H.Uid, H.Savedfile, (int) name); 252 return (0); 253 } 254 if (write(1, buf, i) != i) { 255 if (name == 0) 256 perror(pattern); 257 unlink(pattern); 258 return (-1); 259 } 260 } 261 } 262 263 /* 264 * Blast the last 5 characters of cp to be the process number. 265 */ 266 mkdigits(cp) 267 char *cp; 268 { 269 register int i, j; 270 271 for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--) 272 *--cp = i % 10 | '0'; 273 } 274 275 /* 276 * Make the name in cp be unique by clobbering up to 277 * three alphabetic characters into a sequence of the form 'aab', 'aac', etc. 278 * Mktemp gets weird names too quickly to be useful here. 279 */ 280 mknext(cp) 281 char *cp; 282 { 283 char *dcp; 284 struct stat stb; 285 286 dcp = cp + strlen(cp) - 1; 287 while (isdigit(*dcp)) 288 dcp--; 289 whoops: 290 if (dcp[0] == 'z') { 291 dcp[0] = 'a'; 292 if (dcp[-1] == 'z') { 293 dcp[-1] = 'a'; 294 if (dcp[-2] == 'z') 295 fprintf(stderr, "Can't find a name\t"); 296 dcp[-2]++; 297 } else 298 dcp[-1]++; 299 } else 300 dcp[0]++; 301 if (stat(cp, &stb) == 0) 302 goto whoops; 303 } 304 305 /* 306 * Notify user uid that his file fname has been saved. 307 */ 308 notify(uid, fname, flag) 309 int uid; 310 char *fname; 311 { 312 struct passwd *pp = getpwuid(uid); 313 register FILE *mf; 314 char cmd[BUFSIZ]; 315 316 if (pp == NULL) 317 return; 318 sprintf(cmd, "mail %s", pp->pw_name); 319 mf = popen(cmd, "w"); 320 if (mf == NULL) 321 return; 322 setbuf(mf, cmd); 323 if (fname[0] == 0) { 324 fprintf(mf, 325 "A copy of an editor buffer of yours was saved when %s.\n", 326 flag ? "the system went down" : "the editor was killed"); 327 fprintf(mf, 328 "No name was associated with this buffer so it has been named \"LOST\".\n"); 329 } else 330 fprintf(mf, 331 "A copy of an editor buffer of your file \"%s\"\nwas saved when %s.\n", fname, 332 /* 333 * "the editor was killed" is perhaps still not an ideal 334 * error message. Usually, either it was forcably terminated 335 * or the phone was hung up, but we don't know which. 336 */ 337 flag ? "the system went down" : "the editor was killed"); 338 fprintf(mf, 339 "This buffer can be retrieved using the \"recover\" command of the editor.\n"); 340 fprintf(mf, 341 "An easy way to do this is to give the command \"ex -r %s\".\n",fname); 342 fprintf(mf, 343 "This works for \"edit\" and \"vi\" also.\n"); 344 pclose(mf); 345 } 346 347 /* 348 * people making love 349 * never exactly the same 350 * just like a snowflake 351 */ 352 353 #ifdef lint 354 Ignore(a) 355 int a; 356 { 357 358 a = a; 359 } 360 361 Ignorl(a) 362 long a; 363 { 364 365 a = a; 366 } 367 #endif 368