xref: /inferno-os/liblogfs/walk.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1 #include "logfsos.h"
2 #include "logfs.h"
3 #include "fcall.h"
4 #include "local.h"
5 
6 char *
7 logfsserverwalk(LogfsServer *server, u32int fid, u32int newfid, ushort nwname, char **wname, ushort *nwqid, Qid *wqid)
8 {
9 	ushort i;
10 	Entry *e;
11 	char *errmsg;
12 	Fid *f;
13 	if(server->trace > 1) {
14 		print("logfsserverwalk(%ud, %ud, %ud, \"", fid, newfid, nwname);
15 		for(i = 0; i < nwname; i++) {
16 			if(i > 0)
17 				print("/");
18 			print("%s", wname[i]);
19 		}
20 		print("\")\n");
21 	}
22 	f = logfsfidmapfindentry(server->fidmap, fid);
23 	if(f == nil)
24 		return logfsebadfid;
25 	if(f->openmode >= 0)
26 		return logfsefidopen;
27 	errmsg = nil;
28 	e = f->entry;
29 	if(e->deadandgone)
30 		return Eio;
31 	for(i = 0; i < nwname; i++) {
32 		Entry *se;
33 		/*
34 		 * deal with ..
35 		 */
36 		if(strcmp(wname[i], "..") == 0)
37 			se = e->parent;
38 		else if(strcmp(wname[i], ".") == 0)
39 			se = e;
40 		else {
41 			/*
42 			 * is it a directory?
43 			 */
44 			if((e->qid.type & QTDIR) == 0) {
45 				errmsg = Enotdir;
46 				break;
47 			}
48 			/*
49 			 * can we walk the walk, or just talk the protocol?
50 			 */
51 			if(!logfsuserpermcheck(server, e, f, DMEXEC)) {
52 				errmsg = Eperm;
53 				break;
54 			}
55 			/*
56 			 * search current entry for nwname[i]
57 			 */
58 			for(se = e->u.dir.list; se; se = se->next)
59 				if(strcmp(se->name, wname[i]) == 0)
60 					break;
61 			if(se == nil) {
62 				errmsg = Enonexist;
63 				break;
64 			}
65 		}
66 		wqid[i] = se->qid;
67 		e = se;
68 	}
69 	if(nwname > 0 && i == 0) {
70 		/*
71 		 * fell at the first fence
72 		 */
73 		return errmsg;
74 	}
75 	*nwqid = i;
76 	if(i < nwname)
77 		return nil;
78 	/*
79 	 * new fid required?
80 	 */
81 	if(fid != newfid) {
82 		Fid *newf;
83 		char *errmsg;
84 		errmsg = logfsfidmapnewentry(server->fidmap, newfid, &newf);
85 		if(errmsg)
86 			return errmsg;
87 		if(newf == nil)
88 			return logfsefidinuse;
89 		newf->entry = e;
90 		newf->uname = f->uname;
91 		e->inuse++;
92 	}
93 	else {
94 		/*
95 		 * this may now be right
96 		 * 1. increment reference on new entry first in case e and f->entry are the same
97 		 * 2. clunk the old one in case this has the effect of removing an old entry
98 		 * 3. dump the directory read state if the entry has changed
99 		 */
100 		e->inuse++;
101 		logfsentryclunk(f->entry);
102 		if(e != f->entry)
103 			logfsdrsfree(&f->drs);
104 		f->entry = e;
105 	}
106 	return nil;
107 }
108 
109