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