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 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);
222 if (unlink(path) != 0) {
231 SLOG("%s is a directory", path);
232 return FTW_SKIP_SUBTREE;
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));
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);
255 if (rmdir(path) != 0) {
256 SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
265 SLOG("%s is a device file.", path);
270 SLOG("%s is a socket.", path);
275 SLOG("%s is a FIFO.", path);
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)
289 /* Get CNID of Parent and add new childir to CNID database */
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);
298 if (volume.volume.vfs->vfs_deletefile(&volume.volume, -1, path) != 0) {
299 SLOG("Error removing adouble file for: %s", path);
306 if (unlink(path) != 0) {
315 (void)printf("%s\n", path);