Date: Thu, 1 Nov 2012 15:25:05 -0700 From: Garrett Cooper <yanegomi@gmail.com> To: FreeBSD FS <freebsd-fs@freebsd.org> Subject: Inconsistent/potentially incorrect behavior with relative lookups via chdir(2) on UFS/ZFS Message-ID: <CAGH67wSa7y-Nz=Pm%2BpxL3TRyKvBdDq2s3paSCu3LB-AFwmO_2g@mail.gmail.com>
index | next in thread | raw e-mail
[-- Attachment #1 --]
Hi,
Just doing some interop testing on UFS/ZFS to develop a baseline for
filesystem behavior, and I noticed some inconsistencies with the ENOENT
requirement in chdir(2) when dealing with relative ".." paths (dot-dot
lookups). In particular...
1. I would have expected chdir('.') to have failed in UFS/ZFS with
ENOENT if '.' wasn't present, but it didn't.
2. I would have expected chdir('..') to have failed in ZFS with ENOENT
if '..' wasn't present, but it didn't.
Sidenote: python doesn't do any special handling with os.chdir, per
Modules/posixmodule.c (I checked).
The full test I ran is included below.
Thoughts?
Thanks,
-Garrett
# uname -a
FreeBSD forza.west.isilon.com 9.1-PRERELEASE FreeBSD 9.1-PRERELEASE #0
r240770: Thu Sep 20 19:28:45 PDT 2012
gcooper@forza.west.isilon.com:/usr/obj/usr/src/sys/FORZA
amd64
UFS:
# (set -x; rm -Rf *; for mc in '' 1; do export MUSICAL_CHAIRS=$mc; for d in
'' parent/child; do export BASEDIR=$d; python ~gcooper/pull_rug.py; env
CHROOT=1 python ~gcooper/pull_rug.py; done; done)
+ cd /tmp/foo/bar/baz/
+ rm -Rf child parent
+ for mc in ''\'''\''' 1
+ export MUSICAL_CHAIRS=
+ MUSICAL_CHAIRS=
+ for d in ''\'''\''' parent/child
+ export BASEDIR=
+ BASEDIR=
+ python /home/gcooper/pull_rug.py
did not fail with chdir(.)
cwd after is indeterminate
+ env CHROOT=1 python /home/gcooper/pull_rug.py
did not fail with chdir(.)
cwd after is indeterminate
+ for d in ''\'''\''' parent/child
+ export BASEDIR=parent/child
+ BASEDIR=parent/child
+ python /home/gcooper/pull_rug.py
did not fail with chdir(.)
cwd after is indeterminate
+ env CHROOT=1 python /home/gcooper/pull_rug.py
did not fail with chdir(.)
cwd after is indeterminate
+ for mc in ''\'''\''' 1
+ export MUSICAL_CHAIRS=1
+ MUSICAL_CHAIRS=1
+ for d in ''\'''\''' parent/child
+ export BASEDIR=
+ BASEDIR=
+ python /home/gcooper/pull_rug.py
[parent,before] inode is: 9985
[parent,after] inode is: 9988
[child] inode from fstat is: 9985
[child] inode from stat is: 9988
did not fail with chdir(.)
cwd after is indeterminate
+ env CHROOT=1 python /home/gcooper/pull_rug.py
[parent,before] inode is: 9989
[parent,after] inode is: 9985
[child] inode from fstat is: 4
did not fail with chdir(.)
cwd after is indeterminate
+ for d in ''\'''\''' parent/child
+ export BASEDIR=parent/child
+ BASEDIR=parent/child
+ python /home/gcooper/pull_rug.py
[parent,before] inode is: 5
[parent,after] inode is: 8
[child] inode from fstat is: 5
[child] inode from stat is: 8
did not fail with chdir(.)
cwd after is indeterminate
+ env CHROOT=1 python /home/gcooper/pull_rug.py
[parent,before] inode is: 10
[parent,after] inode is: 6
[child] inode from fstat is: 4
did not fail with chdir(.)
cwd after is indeterminate
ZFS:
# (set -x; rm -Rf *; for mc in '' 1; do export MUSICAL_CHAIRS=$mc; for d in
'' parent/child; do export BASEDIR=$d; python ~gcooper/pull_rug.py; env
CHROOT=1 python ~gcooper/pull_rug.py; done; done)
+ cd /root/foo/bar/baz/
+ rm -Rf child parent
+ for mc in ''\'''\''' 1
+ export MUSICAL_CHAIRS=
+ MUSICAL_CHAIRS=
+ for d in ''\'''\''' parent/child
+ export BASEDIR=
+ BASEDIR=
+ python /home/gcooper/pull_rug.py
did not fail with chdir(.)
did not fail with chdir(../..)
cwd after is /root/foo/bar
+ env CHROOT=1 python /home/gcooper/pull_rug.py
did not fail with chdir(.)
did not fail with chdir(../..)
cwd after is /
+ for d in ''\'''\''' parent/child
+ export BASEDIR=parent/child
+ BASEDIR=parent/child
+ python /home/gcooper/pull_rug.py
did not fail with chdir(.)
cwd after is indeterminate
+ env CHROOT=1 python /home/gcooper/pull_rug.py
did not fail with chdir(.)
cwd after is indeterminate
+ for mc in ''\'''\''' 1
+ export MUSICAL_CHAIRS=1
+ MUSICAL_CHAIRS=1
+ for d in ''\'''\''' parent/child
+ export BASEDIR=
+ BASEDIR=
+ python /home/gcooper/pull_rug.py
[parent,before] inode is: 3688787
[parent,after] inode is: 3688789
[child] inode from fstat is: 3688787
[child] inode from stat is: 3688789
did not fail with chdir(.)
did not fail with chdir(../..)
cwd after is /root/foo/bar
+ env CHROOT=1 python /home/gcooper/pull_rug.py
[parent,before] inode is: 3688790
[parent,after] inode is: 3688792
[child] inode from fstat is: 4
did not fail with chdir(.)
did not fail with chdir(../..)
cwd after is /
+ for d in ''\'''\''' parent/child
+ export BASEDIR=parent/child
+ BASEDIR=parent/child
+ python /home/gcooper/pull_rug.py
[parent,before] inode is: 3688794
[parent,after] inode is: 3688797
[child] inode from fstat is: 3688794
[child] inode from stat is: 3688797
did not fail with chdir(.)
cwd after is indeterminate
+ env CHROOT=1 python /home/gcooper/pull_rug.py
[parent,before] inode is: 3688799
[parent,after] inode is: 3688802
[child] inode from fstat is: 4
did not fail with chdir(.)
cwd after is indeterminate
[-- Attachment #2 --]
#!/usr/bin/env python
import errno
import os
import shutil
import signal
import sys
import traceback
import time
CHROOT = os.getenv('CHROOT')
basedir = os.environ.get('BASEDIR') or 'child'
starting_pt = CHROOT and '/' or os.path.join(os.getcwd(), basedir)
final_destination = os.path.join(basedir, 'child1')
if os.path.exists(basedir):
shutil.rmtree(basedir.split(os.path.sep)[0])
os.makedirs(basedir, 0700)
os.symlink('../child', final_destination)
child_pid = os.fork()
if child_pid:
time.sleep(5)
try:
if os.getenv('MUSICAL_CHAIRS'):
print '[parent,before] inode is:', os.stat(basedir).st_ino
shutil.rmtree(basedir.split(os.path.sep)[0])
if os.getenv('MUSICAL_CHAIRS'):
os.makedirs(basedir, 0700)
print '[parent,after] inode is:', os.stat(basedir).st_ino
except:
sys.stdout.write(traceback.print_exc())
os.kill(child_pid, signal.SIGKILL)
sys.exit(2)
else:
fd = os.open(starting_pt, os.O_RDONLY)
if CHROOT:
os.chroot('.')
os.chdir(final_destination)
time.sleep(10)
if os.getenv('MUSICAL_CHAIRS'):
# Can't do this in python because os.path.{abs,rel}path/os.stat fails
# if the path is missing ;).
print '[child] inode from fstat is:', os.fstat(fd).st_ino
os.system('echo \[child\] inode from stat is: `stat -f %%i %s`'
% (starting_pt, ))
signal.alarm(5)
try:
print os.getcwd()
print 'did not fail when calling getcwd()'
except OSError as ose:
if ose.errno != errno.ENOENT:
raise
try:
os.chdir('.')
print 'did not fail with chdir(.)'
except OSError as ose:
if ose.errno != errno.ENOENT:
raise
try:
os.chdir('../..')
print 'did not fail with chdir(../..)'
except OSError as ose:
if ose.errno != errno.ENOENT:
raise
try:
print 'cwd after is', os.getcwd()
except OSError as ose:
print 'indeterminate'
signal.alarm(0)
os._exit(0)
sys.exit(os.waitpid(child_pid, 0)[1])
help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAGH67wSa7y-Nz=Pm%2BpxL3TRyKvBdDq2s3paSCu3LB-AFwmO_2g>
