#lang racket (require (planet murphy/packed-io:1:1) "../main.rkt" "../errno.rkt" (only-in "../network/message.rkt" max-message-size) "data.rkt") (define server-file-handle% (class* object% (file-handle<%>) (super-new) (init-field file context [current-i/o-state #f]) (define/public-final (->file) (or file (raise-9p-error ESTALE))) (define/public-final (->context) context) (define/public-final walk (case-lambda [() (walk-self)] [(name) (if (string=? name "..") (walk-parent) (walk-child name))] [names (foldl (λ (name file) (send file walk name)) this names)])) (define/public-final (walk-self) (let ([file (->file)] [context (->context)]) (send file attach context))) (define/public-final (walk-parent) (let ([file (->file)] [context (->context)]) (send (send file parent) attach context))) (define/public (walk-child name) (raise-9p-error ENOTDIR)) (define/public-final (read-stat) (let ([file (->file)] [context (->context)]) (send file read-stat context))) (define/public-final (write-stat new-stat) (let* ([file (->file)] [context (->context)] [mode (match new-stat [(struct stat (#f #f #f (and mode (or #f (? (λ (mode) (eq? (zero? (bitwise-and (file-mode-type mode) (file-type dir))) (not (is-a? file server-directory%))))))) _ mtime (and length (or #f 0)) name #f (and gid (or #f (? (λ (gid) (send context in-group? gid))))) #f)) (filter symbol? (list (and mode (touch-mode mode)) (and mtime (touch-mode mtime)) (and length (touch-mode length)) (and name (touch-mode name)) (and gid (touch-mode gid))))] [_ (raise-9p-error EINVAL)])]) (if (send context can-touch? file mode) (send file write-stat context new-stat) (raise-9p-error EACCESS)))) (define/public-final (i/o-state) current-i/o-state) (define/public-final (open mode) (if (not current-i/o-state) (let ([file (->file)] [context (->context)]) (if (send context can-access? file mode) (let-values ([(i/o-state i/o-unit) (send file open context mode)]) (set! current-i/o-state i/o-state) i/o-unit) (raise-9p-error EACCESS))) (raise-9p-error EISCONN))) (define/public-final (read size offset) (if current-i/o-state (send (->file) read (->context) (i/o-state) size offset) (raise-9p-error ENOTCONN))) (define/public-final (write data offset) (if current-i/o-state (send (->file) write (->context) (i/o-state) data offset) (raise-9p-error ENOTCONN))) (define/private (invalidate thunk) (when file (let ([pending-exn #f]) (define (recording-exceptions thunk) (let/ec return (call-with-exception-handler (λ (exn) (set! pending-exn exn) (return)) thunk))) (when current-i/o-state (recording-exceptions (λ () (send (->file) clunk (->context) (i/o-state)))) (set! current-i/o-state #f)) (recording-exceptions (λ () (inner (void) clunk))) (recording-exceptions thunk) (set! file #f) (when pending-exn (raise pending-exn))))) (define/pubment (clunk) (invalidate void)) (define/public-final (remove) (invalidate (λ () (let ([file (->file)] [context (->context)]) (if (send context can-remove? file) (send file remove context) (raise-9p-error EACCESS)))))) )) (define server-directory-handle% (class* server-file-handle% (directory-handle<%>) (super-new) (inherit ->file ->context) (define/override-final (walk-child name) (let ([file (->file)] [context (->context)]) (if (send context can-access? file (open-mode x)) (send (send file child name) attach context) (raise-9p-error EACCESS)))) (define/public-final (in-entries) (let ([file (->file)] [context (->context)]) (if (send context can-access? file (open-mode r)) (send file in-entries context) (raise-9p-error EACCESS)))) (define/public-final (create name perm mode) (let ([file (->file)] [context (->context)]) (if (send context can-access? file (open-mode w)) (send (send file create context name perm mode) attach context mode) (raise-9p-error EACCESS)))) )) (define server-file% (class object% (super-new) (define/public (parent) (raise-9p-error ENOSYS)) (define/public (attach context [mode #f]) (let*-values ([(i/o-state i/o-unit) (if mode (open context mode) (values #f #f))] [(handle) (new server-file-handle% [file this] [context context] [current-i/o-state i/o-state])]) (if mode (values handle (or i/o-unit 0)) handle))) (define/public (read-stat context) (raise-9p-error ENOSYS)) (define/public (write-stat context stat) (raise-9p-error EROFS)) (define/public (open context mode) (values #t (- (max-message-size) 24))) (define/public (read context i/o-state size offset) eof) (define/public (write context i/o-state data offset) (raise-9p-error EROFS)) (define/public (clunk context i/o-state) (void)) (define/public (remove context) (raise-9p-error EROFS)) )) (define server-file<%> (class->interface server-file%)) (define server-file-cursor% (class object% (super-new) (init-field [current-offset 0] [with-i/o-unit (- (max-message-size) 24)]) (define/public-final offset (case-lambda [() (or current-offset (raise-9p-error ENOTCONN))] [(position) (set! current-offset position)])) (define/public-final (i/o-unit) with-i/o-unit) (define/pubment (read size [at-offset (offset)]) (if current-offset (let ([data (inner eof read (min size (i/o-unit)) at-offset)]) (when (bytes? data) (offset (+ at-offset (bytes-length data)))) data) (raise-9p-error ENOTCONN))) (define/pubment (write data [at-offset (offset)]) (if current-offset (let ([size (inner (raise-9p-error EROFS) write data at-offset)]) (offset (+ at-offset size)) size) (raise-9p-error ENOTCONN))) (define/pubment (clunk) (when current-offset (dynamic-wind void (λ () (inner (void) clunk)) (λ () (set! current-offset #f))))) )) (define server-file:cursor-mixin (mixin (server-file<%>) () (super-new) (define/public (make-cursor context mode) (raise-9p-error ENOSYS)) (define/override-final (open context mode) (let ([cursor (make-cursor context mode)]) (values cursor (send cursor i/o-unit)))) (define/override-final (read context cursor size offset) (send cursor read size offset)) (define/override-final (write context cursor data offset) (send cursor write data offset)) (define/override-final (clunk context cursor) (send cursor clunk)) )) (define server-directory-cursor% (class server-file-cursor% (super-new) (init-field entries) (inherit i/o-unit offset) (define-values (peek-entry drop-entry!) (values #f #f)) (define/private (reset) (set!-values (peek-entry drop-entry!) (let-values ([(has-next-entry? next-entry) (sequence-generate entries)] [(buffered-entry) #f]) (values (λ () (unless buffered-entry (set! buffered-entry (if (has-next-entry?) (pack stat/p (next-entry)) eof))) buffered-entry) (λ () (set! buffered-entry #f)))))) (define/augment-final (read size at-offset) (cond [(zero? at-offset) (reset)] [(not (= at-offset (offset))) (raise-9p-error ESPIPE)]) (call-with-output-bytes (λ (out) (let more () (let ([pos (file-position out)] [entry (peek-entry)]) (when (and (not (eof-object? entry)) (<= (+ pos (bytes-length entry)) size)) (drop-entry!) (write-bytes entry out) (more))))))) (define/augment-final (write data offset) (raise-9p-error EISDIR)) (define/augment (clunk) (inner (void) clunk) (set!-values (peek-entry drop-entry!) (values #f #f))) )) (define server-directory% (class (server-file:cursor-mixin server-file%) (super-new) (define/public (child name) (raise-9p-error ENOENT)) (define/public (in-entries context) (in-list null)) (define/override-final (make-cursor context mode) (new server-directory-cursor% [entries (in-entries context)])) (define/override (attach context [mode #f]) (let ([handle (new server-directory-handle% [file this] [context context])]) (if mode (values handle 0) handle))) (define/public (create context name perm mode) (raise-9p-error EROFS)) )) (define server-directory<%> (class->interface server-directory%)) (provide server-file-handle% server-directory-handle% server-file<%> server-file% server-file-cursor% server-file:cursor-mixin server-directory-cursor% server-directory<%> server-directory%)