xref: /plan9/sys/src/9/port/devsrv.c (revision ee17ab8015e4a168cf53c39988aec397b589220f)
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 	if(sp == nil || sname == nil) {
154 		free(sp);
155 		free(sname);
156 		error(Enomem);
157 	}
158 
159 	qlock(&srvlk);
160 	if(waserror()){
161 		free(sp);
162 		qunlock(&srvlk);
163 		nexterror();
164 	}
165 	if(srvlookup(name, -1))
166 		error(Eexist);
167 
168 	sp->path = qidpath++;
169 	sp->link = srv;
170 	strcpy(sname, name);
171 	sp->name = sname;
172 	c->qid.type = QTFILE;
173 	c->qid.path = sp->path;
174 	srv = sp;
175 	qunlock(&srvlk);
176 	poperror();
177 
178 	kstrdup(&sp->owner, up->user);
179 	sp->perm = perm&0777;
180 
181 	c->flag |= COPEN;
182 	c->mode = OWRITE;
183 }
184 
185 static void
186 srvremove(Chan *c)
187 {
188 	Srv *sp, **l;
189 
190 	if(c->qid.type == QTDIR)
191 		error(Eperm);
192 
193 	qlock(&srvlk);
194 	if(waserror()){
195 		qunlock(&srvlk);
196 		nexterror();
197 	}
198 	l = &srv;
199 	for(sp = *l; sp; sp = sp->link) {
200 		if(sp->path == c->qid.path)
201 			break;
202 
203 		l = &sp->link;
204 	}
205 	if(sp == 0)
206 		error(Enonexist);
207 
208 	/*
209 	 * Only eve can remove system services.
210 	 * No one can remove #s/boot.
211 	 */
212 	if(strcmp(sp->owner, eve) == 0 && !iseve())
213 		error(Eperm);
214 	if(strcmp(sp->name, "boot") == 0)
215 		error(Eperm);
216 
217 	/*
218 	 * No removing personal services.
219 	 */
220 	if((sp->perm&7) != 7 && strcmp(sp->owner, up->user) && !iseve())
221 		error(Eperm);
222 
223 	*l = sp->link;
224 	qunlock(&srvlk);
225 	poperror();
226 
227 	if(sp->chan)
228 		cclose(sp->chan);
229 	free(sp->name);
230 	free(sp);
231 }
232 
233 static int
234 srvwstat(Chan *c, uchar *dp, int n)
235 {
236 	char *strs;
237 	Dir d;
238 	Srv *sp;
239 
240 	if(c->qid.type & QTDIR)
241 		error(Eperm);
242 
243 	strs = nil;
244 	qlock(&srvlk);
245 	if(waserror()){
246 		qunlock(&srvlk);
247 		free(strs);
248 		nexterror();
249 	}
250 
251 	sp = srvlookup(nil, c->qid.path);
252 	if(sp == 0)
253 		error(Enonexist);
254 
255 	if(strcmp(sp->owner, up->user) != 0 && !iseve())
256 		error(Eperm);
257 
258 	strs = smalloc(n);
259 	n = convM2D(dp, n, &d, strs);
260 	if(n == 0)
261 		error(Eshortstat);
262 	if(d.mode != ~0UL)
263 		sp->perm = d.mode & 0777;
264 	if(d.uid && *d.uid)
265 		kstrdup(&sp->owner, d.uid);
266 	if(d.name && *d.name && strcmp(sp->name, d.name) != 0) {
267 		if(strchr(d.name, '/') != nil)
268 			error(Ebadchar);
269 		kstrdup(&sp->name, d.name);
270 	}
271 	qunlock(&srvlk);
272 	free(strs);
273 	poperror();
274 	return n;
275 }
276 
277 static void
278 srvclose(Chan *c)
279 {
280 	/*
281 	 * in theory we need to override any changes in removability
282 	 * since open, but since all that's checked is the owner,
283 	 * which is immutable, all is well.
284 	 */
285 	if(c->flag & CRCLOSE){
286 		if(waserror())
287 			return;
288 		srvremove(c);
289 		poperror();
290 	}
291 }
292 
293 static long
294 srvread(Chan *c, void *va, long n, vlong)
295 {
296 	isdir(c);
297 	return devdirread(c, va, n, 0, 0, srvgen);
298 }
299 
300 static long
301 srvwrite(Chan *c, void *va, long n, vlong)
302 {
303 	Srv *sp;
304 	Chan *c1;
305 	int fd;
306 	char buf[32];
307 
308 	if(n >= sizeof buf)
309 		error(Egreg);
310 	memmove(buf, va, n);	/* so we can NUL-terminate */
311 	buf[n] = 0;
312 	fd = strtoul(buf, 0, 0);
313 
314 	c1 = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
315 
316 	qlock(&srvlk);
317 	if(waserror()) {
318 		qunlock(&srvlk);
319 		cclose(c1);
320 		nexterror();
321 	}
322 	if(c1->flag & (CCEXEC|CRCLOSE))
323 		error("posted fd has remove-on-close or close-on-exec");
324 	if(c1->qid.type & QTAUTH)
325 		error("cannot post auth file in srv");
326 	sp = srvlookup(nil, c->qid.path);
327 	if(sp == 0)
328 		error(Enonexist);
329 
330 	if(sp->chan)
331 		error(Ebadusefd);
332 
333 	sp->chan = c1;
334 	qunlock(&srvlk);
335 	poperror();
336 	return n;
337 }
338 
339 Dev srvdevtab = {
340 	's',
341 	"srv",
342 
343 	devreset,
344 	srvinit,
345 	devshutdown,
346 	srvattach,
347 	srvwalk,
348 	srvstat,
349 	srvopen,
350 	srvcreate,
351 	srvclose,
352 	srvread,
353 	devbread,
354 	srvwrite,
355 	devbwrite,
356 	srvremove,
357 	srvwstat,
358 };
359