Implement a -delete option to find. The code is extremely paranoid and

goes to a fair degree of trouble to enable something like this to
be safe:  cd /tmp && find . -mtime +7 -delete

It removes both files and directories.  It does not attempt to remove
immutable files (an earlier version I showed to a few people did a chflags
and tried to blow away even immutable files.  Too risky..)

It is thought to be safe because it forces the fts(3) driven descent to
only do "minimal risk" stuff.  specifically, -follow is disabled, it does
checking to see that it chdir'ed to the directory it thought it was
going to, it will *not* pass a pathname with a '/' character in it to
unlink(), so it should be totally immune to symlink tree races.  If it runs
into something "fishy", it bails out rather than blunder ahead.. It's better
to do that if somebody is trying to compromise security rather than risk
giving them an opportunity.  Since the unlink()/rmdir() is being called
from within the current working directory during the tree descent, there
are no fork/exec overheads or races.

As a side effect of this paranoia, you cannot do a
"find /somewhere/dir -delete", as the last argument to rmdir() is
"/somewhere/dir", and the checking won't allow it.  Besides, one would use
rm -rf for that case anyway. :-)

Reviewed by: pst (some time ago, but I've removed the immutable file
deletion code that he complained about since he last saw it)
This commit is contained in:
Peter Wemm 1996-10-04 12:54:07 +00:00
parent 7d1e36586d
commit abacbbbf01
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=18681
5 changed files with 70 additions and 2 deletions

View File

@ -49,6 +49,7 @@ int queryuser __P((char **));
PLAN *c_atime __P((char *));
PLAN *c_ctime __P((char *));
PLAN *c_delete __P((void));
PLAN *c_depth __P((void));
PLAN *c_exec __P((char ***, int));
PLAN *c_follow __P((void));

View File

@ -33,7 +33,7 @@
.\" SUCH DAMAGE.
.\"
.\" @(#)find.1 8.4 (Berkeley) 4/1/94
.\" $Id$
.\" $Id: find.1,v 1.3 1996/08/29 18:05:51 wosch Exp $
.\"
.Dd April 1, 1994
.Dt FIND 1
@ -143,6 +143,13 @@ information and the time
was started, rounded up to the next full 24\-hour period, is
.Ar n
24\-hour periods.
.It Ic -delete
Delete found files and/or directories. Always returns True. This executes
from the current working directory as
.Nm
recurses down the tree. It will not attempt to delete a filename with a ``/''
character in it's pathname relative to "." for security reasons.
Depth\-first traversal processing is implied by this option.
.It Ic -exec Ar utility Op argument ... ;
True if the program named
.Ar utility
@ -414,6 +421,7 @@ and
options and the
.Ic -inum ,
.Ic -print0 ,
.Ic -delete ,
and
.Ic -ls
primaries are extensions to
@ -461,6 +469,11 @@ These problems are handled by the
option and the
.Xr getopt 3
``--'' construct.
.Pp
The
.Ic -delete
primary do not interact well with other options that cause the filesystem
tree traversal options to be changed.
.Sh HISTORY
A
.Nm

View File

@ -43,7 +43,7 @@ enum ntype {
N_FSTYPE, N_GROUP, N_INUM, N_LINKS, N_LS, N_MTIME, N_NAME, N_NEWER,
N_NOGROUP, N_NOT, N_NOUSER, N_OK, N_OPENPAREN, N_OR, N_PATH,
N_PERM, N_PRINT, N_PRUNE, N_SIZE, N_TYPE, N_USER, N_XDEV,
N_PRINT0
N_PRINT0, N_DELETE
};
/* node definition */

View File

@ -936,6 +936,59 @@ c_type(typestring)
return (new);
}
/*
* -delete functions --
*
* True always. Makes it's best shot and continues on regardless.
*/
int
f_delete(plan, entry)
PLAN *plan;
FTSENT *entry;
{
/* ignore these from fts */
if (strcmp(entry->fts_accpath, ".") == 0 ||
strcmp(entry->fts_accpath, "..") == 0)
return (1);
/* sanity check */
if (isdepth == 0 || /* depth off */
(ftsoptions & FTS_NOSTAT) || /* not stat()ing */
!(ftsoptions & FTS_PHYSICAL) || /* physical off */
(ftsoptions & FTS_LOGICAL)) /* or finally, logical on */
errx(1, "-delete: insecure options got turned on");
/* Potentially unsafe - do not accept relative paths whatsoever */
if (strchr(entry->fts_accpath, '/') != NULL)
errx(1, "-delete: %s: relative path potentially not safe",
entry->fts_accpath);
/* rmdir directories, unlink everything else */
if (S_ISDIR(entry->fts_statp->st_mode)) {
if (rmdir(entry->fts_accpath) < 0)
warn("-delete: rmdir(%s)", entry->fts_path);
} else {
if (unlink(entry->fts_accpath) < 0)
warn("-delete: unlink(%s)", entry->fts_path);
}
/* "succeed" */
return (1);
}
PLAN *
c_delete()
{
ftsoptions &= ~FTS_NOSTAT; /* no optimise */
ftsoptions |= FTS_PHYSICAL; /* disable -follow */
ftsoptions &= ~FTS_LOGICAL; /* disable -follow */
isoutput = 1; /* possible output */
isdepth = 1; /* -depth implied */
return (palloc(N_DELETE, f_delete));
}
/*
* -user uname functions --
*

View File

@ -60,6 +60,7 @@ static OPTION const options[] = {
{ "-and", N_AND, NULL, O_NONE },
{ "-atime", N_ATIME, c_atime, O_ARGV },
{ "-ctime", N_CTIME, c_ctime, O_ARGV },
{ "-delete", N_DELETE, c_delete, O_ZERO },
{ "-depth", N_DEPTH, c_depth, O_ZERO },
{ "-exec", N_EXEC, c_exec, O_ARGVP },
{ "-follow", N_FOLLOW, c_follow, O_ZERO },