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