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 && STRCMP(path, == , ".AppleDouble")) {
207 if (unlink(path) != 0)
212 /* Get CNID of Parent and add new childir to CNID database */
214 cnid = cnid_for_path(&volume.volinfo, &volume.volume, path, &did);
215 if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
216 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
221 if (unlink(path) != 0) {
230 SLOG("%s is a directory", path);
231 return FTW_SKIP_SUBTREE;
234 if (volume.volinfo.v_path) {
235 if ((volume.volinfo.v_adouble == AD_VERSION2)
236 && STRCMP(path, == , ".AppleDouble")) {
237 if (rmdir(path) != 0) {
238 SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
245 /* Get CNID of Parent and add new childir to CNID database */
246 did = cnid_for_path(&volume.volinfo, &volume.volume, path, &pdid);
247 if (cnid_delete(volume.volume.v_cdb, did) != 0) {
248 SLOG("Error removing CNID %u for %s", ntohl(did), path);
253 if (rmdir(path) != 0) {
254 SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
263 SLOG("%s is a device file.", path);
268 SLOG("%s is a socket.", path);
273 SLOG("%s is a FIFO.", path);
278 if (volume.volinfo.v_path) {
279 if ((volume.volinfo.v_adouble == AD_VERSION2)
280 && STRCMP(path, == , ".AppleDouble")) {
281 if (unlink(path) != 0)
286 /* Get CNID of Parent and add new childir to CNID database */
288 cnid = cnid_for_path(&volume.volinfo, &volume.volume, path, &did);
289 if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
290 SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
295 if (volume.volume.vfs->vfs_deletefile(&volume.volume, -1, path) != 0) {
296 SLOG("Error removing adouble file for: %s", path);
303 if (unlink(path) != 0) {
312 (void)printf("%s\n", path);