189c9de7dSDavid van Moolenbroek
289c9de7dSDavid van Moolenbroek #include "fsdriver.h"
389c9de7dSDavid van Moolenbroek
489c9de7dSDavid van Moolenbroek /*
589c9de7dSDavid van Moolenbroek * Check whether the given node may be accessed as directory.
689c9de7dSDavid van Moolenbroek * Return OK or an appropriate error code.
789c9de7dSDavid van Moolenbroek */
889c9de7dSDavid van Moolenbroek static int
access_as_dir(struct fsdriver_node * __restrict node,vfs_ucred_t * __restrict ucred)989c9de7dSDavid van Moolenbroek access_as_dir(struct fsdriver_node * __restrict node,
1089c9de7dSDavid van Moolenbroek vfs_ucred_t * __restrict ucred)
1189c9de7dSDavid van Moolenbroek {
1289c9de7dSDavid van Moolenbroek mode_t mask;
1389c9de7dSDavid van Moolenbroek int i;
1489c9de7dSDavid van Moolenbroek
1589c9de7dSDavid van Moolenbroek /* The file must be a directory to begin with. */
1689c9de7dSDavid van Moolenbroek if (!S_ISDIR(node->fn_mode)) return ENOTDIR;
1789c9de7dSDavid van Moolenbroek
1889c9de7dSDavid van Moolenbroek /* The root user may access anything at all. */
1989c9de7dSDavid van Moolenbroek if (ucred->vu_uid == ROOT_UID) return OK;
2089c9de7dSDavid van Moolenbroek
2189c9de7dSDavid van Moolenbroek /* Otherwise, the caller must have search access to the directory. */
2289c9de7dSDavid van Moolenbroek if (ucred->vu_uid == node->fn_uid) mask = S_IXUSR;
2389c9de7dSDavid van Moolenbroek else if (ucred->vu_gid == node->fn_gid) mask = S_IXGRP;
2489c9de7dSDavid van Moolenbroek else {
2589c9de7dSDavid van Moolenbroek mask = S_IXOTH;
2689c9de7dSDavid van Moolenbroek
2789c9de7dSDavid van Moolenbroek for (i = 0; i < ucred->vu_ngroups; i++) {
2889c9de7dSDavid van Moolenbroek if (ucred->vu_sgroups[i] == node->fn_gid) {
2989c9de7dSDavid van Moolenbroek mask = S_IXGRP;
3089c9de7dSDavid van Moolenbroek
3189c9de7dSDavid van Moolenbroek break;
3289c9de7dSDavid van Moolenbroek }
3389c9de7dSDavid van Moolenbroek }
3489c9de7dSDavid van Moolenbroek }
3589c9de7dSDavid van Moolenbroek
3689c9de7dSDavid van Moolenbroek return (node->fn_mode & mask) ? OK : EACCES;
3789c9de7dSDavid van Moolenbroek }
3889c9de7dSDavid van Moolenbroek
3989c9de7dSDavid van Moolenbroek /*
4089c9de7dSDavid van Moolenbroek * Get the next path component from a path. Return the start and end of the
4189c9de7dSDavid van Moolenbroek * component into the path, and store its name in a null-terminated buffer.
4289c9de7dSDavid van Moolenbroek */
4389c9de7dSDavid van Moolenbroek static int
next_name(char ** ptr,char ** start,char * __restrict name,size_t namesize)4489c9de7dSDavid van Moolenbroek next_name(char ** ptr, char ** start, char * __restrict name, size_t namesize)
4589c9de7dSDavid van Moolenbroek {
4689c9de7dSDavid van Moolenbroek char *p;
4789c9de7dSDavid van Moolenbroek unsigned int i;
4889c9de7dSDavid van Moolenbroek
4989c9de7dSDavid van Moolenbroek /* Skip one or more path separator characters; they have no effect. */
5089c9de7dSDavid van Moolenbroek for (p = *ptr; *p == '/'; p++);
5189c9de7dSDavid van Moolenbroek
5289c9de7dSDavid van Moolenbroek *start = p;
5389c9de7dSDavid van Moolenbroek
5489c9de7dSDavid van Moolenbroek if (*p) {
5589c9de7dSDavid van Moolenbroek /*
5689c9de7dSDavid van Moolenbroek * Copy as much of the name as possible, up to the next path
5789c9de7dSDavid van Moolenbroek * separator. Return an error if the name does not fit.
5889c9de7dSDavid van Moolenbroek */
5989c9de7dSDavid van Moolenbroek for (i = 0; *p && *p != '/' && i < namesize; p++, i++)
6089c9de7dSDavid van Moolenbroek name[i] = *p;
6189c9de7dSDavid van Moolenbroek
6289c9de7dSDavid van Moolenbroek if (i >= namesize)
6389c9de7dSDavid van Moolenbroek return ENAMETOOLONG;
6489c9de7dSDavid van Moolenbroek
6589c9de7dSDavid van Moolenbroek name[i] = 0;
6689c9de7dSDavid van Moolenbroek } else
6789c9de7dSDavid van Moolenbroek /* An empty path component implies the current directory. */
6889c9de7dSDavid van Moolenbroek strlcpy(name, ".", namesize);
6989c9de7dSDavid van Moolenbroek
7089c9de7dSDavid van Moolenbroek /*
7189c9de7dSDavid van Moolenbroek * Return a pointer to the first character not part of this component.
7289c9de7dSDavid van Moolenbroek * This would typically be either the path separator or a null.
7389c9de7dSDavid van Moolenbroek */
7489c9de7dSDavid van Moolenbroek *ptr = p;
7589c9de7dSDavid van Moolenbroek return OK;
7689c9de7dSDavid van Moolenbroek }
7789c9de7dSDavid van Moolenbroek
7889c9de7dSDavid van Moolenbroek /*
7989c9de7dSDavid van Moolenbroek * Given a symbolic link, resolve and return the contents of the link, followed
8089c9de7dSDavid van Moolenbroek * by the remaining part of the path that has not yet been resolved (the tail).
8189c9de7dSDavid van Moolenbroek * Note that the tail points into the given destination buffer.
8289c9de7dSDavid van Moolenbroek */
8389c9de7dSDavid van Moolenbroek static int
resolve_link(const struct fsdriver * __restrict fdp,ino_t ino_nr,char * pptr,size_t size,char * tail)8489c9de7dSDavid van Moolenbroek resolve_link(const struct fsdriver * __restrict fdp, ino_t ino_nr, char * pptr,
8589c9de7dSDavid van Moolenbroek size_t size, char * tail)
8689c9de7dSDavid van Moolenbroek {
8789c9de7dSDavid van Moolenbroek struct fsdriver_data data;
8889c9de7dSDavid van Moolenbroek char path[PATH_MAX];
8989c9de7dSDavid van Moolenbroek ssize_t r;
9089c9de7dSDavid van Moolenbroek
9189c9de7dSDavid van Moolenbroek data.endpt = SELF;
9289c9de7dSDavid van Moolenbroek data.ptr = path;
9389c9de7dSDavid van Moolenbroek data.size = sizeof(path) - 1;
9489c9de7dSDavid van Moolenbroek
9589c9de7dSDavid van Moolenbroek /*
9689c9de7dSDavid van Moolenbroek * Let the file system the symbolic link. Note that the resulting path
9789c9de7dSDavid van Moolenbroek * is not null-terminated.
9889c9de7dSDavid van Moolenbroek */
9989c9de7dSDavid van Moolenbroek if ((r = fdp->fdr_rdlink(ino_nr, &data, data.size)) < 0)
10089c9de7dSDavid van Moolenbroek return r;
10189c9de7dSDavid van Moolenbroek
10289c9de7dSDavid van Moolenbroek /* Append the remaining part of the original path to be resolved. */
10389c9de7dSDavid van Moolenbroek if (r + strlen(tail) >= sizeof(path))
10489c9de7dSDavid van Moolenbroek return ENAMETOOLONG;
10589c9de7dSDavid van Moolenbroek
10689c9de7dSDavid van Moolenbroek strlcpy(&path[r], tail, sizeof(path) - r);
10789c9de7dSDavid van Moolenbroek
10889c9de7dSDavid van Moolenbroek /* Copy back the result to the original buffer. */
10989c9de7dSDavid van Moolenbroek strlcpy(pptr, path, size);
11089c9de7dSDavid van Moolenbroek
11189c9de7dSDavid van Moolenbroek return OK;
11289c9de7dSDavid van Moolenbroek }
11389c9de7dSDavid van Moolenbroek
11489c9de7dSDavid van Moolenbroek /*
11589c9de7dSDavid van Moolenbroek * Process a LOOKUP request from VFS.
11689c9de7dSDavid van Moolenbroek */
11789c9de7dSDavid van Moolenbroek int
fsdriver_lookup(const struct fsdriver * __restrict fdp,const message * __restrict m_in,message * __restrict m_out)11889c9de7dSDavid van Moolenbroek fsdriver_lookup(const struct fsdriver * __restrict fdp,
11989c9de7dSDavid van Moolenbroek const message * __restrict m_in, message * __restrict m_out)
12089c9de7dSDavid van Moolenbroek {
12189c9de7dSDavid van Moolenbroek ino_t dir_ino_nr, root_ino_nr;
12289c9de7dSDavid van Moolenbroek struct fsdriver_node cur_node, next_node;
12389c9de7dSDavid van Moolenbroek char path[PATH_MAX], name[NAME_MAX+1];
12489c9de7dSDavid van Moolenbroek char *ptr, *last;
12589c9de7dSDavid van Moolenbroek cp_grant_id_t path_grant;
12689c9de7dSDavid van Moolenbroek vfs_ucred_t ucred;
12789c9de7dSDavid van Moolenbroek unsigned int flags;
12889c9de7dSDavid van Moolenbroek size_t path_len, path_size;
12989c9de7dSDavid van Moolenbroek int r, r2, going_up, is_mountpt, symloop;
13089c9de7dSDavid van Moolenbroek
13189c9de7dSDavid van Moolenbroek if (fdp->fdr_lookup == NULL)
13289c9de7dSDavid van Moolenbroek return ENOSYS;
13389c9de7dSDavid van Moolenbroek
13489c9de7dSDavid van Moolenbroek dir_ino_nr = m_in->m_vfs_fs_lookup.dir_ino;
13589c9de7dSDavid van Moolenbroek root_ino_nr = m_in->m_vfs_fs_lookup.root_ino;
13689c9de7dSDavid van Moolenbroek path_grant = m_in->m_vfs_fs_lookup.grant_path;
13789c9de7dSDavid van Moolenbroek path_size = m_in->m_vfs_fs_lookup.path_size;
13889c9de7dSDavid van Moolenbroek path_len = m_in->m_vfs_fs_lookup.path_len;
13989c9de7dSDavid van Moolenbroek flags = m_in->m_vfs_fs_lookup.flags;
14089c9de7dSDavid van Moolenbroek
14189c9de7dSDavid van Moolenbroek /* Fetch the path name. */
14289c9de7dSDavid van Moolenbroek if ((r = fsdriver_getname(m_in->m_source, path_grant, path_len, path,
14389c9de7dSDavid van Moolenbroek sizeof(path), FALSE /*not_empty*/)) != OK)
14489c9de7dSDavid van Moolenbroek return r;
14589c9de7dSDavid van Moolenbroek
14689c9de7dSDavid van Moolenbroek /* Fetch the caller's credentials. */
14789c9de7dSDavid van Moolenbroek if (flags & PATH_GET_UCRED) {
14889c9de7dSDavid van Moolenbroek if (m_in->m_vfs_fs_lookup.ucred_size != sizeof(ucred)) {
14989c9de7dSDavid van Moolenbroek printf("fsdriver: bad credential structure\n");
15089c9de7dSDavid van Moolenbroek
15189c9de7dSDavid van Moolenbroek return EINVAL;
15289c9de7dSDavid van Moolenbroek }
15389c9de7dSDavid van Moolenbroek
15489c9de7dSDavid van Moolenbroek if ((r = sys_safecopyfrom(m_in->m_source,
15589c9de7dSDavid van Moolenbroek m_in->m_vfs_fs_lookup.grant_ucred, 0, (vir_bytes)&ucred,
15689c9de7dSDavid van Moolenbroek (phys_bytes)m_in->m_vfs_fs_lookup.ucred_size)) != OK)
15789c9de7dSDavid van Moolenbroek return r;
15889c9de7dSDavid van Moolenbroek } else {
15989c9de7dSDavid van Moolenbroek ucred.vu_uid = m_in->m_vfs_fs_lookup.uid;
16089c9de7dSDavid van Moolenbroek ucred.vu_gid = m_in->m_vfs_fs_lookup.gid;
16189c9de7dSDavid van Moolenbroek ucred.vu_ngroups = 0;
16289c9de7dSDavid van Moolenbroek }
16389c9de7dSDavid van Moolenbroek
16489c9de7dSDavid van Moolenbroek /* Start the actual lookup by referencing the starting inode. */
16589c9de7dSDavid van Moolenbroek strlcpy(name, ".", sizeof(name)); /* allow a non-const argument */
16689c9de7dSDavid van Moolenbroek
16789c9de7dSDavid van Moolenbroek r = fdp->fdr_lookup(dir_ino_nr, name, &cur_node, &is_mountpt);
16889c9de7dSDavid van Moolenbroek if (r != OK)
16989c9de7dSDavid van Moolenbroek return r;
17089c9de7dSDavid van Moolenbroek
17189c9de7dSDavid van Moolenbroek symloop = 0;
17289c9de7dSDavid van Moolenbroek
17389c9de7dSDavid van Moolenbroek /* Whenever we leave this loop, 'cur_node' holds a referenced inode. */
17489c9de7dSDavid van Moolenbroek for (ptr = last = path; *ptr != 0; ) {
17589c9de7dSDavid van Moolenbroek /*
17689c9de7dSDavid van Moolenbroek * Get the next path component. The result is a non-empty
17789c9de7dSDavid van Moolenbroek * string.
17889c9de7dSDavid van Moolenbroek */
17989c9de7dSDavid van Moolenbroek if ((r = next_name(&ptr, &last, name, sizeof(name))) != OK)
18089c9de7dSDavid van Moolenbroek break;
18189c9de7dSDavid van Moolenbroek
18289c9de7dSDavid van Moolenbroek if (is_mountpt) {
18389c9de7dSDavid van Moolenbroek /*
18489c9de7dSDavid van Moolenbroek * If we start off from a mount point, the next path
18589c9de7dSDavid van Moolenbroek * component *must* cause us to go up. Anything else
18689c9de7dSDavid van Moolenbroek * is a protocol violation.
18789c9de7dSDavid van Moolenbroek */
18889c9de7dSDavid van Moolenbroek if (strcmp(name, "..")) {
18989c9de7dSDavid van Moolenbroek r = EINVAL;
19089c9de7dSDavid van Moolenbroek break;
19189c9de7dSDavid van Moolenbroek }
19289c9de7dSDavid van Moolenbroek } else {
19389c9de7dSDavid van Moolenbroek /*
19489c9de7dSDavid van Moolenbroek * There is more path to process. That means that the
19589c9de7dSDavid van Moolenbroek * current file is now being accessed as a directory.
19689c9de7dSDavid van Moolenbroek * Check type and permissions.
19789c9de7dSDavid van Moolenbroek */
19889c9de7dSDavid van Moolenbroek if ((r = access_as_dir(&cur_node, &ucred)) != OK)
19989c9de7dSDavid van Moolenbroek break;
20089c9de7dSDavid van Moolenbroek }
20189c9de7dSDavid van Moolenbroek
20289c9de7dSDavid van Moolenbroek /* A single-dot component resolves to the current directory. */
20389c9de7dSDavid van Moolenbroek if (!strcmp(name, "."))
20489c9de7dSDavid van Moolenbroek continue;
20589c9de7dSDavid van Moolenbroek
20689c9de7dSDavid van Moolenbroek /* A dot-dot component resolves to the parent directory. */
20789c9de7dSDavid van Moolenbroek going_up = !strcmp(name, "..");
20889c9de7dSDavid van Moolenbroek
20989c9de7dSDavid van Moolenbroek if (going_up) {
21089c9de7dSDavid van Moolenbroek /*
21189c9de7dSDavid van Moolenbroek * The parent of the process's root directory is the
21289c9de7dSDavid van Moolenbroek * same root directory. All processes have a root
21389c9de7dSDavid van Moolenbroek * directory, so this check also covers the case of
21489c9de7dSDavid van Moolenbroek * going up from the global system root directory.
21589c9de7dSDavid van Moolenbroek */
21689c9de7dSDavid van Moolenbroek if (cur_node.fn_ino_nr == root_ino_nr)
21789c9de7dSDavid van Moolenbroek continue;
21889c9de7dSDavid van Moolenbroek
21989c9de7dSDavid van Moolenbroek /*
22089c9de7dSDavid van Moolenbroek * Going up from the file system's root directory means
22189c9de7dSDavid van Moolenbroek * crossing mount points. As indicated, the root file
22289c9de7dSDavid van Moolenbroek * system is already covered by the check above.
22389c9de7dSDavid van Moolenbroek */
22489c9de7dSDavid van Moolenbroek if (cur_node.fn_ino_nr == fsdriver_root) {
22589c9de7dSDavid van Moolenbroek ptr = last;
22689c9de7dSDavid van Moolenbroek
22789c9de7dSDavid van Moolenbroek r = ELEAVEMOUNT;
22889c9de7dSDavid van Moolenbroek break;
22989c9de7dSDavid van Moolenbroek }
23089c9de7dSDavid van Moolenbroek }
23189c9de7dSDavid van Moolenbroek
23289c9de7dSDavid van Moolenbroek /*
23389c9de7dSDavid van Moolenbroek * Descend into a child node or go up to a parent node, by
23489c9de7dSDavid van Moolenbroek * asking the actual file system to perform a one-step
23589c9de7dSDavid van Moolenbroek * resolution. The result, if successful, is an open
23689c9de7dSDavid van Moolenbroek * (referenced) inode.
23789c9de7dSDavid van Moolenbroek */
23889c9de7dSDavid van Moolenbroek if ((r = fdp->fdr_lookup(cur_node.fn_ino_nr, name, &next_node,
23989c9de7dSDavid van Moolenbroek &is_mountpt)) != OK)
24089c9de7dSDavid van Moolenbroek break;
24189c9de7dSDavid van Moolenbroek
24289c9de7dSDavid van Moolenbroek /* Sanity check: a parent node must always be a directory. */
24389c9de7dSDavid van Moolenbroek if (going_up && !S_ISDIR(next_node.fn_mode))
24489c9de7dSDavid van Moolenbroek panic("fsdriver: ascending into nondirectory");
24589c9de7dSDavid van Moolenbroek
24689c9de7dSDavid van Moolenbroek /*
24789c9de7dSDavid van Moolenbroek * Perform symlink resolution, unless the symlink is the last
24889c9de7dSDavid van Moolenbroek * path component and VFS is asking us not to resolve it.
24989c9de7dSDavid van Moolenbroek */
25089c9de7dSDavid van Moolenbroek if (S_ISLNK(next_node.fn_mode) &&
25189c9de7dSDavid van Moolenbroek (*ptr || !(flags & PATH_RET_SYMLINK))) {
25289c9de7dSDavid van Moolenbroek /*
25389c9de7dSDavid van Moolenbroek * Resolve the symlink, and append the remaining
25489c9de7dSDavid van Moolenbroek * unresolved part of the path.
25589c9de7dSDavid van Moolenbroek */
25689c9de7dSDavid van Moolenbroek if (++symloop < _POSIX_SYMLOOP_MAX)
25789c9de7dSDavid van Moolenbroek r = resolve_link(fdp, next_node.fn_ino_nr,
25889c9de7dSDavid van Moolenbroek path, sizeof(path), ptr);
25989c9de7dSDavid van Moolenbroek else
26089c9de7dSDavid van Moolenbroek r = ELOOP;
26189c9de7dSDavid van Moolenbroek
262*3f30eb69SDavid van Moolenbroek if (fdp->fdr_putnode != NULL)
26389c9de7dSDavid van Moolenbroek fdp->fdr_putnode(next_node.fn_ino_nr, 1);
26489c9de7dSDavid van Moolenbroek
26589c9de7dSDavid van Moolenbroek if (r != OK)
26689c9de7dSDavid van Moolenbroek break;
26789c9de7dSDavid van Moolenbroek
26889c9de7dSDavid van Moolenbroek ptr = path;
26989c9de7dSDavid van Moolenbroek
27089c9de7dSDavid van Moolenbroek /* If the symlink is absolute, return it to VFS. */
27189c9de7dSDavid van Moolenbroek if (path[0] == '/') {
27289c9de7dSDavid van Moolenbroek r = ESYMLINK;
27389c9de7dSDavid van Moolenbroek break;
27489c9de7dSDavid van Moolenbroek }
27589c9de7dSDavid van Moolenbroek
27689c9de7dSDavid van Moolenbroek continue;
27789c9de7dSDavid van Moolenbroek }
27889c9de7dSDavid van Moolenbroek
27989c9de7dSDavid van Moolenbroek /* We have found a new node. Continue from this node. */
280*3f30eb69SDavid van Moolenbroek if (fdp->fdr_putnode != NULL)
28189c9de7dSDavid van Moolenbroek fdp->fdr_putnode(cur_node.fn_ino_nr, 1);
28289c9de7dSDavid van Moolenbroek
28389c9de7dSDavid van Moolenbroek cur_node = next_node;
28489c9de7dSDavid van Moolenbroek
28589c9de7dSDavid van Moolenbroek /*
28689c9de7dSDavid van Moolenbroek * If the new node is a mount point, yield to another file
28789c9de7dSDavid van Moolenbroek * system.
28889c9de7dSDavid van Moolenbroek */
28989c9de7dSDavid van Moolenbroek if (is_mountpt) {
29089c9de7dSDavid van Moolenbroek r = EENTERMOUNT;
29189c9de7dSDavid van Moolenbroek break;
29289c9de7dSDavid van Moolenbroek }
29389c9de7dSDavid van Moolenbroek }
29489c9de7dSDavid van Moolenbroek
29589c9de7dSDavid van Moolenbroek /* For special redirection errors, we need to return extra details. */
29689c9de7dSDavid van Moolenbroek if (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) {
29789c9de7dSDavid van Moolenbroek /* Copy back the path if we resolved at least one symlink. */
29889c9de7dSDavid van Moolenbroek if (symloop > 0) {
29989c9de7dSDavid van Moolenbroek if ((path_len = strlen(path) + 1) > path_size)
30089c9de7dSDavid van Moolenbroek return ENAMETOOLONG;
30189c9de7dSDavid van Moolenbroek
30289c9de7dSDavid van Moolenbroek r2 = sys_safecopyto(m_in->m_source, path_grant, 0,
30389c9de7dSDavid van Moolenbroek (vir_bytes)path, (phys_bytes)path_len);
30489c9de7dSDavid van Moolenbroek } else
30589c9de7dSDavid van Moolenbroek r2 = OK;
30689c9de7dSDavid van Moolenbroek
30789c9de7dSDavid van Moolenbroek if (r2 == OK) {
30889c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.offset = (int)(ptr - path);
30989c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.symloop = symloop;
31089c9de7dSDavid van Moolenbroek
31189c9de7dSDavid van Moolenbroek if (r == EENTERMOUNT)
31289c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.inode =
31389c9de7dSDavid van Moolenbroek cur_node.fn_ino_nr;
31489c9de7dSDavid van Moolenbroek } else
31589c9de7dSDavid van Moolenbroek r = r2;
31689c9de7dSDavid van Moolenbroek }
31789c9de7dSDavid van Moolenbroek
31889c9de7dSDavid van Moolenbroek /*
31989c9de7dSDavid van Moolenbroek * On success, leave the resulting file open and return its details.
32089c9de7dSDavid van Moolenbroek * If an error occurred, close the file and return error information.
32189c9de7dSDavid van Moolenbroek */
32289c9de7dSDavid van Moolenbroek if (r == OK) {
32389c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.inode = cur_node.fn_ino_nr;
32489c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.mode = cur_node.fn_mode;
32589c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.file_size = cur_node.fn_size;
32689c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.uid = cur_node.fn_uid;
32789c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.gid = cur_node.fn_gid;
32889c9de7dSDavid van Moolenbroek m_out->m_fs_vfs_lookup.device = cur_node.fn_dev;
329*3f30eb69SDavid van Moolenbroek } else if (fdp->fdr_putnode != NULL)
33089c9de7dSDavid van Moolenbroek fdp->fdr_putnode(cur_node.fn_ino_nr, 1);
33189c9de7dSDavid van Moolenbroek
33289c9de7dSDavid van Moolenbroek return r;
33389c9de7dSDavid van Moolenbroek }
334