xref: /minix3/minix/servers/vfs/path.c (revision 27852ebe53d5bf221cf5058cb7e858fa8fa8895e)
1433d6423SLionel Sambuc /* lookup() is the main routine that controls the path name lookup. It
2433d6423SLionel Sambuc  * handles mountpoints and symbolic links. The actual lookup requests
3433d6423SLionel Sambuc  * are sent through the req_lookup wrapper function.
4433d6423SLionel Sambuc  */
5433d6423SLionel Sambuc 
6433d6423SLionel Sambuc #include "fs.h"
7433d6423SLionel Sambuc #include <string.h>
8433d6423SLionel Sambuc #include <minix/callnr.h>
9433d6423SLionel Sambuc #include <minix/com.h>
10433d6423SLionel Sambuc #include <minix/const.h>
11433d6423SLionel Sambuc #include <minix/endpoint.h>
12433d6423SLionel Sambuc #include <stddef.h>
13433d6423SLionel Sambuc #include <unistd.h>
14433d6423SLionel Sambuc #include <assert.h>
15433d6423SLionel Sambuc #include <minix/vfsif.h>
16433d6423SLionel Sambuc #include <sys/param.h>
17433d6423SLionel Sambuc #include <sys/stat.h>
18433d6423SLionel Sambuc #include <sys/dirent.h>
19433d6423SLionel Sambuc #include "vmnt.h"
20433d6423SLionel Sambuc #include "vnode.h"
21433d6423SLionel Sambuc #include "path.h"
22433d6423SLionel Sambuc 
23433d6423SLionel Sambuc /* Set to following define to 1 if you really want to use the POSIX definition
24433d6423SLionel Sambuc  * (IEEE Std 1003.1, 2004) of pathname resolution. POSIX requires pathnames
25433d6423SLionel Sambuc  * with a traling slash (and that do not entirely consist of slash characters)
26433d6423SLionel Sambuc  * to be treated as if a single dot is appended. This means that for example
27433d6423SLionel Sambuc  * mkdir("dir/", ...) and rmdir("dir/") will fail because the call tries to
28433d6423SLionel Sambuc  * create or remove the directory '.'. Historically, Unix systems just ignore
29433d6423SLionel Sambuc  * trailing slashes.
30433d6423SLionel Sambuc  */
31433d6423SLionel Sambuc #define DO_POSIX_PATHNAME_RES	0
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc static int lookup(struct vnode *dirp, struct lookup *resolve,
34433d6423SLionel Sambuc 	node_details_t *node, struct fproc *rfp);
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc /*===========================================================================*
37433d6423SLionel Sambuc  *				advance					     *
38433d6423SLionel Sambuc  *===========================================================================*/
39a0814afbSRichard Sailer struct vnode *
advance(struct vnode * dirp,struct lookup * resolve,struct fproc * rfp)40a0814afbSRichard Sailer advance(struct vnode *dirp, struct lookup *resolve, struct fproc *rfp)
41433d6423SLionel Sambuc {
42433d6423SLionel Sambuc /* Resolve a path name starting at dirp to a vnode. */
43433d6423SLionel Sambuc   int r;
44433d6423SLionel Sambuc   int do_downgrade = 1;
45433d6423SLionel Sambuc   struct vnode *new_vp, *vp;
46433d6423SLionel Sambuc   struct vmnt *vmp;
47433d6423SLionel Sambuc   struct node_details res = {0,0,0,0,0,0,0};
48433d6423SLionel Sambuc   tll_access_t initial_locktype;
49433d6423SLionel Sambuc 
50433d6423SLionel Sambuc   assert(dirp);
51433d6423SLionel Sambuc   assert(resolve->l_vnode_lock != TLL_NONE);
52433d6423SLionel Sambuc   assert(resolve->l_vmnt_lock != TLL_NONE);
53433d6423SLionel Sambuc 
54433d6423SLionel Sambuc   if (resolve->l_vnode_lock == VNODE_READ)
55433d6423SLionel Sambuc 	initial_locktype = VNODE_OPCL;
56433d6423SLionel Sambuc   else
57433d6423SLionel Sambuc 	initial_locktype = resolve->l_vnode_lock;
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc   /* Get a free vnode and lock it */
60433d6423SLionel Sambuc   if ((new_vp = get_free_vnode()) == NULL) return(NULL);
61433d6423SLionel Sambuc   lock_vnode(new_vp, initial_locktype);
62433d6423SLionel Sambuc 
63433d6423SLionel Sambuc   /* Lookup vnode belonging to the file. */
64433d6423SLionel Sambuc   if ((r = lookup(dirp, resolve, &res, rfp)) != OK) {
65433d6423SLionel Sambuc 	err_code = r;
66433d6423SLionel Sambuc 	unlock_vnode(new_vp);
67433d6423SLionel Sambuc 	return(NULL);
68433d6423SLionel Sambuc   }
69433d6423SLionel Sambuc 
70433d6423SLionel Sambuc   /* Check whether we already have a vnode for that file */
71433d6423SLionel Sambuc   if ((vp = find_vnode(res.fs_e, res.inode_nr)) != NULL) {
72433d6423SLionel Sambuc 	unlock_vnode(new_vp);	/* Don't need this anymore */
73433d6423SLionel Sambuc 	do_downgrade = (lock_vnode(vp, initial_locktype) != EBUSY);
74433d6423SLionel Sambuc 
75433d6423SLionel Sambuc 	/* Unfortunately, by the time we get the lock, another thread might've
76433d6423SLionel Sambuc 	 * rid of the vnode (e.g., find_vnode found the vnode while a
77433d6423SLionel Sambuc 	 * req_putnode was being processed). */
78433d6423SLionel Sambuc 	if (vp->v_ref_count == 0) { /* vnode vanished! */
79433d6423SLionel Sambuc 		/* As the lookup before increased the usage counters in the FS,
80433d6423SLionel Sambuc 		 * we can simply set the usage counters to 1 and proceed as
81433d6423SLionel Sambuc 		 * normal, because the putnode resulted in a use count of 1 in
82433d6423SLionel Sambuc 		 * the FS. Other data is still valid, because the vnode was
83433d6423SLionel Sambuc 		 * marked as pending lock, so get_free_vnode hasn't
84433d6423SLionel Sambuc 		 * reinitialized the vnode yet. */
85433d6423SLionel Sambuc 		vp->v_fs_count = 1;
86433d6423SLionel Sambuc 		if (vp->v_mapfs_e != NONE) vp->v_mapfs_count = 1;
87433d6423SLionel Sambuc 	} else {
88433d6423SLionel Sambuc 		vp->v_fs_count++;	/* We got a reference from the FS */
89433d6423SLionel Sambuc 	}
90433d6423SLionel Sambuc 
91433d6423SLionel Sambuc   } else {
92433d6423SLionel Sambuc 	/* Vnode not found, fill in the free vnode's fields */
93433d6423SLionel Sambuc 
94433d6423SLionel Sambuc 	new_vp->v_fs_e = res.fs_e;
95433d6423SLionel Sambuc 	new_vp->v_inode_nr = res.inode_nr;
96433d6423SLionel Sambuc 	new_vp->v_mode = res.fmode;
97433d6423SLionel Sambuc 	new_vp->v_size = res.fsize;
98433d6423SLionel Sambuc 	new_vp->v_uid = res.uid;
99433d6423SLionel Sambuc 	new_vp->v_gid = res.gid;
100433d6423SLionel Sambuc 	new_vp->v_sdev = res.dev;
101433d6423SLionel Sambuc 
102433d6423SLionel Sambuc 	if( (vmp = find_vmnt(new_vp->v_fs_e)) == NULL)
103433d6423SLionel Sambuc 		  panic("advance: vmnt not found");
104433d6423SLionel Sambuc 
105433d6423SLionel Sambuc 	new_vp->v_vmnt = vmp;
106433d6423SLionel Sambuc 	new_vp->v_dev = vmp->m_dev;
107433d6423SLionel Sambuc 	new_vp->v_fs_count = 1;
108433d6423SLionel Sambuc 
109433d6423SLionel Sambuc 	vp = new_vp;
110433d6423SLionel Sambuc   }
111433d6423SLionel Sambuc 
112433d6423SLionel Sambuc   dup_vnode(vp);
113433d6423SLionel Sambuc   if (do_downgrade) {
114433d6423SLionel Sambuc 	/* Only downgrade a lock if we managed to lock it in the first place */
115433d6423SLionel Sambuc 	*(resolve->l_vnode) = vp;
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc 	if (initial_locktype != resolve->l_vnode_lock)
118433d6423SLionel Sambuc 		tll_downgrade(&vp->v_lock);
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc #if LOCK_DEBUG
121433d6423SLionel Sambuc 	if (resolve->l_vnode_lock == VNODE_READ)
122433d6423SLionel Sambuc 		fp->fp_vp_rdlocks++;
123433d6423SLionel Sambuc #endif
124433d6423SLionel Sambuc   }
125433d6423SLionel Sambuc 
126433d6423SLionel Sambuc   return(vp);
127433d6423SLionel Sambuc }
128433d6423SLionel Sambuc 
129433d6423SLionel Sambuc /*===========================================================================*
130433d6423SLionel Sambuc  *				eat_path				     *
131433d6423SLionel Sambuc  *===========================================================================*/
132a0814afbSRichard Sailer struct vnode *
eat_path(struct lookup * resolve,struct fproc * rfp)133a0814afbSRichard Sailer eat_path(struct lookup *resolve, struct fproc *rfp)
134433d6423SLionel Sambuc {
135433d6423SLionel Sambuc /* Resolve path to a vnode. advance does the actual work. */
136433d6423SLionel Sambuc   struct vnode *start_dir;
137433d6423SLionel Sambuc 
138433d6423SLionel Sambuc   start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd : rfp->fp_wd);
139433d6423SLionel Sambuc   return advance(start_dir, resolve, rfp);
140433d6423SLionel Sambuc }
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc /*===========================================================================*
143433d6423SLionel Sambuc  *				last_dir				     *
144433d6423SLionel Sambuc  *===========================================================================*/
145a0814afbSRichard Sailer struct vnode *
last_dir(struct lookup * resolve,struct fproc * rfp)146a0814afbSRichard Sailer last_dir(struct lookup *resolve, struct fproc *rfp)
147433d6423SLionel Sambuc {
148433d6423SLionel Sambuc /* Parse a path, as far as the last directory, fetch the vnode
149433d6423SLionel Sambuc  * for the last directory into the vnode table, and return a pointer to the
150433d6423SLionel Sambuc  * vnode. In addition, return the final component of the path in 'string'. If
151433d6423SLionel Sambuc  * the last directory can't be opened, return NULL and the reason for
152433d6423SLionel Sambuc  * failure in 'err_code'. We can't parse component by component as that would
153433d6423SLionel Sambuc  * be too expensive. Alternatively, we cut off the last component of the path,
154433d6423SLionel Sambuc  * and parse the path up to the penultimate component.
155433d6423SLionel Sambuc  */
156433d6423SLionel Sambuc 
157433d6423SLionel Sambuc   size_t len;
158433d6423SLionel Sambuc   char *cp;
159433d6423SLionel Sambuc   char dir_entry[NAME_MAX+1];
160433d6423SLionel Sambuc   struct vnode *start_dir, *res_vp, *sym_vp, *sym_vp_l, *loop_start;
161433d6423SLionel Sambuc   struct vmnt *sym_vmp = NULL;
162433d6423SLionel Sambuc   int r, symloop = 0, ret_on_symlink = 0;
163433d6423SLionel Sambuc   struct lookup symlink;
164433d6423SLionel Sambuc 
165433d6423SLionel Sambuc   *resolve->l_vnode = NULL;
166433d6423SLionel Sambuc   *resolve->l_vmp = NULL;
167433d6423SLionel Sambuc   loop_start = NULL;
168433d6423SLionel Sambuc   sym_vp = NULL;
169433d6423SLionel Sambuc 
170433d6423SLionel Sambuc   ret_on_symlink = !!(resolve->l_flags & PATH_RET_SYMLINK);
171433d6423SLionel Sambuc 
172433d6423SLionel Sambuc   do {
173433d6423SLionel Sambuc 	/* Is the path absolute or relative? Initialize 'start_dir'
174433d6423SLionel Sambuc 	 * accordingly. Use loop_start in case we're looping.
175433d6423SLionel Sambuc 	 */
176433d6423SLionel Sambuc 	if (loop_start != NULL)
177433d6423SLionel Sambuc 		start_dir = loop_start;
178433d6423SLionel Sambuc 	else
179433d6423SLionel Sambuc 		start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd:rfp->fp_wd);
180433d6423SLionel Sambuc 
181433d6423SLionel Sambuc 	len = strlen(resolve->l_path);
182433d6423SLionel Sambuc 
183433d6423SLionel Sambuc 	/* If path is empty, return ENOENT. */
184433d6423SLionel Sambuc 	if (len == 0)	{
185433d6423SLionel Sambuc 		err_code = ENOENT;
186433d6423SLionel Sambuc 		res_vp = NULL;
187433d6423SLionel Sambuc 		break;
188433d6423SLionel Sambuc 	}
189433d6423SLionel Sambuc 
190433d6423SLionel Sambuc #if !DO_POSIX_PATHNAME_RES
191433d6423SLionel Sambuc 	/* Remove trailing slashes */
192433d6423SLionel Sambuc 	while (len > 1 && resolve->l_path[len-1] == '/') {
193433d6423SLionel Sambuc 		len--;
194433d6423SLionel Sambuc 		resolve->l_path[len]= '\0';
195433d6423SLionel Sambuc 	}
196433d6423SLionel Sambuc #endif
197433d6423SLionel Sambuc 
198433d6423SLionel Sambuc 	cp = strrchr(resolve->l_path, '/');
199433d6423SLionel Sambuc 	if (cp == NULL) {
200433d6423SLionel Sambuc 		/* Just an entry in the current working directory. Prepend
201433d6423SLionel Sambuc 		 * "./" in front of the path and resolve it.
202433d6423SLionel Sambuc 		 */
203433d6423SLionel Sambuc 		if (strlcpy(dir_entry, resolve->l_path, NAME_MAX+1) >= NAME_MAX + 1) {
204433d6423SLionel Sambuc 			err_code = ENAMETOOLONG;
205433d6423SLionel Sambuc 			res_vp = NULL;
206433d6423SLionel Sambuc 			break;
207433d6423SLionel Sambuc 		}
208433d6423SLionel Sambuc 		dir_entry[NAME_MAX] = '\0';
209433d6423SLionel Sambuc 		resolve->l_path[0] = '.';
210433d6423SLionel Sambuc 		resolve->l_path[1] = '\0';
211433d6423SLionel Sambuc 	} else if (cp[1] == '\0') {
212433d6423SLionel Sambuc 		/* Path ends in a slash. The directory entry is '.' */
213433d6423SLionel Sambuc 		strlcpy(dir_entry, ".", NAME_MAX+1);
214433d6423SLionel Sambuc 	} else {
215433d6423SLionel Sambuc 		/* A path name for the directory and a directory entry */
216433d6423SLionel Sambuc 		if (strlcpy(dir_entry, cp+1, NAME_MAX+1) >= NAME_MAX + 1) {
217433d6423SLionel Sambuc 			err_code = ENAMETOOLONG;
218433d6423SLionel Sambuc 			res_vp = NULL;
219433d6423SLionel Sambuc 			break;
220433d6423SLionel Sambuc 		}
221433d6423SLionel Sambuc 		cp[1] = '\0';
222433d6423SLionel Sambuc 		dir_entry[NAME_MAX] = '\0';
223433d6423SLionel Sambuc 	}
224433d6423SLionel Sambuc 
225433d6423SLionel Sambuc 	/* Remove trailing slashes */
226433d6423SLionel Sambuc 	while (cp > resolve->l_path && cp[0] == '/') {
227433d6423SLionel Sambuc 		cp[0]= '\0';
228433d6423SLionel Sambuc 		cp--;
229433d6423SLionel Sambuc 	}
230433d6423SLionel Sambuc 
231433d6423SLionel Sambuc 	/* Resolve up to and including the last directory of the path. Turn off
232433d6423SLionel Sambuc 	 * PATH_RET_SYMLINK, because we do want to follow the symlink in this
233433d6423SLionel Sambuc 	 * case. That is, the flag is meant for the actual filename of the path,
234433d6423SLionel Sambuc 	 * not the last directory.
235433d6423SLionel Sambuc 	 */
236433d6423SLionel Sambuc 	resolve->l_flags &= ~PATH_RET_SYMLINK;
237433d6423SLionel Sambuc 	if ((res_vp = advance(start_dir, resolve, rfp)) == NULL) {
238433d6423SLionel Sambuc 		break;
239433d6423SLionel Sambuc 	}
240433d6423SLionel Sambuc 
241433d6423SLionel Sambuc 	/* If the directory entry is not a symlink we're done now. If it is a
242433d6423SLionel Sambuc 	 * symlink, then we're not at the last directory, yet. */
243433d6423SLionel Sambuc 
244433d6423SLionel Sambuc 	/* Copy the directory entry back to user_fullpath */
245433d6423SLionel Sambuc 	strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1);
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc 	/* Look up the directory entry, but do not follow the symlink when it
248433d6423SLionel Sambuc 	 * is one. Note: depending on the previous advance, we might not be
249433d6423SLionel Sambuc 	 * able to lock the resulting vnode. For example, when we look up "./."
250433d6423SLionel Sambuc 	 * and request a VNODE_WRITE lock on the result, then the previous
251433d6423SLionel Sambuc 	 * advance has "./" locked. The next advance to "." will try to lock
252433d6423SLionel Sambuc 	 * the same vnode with a VNODE_READ lock, and fail. When that happens,
253433d6423SLionel Sambuc 	 * sym_vp_l will be NULL and we must not unlock the vnode. If we would
254433d6423SLionel Sambuc 	 * unlock, we actually unlock the vnode locked by the previous advance.
255433d6423SLionel Sambuc 	 */
256433d6423SLionel Sambuc 	lookup_init(&symlink, resolve->l_path,
257433d6423SLionel Sambuc 		    resolve->l_flags|PATH_RET_SYMLINK, &sym_vmp, &sym_vp_l);
258433d6423SLionel Sambuc 	symlink.l_vmnt_lock = VMNT_READ;
259433d6423SLionel Sambuc 	symlink.l_vnode_lock = VNODE_READ;
260433d6423SLionel Sambuc 	sym_vp = advance(res_vp, &symlink, rfp);
261433d6423SLionel Sambuc 
262433d6423SLionel Sambuc 	if (sym_vp == NULL) break;
263433d6423SLionel Sambuc 
264433d6423SLionel Sambuc 	if (S_ISLNK(sym_vp->v_mode)) {
265433d6423SLionel Sambuc 		/* Last component is a symlink, but if we've been asked to not
266433d6423SLionel Sambuc 		 * resolve it, return now.
267433d6423SLionel Sambuc 		 */
268433d6423SLionel Sambuc 		if (ret_on_symlink) {
269433d6423SLionel Sambuc 			break;
270433d6423SLionel Sambuc 		}
271433d6423SLionel Sambuc 
272433d6423SLionel Sambuc 		r = req_rdlink(sym_vp->v_fs_e, sym_vp->v_inode_nr, NONE,
273433d6423SLionel Sambuc 				(vir_bytes) resolve->l_path, PATH_MAX - 1, 1);
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc 		if (r < 0) {
276433d6423SLionel Sambuc 			/* Failed to read link */
277433d6423SLionel Sambuc 			err_code = r;
278433d6423SLionel Sambuc 			unlock_vnode(res_vp);
279433d6423SLionel Sambuc 			unlock_vmnt(*resolve->l_vmp);
280433d6423SLionel Sambuc 			put_vnode(res_vp);
281433d6423SLionel Sambuc 			*resolve->l_vmp = NULL;
282433d6423SLionel Sambuc 			*resolve->l_vnode = NULL;
283433d6423SLionel Sambuc 			res_vp = NULL;
284433d6423SLionel Sambuc 			break;
285433d6423SLionel Sambuc 		}
286433d6423SLionel Sambuc 		resolve->l_path[r] = '\0';
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc 		if (strrchr(resolve->l_path, '/') != NULL) {
289433d6423SLionel Sambuc 			if (sym_vp_l != NULL)
290433d6423SLionel Sambuc 				unlock_vnode(sym_vp);
291433d6423SLionel Sambuc 			unlock_vmnt(*resolve->l_vmp);
292433d6423SLionel Sambuc 			if (sym_vmp != NULL)
293433d6423SLionel Sambuc 				unlock_vmnt(sym_vmp);
294433d6423SLionel Sambuc 			*resolve->l_vmp = NULL;
295433d6423SLionel Sambuc 			put_vnode(sym_vp);
296433d6423SLionel Sambuc 			sym_vp = NULL;
297433d6423SLionel Sambuc 
298433d6423SLionel Sambuc 			symloop++;
299433d6423SLionel Sambuc 
300433d6423SLionel Sambuc 			/* Relative symlinks are relative to res_vp, not cwd */
301433d6423SLionel Sambuc 			if (resolve->l_path[0] != '/') {
302433d6423SLionel Sambuc 				loop_start = res_vp;
303433d6423SLionel Sambuc 			} else {
304433d6423SLionel Sambuc 				/* Absolute symlink, forget about res_vp */
305433d6423SLionel Sambuc 				unlock_vnode(res_vp);
306433d6423SLionel Sambuc 				put_vnode(res_vp);
307433d6423SLionel Sambuc 			}
308433d6423SLionel Sambuc 
309433d6423SLionel Sambuc 			continue;
310433d6423SLionel Sambuc 		}
311433d6423SLionel Sambuc 	} else {
312433d6423SLionel Sambuc 		symloop = 0;	/* Not a symlink, so restart counting */
313433d6423SLionel Sambuc 
314433d6423SLionel Sambuc 		/* If we're crossing a mount point, return root node of mount
315433d6423SLionel Sambuc 		 * point on which the file resides. That's the 'real' last
316433d6423SLionel Sambuc 		 * dir that holds the file we're looking for.
317433d6423SLionel Sambuc 		 */
318433d6423SLionel Sambuc 		if (sym_vp->v_fs_e != res_vp->v_fs_e) {
319433d6423SLionel Sambuc 			assert(sym_vmp != NULL);
320433d6423SLionel Sambuc 
321433d6423SLionel Sambuc 			/* Unlock final file, it might have wrong lock types */
322433d6423SLionel Sambuc 			if (sym_vp_l != NULL)
323433d6423SLionel Sambuc 				unlock_vnode(sym_vp);
324433d6423SLionel Sambuc 			unlock_vmnt(sym_vmp);
325433d6423SLionel Sambuc 			put_vnode(sym_vp);
326433d6423SLionel Sambuc 			sym_vp = NULL;
327433d6423SLionel Sambuc 
328433d6423SLionel Sambuc 			/* Also unlock and release erroneous result */
329433d6423SLionel Sambuc 			unlock_vnode(*resolve->l_vnode);
330433d6423SLionel Sambuc 			unlock_vmnt(*resolve->l_vmp);
331433d6423SLionel Sambuc 			put_vnode(res_vp);
332433d6423SLionel Sambuc 
333433d6423SLionel Sambuc 			/* Relock vmnt and vnode with correct lock types */
334433d6423SLionel Sambuc 			lock_vmnt(sym_vmp, resolve->l_vmnt_lock);
335433d6423SLionel Sambuc 			lock_vnode(sym_vmp->m_root_node, resolve->l_vnode_lock);
336433d6423SLionel Sambuc 			res_vp = sym_vmp->m_root_node;
337433d6423SLionel Sambuc 			dup_vnode(res_vp);
338433d6423SLionel Sambuc 			*resolve->l_vnode = res_vp;
339433d6423SLionel Sambuc 			*resolve->l_vmp = sym_vmp;
340433d6423SLionel Sambuc 
341433d6423SLionel Sambuc 			/* We've effectively resolved the final component, so
342433d6423SLionel Sambuc 			 * change it to current directory to prevent future
343433d6423SLionel Sambuc 			 * 'advances' of returning erroneous results.
344433d6423SLionel Sambuc 			 */
345433d6423SLionel Sambuc 			strlcpy(dir_entry, ".", NAME_MAX+1);
346433d6423SLionel Sambuc 		}
347433d6423SLionel Sambuc 	}
348433d6423SLionel Sambuc 	break;
349433d6423SLionel Sambuc   } while (symloop < _POSIX_SYMLOOP_MAX);
350433d6423SLionel Sambuc 
351433d6423SLionel Sambuc   if (symloop >= _POSIX_SYMLOOP_MAX) {
352433d6423SLionel Sambuc 	err_code = ELOOP;
353433d6423SLionel Sambuc 	res_vp = NULL;
354433d6423SLionel Sambuc   }
355433d6423SLionel Sambuc 
356433d6423SLionel Sambuc   if (sym_vp != NULL) {
357433d6423SLionel Sambuc 	if (sym_vp_l != NULL) {
358433d6423SLionel Sambuc 		unlock_vnode(sym_vp);
359433d6423SLionel Sambuc 	}
360433d6423SLionel Sambuc 	if (sym_vmp != NULL) {
361433d6423SLionel Sambuc 		unlock_vmnt(sym_vmp);
362433d6423SLionel Sambuc 	}
363433d6423SLionel Sambuc 	put_vnode(sym_vp);
364433d6423SLionel Sambuc   }
365433d6423SLionel Sambuc 
366433d6423SLionel Sambuc   if (loop_start != NULL) {
367433d6423SLionel Sambuc 	unlock_vnode(loop_start);
368433d6423SLionel Sambuc 	put_vnode(loop_start);
369433d6423SLionel Sambuc   }
370433d6423SLionel Sambuc 
371433d6423SLionel Sambuc   /* Copy the directory entry back to user_fullpath */
372433d6423SLionel Sambuc   strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1);
373433d6423SLionel Sambuc 
374433d6423SLionel Sambuc   /* Turn PATH_RET_SYMLINK flag back on if it was on */
375433d6423SLionel Sambuc   if (ret_on_symlink) resolve->l_flags |= PATH_RET_SYMLINK;
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc   return(res_vp);
378433d6423SLionel Sambuc }
379433d6423SLionel Sambuc 
380433d6423SLionel Sambuc /*===========================================================================*
381433d6423SLionel Sambuc  *				lookup					     *
382433d6423SLionel Sambuc  *===========================================================================*/
383a0814afbSRichard Sailer static int
lookup(struct vnode * start_node,struct lookup * resolve,node_details_t * result_node,struct fproc * rfp)384a0814afbSRichard Sailer lookup(struct vnode *start_node, struct lookup *resolve, node_details_t *result_node, struct fproc *rfp)
385433d6423SLionel Sambuc {
386433d6423SLionel Sambuc /* Resolve a path name relative to start_node. */
387433d6423SLionel Sambuc 
388433d6423SLionel Sambuc   int r, symloop;
389433d6423SLionel Sambuc   endpoint_t fs_e;
390433d6423SLionel Sambuc   size_t path_off, path_left_len;
391433d6423SLionel Sambuc   ino_t dir_ino, root_ino;
392433d6423SLionel Sambuc   uid_t uid;
393433d6423SLionel Sambuc   gid_t gid;
394433d6423SLionel Sambuc   struct vnode *dir_vp;
395433d6423SLionel Sambuc   struct vmnt *vmp, *vmpres;
396433d6423SLionel Sambuc   struct lookup_res res;
397433d6423SLionel Sambuc   tll_access_t mnt_lock_type;
398433d6423SLionel Sambuc 
399433d6423SLionel Sambuc   assert(resolve->l_vmp);
400433d6423SLionel Sambuc   assert(resolve->l_vnode);
401433d6423SLionel Sambuc 
402433d6423SLionel Sambuc   *(resolve->l_vmp) = vmpres = NULL; /* No vmnt found nor locked yet */
403433d6423SLionel Sambuc 
404433d6423SLionel Sambuc   /* Empty (start) path? */
405433d6423SLionel Sambuc   if (resolve->l_path[0] == '\0') {
406433d6423SLionel Sambuc 	result_node->inode_nr = 0;
407433d6423SLionel Sambuc 	return(ENOENT);
408433d6423SLionel Sambuc   }
409433d6423SLionel Sambuc 
410433d6423SLionel Sambuc   if (!rfp->fp_rd || !rfp->fp_wd) {
411433d6423SLionel Sambuc 	printf("VFS: lookup %d: no rd/wd\n", rfp->fp_endpoint);
412433d6423SLionel Sambuc 	return(ENOENT);
413433d6423SLionel Sambuc   }
414433d6423SLionel Sambuc 
415433d6423SLionel Sambuc   fs_e = start_node->v_fs_e;
416433d6423SLionel Sambuc   dir_ino = start_node->v_inode_nr;
417433d6423SLionel Sambuc   vmpres = find_vmnt(fs_e);
418433d6423SLionel Sambuc 
419433d6423SLionel Sambuc   if (vmpres == NULL) return(EIO);	/* mountpoint vanished? */
420433d6423SLionel Sambuc 
421433d6423SLionel Sambuc   /* Is the process' root directory on the same partition?,
422433d6423SLionel Sambuc    * if so, set the chroot directory too. */
423e7b4aa7dSDavid van Moolenbroek   if (rfp->fp_rd->v_dev == start_node->v_dev)
424433d6423SLionel Sambuc 	root_ino = rfp->fp_rd->v_inode_nr;
425433d6423SLionel Sambuc   else
426433d6423SLionel Sambuc 	root_ino = 0;
427433d6423SLionel Sambuc 
428433d6423SLionel Sambuc   /* Set user and group ids according to the system call */
429433d6423SLionel Sambuc   uid = (job_call_nr == VFS_ACCESS ? rfp->fp_realuid : rfp->fp_effuid);
430433d6423SLionel Sambuc   gid = (job_call_nr == VFS_ACCESS ? rfp->fp_realgid : rfp->fp_effgid);
431433d6423SLionel Sambuc 
432433d6423SLionel Sambuc   symloop = 0;	/* Number of symlinks seen so far */
433433d6423SLionel Sambuc 
434433d6423SLionel Sambuc   /* Lock vmnt */
435433d6423SLionel Sambuc   if (resolve->l_vmnt_lock == VMNT_READ)
436433d6423SLionel Sambuc 	mnt_lock_type = VMNT_WRITE;
437433d6423SLionel Sambuc   else
438433d6423SLionel Sambuc 	mnt_lock_type = resolve->l_vmnt_lock;
439433d6423SLionel Sambuc 
440433d6423SLionel Sambuc   if ((r = lock_vmnt(vmpres, mnt_lock_type)) != OK) {
441433d6423SLionel Sambuc 	if (r == EBUSY) /* vmnt already locked */
442433d6423SLionel Sambuc 		vmpres = NULL;
443433d6423SLionel Sambuc 	else
444433d6423SLionel Sambuc 		return(r);
445433d6423SLionel Sambuc   }
446433d6423SLionel Sambuc   *(resolve->l_vmp) = vmpres;
447433d6423SLionel Sambuc 
448433d6423SLionel Sambuc   /* Issue the request */
449433d6423SLionel Sambuc   r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp);
450433d6423SLionel Sambuc 
451433d6423SLionel Sambuc   if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) {
452433d6423SLionel Sambuc 	if (vmpres) unlock_vmnt(vmpres);
453433d6423SLionel Sambuc 	*(resolve->l_vmp) = NULL;
454433d6423SLionel Sambuc 	return(r); /* i.e., an error occured */
455433d6423SLionel Sambuc   }
456433d6423SLionel Sambuc 
457433d6423SLionel Sambuc   /* While the response is related to mount control set the
458433d6423SLionel Sambuc    * new requests respectively */
459433d6423SLionel Sambuc   while (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) {
460433d6423SLionel Sambuc 	/* Update user_fullpath to reflect what's left to be parsed. */
461433d6423SLionel Sambuc 	path_off = res.char_processed;
462433d6423SLionel Sambuc 	path_left_len = strlen(&resolve->l_path[path_off]);
463433d6423SLionel Sambuc 	memmove(resolve->l_path, &resolve->l_path[path_off], path_left_len);
464433d6423SLionel Sambuc 	resolve->l_path[path_left_len] = '\0'; /* terminate string */
465433d6423SLionel Sambuc 
466433d6423SLionel Sambuc 	/* Update the current value of the symloop counter */
467433d6423SLionel Sambuc 	symloop += res.symloop;
468433d6423SLionel Sambuc 	if (symloop > _POSIX_SYMLOOP_MAX) {
469433d6423SLionel Sambuc 		if (vmpres) unlock_vmnt(vmpres);
470433d6423SLionel Sambuc 		*(resolve->l_vmp) = NULL;
471433d6423SLionel Sambuc 		return(ELOOP);
472433d6423SLionel Sambuc 	}
473433d6423SLionel Sambuc 
474433d6423SLionel Sambuc 	/* Symlink encountered with absolute path */
475433d6423SLionel Sambuc 	if (r == ESYMLINK) {
476433d6423SLionel Sambuc 		dir_vp = rfp->fp_rd;
477433d6423SLionel Sambuc 		vmp = NULL;
478433d6423SLionel Sambuc 	} else if (r == EENTERMOUNT) {
479433d6423SLionel Sambuc 		/* Entering a new partition */
480433d6423SLionel Sambuc 		dir_vp = NULL;
481433d6423SLionel Sambuc 		/* Start node is now the mounted partition's root node */
482433d6423SLionel Sambuc 		for (vmp = &vmnt[0]; vmp != &vmnt[NR_MNTS]; ++vmp) {
483433d6423SLionel Sambuc 			if (vmp->m_dev != NO_DEV && vmp->m_mounted_on) {
484433d6423SLionel Sambuc 			   if (vmp->m_mounted_on->v_inode_nr == res.inode_nr &&
485433d6423SLionel Sambuc 			       vmp->m_mounted_on->v_fs_e == res.fs_e) {
486433d6423SLionel Sambuc 				dir_vp = vmp->m_root_node;
487433d6423SLionel Sambuc 				break;
488433d6423SLionel Sambuc 			   }
489433d6423SLionel Sambuc 			}
490433d6423SLionel Sambuc 		}
491433d6423SLionel Sambuc 		if (dir_vp == NULL) {
492433d6423SLionel Sambuc 			printf("VFS: path lookup error; root node not found\n");
493433d6423SLionel Sambuc 			if (vmpres) unlock_vmnt(vmpres);
494433d6423SLionel Sambuc 			*(resolve->l_vmp) = NULL;
495433d6423SLionel Sambuc 			return(EIO);
496433d6423SLionel Sambuc 		}
497433d6423SLionel Sambuc 	} else {
498433d6423SLionel Sambuc 		/* Climbing up mount */
499433d6423SLionel Sambuc 		/* Find the vmnt that represents the partition on
500433d6423SLionel Sambuc 		 * which we "climb up". */
501433d6423SLionel Sambuc 		if ((vmp = find_vmnt(res.fs_e)) == NULL) {
502433d6423SLionel Sambuc 			panic("VFS lookup: can't find parent vmnt");
503433d6423SLionel Sambuc 		}
504433d6423SLionel Sambuc 
505433d6423SLionel Sambuc 		/* Make sure that the child FS does not feed a bogus path
506433d6423SLionel Sambuc 		 * to the parent FS. That is, when we climb up the tree, we
507433d6423SLionel Sambuc 		 * must've encountered ".." in the path, and that is exactly
508433d6423SLionel Sambuc 		 * what we're going to feed to the parent */
509433d6423SLionel Sambuc 		if(strncmp(resolve->l_path, "..", 2) != 0 ||
510433d6423SLionel Sambuc 		   (resolve->l_path[2] != '\0' && resolve->l_path[2] != '/')) {
511433d6423SLionel Sambuc 			printf("VFS: bogus path: %s\n", resolve->l_path);
512433d6423SLionel Sambuc 			if (vmpres) unlock_vmnt(vmpres);
513433d6423SLionel Sambuc 			*(resolve->l_vmp) = NULL;
514433d6423SLionel Sambuc 			return(ENOENT);
515433d6423SLionel Sambuc 		}
516433d6423SLionel Sambuc 
517433d6423SLionel Sambuc 		/* Start node is the vnode on which the partition is
518433d6423SLionel Sambuc 		 * mounted */
519433d6423SLionel Sambuc 		dir_vp = vmp->m_mounted_on;
520433d6423SLionel Sambuc 	}
521433d6423SLionel Sambuc 
522433d6423SLionel Sambuc 	/* Set the starting directories inode number and FS endpoint */
523433d6423SLionel Sambuc 	fs_e = dir_vp->v_fs_e;
524433d6423SLionel Sambuc 	dir_ino = dir_vp->v_inode_nr;
525433d6423SLionel Sambuc 
526433d6423SLionel Sambuc 	/* Is the process' root directory on the same partition?,
527433d6423SLionel Sambuc 	 * if so, set the chroot directory too. */
528433d6423SLionel Sambuc 	if (dir_vp->v_dev == rfp->fp_rd->v_dev)
529433d6423SLionel Sambuc 		root_ino = rfp->fp_rd->v_inode_nr;
530433d6423SLionel Sambuc 	else
531433d6423SLionel Sambuc 		root_ino = 0;
532433d6423SLionel Sambuc 
533433d6423SLionel Sambuc 	/* Unlock a previously locked vmnt if locked and lock new vmnt */
534433d6423SLionel Sambuc 	if (vmpres) unlock_vmnt(vmpres);
535433d6423SLionel Sambuc 	vmpres = find_vmnt(fs_e);
536433d6423SLionel Sambuc 	if (vmpres == NULL) return(EIO);	/* mount point vanished? */
537433d6423SLionel Sambuc 	if ((r = lock_vmnt(vmpres, mnt_lock_type)) != OK) {
538433d6423SLionel Sambuc 		if (r == EBUSY)
539433d6423SLionel Sambuc 			vmpres = NULL;	/* Already locked */
540433d6423SLionel Sambuc 		else
541433d6423SLionel Sambuc 			return(r);
542433d6423SLionel Sambuc 	}
543433d6423SLionel Sambuc 	*(resolve->l_vmp) = vmpres;
544433d6423SLionel Sambuc 
545433d6423SLionel Sambuc 	r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp);
546433d6423SLionel Sambuc 
547433d6423SLionel Sambuc 	if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) {
548433d6423SLionel Sambuc 		if (vmpres) unlock_vmnt(vmpres);
549433d6423SLionel Sambuc 		*(resolve->l_vmp) = NULL;
550433d6423SLionel Sambuc 		return(r);
551433d6423SLionel Sambuc 	}
552433d6423SLionel Sambuc   }
553433d6423SLionel Sambuc 
554433d6423SLionel Sambuc   if (*(resolve->l_vmp) != NULL && resolve->l_vmnt_lock != mnt_lock_type) {
555433d6423SLionel Sambuc 	/* downgrade VMNT_WRITE to VMNT_READ */
556433d6423SLionel Sambuc 	downgrade_vmnt_lock(*(resolve->l_vmp));
557433d6423SLionel Sambuc   }
558433d6423SLionel Sambuc 
559433d6423SLionel Sambuc   /* Fill in response fields */
560433d6423SLionel Sambuc   result_node->inode_nr = res.inode_nr;
561433d6423SLionel Sambuc   result_node->fmode = res.fmode;
562433d6423SLionel Sambuc   result_node->fsize = res.fsize;
563433d6423SLionel Sambuc   result_node->dev = res.dev;
564433d6423SLionel Sambuc   result_node->fs_e = res.fs_e;
565433d6423SLionel Sambuc   result_node->uid = res.uid;
566433d6423SLionel Sambuc   result_node->gid = res.gid;
567433d6423SLionel Sambuc 
568433d6423SLionel Sambuc   return(r);
569433d6423SLionel Sambuc }
570433d6423SLionel Sambuc 
571433d6423SLionel Sambuc /*===========================================================================*
572433d6423SLionel Sambuc  *				lookup_init				     *
573433d6423SLionel Sambuc  *===========================================================================*/
574a0814afbSRichard Sailer void
lookup_init(struct lookup * resolve,char * path,int flags,struct vmnt ** vmp,struct vnode ** vp)575a0814afbSRichard Sailer lookup_init(struct lookup *resolve, char *path, int flags, struct vmnt **vmp, struct vnode **vp)
576433d6423SLionel Sambuc {
577433d6423SLionel Sambuc   assert(vmp != NULL);
578433d6423SLionel Sambuc   assert(vp != NULL);
579433d6423SLionel Sambuc 
580433d6423SLionel Sambuc   resolve->l_path = path;
581433d6423SLionel Sambuc   resolve->l_flags = flags;
582433d6423SLionel Sambuc   resolve->l_vmp = vmp;
583433d6423SLionel Sambuc   resolve->l_vnode = vp;
584433d6423SLionel Sambuc   resolve->l_vmnt_lock = TLL_NONE;
585433d6423SLionel Sambuc   resolve->l_vnode_lock = TLL_NONE;
586433d6423SLionel Sambuc   *vmp = NULL;	/* Initialize lookup result to NULL */
587433d6423SLionel Sambuc   *vp = NULL;
588433d6423SLionel Sambuc }
589433d6423SLionel Sambuc 
590433d6423SLionel Sambuc /*===========================================================================*
591433d6423SLionel Sambuc  *				get_name				     *
592433d6423SLionel Sambuc  *===========================================================================*/
593a0814afbSRichard Sailer int
get_name(struct vnode * dirp,struct vnode * entry,char ename[NAME_MAX+1])594a0814afbSRichard Sailer get_name(struct vnode *dirp, struct vnode *entry, char ename[NAME_MAX + 1])
595433d6423SLionel Sambuc {
596433d6423SLionel Sambuc #define DIR_ENTRIES 8
597433d6423SLionel Sambuc #define DIR_ENTRY_SIZE (sizeof(struct dirent) + NAME_MAX)
598433d6423SLionel Sambuc   off_t pos, new_pos;
599433d6423SLionel Sambuc   int r, consumed, totalbytes, name_len;
600433d6423SLionel Sambuc   char buf[DIR_ENTRY_SIZE * DIR_ENTRIES];
601433d6423SLionel Sambuc   struct dirent *cur;
602433d6423SLionel Sambuc 
603433d6423SLionel Sambuc   pos = 0;
604433d6423SLionel Sambuc 
605433d6423SLionel Sambuc   if (!S_ISDIR(dirp->v_mode)) return(EBADF);
606433d6423SLionel Sambuc 
607433d6423SLionel Sambuc   do {
608433d6423SLionel Sambuc 	r = req_getdents(dirp->v_fs_e, dirp->v_inode_nr, pos, (vir_bytes)buf,
609433d6423SLionel Sambuc 		sizeof(buf), &new_pos, 1);
610433d6423SLionel Sambuc 
611433d6423SLionel Sambuc 	if (r == 0) {
612433d6423SLionel Sambuc 		return(ENOENT); /* end of entries -- matching inode !found */
613433d6423SLionel Sambuc 	} else if (r < 0) {
614433d6423SLionel Sambuc 		return(r); /* error */
615433d6423SLionel Sambuc 	}
616433d6423SLionel Sambuc 
617433d6423SLionel Sambuc 	consumed = 0; /* bytes consumed */
618433d6423SLionel Sambuc 	totalbytes = r; /* number of bytes to consume */
619433d6423SLionel Sambuc 
620433d6423SLionel Sambuc 	do {
621433d6423SLionel Sambuc 		cur = (struct dirent *) (buf + consumed);
622433d6423SLionel Sambuc 		name_len = cur->d_reclen - offsetof(struct dirent, d_name) - 1;
623433d6423SLionel Sambuc 
624433d6423SLionel Sambuc 		if(cur->d_name + name_len+1 > &buf[sizeof(buf)])
625433d6423SLionel Sambuc 			return(EINVAL);	/* Rubbish in dir entry */
626433d6423SLionel Sambuc 		if (entry->v_inode_nr == cur->d_fileno) {
627433d6423SLionel Sambuc 			/* found the entry we were looking for */
628433d6423SLionel Sambuc 			int copylen = MIN(name_len + 1, NAME_MAX + 1);
629433d6423SLionel Sambuc 			if (strlcpy(ename, cur->d_name, copylen) >= copylen) {
630433d6423SLionel Sambuc 				return(ENAMETOOLONG);
631433d6423SLionel Sambuc 			}
632433d6423SLionel Sambuc 			ename[NAME_MAX] = '\0';
633433d6423SLionel Sambuc 			return(OK);
634433d6423SLionel Sambuc 		}
635433d6423SLionel Sambuc 
636433d6423SLionel Sambuc 		/* not a match -- move on to the next dirent */
637433d6423SLionel Sambuc 		consumed += cur->d_reclen;
638433d6423SLionel Sambuc 	} while (consumed < totalbytes);
639433d6423SLionel Sambuc 
640433d6423SLionel Sambuc 	pos = new_pos;
641433d6423SLionel Sambuc   } while (1);
642433d6423SLionel Sambuc }
643433d6423SLionel Sambuc 
644433d6423SLionel Sambuc /*===========================================================================*
645433d6423SLionel Sambuc  *				canonical_path				     *
646433d6423SLionel Sambuc  *===========================================================================*/
647a0814afbSRichard Sailer int
canonical_path(char orig_path[PATH_MAX],struct fproc * rfp)648a0814afbSRichard Sailer canonical_path(char orig_path[PATH_MAX], struct fproc *rfp)
649433d6423SLionel Sambuc {
650433d6423SLionel Sambuc /* Find canonical path of a given path */
651433d6423SLionel Sambuc   int len = 0;
652433d6423SLionel Sambuc   int r, symloop = 0;
653433d6423SLionel Sambuc   struct vnode *dir_vp, *parent_dir;
654433d6423SLionel Sambuc   struct vmnt *dir_vmp, *parent_vmp;
655433d6423SLionel Sambuc   char component[NAME_MAX+1];	/* NAME_MAX does /not/ include '\0' */
656433d6423SLionel Sambuc   char temp_path[PATH_MAX];
657433d6423SLionel Sambuc   struct lookup resolve;
658433d6423SLionel Sambuc 
659433d6423SLionel Sambuc   parent_dir = dir_vp = NULL;
660433d6423SLionel Sambuc   parent_vmp = dir_vmp = NULL;
661433d6423SLionel Sambuc   strlcpy(temp_path, orig_path, PATH_MAX);
662433d6423SLionel Sambuc   temp_path[PATH_MAX - 1] = '\0';
663433d6423SLionel Sambuc 
664433d6423SLionel Sambuc   /* First resolve path to the last directory holding the file */
665433d6423SLionel Sambuc   do {
666433d6423SLionel Sambuc 	if (dir_vp) {
667433d6423SLionel Sambuc 		unlock_vnode(dir_vp);
668433d6423SLionel Sambuc 		unlock_vmnt(dir_vmp);
669433d6423SLionel Sambuc 		put_vnode(dir_vp);
670433d6423SLionel Sambuc 	}
671433d6423SLionel Sambuc 
672433d6423SLionel Sambuc 	lookup_init(&resolve, temp_path, PATH_NOFLAGS, &dir_vmp, &dir_vp);
673433d6423SLionel Sambuc 	resolve.l_vmnt_lock = VMNT_READ;
674433d6423SLionel Sambuc 	resolve.l_vnode_lock = VNODE_READ;
675433d6423SLionel Sambuc 	if ((dir_vp = last_dir(&resolve, rfp)) == NULL) return(err_code);
676433d6423SLionel Sambuc 
677433d6423SLionel Sambuc 	/* dir_vp points to dir and resolve path now contains only the
678433d6423SLionel Sambuc 	 * filename.
679433d6423SLionel Sambuc 	 */
680433d6423SLionel Sambuc 	strlcpy(orig_path, temp_path, NAME_MAX+1);	/* Store file name */
681433d6423SLionel Sambuc 
682433d6423SLionel Sambuc 	/* If we're just crossing a mount point, our name has changed to '.' */
683433d6423SLionel Sambuc 	if (!strcmp(orig_path, ".")) orig_path[0] = '\0';
684433d6423SLionel Sambuc 
685433d6423SLionel Sambuc 	/* check if the file is a symlink, if so resolve it */
686433d6423SLionel Sambuc 	r = rdlink_direct(orig_path, temp_path, rfp);
687433d6423SLionel Sambuc 
688433d6423SLionel Sambuc 	if (r <= 0)
689433d6423SLionel Sambuc 		break;
690433d6423SLionel Sambuc 
691433d6423SLionel Sambuc 	/* encountered a symlink -- loop again */
692433d6423SLionel Sambuc 	strlcpy(orig_path, temp_path, PATH_MAX);
693433d6423SLionel Sambuc 	symloop++;
694433d6423SLionel Sambuc   } while (symloop < _POSIX_SYMLOOP_MAX);
695433d6423SLionel Sambuc 
696433d6423SLionel Sambuc   if (symloop >= _POSIX_SYMLOOP_MAX) {
697433d6423SLionel Sambuc 	if (dir_vp) {
698433d6423SLionel Sambuc 		unlock_vnode(dir_vp);
699433d6423SLionel Sambuc 		unlock_vmnt(dir_vmp);
700433d6423SLionel Sambuc 		put_vnode(dir_vp);
701433d6423SLionel Sambuc 	}
702433d6423SLionel Sambuc 	return(ELOOP);
703433d6423SLionel Sambuc   }
704433d6423SLionel Sambuc 
705433d6423SLionel Sambuc   /* We've got the filename and the actual directory holding the file. From
706433d6423SLionel Sambuc    * here we start building up the canonical path by climbing up the tree */
707433d6423SLionel Sambuc   while (dir_vp != rfp->fp_rd) {
708433d6423SLionel Sambuc 
709433d6423SLionel Sambuc 	strlcpy(temp_path, "..", NAME_MAX+1);
710433d6423SLionel Sambuc 
711433d6423SLionel Sambuc 	/* check if we're at the root node of the file system */
712433d6423SLionel Sambuc 	if (dir_vp->v_vmnt->m_root_node == dir_vp) {
713433d6423SLionel Sambuc 		if (dir_vp->v_vmnt->m_mounted_on == NULL) {
714433d6423SLionel Sambuc 			/* Bail out, we can't go any higher */
715433d6423SLionel Sambuc 			break;
716433d6423SLionel Sambuc 		}
717433d6423SLionel Sambuc 		unlock_vnode(dir_vp);
718433d6423SLionel Sambuc 		unlock_vmnt(dir_vmp);
719433d6423SLionel Sambuc 		put_vnode(dir_vp);
720433d6423SLionel Sambuc 		dir_vp = dir_vp->v_vmnt->m_mounted_on;
721433d6423SLionel Sambuc 		dir_vmp = dir_vp->v_vmnt;
722433d6423SLionel Sambuc 		if (lock_vmnt(dir_vmp, VMNT_READ) != OK)
723433d6423SLionel Sambuc 			panic("failed to lock vmnt");
724433d6423SLionel Sambuc 		if (lock_vnode(dir_vp, VNODE_READ) != OK)
725433d6423SLionel Sambuc 			panic("failed to lock vnode");
726433d6423SLionel Sambuc 		dup_vnode(dir_vp);
727433d6423SLionel Sambuc 	}
728433d6423SLionel Sambuc 
729433d6423SLionel Sambuc 	lookup_init(&resolve, temp_path, PATH_NOFLAGS, &parent_vmp,
730433d6423SLionel Sambuc 		    &parent_dir);
731433d6423SLionel Sambuc 	resolve.l_vmnt_lock = VMNT_READ;
732433d6423SLionel Sambuc 	resolve.l_vnode_lock = VNODE_READ;
733433d6423SLionel Sambuc 
734433d6423SLionel Sambuc 	if ((parent_dir = advance(dir_vp, &resolve, rfp)) == NULL) {
735433d6423SLionel Sambuc 		unlock_vnode(dir_vp);
736433d6423SLionel Sambuc 		unlock_vmnt(dir_vmp);
737433d6423SLionel Sambuc 		put_vnode(dir_vp);
738433d6423SLionel Sambuc 		return(err_code);
739433d6423SLionel Sambuc 	}
740433d6423SLionel Sambuc 
741433d6423SLionel Sambuc 	/* now we have to retrieve the name of the parent directory */
742433d6423SLionel Sambuc 	if ((r = get_name(parent_dir, dir_vp, component)) != OK) {
743433d6423SLionel Sambuc 		unlock_vnode(parent_dir);
744433d6423SLionel Sambuc 		unlock_vmnt(parent_vmp);
745433d6423SLionel Sambuc 		unlock_vnode(dir_vp);
746433d6423SLionel Sambuc 		unlock_vmnt(dir_vmp);
747433d6423SLionel Sambuc 		put_vnode(parent_dir);
748433d6423SLionel Sambuc 		put_vnode(dir_vp);
749433d6423SLionel Sambuc 		return(r);
750433d6423SLionel Sambuc 	}
751433d6423SLionel Sambuc 
752433d6423SLionel Sambuc 	len += strlen(component) + 1;
753433d6423SLionel Sambuc 	if (len >= PATH_MAX) {
754433d6423SLionel Sambuc 		/* adding the component to orig_path would exceed PATH_MAX */
755433d6423SLionel Sambuc 		unlock_vnode(parent_dir);
756433d6423SLionel Sambuc 		unlock_vmnt(parent_vmp);
757433d6423SLionel Sambuc 		unlock_vnode(dir_vp);
758433d6423SLionel Sambuc 		unlock_vmnt(dir_vmp);
759433d6423SLionel Sambuc 		put_vnode(parent_dir);
760433d6423SLionel Sambuc 		put_vnode(dir_vp);
761433d6423SLionel Sambuc 		return(ENOMEM);
762433d6423SLionel Sambuc 	}
763433d6423SLionel Sambuc 
764433d6423SLionel Sambuc 	/* Store result of component in orig_path. First make space by moving
765433d6423SLionel Sambuc 	 * the contents of orig_path to the right. Move strlen + 1 bytes to
766433d6423SLionel Sambuc 	 * include the terminating '\0'. Move to strlen + 1 bytes to reserve
767433d6423SLionel Sambuc 	 * space for the slash.
768433d6423SLionel Sambuc 	 */
769433d6423SLionel Sambuc 	memmove(orig_path+strlen(component)+1, orig_path, strlen(orig_path)+1);
770433d6423SLionel Sambuc 	/* Copy component into canon_path */
771433d6423SLionel Sambuc 	memmove(orig_path, component, strlen(component));
772433d6423SLionel Sambuc 	/* Put slash into place */
773433d6423SLionel Sambuc 	orig_path[strlen(component)] = '/';
774433d6423SLionel Sambuc 
775433d6423SLionel Sambuc 	/* Store parent_dir result, and continue the loop once more */
776433d6423SLionel Sambuc 	unlock_vnode(dir_vp);
777433d6423SLionel Sambuc 	unlock_vmnt(dir_vmp);
778433d6423SLionel Sambuc 	put_vnode(dir_vp);
779433d6423SLionel Sambuc 	dir_vp = parent_dir;
780433d6423SLionel Sambuc 	dir_vmp = parent_vmp;
781433d6423SLionel Sambuc 	parent_vmp = NULL;
782433d6423SLionel Sambuc   }
783433d6423SLionel Sambuc 
784433d6423SLionel Sambuc   unlock_vmnt(dir_vmp);
785433d6423SLionel Sambuc   unlock_vnode(dir_vp);
786433d6423SLionel Sambuc   put_vnode(dir_vp);
787433d6423SLionel Sambuc 
788433d6423SLionel Sambuc   /* add the leading slash */
789433d6423SLionel Sambuc   len = strlen(orig_path);
790433d6423SLionel Sambuc   if (strlen(orig_path) >= PATH_MAX) return(ENAMETOOLONG);
791433d6423SLionel Sambuc   memmove(orig_path+1, orig_path, len + 1 /* include terminating nul */);
792433d6423SLionel Sambuc   orig_path[0] = '/';
793433d6423SLionel Sambuc 
794433d6423SLionel Sambuc   /* remove trailing slash if there is any */
795433d6423SLionel Sambuc   if (len > 1 && orig_path[len] == '/') orig_path[len] = '\0';
796433d6423SLionel Sambuc 
797433d6423SLionel Sambuc   return(OK);
798433d6423SLionel Sambuc }
799433d6423SLionel Sambuc 
800433d6423SLionel Sambuc /*===========================================================================*
801dd969671SDavid van Moolenbroek  *				do_socketpath				     *
802433d6423SLionel Sambuc  *===========================================================================*/
do_socketpath(void)803dd969671SDavid van Moolenbroek int do_socketpath(void)
804dd969671SDavid van Moolenbroek {
805dd969671SDavid van Moolenbroek /*
806dd969671SDavid van Moolenbroek  * Perform a path action on an on-disk socket file.  This call may be performed
807dd969671SDavid van Moolenbroek  * by the UDS service only.  The action is always on behalf of a user process
808dd969671SDavid van Moolenbroek  * that is currently making a socket call to the UDS service, and thus, VFS may
809dd969671SDavid van Moolenbroek  * rely on the fact that the user process is blocked.  TODO: there should be
810dd969671SDavid van Moolenbroek  * checks in place to prevent (even accidental) abuse of this function, though.
811dd969671SDavid van Moolenbroek  */
812dd969671SDavid van Moolenbroek   int r, what, slot;
813433d6423SLionel Sambuc   endpoint_t ep;
814433d6423SLionel Sambuc   cp_grant_id_t io_gr;
815433d6423SLionel Sambuc   size_t pathlen;
816dd969671SDavid van Moolenbroek   struct vnode *dirp, *vp;
817dd969671SDavid van Moolenbroek   struct vmnt *vmp, *vmp2;
818433d6423SLionel Sambuc   struct fproc *rfp;
819dd969671SDavid van Moolenbroek   char path[PATH_MAX];
820dd969671SDavid van Moolenbroek   struct lookup resolve, resolve2;
821dd969671SDavid van Moolenbroek   mode_t bits;
822dd969671SDavid van Moolenbroek 
823dd969671SDavid van Moolenbroek   /* This should be replaced by an ACL check. */
824dd969671SDavid van Moolenbroek   if (!super_user) return EPERM;
825dd969671SDavid van Moolenbroek 
826dd969671SDavid van Moolenbroek   ep = job_m_in.m_lsys_vfs_socketpath.endpt;
827dd969671SDavid van Moolenbroek   io_gr = job_m_in.m_lsys_vfs_socketpath.grant;
828dd969671SDavid van Moolenbroek   pathlen = job_m_in.m_lsys_vfs_socketpath.count;
829dd969671SDavid van Moolenbroek   what = job_m_in.m_lsys_vfs_socketpath.what;
830433d6423SLionel Sambuc 
831433d6423SLionel Sambuc   if (isokendpt(ep, &slot) != OK) return(EINVAL);
832*27852ebeSDavid van Moolenbroek   rfp = &fproc[slot];
833433d6423SLionel Sambuc 
834*27852ebeSDavid van Moolenbroek   /* Copy in the path name, which must not be empty.  It is typically not null
835*27852ebeSDavid van Moolenbroek    * terminated.
836*27852ebeSDavid van Moolenbroek    */
837*27852ebeSDavid van Moolenbroek   if (pathlen < 1 || pathlen >= sizeof(path)) return(EINVAL);
838dd969671SDavid van Moolenbroek   r = sys_safecopyfrom(who_e, io_gr, (vir_bytes)0, (vir_bytes)path, pathlen);
839433d6423SLionel Sambuc   if (r != OK) return(r);
840dd969671SDavid van Moolenbroek   path[pathlen] = '\0';
841433d6423SLionel Sambuc 
842dd969671SDavid van Moolenbroek   /* Now perform the requested action.  For the SPATH_CHECK action, a socket
843dd969671SDavid van Moolenbroek    * file is expected to exist already, and we should check whether the given
844dd969671SDavid van Moolenbroek    * user process has access to it.  For the SPATH_CREATE action, no file is
845dd969671SDavid van Moolenbroek    * expected to exist yet, and a socket file should be created on behalf of
846dd969671SDavid van Moolenbroek    * the user process.  In both cases, on success, return the socket file's
847dd969671SDavid van Moolenbroek    * device and inode numbers to the caller.
848dd969671SDavid van Moolenbroek    *
849dd969671SDavid van Moolenbroek    * Since the above canonicalization releases all locks once done, we need to
850dd969671SDavid van Moolenbroek    * recheck absolutely everything now.  TODO: do not release locks in between.
851dd969671SDavid van Moolenbroek    */
852*27852ebeSDavid van Moolenbroek   switch (what) {
853dd969671SDavid van Moolenbroek   case SPATH_CHECK:
854dd969671SDavid van Moolenbroek 	lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp);
855433d6423SLionel Sambuc 	resolve.l_vmnt_lock = VMNT_READ;
856433d6423SLionel Sambuc 	resolve.l_vnode_lock = VNODE_READ;
857433d6423SLionel Sambuc 	if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
858433d6423SLionel Sambuc 
859dd969671SDavid van Moolenbroek 	/* Check file type and permissions. */
860dd969671SDavid van Moolenbroek 	if (!S_ISSOCK(vp->v_mode))
861dd969671SDavid van Moolenbroek 		r = ENOTSOCK; /* not in POSIX spec; this is what NetBSD does */
862dd969671SDavid van Moolenbroek 	else
863dd969671SDavid van Moolenbroek 		r = forbidden(rfp, vp, R_BIT | W_BIT);
864dd969671SDavid van Moolenbroek 
865dd969671SDavid van Moolenbroek 	if (r == OK) {
866dd969671SDavid van Moolenbroek 		job_m_out.m_vfs_lsys_socketpath.device = vp->v_dev;
867dd969671SDavid van Moolenbroek 		job_m_out.m_vfs_lsys_socketpath.inode = vp->v_inode_nr;
868dd969671SDavid van Moolenbroek 	}
869433d6423SLionel Sambuc 
870433d6423SLionel Sambuc 	unlock_vnode(vp);
871433d6423SLionel Sambuc 	unlock_vmnt(vmp);
872433d6423SLionel Sambuc 	put_vnode(vp);
873dd969671SDavid van Moolenbroek 	break;
874dd969671SDavid van Moolenbroek 
875dd969671SDavid van Moolenbroek   case SPATH_CREATE:
876dd969671SDavid van Moolenbroek 	/* This is effectively simulating a mknod(2) call by the user process,
877dd969671SDavid van Moolenbroek 	 * including the application of its umask to the file permissions.
878dd969671SDavid van Moolenbroek 	 */
879dd969671SDavid van Moolenbroek 	lookup_init(&resolve, path, PATH_RET_SYMLINK, &vmp, &dirp);
880dd969671SDavid van Moolenbroek 	resolve.l_vmnt_lock = VMNT_WRITE;
881dd969671SDavid van Moolenbroek 	resolve.l_vnode_lock = VNODE_WRITE;
882dd969671SDavid van Moolenbroek 
883dd969671SDavid van Moolenbroek 	if ((dirp = last_dir(&resolve, rfp)) == NULL) return(err_code);
884dd969671SDavid van Moolenbroek 
885dd969671SDavid van Moolenbroek 	bits = S_IFSOCK | (ACCESSPERMS & rfp->fp_umask);
886dd969671SDavid van Moolenbroek 
887dd969671SDavid van Moolenbroek 	if (!S_ISDIR(dirp->v_mode))
888dd969671SDavid van Moolenbroek 		r = ENOTDIR;
889dd969671SDavid van Moolenbroek 	else if ((r = forbidden(rfp, dirp, W_BIT | X_BIT)) == OK) {
890dd969671SDavid van Moolenbroek 		r = req_mknod(dirp->v_fs_e, dirp->v_inode_nr, path,
891dd969671SDavid van Moolenbroek 		    rfp->fp_effuid, rfp->fp_effgid, bits, NO_DEV);
892dd969671SDavid van Moolenbroek 		if (r == OK) {
893dd969671SDavid van Moolenbroek 			/* Now we need to find out the device and inode number
894dd969671SDavid van Moolenbroek 			 * of the socket file we just created.  The vmnt lock
895dd969671SDavid van Moolenbroek 			 * should prevent any trouble here.
896dd969671SDavid van Moolenbroek 			 */
897dd969671SDavid van Moolenbroek 			lookup_init(&resolve2, resolve.l_path,
898dd969671SDavid van Moolenbroek 			    PATH_RET_SYMLINK, &vmp2, &vp);
899dd969671SDavid van Moolenbroek 			resolve2.l_vmnt_lock = VMNT_READ;
900dd969671SDavid van Moolenbroek 			resolve2.l_vnode_lock = VNODE_READ;
901dd969671SDavid van Moolenbroek 			vp = advance(dirp, &resolve2, rfp);
902dd969671SDavid van Moolenbroek 			assert(vmp2 == NULL);
903dd969671SDavid van Moolenbroek 			if (vp != NULL) {
904dd969671SDavid van Moolenbroek 				job_m_out.m_vfs_lsys_socketpath.device =
905dd969671SDavid van Moolenbroek 				    vp->v_dev;
906dd969671SDavid van Moolenbroek 				job_m_out.m_vfs_lsys_socketpath.inode =
907dd969671SDavid van Moolenbroek 				    vp->v_inode_nr;
908dd969671SDavid van Moolenbroek 				unlock_vnode(vp);
909dd969671SDavid van Moolenbroek 				put_vnode(vp);
910dd969671SDavid van Moolenbroek 			} else {
911dd969671SDavid van Moolenbroek 				/* Huh.  This should never happen.  If it does,
912dd969671SDavid van Moolenbroek 				 * we assume the socket file has somehow been
913dd969671SDavid van Moolenbroek 				 * lost, so we do not try to unlink it.
914dd969671SDavid van Moolenbroek 				 */
915dd969671SDavid van Moolenbroek 				printf("VFS: socketpath did not find created "
916dd969671SDavid van Moolenbroek 				    "node at %s (%d)\n", path, err_code);
917dd969671SDavid van Moolenbroek 				r = err_code;
918dd969671SDavid van Moolenbroek 			}
919dd969671SDavid van Moolenbroek 		} else if (r == EEXIST)
920dd969671SDavid van Moolenbroek 			r = EADDRINUSE;
921433d6423SLionel Sambuc 	}
922433d6423SLionel Sambuc 
923dd969671SDavid van Moolenbroek 	unlock_vnode(dirp);
924dd969671SDavid van Moolenbroek 	unlock_vmnt(vmp);
925dd969671SDavid van Moolenbroek 	put_vnode(dirp);
926dd969671SDavid van Moolenbroek 	break;
927433d6423SLionel Sambuc 
928dd969671SDavid van Moolenbroek   default:
929dd969671SDavid van Moolenbroek 	r = ENOSYS;
930dd969671SDavid van Moolenbroek   }
931dd969671SDavid van Moolenbroek 
932dd969671SDavid van Moolenbroek   return(r);
933433d6423SLionel Sambuc }
934