;;; -*- Mode:LISP; Package:USER; Readtable:ZL; Base:10; Fonts:(CPTFONTB) -*- ;;; RAMDISK, FOR TESTING PROGRAMS THAT DEAL WITH THE DISK. ;;; 29-May-86 16:39:40 -GJC ;;; FILEDISK, to make a file on the local file system appear as a disk. ;;; 11-Jun-86 12:50:28 -GJC ;;; Copyright LISP Machine, Inc. 1986 ;;; See filename "Copyright.Text" for ;;; licensing and release information. ;;; ********************************************************* ;;; ********************************************************* ;;; *** NOTE: This is an EXAMPLE, not LMI supported code. *** ;;; *** information contained in this example is subject *** ;;; *** to change without notice. The ways of doing *** ;;; *** the things contained in the example may change *** ;;; *** between system releases. Some techniques which *** ;;; *** are mere examples in one release may become built *** ;;; *** in system features in the next release. Use good *** ;;; *** judgement when copying these techniques. Most *** ;;; *** examples have been motivated by specific customer *** ;;; *** requests, and may not be the best engineered *** ;;; *** or most efficient solution for someone else. *** ;;; ********************************************************* ;;; ********************************************************* (SI:DEFINE-HOST "RAMDISK" :HOST-NAMES '("RAMDISK") :MACHINE-TYPE :KLUDGE :SYSTEM-TYPE :KLUDGE :RAMDISK '(T)) (PUSHNEW :RAMDISK NETWORK:*NETWORK-PROTOCOLS*) (SI:DEFINE-HOST "FILEDISK" :host-names '("FILEDISK") :MACHINE-TYPE :KLUDGE :system-type :KLUDGE :filedisk '(t)) (PUSHNEW :FILEDISK NETWORK:*NETWORK-PROTOCOLS*) (DEFVAR *RAMDISK* NIL) (NET:DEFINE-NETWORK-FUNCTION (NETI:GET-REMOTE-DISK-UNIT :RAMDISK) (HOST DISK-UNIT USE INITP WRITE-P) USE INITP WRITE-P (OR (CADR (ASSQ DISK-UNIT *RAMDISK*)) (FERROR NIL "~A unit ~S not available" HOST DISK-UNIT))) (DEFUN ENABLE-RAMDISK (SIZE UNIT) (CAR (PUSH (LIST UNIT (MAKE-INSTANCE 'RAMDISK :BLOCKS SIZE)) *RAMDISK*))) (DEFFLAVOR RAMDISK (STRING BLOCKS (UNIT 0)) () (:INITABLE-INSTANCE-VARIABLES BLOCKS)) (DEFMETHOD (RAMDISK :AFTER :INIT) (PLIST) PLIST (WRITE-DATA (SETQ STRING (MAKE-ARRAY (* BLOCKS 1024) :TYPE 'ART-STRING)) '(STRING 1) 0 "LABL" ; MAGIC 'INTEGER 1 1 ; LABEL VERSION 'INTEGER 2 1 ; NUMBER OF CYLINDERS 'INTEGER 3 1 ; NUMBER OF HEADS 'INTEGER 4 BLOCKS ; NUMBER OF BLOCKS PER TRACK 'INTEGER 5 BLOCKS ; NUMBER OF BLOCKS PER CYLINDER '(STRING 1) 6 "" ; DEFAULT UCODE '(STRING 1) 7 "" ; DEFAULT LOAD '(STRING 8) 8 (FORMAT NIL "Ramdisk-~D" (ROUND BLOCKS 100)) ; DRIVE MAKE '(STRING 8) 16 "(name)" ; PACK NAME '(STRING 25) 24 "virtual disk drive" ; COMMENT 'INTEGER 128 1 ; NUMBER OF PARTITIONS 'INTEGER 129 7 ; NUMBER OF WORDS PER PARTITION DESCRIPTOR '(STRING 1) 130 "DATA" ; PARTITION DESCRIPTOR 0 - NAME 'INTEGER 131 6 ; - START 'INTEGER 132 (- BLOCKS 6) ; - LENGTH '(STRING 4) 133 "empty partition")) ; - COMMENT (defvar *ramdisk-blt-p* t) (DEFMETHOD (RAMDISK :READ) (RQB ADDRESS) (cond (*ramdisk-blt-p* (check-arg address (and (not (< address 0)) (not (> (+ (si:rqb-npages rqb) address) blocks))) "a legal disk address") (let ((word-address (* address 256))) (%blt (%pointer-plus (%pointer-plus string (si:array-data-offset string)) word-address) (%pointer-plus (%pointer-plus rqb (si:array-data-offset rqb)) (quotient (si:array-index-offset (si:rqb-buffer rqb)) 2)) (* (si:rqb-npages rqb) 256) 1))) ('else (LET ((byte-address (* address 1024)) (NBYTES (* (SI:RQB-NPAGES RQB) 1024))) (COPY-ARRAY-PORTION STRING byte-address (+ byte-address NBYTES) (SI:RQB-8-BIT-BUFFER RQB) 0 NBYTES))))) (DEFMETHOD (RAMDISK :WRITE) (RQB ADDRESS) (cond (*ramdisk-blt-p* (check-arg address (and (not (< address 0)) (not (> (+ (si:rqb-npages rqb) address) blocks))) "a legal disk address") (let ((word-address (* address 256))) (%blt (%pointer-plus (%pointer-plus rqb (si:array-data-offset rqb)) (quotient (si:array-index-offset (si:rqb-buffer rqb)) 2)) (%pointer-plus (%pointer-plus string (si:array-data-offset string)) word-address) (* (si:rqb-npages rqb) 256) 1))) ('else (LET ((NBYTES (* (SI:RQB-NPAGES RQB) 1024)) (byte-address (* address 1024))) (COPY-ARRAY-PORTION (SI:RQB-8-BIT-BUFFER RQB) 0 NBYTES STRING byte-address (+ byte-address NBYTES)))))) (DEFMETHOD (RAMDISK :DISPOSE) () NIL) (DEFMETHOD (RAMDISK :UNIT-NUMBER) () UNIT) (DEFMETHOD (RAMDISK :MACHINE-NAME) () "RAMDISK") (DEFUN WRITE-DATUM (STRING KIND WORD VALUE) (COND ((EQ KIND 'INTEGER) (DO ((J 0 (1+ J))) ((= J 4) VALUE) (SETF (AREF STRING (+ J (* WORD 4))) (LDB (BYTE 8 (* J 8)) VALUE)))) ((EQ (CAR KIND) 'STRING) (LET ((LENGTH (CADR KIND))) (COPY-ARRAY-PORTION VALUE 0 (LENGTH VALUE) STRING (* WORD 4) (+ (* WORD 4) (LENGTH VALUE))) (FILL STRING 0 :START (+ (* WORD 4) (LENGTH VALUE)) :END (* 4 (+ WORD LENGTH)))) VALUE))) (DEFUN READ-DATUM (STRING KIND WORD) (COND ((EQ KIND 'INTEGER) (DO ((J 0 (1+ J)) (N 0 (DPB (AREF STRING (+ J (* WORD 4))) (BYTE 8 (* J 8)) N))) ((= J 4) N))) ((EQ (CAR KIND) 'STRING) (LET ((LENGTH (CADR KIND))) (LET ((S (SUBSTRING STRING (* WORD 4) (* 4 (+ WORD LENGTH))))) (SUBSTRING S 0 (STRING-SEARCH-CHAR 0 S))))))) (DEFUN WRITE-DATA (STRING &REST KIND-WORD-VALUE) (DO ((L KIND-WORD-VALUE (CDDDR L))) ((NULL L)) (WRITE-DATUM STRING (CAR L) (CADR L) (CADDR L)))) (defvar *filedisk* nil) (defflavor filedisk (file unit) () :initable-instance-variables) (defmethod (filedisk :print-self) (stream &rest ignored) (format stream "#" (and file (fs:file-truename file)))) (defmethod (filedisk :close-file) () (and file (fs:lmfs-close-file file)) (setq file nil)) (defmethod (filedisk :file) () file) (defun enable-filedisk (filename unit) (let ((pathname (fs:parse-pathname filename))) (or (eq (send pathname :host) si:local-host) (ferror nil "Pathname ~S not on local host" pathname)) (let ((file (FS:LOOKUP-FILE (FS:PATHNAME-RAW-DIRECTORY PATHNAME) (FS:PATHNAME-RAW-NAME PATHNAME) (FS:PATHNAME-RAW-TYPE PATHNAME) (OR (fs:PATHNAME-RAW-VERSION pathname) :NEWEST) :ERROR NIL T))) (push (list unit (make-instance 'filedisk :file file :unit unit)) *filedisk*) (fs:file-truename file)))) (defun disable-filedisk (&optional unit &aux f) (cond ((not unit) (dolist (f (copy-list *filedisk*)) (send (cadr f) :close-file) (setq *filedisk* (delq f *filedisk*)))) ((setq f (assq unit *filedisk*)) (send (cadr f) :close-file) (setq *filedisk* (delq f *filedisk*)) f) ('else (ferror nil "unit not found: ~S" unit)))) (defun make-filedisk (filename &optional blocks) "writes FILENAME with length BLOCKS (* 1024), or updates label if blocks is NIL" (cond ((null blocks) (let ((*filedisk* nil)) (unwind-protect (progn (enable-filedisk filename 0) (send (cadr (car *filedisk*)) :update-label)) (disable-filedisk)))) ('else (check-type blocks (fixnum 6)) (let ((string (make-string 1024))) (with-open-file (stream filename :direction :output) (WRITE-DATA STRING '(STRING 1) 0 "LABL" ; MAGIC 'INTEGER 1 1 ; LABEL VERSION 'INTEGER 2 1 ; NUMBER OF CYLINDERS 'INTEGER 3 1 ; NUMBER OF HEADS 'INTEGER 4 BLOCKS ; NUMBER OF BLOCKS PER TRACK 'INTEGER 5 BLOCKS ; NUMBER OF BLOCKS PER CYLINDER '(STRING 1) 6 "" ; DEFAULT UCODE '(STRING 1) 7 "" ; DEFAULT LOAD '(STRING 8) 8 (FORMAT NIL "FileDisk-~D" (ROUND BLOCKS 100)) ; DRIVE MAKE '(STRING 8) 16 (send (send stream :truename) :name) '(STRING 25) 24 (send (send stream :truename) :string-for-printing) 'INTEGER 128 1 ; NUMBER OF PARTITIONS 'INTEGER 129 7 ; NUMBER OF WORDS PER PARTITION DESCRIPTOR '(STRING 1) 130 "DATA" ; PARTITION DESCRIPTOR 0 - NAME 'INTEGER 131 6 ; - START 'INTEGER 132 (- BLOCKS 6) ; - LENGTH '(STRING 4) 133 "empty partition") ; - COMMENT (send stream :string-out string) (fill string #\*) (dotimes (j (quotient 1024 64)) (setf (aref string (* j 64)) #\return)) (dotimes (j (1- blocks)) (send stream :string-out string))))))) (NET:DEFINE-NETWORK-FUNCTION (NETI:GET-REMOTE-DISK-UNIT :FILEDISK) (HOST DISK-UNIT USE INITP WRITE-P) USE INITP WRITE-P (OR (CADR (ASSQ DISK-UNIT *FILEDISK*)) (FERROR NIL "~A unit ~S not available" HOST DISK-UNIT))) ;;; now for the required usual messages ;;; :read :write :dispose :unit-number :machine-name (defmethod (filedisk :unit-number) () unit) (defmethod (filedisk :machine-name) () (send (fs:file-truename file) :string-for-printing)) (defmethod (filedisk :dispose) () nil) ;; we can use fs:lm-disk-read and fs:lm-disk-write ;; using info in the fs:file-map ;; MAP-NBLOCKS = number of chunks ;; MAP-BLOCK-LOCATION = location of chunk ;; MAP-BLOCK-SIZE = size of chunk in bits. (defmethod (filedisk :read) (rqb address) (transfer-data-via-map rqb address (fs:file-map file) :read)) (defmethod (filedisk :write) (rqb address) (transfer-data-via-map rqb address (fs:file-map file) :write)) (defun transfer-data-via-map (rqb address map operation) (let ((amount-wanted (si:rqb-npages rqb)) (function (ecase operation (:read #'fs:lm-disk-read) (:write #'fs:lm-disk-write)))) (do ((search-index 0 (+ section-size search-index)) (MAP-INDEX 0 (1+ MAP-INDEX)) (n-sections (fs:map-nblocks map)) (section-size)) ((= MAP-INDEX n-sections) (ferror nil "index out of range")) (setq section-size (ceiling (fs:map-block-size map MAP-INDEX) (* 1024 8))) (let* ((section-offset (- address search-index)) (amount-available (- section-size section-offset))) (COND ((< SECTION-OFFSET 0) (FERROR NIL "index out of range")) ((NOT (< AMOUNT-AVAILABLE AMOUNT-WANTED)) ;; easy case. fits in a region (RETURN (FUNCALL FUNCTION RQB (+ (FS:MAP-BLOCK-LOCATION MAP MAP-INDEX) SECTION-OFFSET) AMOUNT-WANTED))) ((> AMOUNT-AVAILABLE 0) ;; in correct section, but not big enough for whole transfer (SI:WITH-DISK-RQB (TEMP-RQB AMOUNT-AVAILABLE) (when (eq operation :write) (copy-array-portion (si:rqb-buffer rqb) 0 (times AMOUNT-AVAILABLE 512) (SI:RQB-BUFFER TEMP-RQB) 0 (times AMOUNT-AVAILABLE 512))) (FUNCALL FUNCTION TEMP-RQB (+ (FS:MAP-BLOCK-LOCATION MAP MAP-INDEX) SECTION-OFFSET) AMOUNT-AVAILABLE) (WHEN (EQ OPERATION :READ) (COPY-ARRAY-PORTION (SI:RQB-BUFFER TEMP-RQB) 0 (TIMES AMOUNT-AVAILABLE 512) (SI:RQB-BUFFER RQB) 0 (TIMES AMOUNT-AVAILABLE 512)))) (INCF MAP-INDEX) (DO ((RQB-INDEX AMOUNT-AVAILABLE (+ amount-available rqb-index))) ((= RQB-INDEX AMOUNT-WANTED)) (IF (= MAP-INDEX N-SECTIONS) (FERROR NIL "transfer address and size out of range")) (SETQ SECTION-SIZE (CEILING (FS:MAP-BLOCK-SIZE MAP MAP-INDEX) (* 1024 8))) (SETQ AMOUNT-AVAILABLE (MIN SECTION-SIZE (- AMOUNT-WANTED RQB-INDEX))) (SI:WITH-DISK-RQB (TEMP-RQB AMOUNT-AVAILABLE) (WHEN (EQ OPERATION :WRITE) (COPY-ARRAY-PORTION (SI:RQB-BUFFER RQB) (* RQB-INDEX 512) (* (+ RQB-INDEX AMOUNT-AVAILABLE) 512) (SI:RQB-BUFFER TEMP-RQB) 0 (TIMES AMOUNT-AVAILABLE 512))) (FUNCALL FUNCTION TEMP-RQB (FS:MAP-BLOCK-LOCATION MAP MAP-INDEX) AMOUNT-AVAILABLE) (WHEN (EQ OPERATION :READ) (COPY-ARRAY-PORTION (SI:RQB-BUFFER TEMP-RQB) 0 (TIMES AMOUNT-AVAILABLE 512) (SI:RQB-BUFFER RQB) (* RQB-INDEX 512) (* (+ RQB-INDEX AMOUNT-AVAILABLE) 512)))) (INCF MAP-INDEX)) (RETURN T))))))) (defmethod (filedisk :total-pages) () (do ((j 0 (1+ j)) (n (fs:map-nblocks (fs:file-map file))) (sum 0 (+ (ceiling (fs:map-block-size (fs:file-map file) j) (* 1024 8)) sum))) ((= j n) sum))) (defmethod (filedisk :describe-map) () (format t "~&~S has ~D pages in ~D sections:~%" file (send self :total-pages) (fs:map-nblocks (fs:file-map file))) (dotimes (j (fs:map-nblocks (fs:file-map file))) (format t "[~3D] ~3D at ~6D~%" j (ceiling (fs:map-block-size (fs:file-map file) j) (* 1024 8)) (fs:map-block-location (fs:file-map file) j)))) (defmethod (filedisk :update-label) (&optional (verbose t)) ;; update the label to reflect the actual size of the map. (si:with-disk-rqb (rqb 1) (send self :read rqb 0) (let ((string (si:rqb-8-bit-buffer rqb))) (let ((label-blocks (read-datum string 'integer 4)) (actual-blocks (send self :total-pages))) (cond ((= label-blocks actual-blocks) (and verbose (format t "~&~S has ~D blocks. No need to change.~%" self label-blocks))) ((< label-blocks actual-blocks) (and verbose (format t "~&~S has ~D actual blocks. Growing label amount by ~D.~%" self actual-blocks (- actual-blocks label-blocks))) (write-data string 'INTEGER 4 actual-blocks 'INTEGER 5 actual-blocks) (send self :write rqb 0)) ((> label-blocks actual-blocks) (and verbose (format t "~&~S has only ~D actual blocks. Reducing label amount by ~D.~%" self actual-blocks (- label-blocks actual-blocks))) (write-data string 'INTEGER 4 actual-blocks 'INTEGER 5 actual-blocks) (send self :write rqb 0)))))) self) (defun randomize-file (filename &optional (percent 10)) (let ((*filedisk* nil)) (unwind-protect (let ((truename (enable-filedisk filename 0))) (si:with-disk-rqb (rqb 1) (let* ((unit (cadr (car *filedisk*))) (buffer (si:rqb-buffer rqb)) (n (send unit :total-pages)) (m (ceiling (* n percent) 100))) (format t "~&Randomizing ~D out of the ~D pages of data in ~S~%" m n truename) (dotimes (j m) (let ((b (random n))) (send unit :read rqb b) (dotimes (k (ceiling (* 512 percent) 100)) (let ((i (random 512))) (setf (aref buffer i) (logxor (random (expt 2 16)) (aref buffer i))))) (princ ".") (send unit :write rqb b)))))) (disable-filedisk)))) (compile-flavor-methods ramdisk filedisk)