source: trunk/athena/bin/delete/delete.c @ 23659

Revision 23659, 14.5 KB checked in by broder, 15 years ago (diff)
In delete: * Apply patches from jik: - When recursively deleting a directory, read in the directory's entire contents before deleting anything so readdir doesn't get confused
Line 
1/*
2 * $Id: delete.c,v 1.33 1999-01-22 23:08:52 ghudson Exp $
3 *
4 * This program is a replacement for rm.  Instead of actually deleting
5 * files, it marks them for deletion by prefixing them with a ".#"
6 * prefix.
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_delete_c[] = "$Id: delete.c,v 1.33 1999-01-22 23:08:52 ghudson Exp $";
14#endif
15
16#include <sys/types.h>
17#ifdef HAVE_AFS
18#include <sys/time.h>
19#endif
20#include <stdio.h>
21#include <dirent.h>
22#include <string.h>
23#include <unistd.h>
24
25#include <sys/param.h>
26#include <errno.h>
27#include "errors.h"
28#include "delete_errs.h"
29#include "util.h"
30#include "delete.h"
31#include "mit-copying.h"
32
33
34
35/*
36 * ALGORITHM:
37 *
38 * 1. Parse command-line arguments and set flags.
39 * 2. Call the function delete() for each filename command-line argument.
40 *
41 * delete():
42 *
43 * 1. Can the file be lstat'd?
44 *    no -- abort
45 *    yes -- continue
46 * 2. Is the file a directory?
47 *    yes -- is it a dotfile?
48 *           yes -- abort
49 *           no -- continue
50 *        -- is the filesonly option set?
51 *           yes -- is the recursive option specified?
52 *                  yes -- continue
53 *                  no -- abort
54 *           no -- is the directory empty?
55 *                  yes -- remove it
56 *                  no -- is the directoriesonly option set?
57 *                        yes -- abort
58 *                        no -- continue
59 *                     -- is the recursive option specified?
60 *                        yes -- continue
61 *                        no -- abort
62 *    no -- is the directoriesonly option set?
63 *          yes -- abort
64 *          no -- continue
65 * 3. If the file is a file, remove it.
66 * 4. If the file is a directory, open it and pass each of its members
67 *    (excluding . files) to delete().
68 */
69
70
71int force, interactive, recursive, noop, verbose, filesonly, directoriesonly;
72int emulate_rm, linked_to_rm, linked_to_rmdir;
73#ifdef HAVE_AFS
74struct timeval tvp[2];
75#endif
76
77main(argc, argv)
78int argc;
79char *argv[];
80{
81     extern char *optarg;
82     extern int optind;
83     int arg;
84     
85     whoami = lastpart(argv[0]);
86
87#if defined(__APPLE__) && defined(__MACH__)
88     add_error_table(&et_del_error_table);
89#else
90     initialize_del_error_table();
91#endif
92
93#ifdef HAVE_AFS
94     gettimeofday(&tvp[0], (struct timezone *)0);
95     tvp[1] = tvp[0];
96#endif
97
98     force = interactive = recursive = noop = verbose = filesonly =
99          directoriesonly = emulate_rm = linked_to_rm = linked_to_rmdir = 0;
100
101     if (!strcmp(whoami, "rm"))
102          emulate_rm++, filesonly++, linked_to_rm++;
103     if (!strcmp(whoami, "rmdir") || !strcmp(whoami, "rd"))
104          emulate_rm++, directoriesonly++, linked_to_rmdir++;
105     
106     while ((arg = getopt(argc, argv, "efirnvFD")) != -1) {
107          switch (arg) {
108          case 'r':
109               recursive++;
110               if (directoriesonly) {
111                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
112                            whoami);
113                    usage();
114                    exit(1);
115               }
116               break;
117          case 'f':
118               force++;
119               break;
120          case 'i':
121               interactive++;
122               break;
123          case 'n':
124               noop++;
125               break;
126          case 'v':
127               verbose++;
128               break;
129          case 'e':
130               emulate_rm++;
131               break;
132          case 'F':
133               filesonly++;
134               if (directoriesonly) {
135                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
136                            whoami);
137                    usage();
138                    exit(1);
139               }
140               break;
141          case 'D':
142               directoriesonly++;
143               if (recursive) {
144                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
145                            whoami);
146                    usage();
147                    exit(1);
148               }
149               if (filesonly) {
150                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
151                            whoami);
152                    usage();
153                    exit(1);
154               }
155               break;
156          default:
157               usage();
158               exit(1);
159          }
160     }
161     report_errors = ! (force || emulate_rm);
162     
163     if (optind == argc) {
164          if (! force) {
165               fprintf(stderr, "%s: no files specified.\n", whoami);
166               usage();
167          }
168          exit(force ? 0 : 1);
169     }
170     while (optind < argc) {
171          if (delete(argv[optind], 0))
172               error(argv[optind]);
173          optind++;
174     }
175     exit(((! force) && error_occurred) ? 1 : 0);
176}
177
178
179
180usage()
181{
182     printf("Usage: %s [ options ] filename ...\n", whoami);
183     printf("Options are:\n");
184     if (! linked_to_rmdir)
185          printf("     -r     recursive\n");
186     printf("     -i     interactive\n");
187     printf("     -f     force\n");
188     printf("     -n     noop\n");
189     printf("     -v     verbose\n");
190     if (! (linked_to_rmdir || linked_to_rm)) {
191          printf("     -e     emulate rm/rmdir\n");
192          printf("     -F     files only\n");
193          printf("     -D     directories only\n");
194     }
195     printf("     --     end options and start filenames\n");
196     if (! (linked_to_rmdir || linked_to_rm)) {
197          printf("-r and -D are mutually exclusive\n");
198          printf("-F and -D are mutually exclusive\n");
199     }
200}
201
202
203
204
205delete(filename, recursed)
206char *filename;
207int recursed;
208{
209     struct stat stat_buf;
210     int retval;
211     
212     /* can the file be lstat'd? */
213     if (lstat(filename, &stat_buf) == -1) {
214          set_error(errno);
215          if (emulate_rm && (! force))
216               fprintf(stderr, "%s: %s nonexistent\n", whoami, filename);
217          error(filename);
218          return error_code;
219     }
220
221     /* is the file a directory? */
222     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
223          /* is the file a dot file? */
224          if (is_dotfile(lastpart(filename))) {
225               set_error(DELETE_IS_DOTFILE);
226               if (emulate_rm && (! force))
227                    fprintf(stderr, "%s: cannot remove `.' or `..'\n", whoami);
228               error(filename);
229               return error_code;
230          }
231
232          /* is the filesonly option set? */
233          if (filesonly) {
234               /* is the recursive option specified? */
235               if (recursive) {
236                    if (retval = recursive_delete(filename, stat_buf,
237                                                  recursed)) {
238                         error(filename);
239                         return retval;
240                    }
241               }
242               else {
243                    if (emulate_rm && (! force))
244                         fprintf(stderr, "%s: %s directory\n", whoami,
245                                 filename);
246                    set_error(DELETE_CANT_DEL_DIR);
247                    error(filename);
248                    return error_code;
249               }
250          }
251          else {
252               /* is the directory empty? */
253               if ((retval = empty_directory(filename)) < 0) {
254                    error(filename);
255                    if (! emulate_rm)
256                         return error_code;
257               }
258
259               if (retval > 0) {
260                    /* remove it */
261                    if (retval = do_move(filename, stat_buf, 0)) {
262                         error(filename);
263                         return error_code;
264                    }
265               }
266               else {
267                    /* is the directoriesonly option set? */
268                    if (directoriesonly) {
269                         if (emulate_rm && (! force))
270                              fprintf(stderr, "%s: %s: Directory not empty\n",
271                                      whoami, filename);
272                         set_error(DELETE_DIR_NOT_EMPTY);
273                         error(filename);
274                         return error_code;
275                    }
276                    else {
277                         /* is the recursive option specified? */
278                         if (recursive) {
279                              if (retval = recursive_delete(filename, stat_buf,
280                                                            recursed)) {
281                                   error(filename);
282                                   return error_code;
283                              }
284                         }
285                         else {
286                              if (emulate_rm && (! force))
287                                   fprintf(stderr, "%s: %s not empty\n",
288                                           whoami, filename);
289                              set_error(DELETE_DIR_NOT_EMPTY);
290                              error(filename);
291                              return error_code;
292                         }
293                    }
294               }
295          }
296     }
297     else {
298          /* is the directoriesonly option set? */
299          if (directoriesonly) {
300               if (emulate_rm && (! force))
301                    fprintf(stderr, "%s: %s: Not a directory\n", whoami,
302                            filename);
303               set_error(DELETE_CANT_DEL_FILE);
304               error(filename);
305               return error_code;
306          }
307          else {
308               if (retval = do_move(filename, stat_buf, 0)) {
309                    error(filename);
310                    return error_code;
311               }
312          }
313     }
314     return 0;
315}
316         
317
318                 
319                         
320               
321empty_directory(filename)
322char *filename;
323{
324     DIR *dirp;
325     struct dirent *dp;
326
327     dirp = Opendir(filename);
328     if (! dirp) {
329          set_error(errno);
330          error(filename);
331          return -1;
332     }
333     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
334          if (is_dotfile(dp->d_name))
335               continue;
336          if (is_deleted(dp->d_name))
337               continue;
338          else {
339               closedir(dirp);
340               return 0;
341          }
342     }
343     closedir(dirp);
344     return 1;
345}
346
347
348
349
350recursive_delete(filename, stat_buf, recursed)
351char *filename;
352struct stat stat_buf;
353int recursed;
354{
355     DIR *dirp;
356     struct dirent *dp;
357     int status = 0;
358     char newfile[MAXPATHLEN];
359     int retval = 0;
360     char **dir_contents = 0;
361     int num_dir_contents = 0, dir_contents_size = 0;
362     
363     if (interactive && recursed) {
364          printf("%s: remove directory %s? ", whoami, filename);
365          if (! yes()) {
366               set_status(DELETE_NOT_DELETED);
367               return error_code;
368          }
369     }
370     dirp = Opendir(filename);
371     if (! dirp) {
372          if (emulate_rm && (! force))
373               fprintf(stderr, "%s: %s not changed\n", whoami, filename);
374          set_error(errno);
375          error(filename);
376          return error_code;
377     }
378     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
379          if (is_dotfile(dp->d_name))
380               continue;
381          if (is_deleted(dp->d_name))
382               continue;
383          (void) strcpy(newfile, append(filename, dp->d_name));
384          if (! *newfile) {
385            error(filename);
386            status = error_code;
387            break;
388          }
389          if ((retval = add_str(&dir_contents, num_dir_contents,
390                                &dir_contents_size, newfile))) {
391            error(filename);
392            status = retval;
393            break;
394          }
395          num_dir_contents++;
396     }
397
398     if (!status && num_dir_contents) {
399       int i;
400       for (i = 0; i < num_dir_contents; i++) {
401         retval = delete(dir_contents[i], 1);
402         if (retval) {
403           error(dir_contents[i]);
404           status = retval;
405         }
406       }
407     }
408
409     free_list(dir_contents, num_dir_contents);
410     closedir(dirp);
411
412     if (status && (! emulate_rm)) {
413          set_warning(DELETE_DIR_NOT_EMPTY);
414          error(filename);
415     }
416     else
417          retval = do_move(filename, stat_buf, status);
418     
419     if (retval)
420          status = retval;
421
422     return status;
423}
424
425                                         
426
427
428
429
430do_move(filename, stat_buf, subs_not_deleted)
431char *filename;
432struct stat stat_buf;
433int subs_not_deleted; /* If the file in question is a directory, and  */
434                      /* there is something underneath it that hasn't */
435                      /* been removed, this will be set to true.      */
436                      /* The program asks if the user wants to delete */
437                      /* the directory, and if the user says yes,     */
438                      /* checks the value of subs_not_deleted.  If    */
439                      /* it's true, an error results.                 */
440                      /* This is used only when emulating rm.         */
441{
442     char *last;
443     char buf[MAXPATHLEN];
444     char name[MAXNAMLEN];
445     struct stat deleted_buf;
446
447     (void) strncpy(buf, filename, MAXPATHLEN);
448     last = lastpart(buf);
449     if (strlen(last) > MAXNAMLEN) {
450          if (emulate_rm && (! force))
451               fprintf(stderr, "%s: %s: filename too long\n", whoami,
452                       filename);
453          set_error(ENAMETOOLONG);
454          error(filename);
455          return error_code;
456     }
457     (void) strcpy(name, last);
458     if (strlen(buf) + 3 > MAXPATHLEN) {
459          if (emulate_rm && (! force))
460               fprintf(stderr, "%s: %s: pathname too long\n", whoami,
461                       filename);
462          set_error(ENAMETOOLONG);
463          error(filename);
464          return error_code;
465     }
466     *last = '\0';
467     (void) strcat(buf, ".#");
468     (void) strcat(buf, name);
469     if (interactive) {
470          printf("%s: remove %s? ", whoami, filename);
471          if (! yes()) {
472               set_status(DELETE_NOT_DELETED);
473               return error_code;
474          }
475     }
476     else if ((! force)
477#ifdef S_IFLNK
478              && ((stat_buf.st_mode & S_IFMT) != S_IFLNK)
479#endif
480              && access(filename, W_OK)) {
481          if (emulate_rm)
482               printf("%s: override protection %o for %s? ", whoami,
483                      stat_buf.st_mode & 0777, filename);
484          else
485               printf("%s: File %s not writeable.  Delete anyway? ", whoami,
486                      filename);
487          if (! yes()) {
488               set_status(DELETE_NOT_DELETED);
489               return error_code;
490          }
491     }
492     if (emulate_rm && subs_not_deleted) {
493          if (! force)
494               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
495          return 1;
496     }
497     if (noop) {
498          fprintf(stderr, "%s: %s would be removed\n", whoami, filename);
499          return 0;
500     }
501     if ((! lstat(buf, &deleted_buf)) && unlink_completely(buf)) {
502          if (emulate_rm && (! force))
503               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
504          error(filename);
505          return error_code;
506     }
507     if (rename(filename, buf)) {
508          if (emulate_rm && (! force))
509               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
510          set_error(errno);
511          error(filename);
512          return error_code;
513     }
514     else {
515          if (verbose)
516               fprintf(stderr, "%s: %s removed\n", whoami, filename);
517#ifdef HAVE_AFS
518          /*
519           * Normally, expunge uses the ctime to determine how long
520           * ago a file was deleted (since the ctime is normally
521           * updated when a file is renamed).  However, in AFS,
522           * renaming a file does not change the ctime, mtime OR
523           * atime, so we have to use utimes to force the change.
524           * This unfortunately causes the loss of the real mtime, but
525           * there's nothing we can do about that, if we want expunge
526           * to be able to do the right thing.
527           *
528           * Don't bother checking for errors, because we can't do
529           * anything about them anyway, and in any case, this isn't a
530           * *really* important operation.
531           */
532          utimes(buf, tvp);
533#endif
534          return 0;
535     }
536}
537
538
539
540unlink_completely(filename)
541char *filename;
542{
543     char buf[MAXPATHLEN];
544     struct stat stat_buf;
545     DIR *dirp;
546     struct dirent *dp;
547     int status = 0;
548     int retval;
549     
550     if (lstat(filename, &stat_buf)) {
551          set_error(errno);
552          error(filename);
553          return error_code;
554     }
555
556     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
557          dirp = Opendir(filename);
558          if (! dirp) {
559               set_error(errno);
560               error(filename);
561               return error_code;
562          }
563         
564          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
565               if (is_dotfile(dp->d_name))
566                    continue;
567               (void) strcpy(buf, append(filename, dp->d_name));
568               if (! *buf) {
569                    status = error_code;
570                    error(filename);
571                    continue;
572               }
573               retval = unlink_completely(buf);
574               if (retval) {
575                    status = retval;
576                    error(filename);
577               }
578          }
579          closedir(dirp);
580
581          if (status)
582               return status;
583
584          retval = rmdir(filename);
585          if (retval) {
586               set_error(errno);
587               error(filename);
588               return errno;
589          }
590     }
591     else {
592          retval = unlink(filename);
593          if (retval) {
594               set_error(errno);
595               error(filename);
596               return error_code;
597          }
598          else
599               return 0;
600     }
601     return 0;
602}
Note: See TracBrowser for help on using the repository browser.