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