123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
- MODULE ISO9660Files; (** AUTHOR "?/be"; PURPOSE "ISO 9660 File System (ported from Native Oberon)"; *)
- IMPORT SYSTEM, Modules, Files, KernelLog, Strings;
- CONST
- debug = FALSE; nameDebug = FALSE;
- SS = 2048;
- MaxBufs = 4;
- Directory = 1;
- eFileDoesNotExist = 8903;
- eCannotOpenSubDir = 8916;
- eInvalidFirstCluster = 8917;
- eNameIsWild = 8927;
- eInvalidFileName = 8941;
- eInvalidVolume = 9000;
- TYPE
- Filename = ARRAY 256 OF CHAR;
- VolDesc = POINTER TO RECORD
- root, rootDirSize: LONGINT (* sector number of root directory and root directory size *)
- END;
- Buffer = POINTER TO RECORD (Files.Hint)
- pos, lim: LONGINT;
- next: Buffer;
- data: POINTER TO ARRAY OF CHAR
- END;
- FileSystem = OBJECT(Files.FileSystem)
- VAR
- pri, sup, cur: VolDesc;
- jolietLevel: LONGINT;
- (** Open an existing file. The same file descriptor is returned if a file is opened multiple times. End users use Files.Old instead. *)
- PROCEDURE Old0*(name: ARRAY OF CHAR): Files.File;
- VAR f: File; namebuf: Filename; dircl, dirpos, time, date, filecl, len: LONGINT; attr: SET; res: INTEGER;
- BEGIN {EXCLUSIVE}
- res := 0; f := NIL;
- Check(name, namebuf, res);
- IF res = 0 THEN
- LocateFile(namebuf, SELF, dircl, dirpos, time, date, filecl, len, attr, res);
- IF debug THEN LogString("Old0; filecl: "); LogInt(filecl); LogLn END;
- IF res = 0 THEN
- IF Directory IN attr THEN res := eCannotOpenSubDir
- ELSIF filecl < 16 THEN res := eInvalidFirstCluster
- ELSE f := OpenFile(namebuf, SELF, dircl, dirpos, time, date, filecl, len, attr)
- END
- END
- END;
- RETURN f
- END Old0;
- (** Enumerate canonical file names. mask may contain * wildcards. For internal use only. End users use Enumerator instead. *)
- PROCEDURE Enumerate0*(mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator);
- VAR
- fname, name, mmask, pname, fullname: Filename;
- f: Files.File; R: Files.Rider;
- attr: SET; bAddToEnum: BOOLEAN;
- pos, time, date, len, cl: LONGINT; res: INTEGER;
- BEGIN {EXCLUSIVE}
- Check(mask, name, res);
- IF (res = 0) OR (res = eNameIsWild) THEN
- SeparateName(name, name, mmask); IF (mmask = "") THEN COPY("*", mmask) END;
- Files.JoinName(prefix, name, pname);
- IF nameDebug THEN LogString("Enumerate; dir name: "); LogString(pname); LogLn END;
- f := OldDir(SELF, name);
- IF f # NIL THEN
- f.Set(R, 0); pos := 0;
- LOOP
- MatchFile(R, mmask, fname, pos, cl, time, date, len, attr, res);
- IF res # 0 THEN EXIT END;
- COPY(pname, fullname);
- IF name[0] # 0X THEN Files.AppendStr("/", fullname) END;
- Files.AppendStr(fname, fullname);
- bAddToEnum := TRUE;
- IF Directory IN attr THEN
- IF (fname = ".") OR (fname = "..") THEN
- bAddToEnum := FALSE;
- END;
- flags := { Files.Directory };
- len := 0;
- ELSE
- flags := {};
- END;
- IF bAddToEnum THEN
- enum.PutEntry(fullname, flags, time, date, len)
- END;
- END;
- ELSE res := eFileDoesNotExist
- END
- END
- END Enumerate0;
- (** Return the unique non-zero key of the named file, if it exists. *)
- PROCEDURE FileKey*(name: ARRAY OF CHAR): LONGINT;
- VAR res: INTEGER; namebuf: Filename; t, key, filecl: LONGINT; attr: SET;
- BEGIN {EXCLUSIVE}
- IF nameDebug THEN LogString("OFSFATFiles.FileKey: "); LogString(name); LogLn END;
- key := 0;
- Check(name, namebuf, res);
- IF res = 0 THEN
- LocateFile(namebuf, SELF, t, t, t, t, filecl, t, attr, res);
- IF res = 0 THEN key := filecl END
- END;
- RETURN key
- END FileKey;
- (** Finalize the file system. *)
- PROCEDURE Finalize*;
- BEGIN {EXCLUSIVE}
- vol.Finalize;
- Finalize^
- END Finalize;
- END FileSystem;
- File = OBJECT (Files.File)
- VAR
- len,
- time, date,
- filecl: LONGINT; (* first cluster *)
- attr: SET ; (* ISO file attributes *)
- (* directory info *)
- name: Filename;
- dircl, (* start cluster of dir. that contains entry for file *)
- dirpos: LONGINT; (* position in cluster of dir. in which entry lies *)
- nofbufs: INTEGER;
- firstbuf: Buffer;
- PROCEDURE Set*(VAR r: Files.Rider; pos: LONGINT);
- BEGIN {EXCLUSIVE}
- r.eof := FALSE; r.res := 0; r.file := SELF; r.fs := fs;
- IF (pos < 0) THEN r.apos := 0
- ELSIF (pos < len) THEN r.apos := pos
- ELSE r.apos := len
- END;
- r.hint := firstbuf
- END Set;
- PROCEDURE Pos*(VAR r: Files.Rider): LONGINT;
- BEGIN {EXCLUSIVE}
- RETURN r.apos
- END Pos;
- PROCEDURE FindBuf(pos: LONGINT; hint: Buffer): Buffer;
- VAR buf: Buffer;
- BEGIN
- buf := hint;
- LOOP
- IF (pos >= buf.pos) & (pos < buf.pos+buf.lim) THEN EXIT END;
- buf := buf.next;
- IF buf = hint THEN buf := NIL; EXIT END
- END;
- RETURN buf
- END FindBuf;
- PROCEDURE ReadBuf(buf: Buffer; pos: LONGINT);
- BEGIN
- ASSERT(pos <= len, 100);
- buf.pos := pos - pos MOD fs.vol.blockSize;
- pos := pos DIV fs.vol.blockSize;
- IF pos = len DIV fs.vol.blockSize THEN buf.lim := len MOD fs.vol.blockSize
- ELSE buf.lim := fs.vol.blockSize
- END;
- IF debug THEN LogString("ReadBuf; block: "); LogInt(filecl+pos); LogLn END;
- fs.vol.GetBlock(filecl+pos, buf.data^)
- END ReadBuf;
- PROCEDURE GetBuf(pos: LONGINT; hint: Buffer): Buffer;
- VAR buf: Buffer;
- BEGIN
- buf := FindBuf(pos, hint);
- IF buf = NIL THEN
- IF nofbufs < MaxBufs THEN (*allocate new buffer*)
- NEW(buf); NEW(buf.data, fs.vol.blockSize);
- buf.next := firstbuf.next; firstbuf.next := buf;
- INC(nofbufs)
- ELSE (*reuse one of the buffers; round robin *)
- buf := firstbuf; firstbuf := buf.next;
- END;
- ReadBuf(buf, pos);
- END;
- RETURN buf
- END GetBuf;
- PROCEDURE Read*(VAR r: Files.Rider; VAR x: CHAR);
- VAR buf: Buffer;
- BEGIN {EXCLUSIVE}
- r.res := 0;
- IF (r.apos < len) THEN
- buf := GetBuf(r.apos, r.hint(Buffer));
- x := buf.data[r.apos-buf.pos];
- INC(r.apos)
- ELSE
- x := 0X; r.eof := TRUE
- END
- END Read;
- PROCEDURE ReadBytes*(VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGINT);
- VAR m, pos: LONGINT; src: ADDRESS; buf: Buffer;
- BEGIN {EXCLUSIVE}
- IF LEN(x) < len THEN SYSTEM.HALT(19) END;
- IF len <= 0 THEN RETURN END;
- buf := r.hint(Buffer);
- m := SELF.len - r.apos;
- IF len <= m THEN r.res := 0 ELSE r.eof := TRUE; r.res := len-m; len := m END;
- WHILE len > 0 DO
- buf := GetBuf(r.apos, buf);
- pos := r.apos - buf.pos;
- src := ADDRESSOF(buf.data[pos]); m := buf.lim-pos;
- IF m > len THEN m := len END;
- SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), m);
- DEC(len, m); INC(ofs, m); INC(r.apos, m);
- END;
- r.hint := buf
- END ReadBytes;
- PROCEDURE Length*(): LONGINT;
- BEGIN RETURN len
- END Length;
- PROCEDURE GetDate*(VAR t, d: LONGINT);
- BEGIN t := time; d := date
- END GetDate;
- PROCEDURE GetName*(VAR name: ARRAY OF CHAR);
- BEGIN COPY(SELF.name, name)
- END GetName;
- PROCEDURE Update*;
- END Update; (* nothing *)
- END File;
- VAR (* svr *)
- ExtractNameProc: PROCEDURE(VAR dir, name: ARRAY OF CHAR);
- (* debug procedures *)
- PROCEDURE LogString(s: ARRAY OF CHAR);
- BEGIN
- KernelLog.String(s)
- END LogString;
- PROCEDURE LogInt(i: LONGINT);
- BEGIN
- KernelLog.Int(i, 0)
- END LogInt;
- PROCEDURE LogLn;
- BEGIN
- KernelLog.Ln
- END LogLn;
- (* help procedures *)
- (* Get733 - 32 bit, both byte orders *)
- PROCEDURE Get733(VAR s: ARRAY OF CHAR; first: LONGINT; VAR d: LONGINT);
- BEGIN
- d := LONG(ORD(s[first]));
- d := d + LONG(ORD(s[first+1]))*100H;
- d := d + LONG(ORD(s[first+2]))*10000H;
- d := d + LONG(ORD(s[first+3]))*1000000H
- END Get733;
- (* Check - check filename. Return correct name, or empty name if incorrect. *)
- PROCEDURE Check(s: ARRAY OF CHAR; VAR name: Filename; VAR res: INTEGER);
- VAR i, j: LONGINT; ch: CHAR;
- BEGIN
- IF nameDebug THEN LogString("Check: "); LogString(s) END;
- res := 0; i := 0;
- IF (s[0] = "/") OR (s[0] = "\") THEN j := 1 ELSE j := 0 END; (* remove leading / or \ *)
- LOOP
- ch := s[j];
- IF ch = 0X THEN EXIT END;
- IF ch = "\" THEN ch := "/" END;
- IF (ch < " ") OR (ch >= 07FX) THEN res := eInvalidFileName; i := 0; EXIT END;
- IF (ch = "?") OR (ch = "*") THEN res := eNameIsWild END;
- name[i] := ch;
- INC(i); INC(j)
- END;
- name[i] := 0X;
- IF nameDebug THEN LogString(" => "); LogString(name); LogLn END;
- END Check;
- PROCEDURE GetVolumeDescriptors(fs: FileSystem; res: INTEGER);
- VAR b: ARRAY SS OF CHAR; i: LONGINT; vol: Files.Volume;
- BEGIN
- vol := fs.vol;
- i := 16; fs.pri := NIL; fs.sup := NIL; fs.cur := NIL; fs.jolietLevel := 0;
- REPEAT
- vol.GetBlock(i, b); (* read boot sector *)
- CASE b[0] OF
- 1X: (* Primary volume descriptor *)
- ASSERT(fs.pri = NIL, 102); (* exactly one primary volume desc *)
- NEW(fs.pri);
- Get733(b, 158, fs.pri.root); (* location of root directory *)
- Get733(b, 166, fs.pri.rootDirSize) (* size of root directory in bytes *)
- | 2X: (* Supplementary volume descriptor *)
- ASSERT(fs.sup = NIL, 103); (* 0 or 1 supplementary volume descriptor *)
- ASSERT((b[88] = 25X) & (b[89] = 2FX) & ((b[90] = 40X) OR (b[90] = 43X) OR (b[90] = 45X)), 104);
- IF b[90] = 40X THEN fs.jolietLevel := 1
- ELSIF b[90] = 43X THEN fs.jolietLevel := 2
- ELSIF b[90] = 45X THEN fs.jolietLevel := 3
- END;
- NEW(fs.sup);
- Get733(b, 158, fs.sup.root); (* location of root directory *)
- Get733(b, 166, fs.sup.rootDirSize) (* size of root directory in bytes *)
- ELSE ASSERT((b[0] = 0X) OR (b[0] = 2X) OR (b[0] = 0FFX), 100) (* boot or end *)
- END;
- INC(i)
- UNTIL (res # 0) OR (b[0] = 0FFX);
- IF res = 0 THEN
- IF fs.pri = NIL THEN res := eInvalidVolume
- ELSIF fs.sup # NIL THEN fs.cur := fs.sup; ExtractNameProc := ExtractLongName
- ELSE fs.cur := fs.pri; ExtractNameProc := ExtractShortName
- END
- END;
- END GetVolumeDescriptors;
- (* GetDir - Get information from a directory entry *)
- PROCEDURE GetDir(VAR dir, fname: ARRAY OF CHAR; VAR time, date, cl, len: LONGINT; VAR attr: SET);
- VAR t: LONGINT;
- BEGIN
- ExtractName(dir, fname);
- t (*attr*) := ORD(dir[24]); attr := SYSTEM.VAL(SET, t);
- time := LONG(ORD(dir[20]))*64*64 + LONG(ORD(dir[21]))*64 + LONG(ORD(dir[22]));
- date := LONG(ORD(dir[17]))*32*16 + LONG(ORD(dir[18]))*16 + LONG(ORD(dir[19]));
- Get733(dir, 1, cl);
- Get733(dir, 9, len)
- END GetDir;
- PROCEDURE SplitName(str: ARRAY OF CHAR; VAR prefix, name: ARRAY OF CHAR);
- VAR i, j: LONGINT;
- BEGIN
- IF nameDebug THEN LogString("SplitName: "); LogString(str) END;
- i := -1; j := -1;
- REPEAT INC(i); INC(j); prefix[j] := str[i] UNTIL (str[i] = 0X) OR (str[i] = "/");
- IF str[i] = "/" THEN
- prefix[j] := 0X; j := -1;
- REPEAT INC(i); INC(j); name[j] := str[i] UNTIL name[j] = 0X
- ELSE name[0] := 0X
- END;
- IF nameDebug THEN LogString(" => "); LogString(prefix); LogString(", "); LogString(name); LogLn END
- END SplitName;
- (* SeparateName - separate str into a prefix and a name. *)
- PROCEDURE SeparateName(str: ARRAY OF CHAR; VAR prefix: ARRAY OF CHAR; VAR name: Filename);
- VAR i, j : LONGINT;
- BEGIN
- (* Pre: str is result of a Check operation; all "\"s have been changed to "/" *)
- i := 0; j := -1;
- WHILE str[i] # 0X DO
- IF str[i] = "/" THEN j := i END;
- INC(i)
- END;
- IF j >= 0 THEN
- COPY(str, prefix); prefix[j] := 0X;
- i := -1;
- REPEAT INC(i); INC(j); name[i] := str[j] UNTIL name[i] = 0X
- ELSE COPY(str, name); prefix[0] := 0X
- END
- END SeparateName;
- PROCEDURE ExtractShortName(VAR dir, name: ARRAY OF CHAR);
- VAR i, j, len: LONGINT;
- BEGIN
- len := ORD(dir[31]);
- i := 0; j := 32;
- WHILE (i < len) & (dir[j] # ";") DO name[i] := dir[j]; INC(i); INC(j) END;
- name[i] := 0X;
- END ExtractShortName;
- PROCEDURE ExtractLongName(VAR dir, name: ARRAY OF CHAR);
- VAR i, j, end: LONGINT;
- BEGIN
- end := 33+ORD(dir[31]);
- i := 0; j := 33;
- WHILE (j < end) & (dir[j] # ";") DO name[i] := dir[j]; INC(i); INC(j, 2) END;
- name[i] := 0X;
- END ExtractLongName;
- PROCEDURE ExtractName(VAR dir, name: ARRAY OF CHAR);
- VAR len: LONGINT;
- BEGIN
- len := ORD(dir[31]);
- IF len = 1 THEN
- IF dir[33] = 0X THEN COPY(".", name)
- ELSIF dir[33] = 1X THEN COPY("..", name)
- END
- ELSE ExtractNameProc(dir, name)
- END
- END ExtractName;
- PROCEDURE MatchFile(VAR R: Files.Rider; name: ARRAY OF CHAR; VAR fname: ARRAY OF CHAR;
- VAR pos, cl, time, date, len: LONGINT; VAR attr: SET; VAR res: INTEGER);
- VAR
- found: BOOLEAN; f: File; fs: FileSystem; buf: ARRAY 256 OF CHAR; entryLen, padding: LONGINT;
- BEGIN
- f := R.file(File); fs := R.fs(FileSystem);
- found := FALSE;
- LOOP
- pos := R.file.Pos(R);
- IF debug THEN LogString("MatchFile; pos: "); LogInt(pos); LogLn END;
- R.file.Read(R, buf[0]); entryLen := ORD(buf[0]);
- IF debug THEN LogString("MatchFile; entryLen: "); LogInt(entryLen); LogLn END;
- IF R.eof THEN
- IF debug THEN LogString("MatchFile; eof"); LogLn END;
- EXIT
- END;
- IF entryLen > 0 THEN
- R.file.ReadBytes(R, buf, 0, entryLen-1);
- GetDir(buf, fname, time, date, cl, len, attr);
- found := Strings.Match(name, fname);
- ELSE (* Directory entries may not cross sectors. Skip padding bytes *)
- padding := SS - (R.file.Pos(R) MOD SS);
- IF (padding > 0) & (padding <= 256) THEN
- IF debug THEN LogString("MatchFile; skip padding: "); LogInt(padding); LogLn; END;
- R.file.ReadBytes(R, buf, 0, padding); (* Skip padding *)
- ELSE
- IF debug THEN LogString("MatchFile; Confusing directory structure... givin' up"); END;
- EXIT;
- END;
- END;
- IF found THEN EXIT END;
- END;
- IF found THEN res := 0 ELSE res := eInvalidFileName END;
- END MatchFile;
- PROCEDURE FindFile(fs: FileSystem; name: ARRAY OF CHAR; dircl, dirlen: LONGINT;
- VAR dirpos, time, date, filecl, len: LONGINT; VAR attr: SET; VAR res: INTEGER);
- VAR f: File; R: Files.Rider; fname: Filename;
- BEGIN
- ASSERT(name # "", 100);
- f := OpenFile("", fs, -1, -1, -1, -1, dircl, dirlen, {});
- f.Set(R, 0);
- MatchFile(R, name, fname, dirpos, filecl, time, date, len, attr, res);
- END FindFile;
- PROCEDURE LocateFile(name: ARRAY OF CHAR; fs: FileSystem;
- VAR dircl, dirpos, time, date, filecl, len: LONGINT; VAR attr: SET; VAR res: INTEGER);
- VAR cur: Filename; dirlen: LONGINT;
- BEGIN
- res := 0;
- dircl := fs.cur.root; dirlen := fs.cur.rootDirSize; (* start in root directory *)
- IF name[0] = 0X THEN (* root dir *)
- filecl := dircl; attr := {Directory};
- len := dirlen; dirpos := -1;
- ELSE
- LOOP
- SplitName(name, cur, name);
- FindFile(fs, cur, dircl, dirlen, dirpos, time, date, filecl, len, attr, res);
- IF (res = 0) & (name # "") & ~(Directory IN attr) THEN res := eInvalidFileName END;
- IF (res # 0) OR (name = "") THEN EXIT END;
- dircl := filecl; dirlen := len
- END
- END
- END LocateFile;
- PROCEDURE OpenFile(name: ARRAY OF CHAR; fs: FileSystem; dircl, dirpos, time, date, filecl, len: LONGINT; attr: SET): File;
- VAR f: File; buf: Buffer;
- BEGIN
- NEW(f); COPY(name, f.name); f.fs := fs; f.key := filecl;
- f.dircl := dircl; f.dirpos := dirpos; f.time := time; f.date := date;
- f.filecl := filecl; f.len := len; f.attr := attr;
- NEW(buf); buf.next := buf;
- NEW(buf.data, fs.vol.blockSize);
- IF f.len = 0 THEN buf.pos := 0; buf.lim := 0
- ELSE f.ReadBuf(buf, 0) (* file is not empty *)
- END;
- f.firstbuf := buf; f.nofbufs := 1;
- RETURN f
- END OpenFile;
- PROCEDURE OldDir(fs: Files.FileSystem; name: ARRAY OF CHAR): Files.File;
- VAR f: File; dircl, dirpos, time, date, filecl, len: LONGINT; attr: SET; res: INTEGER;
- BEGIN
- res := 0; f := NIL;
- LocateFile(name, fs(FileSystem), dircl, dirpos, time, date, filecl, len, attr, res);
- IF res = 0 THEN
- IF ~(Directory IN attr) THEN res := eCannotOpenSubDir
- ELSIF filecl < 16 THEN res := eInvalidFirstCluster
- ELSE f := OpenFile(name, fs(FileSystem), dircl, dirpos, time, date, filecl, len, attr)
- END
- END;
- RETURN f
- END OldDir;
- (** Generate a new file system object. OFS.NewVol has volume parameter, OFS.Par has mount prefix. *)
- PROCEDURE NewFS*(context : Files.Parameters);
- VAR fs: FileSystem; res: INTEGER;
- BEGIN
- IF Files.This(context.prefix) = NIL THEN
- NEW(fs); fs.vol := context.vol;
- GetVolumeDescriptors(fs, res);
- IF res = 0 THEN
- IF debug THEN
- LogString(" primary root: "); LogInt(fs.pri.root); LogLn;
- LogString(" primary root dir size: "); LogInt(fs.pri.rootDirSize); LogLn
- END;
- fs.desc[0] := 0X; Files.AppendStr(context.vol.name, fs.desc); Files.AppendStr(" / ISO9660FS", fs.desc);
- Files.Add(fs, context.prefix)
- ELSE context.error.String("No ISO9660 file system found on "); context.error.String(context.vol.name); context.error.Ln;
- END
- ELSE context.error.String(context.prefix); context.error.String(" already in use"); context.error.Ln;
- END;
- END NewFS;
- (* Clean up when module freed. *)
- PROCEDURE Cleanup;
- VAR ft: Files.FileSystemTable; i: LONGINT;
- BEGIN
- IF Modules.shutdown = Modules.None THEN
- Files.GetList(ft);
- IF ft # NIL THEN
- FOR i := 0 TO LEN(ft^)-1 DO
- IF ft[i] IS FileSystem THEN Files.Remove(ft[i]) END
- END
- END
- END
- END Cleanup;
- BEGIN
- Modules.InstallTermHandler(Cleanup)
- END ISO9660Files.
- ISO9660Volumes.Mod
- Partitions.Show
- OFSTools.Mount CD IsoFS IDE2 ~
- OFSTools.Unmount CD0 ~
- SystemTools.Free ISO9660Files ISO9660Volumes ~
- System.Directory CD:*.* ~
- History:
- 30.05.2006 Fixed MatchFile: Directory entries may not cross sector boundaries. Skip padding bytes if necessary. (staubesv)
|