1*a824f5a1SJean-Baptiste Boric /* $NetBSD: remove.c,v 1.1.1.2 2009/08/06 16:55:29 joerg Exp $ */
2*a824f5a1SJean-Baptiste Boric
3*a824f5a1SJean-Baptiste Boric /*-
4*a824f5a1SJean-Baptiste Boric * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
5*a824f5a1SJean-Baptiste Boric * All rights reserved.
6*a824f5a1SJean-Baptiste Boric *
7*a824f5a1SJean-Baptiste Boric * Redistribution and use in source and binary forms, with or without
8*a824f5a1SJean-Baptiste Boric * modification, are permitted provided that the following conditions
9*a824f5a1SJean-Baptiste Boric * are met:
10*a824f5a1SJean-Baptiste Boric *
11*a824f5a1SJean-Baptiste Boric * 1. Redistributions of source code must retain the above copyright
12*a824f5a1SJean-Baptiste Boric * notice, this list of conditions and the following disclaimer.
13*a824f5a1SJean-Baptiste Boric * 2. Redistributions in binary form must reproduce the above copyright
14*a824f5a1SJean-Baptiste Boric * notice, this list of conditions and the following disclaimer in
15*a824f5a1SJean-Baptiste Boric * the documentation and/or other materials provided with the
16*a824f5a1SJean-Baptiste Boric * distribution.
17*a824f5a1SJean-Baptiste Boric *
18*a824f5a1SJean-Baptiste Boric * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19*a824f5a1SJean-Baptiste Boric * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20*a824f5a1SJean-Baptiste Boric * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21*a824f5a1SJean-Baptiste Boric * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22*a824f5a1SJean-Baptiste Boric * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23*a824f5a1SJean-Baptiste Boric * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24*a824f5a1SJean-Baptiste Boric * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25*a824f5a1SJean-Baptiste Boric * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26*a824f5a1SJean-Baptiste Boric * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27*a824f5a1SJean-Baptiste Boric * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28*a824f5a1SJean-Baptiste Boric * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29*a824f5a1SJean-Baptiste Boric * SUCH DAMAGE.
30*a824f5a1SJean-Baptiste Boric */
31*a824f5a1SJean-Baptiste Boric
32*a824f5a1SJean-Baptiste Boric #if HAVE_CONFIG_H
33*a824f5a1SJean-Baptiste Boric #include "config.h"
34*a824f5a1SJean-Baptiste Boric #endif
35*a824f5a1SJean-Baptiste Boric
36*a824f5a1SJean-Baptiste Boric #include <nbcompat.h>
37*a824f5a1SJean-Baptiste Boric
38*a824f5a1SJean-Baptiste Boric #if HAVE_SYS_CDEFS_H
39*a824f5a1SJean-Baptiste Boric #include <sys/cdefs.h>
40*a824f5a1SJean-Baptiste Boric #endif
41*a824f5a1SJean-Baptiste Boric
42*a824f5a1SJean-Baptiste Boric __RCSID("$NetBSD: remove.c,v 1.1.1.2 2009/08/06 16:55:29 joerg Exp $");
43*a824f5a1SJean-Baptiste Boric
44*a824f5a1SJean-Baptiste Boric #if HAVE_DIRENT_H
45*a824f5a1SJean-Baptiste Boric #include <dirent.h>
46*a824f5a1SJean-Baptiste Boric #endif
47*a824f5a1SJean-Baptiste Boric #if HAVE_ERR_H
48*a824f5a1SJean-Baptiste Boric #include <err.h>
49*a824f5a1SJean-Baptiste Boric #endif
50*a824f5a1SJean-Baptiste Boric #include <errno.h>
51*a824f5a1SJean-Baptiste Boric #if HAVE_FCNTL_H
52*a824f5a1SJean-Baptiste Boric #include <fcntl.h>
53*a824f5a1SJean-Baptiste Boric #endif
54*a824f5a1SJean-Baptiste Boric #include <limits.h>
55*a824f5a1SJean-Baptiste Boric #include <stdio.h>
56*a824f5a1SJean-Baptiste Boric #include <stdlib.h>
57*a824f5a1SJean-Baptiste Boric #include <string.h>
58*a824f5a1SJean-Baptiste Boric #include <unistd.h>
59*a824f5a1SJean-Baptiste Boric
60*a824f5a1SJean-Baptiste Boric #include "lib.h"
61*a824f5a1SJean-Baptiste Boric
62*a824f5a1SJean-Baptiste Boric static int
safe_fchdir(int cwd)63*a824f5a1SJean-Baptiste Boric safe_fchdir(int cwd)
64*a824f5a1SJean-Baptiste Boric {
65*a824f5a1SJean-Baptiste Boric int tmp_errno, rv;
66*a824f5a1SJean-Baptiste Boric
67*a824f5a1SJean-Baptiste Boric tmp_errno = errno;
68*a824f5a1SJean-Baptiste Boric rv = fchdir(cwd);
69*a824f5a1SJean-Baptiste Boric errno = tmp_errno;
70*a824f5a1SJean-Baptiste Boric
71*a824f5a1SJean-Baptiste Boric return rv;
72*a824f5a1SJean-Baptiste Boric }
73*a824f5a1SJean-Baptiste Boric
74*a824f5a1SJean-Baptiste Boric static int
long_remove(const char ** path_ptr,int missing_ok,int * did_chdir)75*a824f5a1SJean-Baptiste Boric long_remove(const char **path_ptr, int missing_ok, int *did_chdir)
76*a824f5a1SJean-Baptiste Boric {
77*a824f5a1SJean-Baptiste Boric char tmp_path[PATH_MAX + 1];
78*a824f5a1SJean-Baptiste Boric const char *path;
79*a824f5a1SJean-Baptiste Boric size_t i, len;
80*a824f5a1SJean-Baptiste Boric int rv;
81*a824f5a1SJean-Baptiste Boric
82*a824f5a1SJean-Baptiste Boric path = *path_ptr;
83*a824f5a1SJean-Baptiste Boric len = strlen(path);
84*a824f5a1SJean-Baptiste Boric *did_chdir = 0;
85*a824f5a1SJean-Baptiste Boric
86*a824f5a1SJean-Baptiste Boric while (len >= PATH_MAX) {
87*a824f5a1SJean-Baptiste Boric for (i = PATH_MAX - 1; i > 0; --i) {
88*a824f5a1SJean-Baptiste Boric if (path[i] == '/')
89*a824f5a1SJean-Baptiste Boric break;
90*a824f5a1SJean-Baptiste Boric }
91*a824f5a1SJean-Baptiste Boric if (i == 0) {
92*a824f5a1SJean-Baptiste Boric errno = ENAMETOOLONG;
93*a824f5a1SJean-Baptiste Boric return -1; /* Assumes PATH_MAX > NAME_MAX */
94*a824f5a1SJean-Baptiste Boric }
95*a824f5a1SJean-Baptiste Boric memcpy(tmp_path, path, i);
96*a824f5a1SJean-Baptiste Boric tmp_path[i] = '\0';
97*a824f5a1SJean-Baptiste Boric if (chdir(tmp_path))
98*a824f5a1SJean-Baptiste Boric return -1;
99*a824f5a1SJean-Baptiste Boric *did_chdir = 1;
100*a824f5a1SJean-Baptiste Boric path += i + 1;
101*a824f5a1SJean-Baptiste Boric len -= i + 1;
102*a824f5a1SJean-Baptiste Boric }
103*a824f5a1SJean-Baptiste Boric
104*a824f5a1SJean-Baptiste Boric if (remove(path) == 0 || (errno == ENOENT && missing_ok))
105*a824f5a1SJean-Baptiste Boric rv = 0;
106*a824f5a1SJean-Baptiste Boric else
107*a824f5a1SJean-Baptiste Boric rv = -1;
108*a824f5a1SJean-Baptiste Boric
109*a824f5a1SJean-Baptiste Boric *path_ptr = path;
110*a824f5a1SJean-Baptiste Boric
111*a824f5a1SJean-Baptiste Boric return rv;
112*a824f5a1SJean-Baptiste Boric }
113*a824f5a1SJean-Baptiste Boric
114*a824f5a1SJean-Baptiste Boric static int
recursive_remove_internal(const char * path,int missing_ok,int cwd)115*a824f5a1SJean-Baptiste Boric recursive_remove_internal(const char *path, int missing_ok, int cwd)
116*a824f5a1SJean-Baptiste Boric {
117*a824f5a1SJean-Baptiste Boric DIR *dir;
118*a824f5a1SJean-Baptiste Boric struct dirent *de;
119*a824f5a1SJean-Baptiste Boric const char *sub_path;
120*a824f5a1SJean-Baptiste Boric char *subdir;
121*a824f5a1SJean-Baptiste Boric int did_chdir, rv;
122*a824f5a1SJean-Baptiste Boric
123*a824f5a1SJean-Baptiste Boric /*
124*a824f5a1SJean-Baptiste Boric * If the argument is longer than PATH_MAX, long_remove
125*a824f5a1SJean-Baptiste Boric * will try to shorten it using chdir. So before returning,
126*a824f5a1SJean-Baptiste Boric * make sure to fchdir back to the original cwd.
127*a824f5a1SJean-Baptiste Boric */
128*a824f5a1SJean-Baptiste Boric sub_path = path;
129*a824f5a1SJean-Baptiste Boric if (long_remove(&sub_path, missing_ok, &did_chdir) == 0)
130*a824f5a1SJean-Baptiste Boric rv = 0;
131*a824f5a1SJean-Baptiste Boric else if (errno != ENOTEMPTY) /* Other errors are terminal. */
132*a824f5a1SJean-Baptiste Boric rv = -1;
133*a824f5a1SJean-Baptiste Boric else
134*a824f5a1SJean-Baptiste Boric rv = 1;
135*a824f5a1SJean-Baptiste Boric
136*a824f5a1SJean-Baptiste Boric if (rv != 1) {
137*a824f5a1SJean-Baptiste Boric if (did_chdir && safe_fchdir(cwd) == -1 && rv == 0)
138*a824f5a1SJean-Baptiste Boric rv = -1;
139*a824f5a1SJean-Baptiste Boric return rv;
140*a824f5a1SJean-Baptiste Boric }
141*a824f5a1SJean-Baptiste Boric
142*a824f5a1SJean-Baptiste Boric if ((dir = opendir(sub_path)) == NULL) {
143*a824f5a1SJean-Baptiste Boric if (errno == EMFILE)
144*a824f5a1SJean-Baptiste Boric warn("opendir failed");
145*a824f5a1SJean-Baptiste Boric return -1;
146*a824f5a1SJean-Baptiste Boric }
147*a824f5a1SJean-Baptiste Boric
148*a824f5a1SJean-Baptiste Boric if (did_chdir && fchdir(cwd) == -1)
149*a824f5a1SJean-Baptiste Boric return -1;
150*a824f5a1SJean-Baptiste Boric
151*a824f5a1SJean-Baptiste Boric rv = 0;
152*a824f5a1SJean-Baptiste Boric
153*a824f5a1SJean-Baptiste Boric while ((de = readdir(dir)) != NULL) {
154*a824f5a1SJean-Baptiste Boric if (strcmp(de->d_name, ".") == 0)
155*a824f5a1SJean-Baptiste Boric continue;
156*a824f5a1SJean-Baptiste Boric if (strcmp(de->d_name, "..") == 0)
157*a824f5a1SJean-Baptiste Boric continue;
158*a824f5a1SJean-Baptiste Boric subdir = xasprintf("%s/%s", path, de->d_name);
159*a824f5a1SJean-Baptiste Boric rv = recursive_remove_internal(subdir, 1, cwd);
160*a824f5a1SJean-Baptiste Boric free(subdir);
161*a824f5a1SJean-Baptiste Boric }
162*a824f5a1SJean-Baptiste Boric
163*a824f5a1SJean-Baptiste Boric closedir(dir);
164*a824f5a1SJean-Baptiste Boric
165*a824f5a1SJean-Baptiste Boric safe_fchdir(cwd);
166*a824f5a1SJean-Baptiste Boric
167*a824f5a1SJean-Baptiste Boric rv |= long_remove(&path, missing_ok, &did_chdir);
168*a824f5a1SJean-Baptiste Boric
169*a824f5a1SJean-Baptiste Boric if (did_chdir && safe_fchdir(cwd) == -1 && rv == 0)
170*a824f5a1SJean-Baptiste Boric rv = -1;
171*a824f5a1SJean-Baptiste Boric
172*a824f5a1SJean-Baptiste Boric return rv;
173*a824f5a1SJean-Baptiste Boric }
174*a824f5a1SJean-Baptiste Boric
175*a824f5a1SJean-Baptiste Boric int
recursive_remove(const char * path,int missing_ok)176*a824f5a1SJean-Baptiste Boric recursive_remove(const char *path, int missing_ok)
177*a824f5a1SJean-Baptiste Boric {
178*a824f5a1SJean-Baptiste Boric int orig_cwd, rv;
179*a824f5a1SJean-Baptiste Boric
180*a824f5a1SJean-Baptiste Boric /* First try the easy case of regular file or empty directory. */
181*a824f5a1SJean-Baptiste Boric if (remove(path) == 0 || (errno == ENOENT && missing_ok))
182*a824f5a1SJean-Baptiste Boric return 0;
183*a824f5a1SJean-Baptiste Boric
184*a824f5a1SJean-Baptiste Boric /*
185*a824f5a1SJean-Baptiste Boric * If the path is too long, long_remove will use chdir to shorten it,
186*a824f5a1SJean-Baptiste Boric * so remember the current directory first.
187*a824f5a1SJean-Baptiste Boric */
188*a824f5a1SJean-Baptiste Boric if ((orig_cwd = open(".", O_RDONLY)) == -1)
189*a824f5a1SJean-Baptiste Boric return -1;
190*a824f5a1SJean-Baptiste Boric
191*a824f5a1SJean-Baptiste Boric rv = recursive_remove_internal(path, missing_ok, orig_cwd);
192*a824f5a1SJean-Baptiste Boric
193*a824f5a1SJean-Baptiste Boric close(orig_cwd);
194*a824f5a1SJean-Baptiste Boric return rv;
195*a824f5a1SJean-Baptiste Boric }
196