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.
RevLine 
[1671]1/*
[18055]2 * $Id: undelete.c,v 1.30 2002-11-20 19:09:24 zacheiss Exp $
[1671]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.
[4505]9 * For copying and distribution information, see the file "mit-copying.h."
[1671]10 */
11
[1656]12#include <stdio.h>
13#include <sys/types.h>
[5050]14#include <dirent.h>
[1656]15#include <sys/param.h>
[3049]16#include <string.h>
[2171]17#include <com_err.h>
18#include <errno.h>
[24908]19#include <unistd.h>
[2171]20#include "delete_errs.h"
[1690]21#include "pattern.h"
[4415]22#include "directories.h"
[1690]23#include "undelete.h"
[2171]24#include "shell_regexp.h"
[4505]25#include "mit-copying.h"
[2171]26#include "errors.h"
[24908]27#include "util.h"
[1656]28
[24908]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
[1690]38int interactive, recursive, verbose, directoriesonly, noop, force;
[1705]39
[1684]40
[24908]41int main(int argc, char *argv[])
[1656]42{
[1671]43     extern char *optarg;
44     extern int optind;
45     int arg;
[2171]46     int retval;
[1671]47     
[23271]48#if defined(__APPLE__) && defined(__MACH__)
49     add_error_table(&et_del_error_table);
50#else
[2171]51     initialize_del_error_table();
[23271]52#endif
[2171]53     
[1656]54     whoami = lastpart(argv[0]);
[1674]55     interactive = recursive = verbose = directoriesonly = noop = force = 0;
[2171]56
[1674]57     while ((arg = getopt(argc, argv, "firvnR")) != -1) {
[1671]58          switch (arg) {
[1674]59          case 'f':
60               force++;
61               break;
[1671]62          case 'i':
63               interactive++;
64               break;
65          case 'r':
66               recursive++;
67               if (directoriesonly) {
[1674]68                    fprintf(stderr, "%s: -r and -R and mutually exclusive.\n",
[1671]69                            whoami);
70                    usage();
71                    exit(1);
72               }
73               break;
74          case 'v':
75               verbose++;
76               break;
77          case 'n':
78               noop++;
79               break;
[1674]80          case 'R':
[1671]81               directoriesonly++;
82               if (recursive) {
[1674]83                    fprintf(stderr, "%s: -r and -R are mutually exclusive.\n",
[1671]84                            whoami);
85                    usage();
86                    exit(1);
87               }
[2171]88               break;
[1696]89          default:
90               usage();
91               exit(1);
[1671]92          }
93     }
[2171]94
95     report_errors = ! force;
96     
97     if (optind == argc) {
98          if (interactive_mode())
99               error("interactive_mode");
100     }
[1671]101     else while (optind < argc) {
[2171]102          retval = undelete(argv[optind]);
103          if (retval)
104               error(argv[optind]);
[1671]105          optind++;
106     }
[2171]107     exit(((! force) && error_occurred) ? 1 : 0);
[1671]108}
[1656]109
[1671]110
111
[24908]112static int interactive_mode(void)
[1671]113{
[1686]114     char buf[MAXPATHLEN];
115     char *ptr;
116     int status = 0;
[2171]117     int retval;
118     
[1686]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");
[2171]128               return status;
[1686]129          }
[10977]130          ptr = strchr(buf, '\n');  /* fgets breakage */
[1686]131          if (ptr)
132               *ptr = '\0';
133          if (! *buf)
[2171]134               return status;
135          retval = undelete(buf);
136          if (retval) {
137               error(buf);
138               status = retval;
139          }
140     } while (*buf);
141     return status;
[1656]142}
143
144
145
[24908]146static void usage()
[1671]147{
[1684]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");
[1671]158}
[1656]159
[24908]160static int undelete(char *name)
[1671]161{
[1684]162     char **found_files;
163     int num_found;
164     int status = 0;
[1680]165     filerec *current;
[2171]166     int retval;
[1674]167     
[24908]168     if ((retval =  get_the_files(name, &num_found, &found_files))) {
[2221]169          error(name);
[2171]170          return retval;
171     }
172     
[1674]173     if (num_found) {
[24908]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();
[2171]182
[24908]183       status = recurs_and_undelete(current);
184       if (status) {
185         error(name);
186         return status;
187       }
[1674]188     }
[1676]189     else {
[2221]190          if (no_wildcards(name)) {
[2171]191               set_error(ENOENT)
192          }
193          else
[23660]194               set_error(DELETE_ENOMATCH);
[2221]195          error(name);
[2171]196          return error_code;
[1676]197     }
[2171]198
199     return status;
[1671]200}
201
[1674]202
203
[1680]204
[1732]205
[24908]206static int recurs_and_undelete(filerec *leaf)
[1732]207{
208     int status = 0;
[2171]209     int retval;
210     
211     if (leaf->specified) {
212          retval = do_undelete(leaf);
213          if (retval) {
214               error("do_undelete");
[4032]215               status = retval;
[2171]216          }
217     }
[1732]218
[4032]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               }
[2171]227          }
[1732]228
[4032]229          if (leaf->files) {
230               retval = recurs_and_undelete(leaf->files);
231               if (retval) {
232                    error("recurs_and_undelete");
233                    status = retval;
234               }
[2171]235          }
236     }
[4032]237
[2171]238     if (leaf->next) {
239          retval = recurs_and_undelete(leaf->next);
240          if (retval) {
241               error("recurs_and_undelete");
242               status = retval;
243          }
[1732]244     }
[4032]245
[2221]246     free_leaf(leaf);
[2171]247
248     return status;
[1732]249}
250
251
252
253
254
[2171]255
[24908]256static int process_files(char **files, int num)
[1674]257{
[1680]258     int i;
[1684]259     listrec *filelist;
[2171]260     struct filrec *not_needed;
261     int retval;
262     
[2363]263     filelist = (listrec *) Malloc((unsigned) (sizeof(listrec) * num));
[5050]264     if ((! filelist) && num)
265     {
[2171]266          set_error(errno);
267          error("process_files");
268          return error_code;
[1680]269     }
[2171]270     
[1698]271     for (i = 0; i < num; i++) {
[2363]272          filelist[i].real_name = Malloc((unsigned) (strlen(files[i]) + 1));
[2171]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]);
[2363]279          filelist[i].user_name = Malloc((unsigned) (strlen(files[i]) + 1));
[2171]280          if (! filelist[i].user_name) {
281               set_error(errno);
282               error("process_files");
283               return error_code;
284          }
[2221]285          (void) convert_to_user_name(files[i], filelist[i].user_name);
[1684]286          free(files[i]);
[1680]287     }
[2171]288     free((char *) files);
289         
[24908]290     if ((retval = sort_files(filelist, num))) {
[2171]291          error("sort_files");
292          return retval;
[1680]293     }
[24908]294     if ((retval = unique(&filelist, &num))) {
[2171]295          error("unique");
296          return retval;
297     }
[24908]298     if ((retval = initialize_tree())) {
[2171]299          error("initialize_tree");
300          return retval;
301     }
302         
[1698]303     for (i = 0; i < num; i++) {
[24908]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       }
[1684]312     }
[2171]313     free((char *) filelist);
314     return 0;
[1680]315}
[1674]316
[1680]317     
[1674]318
[1680]319
[1674]320
321
322
323     
[24908]324static int do_undelete(filerec *file_ent)
[1674]325{
326     struct stat stat_buf;
[1684]327     char user_name[MAXPATHLEN], real_name[MAXPATHLEN];
[2171]328     int retval;
329     
[24908]330     if ((retval = get_leaf_path(file_ent, real_name))) {
[2171]331          if (! force)
332               fprintf(stderr, "%s: %s: %s\n", whoami, "get_leaf_path",
333                       error_message(retval));
334          return retval;
335     }
336     
[2221]337     (void) convert_to_user_name(real_name, user_name);
[1684]338
[1676]339     if (interactive) {
[1732]340          if ((file_ent->specs.st_mode & S_IFMT) == S_IFDIR)
[1684]341               printf("%s: Undelete directory %s? ", whoami, user_name);
342          else
343               printf("%s: Undelete %s? ", whoami, user_name);
[2171]344          if (! yes()) {
345               set_status(UNDEL_NOT_UNDELETED);
346               return error_code;
347          }
[1676]348     }
[1680]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? ");
[2171]352          if (! yes()) {
353               set_status(UNDEL_NOT_UNDELETED);
354               return error_code;
355          }
[24908]356          if ((! noop) && (retval = unlink_completely(user_name))) {
[2171]357               error(user_name);
358               return retval;
359          }
[1674]360     }
361     if (noop) {
[1684]362          printf("%s: %s would be undeleted\n", whoami, user_name);
[2171]363          return 0;
[1674]364     }
365
[24908]366     if ((retval = do_file_rename(real_name, user_name))) {
[2171]367          error("do_file_rename");
368          return retval;
369     }
370     else {
[1674]371          if (verbose)
[1680]372               printf("%s: %s undeleted\n", whoami, user_name);
[2171]373          return 0;
[1674]374     }
375}
376
377
378
379
[24908]380static int do_file_rename(char *real_name, char *user_name)
[1674]381{
[1680]382     char *ptr;
[2171]383     int retval;
384     char error_buf[MAXPATHLEN+MAXPATHLEN+14];
[1680]385     char old_name[MAXPATHLEN], new_name[MAXPATHLEN];
386     char buf[MAXPATHLEN];
[1674]387
[2171]388     (void) strcpy(old_name, real_name);
389     (void) strcpy(new_name, real_name);
390
[18055]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;
[1674]402     }
[18055]403     if (ptr > new_name) {
404       *--ptr = '\0';
405       old_name[ptr - new_name] = '\0';
406     }
[24908]407     if ((retval = change_path(real_name, user_name))) {
[18055]408       error("change_path");
409       return retval;
[2171]410     }
411     
412     return 0;
[1674]413}
414
415
416
[1680]417
418
419
[24908]420static int filecmp(const void *arg1, const void *arg2)
[1656]421{
[12144]422     listrec *file1 = (listrec *) arg1, *file2 = (listrec *) arg2;
423
[1684]424     return(strcmp(file1->user_name, file2->user_name));
[1656]425}
426
[1680]427     
428     
[2171]429int sort_files(data, num_data)
[1684]430listrec *data;
[1656]431int num_data;
432{
[12144]433     qsort(data, num_data, sizeof(listrec), filecmp);
[2171]434
435     return 0;
[1656]436}
437
438
439
440
441
[2171]442int unique(the_files, number)
443listrec **the_files;
[1671]444int *number;
445{
[1680]446     int i, last;
[1684]447     int offset;
[2171]448     listrec *files;
449
450     files = *the_files;
[1671]451     for (last = 0, i = 1; i < *number; i++) {
[1684]452          if (! strcmp(files[last].user_name, files[i].user_name)) {
[1671]453               int better;
[1680]454
[1684]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               }
[1671]462               else {
[1684]463                    free (files[last].real_name);
464                    free (files[last].user_name);
465                    files[last].real_name = (char *) NULL;
[1671]466                    last = i;
467               }
468          }
[1684]469          else
[1671]470               last = i;
471     }
[1680]472     
[1671]473     for (offset = 0, i = 0; i + offset < *number; i++) {
[1684]474          if (! files[i].real_name)
[1671]475               offset++;
476          if (i + offset < *number)
477               files[i] = files[i + offset];
478     }
479     *number -= offset;
[2171]480     files = (listrec *) realloc((char *) files,
481                                 (unsigned) (sizeof(listrec) * *number));
[5050]482     if ((! files) && *number)
483     {
[2171]484          set_error(errno);
485          error("realloc");
486          return errno;
[1671]487     }
[2171]488
489     *the_files = files;
490     return 0;
[1671]491}
492
493
494
495
[24908]496static int choose_better(char *str1, char *str2)
[1671]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     
[24908]520static int unlink_completely(char *filename)
[1674]521{
522     char buf[MAXPATHLEN];
523     struct stat stat_buf;
524     DIR *dirp;
[5138]525     struct dirent *dp;
[2171]526     int retval;
[1674]527     int status = 0;
528     
[2171]529     if (lstat(filename, &stat_buf)) {
530          set_error(errno);
531          error(filename);
532          return error_code;
533     }
[1674]534
[1712]535     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
[2482]536          dirp = Opendir(filename);
[2171]537          if (! dirp) {
538               set_error(errno);
539               error(filename);
540               return error_code;
541          }
542               
[1674]543          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
544               if (is_dotfile(dp->d_name))
545                    continue;
[2171]546               (void) strcpy(buf, append(filename, dp->d_name));
[24908]547               if ((retval = unlink_completely(buf))) {
[2171]548                    error(buf);
549                    status = retval;
[1674]550               }
551          }
552          closedir(dirp);
[24908]553          if ((retval = rmdir(filename))) {
[2171]554               set_error(errno);
555               error(filename);
556               return error_code;
557          }
[1674]558     }
[24908]559     else if ((retval = unlink(filename))) {
[2171]560          set_error(errno);
561          error(filename);
562          return error_code;
563     }
564
565     return status;
[1674]566}
567
568
569
570
[2221]571int get_the_files(name, num_found, found)
572char *name;
[1705]573int *num_found;
[2171]574char ***found;
[1705]575{
[2171]576     int retval;
[2221]577     int options;
[1705]578     
[2221]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) {
[2171]587          error("find_matches");
588          return retval;
589     }
[2221]590
[2171]591     return 0;
[1705]592}
Note: See TracBrowser for help on using the repository browser.