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