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