xref: /plan9/sys/src/9/port/devsrv.c (revision 4e3613ab15c331a9ada113286cc0f2a35bc0373d)
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
srvgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)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
srvinit(void)53 srvinit(void)
54 {
55 	qidpath = 1;
56 }
57 
58 static Chan*
srvattach(char * spec)59 srvattach(char *spec)
60 {
61 	return devattach('s', spec);
62 }
63 
64 static Walkqid*
srvwalk(Chan * c,Chan * nc,char ** name,int nname)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*
srvlookup(char * name,ulong qidpath)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
srvstat(Chan * c,uchar * db,int n)81 srvstat(Chan *c, uchar *db, int n)
82 {
83 	return devstat(c, db, n, 0, 0, srvgen);
84 }
85 
86 char*
srvname(Chan * c)87 srvname(Chan *c)
88 {
89 	int size;
90 	Srv *sp;
91 	char *s;
92 
93 	for(sp = srv; sp; sp = sp->link)
94 		if(sp->chan == c){
95 			size = 3+strlen(sp->name)+1;
96 			s = smalloc(size);
97 			snprint(s, size, "#s/%s", sp->name);
98 			return s;
99 		}
100 	return nil;
101 }
102 
103 static Chan*
srvopen(Chan * c,int omode)104 srvopen(Chan *c, int omode)
105 {
106 	Srv *sp;
107 
108 	if(c->qid.type == QTDIR){
109 		if(omode & ORCLOSE)
110 			error(Eperm);
111 		if(omode != OREAD)
112 			error(Eisdir);
113 		c->mode = omode;
114 		c->flag |= COPEN;
115 		c->offset = 0;
116 		return c;
117 	}
118 	qlock(&srvlk);
119 	if(waserror()){
120 		qunlock(&srvlk);
121 		nexterror();
122 	}
123 
124 	sp = srvlookup(nil, c->qid.path);
125 	if(sp == 0 || sp->chan == 0)
126 		error(Eshutdown);
127 
128 	if(omode&OTRUNC)
129 		error("srv file already exists");
130 	if(openmode(omode)!=sp->chan->mode && sp->chan->mode!=ORDWR)
131 		error(Eperm);
132 	devpermcheck(sp->owner, sp->perm, omode);
133 
134 	cclose(c);
135 	incref(sp->chan);
136 	qunlock(&srvlk);
137 	poperror();
138 	return sp->chan;
139 }
140 
141 static void
srvcreate(Chan * c,char * name,int omode,ulong perm)142 srvcreate(Chan *c, char *name, int omode, ulong perm)
143 {
144 	char *sname;
145 	Srv *sp;
146 
147 	if(openmode(omode) != OWRITE)
148 		error(Eperm);
149 
150 	if(omode & OCEXEC)	/* can't happen */
151 		panic("someone broke namec");
152 
153 	sp = smalloc(sizeof *sp);
154 	sname = smalloc(strlen(name)+1);
155 
156 	qlock(&srvlk);
157 	if(waserror()){
158 		free(sp);
159 		free(sname);
160 		qunlock(&srvlk);
161 		nexterror();
162 	}
163 	if(sp == nil || sname == nil)
164 		error(Enomem);
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
srvremove(Chan * c)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->owner);
230 	free(sp->name);
231 	free(sp);
232 }
233 
234 static int
srvwstat(Chan * c,uchar * dp,int n)235 srvwstat(Chan *c, uchar *dp, int n)
236 {
237 	char *strs;
238 	Dir d;
239 	Srv *sp;
240 
241 	if(c->qid.type & QTDIR)
242 		error(Eperm);
243 
244 	strs = nil;
245 	qlock(&srvlk);
246 	if(waserror()){
247 		qunlock(&srvlk);
248 		free(strs);
249 		nexterror();
250 	}
251 
252 	sp = srvlookup(nil, c->qid.path);
253 	if(sp == 0)
254 		error(Enonexist);
255 
256 	if(strcmp(sp->owner, up->user) != 0 && !iseve())
257 		error(Eperm);
258 
259 	strs = smalloc(n);
260 	n = convM2D(dp, n, &d, strs);
261 	if(n == 0)
262 		error(Eshortstat);
263 	if(d.mode != ~0UL)
264 		sp->perm = d.mode & 0777;
265 	if(d.uid && *d.uid)
266 		kstrdup(&sp->owner, d.uid);
267 	if(d.name && *d.name && strcmp(sp->name, d.name) != 0) {
268 		if(strchr(d.name, '/') != nil)
269 			error(Ebadchar);
270 		kstrdup(&sp->name, d.name);
271 	}
272 	qunlock(&srvlk);
273 	free(strs);
274 	poperror();
275 	return n;
276 }
277 
278 static void
srvclose(Chan * c)279 srvclose(Chan *c)
280 {
281 	/*
282 	 * in theory we need to override any changes in removability
283 	 * since open, but since all that's checked is the owner,
284 	 * which is immutable, all is well.
285 	 */
286 	if(c->flag & CRCLOSE){
287 		if(waserror())
288 			return;
289 		srvremove(c);
290 		poperror();
291 	}
292 }
293 
294 static long
srvread(Chan * c,void * va,long n,vlong)295 srvread(Chan *c, void *va, long n, vlong)
296 {
297 	isdir(c);
298 	return devdirread(c, va, n, 0, 0, srvgen);
299 }
300 
301 static long
srvwrite(Chan * c,void * va,long n,vlong)302 srvwrite(Chan *c, void *va, long n, vlong)
303 {
304 	Srv *sp;
305 	Chan *c1;
306 	int fd;
307 	char buf[32];
308 
309 	if(n >= sizeof buf)
310 		error(Egreg);
311 	memmove(buf, va, n);	/* so we can NUL-terminate */
312 	buf[n] = 0;
313 	fd = strtoul(buf, 0, 0);
314 
315 	c1 = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
316 
317 	qlock(&srvlk);
318 	if(waserror()) {
319 		qunlock(&srvlk);
320 		cclose(c1);
321 		nexterror();
322 	}
323 	if(c1->flag & (CCEXEC|CRCLOSE))
324 		error("posted fd has remove-on-close or close-on-exec");
325 	if(c1->qid.type & QTAUTH)
326 		error("cannot post auth file in srv");
327 	sp = srvlookup(nil, c->qid.path);
328 	if(sp == 0)
329 		error(Enonexist);
330 
331 	if(sp->chan)
332 		error(Ebadusefd);
333 
334 	sp->chan = c1;
335 	qunlock(&srvlk);
336 	poperror();
337 	return n;
338 }
339 
340 Dev srvdevtab = {
341 	's',
342 	"srv",
343 
344 	devreset,
345 	srvinit,
346 	devshutdown,
347 	srvattach,
348 	srvwalk,
349 	srvstat,
350 	srvopen,
351 	srvcreate,
352 	srvclose,
353 	srvread,
354 	devbread,
355 	srvwrite,
356 	devbwrite,
357 	srvremove,
358 	srvwstat,
359 };
360