]> arthur.barton.de Git - netatalk.git/blob - bin/ad/ad_rm.c
ad rm
[netatalk.git] / bin / ad / ad_rm.c
1 /*
2  * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include <atalk/ftw.h>
30 #include <atalk/adouble.h>
31 #include <atalk/vfs.h>
32 #include <atalk/util.h>
33 #include <atalk/unix.h>
34 #include <atalk/volume.h>
35 #include <atalk/volinfo.h>
36 #include <atalk/bstrlib.h>
37 #include <atalk/bstradd.h>
38 #include <atalk/queue.h>
39
40 #include "ad.h"
41
42 #define STRIP_TRAILING_SLASH(p) {                                   \
43         while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')  \
44             *--(p).p_end = 0;                                       \
45     }
46
47 static afpvol_t volume;
48
49 static cnid_t did, pdid;
50 static int Rflag;
51 static volatile sig_atomic_t alarmed;
52 static int badrm, rval;
53
54 static char           *netatalk_dirs[] = {
55     ".AppleDB",
56     ".AppleDesktop",
57     NULL
58 };
59
60 /* Forward declarations */
61 static int rm(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
62
63 /*
64   Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
65   Returns pointer to name or NULL.
66 */
67 static const char *check_netatalk_dirs(const char *name)
68 {
69     int c;
70
71     for (c=0; netatalk_dirs[c]; c++) {
72         if ((strcmp(name, netatalk_dirs[c])) == 0)
73             return netatalk_dirs[c];
74     }
75     return NULL;
76 }
77
78 static void upfunc(void)
79 {
80     did = pdid;
81 }
82
83 /*
84   SIGNAL handling:
85   catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
86 */
87
88 static void sig_handler(int signo)
89 {
90     alarmed = 1;
91     return;
92 }
93
94 static void set_signal(void)
95 {
96     struct sigaction sv;
97
98     sv.sa_handler = sig_handler;
99     sv.sa_flags = SA_RESTART;
100     sigemptyset(&sv.sa_mask);
101     if (sigaction(SIGTERM, &sv, NULL) < 0)
102         ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
103
104     if (sigaction(SIGINT, &sv, NULL) < 0)
105         ERROR("error in sigaction(SIGINT): %s", strerror(errno));
106
107     memset(&sv, 0, sizeof(struct sigaction));
108     sv.sa_handler = SIG_IGN;
109     sigemptyset(&sv.sa_mask);
110
111     if (sigaction(SIGABRT, &sv, NULL) < 0)
112         ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
113
114     if (sigaction(SIGHUP, &sv, NULL) < 0)
115         ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
116
117     if (sigaction(SIGQUIT, &sv, NULL) < 0)
118         ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
119 }
120
121 static void usage_rm(void)
122 {
123     printf(
124         "Usage: ad rm [-vR] <file|dir> [<file|dir> ...]\n\n"
125         "The rm utility attempts to remove the non-directory type files specified\n"
126         "on the command line.\n"
127         "If the files and directories reside on an AFP volume, the corresponding\n"
128         "CNIDs are deleted from the volumes database.\n\n"
129         "The options are as follows:\n\n"
130         "   -R   Attempt to remove the file hierarchy rooted in each file argument.\n"
131         "   -v   Be verbose when deleting files, showing them as they are removed.\n"
132         );
133     exit(EXIT_FAILURE);
134 }
135
136 int ad_rm(int argc, char *argv[])
137 {
138     int ch;
139
140     pdid = htonl(1);
141     did = htonl(2);
142
143     while ((ch = getopt(argc, argv, "vR")) != -1)
144         switch (ch) {
145         case 'R':
146             Rflag = 1;
147             break;
148         case 'v':
149             vflag = 1;
150             break;
151         default:
152             usage_rm();
153             break;
154         }
155     argc -= optind;
156     argv += optind;
157
158     if (argc < 1)
159         usage_rm();
160
161     set_signal();
162     cnid_init();
163
164     /* Set end of argument list */
165     argv[argc] = NULL;
166
167     for (int i = 0; argv[i] != NULL; i++) {
168         /* Load .volinfo file for source */
169         openvol(argv[i], &volume);
170
171         if (nftw(argv[i], rm, upfunc, 20, FTW_DEPTH | FTW_PHYS) == -1) {
172             if (alarmed) {
173                 SLOG("...break");
174             } else {
175                 SLOG("Error: %s", argv[i]);
176             }
177             closevol(&volume);
178         }
179     }
180     return rval;
181 }
182
183 static int rm(const char *path,
184               const struct stat *statp,
185               int tflag,
186               struct FTW *ftw)
187 {
188     cnid_t cnid;
189
190     if (alarmed)
191         return -1;
192
193     const char *dir = strrchr(path, '/');
194     if (dir == NULL)
195         dir = path;
196     else
197         dir++;
198     if (check_netatalk_dirs(dir) != NULL)
199         return FTW_SKIP_SUBTREE;
200
201     switch (statp->st_mode & S_IFMT) {
202
203     case S_IFLNK:
204         if (volume.volinfo.v_path) {
205             if ((volume.volinfo.v_adouble == AD_VERSION2)
206                 && STRCMP(path, == , ".AppleDouble")) {
207                 if (unlink(path) != 0)
208                     badrm = rval = 1;
209                 break;
210             }
211
212             /* Get CNID of Parent and add new childir to CNID database */
213             pdid = did;
214             cnid = cnid_for_path(&volume.volinfo, &volume.volume, path, &did);
215             if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
216                 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
217                 return -1;
218             }
219         }
220
221         if (unlink(path) != 0) {
222             badrm = rval = 1;
223             break;
224         }
225
226         break;
227
228     case S_IFDIR:
229         if (!Rflag) {
230             SLOG("%s is a directory", path);
231             return FTW_SKIP_SUBTREE;
232         }
233
234         if (volume.volinfo.v_path) {
235             if ((volume.volinfo.v_adouble == AD_VERSION2)
236                 && STRCMP(path, == , ".AppleDouble")) {
237                 if (rmdir(path) != 0) {
238                     SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
239                     badrm = rval = 1;
240                     return -1;
241                 }
242                 break;
243             }
244
245             /* Get CNID of Parent and add new childir to CNID database */
246             did = cnid_for_path(&volume.volinfo, &volume.volume, path, &pdid);
247             if (cnid_delete(volume.volume.v_cdb, did) != 0) {
248                 SLOG("Error removing CNID %u for %s", ntohl(did), path);
249                 return -1;
250             }
251         }
252
253         if (rmdir(path) != 0) {
254             SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
255             badrm = rval = 1;
256             return -1;
257         }
258
259         break;
260
261     case S_IFBLK:
262     case S_IFCHR:
263         SLOG("%s is a device file.", path);
264         badrm = rval = 1;
265         break;
266
267     case S_IFSOCK:
268         SLOG("%s is a socket.", path);
269         badrm = rval = 1;
270         break;
271
272     case S_IFIFO:
273         SLOG("%s is a FIFO.", path);
274         badrm = rval = 1;
275         break;
276
277     default:
278         if (volume.volinfo.v_path) {
279             if ((volume.volinfo.v_adouble == AD_VERSION2)
280                 && STRCMP(path, == , ".AppleDouble")) {
281                 if (unlink(path) != 0)
282                     badrm = rval = 1;
283                 break;
284             }
285
286             /* Get CNID of Parent and add new childir to CNID database */
287             pdid = did;
288             cnid = cnid_for_path(&volume.volinfo, &volume.volume, path, &did);
289             if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
290                 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
291                 return -1;
292             }
293
294             if (!Rflag) {
295                 if (volume.volume.vfs->vfs_deletefile(&volume.volume, -1, path) != 0) {
296                     SLOG("Error removing adouble file for: %s", path);
297                     badrm = rval = 1;
298                     break;
299                 }
300             }
301         }
302
303         if (unlink(path) != 0) {
304             badrm = rval = 1;
305             break;
306         }
307
308         break;
309     }
310
311     if (vflag && !badrm)
312         (void)printf("%s\n", path);
313
314     return 0;
315 }