source: trunk/athena/bin/delete/undelete.c @ 12350

Revision 12350, 12.3 KB checked in by ghudson, 26 years ago (diff)
Some RCS ID cleanup: delete $Log$ and replace other RCS keywords with $Id$.
Line 
1/*
2 * $Id: undelete.c,v 1.29 1999-01-22 23:09:07 ghudson Exp $
3 *
4 * This program is part of a package including delete, undelete,
5 * lsdel, expunge and purge.  The software suite is meant as a
6 * replacement for rm which allows for file recovery.
7 *
8 * Copyright (c) 1989 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file "mit-copying.h."
10 */
11
12#if (!defined(lint) && !defined(SABER))
13     static char rcsid_undelete_c[] = "$Id: undelete.c,v 1.29 1999-01-22 23:09:07 ghudson Exp $";
14#endif
15
16#include <stdio.h>
17#include <sys/types.h>
18#include <dirent.h>
19#include <sys/param.h>
20#include <string.h>
21#include <com_err.h>
22#include <errno.h>
23#include "delete_errs.h"
24#include "pattern.h"
25#include "util.h"
26#include "directories.h"
27#include "undelete.h"
28#include "shell_regexp.h"
29#include "mit-copying.h"
30#include "errors.h"
31
32int interactive, recursive, verbose, directoriesonly, noop, force;
33
34
35main(argc, argv)
36int argc;
37char *argv[];
38{
39     extern char *optarg;
40     extern int optind;
41     int arg;
42     int retval;
43     
44     initialize_del_error_table();
45     
46     whoami = lastpart(argv[0]);
47     interactive = recursive = verbose = directoriesonly = noop = force = 0;
48
49     while ((arg = getopt(argc, argv, "firvnR")) != -1) {
50          switch (arg) {
51          case 'f':
52               force++;
53               break;
54          case 'i':
55               interactive++;
56               break;
57          case 'r':
58               recursive++;
59               if (directoriesonly) {
60                    fprintf(stderr, "%s: -r and -R and mutually exclusive.\n",
61                            whoami);
62                    usage();
63                    exit(1);
64               }
65               break;
66          case 'v':
67               verbose++;
68               break;
69          case 'n':
70               noop++;
71               break;
72          case 'R':
73               directoriesonly++;
74               if (recursive) {
75                    fprintf(stderr, "%s: -r and -R are mutually exclusive.\n",
76                            whoami);
77                    usage();
78                    exit(1);
79               }
80               break;
81          default:
82               usage();
83               exit(1);
84          }
85     }
86
87     report_errors = ! force;
88     
89     if (optind == argc) {
90          if (interactive_mode())
91               error("interactive_mode");
92     }
93     else while (optind < argc) {
94          retval = undelete(argv[optind]);
95          if (retval)
96               error(argv[optind]);
97          optind++;
98     }
99     exit(((! force) && error_occurred) ? 1 : 0);
100}
101
102
103
104interactive_mode()
105{
106     char buf[MAXPATHLEN];
107     char *ptr;
108     int status = 0;
109     int retval;
110     
111     if (verbose) {
112          printf("Enter the files to be undeleted, one file per line.\n");
113          printf("Hit <RETURN> on a line by itself to exit.\n\n");
114     }
115     do {
116          printf("%s: ", whoami);
117          ptr = fgets(buf, MAXPATHLEN, stdin);
118          if (! ptr) {
119               printf("\n");
120               return status;
121          }
122          ptr = strchr(buf, '\n');  /* fgets breakage */
123          if (ptr)
124               *ptr = '\0';
125          if (! *buf)
126               return status;
127          retval = undelete(buf);
128          if (retval) {
129               error(buf);
130               status = retval;
131          }
132     } while (*buf);
133     return status;
134}
135
136
137
138usage()
139{
140     fprintf(stderr, "Usage: %s [ options ] [filename ...]\n", whoami);
141     fprintf(stderr, "Options are:\n");
142     fprintf(stderr, "     -r     recursive\n");
143     fprintf(stderr, "     -i     interactive\n");
144     fprintf(stderr, "     -f     force\n");
145     fprintf(stderr, "     -v     verbose\n");
146     fprintf(stderr, "     -n     noop\n");
147     fprintf(stderr, "     -R     directories only (i.e. no recursion)\n");
148     fprintf(stderr, "     --     end options and start filenames\n");
149     fprintf(stderr, "-r and -D are mutually exclusive\n");
150}
151
152undelete(name)
153char *name;
154{
155     char **found_files;
156     int num_found;
157     int status = 0;
158     filerec *current;
159     int retval;
160     
161     if (retval =  get_the_files(name, &num_found, &found_files)) {
162          error(name);
163          return retval;
164     }
165     
166     if (num_found) {
167          if (retval = process_files(found_files, num_found)) {
168               error(name);
169               return retval;
170          }
171          if (*name == '/')
172               current = get_root_tree();
173          else
174               current = get_cwd_tree();
175
176          status = recurs_and_undelete(current);
177          if (status) {
178               error(name);
179               return status;
180          }
181     }
182     else {
183          if (no_wildcards(name)) {
184               set_error(ENOENT)
185          }
186          else
187               set_error(ENOMATCH);
188          error(name);
189          return error_code;
190     }
191
192     return status;
193}
194
195
196
197
198
199int recurs_and_undelete(leaf)
200filerec *leaf;
201{
202     int status = 0;
203     int retval;
204     
205     if (leaf->specified) {
206          retval = do_undelete(leaf);
207          if (retval) {
208               error("do_undelete");
209               status = retval;
210          }
211     }
212
213     if (! status) { /* recurse only if if top-level undelete */
214                     /* succeeded or was not requested        */
215          if (leaf->dirs) {
216               retval = recurs_and_undelete(leaf->dirs);
217               if (retval) {
218                    error("recurs_and_undelete");
219                    status = retval;
220               }
221          }
222
223          if (leaf->files) {
224               retval = recurs_and_undelete(leaf->files);
225               if (retval) {
226                    error("recurs_and_undelete");
227                    status = retval;
228               }
229          }
230     }
231
232     if (leaf->next) {
233          retval = recurs_and_undelete(leaf->next);
234          if (retval) {
235               error("recurs_and_undelete");
236               status = retval;
237          }
238     }
239
240     free_leaf(leaf);
241
242     return status;
243}
244
245
246
247
248
249
250int process_files(files, num)
251char **files;
252int num;
253{
254     int i;
255     listrec *filelist;
256     struct filrec *not_needed;
257     int retval;
258     
259     filelist = (listrec *) Malloc((unsigned) (sizeof(listrec) * num));
260     if ((! filelist) && num)
261     {
262          set_error(errno);
263          error("process_files");
264          return error_code;
265     }
266     
267     for (i = 0; i < num; i++) {
268          filelist[i].real_name = Malloc((unsigned) (strlen(files[i]) + 1));
269          if (! filelist[i].real_name) {
270               set_error(errno);
271               error("process_files");
272               return error_code;
273          }
274          (void) strcpy(filelist[i].real_name, files[i]);
275          filelist[i].user_name = Malloc((unsigned) (strlen(files[i]) + 1));
276          if (! filelist[i].user_name) {
277               set_error(errno);
278               error("process_files");
279               return error_code;
280          }
281          (void) convert_to_user_name(files[i], filelist[i].user_name);
282          free(files[i]);
283     }
284     free((char *) files);
285         
286     if (retval = sort_files(filelist, num)) {
287          error("sort_files");
288          return retval;
289     }
290     if (retval = unique(&filelist, &num)) {
291          error("unique");
292          return retval;
293     }
294     if (retval = initialize_tree()) {
295          error("initialize_tree");
296          return retval;
297     }
298         
299     for (i = 0; i < num; i++) {
300          if (retval = add_path_to_tree(filelist[i].real_name, &not_needed)) {
301               error("add_path_to_tree");
302               return retval;
303          }
304          else {
305               free(filelist[i].real_name);
306               free(filelist[i].user_name);
307          }
308     }
309     free((char *) filelist);
310     return 0;
311}
312
313     
314
315
316
317
318
319     
320do_undelete(file_ent)
321filerec *file_ent;
322{
323     struct stat stat_buf;
324     char user_name[MAXPATHLEN], real_name[MAXPATHLEN];
325     int retval;
326     
327     if (retval = get_leaf_path(file_ent, real_name)) {
328          if (! force)
329               fprintf(stderr, "%s: %s: %s\n", whoami, "get_leaf_path",
330                       error_message(retval));
331          return retval;
332     }
333     
334     (void) convert_to_user_name(real_name, user_name);
335
336     if (interactive) {
337          if ((file_ent->specs.st_mode & S_IFMT) == S_IFDIR)
338               printf("%s: Undelete directory %s? ", whoami, user_name);
339          else
340               printf("%s: Undelete %s? ", whoami, user_name);
341          if (! yes()) {
342               set_status(UNDEL_NOT_UNDELETED);
343               return error_code;
344          }
345     }
346     if (! lstat(user_name, &stat_buf)) if (! force) {
347          printf("%s: An undeleted %s already exists.\n", whoami, user_name);
348          printf("Do you wish to continue with the undelete and overwrite that version? ");
349          if (! yes()) {
350               set_status(UNDEL_NOT_UNDELETED);
351               return error_code;
352          }
353          if (! noop) if (retval = unlink_completely(user_name)) {
354               error(user_name);
355               return retval;
356          }
357     }
358     if (noop) {
359          printf("%s: %s would be undeleted\n", whoami, user_name);
360          return 0;
361     }
362
363     if (retval = do_file_rename(real_name, user_name)) {
364          error("do_file_rename");
365          return retval;
366     }
367     else {
368          if (verbose)
369               printf("%s: %s undeleted\n", whoami, user_name);
370          return 0;
371     }
372}
373
374
375
376
377do_file_rename(real_name, user_name)
378char *real_name, *user_name;
379{
380     char *ptr;
381     int retval;
382     char error_buf[MAXPATHLEN+MAXPATHLEN+14];
383     char old_name[MAXPATHLEN], new_name[MAXPATHLEN];
384     char buf[MAXPATHLEN];
385
386     (void) strcpy(old_name, real_name);
387     (void) strcpy(new_name, real_name);
388
389     while (ptr = strrindex(new_name, ".#")) {
390          (void) convert_to_user_name(ptr, ptr);
391          (void) strcpy(ptr, firstpart(ptr, buf));
392          (void) strcpy(&old_name[ptr - new_name],
393                        firstpart(&old_name[ptr - new_name], buf));
394          if (rename(old_name, new_name)) {
395               set_error(errno);
396               (void) sprintf(error_buf, "renaming %s to %s",
397                              old_name, new_name);
398               error(error_buf);
399               return error_code;
400          }
401          if (ptr > new_name) {
402               *--ptr = '\0';
403               old_name[ptr - new_name] = '\0';
404          }
405     }
406     if (retval = change_path(real_name, user_name)) {
407          error("change_path");
408          return retval;
409     }
410     
411     return 0;
412}
413
414
415
416
417
418
419filecmp(arg1, arg2)
420const void *arg1, *arg2;
421{
422     listrec *file1 = (listrec *) arg1, *file2 = (listrec *) arg2;
423
424     return(strcmp(file1->user_name, file2->user_name));
425}
426
427     
428     
429int sort_files(data, num_data)
430listrec *data;
431int num_data;
432{
433     qsort(data, num_data, sizeof(listrec), filecmp);
434
435     return 0;
436}
437
438
439
440
441
442int unique(the_files, number)
443listrec **the_files;
444int *number;
445{
446     int i, last;
447     int offset;
448     listrec *files;
449
450     files = *the_files;
451     for (last = 0, i = 1; i < *number; i++) {
452          if (! strcmp(files[last].user_name, files[i].user_name)) {
453               int better;
454
455               better = choose_better(files[last].real_name,
456                                      files[i].real_name);
457               if (better == 1) { /* the first one is better */
458                    free (files[i].real_name);
459                    free (files[i].user_name);
460                    files[i].real_name = (char *) NULL;
461               }
462               else {
463                    free (files[last].real_name);
464                    free (files[last].user_name);
465                    files[last].real_name = (char *) NULL;
466                    last = i;
467               }
468          }
469          else
470               last = i;
471     }
472     
473     for (offset = 0, i = 0; i + offset < *number; i++) {
474          if (! files[i].real_name)
475               offset++;
476          if (i + offset < *number)
477               files[i] = files[i + offset];
478     }
479     *number -= offset;
480     files = (listrec *) realloc((char *) files,
481                                 (unsigned) (sizeof(listrec) * *number));
482     if ((! files) && *number)
483     {
484          set_error(errno);
485          error("realloc");
486          return errno;
487     }
488
489     *the_files = files;
490     return 0;
491}
492
493
494
495
496choose_better(str1, str2)
497char *str1, *str2;
498{
499     char *pos1, *pos2;
500     
501     pos1 = strindex(str1, ".#");
502     pos2 = strindex(str2, ".#");
503     while (pos1 && pos2) {
504          if (pos1 - str1 < pos2 - str2)
505               return(2);
506          else if (pos2 - str2 < pos1 - str1)
507               return(1);
508          pos1 = strindex(pos1 + 1, ".#");
509          pos2 = strindex(pos2 + 1, ".#");
510     }
511     if (! pos1)
512          return(1);
513     else
514          return(2);
515}
516
517
518
519
520     
521unlink_completely(filename)
522char *filename;
523{
524     char buf[MAXPATHLEN];
525     struct stat stat_buf;
526     DIR *dirp;
527     struct dirent *dp;
528     int retval;
529     int status = 0;
530     
531     if (lstat(filename, &stat_buf)) {
532          set_error(errno);
533          error(filename);
534          return error_code;
535     }
536
537     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
538          dirp = Opendir(filename);
539          if (! dirp) {
540               set_error(errno);
541               error(filename);
542               return error_code;
543          }
544               
545          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
546               if (is_dotfile(dp->d_name))
547                    continue;
548               (void) strcpy(buf, append(filename, dp->d_name));
549               if (retval = unlink_completely(buf)) {
550                    error(buf);
551                    status = retval;
552               }
553          }
554          closedir(dirp);
555          if (retval = rmdir(filename)) {
556               set_error(errno);
557               error(filename);
558               return error_code;
559          }
560     }
561     else if (retval = unlink(filename)) {
562          set_error(errno);
563          error(filename);
564          return error_code;
565     }
566
567     return status;
568}
569
570
571
572
573int get_the_files(name, num_found, found)
574char *name;
575int *num_found;
576char ***found;
577{
578     int retval;
579     int options;
580     
581     options = FIND_DELETED;
582     if (recursive)
583          options |= RECURS_DELETED;
584     if (! directoriesonly)
585          options |= FIND_CONTENTS;
586
587     retval = find_matches(name, num_found, found, options);
588     if (retval) {
589          error("find_matches");
590          return retval;
591     }
592
593     return 0;
594}
Note: See TracBrowser for help on using the repository browser.