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