xref: /plan9-contrib/sys/src/9/port/devsrv.c (revision 3468a4915d661daa200976acc4f80f51aae144b2)
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->owner);
228 	free(sp->name);
229 	free(sp);
230 }
231 
232 static int
233 srvwstat(Chan *c, uchar *dp, int n)
234 {
235 	char *strs;
236 	Dir d;
237 	Srv *sp;
238 
239 	if(c->qid.type & QTDIR)
240 		error(Eperm);
241 
242 	strs = nil;
243 	qlock(&srvlk);
244 	if(waserror()){
245 		qunlock(&srvlk);
246 		free(strs);
247 		nexterror();
248 	}
249 
250 	sp = srvlookup(nil, c->qid.path);
251 	if(sp == 0)
252 		error(Enonexist);
253 
254 	if(strcmp(sp->owner, up->user) != 0 && !iseve())
255 		error(Eperm);
256 
257 	strs = smalloc(n);
258 	n = convM2D(dp, n, &d, strs);
259 	if(n == 0)
260 		error(Eshortstat);
261 	if(d.mode != ~0UL)
262 		sp->perm = d.mode & 0777;
263 	if(d.uid && *d.uid)
264 		kstrdup(&sp->owner, d.uid);
265 	if(d.name && *d.name && strcmp(sp->name, d.name) != 0) {
266 		if(strchr(d.name, '/') != nil)
267 			error(Ebadchar);
268 		kstrdup(&sp->name, d.name);
269 	}
270 	qunlock(&srvlk);
271 	free(strs);
272 	poperror();
273 	return n;
274 }
275 
276 static void
277 srvclose(Chan *c)
278 {
279 	/*
280 	 * in theory we need to override any changes in removability
281 	 * since open, but since all that's checked is the owner,
282 	 * which is immutable, all is well.
283 	 */
284 	if(c->flag & CRCLOSE){
285 		if(waserror())
286 			return;
287 		srvremove(c);
288 		poperror();
289 	}
290 }
291 
292 static long
293 srvread(Chan *c, void *va, long n, vlong)
294 {
295 	isdir(c);
296 	return devdirread(c, va, n, 0, 0, srvgen);
297 }
298 
299 static long
300 srvwrite(Chan *c, void *va, long n, vlong)
301 {
302 	Srv *sp;
303 	Chan *c1;
304 	int fd;
305 	char buf[32];
306 
307 	if(n >= sizeof buf)
308 		error(Egreg);
309 	memmove(buf, va, n);	/* so we can NUL-terminate */
310 	buf[n] = 0;
311 	fd = strtoul(buf, 0, 0);
312 
313 	c1 = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
314 
315 	qlock(&srvlk);
316 	if(waserror()) {
317 		qunlock(&srvlk);
318 		cclose(c1);
319 		nexterror();
320 	}
321 	if(c1->flag & (CCEXEC|CRCLOSE))
322 		error("posted fd has remove-on-close or close-on-exec");
323 	if(c1->qid.type & QTAUTH)
324 		error("cannot post auth file in srv");
325 	sp = srvlookup(nil, c->qid.path);
326 	if(sp == 0)
327 		error(Enonexist);
328 
329 	if(sp->chan)
330 		error(Ebadusefd);
331 
332 	sp->chan = c1;
333 	qunlock(&srvlk);
334 	poperror();
335 	return n;
336 }
337 
338 Dev srvdevtab = {
339 	's',
340 	"srv",
341 
342 	devreset,
343 	srvinit,
344 	devshutdown,
345 	srvattach,
346 	srvwalk,
347 	srvstat,
348 	srvopen,
349 	srvcreate,
350 	srvclose,
351 	srvread,
352 	devbread,
353 	srvwrite,
354 	devbwrite,
355 	srvremove,
356 	srvwstat,
357 };
358