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

Revision 24908, 12.7 KB checked in by ghudson, 14 years ago (diff)
In delete: * Patches from Jonathan Kamens: - The "-f" flag to delete should suppress nonexistent file errors but not other errors. - When the "-v" flag is specified to expunge, the correct totals should be reported. Previously, the totals were incorrect. - Code cleanup.
Line 
1/*
2 * $Id: undelete.c,v 1.30 2002-11-20 19:09:24 zacheiss 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#include <stdio.h>
13#include <sys/types.h>
14#include <dirent.h>
15#include <sys/param.h>
16#include <string.h>
17#include <com_err.h>
18#include <errno.h>
19#include <unistd.h>
20#include "delete_errs.h"
21#include "pattern.h"
22#include "directories.h"
23#include "undelete.h"
24#include "shell_regexp.h"
25#include "mit-copying.h"
26#include "errors.h"
27#include "util.h"
28
29static void usage(void);
30static int interactive_mode(void), undelete(char *name);
31static int recurs_and_undelete(filerec *leaf), do_undelete(filerec *file_ent);
32static int do_file_rename(char *real_name, char *user_name);
33static int process_files(char **files, int num);
34static int unlink_completely(char *filename);
35static int filecmp(const void *arg1, const void *arg2);
36static int choose_better(char *str1, char *str2);
37
38int interactive, recursive, verbose, directoriesonly, noop, force;
39
40
41int main(int argc, char *argv[])
42{
43     extern char *optarg;
44     extern int optind;
45     int arg;
46     int retval;
47     
48#if defined(__APPLE__) && defined(__MACH__)
49     add_error_table(&et_del_error_table);
50#else
51     initialize_del_error_table();
52#endif
53     
54     whoami = lastpart(argv[0]);
55     interactive = recursive = verbose = directoriesonly = noop = force = 0;
56
57     while ((arg = getopt(argc, argv, "firvnR")) != -1) {
58          switch (arg) {
59          case 'f':
60               force++;
61               break;
62          case 'i':
63               interactive++;
64               break;
65          case 'r':
66               recursive++;
67               if (directoriesonly) {
68                    fprintf(stderr, "%s: -r and -R and mutually exclusive.\n",
69                            whoami);
70                    usage();
71                    exit(1);
72               }
73               break;
74          case 'v':
75               verbose++;
76               break;
77          case 'n':
78               noop++;
79               break;
80          case 'R':
81               directoriesonly++;
82               if (recursive) {
83                    fprintf(stderr, "%s: -r and -R are mutually exclusive.\n",
84                            whoami);
85                    usage();
86                    exit(1);
87               }
88               break;
89          default:
90               usage();
91               exit(1);
92          }
93     }
94
95     report_errors = ! force;
96     
97     if (optind == argc) {
98          if (interactive_mode())
99               error("interactive_mode");
100     }
101     else while (optind < argc) {
102          retval = undelete(argv[optind]);
103          if (retval)
104               error(argv[optind]);
105          optind++;
106     }
107     exit(((! force) && error_occurred) ? 1 : 0);
108}
109
110
111
112static int interactive_mode(void)
113{
114     char buf[MAXPATHLEN];
115     char *ptr;
116     int status = 0;
117     int retval;
118     
119     if (verbose) {
120          printf("Enter the files to be undeleted, one file per line.\n");
121          printf("Hit <RETURN> on a line by itself to exit.\n\n");
122     }
123     do {
124          printf("%s: ", whoami);
125          ptr = fgets(buf, MAXPATHLEN, stdin);
126          if (! ptr) {
127               printf("\n");
128               return status;
129          }
130          ptr = strchr(buf, '\n');  /* fgets breakage */
131          if (ptr)
132               *ptr = '\0';
133          if (! *buf)
134               return status;
135          retval = undelete(buf);
136          if (retval) {
137               error(buf);
138               status = retval;
139          }
140     } while (*buf);
141     return status;
142}
143
144
145
146static void usage()
147{
148     fprintf(stderr, "Usage: %s [ options ] [filename ...]\n", whoami);
149     fprintf(stderr, "Options are:\n");
150     fprintf(stderr, "     -r     recursive\n");
151     fprintf(stderr, "     -i     interactive\n");
152     fprintf(stderr, "     -f     force\n");
153     fprintf(stderr, "     -v     verbose\n");
154     fprintf(stderr, "     -n     noop\n");
155     fprintf(stderr, "     -R     directories only (i.e. no recursion)\n");
156     fprintf(stderr, "     --     end options and start filenames\n");
157     fprintf(stderr, "-r and -D are mutually exclusive\n");
158}
159
160static int undelete(char *name)
161{
162     char **found_files;
163     int num_found;
164     int status = 0;
165     filerec *current;
166     int retval;
167     
168     if ((retval =  get_the_files(name, &num_found, &found_files))) {
169          error(name);
170          return retval;
171     }
172     
173     if (num_found) {
174       if ((retval = process_files(found_files, num_found))) {
175         error(name);
176         return retval;
177       }
178       if (*name == '/')
179         current = get_root_tree();
180       else
181         current = get_cwd_tree();
182
183       status = recurs_and_undelete(current);
184       if (status) {
185         error(name);
186         return status;
187       }
188     }
189     else {
190          if (no_wildcards(name)) {
191               set_error(ENOENT)
192          }
193          else
194               set_error(DELETE_ENOMATCH);
195          error(name);
196          return error_code;
197     }
198
199     return status;
200}
201
202
203
204
205
206static int recurs_and_undelete(filerec *leaf)
207{
208     int status = 0;
209     int retval;
210     
211     if (leaf->specified) {
212          retval = do_undelete(leaf);
213          if (retval) {
214               error("do_undelete");
215               status = retval;
216          }
217     }
218
219     if (! status) { /* recurse only if if top-level undelete */
220                     /* succeeded or was not requested        */
221          if (leaf->dirs) {
222               retval = recurs_and_undelete(leaf->dirs);
223               if (retval) {
224                    error("recurs_and_undelete");
225                    status = retval;
226               }
227          }
228
229          if (leaf->files) {
230               retval = recurs_and_undelete(leaf->files);
231               if (retval) {
232                    error("recurs_and_undelete");
233                    status = retval;
234               }
235          }
236     }
237
238     if (leaf->next) {
239          retval = recurs_and_undelete(leaf->next);
240          if (retval) {
241               error("recurs_and_undelete");
242               status = retval;
243          }
244     }
245
246     free_leaf(leaf);
247
248     return status;
249}
250
251
252
253
254
255
256static int process_files(char **files, int num)
257{
258     int i;
259     listrec *filelist;
260     struct filrec *not_needed;
261     int retval;
262     
263     filelist = (listrec *) Malloc((unsigned) (sizeof(listrec) * num));
264     if ((! filelist) && num)
265     {
266          set_error(errno);
267          error("process_files");
268          return error_code;
269     }
270     
271     for (i = 0; i < num; i++) {
272          filelist[i].real_name = Malloc((unsigned) (strlen(files[i]) + 1));
273          if (! filelist[i].real_name) {
274               set_error(errno);
275               error("process_files");
276               return error_code;
277          }
278          (void) strcpy(filelist[i].real_name, files[i]);
279          filelist[i].user_name = Malloc((unsigned) (strlen(files[i]) + 1));
280          if (! filelist[i].user_name) {
281               set_error(errno);
282               error("process_files");
283               return error_code;
284          }
285          (void) convert_to_user_name(files[i], filelist[i].user_name);
286          free(files[i]);
287     }
288     free((char *) files);
289         
290     if ((retval = sort_files(filelist, num))) {
291          error("sort_files");
292          return retval;
293     }
294     if ((retval = unique(&filelist, &num))) {
295          error("unique");
296          return retval;
297     }
298     if ((retval = initialize_tree())) {
299          error("initialize_tree");
300          return retval;
301     }
302         
303     for (i = 0; i < num; i++) {
304       if ((retval = add_path_to_tree(filelist[i].real_name, &not_needed))) {
305         error("add_path_to_tree");
306         return retval;
307       }
308       else {
309         free(filelist[i].real_name);
310         free(filelist[i].user_name);
311       }
312     }
313     free((char *) filelist);
314     return 0;
315}
316
317     
318
319
320
321
322
323     
324static int do_undelete(filerec *file_ent)
325{
326     struct stat stat_buf;
327     char user_name[MAXPATHLEN], real_name[MAXPATHLEN];
328     int retval;
329     
330     if ((retval = get_leaf_path(file_ent, real_name))) {
331          if (! force)
332               fprintf(stderr, "%s: %s: %s\n", whoami, "get_leaf_path",
333                       error_message(retval));
334          return retval;
335     }
336     
337     (void) convert_to_user_name(real_name, user_name);
338
339     if (interactive) {
340          if ((file_ent->specs.st_mode & S_IFMT) == S_IFDIR)
341               printf("%s: Undelete directory %s? ", whoami, user_name);
342          else
343               printf("%s: Undelete %s? ", whoami, user_name);
344          if (! yes()) {
345               set_status(UNDEL_NOT_UNDELETED);
346               return error_code;
347          }
348     }
349     if (! lstat(user_name, &stat_buf)) if (! force) {
350          printf("%s: An undeleted %s already exists.\n", whoami, user_name);
351          printf("Do you wish to continue with the undelete and overwrite that version? ");
352          if (! yes()) {
353               set_status(UNDEL_NOT_UNDELETED);
354               return error_code;
355          }
356          if ((! noop) && (retval = unlink_completely(user_name))) {
357               error(user_name);
358               return retval;
359          }
360     }
361     if (noop) {
362          printf("%s: %s would be undeleted\n", whoami, user_name);
363          return 0;
364     }
365
366     if ((retval = do_file_rename(real_name, user_name))) {
367          error("do_file_rename");
368          return retval;
369     }
370     else {
371          if (verbose)
372               printf("%s: %s undeleted\n", whoami, user_name);
373          return 0;
374     }
375}
376
377
378
379
380static int do_file_rename(char *real_name, char *user_name)
381{
382     char *ptr;
383     int retval;
384     char error_buf[MAXPATHLEN+MAXPATHLEN+14];
385     char old_name[MAXPATHLEN], new_name[MAXPATHLEN];
386     char buf[MAXPATHLEN];
387
388     (void) strcpy(old_name, real_name);
389     (void) strcpy(new_name, real_name);
390
391     ptr = strrindex(new_name, ".#");
392     (void) convert_to_user_name(ptr, ptr);
393     (void) strcpy(ptr, firstpart(ptr, buf));
394     (void) strcpy(&old_name[ptr - new_name],
395                   firstpart(&old_name[ptr - new_name], buf));
396     if (rename(old_name, new_name)) {
397       set_error(errno);
398       (void) sprintf(error_buf, "renaming %s to %s",
399                      old_name, new_name);
400       error(error_buf);
401       return error_code;
402     }
403     if (ptr > new_name) {
404       *--ptr = '\0';
405       old_name[ptr - new_name] = '\0';
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
420static int filecmp(const void *arg1, const void *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
496static int choose_better(char *str1, char *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     
520static int unlink_completely(char *filename)
521{
522     char buf[MAXPATHLEN];
523     struct stat stat_buf;
524     DIR *dirp;
525     struct dirent *dp;
526     int retval;
527     int status = 0;
528     
529     if (lstat(filename, &stat_buf)) {
530          set_error(errno);
531          error(filename);
532          return error_code;
533     }
534
535     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
536          dirp = Opendir(filename);
537          if (! dirp) {
538               set_error(errno);
539               error(filename);
540               return error_code;
541          }
542               
543          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
544               if (is_dotfile(dp->d_name))
545                    continue;
546               (void) strcpy(buf, append(filename, dp->d_name));
547               if ((retval = unlink_completely(buf))) {
548                    error(buf);
549                    status = retval;
550               }
551          }
552          closedir(dirp);
553          if ((retval = rmdir(filename))) {
554               set_error(errno);
555               error(filename);
556               return error_code;
557          }
558     }
559     else if ((retval = unlink(filename))) {
560          set_error(errno);
561          error(filename);
562          return error_code;
563     }
564
565     return status;
566}
567
568
569
570
571int get_the_files(name, num_found, found)
572char *name;
573int *num_found;
574char ***found;
575{
576     int retval;
577     int options;
578     
579     options = FIND_DELETED;
580     if (recursive)
581          options |= RECURS_DELETED;
582     if (! directoriesonly)
583          options |= FIND_CONTENTS;
584
585     retval = find_matches(name, num_found, found, options);
586     if (retval) {
587          error("find_matches");
588          return retval;
589     }
590
591     return 0;
592}
Note: See TracBrowser for help on using the repository browser.