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 */
check_netatalk_dirs(const char * name)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
upfunc(void)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
sig_handler(int signo)87 static void sig_handler(int signo)
88 {
89 alarmed = 1;
90 return;
91 }
92
set_signal(void)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
usage_rm(void)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
ad_rm(int argc,char * argv[],AFPObj * obj)135 int ad_rm(int argc, char *argv[], AFPObj *obj)
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(obj, 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
rm(const char * path,const struct stat * statp,int tflag,struct FTW * ftw)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.vol->v_cdb, volume.vol->v_path, 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.vol->v_cdb, volume.vol->v_path, 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.vol->v_cdb, volume.vol->v_path, 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 }
322