]> arthur.barton.de Git - netatalk.git/blob - bin/ad/ad_rm.c
Convert ad file suite and CNID daemons to use new config code
[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/bstrlib.h>
36 #include <atalk/bstradd.h>
37 #include <atalk/queue.h>
38
39 #include "ad.h"
40
41 #define STRIP_TRAILING_SLASH(p) {                                   \
42         while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')  \
43             *--(p).p_end = 0;                                       \
44     }
45
46 static afpvol_t volume;
47
48 static cnid_t did, pdid;
49 static int Rflag;
50 static volatile sig_atomic_t alarmed;
51 static int badrm, rval;
52
53 static char           *netatalk_dirs[] = {
54     ".AppleDB",
55     ".AppleDesktop",
56     NULL
57 };
58
59 /* Forward declarations */
60 static int rm(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
61
62 /*
63   Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
64   Returns pointer to name or NULL.
65 */
66 static const char *check_netatalk_dirs(const char *name)
67 {
68     int c;
69
70     for (c=0; netatalk_dirs[c]; c++) {
71         if ((strcmp(name, netatalk_dirs[c])) == 0)
72             return netatalk_dirs[c];
73     }
74     return NULL;
75 }
76
77 static void upfunc(void)
78 {
79     did = pdid;
80 }
81
82 /*
83   SIGNAL handling:
84   catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
85 */
86
87 static void sig_handler(int signo)
88 {
89     alarmed = 1;
90     return;
91 }
92
93 static void set_signal(void)
94 {
95     struct sigaction sv;
96
97     sv.sa_handler = sig_handler;
98     sv.sa_flags = SA_RESTART;
99     sigemptyset(&sv.sa_mask);
100     if (sigaction(SIGTERM, &sv, NULL) < 0)
101         ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
102
103     if (sigaction(SIGINT, &sv, NULL) < 0)
104         ERROR("error in sigaction(SIGINT): %s", strerror(errno));
105
106     memset(&sv, 0, sizeof(struct sigaction));
107     sv.sa_handler = SIG_IGN;
108     sigemptyset(&sv.sa_mask);
109
110     if (sigaction(SIGABRT, &sv, NULL) < 0)
111         ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
112
113     if (sigaction(SIGHUP, &sv, NULL) < 0)
114         ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
115
116     if (sigaction(SIGQUIT, &sv, NULL) < 0)
117         ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
118 }
119
120 static void usage_rm(void)
121 {
122     printf(
123         "Usage: ad rm [-vR] <file|dir> [<file|dir> ...]\n\n"
124         "The rm utility attempts to remove the non-directory type files specified\n"
125         "on the command line.\n"
126         "If the files and directories reside on an AFP volume, the corresponding\n"
127         "CNIDs are deleted from the volumes database.\n\n"
128         "The options are as follows:\n\n"
129         "   -R   Attempt to remove the file hierarchy rooted in each file argument.\n"
130         "   -v   Be verbose when deleting files, showing them as they are removed.\n"
131         );
132     exit(EXIT_FAILURE);
133 }
134
135 int ad_rm(int argc, char *argv[])
136 {
137     int ch;
138
139     pdid = htonl(1);
140     did = htonl(2);
141
142     while ((ch = getopt(argc, argv, "vR")) != -1)
143         switch (ch) {
144         case 'R':
145             Rflag = 1;
146             break;
147         case 'v':
148             vflag = 1;
149             break;
150         default:
151             usage_rm();
152             break;
153         }
154     argc -= optind;
155     argv += optind;
156
157     if (argc < 1)
158         usage_rm();
159
160     set_signal();
161     cnid_init();
162
163     /* Set end of argument list */
164     argv[argc] = NULL;
165
166     for (int i = 0; argv[i] != NULL; i++) {
167         /* Load .volinfo file for source */
168         openvol(argv[i], &volume);
169
170         if (nftw(argv[i], rm, upfunc, 20, FTW_DEPTH | FTW_PHYS) == -1) {
171             if (alarmed) {
172                 SLOG("...break");
173             } else {
174                 SLOG("Error: %s", argv[i]);
175             }
176             closevol(&volume);
177         }
178     }
179     return rval;
180 }
181
182 static int rm(const char *path,
183               const struct stat *statp,
184               int tflag,
185               struct FTW *ftw)
186 {
187     cnid_t cnid;
188
189     if (alarmed)
190         return -1;
191
192     const char *dir = strrchr(path, '/');
193     if (dir == NULL)
194         dir = path;
195     else
196         dir++;
197     if (check_netatalk_dirs(dir) != NULL)
198         return FTW_SKIP_SUBTREE;
199
200     switch (statp->st_mode & S_IFMT) {
201
202     case S_IFLNK:
203         if (volume.vol->v_path) {
204             if ((volume.vol->v_adouble == AD_VERSION2)
205                 && (strstr(path, ".AppleDouble") != NULL)) {
206                 /* symlink inside adouble dir */
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             if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
215                 SLOG("Error resolving CNID for %s", path);
216                 return -1;
217             }
218             if (cnid_delete(volume.vol->v_cdb, cnid) != 0) {
219                 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
220                 return -1;
221             }
222         }
223
224         if (unlink(path) != 0) {
225             badrm = rval = 1;
226             break;
227         }
228
229         break;
230
231     case S_IFDIR:
232         if (!Rflag) {
233             SLOG("%s is a directory", path);
234             return FTW_SKIP_SUBTREE;
235         }
236
237         if (volume.vol->v_path) {
238             if ((volume.vol->v_adouble == AD_VERSION2)
239                 && (strstr(path, ".AppleDouble") != NULL)) {
240                 /* should be adouble dir itself */
241                 if (rmdir(path) != 0) {
242                     SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
243                     badrm = rval = 1;
244                     return -1;
245                 }
246                 break;
247             }
248
249             /* Get CNID of Parent and add new childir to CNID database */
250             if ((did = cnid_for_path(&volume, path, &pdid)) == CNID_INVALID) {
251                 SLOG("Error resolving CNID for %s", path);
252                 return -1;
253             }
254             if (cnid_delete(volume.vol->v_cdb, did) != 0) {
255                 SLOG("Error removing CNID %u for %s", ntohl(did), path);
256                 return -1;
257             }
258         }
259
260         if (rmdir(path) != 0) {
261             SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
262             badrm = rval = 1;
263             return -1;
264         }
265
266         break;
267
268     case S_IFBLK:
269     case S_IFCHR:
270         SLOG("%s is a device file.", path);
271         badrm = rval = 1;
272         break;
273
274     case S_IFSOCK:
275         SLOG("%s is a socket.", path);
276         badrm = rval = 1;
277         break;
278
279     case S_IFIFO:
280         SLOG("%s is a FIFO.", path);
281         badrm = rval = 1;
282         break;
283
284     default:
285         if (volume.vol->v_path) {
286             if ((volume.vol->v_adouble == AD_VERSION2)
287                 && (strstr(path, ".AppleDouble") != NULL)) {
288                 /* file in adouble dir */
289                 if (unlink(path) != 0)
290                     badrm = rval = 1;
291                 break;
292             }
293
294             /* Get CNID of Parent and add new childir to CNID database */
295             pdid = did;
296             if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
297                 SLOG("Error resolving CNID for %s", path);
298                 return -1;
299             }
300             if (cnid_delete(volume.vol->v_cdb, cnid) != 0) {
301                 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
302                 return -1;
303             }
304
305             /* Ignore errors, because with -R adouble stuff is always alread gone */
306             volume.vol->vfs->vfs_deletefile(volume.vol, -1, path);
307         }
308
309         if (unlink(path) != 0) {
310             badrm = rval = 1;
311             break;
312         }
313
314         break;
315     }
316
317     if (vflag && !badrm)
318         (void)printf("%s\n", path);
319
320     return 0;
321 }