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