Fix omode handling in dev/srv; have csquery always open #s/cs until we are sure bindi...
[akaros.git] / kern / drivers / dev / srv.c
1 /* Copyright (c) 2014 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * #s (srv) - a chan sharing service.  This was originally based off the Inferno
6  * #s, but it's been completely rewritten to act like what I remember the plan9
7  * one to be like.
8  *
9  *
10  * I tried a style where we hang reference counted objects off c->aux, specific
11  * to each chan.  Instead of looking up via qid.path, we just look at the c->aux
12  * for our struct.  I originally tried having those be reference counted
13  * structs, but that fails for a bunch of reasons.  Without them being reference
14  * counted, we're really just using c->aux as if it was qid.path.
15  *
16  * We can't hang an external reference to an item off c->aux, and have that item
17  * change as we gen (but we can use it as a weak ref, uncounted ref).  The main
18  * thing is that devclone makes a 'half-chan' with a copy of c->aux.  This chan
19  * may or may not be closed later.  If we transfer refs via a gen, we first
20  * assumed we had a ref in the first place (devclone doesn't incref our srv),
21  * and then we might not close.  This ends up decreffing top_dir too much, and
22  * giving it's refs to some other file in the walk. */
23
24 #include <vfs.h>
25 #include <kfs.h>
26 #include <slab.h>
27 #include <kmalloc.h>
28 #include <kref.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <assert.h>
32 #include <error.h>
33 #include <cpio.h>
34 #include <pmap.h>
35 #include <smp.h>
36 #include <ip.h>
37 #include <sys/queue.h>
38
39 #define Qtopdir                 1
40 #define Qsrvfile                2
41
42 struct srvfile {
43         TAILQ_ENTRY(srvfile) link;
44         char *name;
45         struct chan *chan;
46         struct kref ref;                        /* +1 for existing on create, -1 on remove */
47         char *user;
48         uint32_t perm;
49         atomic_t opens;                         /* used for exclusive open checks */
50 };
51
52 struct srvfile *top_dir;
53 TAILQ_HEAD(srvfilelist, srvfile) srvfiles = TAILQ_HEAD_INITIALIZER(srvfiles);
54 /* the lock protects the list and its members.  we don't incref from a list ref
55  * without the lock. (if you're on the list, we can grab a ref). */
56 spinlock_t srvlock = SPINLOCK_INITIALIZER;
57
58 atomic_t nr_srvs = 0;                   /* debugging - concerned about leaking mem */
59
60 /* Given a pointer (internal ref), we attempt to get a kref */
61 static bool grab_ref(struct srvfile *srv)
62 {
63         bool ret = FALSE;
64         struct srvfile *srv_i;
65         spin_lock(&srvlock);
66         TAILQ_FOREACH(srv_i, &srvfiles, link) {
67                 if (srv_i == srv) {
68                         ret = kref_get_not_zero(&srv_i->ref, 1);
69                         break;
70                 }
71         }
72         spin_unlock(&srvlock);
73         return ret;
74 }
75
76 static void srv_release(struct kref *kref)
77 {
78         struct srvfile *srv = container_of(kref, struct srvfile, ref);
79         kfree(srv->user);
80         kfree(srv->name);
81         if (srv->chan)
82                 cclose(srv->chan);
83         kfree(srv);
84         atomic_dec(nr_srvs);
85 }
86
87 static int srvgen(struct chan *c, char *name, struct dirtab *tab,
88                                   int ntab, int s, struct dir *dp)
89 {
90         struct srvfile *prev, *next;
91         struct qid q;
92
93         if (s == DEVDOTDOT) {
94                 /* changing whatever c->aux was to be topdir */
95                 mkqid(&q, Qtopdir, 0, QTDIR);
96                 devdir(c, q, "#s", 0, eve, 0555, dp);
97                 return 1;
98         }
99         spin_lock(&srvlock);
100         TAILQ_FOREACH(next, &srvfiles, link) {
101                 /* come in with s == 0 on the first run */
102                 if (s-- == 0)
103                         break;
104         }
105         if (!next) {
106                 spin_unlock(&srvlock);
107                 return -1;
108         }
109         /* update c to point to our new srvfile.  this keeps the chan and its srv in
110          * sync with what we're genning. */
111         c->aux = next;  /* uncounted ref */
112         mkqid(&q, Qsrvfile, 0, QTFILE);
113         /* once we release the lock, next could disappear, including next->name */
114         strncpy(get_cur_genbuf(), next->name, GENBUF_SZ);
115         devdir(c, q, get_cur_genbuf(), 1 /* length */ , next->user, next->perm, dp);
116         spin_unlock(&srvlock);
117         return 1;
118 }
119
120 static void __srvinit(void)
121 {
122         top_dir = kzmalloc(sizeof(struct srvfile), KMALLOC_WAIT);
123         /* kstrdup, just in case we free this later */
124         kstrdup(&top_dir->name, "srv");
125         kstrdup(&top_dir->user, current ? current->user : "eve");
126         top_dir->perm = DMDIR | 0770;
127         /* +1 for existing, should never decref this */
128         kref_init(&top_dir->ref, fake_release, 1);
129         atomic_set(&top_dir->opens, 0);
130 }
131
132 static void srvinit(void)
133 {
134         run_once(__srvinit());
135 }
136
137 static struct chan *srvattach(char *spec)
138 {
139         /* the inferno attach was pretty complicated, but
140          * we're not sure that complexity is needed. */
141         struct chan *c = devattach('s', spec);
142         mkqid(&c->qid, Qtopdir, 0, QTDIR);
143         /* c->aux is an uncounted ref */
144         c->aux = top_dir;
145         return c;
146 }
147
148 static struct walkqid *srvwalk(struct chan *c, struct chan *nc, char **name,
149                                                            int nname)
150 {
151         return devwalk(c, nc, name, nname, 0, 0, srvgen);
152 }
153
154 static int srvstat(struct chan *c, uint8_t * db, int n)
155 {
156         return devstat(c, db, n, 0, 0, srvgen);
157 }
158
159 static struct chan *srvopen(struct chan *c, int omode)
160 {
161         ERRSTACK(1);
162         struct srvfile *srv;
163         openmode(omode);        /* used as an error checker in plan9, does little now */
164         if (c->qid.type & QTDIR) {
165                 if (!IS_RDONLY(omode))
166                         error(Eisdir);
167                 c->mode = openmode(omode);
168                 c->flag |= COPEN;
169                 c->offset = 0;
170                 return c;
171         }
172         srv = c->aux;
173         if (!grab_ref(srv))
174                 error("Unable to open srv file, concurrent removal");
175         if (waserror()) {
176                 kref_put(&srv->ref);
177                 nexterror();
178         }
179         devpermcheck(srv->user, srv->perm, omode);
180         /* No remove on close support yet */
181 #if 0
182         if (omode & ORCLOSE) {
183                 if (strcmp(srv->user, up->env->user) != 0)
184                         error(Eperm);
185                 else
186                         srv->flags |= SORCLOSE;
187         }
188 #endif
189         if ((srv->perm & DMEXCL) && atomic_read(&srv->opens))
190                 error(Einuse);
191         /* srv->chan is write-once, so we don't need to sync. */
192         if (!srv->chan)
193                 error("srv file has no chan yet");
194         /* this is more than just the ref - 1, since there will be refs in flight
195          * as gens work their way through the list */
196         atomic_inc(&srv->opens);
197         /* the magic of srv: open c, get c->srv->chan back */
198         cclose(c);
199         c = srv->chan;
200         kref_get(&c->ref, 1);
201         poperror();
202         kref_put(&srv->ref);
203         return c;
204 }
205
206 static void srvcreate(struct chan *c, char *name, int omode, uint32_t perm)
207 {
208         struct srvfile *srv;
209         srv = kzmalloc(sizeof(struct srvfile), KMALLOC_WAIT);
210         kstrdup(&srv->name, name);
211         kstrdup(&srv->user, current ? current->user : "eve");
212         srv->perm = 0770;       /* TODO need some security thoughts */
213         atomic_set(&srv->opens, 1);     /* we return it opened */
214         mkqid(&c->qid, Qsrvfile, 0, QTFILE);
215         c->aux = srv;
216         c->mode = openmode(omode);
217         /* one ref for being on the list */
218         kref_init(&srv->ref, srv_release, 1);
219         spin_lock(&srvlock);
220         TAILQ_INSERT_TAIL(&srvfiles, srv, link);
221         spin_unlock(&srvlock);
222         atomic_inc(&nr_srvs);
223 }
224
225 static int srvwstat(struct chan *c, uint8_t * dp, int n)
226 {
227         error("srvwstat not supported yet");
228         return -1;
229 }
230
231 static void srvclose(struct chan *c)
232 {
233         struct srvfile *srv = c->aux;
234         if (!grab_ref(srv))
235                 return;
236         atomic_dec(&srv->opens);
237         kref_put(&srv->ref);
238 }
239
240 static void srvremove(struct chan *c)
241 {
242         struct srvfile *srv_i, *temp;
243
244         spin_lock(&srvlock);
245         TAILQ_FOREACH_SAFE(srv_i, &srvfiles, link, temp) {
246                 if (srv_i == c->aux) {
247                         TAILQ_REMOVE(&srvfiles, srv_i, link);
248                         break;
249                 }
250         }
251         spin_unlock(&srvlock);
252         if (srv_i)
253                 kref_put(&srv_i->ref);  /* dropping ref from the list */
254 }
255
256 /* N.B. srvopen gives the chan back. The only 'reading' we do
257  * in srv is of the top level directory.
258  */
259 static long srvread(struct chan *c, void *va, long count, int64_t offset)
260 {
261         return devdirread(c, va, count, 0, 0, srvgen);
262 }
263
264 static long srvwrite(struct chan *c, void *va, long count, int64_t offset)
265 {
266         ERRSTACK(2);
267         struct srvfile *srv;
268         struct chan *new_chan;
269         char *kbuf = 0;
270         int fd;
271
272         if (c->qid.type & QTDIR)
273                 error(Eperm);
274         srv = c->aux;
275         if (!grab_ref(srv))
276                 error("Unable to write srv file, concurrent removal");
277         if (waserror()) {
278                 kref_put(&srv->ref);
279                 nexterror();
280         }
281         if (srv->chan)
282                 error("srv file already has a stored chan!");
283         if (waserror()) {
284                 kfree(kbuf);
285                 nexterror();
286         }
287         kbuf = kmalloc(count + 1, KMALLOC_WAIT);
288         strncpy(kbuf, va, count);
289         kbuf[count] = 0;
290         fd = strtoul(kbuf, 0, 10);
291         /* the magic of srv: srv stores the chan corresponding to the fd.  -1 for
292          * mode, so we just get the chan with no checks (RDWR would work too). */
293         new_chan = fdtochan(current->fgrp, fd, -1, FALSE, TRUE);
294         /* fdtochan already increffed for us */
295         if (!__sync_bool_compare_and_swap(&srv->chan, 0, new_chan)) {
296                 cclose(new_chan);
297                 error("srv file already has a stored chan!");
298         }
299         poperror();
300         kfree(kbuf);
301         poperror();
302         kref_put(&srv->ref);
303         return count;
304 }
305
306 struct dev srvdevtab __devtab = {
307         's',
308         "srv",
309
310         devreset,
311         srvinit,
312         devshutdown,
313         srvattach,
314         srvwalk,
315         srvstat,
316         srvopen,
317         srvcreate,
318         srvclose,
319         srvread,
320         devbread,
321         srvwrite,
322         devbwrite,
323         srvremove,
324         srvwstat,
325         devpower,
326         devchaninfo,
327 };