;;; -*- Mode:LISP; Package:SYSTEM-INTERNALS; Base:10; Readtable:ZL -*- (DEFFLAVOR TEST-DISK-UNIT ((UNIT 0)) () :initable-instance-variables) (DEFMETHOD (TEST-DISK-UNIT :READ) (RQB ADDRESS) (FORMAT T "~&;;;; DISK READ ON UNIT ~D, ~D PAGE~P AT BLOCK ~D~%" UNIT (RQB-NPAGES RQB) (RQB-NPAGES RQB) ADDRESS) (DISK-READ RQB UNIT ADDRESS)) (DEFMETHOD (TEST-DISK-UNIT :WRITE) (RQB ADDRESS) (FORMAT T "~&;;;; DISK WRITE ON UNIT ~D, ~D PAGE~P AT BLOCK ~D~%" UNIT (RQB-NPAGES RQB) (RQB-NPAGES RQB) ADDRESS) (DISK-WRITE RQB UNIT ADDRESS)) (defmethod (test-disk-unit :dispose) () (format t "~&;;;; DISPOSE OF DISK UNIT~%")) (defmethod (test-disk-unit :unit-number) () (format t "~&;;;; UNIT NUMBER IS ~D~%" UNIT) unit) (defmethod (test-disk-unit :machine-name) () (format t "~&;;;; MACHINE NAME IS ~D~%" "FOO") "FOO") (tcp-application:define-tcp-server tcp-application:DISK "Read and write disk blocks" :toplevel-function 'serial-stream-disk-server :listen-port 101 :STREAM-GENERIC-P t) (defun open-tcp-disk-unit (machine &optional (why "randomness") (unit 0)) (check-type machine string) (let ((u (make-instance 'serial-stream-disk-unit :stream (open (string-append "TCP-HOST:" machine "#101")) :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) why)) u)) (defflavor serial-stream-disk-unit ((unit-number 0) (stream nil)(machine-name nil)) () :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 (send stream :tyi))) (cond ((eq r #\R)) ('else (send self :remote-error r))))) (defmethod (serial-stream-disk-unit :read) (rqb address) (send stream :tyo #\R) (transmit-32b unit-number stream) (transmit-32b (rqb-npages rqb) stream) (transmit-32b address stream) (send stream :force-output) (let ((r (send stream :tyi))) (cond ((eq r #\R) (receive-string stream (rqb-8-bit-buffer rqb))) ('else (send self :remote-error r)))) rqb) (defmethod (serial-stream-disk-unit :write) (rqb address) (send stream :tyo #\W) (transmit-32b unit-number stream) (transmit-32b (rqb-npages rqb) stream) (transmit-32b address stream) (transmit-string (rqb-8-bit-buffer rqb) stream) (send stream :force-output) (let ((r (send stream :tyi))) (cond ((eq r #\R)) ('else (send self :remote-error r)))) rqb) (defmethod (serial-stream-disk-unit :remote-error) (code) (cond ((null code) (ferror nil "serial disk unit end of file")) ((eq code #\E) (ferror nil "serial disk unit error: ~A" (receive-string stream))) ('else (ferror nil "internal bug, unknown error code serial disk unit: ~D" code)))) (defmethod (serial-stream-disk-unit :dispose) () (close stream)) (defmethod (serial-stream-disk-unit :tyi) () (send stream :tyi)) (defun serial-stream-disk-server (stream &aux rqb) ;; this should work over any reliable 8-bit byte stream. (unwind-protect (do ((opcode)) ((null (setq opcode (send stream :tyi)))) (selectq opcode ((#\R #\W) (let ((unit (receive-32b stream)) (n-pages (receive-32b stream)) (address (receive-32b stream))) (cond ((not rqb) (setq rqb (get-disk-rqb n-pages))) ((not (= n-pages (rqb-npages rqb))) (return-disk-rqb (prog1 rqb (setq rqb nil))) (setq rqb (get-disk-rqb n-pages)))) (selectq opcode (#\R (disk-read rqb unit address) (send stream :tyo #\R) (transmit-string (rqb-8-bit-buffer rqb) stream)) (#\W (receive-string stream (rqb-8-bit-buffer rqb)) (disk-write rqb unit address) (send stream :tyo #\R))))) (#\N (tv:notify nil "~A" (receive-string stream)) (send stream :tyo #\R)) (#\B (let ((quantum 17.) part-name part-base part-size part-comment sub-start sub-n top) (let (t (ferror nil "unknown opcode in serial disk stream"))) (send stream :force-output)) (and rqb (return-disk-rqb (prog1 rqb (setq rqb nil)))))) (SETQ STR (CHAOS:PKT-STRING (CHAOS:READ-PKTS CONN))) ;Look at the RFC (LET ((*READ-BASE* 10.)) ;RFC is BAND-TRANSFER READ/WRITE band subset size comment ;subset is NIL or list of rel start and n-blocks (SETQ TEM (READ-FROM-STRING (STRING-APPEND "(" STR ")")))) (AND (NULL BAND-TRANSFER-SERVER-ON) (NOT (MEMBER USER-ID '(NIL ""))) (RETURN (CHAOS:REJECT CONN (FORMAT NIL "This machine is in use by ~A" USER-ID)))) (MULTIPLE-VALUE (PART-BASE PART-SIZE NIL PART-NAME) (SYS:FIND-DISK-PARTITION (THIRD TEM))) (OR PART-BASE (RETURN (CHAOS:REJECT CONN (FORMAT NIL "No /"~A/" partition here." PART-NAME)))) (AND (FOURTH TEM) (SETQ SUB-START (FIRST (FOURTH TEM)) SUB-N (SECOND (FOURTH TEM)))) (COND ((STRING-EQUAL (SECOND TEM) "READ") (SETQ WRITE-P NIL) (SETQ PART-COMMENT (PARTITION-COMMENT PART-NAME 0))) ((STRING-EQUAL (SECOND TEM) "WRITE") (SETQ WRITE-P T) (OR ( (FIFTH TEM) PART-SIZE) (RETURN (CHAOS:REJECT CONN (FORMAT NIL "Partition too small, ~D>~D" (FIFTH TEM) PART-SIZE)))) (SETQ PART-SIZE (FIFTH TEM)) (SETQ PART-COMMENT (STRING (SIXTH TEM)))) ;Comment to store later (T (RETURN (CHAOS:REJECT CONN "Illegal operation, must be READ or WRITE")))) (AND SUB-START (OR (MINUSP SUB-START) (MINUSP SUB-N) (> (+ SUB-START SUB-N) PART-SIZE)) (RETURN (CHAOS:REJECT CONN "Subset outside of partition"))) (CHAOS:ACCEPT CONN) (AND (EQ BAND-TRANSFER-SERVER-ON ':NOTIFY) (PROCESS-RUN-FUNCTION "Notify" 'TV:NOTIFY NIL "BAND-TRANSFER-SERVER: ~:[READ~;WRITE~] of ~A partition by ~A" WRITE-P PART-NAME (CHAOS:HOST-DATA (CHAOS:FOREIGN-ADDRESS CONN)))) (FUNCALL TV:WHO-LINE-FILE-STATE-SHEET ':ADD-SERVER CONN "BAND-TRANSFER") (COND ((NOT WRITE-P) ;Send packet containing size, comment (SETQ PART-SIZE (MEASURED-SIZE-OF-PARTITION PART-NAME)) (SETQ PKT (CHAOS:GET-PKT)) (CHAOS:SET-PKT-STRING PKT (FORMAT NIL "~D ~S" PART-SIZE PART-COMMENT)) (CHAOS:SEND-PKT CONN PKT))) (AND SUB-START (SETQ PART-BASE (+ PART-BASE SUB-START) PART-SIZE SUB-N)) (COND (WRITE-P (UPDATE-PARTITION-COMMENT PART-NAME "Incomplete Copy" 0))) (SETQ RQB (SYS:GET-DISK-RQB QUANTUM) BUF (SYS:RQB-BUFFER RQB)) (SETQ DISK-ERROR-RETRY-COUNT 20.) ;Try to bypass hardware overrun problem (WIRE-DISK-RQB RQB) (SETQ TOP (+ PART-BASE PART-SIZE)) (DO ((BLOCK PART-BASE (+ BLOCK QUANTUM))) (( BLOCK TOP)) (AND (< (SETQ NB (- TOP BLOCK)) QUANTUM) (WIRE-DISK-RQB RQB (SETQ QUANTUM NB))) (COND ((NOT WRITE-P) ;This can modify pages without setting (DISK-READ-WIRED RQB 0 BLOCK) ; the modified bits, but as long as ; we dont depend on data after its unwired, ; it wont hurt. (ARRAY-TO-NET BUF CONN (* QUANTUM PAGE-SIZE 2))) (T (ARRAY-FROM-NET BUF CONN (* QUANTUM PAGE-SIZE 2)) (DISK-WRITE-WIRED RQB 0 BLOCK)))) (CHAOS:FINISH-CONN CONN) (CHAOS:CLOSE-CONN CONN "Done") (AND WRITE-P (UPDATE-PARTITION-COMMENT PART-NAME PART-COMMENT 0)))) (AND RQB (SYS:RETURN-DISK-RQB RQB)) (AND CONN (PROGN (FUNCALL TV:WHO-LINE-FILE-STATE-SHEET ':DELETE-SERVER CONN) (CHAOS:REMOVE-CONN CONN)))) NIL))