$ mvn -C clean install
<dependency> <groupId>com.io7m.jvvfs</groupId> <artifactId>io7m-jvvfs-core</artifactId> <version>4.0.1</version> </dependency>
Copyright © 2014 <code@io7m.com> http://io7m.com Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
final PathReal path = new PathReal("/path/to/application/resources"); final FilesystemType fs = Filesystem.makeWithArchiveDirectory(log, path);
final PathVirtual mount = PathVirtual.ofString("/"); fs.mountArchive("archive.zip", mount);
final PathVirtual path = PathVirtual.ofString("/a/b/c/file.txt"); final InputStream stream = fs.openFile(path);
// If "archive1.zip" contains "/a/b/file.txt", then "archive2.zip" could, for // example, be mounted at "/a" or "/a/b". fs.mount("archive1.zip", PathVirtual.ROOT); assert fs.isDirectory(PathVirtual.ofString("/a")); assert fs.isDirectory(PathVirtual.ofString("/a/b")); fs.mount("archive2.zip", PathVirtual.ofString("/a")); // Explicitly creating "/c" and mounting "archive2.zip" there is also a possibility. fs.createDirectory(PathVirtual.ofString("/c")); assert fs.isDirectory(PathVirtual.ofString("/c")); fs.mount("archive2.zip", PathVirtual.ofString("/c")); // Assuming "/xyz/file.txt" exists in "archive2.zip", both of the following will work: final InputStream s0 = fs.openFile("/a/xyz/file.txt"); final InputStream s1 = fs.openFile("/c/xyz/file.txt");
com.io7m.jvvfs.logs.shell = true com.io7m.jvvfs.level = LOG_ERROR
jvvfs> archives close file-list file-list-long file-read file-size file-time help list-mounts mkdir mount unmount jvvfs> archives complex.zip encrypted.zip files1-3.zip files4-6.zip io7m-jvvfs.properties single-file-and-subdir-implicit.zip single-file-and-subdir.zip single-file-in-subdir-subdir.zip single-file.jar single-file.zip subdir-shadow.zip subdir-subdir-shadow.zip unknown.unknown jvvfs> mkdir /z jvvfs> file-list-long / z/ 2013-06-29 14:19:51 +0000 jvvfs> file-list-long /z jvvfs> mount files1-3.zip /z jvvfs> file-list-long /z file1.txt 2013-05-08 11:35:16 +0000 7 file2.txt 2013-05-08 11:35:20 +0000 7 file3.txt 2013-05-08 11:35:24 +0000 7 jvvfs> file-read /z/file1.txt File 1 jvvfs> list-mounts /z /tmp/jvvfs/files1-3.zip jvvfs> mount complex.zip / jvvfs> list-mounts / /tmp/jvvfs/complex.zip /z /tmp/jvvfs/files1-3.zip jvvfs> file-list-long / a/ 2012-03-15 21:13:12 +0000 b/ 2012-03-15 21:13:12 +0000 z/ 2013-06-28 22:13:47 +0000 jvvfs> file-list-long /a a/ 2012-03-15 21:13:12 +0000 a1.txt 2012-03-15 21:13:12 +0000 9 a2.txt 2012-03-15 21:13:12 +0000 9 a3.txt 2012-03-15 21:13:12 +0000 9 b/ 2012-03-15 21:13:12 +0000 c/ 2012-03-15 21:13:12 +0000 jvvfs> mount files4-6.zip /a jvvfs> file-list-long /a a/ 2012-03-15 21:13:12 +0000 a1.txt 2012-03-15 21:13:12 +0000 9 a2.txt 2012-03-15 21:13:12 +0000 9 a3.txt 2012-03-15 21:13:12 +0000 9 b/ 2012-03-15 21:13:12 +0000 c/ 2012-03-15 21:13:12 +0000 file4.txt 2013-05-08 11:36:02 +0000 7 file5.txt 2013-05-08 11:36:06 +0000 7 file6.txt 2013-05-08 11:36:10 +0000 7 jvvfs> list-mounts /a /tmp/jvvfs/files4-6.zip / /tmp/jvvfs/complex.zip /z /tmp/jvvfs/files1-3.zip jvvfs> close jvvfs> list-mounts jvvfs> file-list-long / jvvfs> mount encrypted.zip / shell : error : filesystem error: archive 'encrypted.zip' appears to be corrupt - invalid CEN header (encrypted entry)
Inductive io (F S : Type) : Type := | Success : S -> io F S | Failure : F -> io F S.
Axiom name : Set. Axiom name_valid : name -> Prop.
Definition path_virtual := list {n : name | name_valid n}. Definition root : path_virtual := nil.
Require Coq.Lists.List. Definition is_prefix (p0 p1 : path_virtual) := List.firstn (length p0) p1 = p0. Definition is_ancestor_of (p0 p1 : path_virtual) := p0 <> p1 /\ is_prefix p0 p1.
Require Coq.Lists.List. Definition is_parent_of (p0 p1 : path_virtual) := is_ancestor_of p0 p1 /\ exists n, app p0 (n :: nil) = p1.
Require Coq.Lists.List. Definition subtract (p0 p1 : path_virtual) (_ : is_ancestor_of p1 p0 \/ p0 = p1) := List.skipn (length p1) p0.
Require Coq.Lists.List. Fixpoint ancestors_including_self (p : path_virtual) : list path_virtual := cons root (match p with | nil => nil | cons y ys => List.map (cons y) (ancestors_including_self ys) end). Definition ancestors (p : path_virtual) : list path_virtual := List.removelast (ancestors_including_self p).
Axiom archive : Set.
Axiom archive_mount : archive -> path_virtual.
Inductive file_reference := | FSReferenceFile | FSReferenceDirectory.
Axiom archive_lookup : archive -> path_virtual -> io error_code (option file_reference).
Axiom archive_lookup_file_ancestor : forall (a : archive) (p q : path_virtual), is_ancestor_of p q -> archive_lookup a p = Success _ _ (Some FSReferenceFile) -> archive_lookup a q = Failure _ _ FSErrorNotADirectory.
Axiom archive_lookup_ancestor : forall (a : archive) (p q : path_virtual), is_ancestor_of p q -> (exists r, archive_lookup a q = Success _ _ (Some r)) -> archive_lookup a p = Success _ _ (Some FSReferenceDirectory).
Axiom archive_directory_list : archive -> path_virtual -> io error_code (list name).
Axiom archive_directory_list_file : forall (a : archive) (p : path_virtual), archive_lookup a p = Success _ _ (Some FSReferenceFile) -> archive_directory_list a p = Failure _ _ FSErrorNotADirectory.
Require Coq.Arith.Peano_dec. Axiom ancestor_or_equal : forall (p q : path_virtual), {is_ancestor_of p q \/ q = p}+{~is_ancestor_of p q \/ q = p}. Fixpoint filesystem_lookup_union' (archives : list archive) (p : path_virtual) (checks : nat) : io error_code (option file_reference) := match archives with | nil => Success _ _ None | cons a rest => let m := archive_mount a in match ancestor_or_equal m p with | right _ => filesystem_lookup_union' rest p checks | left H => match archive_lookup a (subtract p m H) with | Success None => filesystem_lookup_union' rest p (S checks) | Success (Some r) => Success _ _ (Some r) | Failure FSErrorNotADirectory => if Peano_dec.eq_nat_dec checks 0 then Failure _ _ FSErrorNotADirectory else Success _ _ None | Failure e => Failure _ _ e end end end. Definition filesystem_lookup_union (archives : list archive) (p : path_virtual) := filesystem_lookup_union' archives p 0. Fixpoint filesystem_lookup_ancestors (archives : list archive) (p_a : list path_virtual) : io error_code (option file_reference) := match p_a with | nil => Success _ _ (Some (FSReferenceDirectory)) | cons q qs => match filesystem_lookup_union archives q with | Success None => Failure _ _ FSErrorNotADirectory | Success (Some FSReferenceFile) => Failure _ _ FSErrorNotADirectory | Success (Some FSReferenceDirectory) => filesystem_lookup_ancestors archives qs | Failure e => Failure _ _ e end end. Definition filesystem_lookup (archives : list archive) (p : path_virtual) : io error_code (option file_reference) := match filesystem_lookup_ancestors archives (ancestors p) with | Success _ => filesystem_lookup_union archives p | Failure e => Failure _ _ e end.
Require Coq.Arith.Peano_dec. Fixpoint filesystem_lookup_typical (archives : list archive) (p : path_virtual) : io error_code (option file_reference) := match archives with | nil => Success _ _ None | cons a rest => let m := archive_mount a in match ancestor_or_equal m p with | right _ => filesystem_lookup_typical rest p | left H => archive_lookup a (subtract p m H) end end.
Require Coq.Arith.Peano_dec. Require Coq.Lists.ListSet. Axiom name_eq_dec : forall (n m : name), {n = m}+{n <> m}. Fixpoint filesystem_directory_list_union' (archives : list archive) (p : path_virtual) (checks : nat) (current : ListSet.set name) : io error_code (list name) := match archives with | nil => Success _ _ current | cons a rest => let m := archive_mount a in match ancestor_or_equal m p with | right _ => filesystem_directory_list_union' rest p checks current | left H => match archive_directory_list a (subtract p m H) with | Success names => filesystem_directory_list_union' rest p (S checks) (ListSet.set_union name_eq_dec current names) | Failure FSErrorNotADirectory => if Peano_dec.eq_nat_dec checks 0 then Failure _ _ FSErrorNotADirectory else Success _ _ current | Failure e => Failure _ _ e end end end. Definition filesystem_directory_list_union (archives : list archive) (p : path_virtual) := filesystem_directory_list_union' archives p 0 nil. Definition filesystem_directory_list (archives : list archive) (p : path_virtual) : io error_code (list name) := match filesystem_lookup archives p with | Success None => Failure _ _ FSErrorNotADirectory | Success (Some FSReferenceFile) => Failure _ _ FSErrorNotADirectory | Success (Some FSReferenceDirectory) => filesystem_directory_list_union archives p | Failure e => Failure _ _ e end.