]> arthur.barton.de Git - netatalk.git/blob - bin/ad/ad_rm.c
afp_ldap.conf is gone
[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                 && (strstr(path, ".AppleDouble") != NULL)) {
207                 /* symlink inside adouble dir */
208                 if (unlink(path) != 0)
209                     badrm = rval = 1;
210                 break;
211             }
212
213             /* Get CNID of Parent and add new childir to CNID database */
214             pdid = did;
215             if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
216                 SLOG("Error resolving CNID for %s", path);
217                 return -1;
218             }
219             if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
220                 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
221                 return -1;
222             }
223         }
224
225         if (unlink(path) != 0) {
226             badrm = rval = 1;
227             break;
228         }
229
230         break;
231
232     case S_IFDIR:
233         if (!Rflag) {
234             SLOG("%s is a directory", path);
235             return FTW_SKIP_SUBTREE;
236         }
237
238         if (volume.volinfo.v_path) {
239             if ((volume.volinfo.v_adouble == AD_VERSION2)
240                 && (strstr(path, ".AppleDouble") != NULL)) {
241                 /* should be adouble dir itself */
242                 if (rmdir(path) != 0) {
243                     SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
244                     badrm = rval = 1;
245                     return -1;
246                 }
247                 break;
248             }
249
250             /* Get CNID of Parent and add new childir to CNID database */
251             if ((did = cnid_for_path(&volume, path, &pdid)) == CNID_INVALID) {
252                 SLOG("Error resolving CNID for %s", path);
253                 return -1;
254             }
255             if (cnid_delete(volume.volume.v_cdb, did) != 0) {
256                 SLOG("Error removing CNID %u for %s", ntohl(did), path);
257                 return -1;
258             }
259         }
260
261         if (rmdir(path) != 0) {
262             SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
263             badrm = rval = 1;
264             return -1;
265         }
266
267         break;
268
269     case S_IFBLK:
270     case S_IFCHR:
271         SLOG("%s is a device file.", path);
272         badrm = rval = 1;
273         break;
274
275     case S_IFSOCK:
276         SLOG("%s is a socket.", path);
277         badrm = rval = 1;
278         break;
279
280     case S_IFIFO:
281         SLOG("%s is a FIFO.", path);
282         badrm = rval = 1;
283         break;
284
285     default:
286         if (volume.volinfo.v_path) {
287             if ((volume.volinfo.v_adouble == AD_VERSION2)
288                 && (strstr(path, ".AppleDouble") != NULL)) {
289                 /* file in adouble dir */
290                 if (unlink(path) != 0)
291                     badrm = rval = 1;
292                 break;
293             }
294
295             /* Get CNID of Parent and add new childir to CNID database */
296             pdid = did;
297             if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
298                 SLOG("Error resolving CNID for %s", path);
299                 return -1;
300             }
301             if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
302                 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
303                 return -1;
304             }
305
306             /* Ignore errors, because with -R adouble stuff is always alread gone */
307             volume.volume.vfs->vfs_deletefile(&volume.volume, -1, path);
308         }
309
310         if (unlink(path) != 0) {
311             badrm = rval = 1;
312             break;
313         }
314
315         break;
316     }
317
318     if (vflag && !badrm)
319         (void)printf("%s\n", path);
320
321     return 0;
322 }