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

Revision 10977, 14.1 KB checked in by danw, 27 years ago (diff)
assume POSIX. (from svalente)
Line 
1/*
2 * $Source: /afs/dev.mit.edu/source/repository/athena/bin/delete/delete.c,v $
3 * $Author: danw $
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.
10 * For copying and distribution information, see the file "mit-copying.h."
11 */
12
13#if (!defined(lint) && !defined(SABER))
14     static char rcsid_delete_c[] = "$Header: /afs/dev.mit.edu/source/repository/athena/bin/delete/delete.c,v 1.31 1997-12-31 22:35:58 danw Exp $";
15#endif
16
17#include <sys/types.h>
18#ifdef AFS_MOUNTPOINTS
19#include <sys/time.h>
20#endif
21#include <stdio.h>
22#include <dirent.h>
23#include <string.h>
24#include <unistd.h>
25
26#include <sys/param.h>
27#include <errno.h>
28#include "errors.h"
29#include "delete_errs.h"
30#include "util.h"
31#include "delete.h"
32#include "mit-copying.h"
33
34
35
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?
48 *    yes -- is it a dotfile?
49 *           yes -- abort
50 *           no -- continue
51 *        -- is the filesonly option set?
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;
73int emulate_rm, linked_to_rm, linked_to_rmdir;
74#ifdef AFS_MOUNTPOINTS
75struct timeval tvp[2];
76#endif
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
88     initialize_del_error_table();
89
90#ifdef AFS_MOUNTPOINTS
91     gettimeofday(&tvp[0], (struct timezone *)0);
92     tvp[1] = tvp[0];
93#endif
94
95     force = interactive = recursive = noop = verbose = filesonly =
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     
103     while ((arg = getopt(argc, argv, "efirnvFD")) != -1) {
104          switch (arg) {
105          case 'r':
106               recursive++;
107               if (directoriesonly) {
108                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
109                            whoami);
110                    usage();
111                    exit(1);
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;
126          case 'e':
127               emulate_rm++;
128               break;
129          case 'F':
130               filesonly++;
131               if (directoriesonly) {
132                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
133                            whoami);
134                    usage();
135                    exit(1);
136               }
137               break;
138          case 'D':
139               directoriesonly++;
140               if (recursive) {
141                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
142                            whoami);
143                    usage();
144                    exit(1);
145               }
146               if (filesonly) {
147                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
148                            whoami);
149                    usage();
150                    exit(1);
151               }
152               break;
153          default:
154               usage();
155               exit(1);
156          }
157     }
158     report_errors = ! (force || emulate_rm);
159     
160     if (optind == argc) {
161          if (! force) {
162               fprintf(stderr, "%s: no files specified.\n", whoami);
163               usage();
164          }
165          exit(force ? 0 : 1);
166     }
167     while (optind < argc) {
168          if (delete(argv[optind], 0))
169               error(argv[optind]);
170          optind++;
171     }
172     exit(((! force) && error_occurred) ? 1 : 0);
173}
174
175
176
177usage()
178{
179     printf("Usage: %s [ options ] filename ...\n", whoami);
180     printf("Options are:\n");
181     if (! linked_to_rmdir)
182          printf("     -r     recursive\n");
183     printf("     -i     interactive\n");
184     printf("     -f     force\n");
185     printf("     -n     noop\n");
186     printf("     -v     verbose\n");
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     }
192     printf("     --     end options and start filenames\n");
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     }
197}
198
199
200
201
202delete(filename, recursed)
203char *filename;
204int recursed;
205{
206     struct stat stat_buf;
207     int retval;
208     
209     /* can the file be lstat'd? */
210     if (lstat(filename, &stat_buf) == -1) {
211          set_error(errno);
212          if (emulate_rm && (! force))
213               fprintf(stderr, "%s: %s nonexistent\n", whoami, filename);
214          error(filename);
215          return error_code;
216     }
217
218     /* is the file a directory? */
219     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
220          /* is the file a dot file? */
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;
227          }
228
229          /* is the filesonly option set? */
230          if (filesonly) {
231               /* is the recursive option specified? */
232               if (recursive) {
233                    if (retval = recursive_delete(filename, stat_buf,
234                                                  recursed)) {
235                         error(filename);
236                         return retval;
237                    }
238               }
239               else {
240                    if (emulate_rm && (! force))
241                         fprintf(stderr, "%s: %s directory\n", whoami,
242                                 filename);
243                    set_error(DELETE_CANT_DEL_DIR);
244                    error(filename);
245                    return error_code;
246               }
247          }
248          else {
249               /* is the directory empty? */
250               if ((retval = empty_directory(filename)) < 0) {
251                    error(filename);
252                    if (! emulate_rm)
253                         return error_code;
254               }
255
256               if (retval > 0) {
257                    /* remove it */
258                    if (retval = do_move(filename, stat_buf, 0)) {
259                         error(filename);
260                         return error_code;
261                    }
262               }
263               else {
264                    /* is the directoriesonly option set? */
265                    if (directoriesonly) {
266                         if (emulate_rm && (! force))
267                              fprintf(stderr, "%s: %s: Directory not empty\n",
268                                      whoami, filename);
269                         set_error(DELETE_DIR_NOT_EMPTY);
270                         error(filename);
271                         return error_code;
272                    }
273                    else {
274                         /* is the recursive option specified? */
275                         if (recursive) {
276                              if (retval = recursive_delete(filename, stat_buf,
277                                                            recursed)) {
278                                   error(filename);
279                                   return error_code;
280                              }
281                         }
282                         else {
283                              if (emulate_rm && (! force))
284                                   fprintf(stderr, "%s: %s not empty\n",
285                                           whoami, filename);
286                              set_error(DELETE_DIR_NOT_EMPTY);
287                              error(filename);
288                              return error_code;
289                         }
290                    }
291               }
292          }
293     }
294     else {
295          /* is the directoriesonly option set? */
296          if (directoriesonly) {
297               if (emulate_rm && (! force))
298                    fprintf(stderr, "%s: %s: Not a directory\n", whoami,
299                            filename);
300               set_error(DELETE_CANT_DEL_FILE);
301               error(filename);
302               return error_code;
303          }
304          else {
305               if (retval = do_move(filename, stat_buf, 0)) {
306                    error(filename);
307                    return error_code;
308               }
309          }
310     }
311     return 0;
312}
313         
314
315                 
316                         
317               
318empty_directory(filename)
319char *filename;
320{
321     DIR *dirp;
322     struct dirent *dp;
323
324     dirp = Opendir(filename);
325     if (! dirp) {
326          set_error(errno);
327          error(filename);
328          return -1;
329     }
330     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
331          if (is_dotfile(dp->d_name))
332               continue;
333          if (is_deleted(dp->d_name))
334               continue;
335          else {
336               closedir(dirp);
337               return 0;
338          }
339     }
340     closedir(dirp);
341     return 1;
342}
343
344
345
346
347recursive_delete(filename, stat_buf, recursed)
348char *filename;
349struct stat stat_buf;
350int recursed;
351{
352     DIR *dirp;
353     struct dirent *dp;
354     int status = 0;
355     char newfile[MAXPATHLEN];
356     int retval = 0;
357     
358     if (interactive && recursed) {
359          printf("%s: remove directory %s? ", whoami, filename);
360          if (! yes()) {
361               set_status(DELETE_NOT_DELETED);
362               return error_code;
363          }
364     }
365     dirp = Opendir(filename);
366     if (! dirp) {
367          if (emulate_rm && (! force))
368               fprintf(stderr, "%s: %s not changed\n", whoami, filename);
369          set_error(errno);
370          error(filename);
371          return error_code;
372     }
373     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
374          if (is_dotfile(dp->d_name))
375               continue;
376          if (is_deleted(dp->d_name))
377               continue;
378          else {
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               }
390          }
391     }
392     closedir(dirp);
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;
405}
406
407                                         
408
409
410
411
412do_move(filename, stat_buf, subs_not_deleted)
413char *filename;
414struct stat stat_buf;
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.         */
423{
424     char *last;
425     char buf[MAXPATHLEN];
426     char name[MAXNAMLEN];
427     struct stat deleted_buf;
428
429     (void) strncpy(buf, filename, MAXPATHLEN);
430     last = lastpart(buf);
431     if (strlen(last) > MAXNAMLEN) {
432          if (emulate_rm && (! force))
433               fprintf(stderr, "%s: %s: filename too long\n", whoami,
434                       filename);
435          set_error(ENAMETOOLONG);
436          error(filename);
437          return error_code;
438     }
439     (void) strcpy(name, last);
440     if (strlen(buf) + 3 > MAXPATHLEN) {
441          if (emulate_rm && (! force))
442               fprintf(stderr, "%s: %s: pathname too long\n", whoami,
443                       filename);
444          set_error(ENAMETOOLONG);
445          error(filename);
446          return error_code;
447     }
448     *last = '\0';
449     (void) strcat(buf, ".#");
450     (void) strcat(buf, name);
451     if (interactive) {
452          printf("%s: remove %s? ", whoami, filename);
453          if (! yes()) {
454               set_status(DELETE_NOT_DELETED);
455               return error_code;
456          }
457     }
458     else if ((! force)
459#ifdef S_IFLNK
460              && ((stat_buf.st_mode & S_IFMT) != S_IFLNK)
461#endif
462              && access(filename, W_OK)) {
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          }
473     }
474     if (emulate_rm && subs_not_deleted) {
475          if (! force)
476               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
477          return 1;
478     }
479     if (noop) {
480          fprintf(stderr, "%s: %s would be removed\n", whoami, filename);
481          return 0;
482     }
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     }
489     if (rename(filename, buf)) {
490          if (emulate_rm && (! force))
491               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
492          set_error(errno);
493          error(filename);
494          return error_code;
495     }
496     else {
497          if (verbose)
498               fprintf(stderr, "%s: %s removed\n", whoami, filename);
499#ifdef AFS_MOUNTPOINTS
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
516          return 0;
517     }
518}
519
520
521
522unlink_completely(filename)
523char *filename;
524{
525     char buf[MAXPATHLEN];
526     struct stat stat_buf;
527     DIR *dirp;
528     struct dirent *dp;
529     int status = 0;
530     int retval;
531     
532     if (lstat(filename, &stat_buf)) {
533          set_error(errno);
534          error(filename);
535          return error_code;
536     }
537
538     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
539          dirp = Opendir(filename);
540          if (! dirp) {
541               set_error(errno);
542               error(filename);
543               return error_code;
544          }
545         
546          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
547               if (is_dotfile(dp->d_name))
548                    continue;
549               (void) strcpy(buf, append(filename, dp->d_name));
550               if (! *buf) {
551                    status = error_code;
552                    error(filename);
553                    continue;
554               }
555               retval = unlink_completely(buf);
556               if (retval) {
557                    status = retval;
558                    error(filename);
559               }
560          }
561          closedir(dirp);
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          }
572     }
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     }
583     return 0;
584}
Note: See TracBrowser for help on using the repository browser.