2 * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
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.
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.
17 #endif /* HAVE_CONFIG_H */
19 #include <sys/types.h>
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>
41 #define STRIP_TRAILING_SLASH(p) { \
42 while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
46 static afpvol_t volume;
48 static cnid_t did, pdid;
50 static volatile sig_atomic_t alarmed;
51 static int badrm, rval;
53 static char *netatalk_dirs[] = {
59 /* Forward declarations */
60 static int rm(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
63 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
64 Returns pointer to name or NULL.
66 static const char *check_netatalk_dirs(const char *name)
70 for (c=0; netatalk_dirs[c]; c++) {
71 if ((strcmp(name, netatalk_dirs[c])) == 0)
72 return netatalk_dirs[c];
77 static void upfunc(void)
84 catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
87 static void sig_handler(int signo)
93 static void set_signal(void)
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));
103 if (sigaction(SIGINT, &sv, NULL) < 0)
104 ERROR("error in sigaction(SIGINT): %s", strerror(errno));
106 memset(&sv, 0, sizeof(struct sigaction));
107 sv.sa_handler = SIG_IGN;
108 sigemptyset(&sv.sa_mask);
110 if (sigaction(SIGABRT, &sv, NULL) < 0)
111 ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
113 if (sigaction(SIGHUP, &sv, NULL) < 0)
114 ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
116 if (sigaction(SIGQUIT, &sv, NULL) < 0)
117 ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
120 static void usage_rm(void)
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"
135 int ad_rm(int argc, char *argv[], AFPObj *obj)
142 while ((ch = getopt(argc, argv, "vR")) != -1)
163 /* Set end of argument list */
166 for (int i = 0; argv[i] != NULL; i++) {
167 /* Load .volinfo file for source */
168 openvol(obj, argv[i], &volume);
170 if (nftw(argv[i], rm, upfunc, 20, FTW_DEPTH | FTW_PHYS) == -1) {
174 SLOG("Error: %s", argv[i]);
182 static int rm(const char *path,
183 const struct stat *statp,
192 const char *dir = strrchr(path, '/');
197 if (check_netatalk_dirs(dir) != NULL)
198 return FTW_SKIP_SUBTREE;
200 switch (statp->st_mode & S_IFMT) {
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)
212 /* Get CNID of Parent and add new childir to CNID database */
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);
218 if (cnid_delete(volume.vol->v_cdb, cnid) != 0) {
219 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
224 if (unlink(path) != 0) {
233 SLOG("%s is a directory", path);
234 return FTW_SKIP_SUBTREE;
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));
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);
254 if (cnid_delete(volume.vol->v_cdb, did) != 0) {
255 SLOG("Error removing CNID %u for %s", ntohl(did), path);
260 if (rmdir(path) != 0) {
261 SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
270 SLOG("%s is a device file.", path);
275 SLOG("%s is a socket.", path);
280 SLOG("%s is a FIFO.", path);
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)
294 /* Get CNID of Parent and add new childir to CNID database */
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);
300 if (cnid_delete(volume.vol->v_cdb, cnid) != 0) {
301 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
305 /* Ignore errors, because with -R adouble stuff is always alread gone */
306 volume.vol->vfs->vfs_deletefile(volume.vol, -1, path);
309 if (unlink(path) != 0) {
318 (void)printf("%s\n", path);