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/volinfo.h>
36 #include <atalk/bstrlib.h>
37 #include <atalk/bstradd.h>
38 #include <atalk/queue.h>
42 #define STRIP_TRAILING_SLASH(p) { \
43 while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
47 static afpvol_t volume;
49 static cnid_t did, pdid;
51 static volatile sig_atomic_t alarmed;
52 static int badrm, rval;
54 static char *netatalk_dirs[] = {
60 /* Forward declarations */
61 static int rm(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
64 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
65 Returns pointer to name or NULL.
67 static const char *check_netatalk_dirs(const char *name)
71 for (c=0; netatalk_dirs[c]; c++) {
72 if ((strcmp(name, netatalk_dirs[c])) == 0)
73 return netatalk_dirs[c];
78 static void upfunc(void)
85 catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
88 static void sig_handler(int signo)
94 static void set_signal(void)
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));
104 if (sigaction(SIGINT, &sv, NULL) < 0)
105 ERROR("error in sigaction(SIGINT): %s", strerror(errno));
107 memset(&sv, 0, sizeof(struct sigaction));
108 sv.sa_handler = SIG_IGN;
109 sigemptyset(&sv.sa_mask);
111 if (sigaction(SIGABRT, &sv, NULL) < 0)
112 ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
114 if (sigaction(SIGHUP, &sv, NULL) < 0)
115 ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
117 if (sigaction(SIGQUIT, &sv, NULL) < 0)
118 ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
121 static void usage_rm(void)
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"
136 int ad_rm(int argc, char *argv[])
143 while ((ch = getopt(argc, argv, "vR")) != -1)
164 /* Set end of argument list */
167 for (int i = 0; argv[i] != NULL; i++) {
168 /* Load .volinfo file for source */
169 openvol(argv[i], &volume);
171 if (nftw(argv[i], rm, upfunc, 20, FTW_DEPTH | FTW_PHYS) == -1) {
175 SLOG("Error: %s", argv[i]);
183 static int rm(const char *path,
184 const struct stat *statp,
193 const char *dir = strrchr(path, '/');
198 if (check_netatalk_dirs(dir) != NULL)
199 return FTW_SKIP_SUBTREE;
201 switch (statp->st_mode & S_IFMT) {
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)
213 /* Get CNID of Parent and add new childir to CNID database */
215 if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
216 SLOG("Error resolving CNID for %s", path);
219 if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
220 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
225 if (unlink(path) != 0) {
234 SLOG("%s is a directory", path);
235 return FTW_SKIP_SUBTREE;
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));
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);
255 if (cnid_delete(volume.volume.v_cdb, did) != 0) {
256 SLOG("Error removing CNID %u for %s", ntohl(did), path);
261 if (rmdir(path) != 0) {
262 SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
271 SLOG("%s is a device file.", path);
276 SLOG("%s is a socket.", path);
281 SLOG("%s is a FIFO.", path);
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)
295 /* Get CNID of Parent and add new childir to CNID database */
297 if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
298 SLOG("Error resolving CNID for %s", path);
301 if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
302 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
306 /* Ignore errors, because with -R adouble stuff is always alread gone */
307 volume.volume.vfs->vfs_deletefile(&volume.volume, -1, path);
310 if (unlink(path) != 0) {
319 (void)printf("%s\n", path);