Lookup of Symbolic Links

Recall that a symbolic link is a regular file that stores a pathname of another file. A pathname may include symbolic links, and they must be resolved by the kernel.

For example, if /foo/bar is a symbolic link pointing to (containing the pathname) ../dir, the pathname /foo/bar/file must be resolved by the kernel as a reference to the file /dir/file. In this example, the kernel must perform two different lookup operations. The first one resolves /foo/bar; when the kernel discovers that bar is the name of a symbolic link, it must retrieve its content and interpret it as another pathname. The second pathname operation starts from the directory reached by the first operation and continues until the last component of the symbolic link pathname has been resolved. Next, the original lookup operation resumes from the dentry reached in the second one and with the component following the symbolic link in the original pathname.

To further complicate the scenario, the pathname included in a symbolic link may include other symbolic links. You might think that the kernel code that resolves the symbolic links is hard to understand, but this is not true; the code is actually quite simple because it is recursive.

However, untamed recursion is intrinsically dangerous. For instance, suppose that a symbolic link points to itself. Of course, resolving a pathname including such a symbolic link may induce an endless stream of recursive invocations, which in turn quickly leads to a kernel stack overflow. The link_count field in the descriptor of the current process is used to avoid the problem: the field is incremented before each recursive execution and decremented right after. If the field reaches the value 5, the whole lookup operation terminates with an error code. Therefore, the level of nesting of symbolic links can be at most 5.

Furthermore, the total_link_count field in the descriptor of the current process keeps track of how many symbolic links (even nonnested) were followed in the original lookup operation. If this counter reaches the value 40, the lookup operation aborts. Without this counter, a malicious user could create a pathological pathname including many consecutive symbolic links that freezes the kernel in a very long lookup operation.

This is how the code basically works: once the link_path_walk( ) function retrieves the dentry object associated with a component of the pathname, it checks whether the corresponding inode object has a custom follow_link method (see Step 5.k and Step 13 in Section 12.5.1). If so, the inode is a symbolic link that must be interpreted before proceeding with the lookup operation of the original pathname.

In this case, the link_path_walk( ) function invokes do_follow_link( ), passing to it the address of the dentry object of the symbolic link and the address of the nameidata data

1. Checks that current->link_count is less than 5; otherwise, returns the error code -eloop

2. Checks that current->total_link_count is less than 40; otherwise, returns the error code -eloop

3. If the current->need_resched flag is set, invokes schedule( ) to give a chance to preempt the running process

4. Increments current->link count and current->total link count

5. Updates the access time of the inode object associated with the symbolic link to be resolved

6. Invokes the follow_link method of the inode, passing to it the addresses of the dentry object and of the nameidata data structure

7. Decrements the current->link count field

8. Returns the error code returned by the follow_link method (0 for no error)

The follow_link method is a filesystem-dependent function that reads the pathname stored in the symbolic link from the disk. Having filled a buffer with the symbolic link's pathname, most follow_link methods end up invoking the vfs_follow_link( ) function and returning the value taken from it. In turn, the vfs_follow_link( ) does the following:

1. Checks whether the first character of the symbolic link pathname is a slash; if so, the dentry and mnt fields of the nameidata data structure are set so they refer to the current process root directory.

2. Invokes link_path_walk( ) to resolve the symbolic link pathname, passing to it the nameidata data structure.

3. Returns the value taken from link_path_walk( ).

When do_follow_link( ) finally terminates, it returns the address of the dentry object referred to by the symbolic link to the original execution of link_path_walk( ). The link_path_walk( ) assigns this address to the dentry local variable, and then proceeds with the next step.

Continue reading here: Implementations of VFS System Calls

Was this article helpful?

0 0