/** * FUSE-J: Java bindings for FUSE (Filesystem in Userspace by Miklos Szeredi (mszeredi@inf.bme.hu)) * * Copyright (C) 2003 Peter Levart (peter@select-tech.si) * * This program can be distributed under the terms of the GNU LGPL. * See the file COPYING.LIB */ package fuse.zipfs; import fuse.*; import fuse.compat.Filesystem1; import fuse.compat.FuseDirEnt; import fuse.compat.FuseStat; import fuse.zipfs.util.Node; import fuse.zipfs.util.Tree; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; public class ZipFilesystem implements Filesystem1 { private static final Log log = LogFactory.getLog(ZipFilesystem.class); private static final int blockSize = 512; private ZipFile zipFile; private long zipFileTime; private ZipEntry rootEntry; private Tree tree; private FuseStatfs statfs; private ZipFileDataReader zipFileDataReader; public ZipFilesystem(File file) throws IOException { log.info("extracting zip file structure..."); zipFile = new ZipFile(file, ZipFile.OPEN_READ); zipFileTime = file.lastModified(); rootEntry = new ZipEntry("") { public boolean isDirectory() { return true; } }; rootEntry.setTime(zipFileTime); rootEntry.setSize(0); zipFileDataReader = new ZipFileDataReader(zipFile); tree = new Tree(); tree.addNode(rootEntry.getName(), rootEntry); int files = 0; int dirs = 0; int blocks = 0; for (Enumeration e = zipFile.entries(); e.hasMoreElements();) { ZipEntry entry = (ZipEntry) e.nextElement(); tree.addNode(entry.getName(), entry); if (entry.isDirectory()) dirs++; else files++; blocks += (entry.getSize() + blockSize - 1) / blockSize; } statfs = new FuseStatfs(); statfs.blocks = blocks; statfs.blockSize = blockSize; statfs.blocksFree = 0; statfs.files = files + dirs; statfs.filesFree = 0; statfs.namelen = 2048; log.info("zip file structure extracted: " + files + " files, " + dirs + " directories, " + blocks + " blocks (" + blockSize + " byte/block)."); } public void chmod(String path, int mode) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void chown(String path, int uid, int gid) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public FuseStat getattr(String path) throws FuseException { Node node = tree.lookupNode(path); ZipEntry entry = null; if (node == null || (entry = (ZipEntry)node.getValue()) == null) throw new FuseException("No Such Entry").initErrno(FuseException.ENOENT); FuseStat stat = new FuseStat(); stat.mode = entry.isDirectory() ? FuseFtype.TYPE_DIR | 0755 : FuseFtype.TYPE_FILE | 0644; stat.nlink = 1; stat.uid = 0; stat.gid = 0; stat.size = entry.getSize(); stat.atime = stat.mtime = stat.ctime = (int) (entry.getTime() / 1000L); stat.blocks = (int) ((stat.size + 511L) / 512L); return stat; } public FuseDirEnt[] getdir(String path) throws FuseException { Node node = tree.lookupNode(path); ZipEntry entry = null; if (node == null || (entry = (ZipEntry)node.getValue()) == null) throw new FuseException("No Such Entry").initErrno(FuseException.ENOENT); if (!entry.isDirectory()) throw new FuseException("Not A Directory").initErrno(FuseException.ENOTDIR); Collection children = node.getChildren(); FuseDirEnt[] dirEntries = new FuseDirEnt[children.size()]; int i = 0; for (Iterator iter = children.iterator(); iter.hasNext(); i++) { Node childNode = (Node)iter.next(); ZipEntry zipEntry = (ZipEntry)childNode.getValue(); FuseDirEnt dirEntry = new FuseDirEnt(); dirEntries[i] = dirEntry; dirEntry.name = childNode.getName(); dirEntry.mode = zipEntry.isDirectory()? FuseFtype.TYPE_DIR : FuseFtype.TYPE_FILE; } return dirEntries; } public void link(String from, String to) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void mkdir(String path, int mode) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void mknod(String path, int mode, int rdev) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void open(String path, int flags) throws FuseException { ZipEntry entry = getFileZipEntry(path); if (flags == O_WRONLY || flags == O_RDWR) throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void rename(String from, String to) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void rmdir(String path) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public FuseStatfs statfs() throws FuseException { return statfs; } public void symlink(String from, String to) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void truncate(String path, long size) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void unlink(String path) throws FuseException { throw new FuseException("Read Only").initErrno(FuseException.EACCES); } public void utime(String path, int atime, int mtime) throws FuseException { // noop } public String readlink(String path) throws FuseException { throw new FuseException("Not a link").initErrno(FuseException.ENOENT); } public void write(String path, ByteBuffer buf, long offset) throws FuseException { // noop } public void read(String path, ByteBuffer buf, long offset) throws FuseException { ZipEntry zipEntry = getFileZipEntry(path); ZipEntryDataReader reader = zipFileDataReader.getZipEntryDataReader(zipEntry, offset, buf.capacity()); reader.read(buf, offset); } public void release(String path, int flags) throws FuseException { ZipEntry zipEntry = getFileZipEntry(path); zipFileDataReader.releaseZipEntryDataReader(zipEntry); } // // private methods private ZipEntry getFileZipEntry(String path) throws FuseException { Node node = tree.lookupNode(path); ZipEntry entry; if (node == null || (entry = (ZipEntry)node.getValue()) == null) throw new FuseException("No Such Entry").initErrno(FuseException.ENOENT); if (entry.isDirectory()) throw new FuseException("Not A File").initErrno(FuseException.ENOENT); return entry; } private ZipEntry getDirectoryZipEntry(String path) throws FuseException { Node node = tree.lookupNode(path); ZipEntry entry; if (node == null || (entry = (ZipEntry)node.getValue()) == null) throw new FuseException("No Such Entry").initErrno(FuseException.ENOENT); if (!entry.isDirectory()) throw new FuseException("Not A Directory").initErrno(FuseException.ENOENT); return entry; } // // Java entry point public static void main(String[] args) { if (args.length < 1) { System.out.println("Must specify ZIP file"); System.exit(-1); } String fuseArgs[] = new String[args.length - 1]; System.arraycopy(args, 0, fuseArgs, 0, fuseArgs.length); File zipFile = new File(args[args.length - 1]); try { FuseMount.mount(fuseArgs, new ZipFilesystem(zipFile)); } catch (Exception e) { e.printStackTrace(); } } }