xref: /minix3/minix/lib/libfsdriver/lookup.c (revision 3f30eb69f06e23d35b1afcf262f8d5e2c83777c5)
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