<<< Date Index >>>     <<< Thread Index >>>

Re: Changes to the filesystem while find is running - comments?



James Youngman wrote:
> On Mon, Nov 22, 2004 at 06:05:43PM -0800, Martin Buchholz wrote:
> 
> 
>>I mean,
>>- first go back to the parent directory
>>- then lstat("foo"); check if it's a symlink or a real directory
>>- if a symlink, then this is fishy, but it could happen non-maliciously.
>>  I would issue a warning, then continue, without chdir'ing into foo.
>>- if a directory, then probably we've hit the automounter problem.
>>  Don't issue a warning; chdir("foo") again; this time it should work.
> 
> 
> It sounds like this strategy, which is the same as we currently do,
> but with a retry, is fairly sensible.
> 
> The only problem that occurs to me is that we did not recheck "foo" to
> see if it still matches the predictes specified on the find command
> line (think about commands like 
>       find /z -user fred -o -name baz -print
> 
> ... here, if the original "foo" was owned by root but the "new" foo is
> owned by fred, our retrying technique has ensured that we have
> wandered into a place which we are not supposed to.  It's possible
> that we could simply reissue process_path() to retry instead of
> directly trying the chdir("foo") again.  I can't remember offhand if
> that is likely to be a viable strategy.

I don't know the details of find's implementation.
Conceptually, I would want to forget everything I knew about "foo",
except that we've already tried it, So re-process this path again
in its entirety.  This way, when find reports a file, it was
at least reporting the true state of the file AT SOME POINT.

E.g. if a file is changing from a regular file owned by Bob,
to a directory owned by Alice, then
find -user Bob -type d
should never report that file.

> 
>>Unlike replacing directories with symlinks, where the malicious
>>possibilities are evident, I don't see any malicious possibilities
>>arising out of mounted filesystems replaced by other filesystems.
> 
> 
> Is there a consensus agreeing with this point of view?  If so, that
> would make the implementsation much simpler...
> 
> 
> 
>>Hmmm... You're right. I guess you'd have to:
>>FD=open("foo");
>>ST1=lstat("foo");
> 
> 
> I think find would have to use xstat() there because "-L" ("-follow")
> might be in effect, problematic though it is security-wise.  xstat in
> GNU find is a function pointer that points to lstat() if the -P flag
> is in effect (this is the default; the explicit -P is a BSD
> invention), points to stat() if the -L flag is in effect, and points
> to a more complex function optionh_stat() if -H is in effect
> (optionh_stat() eventually calls either stat() or lstat()).

My suggestion is very high-level pseudocode.  There are a lot of details
to get right, as you point out.

> 
>>ST2=fstat(FD);
>>compare(ST1,ST2); 
>>fchdir(FD);
> 
> 
> I wonder what happens there if we're at an automount mount point.
> Does the fchdir() provoke automount() into mounting the filesystem?
> Would you be able to check this, Martin?

It does.  Here's a test program, and a sample run against an
automounter mount point:

-----------------------------------------------------------------
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

void printID(char *file)
{
  struct stat s;
  if (lstat(file,&s) < 0) exit(1);
  printf("file=\"%s\" dev=%ld ino=%ld\n",
         file, (long) s.st_dev, (long) s.st_ino);
}

int main (int argc, char *argv[])
{
  char *d = argv[1];
  printID(d);
  int fdParent = open(".", O_RDONLY);
  int fdChild = open(d, O_RDONLY);
  if (fchdir(fdChild) < 0) exit(1);
  printID(".");
  printID("..");
  if (fchdir(fdParent) < 0) exit(1);
  printID(d);
}
-----------------------------------------------------------------

$ ~/src/a.out make
file="make" dev=81033283 ino=186105
file="." dev=80744820 ino=1353076
file=".." dev=81033281 ino=186097
file="make" dev=80744820 ino=1353076
$ ~/src/a.out make
file="make" dev=80744820 ino=1353076
file="." dev=80744820 ino=1353076
file=".." dev=81033281 ino=186097
file="make" dev=80744820 ino=1353076



> 
>>On a related note,
>>Solaris has some interesting non-standard functions:
>>
>>     int openat(int fildes,  const  char  *path,  int  oflag,  /*
>>     mode_t mode */...);
>>
>>     The openat() function is identical to  the  open()  function
>>     except that the path argument is interpreted relative to the
>>     starting point implied by the fd argument. If the  fd  argu-
>>     ment  has  the special value AT_FDCWD, a relative path argu-
>>     ment will be resolved relative to the current working direc-
>>     tory.  If  the path argument is absolute, the fd argument is
>>     ignored.
> 
> 
> So, with an absolute path or with AT_FDCWD, it's equivalent to
> open(2)?  If'a a shame that openat() doesn't have a flag to prevent it
> following symbolic links.  Or does it?
> 

fstatat has such a flag, bug openat does not.

Another interesting function is


     DIR *fdopendir(int fildes);

     The fdopendir() function opens a directory  stream  for  the
     directory   file   descriptor  fildes.  The  directory  file
     discriptor should not be used or closed following a success-
     ful  function  call,  as  this might cause undefined results
     from future operations on the directory stream obtained from
     the call. Use closedir(3C) to close a directory stream.

BTW, all the Sun man pages are easily googlable with a query like

openat site:docs.sun.com

Martin