! File system for new filestores using Mouse kernel %externalstring(47) copyright %alias "GDMR_(C)_FSYS" = %c "Copyright (C) 1987,1988 George D.M. Ross" ! To do: interaction between delete and open files ! support for append-mode ! improved handling for improperly-closed files %constinteger partitions = 31 %constinteger open file limit = 48 %constinteger header cache size = 96 %constinteger bitmap increment = 6113; ! Prime %constinteger auto extend blocks = 32 %constinteger default initial blocks = 32 %constinteger access update interval = 1024; ! Seconds, ~17 minutes %option "-low-nonstandard-nocheck-nodiag-noline" %constinteger infinity = 16_3FFFFFFF; ! Large enough %constinteger interrupts off = 16_0700; ! SR with IPL 7 %include "Moose:Mouse.Inc" %include "GDMR_H:FSysAcc.Inc" %include "GDMR_H:DateTime.Inc" !! %include "GDMR_H:Dump.Inc" %systemintegerfnspec global heap get(%integer size) %externalstring(127)%fnspec itos(%integer i, j) %systemroutinespec phex(%integer i) ! Dictionary access %externalroutinespec FS insert(%string(31) name, %integer value) %externalpredicatespec FS lookup(%string(31) name, %integername value) %conststring(31) state name = "FSYS_STATE" %owninteger fsys state = 0 ! File access mode flags %constinteger auto truncate flag = 1 %constinteger improper close flag = 2 %constinteger non local flag = 16_00010000 ! Error codes %constinteger bugcheck = -100 %constinteger end of file error = -101 %constinteger file header checksum error = -102 %constinteger index file full error = -103 %constinteger file table full error = -104 %constinteger no such file error = -105 %constinteger no authority error = -106 %constinteger bad token error = -107 %constinteger invalid size error = -108 %constinteger bad operation error = -109 %constinteger file header full error = -110 %constinteger not file structured error = -111 %constinteger partition ID error = -112 %constinteger not implemented error = -113 %constinteger incompatible mode error = -114 %constinteger invalid block error = -115 %constinteger no privilege error = -116 %constinteger partition full error = -117 %constinteger bad refcount increment error = -118 %constinteger non zero refcount error = -119 %constinteger dud file index error = -120 %constinteger improperly closed file error = -121 %constinteger file structured error = -122 ! Bitmap stuff %include "GDMR_H:BitMap" ! File header formats & constants. ! Access definitions (conventionally ID > 0 for user, < 0 for group) %recordformat header access fm(%integer ID, access) %constinteger no access = 0 { File is inaccessible %constinteger read access = 1 { File can be read %constinteger modify access = 2 { File can be modified %constinteger append access = 4 { File can be appended to %constinteger exchange access = 8 { File can be (extent) exchanged %constinteger link access = 16 { File can be (un)linked %constinteger control access = 32 { File attributes can be modified %constinteger deny access = 64 { Invert sense of access bits %constinteger admin required = 128 { Admin privilege needed to use field %constinteger an altering access mode = modify access ! append access ! %c exchange access %constinteger new owner access = read access ! modify access ! %c append access ! exchange access ! %c link access ! control access %constinteger new local access = read access %constinteger new world access = no access; ! nil access -> local %constinteger system ID = -1 ! Flag word definitions %constinteger overlapping file = 16_0002 %constinteger bad block file = 16_0004 %constinteger improperly closed file = 16_0008 %constinteger backup required = 16_0100 %constinteger archive required = 16_0200 %constinteger directory flags = 16_C000; ! Two bits for the dir layer %constinteger multiple references = 16_40000000; ! Pseudo-flag %constinteger world read access = 16_20000000; ! Pseudo-flag ! Extent definition %recordformat extent fm(%integer start, size) ! Header definition %constinteger non extent size = 8 + 2 + 2 + 12 + 8 + %c 12 + 12 + 16 + 4 + 2 + 2 %constinteger extent limit = (512 - non extent size) // 8 %constinteger access table size = extent limit; ! One grows up, other down %recordformat file header fm((%integer checksum, ID, %short header refcount, %short flags, %integer owner, owner access, supervisor, %integer world access, local access, %integer creator, static ID, audit ID, %integer created, modified, accessed, %string(15) creation name, %integer blocks used, %short bytes in last block, %short extent limit, ( %record(header access fm)%array %c access(1 : access table size) %c %or %record(extent fm)%array %c extent(1 : extent limit)) %c ) %or %integerarray x(1 : 128)) ! ID fields: ! directory | partition | sequence | index ! 2 bits | 6 bits | 8 bits | 16 bits %constinteger index part = 16_0000FFFF %constinteger file ID part = 16_00FFFFFF %constinteger partition part = 16_3F000000 %constinteger partition shift = 24 %constinteger partition number mask = partition part >> partition shift %constinteger file ID mask = 16_3FFFFFFF %constinteger file sequence increment = 16_00010000 ! Partition access. Forward specs for procedures included at the end of ! this module to enforce proper layering. %recordformat partition bad list fm(%integer n, %integerarray b(0 : 511)) %integerfnspec partition read(%integer block, %record(*)%name buffer) %integerfnspec partition write(%integer block, %record(*)%name buffer) %routinespec partition enquiry(%integername v, s, h) %routinespec partition info(%integer pn, %integername unit, disc start, size, flags) %routinespec partition bad list(%integer pn, %record(partition bad list fm)%name b) %routinespec partition prefetch(%integer block) %recordformat partition fm(%integer size, flags, index site, index size, bitmap, %bytename header allocation bitmap, %record(file header fm) index header) %constinteger partition valid = 16_0001 %constinteger partition hazarded = 16_0002 %constinteger partition structured = 16_0004 %constinteger access logging enabled = 16_0100 %recordformat partition header fm((%integer checksum, size, index, flags %c ) %or %integerarray x(1 : 128)) ! Various extra stuff can go on the end later.... ! Open file tables. There are two "structures" involved here. The simpler ! of the two is the open file table: this contains details of the user's ! (client's) use of the file; several concurrent users' entries are chained ! together for ease of access on a doubly-linked list. The header cache ! table contains the (shared) header details: in parallel for convenience of ! machine-code access is an array containing the header's ID. %recordformatspec header cache fm %recordformat open file table fm(%integer ID, %integer mode, %integer compatible, %integer last block read, %integer header cache slot, %record(header cache fm)%name fhce, %record(open file table fm)%name last, next) ! Should be something about access here too? %recordformat header cache fm(%integer stamp, refcount, status, dirty, %record(semaphore fm) semaphore, %record(open file table fm)%name use list, %record(file header fm) h) %constinteger slot allocated = 16_80000000 ! Common file system tables %recordformat common tables fm(%record(semaphore fm) initialisation semaphore, %record(semaphore fm) open file table semaphore, %record(partition fm)%array %c partition(1 : partitions), %record(open file table fm)%array %c open file table(1 : open file limit), %integer header cache stamp, %integerarray header ID(1 : header cache size), %record(header cache fm)%array %c header cache(1 : header cache size), %integer header cache hits, header cache misses) %conststring(31) tables name = "FS__FSYS_TABLES" %ownrecord(common tables fm)%name tables == nil ! File header checksum generation & verification %routine set header checksum(%record(file header fm)%name h) %integer i, c c = 0 c <- c + h_x(i) %for i = 2, 1, 128 h_checksum = -c %end %predicate check header checksum(%record(file header fm)%name h) %integer i, c, x %label L, F ! (already) A0 = addr(h) D0 = 0; D1 = 127 L: *add.l (A0)+, D0 *dbra D1, L *bne F %true F: !%false *move.l D0, x {} printstring("Suspect checksum "); write(x, 0) {} printstring(" for "); phex(h_ID); newline c = 0 c <- c + h_x(i) %for i = 1, 1, 128 %true %if c = 0 !! dump(512, byteinteger(addr(h))) %false %end ! File I/O ! Scan through the extent list in the file header looking for the extent ! containing the requested block. Apply the partition offset and do the ! transfer if found. %integerfn read file block(%integer p, %record(file header fm)%name h, %integer block, %record(*)%name buffer, %integername next disc address) %record(extent fm)%name e %integer i, status, partition bits !! printstring("Read file block: partition "); write(p, 0) !! printstring(", header at "); phex(addr(h)) !! printstring(", block "); write(block, 0) !! printstring(", buffer "); phex(addr(buffer)); newline %result = end of file error %if h_extent limit > extent limit partition bits = p << partition shift %for i = extent limit, -1, h_extent limit %cycle e == h_extent(i) %if block < e_size %start ! It's in this extent. Read the block, then check for prefetch. status = partition read(partition bits + e_start + block, buffer) %result = status %if status < 0 %if block = e_size - 1 %start ! Last block in the extent, tell our caller what the next ! one on disc will be (for prefetch). %if i = h_extent limit %start ! No more extents -- indicate no prefetch next disc address = 0 %else next disc address = h_extent(i - 1)_start ! partition bits %finish %else %if status > 0 ! End of current cache chunk. We know that there must be ! another block in the extent, else the previous condition ! would have held and we wouldn't be here. next disc address = (e_start + block + 1) ! partition bits %else ! No prefetch called for immediately next disc address = 0 %finish %result = 0 %else block = block - e_size %finish %repeat !! printstring("Read file block: out of extents") !! newline %result = end of file error %end %integerfn translate file block(%record(file header fm)%name h, %integer block, %integername disc address) %record(extent fm)%name e %integer i %result = end of file error %if h_extent limit > extent limit %for i = extent limit, -1, h_extent limit %cycle e == h_extent(i) %if block < e_size %start disc address = e_start + block %result = 0 %else block = block - e_size %finish %repeat %result = end of file error %end %integerfn write file block(%integer p, %record(file header fm)%name h, %integer block, %record(*)%name buffer) %record(extent fm)%name e %integer i %result = end of file error %if h_extent limit > extent limit %for i = extent limit, -1, h_extent limit %cycle e == h_extent(i) %if block < e_size %start %result = partition write((p << partition shift) + e_start + block, buffer) %else block = block - e_size %finish %repeat %result = end of file error %end ! Index file I/O ! Since the index is just another file we can use the normal block ! transfer routines above. The index's header is cached. %integerfn read index block(%integer pn, n, %record(file header fm)%name h) %integer status, prefetch block !! printstring("Read index block: partition "); write(pn, 0) !! printstring(", slot "); write(n, 0); newline n = n & index part %unless 0 < n <= tables_partition(pn)_index size %start !! printstring("Read index block: n = "); write(n, 0) !! printstring(", pn = "); write(pn, 0); newline %result = dud file index error %finish status = read file block(pn, tables_partition(pn)_index header, n - 1, h, prefetch block) %result = status %if status < 0 %result = file header checksum error %unless check header checksum(h) %result = 0 %end %integerfn write index block(%integer pn, n, %record(file header fm)%name h) !! printstring("Write index block: partition "); write(pn, 0) !! printstring(", slot "); write(n, 0); newline set header checksum(h) n = n & index part %unless 0 < n <= tables_partition(pn)_index size %start !! printstring("Write index block: n = "); write(n, 0) !! printstring(", pn = "); write(pn, 0); newline %result = dud file index error %finish %result = write file block(pn, tables_partition(pn)_index header, n - 1, h) %end ! File size determination ! Scan the extent list adding up the sizes. %integerfn file size(%record(file header fm)%name h) %integer i, s !! display header(h) s = 0 s = s + h_extent(i)_size %for i = extent limit, -1, h_extent limit %result = s %end ! File slot allocation stuff ! Slots in the file header are allocated from a bitmap for each ! partition, access to which is interlocked via the open file table ! semaphore (ASSUMED claimed). ! *Should really cycle through slots* %integerfn allocate file slot(%record(partition fm)%name p) %integer i, j, k %bytename b b == p_header allocation bitmap %for i = 1, 1, p_index size // 8 %cycle %if b # 255 %start k = 1 %for j = 0, 1, 7 %cycle %if b & k = 0 %start ! Found one b = b ! k %result = 8 * i + j - 7 %finish k = k << 1 %repeat %finish b == b [1] %repeat %result = index file full error %end %routine free file slot(%record(partition fm)%name p, %integer slot) %integer byte, bit %bytename b %signal 13, 99, bugcheck %unless 0 < slot <= p_index size slot = slot - 1 byte = slot // 8; bit = slot & 7 b == p_header allocation bitmap b [byte] = b [byte] & (\ (1 << bit)) %end %routine claim file slot(%record(partition fm)%name p, %integer slot) %integer byte, bit %bytename b slot = slot - 1 byte = slot // 8; bit = slot & 7 b == p_header allocation bitmap b [byte] = b [byte] ! (1 << bit) %end ! Initialisation ! Obtain the partition details from the partition module and the ! partition headers (conventionally block 0 of each). If the ! partition is structured, scan the index file and verify and build ! the bitmaps. %routine load claim(%record(file header fm)%name h, %integer bitmap, %integername tally) %record(extent fm)%name e %integer i tally = 0 %for i = extent limit, -1, h_extent limit %cycle e == h_extent(i) %unless claim extent(e_start, e_size, bitmap) %start printstring("Claim extent fails for file ") phex(h_ID); printstring(" extent "); write(i, 0) printstring(": start "); write(e_start, 0) printstring(", size "); write(e_size, 0) newline %finish tally = tally + e_size %repeat %end %bytemap zapped map(%integer bytes) %integer where bytes = (bytes + 7) & (\ 3) where = global heap get(bytes) D0 = bytes // 4 - 1 A0 = where L: *clr.l (A0)+ *dbra D0, L %result == byteinteger(where) %end %routine load partition(%integer pn, structured, hazarded) %record(partition header fm) partition header %record(partition bad list fm) pbl %record(file header fm) fh %record(partition fm)%name p %integer c, j, k, status, px, i, tally, size tally, size %integer unit, disc start, partition size, partition flags !! printstring("Load partition "); write(pn, 0) !! printstring(" structured") %if structured # 0 !! printstring(" hazarded") %if hazarded # 0 !! newline p == tables_partition(pn) px = pn << partition shift p_flags = partition valid ! access logging enabled; !<<<<<<<<< p_flags = p_flags ! partition hazarded %if hazarded # 0 partition info(pn, unit, disc start, partition size, partition flags) !! printstring("Partition "); write(pn, 0) !! printstring(": unit "); write(unit, 0) !! printstring(", disc start "); phex(disc start) !! printstring(", size "); write(partition size, 0) !! printstring(", flags "); phex(partition flags); newline p_size = partition size %return %if structured = 0 ! Obtain the bad list partition bad list(pn, pbl) !! printstring("Partition "); write(pn, 0) !! printstring(" bad list:"); newline !! %if pbl_n = 0 %start !! printstring(" No bad blocks"); newline !! %else !! %for i = 1, 1, pbl_n %cycle !! write(pbl_b(i), 9) !! newline %if i & 7 = 0 !! %repeat !! newline %if i & 7 # 0 !! %finish ! Scan the index file. p_flags = p_flags ! partition structured status = partition read(px, partition header) %if status < 0 %start printstring(" *disc error reading header*") newline %return %finish !! printstring("Got header"); newline c = 0 c <- c + partition header_x(j) %for j = 1, 1, 128 %if c # 0 %start printstring(" *header checksum error*") newline p_flags = 0 %return %finish tally = 1 ! Extract size and index offset, and set up bitmaps p_size = partition header_size %if p_size # partition size %start printstring("Partition "); write(pn, 0) printstring(": header size "); write(p_size, 0) printstring(" # disc header size "); write(partition size, 0) printstring(" ??"); newline %finish p_index site = partition header_index p_bitmap = new bitmap(p_size, bitmap increment) status = partition read(px ! p_index site, p_index header) %if status < 0 %start printstring(" *index header checksum error*") newline p_flags = p_flags & (\ partition structured) %return %finish p_index size = file size(p_index header) & (\ 7) p_header allocation bitmap == zapped map(p_index size // 8) p_header allocation bitmap = 1; ! Claim the index file load claim(p_index header, p_bitmap, size) size tally = size ! Preallocate the bad spots. If any of these happen to overlap the ! index file then there may be complaints... %if pbl_n > 0 %start %for i = 1, 1, pbl_n %cycle %if claim extent(pbl_b(i) & (\ 7), 8, p_bitmap) %start; %finish %repeat %finish !! printstring("Index size is "); write(p_index size, 0); newline %for i = 2, 1, p_index size %cycle !! printstring("Reading "); write(i, 0); newline status = translate file block(p_index header, i - 1, j) %if status = 0 %start ! Check to see if this index block is bad (we'll do this ! the hard way for now...) %for k = 1, 1, pbl_n %cycle %if pbl_b(k) & (\ 7) = j & (\ 7) %start ! Dud block in the current index chunk -- ignore ! the lot as otherwise adjacent files will cause ! lots of errors.... !! printstring("Index slot "); write(i, 0) !! printstring(" at "); write(j, 0) !! printstring(" marked bad"); newline claim file slot(p, i) -> the next one %finish %repeat %else printstring("Failed to translate index slot address: ") write(status, 0); newline p_flags = p_flags & (\ partition hazarded) %continue %finish status = read index block(pn, i, fh) %if status < 0 %start printstring("Failed to read file header "); write(i, 0) space; write(status, 0) newline !fh = 0 !status = write index block(pn, i, fh) !printstring("Zap status "); write(status, 0); newline p_flags = p_flags & (\ partition hazarded); ! Minimise damage %continue %finish %unless check header checksum(fh) %start printstring("File header checksum error for ") write(i, 0) newline p_flags = p_flags & (\ partition hazarded); ! Minimise damage %continue %finish %if fh_ID & index part # 0 %start ! File slot is in use claim file slot(p, i) load claim(fh, p_bitmap, size) tally = tally + 1 size tally = size tally + size %finish the next one: %repeat printstring("Partition "); write(pn, 0) printstring(": "); write(tally, 0) printstring(" file"); print symbol('s') %if tally # 1 printstring(" (max "); write(p_index size, 0); printstring(", ") write(100 * tally // p_index size, 0) printstring("%), "); write(size tally, 0); printstring(" blocks (") write(100 * size tally // p_size, 0); printstring("%)") newline %end %externalroutine fsys initialise %integer v, s, h, i, m, t %record(semaphore fm)%name dt set sem !! printstring("FSys initialise"); newline %if tables == nil %start %if FS lookup(tables name, t) %start ! Tables are already in the dictionary. Map them. tables == record(t) !! printstring("Tables found at "); phex(addr(tables)); newline semaphore wait(tables_initialisation semaphore) signal semaphore(tables_initialisation semaphore) %else ! Tables weren't in the dictionary, so we must create them ! and initialise them. FS insert(state name, addr(fsys state)) !! printstring("Tables size: "); write(size of(tables), 0); newline tables == record(global heap get(size of(tables))); tables = 0 setup semaphore(tables_initialisation semaphore) FS insert(tables name, addr(tables)) !! printstring("Tables created at "); phex(addr(tables)); newline setup semaphore(tables_open file table semaphore) signal semaphore(tables_open file table semaphore) ! Tables initialised, now do the partitions. partition enquiry(v, s, h) %for i = 1, 1, 31 %cycle m = 1 << i load partition(i, s & m, h & m) %if v & m # 0 %repeat %if FS lookup("DATE_SET_SEMAPHORE", i) %start dt set sem == record(i) !! printstring("FSys finds set sem at "); phex(addr(dt set sem)) !! newline semaphore wait(dt set sem) signal semaphore(dt set sem) %else printstring("No date/time set semaphore??"); newline %finish fsys state = 1 signal semaphore(tables_initialisation semaphore) t = -1 %finish %finish %end ! Return valid partition map %externalroutine fsys valid partitions(%integername v, s) %integer i, j v = 0; s = 0 %for i = 1, 1, partitions %cycle j = tables_partition(i)_flags v = v ! (1 << i) %if j & partition valid # 0 s = s ! (1 << i) %if j & partition structured # 0 %repeat %end ! Authority checks %predicate file access authority OK(%record(file header fm)%name h, %record(fsys access fm)%name access, %integer mode, flags) %record(header access fm)%name a %integer permitted access, i, j, have owner access %true %if access == nil %or access_privileges & bypass privilege # 0 permitted access = 0 %for j = 1, 1, h_extent limit - 1 %cycle a == h_access(j) %exit %if a_ID = 0; ! No more groups defined %for i = 1, 1, access_groups %cycle %if access_group(i) = a_ID %start %if a_access & deny access = 0 %start permitted access = permitted access ! a_access %c %if a_access & admin required = 0 %c %or access_privileges & admin privilege # 0 %else permitted access = permitted access & (\ a_access) %finish %finish %repeat %repeat permitted access = permitted access ! h_world access %c %if h_world access & admin required = 0 %c %or access_privileges & admin privilege # 0 permitted access = permitted access ! h_local access %c %if flags & non local flag = 0 %c %and (h_local access & admin required = 0 %c %or access_privileges & admin privilege # 0) %if h_owner access & admin required = 0 %c %or access_privileges & admin privilege # 0 %start have owner access = 0 %for i = 1, 1, access_groups %cycle j = access_group(i) %if j = h_owner %or j = h_supervisor # 0 %or j = h_creator %start have owner access = 1 %exit %finish %repeat permitted access = permitted access ! h_owner access ! control access %c %if h_owner = access_user ID %or h_creator = access_user ID %c %or 0 # h_supervisor = access_user ID %or have owner access # 0 %finish permitted access = permitted access ! read access %c %if access_privileges & readall privilege # 0 permitted access = permitted access ! append access ! exchange access %c %if permitted access & modify access # 0; ! Implied %true %if permitted access & mode = mode %false %end ! Open file table access. ! Each open file has a record here, containing access modes etc and a ! pointer to the file's header cache entry. Files which are open more ! than once share the same header cache entry, which has a reference ! count, a dirty-bit, and an access semaphore as well as the header. ! Semaphore strategy: any manipulations of the headers themselves require ! that the per-header semaphore be claimed. Any manipulations of the ! tables, as opposed to the headers, require that the global semaphore ! be claimed. ! ASSUME the global semaphore has been claimed when these are called. ! For convenience, RELEASE it should an error condition arise. %integerfn get open file slot %record(open file table fm)%name o %integer i ! Scan the tables looking for an unused slot %for i = 1, 1, open file limit %cycle o == tables_open file table(i) %if o_mode = 0 %start o_mode = slot allocated o_ID = -2 %result = i %finish %repeat ! None found signal semaphore(tables_open file table semaphore) %result = file table full error %end ! WHATEVER happens, this one will RELEASE the file table semaphore. If it ! succeeds it will return with the file header semaphore claimed. %integerfn get header(%record(open file table fm)%name o) %constinteger header cache loop = header cache size - 1 %record(header cache fm)%name h %record(open file table fm)%name others %integer x, i, oldest, oldest slot %label IDL, found it !! printstring("Get header for "); phex(o_ID); newline ! First off, search the cache for the header. This loop is based on the ! one used by the partition cache manager. x = addr(tables_header ID(1)) D1 = o_ID { The file ID we're looking for *move.l x, A0 { The address of the first ID *move.l #header cache loop, D0 IDL:*cmp.l (A0)+, D1 { Compare file IDs *dbeq D0, IDL { Test comparison, decrement & loop ! NB: for the DBcc instruction, the cc tests true for NOT performing ! the decrement and branch!! Either the cc test has succeeded, or we've ! reached the end of the loop. The cc are still the same so we can just ! perform the appropriate conditional branch.... *beq found it { Test was true, we've found the entry ! If we drop through here the entry wasn't in the header cache. We'll ! have to allocate a new slot and fetch it in. It's not worth using ! loop mode here as there are too many comparisons involved. oldest = infinity %for i = 1, 1, header cache size %cycle h == tables_header cache(i) %if h_refcount <= 0 %and h_stamp < oldest %start oldest = h_stamp oldest slot = i %finish %repeat ! Found an unused-recently slot. Initialise table entries, knitting. !! printstring("Header cache miss, using "); write(oldest slot, 0); newline o_header cache slot = oldest slot tables_header ID(oldest slot) = o_ID h == tables_header cache(oldest slot) setup semaphore(h_semaphore); ! Implicitly claims it h_refcount = 1 o_last == nil; o_next == nil h_use list == o o_fhce == h tables_header cache misses = tables_header cache misses + 1 tables_header cache stamp = tables_header cache stamp + 1 h_stamp = tables_header cache stamp ! Release the tables semaphore, read in the header (header semaphore ! is still implicitly claimed). signal semaphore(tables_open file table semaphore) i = read index block(o_ID >> partition shift, o_ID, h_h) %if i < 0 %start ! Readin failed: dump it all semaphore wait(tables_open file table semaphore) h_refcount = h_refcount - 1 %if h_refcount <= 0 %start ! Only ourselves interested. tables_header cache(oldest slot) = 0; ! Anonymous slot ID h_stamp = 1; ! Force reuse %else ! Someone else now waiting. Just note the status, then ! signal the semaphore to let them proceed. h_status = i signal semaphore(h_semaphore) %finish o = 0 o_header cache slot = infinity signal semaphore(tables_open file table semaphore) %finish ! All done, return whatever status we have %result = i found it: ! The entry was in the cache. Check for compatibility with other users, ! bump the refcount and link us on. *move.l A0, x o_header cache slot = (x - addr(tables_header ID(1))) >> 2 !! printstring("Header cache hit at "); write(o_header cache slot, 0); newline tables_header cache hits = tables_header cache hits + 1 h == tables_header cache(o_header cache slot) %if h_status < 0 %start ! A previous readin failed. Just return the same error for now.... i = h_status o = 0 o_header cache slot = infinity signal semaphore(tables_open file table semaphore) %result = i %finish tables_header cache stamp = tables_header cache stamp + 1 h_stamp = tables_header cache stamp %if h_refcount <= 0 %start ! No one else interested. No need to check compatibility, just ! link us on and return success.... h_refcount = 1 o_last == nil; o_next == nil h_use list == o o_fhce == h setup semaphore(h_semaphore); ! Implicitly claim it signal semaphore(tables_open file table semaphore) %result = 0 %finish ! The refcount is non-zero, so someone else is already interested in ! this file. Before we link ourselves in we have to check that our ! intended access is allowed by those other users. others == h_use list %if others == nil %start ! Apparently there were no other users!? !! printstring("Bogus refcount for "); phex(o_ID); newline o = 0 o_header cache slot = infinity signal semaphore(tables_open file table semaphore) %result = bugcheck %finish %while others ## nil %cycle %if others_compatible & o_mode # o_mode %c %or o_compatible & others_mode # others_mode %start ! Either they've disallowed us or we've disallowed them o = 0 o_header cache slot = infinity signal semaphore(tables_open file table semaphore) %result = incompatible mode error %finish others == others_next %repeat ! No complaints, so bump the refcount and link us in. Easiest is at ! the front of the list. h_refcount = h_refcount + 1 others == h_use list o_next == others; o_last == nil others_last == o; h_use list == o o_fhce == h ! Finally, switch semaphores and return success signal semaphore(tables_open file table semaphore) semaphore wait(h_semaphore) %result = 0 %end ! This one is entered with the per-header semaphore claimed. It releases ! that one and claims the global semaphore. %routine forget header(%record(open file table fm)%name o) %record(header cache fm)%name h !! %record(open file table fm)%name x !! printstring("Forget header "); phex(o_ID); newline h == o_fhce !! %if h_dirty # 0 %start !! printstring("Forgetting dirty header ??") !! newline !! %finish signal semaphore(h_semaphore) semaphore wait(tables_open file table semaphore) h_refcount = h_refcount - 1 %if h_refcount <= 0 %start ! Header reference count indicates no-one is interested in ! the header. This is the easy case, as we can just zap the ! necessary fields. h_use list == nil %else ! Someone else is interested in this file. We'll have to disentangle ! ourselves the hard way.... !! printstring("Refcount now "); write(h_refcount, 0); newline !! x == h_use list !! %while x ## nil %cycle !! phex(addr(x)); printstring(" -> "); phex(addr(x_next)) !! printstring(" <- "); phex(addr(x_last)) !! printstring(" us") %if x == o !! newline !! x == x_next !! %repeat %if o_last == nil %start ! We were at the head of the list !! printstring("At the head"); newline h_use list == o_next o_next_last == nil %unless o_next == nil %else %if o_next == nil ! We're at the end of the list !! printstring("At the tail"); newline o_last_next == nil %else ! We're in the middle, so two others to adjust !! printstring("In the middle"); newline o_next_last == o_last o_last_next == o_next %finish %finish o = 0; ! Zap the open file entry for safety o_header cache slot = infinity %end ! User-visible interface ! Open, close, read, write, delete, extend etc. %externalintegerfn fsys open file(%record(fsys access fm)%name access, %integer ID, mode, compatible, request flags, %integername token, size, flags) %record(file header fm)%name h %record(extent fm)%name e %record(open file table fm)%name f %record(partition fm)%name p %integer i, pn !! printstring("FSys open file "); phex(ID) !! printstring(" mode "); phex(mode) !! printstring(" compatible "); phex(compatible); newline ID = ID & file ID mask ! Find the partition record pn = ID >> partition shift %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = partition ID error %if p_flags & partition valid = 0 %if p_flags & partition structured = 0 %start %result = no privilege error %c %if access ## nil %and access_privileges & bootarea privilege = 0 token = 16_80000000 ! pn !! printstring("Unstructured open: partition "); write(pn, 0) !! printstring(", size "); write(p_size, 0) !! printstring(" -> "); phex(token); newline size = p_size << 9 flags = 0 %result = 0 %finish ! Need access to the tables, so interlock. semaphore wait(tables_open file table semaphore) token = get open file slot %result = token %if token < 0 f == tables_open file table(token) f_ID = ID; f_mode = mode; f_compatible = compatible ! control access ! Need the file's header to check. i = get header(f) %result = i %if i < 0 h == f_fhce_h %if h_ID # ID & file ID part %start forget header(f) signal semaphore(tables_open file table semaphore) %result = no such file error %finish %unless file access authority OK(h, access, mode, request flags) %start forget header(f) signal semaphore(tables_open file table semaphore) %result = no authority error %finish %if h_flags & improperly closed file # 0 %start forget header(f) signal semaphore(tables_open file table semaphore) %result = improperly closed file error %finish %if p_flags & access logging enabled # 0 %start i = get datestamp h_accessed = i %and f_fhce_dirty = 1 %c %if i - h_accessed >= access update interval %finish %if h_blocks used > 0 %start size = (h_blocks used - 1) << 9 + h_bytes in last block %else ! Empty file, so can't use above calculation size = 0 %finish flags = h_flags & 16_FFFF flags = flags ! multiple references %if h_header refcount >= 2 flags = flags ! world read access %if h_world access & read access # 0 %if mode & an altering access mode # 0 %start ! Update stamp on open (for RWT) h_modified = get datestamp; f_fhce_dirty = 1 %finish f_last block read = -2 %if h_extent limit <= extent limit %start !! printstring("Prefetch first block"); newline e == h_extent(extent limit) partition prefetch(e_start ! (pn << partition shift)) %if e_size < 6 {arbitrary} %and h_extent limit # extent limit %start ! Prefetch the start of the next extent too e == e [-1] partition prefetch(e_start ! (pn << partition shift)) %finish %finish signal semaphore(f_fhce_semaphore) %result = 0 %end %externalintegerfn fsys flush header(%record(fsys access fm)%name access, %integer token) %record(open file table fm)%name f %record(header cache fm)%name fhce %record(file header fm)%name h %integer status, pn %result = 0 %if token & 16_80000000 # 0; ! Unstructured (probably) %result = bad token error %unless 0 < token <= open file limit semaphore wait(tables_open file table semaphore) f == tables_open file table(token) fhce == f_fhce; h == fhce_h pn = f_ID >> partition shift signal semaphore(tables_open file table semaphore) semaphore wait(fhce_semaphore) status = 0; ! Provisionally %if fhce_dirty # 0 %start ! The header has been modified, so we'll have to write ! it out to disc. Maybe it wasn't us, but..... status = write index block(pn, f_ID, h) %if status < 0 %start !! printstring("Flush header: put index status ") !! phex(status); newline %else fhce_dirty = 0 %finish %finish signal semaphore(fhce_semaphore) %result = status %end %externalintegerfn fsys close file(%record(fsys access fm)%name access, %integer token, flags) ! If the refcount is zero we should delete the file too. %record(open file table fm)%name f %record(partition fm)%name p %record(header cache fm)%name fhce %record(file header fm)%name h %record(extent fm)%name e %integer status, i, alloc, keep, pn %result = 0 %if token & 16_80000000 # 0; ! Unstructured (probably) %result = bad token error %unless 0 < token <= open file limit semaphore wait(tables_open file table semaphore) f == tables_open file table(token) %if f_mode = 0 %start signal semaphore(tables_open file table semaphore) %result = bad token error %finish fhce == f_fhce; h == fhce_h pn = f_ID >> partition shift p == tables_partition(pn) signal semaphore(tables_open file table semaphore) semaphore wait(fhce_semaphore) %if f_mode & (modify access ! append access) # 0 %start %if flags & auto truncate flag # 0 %start ! Excess extents at the end may need to be freed. ! (Note that they wouldn't have been readable by the user.) %if flags & improper close flag # 0 %start h_flags = h_flags ! improperly closed file fhce_dirty = 1 %finish %if h_flags & overlapping file # 0 %start -> close it %finish alloc = 0 %for i = extent limit, -1, h_extent limit %cycle e == h_extent(i) %if alloc >= 0 %start ! All used so far... alloc = alloc + e_size %if alloc > h_blocks used %start keep = e_size - alloc + h_blocks used %unless free extent(e_start + keep, e_size - keep, p_bitmap) %start printstring("Free extent failed"); newline %finish %if keep = 0 %start ! Nothing to keep, so forget the entire extent e_start = -infinity; e_size = -1; ! Just in case... h_extent limit = i + 1 %else ! Something still in use, so note the new size e_size = keep h_extent limit = i %finish alloc = -1 fhce_dirty = 1 %finish %else ! Free from here on %unless free extent(e_start, e_size, p_bitmap) %start printstring("Free extent failed"); newline %finish e = 0 fhce_dirty = 1 %finish %repeat %finish %if flags & directory flags # 0 %start h_flags <- (h_flags & \directory flags) ! (flags & directory flags) fhce_dirty = 1 %finish %finish close it: status = 0; ! Provisionally %if fhce_dirty # 0 %start ! The header has been modified, so we'll have to write ! it out to disc. Maybe it wasn't us, but..... status = write index block(pn, f_ID, h) %if status < 0 %start printstring("Close file: put index status ") phex(status); newline %else fhce_dirty = 0 %finish %finish forget header(f) signal semaphore(tables_open file table semaphore) %result = status %end %externalintegerfn fsys truncate open file(%record(fsys access fm)%name access, %integer token, bytes) %record(header cache fm)%name fhce %record(open file table fm)%name f %record(file header fm)%name h %integer mode, blocks !! printstring("FSys truncate open file: access "); phex(addr(access)) !! printstring(", token: "); write(token, 0) !! printstring(", blocks: "); write(blocks, 0) !! printstring(", bytes: "); write(bytes, 0); newline %result = bad token error %unless 0 < token <= open file limit %if bytes = 0 %start blocks = 0; bytes = 0 %else %if bytes > 0 blocks = bytes >> 9; bytes = bytes & 511 blocks = blocks - 1 %and bytes = 512 %if bytes = 0 ! NB blocks now one too small %else %result = invalid block error %finish semaphore wait(tables_open file table semaphore) f == tables_open file table(token) %if f_mode = 0 %start signal semaphore(tables_open file table semaphore) %result = bad token error %finish fhce == f_fhce; mode = f_mode signal semaphore(tables_open file table semaphore) %result = bad operation error %if mode & modify access = 0 semaphore wait(fhce_semaphore) h == fhce_h !! printstring("File header at "); phex(addr(h)); newline %if blocks > h_blocks used %c %or (blocks = h_blocks used %c %and bytes > h_bytes in last block) %start signal semaphore(fhce_semaphore) %result = end of file error %finish h_blocks used = blocks + 1 h_bytes in last block = bytes fhce_dirty = 1 signal semaphore(fhce_semaphore) %result = 0 %end %externalintegerfn fsys read file block(%record(fsys access fm)%name access, %integer token, block, %integername bytes, %record(*)%name buffer) %record(partition fm)%name p %record(header cache fm)%name fhce %record(open file table fm)%name f %record(file header fm)%name h %integer pn, status, mode, prefetch block !! printstring("FSys read file block: access "); phex(addr(access)) !! printstring(", token: "); write(token, 0) !! printstring(", block: "); write(block, 0) !! printstring(", buffer: "); phex(addr(buffer)); newline %if token & 16_80000000 # 0 %start ! Unstructured. %result = no privilege error %c %if access ## nil %and access_privileges & bootarea privilege = 0 pn = token & 16_7FFFFFFF %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = partition ID error %if p_flags & partition valid = 0 %result = file structured error %if p_flags & partition structured # 0 !! printstring("Unstructured read: partition "); write(pn, 0) !! printstring(", block "); write(block, 0); newline bytes = 512; ! Assuming we succeed.... status = partition read((pn << partition shift) + block, buffer) %result = status %if status < 0 ! +ve result means we could prefetch, but we won't bother for now.... %result = 0 %finish %result = bad token error %unless 0 < token <= open file limit %result = invalid block error %if block < 0 semaphore wait(tables_open file table semaphore) f == tables_open file table(token) %if f_mode = 0 %start signal semaphore(tables_open file table semaphore) %result = bad token error %finish fhce == f_fhce; mode = f_mode signal semaphore(tables_open file table semaphore) %result = bad operation error %if mode & read access = 0 pn = f_ID >> partition shift semaphore wait(fhce_semaphore) h == fhce_h !! printstring("File header at "); phex(addr(h)); newline %if block >= h_blocks used %start !! phex(f_ID); printstring(" ("); phex(h_ID) !! printstring("): asking for "); write(block, 0) !! printstring(", found "); write(h_blocks used, 0); newline !! display header(h) %if block = h_blocks used %start signal semaphore(fhce_semaphore) bytes = 0 %result = 0 %else signal semaphore(fhce_semaphore) %result = end of file error %finish %finish %if block = h_blocks used - 1 %then bytes = h_bytes in last block %c %else bytes = 512 status = read file block(pn, h, block, buffer, prefetch block) %if prefetch block # 0 %and block = f_last block read + 1 %start !! printstring("Sequential, prefetch indicated"); newline partition prefetch(prefetch block) %finish f_last block read = block signal semaphore(fhce_semaphore) !! printstring("Got block, status "); write(status, 0); newline %result = status %end %externalintegerfn fsys write file block(%record(fsys access fm)%name access, %integer token, block, bytes, %record(*)%name buffer) %record(partition fm)%name p %record(open file table fm)%name f %record(header cache fm)%name fhce %record(file header fm)%name h %record(extent fm)%name e %integer status, pn, mode, extended = 0, actual, start, i %if token & 16_80000000 # 0 %start ! Unstructured. %result = no privilege error %c %if access ## nil %and access_privileges & bootarea privilege = 0 pn = token & 16_7FFFFFFF %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = partition ID error %if p_flags & partition valid = 0 %result = file structured error %if p_flags & partition structured # 0 %result = no authority error %if p_flags & partition hazarded = 0 !! printstring("Unstructured write: partition "); write(pn, 0) !! printstring(", block "); write(block, 0); newline ! ignored. %result = partition write((pn << partition shift) + block, buffer) %finish %result = bad token error %unless 0 < token <= open file limit %result = invalid block error %if block < 0 semaphore wait(tables_open file table semaphore) f == tables_open file table(token) %if f_mode = 0 %start signal semaphore(tables_open file table semaphore) %result = bad token error %finish fhce == f_fhce; mode = f_mode signal semaphore(tables_open file table semaphore) %result = bad operation error %if mode & modify access = 0 pn = f_ID >> partition shift p == tables_partition(pn) semaphore wait(fhce_semaphore) h == fhce_h %if block > h_blocks used %start ! Can't leave (security) holes in the file signal semaphore(fhce_semaphore) %result = bad operation error %finish %if block = h_blocks used %start ! One beyond the (current) end h_blocks used = block + 1 h_bytes in last block = bytes fhce_dirty = 1 %else %if block = h_blocks used - 1 ! Update of current last block h_bytes in last block = bytes fhce_dirty = 1 %else %if bytes # 512 ! Can't put a short block in the middle. signal semaphore(fhce_semaphore) %result = bad operation error %finish try write again: status = write file block(pn, h, block, buffer) %if status # end of file error %or extended # 0 %start signal semaphore(fhce_semaphore) %result = status %finish ! No space, get some more e == h_extent(h_extent limit) %unless allocate extent(auto extend blocks, e_start + e_size, p_bitmap, actual, start) %start signal semaphore(fhce_semaphore) printstring("Extend failed"); newline %result = partition full error %finish %if start = e_start + e_size %start ! New extent is contiguous with the old one !! printstring("Contiguous, appending"); newline e_size = e_size + actual fhce_dirty = 1 extended = 1 -> try write again %finish i = h_extent limit - 1 %if i > 0 %start e == h_extent(i) %if e_start = 0 %start ! Found a slot -- block 0 is reserved e_start = start e_size = actual h_extent limit = i fhce_dirty = 1 extended = 1 -> try write again %finish %finish signal semaphore(fhce_semaphore) %result = file header full error %end %externalintegerfn fsys create file(%record(fsys access fm)%name access, %string(255) creation name, %integer pn, benefactor ID, flags, %integer initial allocation, %integername ID) %record(file header fm) bh %record(partition fm)%name p, bp %record(file header fm)%name h %record(header access fm)%name g, bg %record(open file table fm)%name o %integer slot, status, start, size, i, bpn !! printstring("FSys create file: partition "); write(pn, 0) !! printstring(", benefactor "); phex(benefactor ID); newline pn = pn & partition number mask %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = partition ID error %if p_flags & partition valid = 0 %result = not file structured error %if p_flags & partition structured = 0 ! Get the benefactor's file header here %if benefactor ID # 0 %start !! printstring("Create: benefactor is "); phex(benefactor ID); newline benefactor ID = benefactor ID & file ID mask bpn = benefactor ID >> partition shift %result = partition ID error %unless 0 < bpn <= partitions bp == tables_partition(bpn) %result = partition ID error %if bp_flags & partition valid = 0 %result = not file structured error %if bp_flags & partition structured = 0 ! Should use the cache here<<<<<< i = read index block(bpn, benefactor ID, bh) %result = i %if i < 0 %result = no such file error %if bh_ID # benefactor ID & file ID part %result = no authority error %c %unless file access authority OK(bh, access, modify access, flags) ! NB we check for modify access above so that users can't replace files ! which they otherwise wouldn't have write access to. There's a ! similar check in the file system process for "do rename". %finish ! Now try for some disc allocation initial allocation = default initial blocks %if initial allocation <= 0 %unless allocate extent(initial allocation, -1, p_bitmap, size, start) %start !! printstring("Allocate failed"); newline %result = partition full error %if size < 0 %finish semaphore wait(tables_open file table semaphore) slot = allocate file slot(p) %if slot < 0 %start signal semaphore(tables_open file table semaphore) %result = slot %finish i = get open file slot %result = i %if i < 0 o == tables_open file table(i) o_compatible = 0; o_ID = pn << partition shift ! slot status = get header(o) %result = status %if status < 0 h == o_fhce_h %if h_ID & index part # 0 %start printstring("Duplicate index allocation for file ") phex(h_ID + file sequence increment); newline forget header(o) signal semaphore(tables_open file table semaphore) %result = bugcheck %finish h_ID = (h_ID + file sequence increment ! slot) & file ID part o_ID = h_ID ! (pn << partition shift) tables_header ID(o_header cache slot) = o_ID h_header refcount = 0; ! Nobody (yet) h_flags = backup required %if benefactor ID = 0 %start ! No previous version, so use defaults %if access == nil %start ! System h_owner = system ID h_supervisor = system ID h_local access = new local access h_world access = new local access; !<<<<< %else ! A real user !! printstring("NO benefactor, using user defaults"); newline h_owner = access_user ID h_supervisor = access_supervisor ID h_local access = no access; !<<<<< h_world access = no access; !<<<<< %finish h_owner access = new owner access h_access(1) = 0; ! No groups %else ! There's a previous version, so we take our defaults from that. !! printstring("Defaulting from benefactor: ") !! write(bh_owner, 0); space; write(bh_supervisor, 0); newline h_owner = bh_owner; h_supervisor = bh_supervisor h_owner access = bh_owner access h_local access = bh_local access h_world access = bh_world access ! Now copy the groups (if any) g == h_access(1); bg == bh_access(1); i = 1 %while i < bh_extent limit %and bg_ID # 0 %cycle !! printstring("Group: "); write(bg_ID, 0); newline g = bg bg == bg [1]; g == g [1] i = i + 1 %repeat g = 0 %if i < extent limit; ! Terminate list %finish length(creation name) = 15 %if length(creation name) > 15 h_creation name = creation name h_created = get datestamp h_modified = h_created; h_accessed = h_created %if access == nil %then h_creator = system ID %c %else h_creator = access_user ID h_static ID = 0 h_audit ID = 0 h_blocks used = 0 h_bytes in last block = 0 h_extent limit = extent limit h_extent(extent limit)_start = start h_extent(extent limit)_size = size status = write index block(pn, slot, h) ID = h_ID ! pn << partition shift forget header(o) signal semaphore(tables_open file table semaphore) %result = status %end %externalintegerfn fsys delete file(%record(fsys access fm)%name access, %integer ID, flags) %record(open file table fm)%name o %record(file header fm)%name h %record(partition fm)%name p %record(extent fm)%name e %integer i, status, pn ID = ID & file ID mask pn = ID >> partition shift %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = partition ID error %if p_flags & partition valid = 0 %result = not file structured error %if p_flags & partition structured = 0 semaphore wait(tables_open file table semaphore) i = get open file slot %result = i %if i < 0 o == tables_open file table(i) o_mode = control access; o_compatible = 0; o_ID = ID status = get header(o) %result = status %if status < 0 h == o_fhce_h %if h_ID # ID & file ID part %start forget header(o) signal semaphore(tables_open file table semaphore) %result = no such file error %finish %unless file access authority OK(h, access, link access, flags) %start forget header(o) signal semaphore(tables_open file table semaphore) %result = no authority error %finish %if access ## nil %and h_header refcount > 0 %start forget header(o) signal semaphore(tables_open file table semaphore) %result = non zero refcount error %finish h_ID = h_ID & (\ index part) %if h_flags & overlapping file = 0 %start %for i = extent limit, -1, h_extent limit %cycle e == h_extent(i) %unless free extent(e_start, e_size, p_bitmap) %start printstring("Free extent failed"); newline %finish e = 0 %repeat %finish h_blocks used = 0; h_bytes in last block = 0 status = write index block(pn, ID, h) %if o_fhce_refcount # 1 %start printstring("Delete: bad file tables refcount ") write(o_fhce_refcount, 0); printstring(" for "); phex(ID) newline %finish forget header(o) free file slot(p, ID & index part) signal semaphore(tables_open file table semaphore) %result = status %end ! BEWARE deadlocks.... !%externalintegerfn fsys exchange(%record(fsys access fm)%name access, ! %integer ID1, ID2, flags) ! %record(partition fm)%name p ! %record(file header fm)%name h1, h2 ! %record(open file table fm)%name f1, f2 ! %record(extent fm)%name e1, e2 ! %integer t1, t2 ! %record(extent fm) e ! %integer i, j, pn ! %short q ! ID1 = ID1 & file ID mask; ID2 = ID2 & file ID mask ! pn = ID1 >> partition shift ! %result = bad operation error %unless pn = ID2 >> partition shift ! %result = partition ID error %unless 0 < pn <= partitions ! p == tables_partition(pn) ! %result = partition ID error %if p_flags & partition valid = 0 ! %result = not file structured error %if p_flags & partition structured = 0 ! semaphore wait(tables_open file table semaphore) ! %result = incompatible mode error %unless compatible mode(-1, ID1, 0) ! t1 = get open file slot; %result = t1 %if t1 < 0 ! f1 == tables_open file table(t1) ! f1_ID = ID1; f1_compatible = 0 ! f1_mode = 0 %and %result = incompatible mode error %c ! %unless compatible mode(-1, ID2, 0); !??? ! t2 = get open file slot ! f1_mode = 0 %and %result = t2 %if t2 < 0 ! f2 == tables_open file table(t2) ! f2_ID = ID1; f2_compatible = 0 ! i = get header(f1) ! f1_mode = 0 %and f2_mode = 0 %and %result = i %if i < 0 ! h1 == f1_fh_h ! f1_mode = 0 %and f2_mode = 0 %and %result = no such file error %c ! %if h1_ID # ID1 & file ID part ! semaphore wait(tables_open file table semaphore); ! Released by 'get header' ! i = get header(f2) ! %if i < 0 %start ! forget header(f1_fh); f1_fh == nil ! f1_mode = 0 ! f2_mode = 0 ! %result = i ! %finish ! h2 == f2_fh_h ! %if h2_ID # ID2 & file ID part %start ! forget header(f1_fh); f1_fh == nil ! forget header(f2_fh); f2_fh == nil ! f1_mode = 0 ! f2_mode = 0 ! %result = no such file error ! %finish ! i = h1_static ID; h1_static ID = h2_static ID; h2_static ID = i ! q = h1_bytes in last block ! h1_bytes in last block = h2_bytes in last block ! h2_bytes in last block = q ! i = h1_blocks used ! h1_blocks used = h2_blocks used ! h2_blocks used = i ! %for i = 1, 1, extent limit %cycle ! e1 == h1_extent(i); e2 == h2_extent(i) ! %exit %if e1_size = 0 = e2_size; ! All done ! e = e1; e1 = e2; e2 = e ! %repeat ! i = write index block(pn, ID1, h1) ! forget header(f1_fh); f1_fh == nil ! f1_mode = 0 ! j = write index block(pn, ID2, h2) ! forget header(f2_fh); f2_fh == nil ! f2_mode = 0 ! %result = i %if i < 0 ! %result = j !%end %externalintegerfn fsys bump refcount(%record(fsys access fm)%name access, %integer ID, flags, increment) ! Deletion is implied if the refcount -> 0 %record(open file table fm)%name o %record(file header fm)%name h %record(partition fm)%name p %record(extent fm)%name e %integer i, status, pn, final refcount %result = bad refcount increment error %unless -1 <= increment <= 1 ID = ID & file ID mask pn = ID >> partition shift %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = 0 %if p_flags & partition structured = 0; ! Ignore it semaphore wait(tables_open file table semaphore) i = get open file slot %result = i %if i < 0 o == tables_open file table(i) o_mode = control access; o_compatible = control access; o_ID = ID status = get header(o) %result = status %if status < 0 h == o_fhce_h %if h_ID # ID & file ID part %start forget header(o) signal semaphore(tables_open file table semaphore) %result = no such file error %finish %unless file access authority OK(h, access, link access, flags) %start forget header(o) signal semaphore(tables_open file table semaphore) %result = no authority error %finish ! Now modify the file's refcount h_header refcount = h_header refcount + increment final refcount = h_header refcount !? h_modified = get datestamp !! phex(ID); printstring(" refcount -> ") !! write(h_header refcount, 0); newline status = write index block(pn, ID, h) o_fhce_dirty = 0; !??? forget header(o) signal semaphore(tables_open file table semaphore) %result = status %if status < 0 %result = fsys delete file(access, ID, flags) %if final refcount <= 0 %result = 0 %end %externalintegerfn fsys get full ID(%integer partial ID, %integername full ID) %record(file header fm) h %integer status partial ID = partial ID & file ID mask status = read index block(partial ID >> partition shift, partial ID, h) %result = status %if status # 0 %result = no such file error %if h_ID & index part = 0 full ID = h_ID ! (partial ID & partition part) %result = 0 %end %externalintegerfn fsys read file header(%record(fsys access fm)%name access, %integer ID, flags, %record(*)%name fh) %record(file header fm) h %record(partition fm)%name p %integer i, pn ID = ID & file ID mask ! Find the partition record pn = ID >> partition shift %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = partition ID error %if p_flags & partition valid = 0 %result = not file structured error %if p_flags & partition structured = 0 ! Should really go via the cache here <<<<<<<<< i = read index block(pn, ID, h) %result = i %if i < 0 %result = no such file error %if h_ID # ID & file ID part %result = no authority error %c %unless file access authority OK(h, access, read access, flags) fh = h %result = 0 %end %externalintegerfn fsys obtain attributes(%record(fsys access fm)%name access, %integer ID, flags, %record(attributes list fm)%name a) %record(open file table fm)%name o %record(file header fm)%name h %record(partition fm)%name p %integer i, status, pn, privileged, group pos = 1 %switch r(first attribute - 1 : last attribute) ID = ID & file ID mask ! Find the partition record pn = ID >> partition shift %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = partition ID error %if p_flags & partition valid = 0 %result = not file structured error %if p_flags & partition structured = 0 semaphore wait(tables_open file table semaphore) i = get open file slot %result = i %if i < 0 o == tables_open file table(i) ! Note that we specify control access mode here even though we later ! check for read access to the file. This allows the protection ! mechanism to work sensibly while at the same time not unnecessarily ! blocking shared access. We specify that any other concurrent users ! can have anything but control access in order to maintain a consistent ! view of the file's attributes. o_mode = control access; o_compatible = \control access; o_ID = ID status = get header(o) %result = status %if status < 0 h == o_fhce_h %if h_ID # ID & file ID part %start forget header(o) signal semaphore(tables_open file table semaphore) %result = no such file error %finish %unless file access authority OK(h, access, read access, flags) %start forget header(o) signal semaphore(tables_open file table semaphore) %result = no authority error %finish %if access == nil %then privileged = -1 %c %else privileged = access_privileges & admin privilege ! Now scan the list, filling in the various attribute requests %while a ## nil %cycle -> r(a_code) %if first attribute <= a_code <= last attribute r(*): a_status = attribute unavailable -> next r(file ID attribute): a_numeric = h_ID -> OK next r(file owner attribute): a_numeric = h_owner -> OK next r(file supervisor attribute): a_numeric = h_supervisor -> OK next r(owner access attribute): a_numeric = h_owner access -> OK next r(local access attribute): a_numeric = h_local access -> OK next r(world access attribute): a_numeric = h_world access -> OK next r(group access attribute): group pos = group pos + 1 %if group pos >= h_extent limit %or h_access(group pos)_ID = 0 %start group pos = infinity; ! Abort any other pending requests a_status = attribute unavailable -> next %finish a_numeric2 = h_access(group pos)_ID a_numeric = h_access(group pos)_access -> OK next r(defined groups attribute): a_status = attribute unavailable -> next r(file creator attribute): a_numeric = h_creator -> OK next r(static ID attribute): a_numeric = h_static ID -> OK next r(audit ID attribute): a_numeric = h_audit ID -> OK next r(date created attribute): a_numeric = h_created -> OK next r(date modified attribute): a_numeric = h_modified -> OK next r(date accessed attribute): a_numeric = h_accessed -> OK next r(creation name attribute): a_textual = h_creation name -> OK next r(file size attribute): a_numeric = 512 * h_blocks used + h_bytes in last block - 512 -> OK next r(file extents attribute): a_numeric = extent limit - h_extent limit + 1 -> OK next r(file flags attribute): a_numeric = h_flags -> OK next OK next: a_status = attribute OK next: a == a_next %repeat forget header(o) signal semaphore(tables_open file table semaphore) %result = 0 %end %externalintegerfn fsys modify attributes(%record(fsys access fm)%name access, %integer ID, flags, %record(attributes list fm)%name a) %record(open file table fm)%name o %record(file header fm)%name h %record(partition fm)%name p %integer i, status, pn, privileged, group pos = 1 %switch r(first attribute - 1 : last attribute) ID = ID & file ID mask pn = ID >> partition shift %result = partition ID error %unless 0 < pn <= partitions p == tables_partition(pn) %result = partition ID error %if p_flags & partition valid = 0 %result = not file structured error %if p_flags & partition structured = 0 semaphore wait(tables_open file table semaphore) i = get open file slot %result = i %if i < 0 o == tables_open file table(i) o_mode = control access; o_compatible = control access; o_ID = ID status = get header(o) %result = status %if status < 0 h == o_fhce_h %if h_ID # ID & file ID part %start forget header(o) signal semaphore(tables_open file table semaphore) %result = no such file error %finish %unless file access authority OK(h, access, control access, flags) %start forget header(o) signal semaphore(tables_open file table semaphore) %result = no authority error %finish %if access == nil %then privileged = -1 %c %else privileged = access_privileges & admin privilege ! Now scan the attributes list, performing modifications as requested %while a ## nil %cycle -> r(a_code) %if first attribute <= a_code <= last attribute r(file owner attribute): -> unavailable %if privileged = 0 !! printstring("Set owner: "); write(a_numeric, 0); newline h_owner = a_numeric -> OK next r(file supervisor attribute): -> unavailable %if privileged = 0 !! printstring("Set supervisor: "); write(a_numeric, 0); newline h_supervisor = a_numeric -> OK next r(owner access attribute): a_numeric = (a_numeric & (\ admin required)) ! %c (h_owner access & admin required) %c %if access_privileges & admin privilege = 0 !! printstring("Set owner access: "); phex(a_numeric); newline h_owner access = a_numeric -> OK next r(local access attribute): a_numeric = (a_numeric & (\ admin required)) ! %c (h_local access & admin required) %c %if access_privileges & admin privilege = 0 !! printstring("Set local access: "); phex(a_numeric); newline h_local access = a_numeric -> OK next r(world access attribute): a_numeric = (a_numeric & (\ admin required)) ! %c (h_world access & admin required) %c %if access_privileges & admin privilege = 0 !! printstring("Set world access: "); phex(a_numeric); newline h_world access = a_numeric -> OK next r(group access attribute): !! printstring("Set group access: userID "); write(a_numeric2, 0) !! printstring(", access "); write(a_numeric, 0) !! printstring(", slot "); write(group pos, 0); newline %if group pos >= h_extent limit %start a_status = attribute list overflow -> next %finish h_access(group pos)_ID = a_numeric2 h_access(group pos)_access = a_numeric group pos = group pos + 1 h_access(group pos)_ID = 0 %if group pos < h_extent limit -> OK next r(file flags attribute): !! printstring("Set flags: "); phex(a_numeric); newline h_flags = a_numeric -> OK next ! The following are either unimplemented as yet, or cannot meaningfully ! be modified by the user. !r(file ID attribute): !r(defined groups attribute): !r(file creator attribute): !r(static ID attribute): !r(audit ID attribute): !r(date created attribute): !r(date modified attribute): !r(date accessed attribute): !r(creation name attribute): !r(file size attribute): !r(file extents attribute): unavailable: r(*): a_status = attribute unavailable -> next OK next: a_status = attribute OK next: a == a_next %repeat ! All done, mark the header as modified and write it out. h_modified = get datestamp ! Should we write it out here?? status = write index block(pn, ID, h) o_fhce_dirty = 0 forget header(o) signal semaphore(tables_open file table semaphore) %result = status %end ! Partition module included here to force proper layering %include "GDMR_H:Part" %end %of %file