;;; -*- Mode:LISP; Package:FILE-SYSTEM; Readtable:ZL; Base:10 -*-
;;;; Basic Structures

(DEFVAR LOCAL-FILE-SYSTEM-AREA (MAKE-AREA :NAME 'LOCAL-FILE-SYSTEM-AREA :VOLATILITY 2)
  "All of the internal structures made by the file system are consed in this area.")

(DEFSTRUCT (DISK-CONFIGURATION (:TYPE :NAMED-ARRAY)
			       (:CONC-NAME DC-)
			       (:ALTERANT NIL)
			       (:MAKE-ARRAY (:AREA LOCAL-FILE-SYSTEM-AREA))
			       (:DEFAULT-POINTER DISK-CONFIGURATION))
  VERSION					;The version of the format on disk
  PARTITION-SIZE
  PUT-BASE					;Relative address of the page usage table
  PUT-SIZE
  ROOT-DIRECTORY				;The root directory.
  )

(DEFSTRUCT (DIRECTORY (:TYPE :NAMED-ARRAY)
		      :CONC-NAME
		      (:ALTERANT NIL)
		      (:MAKE-ARRAY (:AREA LOCAL-FILE-SYSTEM-AREA)))
  NAME						;The name of the directory
  LOCK						;The lock associated with the directory
  MAP						;The map of the directory.
  FILES						;The files specified in the map.
  )

(DEFSTRUCT (BASIC-FILE (:TYPE :NAMED-ARRAY)
		       (:CONC-NAME FILE-)
		       (:CONSTRUCTOR NIL)
		       (:ALTERANT NIL))
  NAME						;A string.
  LOCK						;A contention lock.
  MAP						;The map of the data.
  FILES						;Used only if the file is a directory.
  TYPE						;A string.
  VERSION					;A number (16b).
  )

(DEFSTRUCT (FILE (:TYPE :NAMED-ARRAY)
		 (:INCLUDE BASIC-FILE)
		 :CONC-NAME
		 (:MAKE-ARRAY (:AREA LOCAL-FILE-SYSTEM-AREA)))
  DEFAULT-BYTE-SIZE
  OVERWRITE-FILE				;Links overwriting/overwritten files.
  AUTHOR-INTERNAL				;A string
  CREATION-DATE-INTERNAL			;A number (32 bits)
  DIRECTORY					;Mommy.
  OPEN-COUNT					;The number of times this file is open
						;-1 if open for overwrite.
  ATTRIBUTES					;A 16-bit word of common properties
  PLIST						;Arbitrary properties.
  GENERATION-NUMBER
  )

(DEFSTRUCT (LINK (:TYPE :NAMED-ARRAY)
		 (:INCLUDE BASIC-FILE)
		 :CONC-NAME
		 (:ALTERANT NIL)
		 (:MAKE-ARRAY (:AREA LOCAL-FILE-SYSTEM-AREA)))
  TO-STRING					;The string linked to, ie "AI: DLA; FOO >"
  )

;;;; Property Bit Definitions

(DEFVAR ATTRIBUTES (MAKE-ARRAY 16.))

(DEFMACRO DEFINE-ATTRIBUTES (&BODY NUMBERS-AND-NAMES)
  `(PROGN 'COMPILE
	  . ,(LOOP FOR (NUMBER NAME) ON NUMBERS-AND-NAMES BY #'CDDR
		   NCONCING `((DEFPROP ,NAME ,(1+ (* NUMBER 64.)) ATTRIBUTE)
			      (ASET ',NAME ATTRIBUTES ,NUMBER)))))

(DEFINE-ATTRIBUTES
  0  :DONT-DELETE				;File cannot be deleted if on.
  1  :CLOSED					;File is still being written if off.
  2  :DELETED					;File is deleted but still open somewhere.
  3  :DUMPED					;File has been dumped
  4  :DONT-REAP					;Dont reap this file (not used internally)
  5  :CHARACTERS				;The file is a CHARACTERS file.
  6  :DIRECTORY					;The file is a subdirectory.

; 10 :QFASLP
; 11 :LISP					;Obsolete
; 12 :TEXT					;Obsolete
; 13 :MAY-BE-REAPED				;This bit is used by reaping programs.
						;If it is on, the file is liable to be
						;reaped.  Also, a reaping program may
						;turn it on as a warning that it is liable
						;to be reaped the next time around.
  14 :HEADER-BLOCK
  )

(DEFSUBST FILE-ATTRIBUTE (FILE INDICATOR)
  (LDB-TEST (GET INDICATOR 'ATTRIBUTE) (FILE-ATTRIBUTES FILE)))

(defsetf file-attribute set-file-attribute)

(DEFUN SET-FILE-ATTRIBUTE (FILE INDICATOR VAL)
  (SETF (FILE-ATTRIBUTES FILE)
	(DPB (IF VAL 1 0) (GET INDICATOR 'ATTRIBUTE) (FILE-ATTRIBUTES FILE)))
  (NOT (NULL VAL)))

;;; Syntax for attributes.

(DEFSUBST FILE-CLOSED? (FILE)
  (FILE-ATTRIBUTE FILE :CLOSED))

(DEFSUBST FILE-DELETED? (FILE)
  (FILE-ATTRIBUTE FILE :DELETED))

(DEFSUBST DIRECTORY? (FILE)
  (FILE-ATTRIBUTE FILE :DIRECTORY))

(DEFSUBST ROOT-DIRECTORY? (FILE)
  (NULL (FILE-DIRECTORY FILE)))

(DEFSUBST FILE-NPAGES (FILE)
  (MAP-NPAGES (FILE-MAP FILE)))


;;;; Printers

(DEFSELECT FILE-NSI
  (:PRINT-SELF (FILE STREAM IGNORE IGNORE)
    (SI:PRINTING-RANDOM-OBJECT (FILE STREAM :NO-POINTER)
      (COND ((DIRECTORY? FILE)
	     (FORMAT STREAM "LMFS-Directory~{ ~A~}" (DIRECTORY-FULL-NAME FILE)))
	    (T
	     (FORMAT STREAM "LMFS-File ~S"
		     (LM-NAMESTRING NIL NIL
				    (DIRECTORY-FULL-NAME (FILE-DIRECTORY FILE))
				    (FILE-NAME FILE)
				    (FILE-TYPE FILE)
				    (FILE-VERSION FILE))))))))

(DEFSELECT DIRECTORY-NSI
  (:PRINT-SELF (DIRECTORY STREAM IGNORE IGNORE)
    (SI:PRINTING-RANDOM-OBJECT (DIRECTORY STREAM :TYPEP :NO-POINTER)
      (PRIN1 (DIRECTORY-NAME DIRECTORY) STREAM))))

(DEFUN DIRECTORY-FULL-NAME (DIRECTORY)
  (AND (NOT (ROOT-DIRECTORY? DIRECTORY))
       (APPEND (DIRECTORY-FULL-NAME (FILE-DIRECTORY DIRECTORY))
	       (LIST (DIRECTORY-NAME DIRECTORY)))))

(DEFSELECT MAP-NSI
  (:PRINT-SELF (MAP STREAM IGNORE IGNORE)
    (SI:PRINTING-RANDOM-OBJECT (MAP STREAM :TYPEP)
      (FORMAT STREAM "~D block~:P" (MAP-NBLOCKS MAP))))
  (:DESCRIBE (MAP) (DESCRIBE-MAP MAP)))

(DEFPROP FILE	   FILE-NSI	 NAMED-STRUCTURE-INVOKE)
(DEFPROP LINK	   FILE-NSI	 NAMED-STRUCTURE-INVOKE)
(DEFPROP DIRECTORY DIRECTORY-NSI NAMED-STRUCTURE-INVOKE)
(DEFPROP MAP	   MAP-NSI	 NAMED-STRUCTURE-INVOKE)

;;; Randomness.

(DEFUN LMFS-KEYWORD-OPTION (OPTIONS KEYWORD DEFAULT)
  (COND ((NULL OPTIONS)
	 DEFAULT)
	((EQ KEYWORD (CAR OPTIONS))
	 (CADR OPTIONS))
	(T
	 (LMFS-KEYWORD-OPTION (CDDR OPTIONS) KEYWORD DEFAULT))))

;;;; Locking Macros

(DEFMACRO LOCKING (LOCK &BODY BODY)
  "Execute BODY with LOCK locked."
  (LET ((LOCK-CELL (GENSYM)))
    `(LET ((,LOCK-CELL (LOCF ,LOCK)))
       (UNWIND-PROTECT
	 (PROGN (PROCESS-LOCK ,LOCK-CELL) . ,BODY)
	 (%STORE-CONDITIONAL ,LOCK-CELL CURRENT-PROCESS NIL)))))

(DEFMACRO LOCKING-RECURSIVELY (LOCK &BODY BODY)
  "Execute BODY with LOCK locked; don't die if already locked."
  (LET ((LOCK-CELL (GENSYM))
	(LOCK-OWNED (GENSYM)))
    `(LET* ((,LOCK-CELL (LOCF ,LOCK))
	    (,LOCK-OWNED (EQ (CAR ,LOCK-CELL) CURRENT-PROCESS)))
       (UNWIND-PROTECT
	 (PROGN (IF (NOT ,LOCK-OWNED)
		    (PROCESS-LOCK ,LOCK-CELL))
		. ,BODY)
	 (IF (NOT ,LOCK-OWNED)			;unlock it only if you locked it.
	     (%STORE-CONDITIONAL ,LOCK-CELL CURRENT-PROCESS NIL))))))

(DEFMACRO REQUIRE-LOCK (LOCK)
  (LET ((ERSTR (FORMAT NIL "~S should be locked here but is not."
		       (IF (ATOM LOCK) LOCK (CAR LOCK)))))
    `(IF (NOT (EQ ,LOCK CURRENT-PROCESS))
	 (FERROR NIL ,ERSTR))))

(DEFMACRO MODIFYING-DIRECTORY (DIRECTORY &BODY BODY)
  "Lock DIRECTORY, execute BODY, and then mark DIRECTORY as modified."
  (LET ((LOCK-CELL (GENSYM)))
    `(LET ((,LOCK-CELL (LOCF (DIRECTORY-LOCK ,DIRECTORY))))
       (UNWIND-PROTECT
	 (PROGN (PROCESS-LOCK ,LOCK-CELL) . ,BODY)
	 (PROGN (SETF (FILE-CLOSED? DIRECTORY) NIL)
		(%STORE-CONDITIONAL ,LOCK-CELL CURRENT-PROCESS NIL))))))

(DEFMACRO OPEN-INPUT-FILE ((FILE PATHNAME) &BODY BODY)
  `(LET ((,FILE NIL))
     (UNWIND-PROTECT
       (PROGN
	 (SETQ ,FILE (LOOKUP-FILE
		       (PATHNAME-RAW-DIRECTORY ,PATHNAME)
		       (PATHNAME-RAW-NAME ,PATHNAME)
		       (PATHNAME-RAW-TYPE ,PATHNAME)
		       (PATHNAME-RAW-VERSION ,PATHNAME)
		       :ERROR NIL T))
	 . ,BODY)
       (WHEN ,FILE
	 (LMFS-CLOSE-FILE ,FILE)))))

(DEFMACRO OPEN-INPUT-FILE-OR-DIRECTORY ((FILE PATHNAME) &BODY BODY)
  `(LET ((,FILE NIL))
     (UNWIND-PROTECT
       (PROGN
	 (SETQ ,FILE (LOOKUP-FILE
		       (PATHNAME-RAW-DIRECTORY ,PATHNAME)
		       (PATHNAME-RAW-NAME ,PATHNAME)
		       (PATHNAME-RAW-TYPE ,PATHNAME)
		       (PATHNAME-RAW-VERSION ,PATHNAME)
		       :ERROR NIL))
	 . ,BODY)
       (WHEN ,FILE
	 (LMFS-CLOSE-FILE ,FILE)))))

(DEFCONST LM-VERSION 5
  "Disk format version we will create now if we initialize a partition.")
;Version 4 differs from version 3 in having the
;root directory look like all other directories.
;Version 5 has :HEADER-BLOCK attribute and in-place directory update

(DEFUN INSTALL-NEW-LM-VERSION ()
  (SETF (DC-VERSION) LM-VERSION))

(DEFCONST LM-MINIMUM-VERSION 3
  "Lowest format version we can handle.")

(DEFVAR LM-UNIT 0)				;See SET-LM-BAND
(DEFVAR LM-PARTITION "FILE")
(DEFVAR LM-PARTITION-POSSIBILITIES '((0 "DFIL") (0 "FILE") (4 "FILE")))
(DEFVAR LM-PARTITION-BASE)			;The address of the partition.

;; ***** This should be replaced by a device scheme.  *****
(DEFVAR DISK-CONFIGURATION)
(DEFVAR DISK-CONFIGURATION-RQB)
(DEFVAR DISK-CONFIGURATION-BUFFER)
(DEFVAR DISK-CONFIGURATION-BUFFER-POINTER)
(DEFVAR DISK-CONFIGURATION-LOCK NIL)

(DEFVAR PUT-LOCK NIL)
(DEFVAR PUT-SCANNING-INDEX 0)
(DEFVAR PUT-RQB)		;The page usage table is indirected into this.
(DEFVAR PAGE-USAGE-TABLE)	;The actual table.

;; The first entry in the page usage table tells whether the table is consistent
;; with the rest of the system.  If it is not, this only reflects the fact that
;; some pages are lost...
(DEFCONST PUT-CONSISTENT 1)
(DEFCONST PUT-INCONSISTENT 2)

;; The remaining entries describe all the remaining pages in the file (not counting
;; the configuration page, which is always used).
(DEFCONST PUT-FREE 0)				;Page is up for grabs.
(DEFCONST PUT-RESERVED 1)			;Page is allocated but (likely) deletable.
(DEFCONST PUT-USED 2)				;Page is allocated in core and on disk.
(DEFCONST PUT-UNUSABLE 3)			;Page should not be referenced ever.

(DEFVAR PAGE-SIZE-IN-BITS (* PAGE-SIZE 32.))

;; This is also in MTDEFS
(REMPROP 'QUOTIENT-CEILING 'SOURCE-FILE-NAME)
(DEFSUBST QUOTIENT-CEILING (Y X) (CEILING Y X))

;; Get a buffer of the correct byte size
(DEFUN GET-RQB-ARRAY (RQB BYTE-SIZE &AUX TYPE)
  (COND ((= BYTE-SIZE 16.) (RQB-BUFFER RQB))
	((= BYTE-SIZE 8) (RQB-8-BIT-BUFFER RQB))
	((SETQ TYPE (CDR (ASSQ BYTE-SIZE '((4 . ART-4B) (2 . ART-2B) (1 . ART-1B)))))
	 (MAKE-ARRAY (FLOOR (* (RQB-NPAGES RQB) PAGE-SIZE-IN-BITS) BYTE-SIZE)
		     :AREA LOCAL-FILE-SYSTEM-AREA
		     :TYPE TYPE
		     :DISPLACED-TO RQB
		     ;; This is a system bug.  Don't try to figure it out.
		     :DISPLACED-INDEX-OFFSET (* (%P-CONTENTS-OFFSET (RQB-BUFFER RQB) 3)
						 (FLOOR 16. BYTE-SIZE))))
	(T (FERROR NIL "~D is an invalid byte size." BYTE-SIZE))))

;; These are the only interface to the disk in the file system, with the exception
;; of FIND-DISK-PARTITION and UPDATE-PARTITION-COMMENT.
;; The unit is always LM-UNIT, nd the address is relative to
;; the current partition (LM-PARTITION).

(DEFUN LM-DISK-WRITE (RQB ADDR &OPTIONAL (NPAGES (RQB-NPAGES RQB)))
; (COND ((EQ RQB PUT-RQB) (FORMAT T "~%>>Writing the page usage table"))
;	((EQ RQB DISK-CONFIGURATION-RQB) (FORMAT T "~%>>Writing configuration"))
;	(WITHIN-FILE-SYSTEM (FORMAT T "~%>>Writing an internal structure"))
;	(T (FORMAT T "~%>>Writing a file block")))
  (COND ((< ADDR (DC-PARTITION-SIZE))
	 (si:disk-write-n-pages rqb lm-unit (+ addr lm-partition-base) npages))
	(T (FERROR NIL "Disk Write out of range."))))

(DEFUN LM-DISK-READ (RQB ADDR &OPTIONAL (NPAGES (RQB-NPAGES RQB)))
  (COND ((< ADDR (DC-PARTITION-SIZE))
	 (si:disk-read-n-pages rqb lm-unit (+ addr lm-partition-base) npages))
	(T (FERROR NIL "Disk Read out of range."))))


;;; Use this to write out new maps.

(DEFMACRO-DISPLACE WITH-MAP-STREAM-OUT ((STREAM) &BODY BODY)
  "A variant of WITH-OPEN-STREAM, it returns a new map."
  `(LET ((.FILE-ABORTED-FLAG. :ABORT)
	 (,STREAM NIL))
     (UNWIND-PROTECT
       (PROGN (SETQ ,STREAM (MAKE-MAP-STREAM-OUT))
	      ,@BODY
	      (SETQ .FILE-ABORTED-FLAG. NIL))
       (IF (AND (NOT (NULL ,STREAM))
		(NOT (ERRORP ,STREAM)))
	   (SEND ,STREAM :CLOSE .FILE-ABORTED-FLAG.)))
     (AND (NULL .FILE-ABORTED-FLAG.)
	  (SEND ,STREAM :MAP))))

(DEFMACRO-DISPLACE WITH-MAP-STREAM-IN ((STREAM . ARGS) &BODY BODY)
  "A variant of WITH-OPEN-STREAM."
  `(LET ((.FILE-ABORTED-FLAG. :ABORT)
	 (,STREAM NIL))
     (UNWIND-PROTECT
       (PROG1 (PROGN (SETQ ,STREAM (MAKE-MAP-STREAM-IN . ,ARGS))
		     . ,BODY)
	      (SETQ .FILE-ABORTED-FLAG. NIL))
       (IF (AND (NOT (NULL ,STREAM))
		(NOT (ERRORP ,STREAM)))
	   (SEND ,STREAM :CLOSE .FILE-ABORTED-FLAG.)))))

;; Directory information on the disk is stored in packed format.  The
;; following functions allow the file system to read and write fixnums
;; and bignums in such a packed format.

;(DEFUN GET-BYTES (STREAM NBYTES)
;  (LOOP WITH V = 0 AND I = 0
;	AS C = (SEND STREAM :TYI)
;	WHEN (NULL C) RETURN NIL
;	DO (SETQ V (+ (* V 256.) C)
;		 I (1+ I))
;	WHEN ( I NBYTES) RETURN V))
;
;(DEFUN PUT-BYTES (STREAM NBYTES VALUE)
;  (LOOP FOR PPSS = (+ (* (1- NBYTES) 512.) 8) THEN (- PPSS 512.)
;	DO (SEND STREAM :TYO (LDB PPSS VALUE))
;	UNTIL (= PPSS 8))
;  VALUE)

;; These macros do not use ONCE-ONLY because it loses in the simple case of NBYTES = 1.

(DEFMACRO GET-BYTES (STREAM NBYTES)
  (LOOP FOR I FROM (1- NBYTES) DOWNTO 0
	AS X = `(SEND ,STREAM :TYI)
	WHEN (PLUSP I) DO (SETQ X `(* ,X ,(^ 256. I)))
	COLLECT X INTO FORMS
	FINALLY (IF (= NBYTES 1)
		    (SETQ FORMS (CAR FORMS))
		  (SETQ FORMS (CONS '+ FORMS)))
		(RETURN FORMS)))

(DEFMACRO PUT-BYTES (STREAM NBYTES VALUE)
  `(PROGN . ,(LOOP FOR PPSS FROM (+ (* (1- NBYTES) #o1000) #o10) DOWNTO #o0010 BY #o1000
		   COLLECTING `(SEND ,STREAM :TYO (LDB ,PPSS ,VALUE)))))

(DEFUN PUT-STRING (STRING STREAM)
  (SEND STREAM :TYO (ARRAY-ACTIVE-LENGTH STRING))
  (SEND STREAM :STRING-OUT STRING))

(DEFUN GET-STRING (STREAM)
  (LET* ((LEN (SEND STREAM :TYI))
	 (ARR (MAKE-ARRAY LEN :TYPE 'ART-STRING)))
    (SEND STREAM :STRING-IN NIL ARR 0 LEN)
    ARR))


(DEFVAR *NON-WRITEABLE-PROPERTIES* '(%TRUENAME-INTERNAL %HEADER-INTERNAL))

(DEFUN WRITE-PROPERTY-LIST (STREAM PLIST)
  (DO ((L PLIST (CDDR L))
       (X 0))
      ((NULL L)
       (PUT-BYTES STREAM 1 X))
    (OR (MEMQ (CAR L) *NON-WRITEABLE-PROPERTIES*)
	(INCF X)))
  (DO ((L PLIST (CDDR L))
       (PROP))
      ((NULL L))
    (WHEN (NOT (MEMQ (CAR L) *NON-WRITEABLE-PROPERTIES*))
      (PUT-STRING (GET-PNAME (CAR L)) STREAM)
      (COND ((NULL (SETQ PROP (CADR L)))
	     (SEND STREAM :TYO 0))
	    ((EQ PROP T)
	     (SEND STREAM :TYO 1))
	    ((SYMBOLP PROP)
	     (COND ((keywordp prop)
		    (SEND STREAM :TYO 2)
		    (PUT-STRING (GET-PNAME PROP) STREAM))
		   (T (SEND STREAM :TYO 3)
		      ;; Use this odd order for ease in interning later.
		      (PUT-STRING (GET-PNAME PROP) STREAM)
		      (PUT-STRING (PACKAGE-NAME (SYMBOL-PACKAGE PROP)) STREAM))))
	    ((INTEGERP PROP)
	     (SEND STREAM :TYO 4)
	     (PUT-BYTES STREAM 3 PROP))
	    ((STRINGP PROP)
	     (SEND STREAM :TYO 5)
	     (PUT-STRING PROP STREAM))
	    ((CONSP PROP)
	     (SEND STREAM :TYO 6)
	     (LET ((*PACKAGE* SI:PKG-USER-PACKAGE)
		   (*READTABLE* si:INITIAL-READTABLE)
		   (*PRINT-BASE* 10.)
		   (*NOPOINT T)
		   (*PRINT-RADIX* NIL)
		   (SI:PRINT-READABLY T))
	       (PRIN1 PROP STREAM)))
	    ((TYPEP PROP 'PATHNAME)
	     (SEND STREAM :TYO 7)
	     (LET ((PACKAGE SI:PKG-USER-PACKAGE)
		   (READTABLE SI:INITIAL-READTABLE)
		   (BASE 10.)
		   (*NOPOINT T)
		   (SI:PRINT-READABLY T))
	       ;; These are the arguments to MAKE-FASLOAD-PATHNAME.
	       (SEND STREAM :TYO #/()
	       (PRIN1-THEN-SPACE (SEND (PATHNAME-HOST PROP) :NAME-AS-FILE-COMPUTER) STREAM)
	       (PRIN1-THEN-SPACE (PATHNAME-DEVICE PROP) STREAM)
	       (PRIN1-THEN-SPACE (PATHNAME-DIRECTORY PROP) STREAM)
	       (PRIN1-THEN-SPACE (PATHNAME-NAME PROP) STREAM)
	       (PRIN1-THEN-SPACE (PATHNAME-TYPE PROP) STREAM)
	       (PRIN1 (PATHNAME-VERSION PROP) STREAM)
	       (SEND STREAM :TYO #/))))
	    (T (FERROR NIL "I don't know how to write ~S as a property." PROP))))))


(DEFUN READ-PROPERTY-LIST (STREAM &AUX LIST (PAK SI:PKG-KEYWORD-PACKAGE))
  (SETQ LIST (MAKE-LIST (* (SEND STREAM :TYI) 2)))
  (DO ((L LIST (CDDR L)))
      ((NULL L))
    (RPLACA L (INTERN (GET-STRING STREAM) PAK))
    (CASE (SEND STREAM :TYI)
      (0)
      (1 (SETF (CADR L) T))
      (2 (SETF (CADR L) (INTERN (GET-STRING STREAM) PAK)))
      (3 (SETF (CADR L) (INTERN (GET-STRING STREAM)
				(PKG-FIND-PACKAGE (GET-STRING STREAM) :ASK))))
      (4 (SETF (CADR L) (GET-BYTES STREAM 3)))
      (5 (SETF (CADR L) (GET-STRING STREAM)))
      (6 (SETF (CADR L)
	       (LET ((*READ-BASE* 10.)
		     (*PACKAGE* SI:PKG-USER-PACKAGE)
		     (*READTABLE* SI:INITIAL-READTABLE))
		   ;; This can lose pretty badly with #<'s, etc.  -- DLA
		   (CLI:READ STREAM))))
      (7 (LET* ((*READ-BASE* 10.)
		(*PACKAGE* SI:PKG-USER-PACKAGE)
		(*READTABLE* SI:INITIAL-READTABLE)
		(FORM (CLI:READ STREAM))
		(DEFAULT-CONS-AREA WORKING-STORAGE-AREA))	;<-*
	   (SETF (CADR L)
		 (IF (= (LENGTH FORM) 6)
		     (APPLY #'FS:MAKE-FASLOAD-PATHNAME FORM)
		   (EVAL FORM)))))		;Obsolete form for pathnames to be written.
      (OTHERWISE (FERROR NIL "Invalid Plist property designator."))))
  LIST)

;; Functions for creating and manipulating MAPs.

;;; the default leader length used to be 2, but I cannot find anything
;;; in local file that uses the second slot.  I'm going to use another
;;; slot for the map's owner that will be stored into if the map belongs
;;; to a directory and the :SAVE-DIRECTORY-MAPS debug mode is enabled.
;;; -dg 3/4/86

(defconst *map-leader-length* 3)
  
(DEFSUBST MAP-NBLOCKS (MAP)
  (ARRAY-LEADER MAP 0))

(defsubst map-owner (map)
  (array-leader map 2))

(defsetf map-owner (map) (owner)
  `(store-array-leader ,owner ,map 2))

(DEFSUBST MAP-BLOCK-LOCATION (MAP INDEX)
  (AREF MAP INDEX 0))

(DEFSUBST MAP-BLOCK-SIZE (MAP INDEX)
  (AREF MAP INDEX 1))

(DEFUN MAP-CREATE (&OPTIONAL (NBLOCKS 32.) INPUTP &AUX MAP)
  (SETQ MAP (MAKE-ARRAY (LIST-IN-AREA LOCAL-FILE-SYSTEM-AREA NBLOCKS 2)
			:LEADER-LENGTH *map-leader-length*
			:NAMED-STRUCTURE-SYMBOL 'MAP))
  (SETF (MAP-NBLOCKS MAP) (IF INPUTP NBLOCKS 0))
  MAP)

;; Returns the number of pages used (as opposed to the number of blocks) in the map.
(DEFUN MAP-NPAGES (MAP)
  (LOOP WITH NBLOCKS = (MAP-NBLOCKS MAP)
	FOR I FROM 0 BELOW NBLOCKS
	SUMMING (CEILING (MAP-BLOCK-SIZE MAP I) PAGE-SIZE-IN-BITS)))

(DEFUN MAP-NPAGES-AVAILABLE (MAP)
  (DO ((N (ARRAY-DIMENSION MAP 0))
       (J 0 (1+ J))
       (TOTAL 0)
       (SIZE))
      ((OR (= J N)
	   (NULL (SETQ SIZE (MAP-BLOCK-SIZE MAP J))))
       TOTAL)
    (INCF TOTAL (CEILING SIZE PAGE-SIZE-IN-BITS))))

;; This is used during output, when a block is added onto the end of a file.
(DEFUN MAP-APPEND-BLOCK (MAP LOCATION SIZE &AUX (NBLOCKS (MAP-NBLOCKS MAP)))
  (AND (= NBLOCKS (ARRAY-DIMENSION MAP 0))
       (ARRAY-GROW MAP (+ NBLOCKS 32.) 2))
  (WITHOUT-INTERRUPTS
    (SETF (MAP-NBLOCKS MAP) (1+ NBLOCKS))
    (SETF (MAP-BLOCK-LOCATION MAP NBLOCKS) LOCATION)
    (SETF (MAP-BLOCK-SIZE MAP NBLOCKS) SIZE)))

;; Returns the length of the area mapped in bits.
(DEFUN MAP-LENGTH (MAP)
  (LOOP WITH NBLOCKS = (MAP-NBLOCKS MAP)
	FOR I FROM 0 BELOW NBLOCKS
	SUMMING (MAP-BLOCK-SIZE MAP I)))

(DEFUN MAP-READ (STREAM)
  (LOOP WITH NBLOCKS = (GET-BYTES STREAM 2)
	WITH MAP = (MAP-CREATE NBLOCKS T)
	FOR I FROM 0 BELOW NBLOCKS
	DOING (SETF (MAP-BLOCK-LOCATION MAP I) (GET-BYTES STREAM 3))
	      (SETF (MAP-BLOCK-SIZE MAP I) (GET-BYTES STREAM 3))
	FINALLY (RETURN MAP)))

(DEFUN MAP-WRITE (STREAM MAP)
  (LET ((NBLOCKS (MAP-NBLOCKS MAP)))
    (PUT-BYTES STREAM 2 NBLOCKS)
    (DOTIMES (I NBLOCKS)
      (MAP-WRITE-1-BLOCK STREAM
			 (MAP-BLOCK-LOCATION MAP I)
			 (MAP-BLOCK-SIZE MAP I))))
  MAP)

(DEFUN EXTENDED-MAP-READ (STREAM)
  (LET ((MAP (MAP-READ STREAM)))
    (SETF (MAP-NBLOCKS MAP) (GET-BYTES STREAM 2))
    MAP))

(DEFUN EXTENDED-MAP-WRITE (STREAM MAP)
  (LET ((NBLOCKS (DO ((N (ARRAY-DIMENSION MAP 0))
		      (J 0 (1+ J)))
		     ((OR (= J N) (NOT (MAP-BLOCK-SIZE MAP J)))
		      J))))
    (PUT-BYTES STREAM 2 NBLOCKS)
    (DOTIMES (I NBLOCKS)
      (MAP-WRITE-1-BLOCK STREAM
			 (MAP-BLOCK-LOCATION MAP I)
			 (MAP-BLOCK-SIZE MAP I)))
    (PUT-BYTES STREAM 2 (MAP-NBLOCKS MAP)))
  MAP)
  

(DEFUN MAP-WRITE-1-BLOCK (STREAM LOCATION SIZE)
  (PUT-BYTES STREAM 3 LOCATION)
  (PUT-BYTES STREAM 3 SIZE))

(DEFUN DESCRIBE-MAP (MAP &OPTIONAL (STREAM STANDARD-OUTPUT))
  (FORMAT STREAM "~&~S maps out the following blocks:~@
~2@TIndex~3@TLocation~7@TSize~3@TStatus~%" MAP)
  (LET ((NBITS 0))
    (DOTIMES (I (MAP-NBLOCKS MAP))
      (FORMAT STREAM "~6O: ~10O ~10O   ~[Free~;Reserved~;Used~;Bad~]~%"
	      I (MAP-BLOCK-LOCATION MAP I) (MAP-BLOCK-SIZE MAP I)
	      (AREF PAGE-USAGE-TABLE (MAP-BLOCK-LOCATION MAP I)))
      (INCF NBITS (MAP-BLOCK-SIZE MAP I)))
    (FORMAT STREAM "~%Total of ~D block~:P, ~D bit~:P.~%"
	    (MAP-NBLOCKS MAP)
	    NBITS)))

(DEFUN FILE-DATA-LENGTH (FILE)
  (MAP-LENGTH (FILE-MAP FILE)))

(DEFUN FILE-NPAGES (FILE)
  (MAP-NPAGES (FILE-MAP FILE)))

(DEFUN FILE-TRUENAME (FILE)
  ;; If at all possible, make the host name be a normal network host name, since these
  ;; names are transportable among machines, whereas "LM" is not.
  (OR (GETF (FILE-PLIST FILE) '%TRUENAME-INTERNAL)
      (SETF (GETF (FILE-PLIST FILE) '%TRUENAME-INTERNAL)
	    (MAKE-PATHNAME :HOST (IF SI:LOCAL-HOST (SEND SI:LOCAL-HOST :NAME-AS-FILE-COMPUTER) "LM")
			   :DEVICE "DSK"
			   :DIRECTORY (OR (DIRECTORY-FULL-NAME (FILE-DIRECTORY FILE))
					  :ROOT)
			   :NAME (FILE-NAME FILE)
			   :TYPE (IF (EQUAL (FILE-TYPE FILE) "")
				     :UNSPECIFIC
				   (FILE-TYPE FILE))
			   :VERSION (FILE-VERSION FILE)))))

;; Error handler interface.

(DEFMACRO IDENTIFY-FILE-OPERATION (OPERATION &BODY BODY)
  `(LET ((*CURRENT-FILE-OPERATION* (OR *CURRENT-FILE-OPERATION* ,OPERATION)))
     . ,BODY))

;; Wrap this around things which want to do things the simple way.
;; If ERROR-P is non-NIL and an error occurs, the error object is returned from this form
(DEFMACRO HANDLING-ERRORS (ERROR-P &BODY BODY)
  `(CATCH 'LM-TRAP-ERRORS
     (LET ((LM-TRAP-ERRORS (NOT ,ERROR-P)))
       (FILE-OPERATION-RETRY
	 . ,BODY))))

;; This is bound to T if one is inside LM-TRAP-ERROR, in which case control
;; is thrown to LM-TRAP-ERROR
(DEFVAR LM-TRAP-ERRORS NIL)

(DEFVAR *CURRENT-FILE-OPERATION* NIL)
(DEFVAR *CURRENT-OPERATION-PATHNAME* NIL)

(DEFPROP LM-SIGNAL-ERROR T :ERROR-REPORTER)
(DEFUN LM-SIGNAL-ERROR (SYMBOL &OPTIONAL SOURCE PROCEEDABLE &REST MAKE-CONDITION-ARGS)
  (OR SOURCE
      ;; SELF IS PROBABLY NEVER THE PATHNAME
      ;; SINCE SOMEBODY CHANGED FILE-HOST PATHNAMES
      ;; TO USE THE "ACCESS" ABSTRACTION.
      ;; 5-Feb-87 18:29:45 -GJC
      (AND (OR (TYPEP SELF 'PATHNAME)
	       (TYPEP SELF 'SI:FILE-STREAM-MIXIN))
	   (SETQ SOURCE SELF))
      (SETQ SOURCE *CURRENT-OPERATION-PATHNAME*))
  (AND SOURCE (NOT (TYPEP SOURCE 'PATHNAME))
       (SETQ SOURCE (SEND SOURCE :PATHNAME)))
  (LET ((ERROR
	  (APPLY #'MAKE-CONDITION
		 (OR (GET SYMBOL 'ERROR-SIGNALER) SYMBOL)
		 (LET ((STRING (GET SYMBOL 'ERROR-STRING)))
		   (IF SOURCE
		       (STRING-APPEND STRING " for "
				      (LET ((S (SEND SOURCE :STRING-FOR-PRINTING)))
					(COND ((STRING-SEARCH "~" S)
					       (WITH-OUTPUT-TO-STRING (SO)
						 (WITH-INPUT-FROM-STRING (SI S)
						   (DO ((C))
						       ((NOT (SETQ C (SEND SI :TYI))))
						     (IF (= #\~ C) (SEND SO :TYO C))
						     (SEND SO :TYO C)))))
					      ('ELSE
					       S))))
		     STRING))
		 SOURCE MAKE-CONDITION-ARGS)))
       (IF LM-TRAP-ERRORS
	   (THROW 'LM-TRAP-ERRORS ERROR))
       (SIGNAL ERROR
	       :PROCEED-TYPES
	       (COND ((CONSP PROCEEDABLE) PROCEEDABLE)
		     (PROCEEDABLE '(:RETRY-FILE-OPERATION))))))

;; Use this when an error occurs in file lookup.
(DEFPROP LM-LOOKUP-ERROR T :ERROR-REPORTER)
(DEFUN LM-LOOKUP-ERROR (SYMBOL DIR NAM TYP VER &OPTIONAL (OPERATION *CURRENT-FILE-OPERATION*))
  (LM-SIGNAL-ERROR SYMBOL
		   (MAKE-PATHNAME :HOST "LM" :DEVICE "DSK"
				  :DIRECTORY
				  (COND ((STRINGP DIR) DIR)
					((CONSP DIR) DIR)
					(T
					 (DIRECTORY-NAME DIR)))
				  :NAME NAM :TYPE TYP :VERSION VER)
		   NIL
		   OPERATION))

;; This is to aid in debugging.  It basically provides a warning at compile time
;; if you haven't defined an error.
(DEFUN LM-ERROR-SYMBOL-CHECK (FORM)
  (LET ((X (SECOND FORM)))
    (AND (EQ (CAR-SAFE X) 'QUOTE)
	 (PROGN
	   (WHEN (NOT (GET (CADR X) 'ERROR-STRING))
	     (COMPILER:BARF (CADR X) "is not a defined error symbol" 'COMPILER:WARN))
	   (OR (GET (CADR X) 'ERROR-SIGNALER)
	       (GET (CADR X) 'EH:MAKE-CONDITION-FUNCTION)
	     (COMPILER:BARF (CADR X) "is not a defined signal name" 'COMPILER:WARN))))
    FORM))

(COMPILER:ADD-OPTIMIZER LM-SIGNAL-ERROR LM-ERROR-SYMBOL-CHECK)
(COMPILER:ADD-OPTIMIZER LM-LOOKUP-ERROR LM-ERROR-SYMBOL-CHECK)
(COMPILER:ADD-OPTIMIZER LM-RENAME-ERROR LM-ERROR-SYMBOL-CHECK)

;; Error Definitions
(DEFMACRO DEFINE-ERRORS (&REST SYMBOLS-AND-STRINGS)
  `(PROGN 'COMPILE
	  . ,(LOOP FOR (SYMBOL STRING) ON SYMBOLS-AND-STRINGS BY #'CDDR
		   COLLECTING `(DEFPROP ,SYMBOL ,STRING ERROR-STRING))))

(DEFINE-ERRORS
;; Error signalers in SYS:IO;OPEN
  RENAME-TO-EXISTING-FILE	"Attempt to rename to an existing file"
  RENAME-ACROSS-DIRECTORIES	"Attempt to rename across directories"
  FILE-NOT-FOUND		"File not found"
  FILE-ALREADY-EXISTS		"File already exists"
  DIRECTORY-NOT-FOUND		"Directory not found"
  INVALID-BYTE-SIZE		"Illegal byte size"
  NO-MORE-ROOM			"Disk full"
  NO-FILE-SYSTEM		"File system not mounted"
  DIRECTORY-NOT-EMPTY		"Non-empty directories cannot be deleted"
  DONT-DELETE-FLAG-SET		"File has DONT-DELETE bit set"
  UNIMPLEMENTED-OPTION		"Unimplemented OPEN option"
  FILE-LOCKED			"File is locked"
  UNKNOWN-OPERATION		"Unknown operation"

;; Error signalers defined below.
  UNDELETE-UNCLOSED-FILE	"Cannot undelete unclosed file"
  OPEN-DELETED-FILE		"File is deleted"
  OPEN-DELETED-DIRECTORY	"Directory is deleted"
  OPEN-OUTPUT-FILE		"File is open for output"
  OPEN-UNFINISHED-DIRECTORY	"Directory still being created"
  FILE-IS-SUBDIRECTORY		"File is a subdirectory"
  UNSETTABLE-PROPERTY		"Unsettable property"
  INVALID-PROPERTY-NAME		"Bad property"
  VERSION-TOO-LARGE		"Version must be smaller than 65536"
  INVALID-DIRECTORY-NAME	"Invalid name given for a directory"
  RENAME-DIRECTORY-INVALID-TYPE "Invalid new type or version for renaming a subdirectory."
  LINKS-NOT-SUPPORTED		"Links not supported"
  BAD-TYPE-FOR-SUBDIRECTORY	"Invalid type for subdirectory"
  SUPERSEDE-DIRECTORY		"Superseding a directory"
  TOP-LEVEL-FILE		"No non-directory files in root"

  ;; also defined in OPEN, experimental.
  INVALID-WILDCARD              "Wildcard that got through pathname parsing but file system didn't like it."
  WRONG-KIND-OF-FILE            "Wrong kind of file for this operation"
  ACCESS-ERROR                  "operation denied by access control"
  INCORRECT-ACCESS-TO-FILE      "operation to file denied by access control"
  INCORRECT-ACCESS-TO-DIRECTORY "operation to directory denied by access control"
  )

(DEFPROP UNDELETE-UNCLOSED-FILE FILE-LOCKED ERROR-SIGNALER)

(DEFSIGNAL OPEN-DELETED-FILE FILE-OPERATION-FAILURE (PATHNAME OPERATION)
	   "Opening a file that is deleted.")

(DEFSIGNAL OPEN-DELETED-DIRECTORY FILE-LOOKUP-ERROR (PATHNAME OPERATION)
	   "Containing directory is deleted.")

(DEFPROP OPEN-OUTPUT-FILE FILE-OPEN-FOR-OUTPUT ERROR-SIGNALER)

(DEFSIGNAL OPEN-UNFINISHED-DIRECTORY FILE-LOOKUP-ERROR (PATHNAME OPERATION)
  "Containing directory is still being created.")

(DEFSIGNAL FILE-IS-SUBDIRECTORY
	   (FILE-OPERATION-FAILURE WRONG-KIND-OF-FILE INVALID-OPERATION-FOR-DIRECTORY)
	   (PATHNAME OPERATION)
  "Attempt to open a file which is a directory.")

(DEFSIGNAL UNSETTABLE-PROPERTY CHANGE-PROPERTY-FAILURE (PATHNAME PROPERTY)
	   "PROPERTY isn't one of the properties this file system allows users to set.")

(DEFSIGNAL VERSION-TOO-LARGE
	   (FILE-OPERATION-FAILURE INVALID-PATHNAME-SYNTAX VERSION-TOO-LARGE)
	   (PATHNAME OPERATION)
  "Version larger that 65536.")

(DEFSIGNAL INVALID-DIRECTORY-NAME
	   (FILE-OPERATION-FAILURE INVALID-PATHNAME-SYNTAX INVALID-DIRECTORY-NAME)
	   (PATHNAME OPERATION)
  "")

(DEFSIGNAL RENAME-DIRECTORY-INVALID-TYPE
	   (RENAME-FAILURE RENAME-DIRECTORY-INVALID-TYPE)
	   (PATHNAME NEW-PATHNAME)
  "")

(DEFSIGNAL LINKS-NOT-SUPPORTED
	   (FILE-OPERATION-FAILURE CREATION-FAILURE CREATE-LINK-FAILURE
				   LINKS-NOT-SUPPORTED)
	   (PATHNAME OPERATION)
  "Links are not supported in this file system.")

(DEFSIGNAL BAD-TYPE-FOR-SUBDIRECTORY
	   (FILE-OPERATION-FAILURE CREATION-FAILURE CREATE-DIRECTORY-FAILURE
				   BAD-TYPE-FOR-SUBDIRECTORY)
	   (PATHNAME OPERATION)
  "")

(DEFSIGNAL SUPERSEDE-DIRECTORY
	   (FILE-OPERATION-FAILURE CREATION-FAILURE TOP-LEVEL-FILE)
	   (PATHNAME OPERATION)
  "Superseding a file which is a subdirectory.")

(DEFSIGNAL TOP-LEVEL-FILE
	   (FILE-OPERATION-FAILURE CREATION-FAILURE TOP-LEVEL-FILE)
	   (PATHNAME OPERATION)
  "Attempt to create a file not a directory in the root directory.")

;; ALLOCATE-DISK-BLOCK returns the base and size of a block which has pages
;; pre-set to PUT-RESERVED.
;; {VERIFY, CHANGE, SET}-{BLOCK, MAP}-DISK-SPACE.  They should all be called
;; inside the special form USING-PUT.  SET-... should be used minimally.
;; WRITE-PUT (&optional force-p) may be called any time inside a USING-PUT
;; form to force the put to be written onto the disk.  If FORCE-P is nil, and
;; the PUT hasn't been modified in core, this is a no-op.  (WRITE-PUT) is implicit
;; when exitting a USING-PUT.  The USING-PUT special form may be enterred recursively,
;; and in fact usually is.

;; USE-{MAP,BLOCK}-DISK-SPACE??

;; This variable is T if the put has been modified in core and not written to the disk.
(DEFVAR PUT-MODIFIED NIL)

;; This array contains four numbers:  The number of pages in the PUT which
;; have the index as their status.
(DEFVAR PUT-USAGE-ARRAY (MAKE-ARRAY 4 :TYPE 'ART-32B))

;; This is the minimum number of free pages that must be available in order
;; to write files.  Note that internal structures may still be written.
(DEFVAR PUT-MINIMUM-FREE-PAGES 50.)

;; This is bound by LM-WRITE-DIRECTORY to T.  This tells ALLOCATE-DISK-BLOCK
;; that it is OK to go below PUT-MINIMUM-FREE-PAGES.
(DEFVAR WRITING-INTERNAL-STRUCTURES NIL)

(DEFVAR STANDARD-BLOCK-SIZE 20.)

(DEFUN ALLOCATE-DISK-BLOCK (&OPTIONAL (REQUESTED-NPAGES STANDARD-BLOCK-SIZE)
			    &AUX RNP (AVAILABLE 0) FREE-INDEX FREE-NPAGES
			    (PART-SIZE (DC-PARTITION-SIZE)))
  (DO-NAMED ALLOCATE-DISK-BLOCK () (NIL)
    (LOCKING-RECURSIVELY PUT-LOCK
      (COND ((OR WRITING-INTERNAL-STRUCTURES
		 (PLUSP (SETQ AVAILABLE (- (AREF PUT-USAGE-ARRAY PUT-FREE)
					   PUT-MINIMUM-FREE-PAGES))))
	     (SETQ RNP (IF WRITING-INTERNAL-STRUCTURES
			   REQUESTED-NPAGES
			 (MIN REQUESTED-NPAGES AVAILABLE)))
	     (SETQ FREE-INDEX
		   (OR (%STRING-SEARCH-CHAR PUT-FREE PAGE-USAGE-TABLE
					    PUT-SCANNING-INDEX PART-SIZE)
		       (%STRING-SEARCH-CHAR PUT-FREE PAGE-USAGE-TABLE
					    1 PUT-SCANNING-INDEX)
		       (FERROR NIL "Ran out of disk space on a directory write")))
	     (DO ((I FREE-INDEX (1+ I))
		  (P 0 (1+ P)))
		 (NIL)
	       (COND ((OR ( P RNP)
			  ( I PART-SIZE)
			  ( (AREF PAGE-USAGE-TABLE I) PUT-FREE))
		      (SETQ PUT-SCANNING-INDEX I)
		      (DECF (AREF PUT-USAGE-ARRAY PUT-FREE) P)
		      (INCF (AREF PUT-USAGE-ARRAY PUT-RESERVED) P)
		      (SETQ FREE-NPAGES P)
		      (RETURN-FROM ALLOCATE-DISK-BLOCK (VALUES FREE-INDEX FREE-NPAGES)))
		     (T (ASET PUT-RESERVED PAGE-USAGE-TABLE I)))))))
    ;; If we get here, the disk is full.  Leave the PUT unlocked so people
    ;; can delete files, and signal a proceedable error.
    (LM-SIGNAL-ERROR 'NO-MORE-ROOM NIL T *CURRENT-FILE-OPERATION*)))

(DEFUN WRITE-PUT (&OPTIONAL FORCE-P)
  (REQUIRE-LOCK PUT-LOCK)
  (COND ((OR PUT-MODIFIED FORCE-P)
	 (LM-DISK-WRITE PUT-RQB (DC-PUT-BASE))
	 (SETQ PUT-MODIFIED NIL))))

(DEFMACRO USING-PUT (&BODY BODY)
  (LET ((LOCK-OWNED (GENSYM)))
    `(LET ((,LOCK-OWNED (EQ PUT-LOCK CURRENT-PROCESS))
	   OLD-STATE)
       (UNWIND-PROTECT
	 (PROGN
	   (COND ((NOT ,LOCK-OWNED)
		  (PROCESS-LOCK (LOCF PUT-LOCK))
		  (IF (NOT (NULL PUT-MODIFIED))
		      (FERROR NIL "PUT evidently modified while unlocked"))
		  (SETQ OLD-STATE (AREF PAGE-USAGE-TABLE 0))
		  (ASET PUT-INCONSISTENT PAGE-USAGE-TABLE 0)))
	   . ,BODY)
	 (COND ((NOT ,LOCK-OWNED)
		(ASET OLD-STATE PAGE-USAGE-TABLE 0)
		(WRITE-PUT)
		(PROCESS-UNLOCK (LOCF PUT-LOCK))))))))

(defun maint-reserve-disk-surface-in-file-partition (physical-cylinder head &optional (new-status put-unusable))
  "CYL and HEAD are physical, such as SDU prints on WHY after crash.
Remember, the SDU prints cylinders and heads in DECIMAL"
  (let* ((unit lm-unit)
	 (partition-base lm-partition-base)
	 (logical-cylinder (- physical-cylinder (si:get-cylinder-offset-for-unit unit)))  ;**verify this is right
				;for unit 0 ***
	 (SECTORS-PER-TRACK (AREF si:DISK-SECTORS-PER-TRACK-ARRAY UNIT))
	 (HEADS-PER-CYLINDER (AREF si:DISK-HEADS-PER-CYLINDER-ARRAY UNIT))
	 (physical-block (+ (* logical-cylinder heads-per-cylinder sectors-per-track)
			    (* head sectors-per-track)))
	 (partition-block (- physical-block partition-base)))
    (cond ((or (< partition-block 0)
	       (>= partition-block (array-length page-usage-table)))
	   (ferror "That physical block is not within the file partition")))
    (using-put
      (dotimes (c sectors-per-track)
	(maint-change-block-disk-space (+ partition-block c) new-status)))
    ))

(DEFUN MAINT-CHANGE-BLOCK-DISK-SPACE (LOC NEW-STATUS)
  (REQUIRE-LOCK PUT-LOCK)
  (SETQ PUT-MODIFIED T)
  (let ((old-status (aref page-usage-table loc)))
    (format t "~%changing usage status for block ~o from ~s to ~s" loc old-status new-status)
    (DECF (AREF PUT-USAGE-ARRAY OLD-STATUS) 1)
    (INCF (AREF PUT-USAGE-ARRAY NEW-STATUS) 1)
    (ASET NEW-STATUS PAGE-USAGE-TABLE loc)))

(DEFUN CHANGE-BLOCK-DISK-SPACE (LOC NPAGES OLD-STATUS NEW-STATUS &AUX LIM)
  (REQUIRE-LOCK PUT-LOCK)
  (SETQ LIM (+ LOC NPAGES))
  (AND (PLUSP NPAGES)
       (NOT (AND (= OLD-STATUS PUT-RESERVED) (= NEW-STATUS PUT-FREE)))
       (SETQ PUT-MODIFIED T))
  (DO ((I LOC (1+ I)))
      (( I LIM)
       (DECF (AREF PUT-USAGE-ARRAY OLD-STATUS) NPAGES)
       (INCF (AREF PUT-USAGE-ARRAY NEW-STATUS) NPAGES)
       T)
      (cond ((not (= (AREF PAGE-USAGE-TABLE I) OLD-STATUS))
	     (format t "Unexpected disk space status in CHANGE-MAP-DISK-SPACE")
	     (aset (max new-status old-status) page-usage-table i))
	    (t
	     (ASET NEW-STATUS PAGE-USAGE-TABLE I)))))

(DEFUN CHANGE-MAP-DISK-SPACE (MAP OLD-STATUS NEW-STATUS
			      &AUX NBLOCKS LIM LOC COUNT (NPAGES 0))
  (REQUIRE-LOCK PUT-LOCK)
  (SETQ NBLOCKS (MAP-NBLOCKS MAP))
  (AND (PLUSP NBLOCKS)
       (NOT (AND (= OLD-STATUS PUT-RESERVED) (= NEW-STATUS PUT-FREE)))
       (SETQ PUT-MODIFIED T))
  (DO ((MAP-INDEX 0 (1+ MAP-INDEX)))
      (( MAP-INDEX NBLOCKS)
       (DECF (AREF PUT-USAGE-ARRAY OLD-STATUS) NPAGES)
       (INCF (AREF PUT-USAGE-ARRAY NEW-STATUS) NPAGES)
       T)
    (SETQ LOC (MAP-BLOCK-LOCATION MAP MAP-INDEX)
	  COUNT (CEILING (MAP-BLOCK-SIZE MAP MAP-INDEX) PAGE-SIZE-IN-BITS)
	  LIM (+ LOC COUNT))
    (DO ((I LOC (1+ I)))
	(( I LIM) (INCF NPAGES COUNT))
      (cond ((not (= (AREF PAGE-USAGE-TABLE I) OLD-STATUS))
	     (format t "Unexpected disk space status in CHANGE-MAP-DISK-SPACE")
	     (aset (max new-status old-status) page-usage-table i))
	    (t
	     (ASET NEW-STATUS PAGE-USAGE-TABLE I))))))

(DEFUN SET-MAP-DISK-SPACE (MAP NEW-STATUS)
  (REQUIRE-LOCK PUT-LOCK)
  (WHEN (> (MAP-NBLOCKS MAP) 0)
    (SETQ PUT-MODIFIED T)
    (DOTIMES (MAP-INDEX (MAP-NBLOCKS MAP))
      (LET ((COUNT (CEILING (MAP-BLOCK-SIZE MAP MAP-INDEX) PAGE-SIZE-IN-BITS))
	    (LOC (MAP-BLOCK-LOCATION MAP MAP-INDEX)))
	(DOTIMES (I COUNT)
	  (DECF (AREF PUT-USAGE-ARRAY (AREF PAGE-USAGE-TABLE (+ LOC I))))
	  (INCF (AREF PUT-USAGE-ARRAY NEW-STATUS))
	  (ASET NEW-STATUS PAGE-USAGE-TABLE (+ LOC I)))))))

(DEFUN DETERMINE-MAP-DISK-SPACE-STATUS (MAP)
  (REQUIRE-LOCK PUT-LOCK)
  (LET ((STATUS NIL))
    (DOTIMES (MAP-INDEX (MAP-NBLOCKS MAP))
      (LET ((COUNT (CEILING (MAP-BLOCK-SIZE MAP MAP-INDEX) PAGE-SIZE-IN-BITS))
	    (LOC (MAP-BLOCK-LOCATION MAP MAP-INDEX)))
	(DOTIMES (I COUNT)
	  (COND ((NULL STATUS)
		 (SETQ STATUS (AREF PAGE-USAGE-TABLE (+ LOC I))))
		((NOT (= STATUS (AREF PAGE-USAGE-TABLE (+ LOC I))))
		 (FERROR NIL "Inconsistent status in DETERMINE-MAP-DISK-SPACE-STATUS"))))))
    STATUS))

;; Binding this variable to non-NIL causes auto creation of directories.
;; This is used internally to create directories, but may be bound by things
;; such as magtape restoration programs.
(DEFVAR LM-AUTOMATICALLY-CREATE-DIRECTORIES NIL)

;; This is a variable with all file streams open since boot time.
;; The :OPEN-STREAMS message to a LOCAL-LISPM-HOST returns COPYLIST of this.
(DEFVAR LM-FILE-STREAMS-LIST NIL)

(DEFVAR LM-FILE-STREAMS-LIST NIL)

;;; debuggung stuff - started by dg 3/4/86

(defconst *lmfs-valid-debug-modes* '(:check-directory-maps))

(defconst *lmfs-active-debug-modes* ()
  "This should be a list of active debug modes, or NIL for normal operation.")


;; [HEADER-ID][VERSION][ID-STRING][CDATE][SELF][FLAG][S0-MAP][S1-MAP]
;; 0          28       32         72     76    80    84

;; in version 1, the Sn-MAP's are regular maps.
;; in version 2, the Sn-MAP's are extended maps.
;; in version 3, the hight bits of the FLAG are a generation counter for
;; files created in the directory and the directory entries are extended
;; to include this number.

(DEFVAR NEW-HEADER-FORMAT-VERSION 2)
(DEFVAR NEW-HEADER-ID (STRING-APPEND "_THIS_" "IS_A_DIRECTORY_" "HEADER_"))
(DEFVAR NEW-HEADER-VERSION-OFFSET (LENGTH NEW-HEADER-ID))
(DEFVAR NEW-HEADER-ID-STRING-OFFSET (+ NEW-HEADER-VERSION-OFFSET 4))
(DEFVAR NEW-HEADER-CDATE-OFFSET	(+ NEW-HEADER-ID-STRING-OFFSET 40))
(DEFVAR NEW-HEADER-SELF-OFFSET (+ NEW-HEADER-CDATE-OFFSET 4))
(DEFVAR NEW-HEADER-FLAG-OFFSET (+ NEW-HEADER-SELF-OFFSET 4))
(DEFVAR NEW-HEADER-S0-MAP-OFFSET (+ NEW-HEADER-FLAG-OFFSET 4))
(DEFVAR NEW-HEADER-MAX-MAP-BLOCKS (FLOOR (- (FLOOR (- 1024. NEW-HEADER-S0-MAP-OFFSET) 2) 2)
					 6.))

(DEFUN STRING-GET-32B (ST OFFSET)
  (DPB (AREF ST (+ OFFSET 3))
       (BYTE 8. 24.)
       (DPB (AREF ST (+ OFFSET 2))
	    (BYTE 8. 16.)
	    (DPB (AREF ST (+ OFFSET 1))
		 (BYTE 8 8)
		 (AREF ST OFFSET)))))

(DEFUN STRING-PUT-32B (ST OFFSET VALUE)
  (SETF (AREF ST OFFSET) (LDB (BYTE 8. 0) VALUE))
  (SETF (AREF ST (+ OFFSET 1)) (LDB (BYTE 8. 8.) VALUE))
  (SETF (AREF ST (+ OFFSET 2)) (LDB (BYTE 8. 16.) VALUE))
  (SETF (AREF ST (+ OFFSET 3)) (LDB (BYTE 8. 24.) VALUE))
  VALUE)

(DEFSTRUCT (DIRHEADER :CONC-NAME :NAMED)
  ID-STRING
  FLAG
  S0-MAP
  S1-MAP)

(DEFMACRO DIRHEADER-FLAG-BIT (H)
  `(LDB ,(BYTE 1 0) (DIRHEADER-FLAG ,H)))

(DEFMACRO DIRHEADER-FLAG-GENERATION-COUNT (H)
  `(LDB ,(BYTE 31 1) (DIRHEADER-FLAG ,H)))
