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

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