/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* nftw - new file tree walk
*
* int nftw(char *path, int (*fn)(), int depth, int flags);
*
* Derived from System V ftw() by David Korn
*
* nftw visits each file and directory in the tree starting at
* path. It uses the generic directory reading library so it works
* for any file system type. The flags field is used to specify:
* FTW_PHYS Physical walk, does not follow symbolic links
* Otherwise, nftw will follow links but will not
* walk down any path the crosses itself.
* FTW_MOUNT The walk will not cross a mount point.
* FTW_DEPTH All subdirectories will be visited before the
* directory itself.
* FTW_CHDIR The walk will change to each directory before
* reading it. This is faster but core dumps
* may not get generated.
*
* The following flags are private, and are used by the find
* utility:
* FTW_ANYERR Call the callback function and return
* FTW_NS on any stat failure, not just
* lack of permission.
* FTW_HOPTION Use stat the first time the walk
* function is called, regardless of
* whether or not FTW_PHYS is specified.
* FTW_NOLOOP Allow find utility to detect infinite loops created
* by both symbolic and hard linked directories.
*
* fn is called with four arguments at each file and directory.
* The first argument is the pathname of the object, the second
* is a pointer to the stat buffer and the third is an integer
* giving additional information as follows:
*
* FTW_F The object is a file.
* FTW_D The object is a directory.
* FTW_DP The object is a directory and subdirectories
* have been visited.
* FTW_SL The object is a symbolic link.
* FTW_SLN The object is a symbolic link pointing at a
* non-existing file.
* FTW_DNR The object is a directory that cannot be read.
* fn will not be called for any of its descendants.
* FTW_NS Stat failed on the object because of lack of
* appropriate permission. The stat buffer passed to fn
* is undefined. Stat failure for any reason is
* considered an error and nftw will return -1.
* The following value is private, and is used by the find utility:
* FTW_DL An infinite loop has been detected.
* The fourth argument is a struct FTW* which contains the depth
* and the offset into pathname to the base name.
* If fn returns nonzero, nftw returns this value to its caller.
*
* depth limits the number of open directories that ftw uses
* before it starts recycling file descriptors. In general,
* a file descriptor is used for each level. When FTW_CHDIR isn't set,
* in order to descend to arbitrary depths, nftw requires 2 file
* descriptors to be open during the call to openat(), therefore if
* the depth argument is less than 2 nftw will not use openat(), and
* it will fail with ENAMETOOLONG if it descends to a directory that
* exceeds PATH_MAX.
*
*/
#include "lint.h"
#include <mtlib.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <ftw.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <thread.h>
#include <synch.h>
#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#else
#endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */
#ifndef PATH_MAX
#endif
/*
* Local variables (used to be static local).
* Putting them into a structure that is passed
* around makes nftw() MT-safe with no locking required.
*/
struct Save {
char *comp;
long here;
};
struct Var {
char *home;
char *fullpath;
char *tmppath;
int curflags;
int walklevel;
int flags);
};
static DIR *nocdopendir(const char *);
static const char *get_unrooted(const char *);
/*
* This is the recursive walker.
*/
static int
{
char *p, *tmp;
int type;
char *comp;
char *q;
int rc = 0;
int oldbase;
int skip;
else
} else {
}
} else {
}
/*
* Determine the type of the component.
*
* Note that if the component is a trigger mount, this
* will cause it to load.
*/
if (depth <= 1)
/*
* If opendirf fails because there
* are OPEN_MAX fd in the calling
* process, and we close the oldest
* fd, and another opendirf doesn't
* fail, depth is set to 1.
*/
depth = 1;
} else {
goto fail;
}
}
} else {
}
/*
* If FTW_ANYERR is specified, then a stat error
* other than ENOENT automatically results in
* failure. This allows the callback function
* to properly handle ENAMETOOLONG and ELOOP and
* things of that nature, that would be masked
* by calling lstat before failing.
*/
goto fail;
} else {
/*
* Statf has failed. If stat was used instead of lstat,
* try using lstat. If lstat doesn't fail, "comp"
* must be a symbolic link pointing to a non-existent
* file. Such a symbolic link should be ignored.
* Also check the file type, if possible, for symbolic
* link.
*/
/*
* Ignore bad symbolic link, let "fn"
* report it.
*/
} else {
fail:
/*
* if FTW_ANYERR is set in flags, we call
* the user function with FTW_NS set, regardless
* of the reason stat failed.
*/
return (-1);
}
}
/*
* If the walk is not supposed to cross a mount point,
* and it did, get ready to return.
*/
goto quit;
/*
* If current component is not a directory, call user
* specified function and get ready to return.
*/
if (rc > 0)
goto quit;
*component++ = '/';
*component = 0;
/*
* Security check (there is a window between
* (*vp->statf)() and opendir() above).
*/
rc = -1;
goto quit;
}
} else {
goto quit;
}
}
/*
* If the walk has followed a symbolic link (FTW_PHYS is not set),
* traverse the walk back to make sure there is not a loop.
* The find utility (FTW_NOLOOP is set) detects infinite loops
* in both symbolic and hard linked directories.
*/
while (sp) {
/*
* If the same node has already been visited, there
* is a loop. Get ready to return.
*/
/* private interface for find util */
goto fail;
}
goto quit;
}
}
}
continue;
if (*q == '.') {
if (q[1] == 0)
continue;
else if (q[1] == '.' && q[2] == 0)
continue;
}
}
/*
* When the space needed for vp->home has
* exceeded the amount of space that has
* been allocated, realloc() more space
* and adjust pointers to point to the
* (possibly moved) new block for vp->home
*/
rc = -1;
goto quit;
}
}
}
p = component;
while (*q != '\0')
*p++ = *q++;
*p = '\0';
/* Call walk() recursively. */
}
*component = 0;
} else {
}
rc = -1;
goto quit;
}
}
if (rc != 0) {
continue;
}
goto quit; /* this seems extreme */
}
}
*--component = 0;
quit:
/* try to change back to previous directory */
rc = -1;
}
} else {
cdval = -1;
}
*comp = 0;
if (cdval < 0) {
rc = -1;
} else {
/* Security check */
last, 0) < 0 ||
rc = -1;
}
}
}
}
}
return (val);
else
return (rc);
}
int
{
char *dp;
char *base;
char *endhome;
int save_errno;
return (-1);
/*
* If the walk is going to change directory before
* reading it, save current working directory.
*/
return (-1);
}
}
if (*path == '/')
else {
*dp++ = '/';
}
while (*path) {
if (*dp == '/')
}
*dp = 0;
if (*path) {
return (-1);
}
/*
* If doing chdir()'s, set var.opendirf to cdopendir.
* If not doing chdir()'s and if nftw()'s depth arg >= 2,
* set var.opendirf to nocdopendir. In order to
* descend to arbitrary depths without doing chdir()'s, nftw()
* requires a depth arg >= 2 so that nocdopendir() can use openat()
* to traverse the directories. So when not doing
* chdir()'s if nftw()'s depth arg <= 1, set var.opendirf to
* cdopendir.
* If doing a physical walk (not following symbolic link), set
* to cdstat() or nocdstat().
*/
else
} else {
else
}
/*
* If walk is not going to cross a mount point,
* save the current mount point.
*/
else
goto done;
}
/*
* Call walk() which does most of the work.
* walk() uses errno in a rather obtuse way
* so we shield any incoming errno.
*/
save_errno = errno;
errno = 0;
if (errno == 0)
errno = save_errno;
done:
*endhome = 0;
return (rc);
}
/*
* Get stat info on path when FTW_CHDIR is set.
*/
/*ARGSUSED1*/
static int
{
}
/*
* Get lstat info on path when FTW_CHDIR is set.
*/
/*ARGSUSED1*/
static int
{
flags | AT_SYMLINK_NOFOLLOW));
}
/*
* Get stat info on path when FTW_CHDIR is not set.
*/
static int
{
int fd;
const char *basepath;
/* get basename of path */
} else {
}
}
/*
* Get lstat info on path when FTW_CHDIR is not set.
*/
static int
{
int fd;
const char *basepath;
/* get basename of path */
} else {
}
}
/*
* Open path directory when FTW_CHDIR is set.
*
*/
static DIR *
{
}
/*
* Open path directory when FTW_CHDIR is not set.
*/
static DIR *
{
return (NULL);
}
return (NULL);
}
return (NULL);
}
}
}
}
return (fdd);
}
/*
* return pointer basename of path, which may contain trailing slashes
*
* We do this when we do not chdir() on the input.
*/
static const char *
{
const char *ptr;
return (NULL);
/* find last char in path before any trailing slashes */
;
return (ptr);
if (*--ptr == '/')
return (++ptr);
return (ptr);
}
/*
* close the oldest directory. It saves the seek offset.
* return value is 0 unless it was unable to close any descriptor
*/
static int
{
while (sp) {
break;
}
return (0);
return (1);
}