blog

Image of Cicada

A Story About a Bug

by

I’m going to tell you a little story about a bug. Like most bugs, it started with an invalid assumption. You see, I was ambushed by permissions on a network file system. Here’s what my code looked like, at least in spirit:

import os
import os.path
# Build a path
filepath = 'relative/path/to/file.txt'
dirpath = os.path.dirname(filepath)
# Create the directories in the path
os.makedirs(dirpath)
print("Success")

Go head and try that. Let me know how it works for you. It works fine for me. Well, it usually works fine for me. It could fail if I don’t have permission to create a directory in the current directory. Let’s add a test:

import os
import os.path
# Build a path
filepath = 'relative/path/to/file.txt'
dirpath = os.path.dirname(filepath)
# Create the directories in the path
if os.access('.', os.W_OK):
    os.makedirs(dirpath)
    print("Success")
else:
    print("No write permission for current directory")

It seems to work:

troy$ chmod 555 .
troy$ python demonstration2.py
No write permission for current directory
troy$ ls -l
total 96
-rw-r--r--  1 troy  staff    194 Apr 27 11:20 demonstration1.py
-rw-r--r--  1 troy  staff    290 Apr 27 11:21 demonstration2.py
troy$ chmod 755 .
troy$ python demonstration2.py
Success
troy$ ls -l
total 96
-rw-r--r--  1 troy  staff    194 Apr 27 11:20 demonstration1.py
-rw-r--r--  1 troy  staff    290 Apr 27 11:21 demonstration2.py
drwxr-xr-x  3 troy  staff    102 Apr 27 11:22 relative

Stepping back from the task at hand, let me mention that software developers rarely have great control over the execution environment. Let’s look at what happened to me when a user ran my directory creation code:

troy$ python path/to/demonstration2.py
Traceback (most recent call last):
  File "path/to/demonstration2.py", line 10, in <module>
    os.makedirs(dirpath)
  File "path/to/lib/python2.7/os.py", line 150, in makedirs
    makedirs(head, mode)
  File "path/to/lib/python2.7/os.py", line 157, in makedirs
    mkdir(name, mode)
OSError: [Errno 13] Permission denied: 'relative/path'

That’s not what I had expected. I had the user run some commands:

user$ mkdir directory
user$ touch file
user$ mkdir directory/child
mkdir: directory/child: Permission denied
user$ ls -l
total 32
drwxr-xr-x  2 user  staff  16384 Apr 27 11:30 directory
-rw-r--r--@ 1 user  staff      0 Apr 27 11:30 file

The user was able to create directories and files, but not subdirectories. This seems peculiar. While I knew that the user intended to use my code on a network file system, I hadn’t considered that directory creation would fail—especially not after creating the first directory successfully.

Software screen capture

The source of this particular issue was in the configuration of the file server. The OS X file server can be configured allow creation of directories but not child files or directories. It can even be configured to create directories and files with specific permissions, ignoring the mode passed to calls like mkdir().

I was reminded of a few things from this:

  • Code defensively. You know that function calls can fail. Handle the errors. (At least log or report them.)
  • Your file system may not always cooperate with the OS-level function calls.
  • This is especially true when you are not in control of the execution environment.

+ more