;;; -*- Mode:LISP; Package:FILE-SYSTEM; Base:10; Readtable:ZL -*- ;;; ;;; hacks for fixing broken LMFS file systems. ;;; ;;; (defconst *file-block-usage-map* nil) (defun setup-file-block-usage-map () (unless (and *file-block-usage-map* (= (array-length page-usage-table) (array-length *file-block-usage-map*))) (setq *file-block-usage-map* (make-array (list (array-length page-usage-table) 2) :type 'art-2b))) (fillarray *file-block-usage-map* nil)) (defun assess-put () (let ((free 0) (reserved 0) (used 0) (dead 0)) (do ((count 1 (add1 count))) ((= count (length page-usage-table)) (format t "~%Free: ~D; Reserved: ~D; Used: ~D; Dead: ~D;" free reserved used dead) (values free reserved used dead)) (case (aref page-usage-table count) (0 (incf free)) (1 (incf reserved)) (2 (incf used)) (3 (incf dead)))))) (defun record-page-usage-table-info (&optional (into *file-block-usage-map*)) (do ((count 1 (add1 count)) (len (length page-usage-table))) ((= count len)) (setf (aref into count 0) (aref page-usage-table count)))) (defun record-current-pointers (&optional (file (dc-root-directory))) (let ((map (file-map file))) (dotimes (c (map-nblocks map)) (setf (aref *file-block-usage-map* (map-block-location map c) 1) 3)) (when (directory? file) (print file) (dolist (f (fs:read-directory-files file)) (record-current-pointers f))))) (defun number-of-valid-pointers (&optional (array *file-block-usage-map*)) (let ((num 0)) (do ((count 1 (add1 count))) ((= count (floor (array-length array) 2)) num) (when (= (aref array count 1) 3) (incf num))))) (defun list-of-blocks-in-limbo (&optional (array *file-block-usage-map*)) (let (list) (do ((count 1 (add1 count))) ((= count (floor (array-length array) 2)) (reverse list)) (when (and (= (aref *file-block-usage-map* count 0) put-used) (not (= (aref *file-block-usage-map* count 1) 3))) (push count list))))) (defconst *review-documentation* " : Next Block : Previous Block : show whole block <^c> : change number of characters to display : record this block as useful and go to next : quit and return list of useful blocks : print this info ") (defun review-blocks (list &key (unit 0) (offset 0)) (using-resource (rqb si:rqb 1 4) (let ((string (si:rqb-8-bit-buffer rqb)) (chars-to-display 80) accepted-list) (do ((count 0 (add1 count)) addr) ((= count (length list)) (reverse accepted-list)) (setq addr (nth count list)) (si:disk-read rqb unit (+ offset addr)) (send *terminal-io* :clear-screen) (format t "[Relative block number: ~D]~2%" addr) (send *terminal-io* :string-out string 0 chars-to-display) (do (option finished?) (finished?) (setq option (tyi) finished? (case option (#\rubout t) (#\space (send *terminal-io* :clear-screen) (format t "[Relative block number: ~D]~2%" addr) (send *terminal-io* :string-out string)) (#\altmode (decf count (if (= count 0) 1 2)) t) (#\control-c (do ((num (prompt-and-read :number "~&Chars to display (1 to 1024)") (prompt-and-read :number "~&Chars to display (1 to 1024)"))) ((and (< num 1025) (> num 0)) (setq chars-to-display num)))) (#\return (push addr accepted-list) t) (#\end (return-from review-blocks (reverse accepted-list))) (#\help (format t *review-documentation*)) (t (tv:beep) nil)))))))) ;;; Directions for PW: ;;; ;;; (SETUP-FILE-BLOCK-USAGE-MAP) ;;; (RECORD-PAGE-USAGE-TABLE-INFO) ;;; (RECORD-CURRENT-POINTERS) ;;; (DEFCONST (LIST-OF-BLOCKS-IN-LIMBO)) ;;; (REVIEW-BLOCKS ) ;;; (defun save-all-ascii (slist &optional (save-file "lam3:pw;blocks.save") (unit 0)) (using-resource (rqb si:rqb 1 4) (let ((string (si:rqb-8-bit-buffer rqb))) (with-open-file (f save-file :direction :output) (do* ((l slist (cdr l)) (addr (car l) (car l))) ((null addr)) (print addr) (si:disk-read rqb unit (+ 162025 addr)) (when (all-ascii string) (tyo #/!) (send f :string-out string) (send f :string-out "+---------------------------------------------------------+"))))))) (defun all-ascii (string) (dotimes (i 100. t) (let ((char (aref string i))) (unless (or (typep char '(integer 31 128)) (= char 141.)) (return-from all-ascii nil))))) (defmacro with-wired-block (var &body body) `(using-resource (,var si:rqb 1 4) (unwind-protect (progn (si:wire-disk-rqb ,var) ,@body) (when ,var (si:unwire-disk-rqb ,var))))) (defun search-for-blocks-with-string (search-string &key block-status block-list at-position (search-offset 0) (partition-name "FILE") (disk-unit 0) &aux return-list) (check-type block-status (or null (integer 0 4))) (multiple-value-bind (start nil nil nil) (si:find-disk-partition partition-name nil disk-unit) (unless start (ferror nil "Invalid file partition name ~S on disk unit ~D" partition-name 0)) (with-wired-block block (format t "~&Searching through pages: ") (let ((block-string (si:rqb-8-bit-buffer block)) (xpos (car (cursorpos))) (ypos (cdr (cursorpos)))) (if block-list (let ((length (length block-list)) (count 0)) (dolist (page block-list) (incf count) (when (zerop (remainder count 100)) (cursorpos xpos ypos) (cursorpos 'l) (princ (- length count))) (si:disk-read block disk-unit (+ page start)) (when (if at-position (string-equal search-string block-string :start2 at-position :end2 (length search-string)) (string-search search-string block-string search-offset)) (format t "~&Found one: ~D" page) (push page return-list)))) (dotimes (page (length page-usage-table)) (when (zerop (remainder page 1000)) (cursorpos xpos ypos) (cursorpos 'l) (princ page)) (when (or (null block-status) (= (ar-1 page-usage-table page) block-status)) (si:disk-read block 0 (+ page start)) (when (if at-position (string-equal search-string block-string :start2 at-position :end2 (length search-string)) (string-search search-string block-string search-offset)) (format t "~&Found one: ~D" page) (push page return-list)))))))) (tv:beep) (reverse return-list)) (defun dump-lm-blocks-to-file (filename page-list) (with-open-file (f filename :direction :output :characters t :byte-size 8) (with-wired-block block (let ((block-string (si:rqb-8-bit-buffer block))) (dolist (page page-list) (si:disk-read block 0 (+ page lm-partition-base)) (format f "~&#D~D~%~S~2%" page block-string))))) (tv:beep)) (defun look-at-directory-entries (directory) (let ((entries '())) (with-map-stream-in (stream (file-map (lookup-directory directory))) (loop (let ((entry (read-directory-entry stream directory))) (cond (entry (format t "~&Got entry: ~S" entry) (describe entry) (push entry entries)) (t (format t "~%End of directory ~A." (directory-name directory)) (return (nreverse entries))))))))) (defun read-directory-entries-from-file (file directory) (with-open-file (f file :direction :input) (do (entries (entry (read-directory-entry f directory) (read-directory-entry f directory))) ((null entry) (reverse entries)) (push entry entries)))) (defun view-directory-map-stream (directory) (with-map-stream-in (stream (file-map (lookup-directory directory))) (stream-copy-until-eof stream *standard-output*))) (defun remove-equal-blocks (block-list &key (offset lm-partition-base) (disk-unit 0)) (with-wired-block block (let ((block-string (si:rqb-8-bit-buffer block)) return-list string-list) (dolist (block-addr block-list) (si:disk-read block disk-unit (+ block-addr offset)) (block search (dolist (str string-list (let ((str (make-string 1024))) (copy-array-contents block-string str) (push str string-list) (push block-addr return-list))) (when (string-equal str block-string) (format t "~& one equal block found") (return-from search nil)))))))) (defun revert-directory (directory) (setf (directory-files directory) :disk) (read-directory-files directory)) (defun check-directory-tree (&optional (directory (dc-root-directory))) (dolist (file (read-directory-files directory)) (when (directory? file) (print file) (check-directory-tree file)))) (defun check-files-for-top-level-atoms (file-list) (let ((return-list)) (dolist (file file-list return-list) (case (send (car file) :canonical-type) (:lisp (with-open-file (f (car file)) (format t "~&Checking /"~A/" ... " (car file)) (condition-case () (do-forever (unless (consp (read f)) (ferror nil ""))) (fs:end-of-file (format t "[GOOD]")) (error (tv:beep) (format t "[BAD]") (push (car file) return-list))))))))) ;;;; filesystem excerciser (defmacro with-abort-catch-and-notify (((format-string &rest format-args) &rest abort-forms) &body body) `(condition-case () (progn . ,body) (sys:abort (format t ,format-string . ,format-args) . ,abort-forms))) (defun get-random-directory () (nth (random 4) '("lmfs-temp" ("lmfs-temp" "temp1") ("lmfs-temp" "temp2") ("lmfs-temp" "temp3")))) (defun get-random-canonical-type () (nth (random 3) '(:lisp :text :qfasl))) (defun create-files-randomly (number-of-files existing-pathnames &optional all-new-pathnames) (using-resource (buf si:dma-buffer 200) (fill (si:dma-buffer-string buf) #/?) (dotimes (c number-of-files existing-pathnames) (let ((pathname (if (and (not all-new-pathnames) (> (length existing-pathnames) 20) (< (random 5) 3)) (let ((op (nth (random (length existing-pathnames)) existing-pathnames))) (if (zerop (random 2)) op (send op :new-version :newest))) (fs:make-pathname :host si:local-host :directory (get-random-directory) :name (substring (gensym "tmpfil") 0) :canonical-type (get-random-canonical-type))))) (with-abort-catch-and-notify (("~&Aborted opening file ~A (during create-random-file)" pathname) (push (cons :open-for-output pathname) *lmfs-files-aborted*)) (with-open-file (f pathname :direction :output) (with-abort-catch-and-notify (("~&Aborted writing data into ~A (in create-files-randomly)" pathname) (push (cons :write-data (send f :truename)) *lmfs-files-aborted*)) (format t "~&Creating random file: ~A" (send f :truename)) (pushnew (send f :truename) existing-pathnames) (let ((factor (// (send f :byte-size) 8))) (send f :string-out (case factor (1 (si:dma-buffer-string buf)) (2 (si:dma-buffer-16b buf))) 0 (random (// (* 200 1024.) factor))))))))))) (defun delete-files-randomly (number-of-files-to-delete file-list) (dotimes (count number-of-files-to-delete file-list) (let ((file (nth (random (length file-list)) file-list))) (format t "~&Deleting random file: ~A" file) (with-abort-catch-and-notify (("~&Aborted deleting file ~A (delete-files-randomly)" file) (push (cons :delete file) *lmfs-files-aborted*) (setq file-list (delq file file-list))) (fs:delete-file file) (setq file-list (delq file file-list)))))) (defun open-files-randomly (number-of-files-to-open file-list) (dotimes (count number-of-files-to-open) (let ((file (nth (random (length file-list)) file-list))) (with-abort-catch-and-notify (("~&Aborted opening file ~A (open-files-randomly)" file) (push (cons :open file) *lmfs-files-aborted*)) (with-open-file (f file) (with-abort-catch-and-notify (("~&Aborted with file open ~A (open-files-randomly)" file) (push (cons :file-open file) *lmfs-files-aborted*)) (format t "~&Opening random file: ~A" (send f :truename)) f)))))) (defun expunge-random-directories () (dotimes (times (random 4)) (let ((directory (get-random-directory))) (format t "~&Expunging random directory: ~A" directory) (with-abort-catch-and-notify (("~&Aborted during expunge of directory: ~A" directory) (push (cons :expunge-directory directory) *lmfs-files-aborted*)) (fs:expunge-directory (fs:make-pathname :host si:local-host :directory directory :name :wild :type :wild :version :wild)))))) (defun exercise-file-system (&key run-abort-process times output-file &aux existing-files) (with-open-stream (*standard-output* (if output-file (si:make-broadcast-stream (open output-file :direction :output) *standard-output*) *standard-output*)) (let ((*lmfs-active-debug-modes* '(:check-directory-maps))) (when (yes-or-no-p "~&*** Warning: This function may crash your filesystem!!! Go ahead?") (fs:lm-salvage) (fs:create-directory "lm:lmfs-temp;") (fs:create-directory "lm:lmfs-temp.temp1;") (fs:create-directory "lm:lmfs-temp.temp2;") (fs:create-directory "lm:lmfs-temp.temp3;") (when run-abort-process (run-abort-process si:current-process)) (dotimes (count (or times 1)) (with-abort-catch-and-notify (("~&Aborted during excerciser loop")) (setq existing-files (create-files-randomly (+ 20 (random 30)) existing-files)) (open-files-randomly (floor (length existing-files) 2) existing-files) (setq existing-files (delete-files-randomly (round (* (length existing-files) (// (random 6) 10.0))) existing-files)) (open-files-randomly (floor (length existing-files) 2) existing-files) (expunge-random-directories))) (kill-abort-process) (format t "~&Deleting all files ...") (dolist (file existing-files) (fs:deletef file)) (fs:expunge-directory "lm:lmfs-temp.temp3;*.*#*") (fs:expunge-directory "lm:lmfs-temp.temp2;*.*#*") (fs:expunge-directory "lm:lmfs-temp.temp1;*.*#*") (dolist (dir '(("lmfs-temp" "temp1") ("lmfs-temp" "temp2") ("lmfs-temp" "temp3"))) (fs:lmfs-delete-empty-directory (fs:lookup-directory dir) nil)) (fs:expunge-directory "lm:lmfs-temp;*.*#*"))))) (defconst *lmfs-exercise-abort-signalled* ()) (defconst *lmfs-files-aborted* ()) (defconst *abort-process-running* ()) (defconst *abort-process-probability* 500) (defun signal-abort-p () (and (null *lmfs-exercise-abort-signalled*) (zerop *abort-process-probability*))) (defun abort-process (process) (do-forever (catch-error-restart (error "") (process-wait "Waiting to Abort" 'signal-abort-p) (send process :interrupt 'signal 'sys:abort :format-string "Randomly aborted...")))) (defun run-abort-process (for-process) (kill-abort-process) (setq *abort-process-running* (process-run-function "Random Abort Generator" 'abort-process for-process))) (defun kill-abort-process () (when *abort-process-running* (send *abort-process-running* :kill)))