;;; -*- Mode:LISP; Package:TCP-APPLICATION; Base:10; Readtable:CL -*- #| Copyright LISP Machine, Inc. 1985, 1986, 1987 See filename "Copyright.Text" for licensing and release information. This random access disk protocol gives us about 20Kbytes/second. About 17 minutes to transfer a band of 20Kblocks using SI:COPY-DISK-PARTITION. We could do twice as well with a simple disk partition stream and stream-copy-until-eof. |# (define-network-service *tcp-disk-service* :disk :tcp "Read and write disk blocks" :toplevel-function 'serial-stream-disk-server :listen-port (sym ipport-lmidisk) :auto-enable? t) (define-network-function (net:get-remote-disk-unit :internet) (host unit usage &optional initp writep) (declare (ignore initp)) (let ((machine (send host :name)) (stream) (normalp)) (unwind-protect (let ((u (make-instance 'serial-stream-disk-unit :stream (setq stream (open-easy-tcp-stream machine (sym-value 'ipport-lmidisk) nil :keyword "Remote DISK access" :input-buffers (if writep 1 16.) :output-buffers (if writep 16. 1))) :unit-number unit :machine-name machine))) (send u :notify (format nil "Disk being hacked remotely by ~A@~A -- ~A" si:user-id (send si:local-host :name) usage)) (prog1 u (setq normalp t))) (or normalp (not stream) (close stream :abort t))))) (defflavor serial-stream-disk-unit ((unit-number 0) (stream nil) (machine-name nil) (anticipate-operation nil) anticipate-address anticipated-operations anticipate-rqb-npages) () :initable-instance-variables :gettable-instance-variables) (defun transmit-32b (word stream) (do ((j 0 (+ j 8))) ((= j 32)) (send stream :tyo (ldb (byte 8 j) word)))) (defun receive-32b (stream) (do ((j 0 (+ j 8)) (word 0 (dpb (send stream :tyi) (byte 8 j) word))) ((= j 32) word))) (defun transmit-string (string stream) (transmit-32b (length string) stream) (send stream :string-out string)) (defun receive-string (stream &optional to-string) (let* ((length (receive-32b stream)) (string (or to-string (make-string length)))) (send stream :string-in nil string) string)) (defmethod (serial-stream-disk-unit :notify) (string) (send stream :tyo #\N) (transmit-string string stream) (send stream :force-output) (let ((r (int-char-if-any (send stream :tyi)))) (cond ((eq r #\R)) (t (send self :remote-error r))))) (defmethod (serial-stream-disk-unit :read) (rqb address) (cond ((not anticipate-operation) (send stream :tyo #\R) (transmit-32b unit-number stream) (transmit-32b (si:rqb-npages rqb) stream) (transmit-32b address stream) (send stream :force-output) (let ((r (int-char-if-any (send stream :tyi)))) (cond ((eq r #\R) (receive-string stream (si:rqb-8-bit-buffer rqb))) (t (send self :remote-error r))))) ((and (eq anticipate-operation :read) (= anticipate-address address) (= anticipate-rqb-npages (si:rqb-npages rqb))) (receive-string stream (si:rqb-8-bit-buffer rqb)) (decf anticipated-operations) (incf anticipate-address anticipate-rqb-npages) (when (zerop anticipated-operations) (setq anticipate-operation nil))) (t (send self :anticipation-protocol-violation :read rqb address))) rqb) (defmethod (serial-stream-disk-unit :write) (rqb address) (cond ((not anticipate-operation) (send stream :tyo #\W) (transmit-32b unit-number stream) (transmit-32b (si:rqb-npages rqb) stream) (transmit-32b address stream) (transmit-string (si:rqb-8-bit-buffer rqb) stream) (send stream :force-output) (let ((r (int-char-if-any (send stream :tyi)))) (cond ((eq r #\R)) (t (send self :remote-error r))))) ((and (eq anticipate-operation :write) (= anticipate-address address) (= anticipate-rqb-npages (si:rqb-npages rqb))) (transmit-string (si:rqb-8-bit-buffer rqb) stream) (decf anticipated-operations) (incf anticipate-address anticipate-rqb-npages) (when (zerop anticipated-operations) (setq anticipate-operation nil) (send stream :force-output))) (t (send self :anticipation-protocol-violation :write rqb address))) rqb) (defmethod (serial-stream-disk-unit :anticipate-operations) (kind start-address n-operations rqb-npages) (setq anticipate-operation kind) (setq anticipate-address start-address) (setq anticipated-operations n-operations) (setq anticipate-rqb-npages rqb-npages) (send stream :tyo #\A) (send stream :tyo (ecase kind (:read #\R) (:write #\W))) (transmit-32b unit-number stream) (transmit-32b rqb-npages stream) (transmit-32b start-address stream) (transmit-32b n-operations stream) (send stream :force-output)) (defmethod (serial-stream-disk-unit :remote-error) (code) (cond ((null code) (error "serial disk unit end of file")) ((eq code #\E) (error "serial disk unit error: ~A" (receive-string stream))) (t (error "internal bug, unknown error code serial disk unit: ~D" code)))) (defmethod (serial-stream-disk-unit :anticipation-protocol-violation) (op rqb address) (error "Protocol violation: Mismatch in anticipated operation, expected ~S of ~D pages at ~D~ ~%But was requested to ~S of ~D pages at ~D" anticipate-operation anticipate-rqb-npages anticipate-address op (si:rqb-npages rqb) address)) (defmethod (serial-stream-disk-unit :dispose) () (close stream :abort t)) (defun serial-stream-disk-server (stream) ;; This allows someone to set up the password database to allow disk transfers ;; only from certain hosts. Add an initial password negotiation in future. (or (validate-network-server-password "FOO" "BAR" si:local-host) (return-from serial-stream-disk-server nil)) (serial-stream-disk-server-internal stream)) (defun serial-stream-disk-server-internal (stream &aux rqb) (unwind-protect (do ((opcode)) ((null (setq opcode (int-char-if-any (send stream :tyi))))) (ecase opcode ((#\R #\W) (let ((unit (receive-32b stream)) (n-pages (receive-32b stream)) (address (receive-32b stream))) (cond ((not rqb) (setq rqb (si:get-disk-rqb n-pages))) ((not (= n-pages (si:rqb-npages rqb))) (si:return-disk-rqb (prog1 rqb (setq rqb nil))) (setq rqb (si:get-disk-rqb n-pages)))) (ecase opcode (#\R (si:disk-read rqb unit address) (send stream :tyo #\R) (transmit-string (si:rqb-8-bit-buffer rqb) stream)) (#\W (receive-string stream (si:rqb-8-bit-buffer rqb)) (si:disk-write rqb unit address) (send stream :tyo #\R))))) (#\A (let ((op (int-char-if-any (send stream :tyi))) (unit (receive-32b stream)) (n-pages (receive-32b stream)) (address (receive-32b stream)) (n-operations (receive-32b stream))) (cond ((not rqb) (setq rqb (si:get-disk-rqb n-pages))) ((not (= n-pages (si:rqb-npages rqb))) (si:return-disk-rqb (prog1 rqb (setq rqb nil))) (setq rqb (si:get-disk-rqb n-pages)))) (send stream :build-more-buffers (if (eq op #\W) 16. 0) ;Network input buffers (if (eq op #\R) 16. 0)) ;Network output buffers (dotimes (j n-operations) (ecase op (#\R (si:disk-read rqb unit address) (incf address n-pages) (transmit-string (si:rqb-8-bit-buffer rqb) stream)) (#\W (receive-string stream (si:rqb-8-bit-buffer rqb)) (si:disk-write rqb unit address) (incf address n-pages)))))) (#\N (tv:notify nil "~A" (receive-string stream)) (send stream :tyo #\R))) (send stream :force-output)) (and rqb (si:return-disk-rqb (prog1 rqb (setq rqb nil))))))