The pathwalk() function
The pathwalk() function is called only by connect_msg_to_attr() and by the rename function (c_rename(), which we'll see later). Let's look at this lowest-level function first, and then we'll proceed up the call hierarchy.
int
pathwalk (resmgr_context_t *ctp, char *pathname,
cfs_attr_t *mountpoint, int flags, des_t *output,
int *nrets, struct _client_info *cinfo)
{
int nels;
int sts;
char *p;
// 1) first, we break apart the slash-separated pathname
memset (output, 0, sizeof (output [0]) * *nrets);
output [0].attr = mountpoint;
output [0].name = "";
nels = 1;
for (p = strtok (pathname, "/"); p; p = strtok (NULL, "/")) {
if (nels >= *nrets) {
return (E2BIG);
}
output [nels].name = p;
output [nels].attr = NULL;
nels++
}
// 2) next, we analyze each pathname
for (*nrets = 1; *nrets < nels; ++*nrets) {
// 3) only directories can have children.
if (!S_ISDIR (output [*nrets - 1].attr -> attr.mode)) {
return (ENOTDIR);
}
// 4) check access permissions
sts = iofunc_check_access (ctp,
&output [*nrets-1].attr -> attr,
S_IEXEC, cinfo);
if (sts != EOK) {
return (sts);
}
// 5) search for the entry
output [*nrets].attr = search_dir (output [*nrets].name,
output [*nrets-1].attr);
if (!output [*nrets].attr) {
++*nrets;
return (ENOENT);
}
// 6) process the entry
if (S_ISLNK (output [*nrets].attr -> attr.mode)) {
++*nrets;
return (EOK);
}
}
// 7) everything was okay
return (EOK);
}
The pathwalk() function fills the output parameter with the pathnames and attributes structures of each pathname component. The *nrets parameter is used as both an input and an output. In the input case it tells pathwalk() how big the output array is, and when pathwalk() returns, *nrets is used to indicate how many elements were successfully processed (see the walkthrough below). Note that the way that we've broken the string into pieces first, and then processed the individual components one at a time means that when we abort the function (for any of a number of reasons as described in the walkthrough), the output array may have elements that are valid past where the *nrets variable indicates. This is actually useful; for example, it lets us get the pathname of a file or directory that we're creating (and hence doesn't exist). It also lets us check if there are additional components past the one that we're creating, which would be an error.
Detailed walkthrough:
- The first element of the output string is, by definition, the attributes structure
corresponding to the mount point and to the empty string.
The
forloop breaks the pathname string apart at each and every / character, and checks to see that there aren't too many of them (the caller tells us how many they have room for in the passed parameter *nrets).Note:Note that we use strtok() which isn't thread-safe; in this resource manager we are single-threaded. We would have used strtok_r() if thread-safety were a concern. - Next, we enter a
forloop that analyzes each pathname component. It's within this loop that we do all of the checking. Note that the variable *nrets points to thecurrent
pathname element. - In order for the current pathname element to even be valid, its parent (i.e. *nrets minus 1) must be a directory, since only directories can have children. If that isn't the case, we return ENOTDIR and abort. Note that when we abort, the *nrets return value includes the nondirectory that failed.
- We use the helper function iofunc_check_access() to verify accessibility for the component. Note that if we abort, *nrets includes the inaccessible directory.
- At this point, we have verified that everything is okay up to the entry, and all we need to do is find the entry within the directory. The helper function search_dir() looks through the dirblocks array member of the extended attributes structure and tries to find our entry. If the entry isn't found, *nrets includes the entry. (This is important to make note of when creating files or directories that don't yet exist!)
- We check if the entry itself is a symbolic link. If it is, we give up, and let higher levels of software deal with it. We return EOK because there's nothing actually wrong with being a symbolic link, it's just that we can't do anything about it at this level. (Why? The symbolic link could be a link to a completely different filesystem that we have no knowledge of.) The higher levels of software will eventually tell the client that the entry is a symlink, and the client's library then tries the path again — that's why we don't worry about infinite symlink loops and other stuff in our resource manager. The *nrets return value includes the entry.
- Finally, if everything works, (we've gone through all of the entries and found and verified each and every one of them) we return EOK and *nrets contains all pathname elements.
The job of *nrets is to give the higher-level routines an indication of where the processing stopped. The return value from pathwalk() will tell them why it stopped.
