Possible ways to refactor Unix programs

Use explicit namespaces for looking up filenames

Change programs to use explicit namespaces (which can be passed around as object-capabilities) for looking up filenames in.

For example, calls to open() would be replaced with calls to a hypothetical namespace_open():

int open(const char *filename, int flags, int mode);
int namespace_open(cap_t namespace, const char *filename, int flags, int mode);

namespace_open() is similar to openat(). The difference is that openat() can escape to the parent directory using ".." in pathnames; Unix directory file descriptors do not convey limited authority. Namespaces as used in namespace_open() can be unconnected to each other. Another difference is that openat() will ignore its directory FD argument when given an absolute pathname, whereas our namespaces can apply to absolute pathnames.

Adding namespace arguments is simpler than refactoring a program to use file and directory objects. It provides a simple transition strategy: On a system without any special support for namespaces, the namespace-based functions can be implemented as simple wrappers for the original functions that ignore the namespace argument. Code for constructing pathnames (concatenating strings, adding slashes etc.) does not need to be changed.

Programs might need only two namespaces: the caller's namespace (containing objects passed from the caller), and the callee's installation endowment.

We would need to add namespace arguments to utility functions that can be applied to any namespace. However, in many cases, the appropriate namespace could be taken from a global variable (or from a global getter function).

Refactoring tools

Examining source:

Changing source:

Refactoring process: If the program can be recompiled quickly (within a couple of seconds) and it has a good set of test cases, the workflow can become: Make a small change by switching one function call to use its namespace-based counterpart, recompile, run tests, check that the tests passed. Tests can be run under the "watch" tool.

Comparison: AppArmor

AppArmor's "hat" scheme bears some resemblance to this namespace scheme. Although the two are very different semantically, the AppArmor "hat" scheme asks the programmer to make some similar decisions when changing code.

A process running under AppArmor has a number of "hats" to choose from (defined statically by the AppArmor profile that the process is running under). When the process performs an access, it can pick one of the "hats" to use. This is done by setting the process's current hat, which is a piece of global state (similar to a process's user/group IDs). The current hat is a kind of AmbientAuthority, so this is not an object-capability scheme.

In contrast, namespaces would be passed as arguments. A process might have a small, fixed number of namespaces it uses (this is the similarity with AppArmor). However, there is also scope for dynamically creating namespaces.

RefactoringUnixPrograms (last edited 2007-05-26 13:46:06 by MarkSeaborn)