xref: /plan9-contrib/sys/src/9k/port/devsrv.c (revision 406c76facc4b13aa2a55454bf4091aab9f03da22)
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 long
srvstat(Chan * c,uchar * db,long n)81 srvstat(Chan *c, uchar *db, long 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,int perm)142 srvcreate(Chan *c, char *name, int omode, int perm)
143 {
144 	Srv *sp;
145 	char *sname;
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(sname);
159 		free(sp);
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 long
srvwstat(Chan * c,uchar * dp,long n)235 srvwstat(Chan *c, uchar *dp, long n)
236 {
237 	Dir d;
238 	Srv *sp;
239 	char *strs;
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 
273 	qunlock(&srvlk);
274 	free(strs);
275 	poperror();
276 	return n;
277 }
278 
279 static void
srvclose(Chan * c)280 srvclose(Chan *c)
281 {
282 	/*
283 	 * in theory we need to override any changes in removability
284 	 * since open, but since all that's checked is the owner,
285 	 * which is immutable, all is well.
286 	 */
287 	if(c->flag & CRCLOSE){
288 		if(waserror())
289 			return;
290 		srvremove(c);
291 		poperror();
292 	}
293 }
294 
295 static long
srvread(Chan * c,void * va,long n,vlong)296 srvread(Chan *c, void *va, long n, vlong)
297 {
298 	isdir(c);
299 	return devdirread(c, va, n, 0, 0, srvgen);
300 }
301 
302 static long
srvwrite(Chan * c,void * va,long n,vlong)303 srvwrite(Chan *c, void *va, long n, vlong)
304 {
305 	Srv *sp;
306 	Chan *c1;
307 	int fd;
308 	char buf[32];
309 
310 	if(n >= sizeof buf)
311 		error(Egreg);
312 	memmove(buf, va, n);	/* so we can NUL-terminate */
313 	buf[n] = 0;
314 	fd = strtoul(buf, 0, 0);
315 
316 	c1 = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
317 
318 	qlock(&srvlk);
319 	if(waserror()) {
320 		qunlock(&srvlk);
321 		cclose(c1);
322 		nexterror();
323 	}
324 	if(c1->flag & (CCEXEC|CRCLOSE))
325 		error("posted fd has remove-on-close or close-on-exec");
326 	if(c1->qid.type & QTAUTH)
327 		error("cannot post auth file in srv");
328 	sp = srvlookup(nil, c->qid.path);
329 	if(sp == 0)
330 		error(Enonexist);
331 
332 	if(sp->chan)
333 		error(Ebadusefd);
334 
335 	sp->chan = c1;
336 	qunlock(&srvlk);
337 	poperror();
338 	return n;
339 }
340 
341 Dev srvdevtab = {
342 	's',
343 	"srv",
344 
345 	devreset,
346 	srvinit,
347 	devshutdown,
348 	srvattach,
349 	srvwalk,
350 	srvstat,
351 	srvopen,
352 	srvcreate,
353 	srvclose,
354 	srvread,
355 	devbread,
356 	srvwrite,
357 	devbwrite,
358 	srvremove,
359 	srvwstat,
360 };
361