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

Revision 23660, 12.4 KB checked in by broder, 16 years ago (diff)
In delete: * Apply patches from jik: - Change the ENOMATCH error constant to DELETE_ENOMATCH to avoid conflicting with a system error constant of the same name
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#if (!defined(lint) && !defined(SABER))
13     static char rcsid_undelete_c[] = "$Id: undelete.c,v 1.30 2002-11-20 19:09:24 zacheiss 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#if defined(__APPLE__) && defined(__MACH__)
45     add_error_table(&et_del_error_table);
46#else
47     initialize_del_error_table();
48#endif
49     
50     whoami = lastpart(argv[0]);
51     interactive = recursive = verbose = directoriesonly = noop = force = 0;
52
53     while ((arg = getopt(argc, argv, "firvnR")) != -1) {
54          switch (arg) {
55          case 'f':
56               force++;
57               break;
58          case 'i':
59               interactive++;
60               break;
61          case 'r':
62               recursive++;
63               if (directoriesonly) {
64                    fprintf(stderr, "%s: -r and -R and mutually exclusive.\n",
65                            whoami);
66                    usage();
67                    exit(1);
68               }
69               break;
70          case 'v':
71               verbose++;
72               break;
73          case 'n':
74               noop++;
75               break;
76          case 'R':
77               directoriesonly++;
78               if (recursive) {
79                    fprintf(stderr, "%s: -r and -R are mutually exclusive.\n",
80                            whoami);
81                    usage();
82                    exit(1);
83               }
84               break;
85          default:
86               usage();
87               exit(1);
88          }
89     }
90
91     report_errors = ! force;
92     
93     if (optind == argc) {
94          if (interactive_mode())
95               error("interactive_mode");
96     }
97     else while (optind < argc) {
98          retval = undelete(argv[optind]);
99          if (retval)
100               error(argv[optind]);
101          optind++;
102     }
103     exit(((! force) && error_occurred) ? 1 : 0);
104}
105
106
107
108interactive_mode()
109{
110     char buf[MAXPATHLEN];
111     char *ptr;
112     int status = 0;
113     int retval;
114     
115     if (verbose) {
116          printf("Enter the files to be undeleted, one file per line.\n");
117          printf("Hit <RETURN> on a line by itself to exit.\n\n");
118     }
119     do {
120          printf("%s: ", whoami);
121          ptr = fgets(buf, MAXPATHLEN, stdin);
122          if (! ptr) {
123               printf("\n");
124               return status;
125          }
126          ptr = strchr(buf, '\n');  /* fgets breakage */
127          if (ptr)
128               *ptr = '\0';
129          if (! *buf)
130               return status;
131          retval = undelete(buf);
132          if (retval) {
133               error(buf);
134               status = retval;
135          }
136     } while (*buf);
137     return status;
138}
139
140
141
142usage()
143{
144     fprintf(stderr, "Usage: %s [ options ] [filename ...]\n", whoami);
145     fprintf(stderr, "Options are:\n");
146     fprintf(stderr, "     -r     recursive\n");
147     fprintf(stderr, "     -i     interactive\n");
148     fprintf(stderr, "     -f     force\n");
149     fprintf(stderr, "     -v     verbose\n");
150     fprintf(stderr, "     -n     noop\n");
151     fprintf(stderr, "     -R     directories only (i.e. no recursion)\n");
152     fprintf(stderr, "     --     end options and start filenames\n");
153     fprintf(stderr, "-r and -D are mutually exclusive\n");
154}
155
156undelete(name)
157char *name;
158{
159     char **found_files;
160     int num_found;
161     int status = 0;
162     filerec *current;
163     int retval;
164     
165     if (retval =  get_the_files(name, &num_found, &found_files)) {
166          error(name);
167          return retval;
168     }
169     
170     if (num_found) {
171          if (retval = process_files(found_files, num_found)) {
172               error(name);
173               return retval;
174          }
175          if (*name == '/')
176               current = get_root_tree();
177          else
178               current = get_cwd_tree();
179
180          status = recurs_and_undelete(current);
181          if (status) {
182               error(name);
183               return status;
184          }
185     }
186     else {
187          if (no_wildcards(name)) {
188               set_error(ENOENT)
189          }
190          else
191               set_error(DELETE_ENOMATCH);
192          error(name);
193          return error_code;
194     }
195
196     return status;
197}
198
199
200
201
202
203int recurs_and_undelete(leaf)
204filerec *leaf;
205{
206     int status = 0;
207     int retval;
208     
209     if (leaf->specified) {
210          retval = do_undelete(leaf);
211          if (retval) {
212               error("do_undelete");
213               status = retval;
214          }
215     }
216
217     if (! status) { /* recurse only if if top-level undelete */
218                     /* succeeded or was not requested        */
219          if (leaf->dirs) {
220               retval = recurs_and_undelete(leaf->dirs);
221               if (retval) {
222                    error("recurs_and_undelete");
223                    status = retval;
224               }
225          }
226
227          if (leaf->files) {
228               retval = recurs_and_undelete(leaf->files);
229               if (retval) {
230                    error("recurs_and_undelete");
231                    status = retval;
232               }
233          }
234     }
235
236     if (leaf->next) {
237          retval = recurs_and_undelete(leaf->next);
238          if (retval) {
239               error("recurs_and_undelete");
240               status = retval;
241          }
242     }
243
244     free_leaf(leaf);
245
246     return status;
247}
248
249
250
251
252
253
254int process_files(files, num)
255char **files;
256int 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     
324do_undelete(file_ent)
325filerec *file_ent;
326{
327     struct stat stat_buf;
328     char user_name[MAXPATHLEN], real_name[MAXPATHLEN];
329     int retval;
330     
331     if (retval = get_leaf_path(file_ent, real_name)) {
332          if (! force)
333               fprintf(stderr, "%s: %s: %s\n", whoami, "get_leaf_path",
334                       error_message(retval));
335          return retval;
336     }
337     
338     (void) convert_to_user_name(real_name, user_name);
339
340     if (interactive) {
341          if ((file_ent->specs.st_mode & S_IFMT) == S_IFDIR)
342               printf("%s: Undelete directory %s? ", whoami, user_name);
343          else
344               printf("%s: Undelete %s? ", whoami, user_name);
345          if (! yes()) {
346               set_status(UNDEL_NOT_UNDELETED);
347               return error_code;
348          }
349     }
350     if (! lstat(user_name, &stat_buf)) if (! force) {
351          printf("%s: An undeleted %s already exists.\n", whoami, user_name);
352          printf("Do you wish to continue with the undelete and overwrite that version? ");
353          if (! yes()) {
354               set_status(UNDEL_NOT_UNDELETED);
355               return error_code;
356          }
357          if (! noop) if (retval = unlink_completely(user_name)) {
358               error(user_name);
359               return retval;
360          }
361     }
362     if (noop) {
363          printf("%s: %s would be undeleted\n", whoami, user_name);
364          return 0;
365     }
366
367     if (retval = do_file_rename(real_name, user_name)) {
368          error("do_file_rename");
369          return retval;
370     }
371     else {
372          if (verbose)
373               printf("%s: %s undeleted\n", whoami, user_name);
374          return 0;
375     }
376}
377
378
379
380
381do_file_rename(real_name, user_name)
382char *real_name, *user_name;
383{
384     char *ptr;
385     int retval;
386     char error_buf[MAXPATHLEN+MAXPATHLEN+14];
387     char old_name[MAXPATHLEN], new_name[MAXPATHLEN];
388     char buf[MAXPATHLEN];
389
390     (void) strcpy(old_name, real_name);
391     (void) strcpy(new_name, real_name);
392
393     ptr = strrindex(new_name, ".#");
394     (void) convert_to_user_name(ptr, ptr);
395     (void) strcpy(ptr, firstpart(ptr, buf));
396     (void) strcpy(&old_name[ptr - new_name],
397                   firstpart(&old_name[ptr - new_name], buf));
398     if (rename(old_name, new_name)) {
399       set_error(errno);
400       (void) sprintf(error_buf, "renaming %s to %s",
401                      old_name, new_name);
402       error(error_buf);
403       return error_code;
404     }
405     if (ptr > new_name) {
406       *--ptr = '\0';
407       old_name[ptr - new_name] = '\0';
408     }
409     if (retval = change_path(real_name, user_name)) {
410       error("change_path");
411       return retval;
412     }
413     
414     return 0;
415}
416
417
418
419
420
421
422filecmp(arg1, arg2)
423const void *arg1, *arg2;
424{
425     listrec *file1 = (listrec *) arg1, *file2 = (listrec *) arg2;
426
427     return(strcmp(file1->user_name, file2->user_name));
428}
429
430     
431     
432int sort_files(data, num_data)
433listrec *data;
434int num_data;
435{
436     qsort(data, num_data, sizeof(listrec), filecmp);
437
438     return 0;
439}
440
441
442
443
444
445int unique(the_files, number)
446listrec **the_files;
447int *number;
448{
449     int i, last;
450     int offset;
451     listrec *files;
452
453     files = *the_files;
454     for (last = 0, i = 1; i < *number; i++) {
455          if (! strcmp(files[last].user_name, files[i].user_name)) {
456               int better;
457
458               better = choose_better(files[last].real_name,
459                                      files[i].real_name);
460               if (better == 1) { /* the first one is better */
461                    free (files[i].real_name);
462                    free (files[i].user_name);
463                    files[i].real_name = (char *) NULL;
464               }
465               else {
466                    free (files[last].real_name);
467                    free (files[last].user_name);
468                    files[last].real_name = (char *) NULL;
469                    last = i;
470               }
471          }
472          else
473               last = i;
474     }
475     
476     for (offset = 0, i = 0; i + offset < *number; i++) {
477          if (! files[i].real_name)
478               offset++;
479          if (i + offset < *number)
480               files[i] = files[i + offset];
481     }
482     *number -= offset;
483     files = (listrec *) realloc((char *) files,
484                                 (unsigned) (sizeof(listrec) * *number));
485     if ((! files) && *number)
486     {
487          set_error(errno);
488          error("realloc");
489          return errno;
490     }
491
492     *the_files = files;
493     return 0;
494}
495
496
497
498
499choose_better(str1, str2)
500char *str1, *str2;
501{
502     char *pos1, *pos2;
503     
504     pos1 = strindex(str1, ".#");
505     pos2 = strindex(str2, ".#");
506     while (pos1 && pos2) {
507          if (pos1 - str1 < pos2 - str2)
508               return(2);
509          else if (pos2 - str2 < pos1 - str1)
510               return(1);
511          pos1 = strindex(pos1 + 1, ".#");
512          pos2 = strindex(pos2 + 1, ".#");
513     }
514     if (! pos1)
515          return(1);
516     else
517          return(2);
518}
519
520
521
522
523     
524unlink_completely(filename)
525char *filename;
526{
527     char buf[MAXPATHLEN];
528     struct stat stat_buf;
529     DIR *dirp;
530     struct dirent *dp;
531     int retval;
532     int status = 0;
533     
534     if (lstat(filename, &stat_buf)) {
535          set_error(errno);
536          error(filename);
537          return error_code;
538     }
539
540     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
541          dirp = Opendir(filename);
542          if (! dirp) {
543               set_error(errno);
544               error(filename);
545               return error_code;
546          }
547               
548          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
549               if (is_dotfile(dp->d_name))
550                    continue;
551               (void) strcpy(buf, append(filename, dp->d_name));
552               if (retval = unlink_completely(buf)) {
553                    error(buf);
554                    status = retval;
555               }
556          }
557          closedir(dirp);
558          if (retval = rmdir(filename)) {
559               set_error(errno);
560               error(filename);
561               return error_code;
562          }
563     }
564     else if (retval = unlink(filename)) {
565          set_error(errno);
566          error(filename);
567          return error_code;
568     }
569
570     return status;
571}
572
573
574
575
576int get_the_files(name, num_found, found)
577char *name;
578int *num_found;
579char ***found;
580{
581     int retval;
582     int options;
583     
584     options = FIND_DELETED;
585     if (recursive)
586          options |= RECURS_DELETED;
587     if (! directoriesonly)
588          options |= FIND_CONTENTS;
589
590     retval = find_matches(name, num_found, found, options);
591     if (retval) {
592          error("find_matches");
593          return retval;
594     }
595
596     return 0;
597}
Note: See TracBrowser for help on using the repository browser.