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

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