in reply to readdir() on a sysopen() handle?

May be you can use opendir and then filter out the symbolic links when reading the directory with readdir .

Replies are listed 'Best First'.
Re^2: readdir() on a sysopen() handle?
by perlhuhn (Novice) on Aug 20, 2017 at 10:49 UTC
    Such a filter would have to use stat() to determine if an entry is a diretory before opening it. The problem is that an entry might change from a directory to a symbolic link between the stat() and the open(). O_NOFOLLOW prevents such race conditions.
      The problem is that an entry might change from a directory to a symbolic link between the stat() and the open().

      Wouldn't a second stat() after the open tell? Well duh, the underlying file could just switch back from symlink to directory between the open() and the second stat, e.g. something that emulates a directory via a maliciously loaded file system module doing sinister things. Just curious - what problem are you trying to solve?

      Correct me if I am wrong, but after getting a handle to something, even if the something is renamed, deleted, and symlinked back, it holds to the original structure being accessed:

      my $path = '/tmp/open'; -d $path and die "remove $path first\n"; mkdir $path; for (qw(foo bar quux)) { open my $fh, '>',"$path/$_"; } mkdir "$path/baz"; for (qw(blorf blorfldyick)) { open my $fh,'>', "$path/baz/$_"; } opendir my $dh1, $path; while(readdir $dh1) { next if /^\.\.?$/; print "read(dh1): $path/$_\n"; if (-d "$path/$_") { opendir my $dh2, "$path/$_" or die; # emulate external change directory to symlink rename "$path/$_","$path/fie"; symlink "$path/fie", "$path/$_" or die; # end emulate if(-l "$path/$_") { print "bogus change to $path/$_:\n"; print " $path/$_ points to ",readlink "$path/$_","\n"; } while (my $e = readdir $dh2) { next if $e =~ /^\.\.?$/; print "read(dh2): $e\n"; } } } __END__ read(dh1): /tmp/open/foo read(dh1): /tmp/open/quux read(dh1): /tmp/open/baz bogus change to /tmp/open/baz: /tmp/open/baz points to /tmp/open/fie read(dh2): blorf read(dh2): blorfldyick read(dh1): /tmp/open/bar

      Side note which might resolve this XY Problem (if so): -d on a symlink returns true up to v5.25.10, so -d resolves symlinks, which it shouldn't do. IMHO this is a bug.

      Apropos race condition: I can't think of anything which would resolve that, other than a system call like openif() into which the expected type is passed as an argument.

      perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
        -d following symlinks is not a bug because it calls stat() which follows symlinks as required by POSIX. You have to use lstat() (or -l) if you don't want that.
      The problem is that an entry might change from a directory to a symbolic link between the stat() and the open(). O_NOFOLLOW prevents such race conditions.
      I fail to see why or how O_NOFOLLOW would prevent this from happening if you were thinking about doing a sysopen followed by a readdir or anything more or less equivalent with a different system call.

      Maybe you should explain more precisely what you're really trying to do.

        I can't speak for the OP, but—

        Problems such as this often arise in scenarios where the effective user has more privileges than necessary for the intended operation. Then the program must either juggle with privileges/capabilities, or otherwise take precaution.