Replace IS_READONLY with an O_WRITE check [5/7]
[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 char*
160 srvname(struct chan *c)
161 {
162         struct srvfile *srv_i;
163         char *s;
164
165         spin_lock(&srvlock);
166         TAILQ_FOREACH(srv_i, &srvfiles, link) {
167                 if(srv_i->chan == c){
168                         int len = 3 + strlen(srv_i->name) + 1;
169                         s = kzmalloc(len, 0);
170                         snprintf(s, len, "#s/%s", srv_i->name);
171                         spin_unlock(&srvlock);
172                         return s;
173                 }
174         }
175         spin_unlock(&srvlock);
176         return NULL;
177 }
178
179 static struct chan *srvopen(struct chan *c, int omode)
180 {
181         ERRSTACK(1);
182         struct srvfile *srv;
183         openmode(omode);        /* used as an error checker in plan9, does little now */
184         if (c->qid.type & QTDIR) {
185                 if (omode & O_WRITE)
186                         error(Eisdir);
187                 c->mode = openmode(omode);
188                 c->flag |= COPEN;
189                 c->offset = 0;
190                 return c;
191         }
192         srv = c->aux;
193         if (!grab_ref(srv))
194                 error("Unable to open srv file, concurrent removal");
195         if (waserror()) {
196                 kref_put(&srv->ref);
197                 nexterror();
198         }
199         devpermcheck(srv->user, srv->perm, omode);
200         /* No remove on close support yet */
201 #if 0
202         if (omode & ORCLOSE) {
203                 if (strcmp(srv->user, up->env->user) != 0)
204                         error(Eperm);
205                 else
206                         srv->flags |= SORCLOSE;
207         }
208 #endif
209         if ((srv->perm & DMEXCL) && atomic_read(&srv->opens))
210                 error(Einuse);
211         /* srv->chan is write-once, so we don't need to sync. */
212         if (!srv->chan)
213                 error("srv file has no chan yet");
214         /* this is more than just the ref - 1, since there will be refs in flight
215          * as gens work their way through the list */
216         atomic_inc(&srv->opens);
217         /* the magic of srv: open c, get c->srv->chan back */
218         cclose(c);
219         c = srv->chan;
220         chan_incref(c);
221         poperror();
222         kref_put(&srv->ref);
223         return c;
224 }
225
226 static void srvcreate(struct chan *c, char *name, int omode, uint32_t perm)
227 {
228         struct srvfile *srv;
229         srv = kzmalloc(sizeof(struct srvfile), KMALLOC_WAIT);
230         kstrdup(&srv->name, name);
231         kstrdup(&srv->user, current ? current->user : "eve");
232         srv->perm = 0770;       /* TODO need some security thoughts */
233         atomic_set(&srv->opens, 1);     /* we return it opened */
234         mkqid(&c->qid, Qsrvfile, 0, QTFILE);
235         c->aux = srv;
236         c->mode = openmode(omode);
237         /* one ref for being on the list */
238         kref_init(&srv->ref, srv_release, 1);
239         spin_lock(&srvlock);
240         TAILQ_INSERT_TAIL(&srvfiles, srv, link);
241         spin_unlock(&srvlock);
242         atomic_inc(&nr_srvs);
243 }
244
245 static int srvwstat(struct chan *c, uint8_t * dp, int n)
246 {
247         error("srvwstat not supported yet");
248         return -1;
249 }
250
251 static void srvclose(struct chan *c)
252 {
253         struct srvfile *srv = c->aux;
254         if (!grab_ref(srv))
255                 return;
256         atomic_dec(&srv->opens);
257         kref_put(&srv->ref);
258 }
259
260 static void srvremove(struct chan *c)
261 {
262         struct srvfile *srv_i, *temp;
263
264         spin_lock(&srvlock);
265         TAILQ_FOREACH_SAFE(srv_i, &srvfiles, link, temp) {
266                 if (srv_i == c->aux) {
267                         TAILQ_REMOVE(&srvfiles, srv_i, link);
268                         break;
269                 }
270         }
271         spin_unlock(&srvlock);
272         if (srv_i)
273                 kref_put(&srv_i->ref);  /* dropping ref from the list */
274 }
275
276 /* N.B. srvopen gives the chan back. The only 'reading' we do
277  * in srv is of the top level directory.
278  */
279 static long srvread(struct chan *c, void *va, long count, int64_t offset)
280 {
281         return devdirread(c, va, count, 0, 0, srvgen);
282 }
283
284 static long srvwrite(struct chan *c, void *va, long count, int64_t offset)
285 {
286         ERRSTACK(2);
287         struct srvfile *srv;
288         struct chan *new_chan;
289         char *kbuf = 0;
290         int fd;
291
292         if (c->qid.type & QTDIR)
293                 error(Eperm);
294         srv = c->aux;
295         if (!grab_ref(srv))
296                 error("Unable to write srv file, concurrent removal");
297         if (waserror()) {
298                 kref_put(&srv->ref);
299                 nexterror();
300         }
301         if (srv->chan)
302                 error("srv file already has a stored chan!");
303         if (waserror()) {
304                 kfree(kbuf);
305                 nexterror();
306         }
307         kbuf = kmalloc(count + 1, KMALLOC_WAIT);
308         strncpy(kbuf, va, count);
309         kbuf[count] = 0;
310         fd = strtoul(kbuf, 0, 10);
311         /* the magic of srv: srv stores the chan corresponding to the fd.  -1 for
312          * mode, so we just get the chan with no checks (RDWR would work too). */
313         new_chan = fdtochan(&current->open_files, fd, -1, FALSE, TRUE);
314         /* fdtochan already increffed for us */
315         if (!__sync_bool_compare_and_swap(&srv->chan, 0, new_chan)) {
316                 cclose(new_chan);
317                 error("srv file already has a stored chan!");
318         }
319         poperror();
320         kfree(kbuf);
321         poperror();
322         kref_put(&srv->ref);
323         return count;
324 }
325
326 struct dev srvdevtab __devtab = {
327         's',
328         "srv",
329
330         devreset,
331         srvinit,
332         devshutdown,
333         srvattach,
334         srvwalk,
335         srvstat,
336         srvopen,
337         srvcreate,
338         srvclose,
339         srvread,
340         devbread,
341         srvwrite,
342         devbwrite,
343         srvremove,
344         srvwstat,
345         devpower,
346         devchaninfo,
347 };