1implement Styxlib; 2 3# 4# Copyright © 1999 Vita Nuova Limited. All rights reserved. 5# Revisions copyright © 2002 Vita Nuova Holdings Limited. All rights reserved. 6# 7 8include "sys.m"; 9 sys: Sys; 10include "styx.m"; 11 styx: Styx; 12 Tmsg, Rmsg: import styx; 13 14include "styxlib.m"; 15 16CHANHASHSIZE: con 32; 17starttime: int; 18timefd: ref Sys->FD; 19 20DEBUG: con 0; 21 22init(s: Styx): string 23{ 24 sys = load Sys Sys->PATH; 25 styx = s; # our caller inits 26 return nil; 27} 28 29Styxserver.new(fd: ref Sys->FD): (chan of ref Tmsg, ref Styxserver) 30{ 31 starttime = now(); 32 srv := ref Styxserver(fd, array[CHANHASHSIZE] of list of ref Chan, getuname(), 0); 33 if(fd == nil) 34 return (nil, srv); 35 tchan := chan of ref Tmsg; 36 sync := chan of int; 37 spawn tmsgreader(fd, srv, tchan, sync); 38 <-sync; 39 return (tchan, srv); 40} 41 42now(): int 43{ 44 if(timefd == nil){ 45 timefd = sys->open("/dev/time", sys->OREAD); 46 if(timefd == nil) 47 return 0; 48 } 49 buf := array[64] of byte; 50 sys->seek(timefd, big 0, 0); 51 n := sys->read(timefd, buf, len buf); 52 if(n < 0) 53 return 0; 54 55 t := (big string buf[0:n]) / big 1000000; 56 return int t; 57} 58 59 60getuname(): string 61{ 62 if ((fd := sys->open("/dev/user", Sys->OREAD)) == nil) 63 return "unknown"; 64 buf := array[Sys->NAMEMAX] of byte; 65 n := sys->read(fd, buf, len buf); 66 if (n <= 0) 67 return "unknown"; 68 return string buf[0:n]; 69} 70 71tmsgreader(fd: ref Sys->FD, srv: ref Styxserver, tchan: chan of ref Tmsg, sync: chan of int) 72{ 73 sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil); 74 sync <-= 1; 75 fd = sys->fildes(fd.fd); 76 while((m := Tmsg.read(fd, srv.msize)) != nil && tagof m != tagof Tmsg.Readerror){ 77 tchan <-= m; 78 m = nil; 79 } 80 tchan <-= m; 81} 82 83Styxserver.reply(srv: self ref Styxserver, m: ref Rmsg): int 84{ 85 if (DEBUG) 86 sys->fprint(sys->fildes(2), "%s\n", m.text()); 87 a := m.pack(); 88 if(a == nil) 89 return -1; 90 return sys->write(srv.fd, a, len a); 91} 92 93Styxserver.devversion(srv: self ref Styxserver, m: ref Tmsg.Version): int 94{ 95 if(srv.msize <= 0) 96 srv.msize = Styx->MAXRPC; 97 (msize, version) := styx->compatible(m, srv.msize, Styx->VERSION); 98 if(msize < 128){ 99 srv.reply(ref Rmsg.Error(m.tag, "unusable message size")); 100 return -1; 101 } 102 srv.msize = msize; 103 srv.reply(ref Rmsg.Version(m.tag, msize, version)); 104 return 0; 105} 106 107Styxserver.devauth(srv: self ref Styxserver, m: ref Tmsg.Auth) 108{ 109 srv.reply(ref Rmsg.Error(m.tag, "authentication not required")); 110} 111 112Styxserver.devattach(srv: self ref Styxserver, m: ref Tmsg.Attach): ref Chan 113{ 114 c := srv.newchan(m.fid); 115 if (c == nil) { 116 srv.reply(ref Rmsg.Error(m.tag, Einuse)); 117 return nil; 118 } 119 c.uname = m.uname; 120 c.qid.qtype = Sys->QTDIR; 121 c.qid.path = big 0; 122 c.path = "dev"; 123 srv.reply(ref Rmsg.Attach(m.tag, c.qid)); 124 return c; 125} 126 127Styxserver.clone(srv: self ref Styxserver, oc: ref Chan, newfid: int): ref Chan 128{ 129 c := srv.newchan(newfid); 130 if (c == nil) 131 return nil; 132 c.qid = oc.qid; 133 c.uname = oc.uname; 134 c.open = oc.open; 135 c.mode = oc.mode; 136 c.path = oc.path; 137 c.data = oc.data; 138 return c; 139} 140 141Styxserver.devflush(srv: self ref Styxserver, m: ref Tmsg.Flush) 142{ 143 srv.reply(ref Rmsg.Flush(m.tag)); 144} 145 146Styxserver.devwalk(srv: self ref Styxserver, m: ref Tmsg.Walk, 147 gen: Dirgenmod, tab: array of Dirtab): ref Chan 148{ 149 c := srv.fidtochan(m.fid); 150 if (c == nil) { 151 srv.reply(ref Rmsg.Error(m.tag, Ebadfid)); 152 return nil; 153 } 154 if (c.open) { 155 srv.reply(ref Rmsg.Error(m.tag, Eopen)); 156 return nil; 157 } 158 if (!c.isdir()) { 159 srv.reply(ref Rmsg.Error(m.tag, Enotdir)); 160 return nil; 161 } 162 # should check permissions here? 163 qids: array of Sys->Qid; 164 cc := ref *c; # walk a temporary copy 165 if(len m.names > 0){ 166 qids = array[len m.names] of Sys->Qid; 167 for(i := 0; i < len m.names; i++){ 168 for(k := 0;; k++){ 169 (ok, d) := gen->dirgen(srv, cc, tab, k); 170 if(ok < 0){ 171 if(i == 0) 172 srv.reply(ref Rmsg.Error(m.tag, Enotfound)); 173 else 174 srv.reply(ref Rmsg.Walk(m.tag, qids[0:i])); 175 return nil; 176 } 177 if (d.name == m.names[i]) { 178 cc.qid = d.qid; 179 cc.path = d.name; 180 qids[i] = cc.qid; 181 break; 182 } 183 } 184 } 185 } 186 # successful walk 187 if(m.newfid != m.fid){ 188 # clone/walk 189 nc := srv.clone(cc, m.newfid); 190 if(nc == nil){ 191 srv.reply(ref Rmsg.Error(m.tag, Einuse)); 192 return nil; 193 } 194 c = nc; 195 }else{ 196 # walk c itself 197 c.qid = cc.qid; 198 c.path = cc.path; 199 } 200 srv.reply(ref Rmsg.Walk(m.tag, qids)); 201 return c; 202} 203 204Styxserver.devclunk(srv: self ref Styxserver, m: ref Tmsg.Clunk): ref Chan 205{ 206 c := srv.fidtochan(m.fid); 207 if (c == nil) { 208 srv.reply(ref Rmsg.Error(m.tag, Ebadfid)); 209 return nil; 210 } 211 srv.chanfree(c); 212 srv.reply(ref Rmsg.Clunk(m.tag)); 213 return c; 214} 215 216Styxserver.devstat(srv: self ref Styxserver, m: ref Tmsg.Stat, 217 gen: Dirgenmod, tab: array of Dirtab) 218{ 219 c := srv.fidtochan(m.fid); 220 if (c == nil) { 221 srv.reply(ref Rmsg.Error(m.tag, Ebadfid)); 222 return; 223 } 224 i := 0; 225 (ok, d) := gen->dirgen(srv, c, tab, i++); 226 while (ok >= 0) { 227 if (ok > 0 && c.qid.path == d.qid.path) { 228 srv.reply(ref Rmsg.Stat(m.tag, d)); 229 return; 230 } 231 (ok, d) = gen->dirgen(srv, c, tab, i++); 232 } 233 # auto-generate entry for directory if not found. 234 # XXX this is asking for trouble, as the permissions given 235 # on stat() of a directory can be different from those given 236 # when reading the directory's entry in its parent dir. 237 if (c.qid.qtype & Sys->QTDIR) 238 srv.reply(ref Rmsg.Stat(m.tag, devdir(c, c.qid, c.path, big 0, srv.uname, Sys->DMDIR|8r555))); 239 else 240 srv.reply(ref Rmsg.Error(m.tag, Enotfound)); 241} 242 243Styxserver.devdirread(srv: self ref Styxserver, m: ref Tmsg.Read, 244 gen: Dirgenmod, tab: array of Dirtab) 245{ 246 c := srv.fidtochan(m.fid); 247 if (c == nil) { 248 srv.reply(ref Rmsg.Error(m.tag, Ebadfid)); 249 return; 250 } 251 offset := int m.offset; 252 data := array[m.count] of byte; 253 start := 0; 254 n := 0; 255 for (k := 0;; k++) { 256 (ok, d) := gen->dirgen(srv, c, tab, k); 257 if(ok < 0){ 258 srv.reply(ref Rmsg.Read(m.tag, data[0:n])); 259 return; 260 } 261 size := styx->packdirsize(d); 262 if(start < offset){ 263 start += size; 264 continue; 265 } 266 if(n+size > m.count) 267 break; 268 data[n:] = styx->packdir(d); 269 n += size; 270 } 271 srv.reply(ref Rmsg.Read(m.tag, data[0:n])); 272} 273 274Styxserver.devopen(srv: self ref Styxserver, m: ref Tmsg.Open, 275 gen: Dirgenmod, tab: array of Dirtab): ref Chan 276{ 277 c := srv.fidtochan(m.fid); 278 if (c == nil) { 279 srv.reply(ref Rmsg.Error(m.tag, Ebadfid)); 280 return nil; 281 } 282 omode := m.mode; 283 i := 0; 284 (ok, d) := gen->dirgen(srv, c, tab, i++); 285 while (ok >= 0) { 286 # XXX dev.c checks vers as well... is that desirable? 287 if (ok > 0 && c.qid.path == d.qid.path) { 288 if (openok(omode, d.mode, c.uname, d.uid, d.gid)) { 289 c.qid.vers = d.qid.vers; 290 break; 291 } 292 srv.reply(ref Rmsg.Error(m.tag, Eperm)); 293 return nil; 294 } 295 (ok, d) = gen->dirgen(srv, c, tab, i++); 296 } 297 if ((c.qid.qtype & Sys->QTDIR) && omode != Sys->OREAD) { 298 srv.reply(ref Rmsg.Error(m.tag, Eperm)); 299 return nil; 300 } 301 if ((c.mode = openmode(omode)) == -1) { 302 srv.reply(ref Rmsg.Error(m.tag, Ebadarg)); 303 return nil; 304 } 305 c.open = 1; 306 c.mode = omode; 307 srv.reply(ref Rmsg.Open(m.tag, c.qid, Styx->MAXFDATA)); 308 return c; 309} 310 311Styxserver.devremove(srv: self ref Styxserver, m: ref Tmsg.Remove): ref Chan 312{ 313 c := srv.fidtochan(m.fid); 314 if (c == nil) { 315 srv.reply(ref Rmsg.Error(m.tag, Ebadfid)); 316 return nil; 317 } 318 srv.chanfree(c); 319 srv.reply(ref Rmsg.Error(m.tag, Eperm)); 320 return c; 321} 322 323Styxserver.fidtochan(srv: self ref Styxserver, fid: int): ref Chan 324{ 325 for (l := srv.chans[fid & (CHANHASHSIZE-1)]; l != nil; l = tl l) 326 if ((hd l).fid == fid) 327 return hd l; 328 return nil; 329} 330 331Styxserver.chanfree(srv: self ref Styxserver, c: ref Chan) 332{ 333 slot := c.fid & (CHANHASHSIZE-1); 334 nl: list of ref Chan; 335 for (l := srv.chans[slot]; l != nil; l = tl l) 336 if ((hd l).fid != c.fid) 337 nl = (hd l) :: nl; 338 srv.chans[slot] = nl; 339} 340 341Styxserver.chanlist(srv: self ref Styxserver): list of ref Chan 342{ 343 cl: list of ref Chan; 344 for (i := 0; i < len srv.chans; i++) 345 for (l := srv.chans[i]; l != nil; l = tl l) 346 cl = hd l :: cl; 347 return cl; 348} 349 350Styxserver.newchan(srv: self ref Styxserver, fid: int): ref Chan 351{ 352 # fid already in use 353 if ((c := srv.fidtochan(fid)) != nil) 354 return nil; 355 c = ref Chan; 356 c.qid = Sys->Qid(big 0, 0, Sys->QTFILE); 357 c.open = 0; 358 c.mode = 0; 359 c.fid = fid; 360 slot := fid & (CHANHASHSIZE-1); 361 srv.chans[slot] = c :: srv.chans[slot]; 362 return c; 363} 364 365devdir(nil: ref Chan, qid: Sys->Qid, name: string, length: big, 366 user: string, perm: int): Sys->Dir 367{ 368 d: Sys->Dir; 369 d.name = name; 370 d.qid = qid; 371 d.dtype = 'X'; 372 d.dev = 0; # XXX what should this be? 373 d.mode = perm; 374 if (qid.qtype & Sys->QTDIR) 375 d.mode |= Sys->DMDIR; 376 d.atime = starttime; # XXX should be better than this. 377 d.mtime = starttime; 378 d.length = length; 379 d.uid = user; 380 d.gid = user; 381 return d; 382} 383 384readbytes(m: ref Tmsg.Read, d: array of byte): ref Rmsg.Read 385{ 386 r := ref Rmsg.Read(m.tag, nil); 387 offset := int m.offset; 388 if (offset >= len d) 389 return r; 390 e := offset + m.count; 391 if (e > len d) 392 e = len d; 393 r.data = d[offset:e]; 394 return r; 395} 396 397readnum(m: ref Tmsg.Read, val, size: int): ref Rmsg.Read 398{ 399 return readbytes(m, sys->aprint("%-*d", size, val)); 400} 401 402readstr(m: ref Tmsg.Read, d: string): ref Rmsg.Read 403{ 404 return readbytes(m, array of byte d); 405} 406 407dirgenmodule(): Dirgenmod 408{ 409 return load Dirgenmod "$self"; 410} 411 412dirgen(srv: ref Styxserver, c: ref Styxlib->Chan, 413 tab: array of Dirtab, i: int): (int, Sys->Dir) 414{ 415 d: Sys->Dir; 416 if (tab == nil || i >= len tab) 417 return (-1, d); 418 return (1, devdir(c, tab[i].qid, tab[i].name, tab[i].length, srv.uname, tab[i].perm)); 419} 420 421openmode(o: int): int 422{ 423 OTRUNC, ORCLOSE, OREAD, ORDWR: import Sys; 424 if(o >= (OTRUNC|ORCLOSE|ORDWR)) 425 return -1; 426 o &= ~(OTRUNC|ORCLOSE); 427 if(o > ORDWR) 428 return -1; 429 return o; 430} 431 432access := array[] of {8r400, 8r200, 8r600, 8r100}; 433openok(omode, perm: int, uname, funame, nil: string): int 434{ 435 # XXX what should we do about groups? 436 # this is inadequate anyway: 437 # OTRUNC 438 # user should be allowed to open it if permission 439 # is allowed to others. 440 mode: int; 441 if (uname == funame) 442 mode = perm; 443 else 444 mode = perm << 6; 445 446 t := access[omode & 3]; 447 return ((t & mode) == t); 448} 449 450Chan.isdir(c: self ref Chan): int 451{ 452 return (c.qid.qtype & Sys->QTDIR) != 0; 453} 454