Skip site navigation (1)Skip section navigation (2)
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>