xref: /titanic_50/usr/src/lib/libcmd/common/rm.c (revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968)
1*da2e3ebdSchin /***********************************************************************
2*da2e3ebdSchin *                                                                      *
3*da2e3ebdSchin *               This software is part of the ast package               *
4*da2e3ebdSchin *           Copyright (c) 1992-2007 AT&T Knowledge Ventures            *
5*da2e3ebdSchin *                      and is licensed under the                       *
6*da2e3ebdSchin *                  Common Public License, Version 1.0                  *
7*da2e3ebdSchin *                      by AT&T Knowledge Ventures                      *
8*da2e3ebdSchin *                                                                      *
9*da2e3ebdSchin *                A copy of the License is available at                 *
10*da2e3ebdSchin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*da2e3ebdSchin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*da2e3ebdSchin *                                                                      *
13*da2e3ebdSchin *              Information and Software Systems Research               *
14*da2e3ebdSchin *                            AT&T Research                             *
15*da2e3ebdSchin *                           Florham Park NJ                            *
16*da2e3ebdSchin *                                                                      *
17*da2e3ebdSchin *                 Glenn Fowler <gsf@research.att.com>                  *
18*da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
19*da2e3ebdSchin *                                                                      *
20*da2e3ebdSchin ***********************************************************************/
21*da2e3ebdSchin #pragma prototyped
22*da2e3ebdSchin /*
23*da2e3ebdSchin  * Glenn Fowler
24*da2e3ebdSchin  * AT&T Research
25*da2e3ebdSchin  *
26*da2e3ebdSchin  * rm [-fir] [file ...]
27*da2e3ebdSchin  */
28*da2e3ebdSchin 
29*da2e3ebdSchin static const char usage[] =
30*da2e3ebdSchin "[-?\n@(#)$Id: rm (AT&T Research) 2006-11-21 $\n]"
31*da2e3ebdSchin USAGE_LICENSE
32*da2e3ebdSchin "[+NAME?rm - remove files]"
33*da2e3ebdSchin "[+DESCRIPTION?\brm\b removes the named \afile\a arguments. By default it"
34*da2e3ebdSchin "	does not remove directories. If a file is unwritable, the"
35*da2e3ebdSchin "	standard input is a terminal, and the \b--force\b option is not"
36*da2e3ebdSchin "	given, \brm\b prompts the user for whether to remove the file."
37*da2e3ebdSchin "	An affirmative response (\by\b or \bY\b) removes the file, a quit"
38*da2e3ebdSchin "	response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
39*da2e3ebdSchin "	all other responses skip the current file.]"
40*da2e3ebdSchin 
41*da2e3ebdSchin "[c|F:clear|clobber?Clear the contents of each file before removing by"
42*da2e3ebdSchin "	writing a 0 filled buffer the same size as the file, executing"
43*da2e3ebdSchin "	\bfsync\b(2) and closing before attempting to remove. Implemented"
44*da2e3ebdSchin "	only on systems that support \bfsync\b(2).]"
45*da2e3ebdSchin "[d:directory?\bremove\b(3) (or \bunlink\b(2)) directories rather than"
46*da2e3ebdSchin "	\brmdir\b(2), and don't require that they be empty before removal."
47*da2e3ebdSchin "	The caller requires sufficient privilege, not to mention a strong"
48*da2e3ebdSchin "	constitution, to use this option. Even though the directory must"
49*da2e3ebdSchin "	not be empty, \brm\b still attempts to empty it before removal.]"
50*da2e3ebdSchin "[f:force?Ignore nonexistent files and never prompt the user.]"
51*da2e3ebdSchin "[i:interactive|prompt?Prompt whether to remove each file."
52*da2e3ebdSchin "	An affirmative response (\by\b or \bY\b) removes the file, a quit"
53*da2e3ebdSchin "	response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
54*da2e3ebdSchin "	all other responses skip the current file.]"
55*da2e3ebdSchin "[r|R:recursive?Remove the contents of directories recursively.]"
56*da2e3ebdSchin "[u:unconditional?If \b--recursive\b and \b--force\b are also enabled then"
57*da2e3ebdSchin "	the owner read, write and execute modes are enabled (if not already"
58*da2e3ebdSchin "	enabled) for each directory before attempting to remove directory"
59*da2e3ebdSchin "	contents.]"
60*da2e3ebdSchin "[v:verbose?Print the name of each file before removing it.]"
61*da2e3ebdSchin 
62*da2e3ebdSchin "\n"
63*da2e3ebdSchin "\nfile ...\n"
64*da2e3ebdSchin "\n"
65*da2e3ebdSchin 
66*da2e3ebdSchin "[+SEE ALSO?\bmv\b(1), \brmdir\b(2), \bunlink\b(2), \bremove\b(3)]"
67*da2e3ebdSchin ;
68*da2e3ebdSchin 
69*da2e3ebdSchin #include <cmd.h>
70*da2e3ebdSchin #include <ls.h>
71*da2e3ebdSchin #include <fts.h>
72*da2e3ebdSchin #include <fs3d.h>
73*da2e3ebdSchin 
74*da2e3ebdSchin #define RM_ENTRY	1
75*da2e3ebdSchin 
76*da2e3ebdSchin #define beenhere(f)	(((f)->fts_number>>1)==(f)->fts_statp->st_nlink)
77*da2e3ebdSchin #define isempty(f)	(!((f)->fts_number&RM_ENTRY))
78*da2e3ebdSchin #define nonempty(f)	((f)->fts_parent->fts_number|=RM_ENTRY)
79*da2e3ebdSchin #define pathchunk(n)	roundof(n,1024)
80*da2e3ebdSchin #define retry(f)	((f)->fts_number=((f)->fts_statp->st_nlink<<1))
81*da2e3ebdSchin 
82*da2e3ebdSchin typedef struct State_s			/* program state		*/
83*da2e3ebdSchin {
84*da2e3ebdSchin 	int		clobber;	/* clear out file data first	*/
85*da2e3ebdSchin 	int		directory;	/* remove(dir) not rmdir(dir)	*/
86*da2e3ebdSchin 	int		force;		/* force actions		*/
87*da2e3ebdSchin 	int		fs3d;		/* 3d enabled			*/
88*da2e3ebdSchin 	int		interactive;	/* prompt for approval		*/
89*da2e3ebdSchin 	int		recursive;	/* remove subtrees too		*/
90*da2e3ebdSchin 	int		terminal;	/* attached to terminal		*/
91*da2e3ebdSchin 	int		uid;		/* caller uid			*/
92*da2e3ebdSchin 	int		unconditional;	/* enable dir rwx on preorder	*/
93*da2e3ebdSchin 	int		verbose;	/* display each file		*/
94*da2e3ebdSchin #if _lib_fsync
95*da2e3ebdSchin 	char		buf[SF_BUFSIZE];/* clobber buffer		*/
96*da2e3ebdSchin #endif
97*da2e3ebdSchin } State_t;
98*da2e3ebdSchin 
99*da2e3ebdSchin /*
100*da2e3ebdSchin  * remove a single file
101*da2e3ebdSchin  */
102*da2e3ebdSchin 
103*da2e3ebdSchin static int
104*da2e3ebdSchin rm(State_t* state, register FTSENT* ent)
105*da2e3ebdSchin {
106*da2e3ebdSchin 	register char*	path;
107*da2e3ebdSchin 	register int	n;
108*da2e3ebdSchin 	int		v;
109*da2e3ebdSchin 	struct stat	st;
110*da2e3ebdSchin 
111*da2e3ebdSchin 	if (cmdquit())
112*da2e3ebdSchin 		return -1;
113*da2e3ebdSchin 	if (ent->fts_info == FTS_NS || ent->fts_info == FTS_ERR || ent->fts_info == FTS_SLNONE)
114*da2e3ebdSchin 	{
115*da2e3ebdSchin 		if (!state->force)
116*da2e3ebdSchin 			error(2, "%s: not found", ent->fts_path);
117*da2e3ebdSchin 	}
118*da2e3ebdSchin 	else if (state->fs3d && iview(ent->fts_statp))
119*da2e3ebdSchin 		fts_set(NiL, ent, FTS_SKIP);
120*da2e3ebdSchin 	else switch (ent->fts_info)
121*da2e3ebdSchin 	{
122*da2e3ebdSchin 	case FTS_DNR:
123*da2e3ebdSchin 	case FTS_DNX:
124*da2e3ebdSchin 		if (state->unconditional)
125*da2e3ebdSchin 		{
126*da2e3ebdSchin 			if (!chmod(ent->fts_name, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU))
127*da2e3ebdSchin 			{
128*da2e3ebdSchin 				fts_set(NiL, ent, FTS_AGAIN);
129*da2e3ebdSchin 				break;
130*da2e3ebdSchin 			}
131*da2e3ebdSchin 			error_info.errors++;
132*da2e3ebdSchin 		}
133*da2e3ebdSchin 		else if (!state->force)
134*da2e3ebdSchin 			error(2, "%s: cannot %s directory", ent->fts_path, (ent->fts_info & FTS_NR) ? "read" : "search");
135*da2e3ebdSchin 		else
136*da2e3ebdSchin 			error_info.errors++;
137*da2e3ebdSchin 		fts_set(NiL, ent, FTS_SKIP);
138*da2e3ebdSchin 		nonempty(ent);
139*da2e3ebdSchin 		break;
140*da2e3ebdSchin 	case FTS_D:
141*da2e3ebdSchin 	case FTS_DC:
142*da2e3ebdSchin 		path = ent->fts_name;
143*da2e3ebdSchin 		if (path[0] == '.' && (!path[1] || path[1] == '.' && !path[2]) && (ent->fts_level > 0 || path[1]))
144*da2e3ebdSchin 		{
145*da2e3ebdSchin 			fts_set(NiL, ent, FTS_SKIP);
146*da2e3ebdSchin 			if (!state->force)
147*da2e3ebdSchin 				error(2, "%s: cannot remove", ent->fts_path);
148*da2e3ebdSchin 			else
149*da2e3ebdSchin 				error_info.errors++;
150*da2e3ebdSchin 			break;
151*da2e3ebdSchin 		}
152*da2e3ebdSchin 		if (!state->recursive)
153*da2e3ebdSchin 		{
154*da2e3ebdSchin 			fts_set(NiL, ent, FTS_SKIP);
155*da2e3ebdSchin 			error(2, "%s: directory", ent->fts_path);
156*da2e3ebdSchin 			break;
157*da2e3ebdSchin 		}
158*da2e3ebdSchin 		if (!beenhere(ent))
159*da2e3ebdSchin 		{
160*da2e3ebdSchin 			if (state->unconditional && (ent->fts_statp->st_mode ^ S_IRWXU))
161*da2e3ebdSchin 				chmod(path, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU);
162*da2e3ebdSchin 			if (ent->fts_level > 0)
163*da2e3ebdSchin 			{
164*da2e3ebdSchin 				char*	s;
165*da2e3ebdSchin 
166*da2e3ebdSchin 				if (ent->fts_accpath == ent->fts_name || !(s = strrchr(ent->fts_accpath, '/')))
167*da2e3ebdSchin 					v = !stat(".", &st);
168*da2e3ebdSchin 				else
169*da2e3ebdSchin 				{
170*da2e3ebdSchin 					path = ent->fts_accpath;
171*da2e3ebdSchin 					*s = 0;
172*da2e3ebdSchin 					v = !stat(path, &st);
173*da2e3ebdSchin 					*s = '/';
174*da2e3ebdSchin 				}
175*da2e3ebdSchin 				if (v)
176*da2e3ebdSchin 					v = st.st_nlink <= 2 || st.st_ino == ent->fts_parent->fts_statp->st_ino && st.st_dev == ent->fts_parent->fts_statp->st_dev || strchr(astconf("PATH_ATTRIBUTES", path, NiL), 'l');
177*da2e3ebdSchin 			}
178*da2e3ebdSchin 			else
179*da2e3ebdSchin 				v = 1;
180*da2e3ebdSchin 			if (v)
181*da2e3ebdSchin 			{
182*da2e3ebdSchin 				if (state->interactive)
183*da2e3ebdSchin 				{
184*da2e3ebdSchin 					if ((v = astquery(-1, "remove directory %s? ", ent->fts_path)) < 0)
185*da2e3ebdSchin 						return -1;
186*da2e3ebdSchin 					if (v > 0)
187*da2e3ebdSchin 					{
188*da2e3ebdSchin 						fts_set(NiL, ent, FTS_SKIP);
189*da2e3ebdSchin 						nonempty(ent);
190*da2e3ebdSchin 					}
191*da2e3ebdSchin 				}
192*da2e3ebdSchin 				if (ent->fts_info == FTS_D)
193*da2e3ebdSchin 					break;
194*da2e3ebdSchin 			}
195*da2e3ebdSchin 			else
196*da2e3ebdSchin 			{
197*da2e3ebdSchin 				ent->fts_info = FTS_DC;
198*da2e3ebdSchin 				error(1, "%s: hard link to directory", ent->fts_path);
199*da2e3ebdSchin 			}
200*da2e3ebdSchin 		}
201*da2e3ebdSchin 		else if (ent->fts_info == FTS_D)
202*da2e3ebdSchin 			break;
203*da2e3ebdSchin 		/*FALLTHROUGH*/
204*da2e3ebdSchin 	case FTS_DP:
205*da2e3ebdSchin 		if (isempty(ent) || state->directory)
206*da2e3ebdSchin 		{
207*da2e3ebdSchin 			path = ent->fts_name;
208*da2e3ebdSchin 			if (path[0] != '.' || path[1])
209*da2e3ebdSchin 			{
210*da2e3ebdSchin 				path = ent->fts_accpath;
211*da2e3ebdSchin 				if (state->verbose)
212*da2e3ebdSchin 					sfputr(sfstdout, ent->fts_path, '\n');
213*da2e3ebdSchin 				if ((ent->fts_info == FTS_DC || state->directory) ? remove(path) : rmdir(path))
214*da2e3ebdSchin 					switch (errno)
215*da2e3ebdSchin 					{
216*da2e3ebdSchin 					case EEXIST:
217*da2e3ebdSchin #if defined(ENOTEMPTY) && (ENOTEMPTY) != (EEXIST)
218*da2e3ebdSchin 					case ENOTEMPTY:
219*da2e3ebdSchin #endif
220*da2e3ebdSchin 						if (ent->fts_info == FTS_DP && !beenhere(ent))
221*da2e3ebdSchin 						{
222*da2e3ebdSchin 							retry(ent);
223*da2e3ebdSchin 							fts_set(NiL, ent, FTS_AGAIN);
224*da2e3ebdSchin 							break;
225*da2e3ebdSchin 						}
226*da2e3ebdSchin 						/*FALLTHROUGH*/
227*da2e3ebdSchin 					default:
228*da2e3ebdSchin 						nonempty(ent);
229*da2e3ebdSchin 						if (!state->force)
230*da2e3ebdSchin 							error(ERROR_SYSTEM|2, "%s: directory not removed", ent->fts_path);
231*da2e3ebdSchin 						else
232*da2e3ebdSchin 							error_info.errors++;
233*da2e3ebdSchin 						break;
234*da2e3ebdSchin 					}
235*da2e3ebdSchin 			}
236*da2e3ebdSchin 			else if (!state->force)
237*da2e3ebdSchin 				error(2, "%s: cannot remove", ent->fts_path);
238*da2e3ebdSchin 			else
239*da2e3ebdSchin 				error_info.errors++;
240*da2e3ebdSchin 		}
241*da2e3ebdSchin 		else
242*da2e3ebdSchin 		{
243*da2e3ebdSchin 			nonempty(ent);
244*da2e3ebdSchin 			if (!state->force)
245*da2e3ebdSchin 				error(2, "%s: directory not removed", ent->fts_path);
246*da2e3ebdSchin 			else
247*da2e3ebdSchin 				error_info.errors++;
248*da2e3ebdSchin 		}
249*da2e3ebdSchin 		break;
250*da2e3ebdSchin 	default:
251*da2e3ebdSchin 		path = ent->fts_accpath;
252*da2e3ebdSchin 		if (state->verbose)
253*da2e3ebdSchin 			sfputr(sfstdout, ent->fts_path, '\n');
254*da2e3ebdSchin 		if (state->interactive)
255*da2e3ebdSchin 		{
256*da2e3ebdSchin 			if ((v = astquery(-1, "remove %s? ", ent->fts_path)) < 0)
257*da2e3ebdSchin 				return -1;
258*da2e3ebdSchin 			if (v > 0)
259*da2e3ebdSchin 			{
260*da2e3ebdSchin 				nonempty(ent);
261*da2e3ebdSchin 				break;
262*da2e3ebdSchin 			}
263*da2e3ebdSchin 		}
264*da2e3ebdSchin 		else if (!state->force && state->terminal && S_ISREG(ent->fts_statp->st_mode))
265*da2e3ebdSchin 		{
266*da2e3ebdSchin 			if ((n = open(path, O_RDWR)) < 0)
267*da2e3ebdSchin 			{
268*da2e3ebdSchin 				if (
269*da2e3ebdSchin #ifdef ENOENT
270*da2e3ebdSchin 					errno != ENOENT &&
271*da2e3ebdSchin #endif
272*da2e3ebdSchin #ifdef EROFS
273*da2e3ebdSchin 					errno != EROFS &&
274*da2e3ebdSchin #endif
275*da2e3ebdSchin 					(v = astquery(-1, "override protection %s for %s? ",
276*da2e3ebdSchin #ifdef ETXTBSY
277*da2e3ebdSchin 					errno == ETXTBSY ? "``running program''" :
278*da2e3ebdSchin #endif
279*da2e3ebdSchin 					ent->fts_statp->st_uid != state->uid ? "``not owner''" :
280*da2e3ebdSchin 					fmtmode(ent->fts_statp->st_mode & S_IPERM, 0) + 1, ent->fts_path)) < 0)
281*da2e3ebdSchin 						return -1;
282*da2e3ebdSchin 					if (v > 0)
283*da2e3ebdSchin 					{
284*da2e3ebdSchin 						nonempty(ent);
285*da2e3ebdSchin 						break;
286*da2e3ebdSchin 					}
287*da2e3ebdSchin 			}
288*da2e3ebdSchin 			else
289*da2e3ebdSchin 				close(n);
290*da2e3ebdSchin 		}
291*da2e3ebdSchin #if _lib_fsync
292*da2e3ebdSchin 		if (state->clobber && S_ISREG(ent->fts_statp->st_mode) && ent->fts_statp->st_size > 0)
293*da2e3ebdSchin 		{
294*da2e3ebdSchin 			if ((n = open(path, O_WRONLY)) < 0)
295*da2e3ebdSchin 				error(ERROR_SYSTEM|2, "%s: cannot clear data", ent->fts_path);
296*da2e3ebdSchin 			else
297*da2e3ebdSchin 			{
298*da2e3ebdSchin 				off_t		c = ent->fts_statp->st_size;
299*da2e3ebdSchin 
300*da2e3ebdSchin 				for (;;)
301*da2e3ebdSchin 				{
302*da2e3ebdSchin 					if (write(n, state->buf, sizeof(state->buf)) != sizeof(state->buf))
303*da2e3ebdSchin 					{
304*da2e3ebdSchin 						error(ERROR_SYSTEM|2, "%s: data clear error", ent->fts_path);
305*da2e3ebdSchin 						break;
306*da2e3ebdSchin 					}
307*da2e3ebdSchin 					if (c <= sizeof(state->buf))
308*da2e3ebdSchin 						break;
309*da2e3ebdSchin 					c -= sizeof(state->buf);
310*da2e3ebdSchin 				}
311*da2e3ebdSchin 				fsync(n);
312*da2e3ebdSchin 				close(n);
313*da2e3ebdSchin 			}
314*da2e3ebdSchin 		}
315*da2e3ebdSchin #endif
316*da2e3ebdSchin 		if (remove(path))
317*da2e3ebdSchin 		{
318*da2e3ebdSchin 			nonempty(ent);
319*da2e3ebdSchin 			if (!state->force || state->interactive)
320*da2e3ebdSchin 				error(ERROR_SYSTEM|2, "%s: not removed", ent->fts_path);
321*da2e3ebdSchin 			else
322*da2e3ebdSchin 				error_info.errors++;
323*da2e3ebdSchin 		}
324*da2e3ebdSchin 		break;
325*da2e3ebdSchin 	}
326*da2e3ebdSchin 	return 0;
327*da2e3ebdSchin }
328*da2e3ebdSchin 
329*da2e3ebdSchin int
330*da2e3ebdSchin b_rm(int argc, register char** argv, void* context)
331*da2e3ebdSchin {
332*da2e3ebdSchin 	State_t		state;
333*da2e3ebdSchin 	FTS*		fts;
334*da2e3ebdSchin 	FTSENT*		ent;
335*da2e3ebdSchin 	int		set3d;
336*da2e3ebdSchin 
337*da2e3ebdSchin 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
338*da2e3ebdSchin 	memset(&state, 0, sizeof(state));
339*da2e3ebdSchin 	state.fs3d = fs3d(FS3D_TEST);
340*da2e3ebdSchin 	state.terminal = isatty(0);
341*da2e3ebdSchin 	for (;;)
342*da2e3ebdSchin 	{
343*da2e3ebdSchin 		switch (optget(argv, usage))
344*da2e3ebdSchin 		{
345*da2e3ebdSchin 		case 'd':
346*da2e3ebdSchin 			state.directory = 1;
347*da2e3ebdSchin 			continue;
348*da2e3ebdSchin 		case 'f':
349*da2e3ebdSchin 			state.force = 1;
350*da2e3ebdSchin 			state.interactive = 0;
351*da2e3ebdSchin 			continue;
352*da2e3ebdSchin 		case 'i':
353*da2e3ebdSchin 			state.interactive = 1;
354*da2e3ebdSchin 			state.force = 0;
355*da2e3ebdSchin 			continue;
356*da2e3ebdSchin 		case 'r':
357*da2e3ebdSchin 		case 'R':
358*da2e3ebdSchin 			state.recursive = 1;
359*da2e3ebdSchin 			continue;
360*da2e3ebdSchin 		case 'F':
361*da2e3ebdSchin #if _lib_fsync
362*da2e3ebdSchin 			state.clobber = 1;
363*da2e3ebdSchin #else
364*da2e3ebdSchin 			error(1, "%s not implemented on this system", opt_info.name);
365*da2e3ebdSchin #endif
366*da2e3ebdSchin 			continue;
367*da2e3ebdSchin 		case 'u':
368*da2e3ebdSchin 			state.unconditional = 1;
369*da2e3ebdSchin 			continue;
370*da2e3ebdSchin 		case 'v':
371*da2e3ebdSchin 			state.verbose = 1;
372*da2e3ebdSchin 			continue;
373*da2e3ebdSchin 		case '?':
374*da2e3ebdSchin 			error(ERROR_USAGE|4, "%s", opt_info.arg);
375*da2e3ebdSchin 			continue;
376*da2e3ebdSchin 		case ':':
377*da2e3ebdSchin 			error(2, "%s", opt_info.arg);
378*da2e3ebdSchin 			continue;
379*da2e3ebdSchin 		}
380*da2e3ebdSchin 		break;
381*da2e3ebdSchin 	}
382*da2e3ebdSchin 	argv += opt_info.index;
383*da2e3ebdSchin 	if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
384*da2e3ebdSchin 		argv++;
385*da2e3ebdSchin 	if (error_info.errors || !*argv)
386*da2e3ebdSchin 		error(ERROR_USAGE|4, "%s", optusage(NiL));
387*da2e3ebdSchin 
388*da2e3ebdSchin 	/*
389*da2e3ebdSchin 	 * do it
390*da2e3ebdSchin 	 */
391*da2e3ebdSchin 
392*da2e3ebdSchin 	if (state.interactive)
393*da2e3ebdSchin 		state.verbose = 0;
394*da2e3ebdSchin 	state.uid = geteuid();
395*da2e3ebdSchin 	state.unconditional = state.unconditional && state.recursive && state.force;
396*da2e3ebdSchin 	if (state.recursive && state.fs3d)
397*da2e3ebdSchin 	{
398*da2e3ebdSchin 		set3d = state.fs3d;
399*da2e3ebdSchin 		state.fs3d = 0;
400*da2e3ebdSchin 		fs3d(0);
401*da2e3ebdSchin 	}
402*da2e3ebdSchin 	else
403*da2e3ebdSchin 		set3d = 0;
404*da2e3ebdSchin 	if (fts = fts_open(argv, FTS_PHYSICAL, NiL))
405*da2e3ebdSchin 	{
406*da2e3ebdSchin 		while ((ent = fts_read(fts)) && !rm(&state, ent));
407*da2e3ebdSchin 		fts_close(fts);
408*da2e3ebdSchin 	}
409*da2e3ebdSchin 	else if (!state.force)
410*da2e3ebdSchin 		error(ERROR_SYSTEM|2, "%s: cannot remove", argv[0]);
411*da2e3ebdSchin 	if (set3d)
412*da2e3ebdSchin 		fs3d(set3d);
413*da2e3ebdSchin 	return error_info.errors != 0;
414*da2e3ebdSchin }
415