xref: /plan9/sys/src/9/port/devsrv.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 
9 typedef struct Srv Srv;
10 struct Srv
11 {
12 	char	*name;
13 	char	*owner;
14 	ulong	perm;
15 	Chan	*chan;
16 	Srv	*link;
17 	ulong	path;
18 };
19 
20 static QLock	srvlk;
21 static Srv	*srv;
22 static int	qidpath;
23 
24 static int
25 srvgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
26 {
27 	Srv *sp;
28 	Qid q;
29 
30 	if(s == DEVDOTDOT){
31 		devdir(c, c->qid, "#s", 0, eve, 0555, dp);
32 		return 1;
33 	}
34 
35 	qlock(&srvlk);
36 	for(sp = srv; sp && s; sp = sp->link)
37 		s--;
38 
39 	if(sp == 0) {
40 		qunlock(&srvlk);
41 		return -1;
42 	}
43 
44 	mkqid(&q, sp->path, 0, QTFILE);
45 	/* make sure name string continues to exist after we release lock */
46 	kstrcpy(up->genbuf, sp->name, sizeof up->genbuf);
47 	devdir(c, q, up->genbuf, 0, sp->owner, sp->perm, dp);
48 	qunlock(&srvlk);
49 	return 1;
50 }
51 
52 static void
53 srvinit(void)
54 {
55 	qidpath = 1;
56 }
57 
58 static Chan*
59 srvattach(char *spec)
60 {
61 	return devattach('s', spec);
62 }
63 
64 static Walkqid*
65 srvwalk(Chan *c, Chan *nc, char **name, int nname)
66 {
67 	return devwalk(c, nc, name, nname, 0, 0, srvgen);
68 }
69 
70 static Srv*
71 srvlookup(char *name, ulong qidpath)
72 {
73 	Srv *sp;
74 	for(sp = srv; sp; sp = sp->link)
75 		if(sp->path == qidpath || (name && strcmp(sp->name, name) == 0))
76 			return sp;
77 	return nil;
78 }
79 
80 static int
81 srvstat(Chan *c, uchar *db, int n)
82 {
83 	return devstat(c, db, n, 0, 0, srvgen);
84 }
85 
86 char*
87 srvname(Chan *c)
88 {
89 	Srv *sp;
90 	char *s;
91 
92 	for(sp = srv; sp; sp = sp->link)
93 		if(sp->chan == c){
94 			s = smalloc(3+strlen(sp->name)+1);
95 			sprint(s, "#s/%s", sp->name);
96 			return s;
97 		}
98 	return nil;
99 }
100 
101 static Chan*
102 srvopen(Chan *c, int omode)
103 {
104 	Srv *sp;
105 
106 	if(c->qid.type == QTDIR){
107 		if(omode & ORCLOSE)
108 			error(Eperm);
109 		if(omode != OREAD)
110 			error(Eisdir);
111 		c->mode = omode;
112 		c->flag |= COPEN;
113 		c->offset = 0;
114 		return c;
115 	}
116 	qlock(&srvlk);
117 	if(waserror()){
118 		qunlock(&srvlk);
119 		nexterror();
120 	}
121 
122 	sp = srvlookup(nil, c->qid.path);
123 	if(sp == 0 || sp->chan == 0)
124 		error(Eshutdown);
125 
126 	if(omode&OTRUNC)
127 		error("srv file already exists");
128 	if(openmode(omode)!=sp->chan->mode && sp->chan->mode!=ORDWR)
129 		error(Eperm);
130 	devpermcheck(sp->owner, sp->perm, omode);
131 
132 	cclose(c);
133 	incref(sp->chan);
134 	qunlock(&srvlk);
135 	poperror();
136 	return sp->chan;
137 }
138 
139 static void
140 srvcreate(Chan *c, char *name, int omode, ulong perm)
141 {
142 	char *sname;
143 	Srv *sp;
144 
145 	if(openmode(omode) != OWRITE)
146 		error(Eperm);
147 
148 	if(omode & OCEXEC)	/* can't happen */
149 		panic("someone broke namec");
150 
151 	sp = smalloc(sizeof *sp);
152 	sname = smalloc(strlen(name)+1);
153 
154 	qlock(&srvlk);
155 	if(waserror()){
156 		free(sp);
157 		free(sname);
158 		qunlock(&srvlk);
159 		nexterror();
160 	}
161 	if(sp == nil || sname == nil)
162 		error(Enomem);
163 	if(srvlookup(name, -1))
164 		error(Eexist);
165 
166 	sp->path = qidpath++;
167 	sp->link = srv;
168 	strcpy(sname, name);
169 	sp->name = sname;
170 	c->qid.type = QTFILE;
171 	c->qid.path = sp->path;
172 	srv = sp;
173 	qunlock(&srvlk);
174 	poperror();
175 
176 	kstrdup(&sp->owner, up->user);
177 	sp->perm = perm&0777;
178 
179 	c->flag |= COPEN;
180 	c->mode = OWRITE;
181 }
182 
183 static void
184 srvremove(Chan *c)
185 {
186 	Srv *sp, **l;
187 
188 	if(c->qid.type == QTDIR)
189 		error(Eperm);
190 
191 	qlock(&srvlk);
192 	if(waserror()){
193 		qunlock(&srvlk);
194 		nexterror();
195 	}
196 	l = &srv;
197 	for(sp = *l; sp; sp = sp->link) {
198 		if(sp->path == c->qid.path)
199 			break;
200 
201 		l = &sp->link;
202 	}
203 	if(sp == 0)
204 		error(Enonexist);
205 
206 	/*
207 	 * Only eve can remove system services.
208 	 * No one can remove #s/boot.
209 	 */
210 	if(strcmp(sp->owner, eve) == 0 && !iseve())
211 		error(Eperm);
212 	if(strcmp(sp->name, "boot") == 0)
213 		error(Eperm);
214 
215 	/*
216 	 * No removing personal services.
217 	 */
218 	if((sp->perm&7) != 7 && strcmp(sp->owner, up->user) && !iseve())
219 		error(Eperm);
220 
221 	*l = sp->link;
222 	qunlock(&srvlk);
223 	poperror();
224 
225 	if(sp->chan)
226 		cclose(sp->chan);
227 	free(sp->name);
228 	free(sp);
229 }
230 
231 static int
232 srvwstat(Chan *c, uchar *dp, int n)
233 {
234 	char *strs;
235 	Dir d;
236 	Srv *sp;
237 
238 	if(c->qid.type & QTDIR)
239 		error(Eperm);
240 
241 	strs = nil;
242 	qlock(&srvlk);
243 	if(waserror()){
244 		qunlock(&srvlk);
245 		free(strs);
246 		nexterror();
247 	}
248 
249 	sp = srvlookup(nil, c->qid.path);
250 	if(sp == 0)
251 		error(Enonexist);
252 
253 	if(strcmp(sp->owner, up->user) != 0 && !iseve())
254 		error(Eperm);
255 
256 	strs = smalloc(n);
257 	n = convM2D(dp, n, &d, strs);
258 	if(n == 0)
259 		error(Eshortstat);
260 	if(d.mode != ~0UL)
261 		sp->perm = d.mode & 0777;
262 	if(d.uid && *d.uid)
263 		kstrdup(&sp->owner, d.uid);
264 	if(d.name && *d.name && strcmp(sp->name, d.name) != 0) {
265 		if(strchr(d.name, '/') != nil)
266 			error(Ebadchar);
267 		kstrdup(&sp->name, d.name);
268 	}
269 	qunlock(&srvlk);
270 	free(strs);
271 	poperror();
272 	return n;
273 }
274 
275 static void
276 srvclose(Chan *c)
277 {
278 	/*
279 	 * in theory we need to override any changes in removability
280 	 * since open, but since all that's checked is the owner,
281 	 * which is immutable, all is well.
282 	 */
283 	if(c->flag & CRCLOSE){
284 		if(waserror())
285 			return;
286 		srvremove(c);
287 		poperror();
288 	}
289 }
290 
291 static long
292 srvread(Chan *c, void *va, long n, vlong)
293 {
294 	isdir(c);
295 	return devdirread(c, va, n, 0, 0, srvgen);
296 }
297 
298 static long
299 srvwrite(Chan *c, void *va, long n, vlong)
300 {
301 	Srv *sp;
302 	Chan *c1;
303 	int fd;
304 	char buf[32];
305 
306 	if(n >= sizeof buf)
307 		error(Egreg);
308 	memmove(buf, va, n);	/* so we can NUL-terminate */
309 	buf[n] = 0;
310 	fd = strtoul(buf, 0, 0);
311 
312 	c1 = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
313 
314 	qlock(&srvlk);
315 	if(waserror()) {
316 		qunlock(&srvlk);
317 		cclose(c1);
318 		nexterror();
319 	}
320 	if(c1->flag & (CCEXEC|CRCLOSE))
321 		error("posted fd has remove-on-close or close-on-exec");
322 	if(c1->qid.type & QTAUTH)
323 		error("cannot post auth file in srv");
324 	sp = srvlookup(nil, c->qid.path);
325 	if(sp == 0)
326 		error(Enonexist);
327 
328 	if(sp->chan)
329 		error(Ebadusefd);
330 
331 	sp->chan = c1;
332 	qunlock(&srvlk);
333 	poperror();
334 	return n;
335 }
336 
337 Dev srvdevtab = {
338 	's',
339 	"srv",
340 
341 	devreset,
342 	srvinit,
343 	devshutdown,
344 	srvattach,
345 	srvwalk,
346 	srvstat,
347 	srvopen,
348 	srvcreate,
349 	srvclose,
350 	srvread,
351 	devbread,
352 	srvwrite,
353 	devbwrite,
354 	srvremove,
355 	srvwstat,
356 };
357