/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.fs.spi; import java.io.IOException; import java.util.HashMap; import org.apache.log4j.Logger; import org.jnode.driver.ApiNotFoundException; import org.jnode.driver.Device; import org.jnode.driver.block.BlockDeviceAPI; import org.jnode.driver.block.FSBlockDeviceAPI; import org.jnode.fs.FSDirectory; import org.jnode.fs.FSEntry; import org.jnode.fs.FSFile; import org.jnode.fs.FileSystem; import org.jnode.fs.FileSystemException; import org.jnode.fs.FileSystemType; /** * This class provide a basic implementation of {@link FileSystem} interface. * * @author Fabien DUMINY */ public abstract class AbstractFileSystem<T extends FSEntry> implements FileSystem<T> { /** My logger */ private static final Logger log = Logger.getLogger(AbstractFileSystem.class); /** The device that contains the file system */ private final Device device; /** API of the block device */ private final BlockDeviceAPI api; /** Type of the file system */ private final FileSystemType<? extends FileSystem<T>> type; /** Root enntry of the file system */ private T rootEntry; /** The file system is read-only */ private boolean readOnly; /** The file system is closed */ private boolean closed; /** The cache of files */ private HashMap<FSEntry, FSFile> files = new HashMap<FSEntry, FSFile>(); /** The cache of directory */ private HashMap<FSEntry, FSDirectory> directories = new HashMap<FSEntry, FSDirectory>(); /** * Construct an AbstractFileSystem in specified readOnly mode * * @param device device contains file system. This paramter is mandatory. * @param readOnly file system should be read-only. * * @throws FileSystemException device is null or device has no {@link BlockDeviceAPI} defined. */ public AbstractFileSystem(Device device, boolean readOnly, FileSystemType<? extends FileSystem<T>> type) throws FileSystemException { if (device == null) throw new FileSystemException("Device cannot be null."); this.device = device; try { api = device.getAPI(BlockDeviceAPI.class); } catch (ApiNotFoundException e) { throw new FileSystemException("Device is not a partition!", e); } this.closed = false; this.readOnly = readOnly; this.type = type; } /** * @see org.jnode.fs.FileSystem#getDevice() */ public final Device getDevice() { return device; } /** * @see org.jnode.fs.FileSystem#getType() */ public final FileSystemType<? extends FileSystem<T>> getType() { return type; } /** * @see org.jnode.fs.FileSystem#getRootEntry() */ public T getRootEntry() throws IOException { if (isClosed()) throw new IOException("FileSystem is closed"); if (rootEntry == null) { rootEntry = createRootEntry(); } return rootEntry; } /** * @see org.jnode.fs.FileSystem#close() */ public void close() throws IOException { if (!closed) { if (!readOnly) { flush(); } api.flush(); files.clear(); directories.clear(); rootEntry = null; files = null; directories = null; closed = true; } } /** * Save any cached data (files, directories, ...) to the device. * * @throws IOException if error occurs during write of datas on the device. */ public void flush() throws IOException { flushFiles(); flushDirectories(); } /** * Returns block device api. * * @return {@link BlockDeviceAPI}. */ public final BlockDeviceAPI getApi() { return api; } /** * Return file system block device api. * * @return {@link BlockDeviceAPI} * * @throws ApiNotFoundException if no api found for this file system device. */ public final FSBlockDeviceAPI getFSApi() throws ApiNotFoundException { return device.getAPI(FSBlockDeviceAPI.class); } /* * (non-Javadoc) * @see org.jnode.fs.FileSystem#isClosed() */ public final boolean isClosed() { return closed; } /** * Returns <tt>true</tt> if file system is read-only. * * @return <tt>true</tt> if file system is read-only. */ public final boolean isReadOnly() { return readOnly; } /** * Sets file system as a read-only file system. * * @param readOnly <tt>true</tt> if file system should be treated as a read-only file system. */ protected final void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } /** * Gets the file for the given entry. * * @param entry * * @return the {@link FSFile} associated with entry. * * @throws IOException if file system is closed. */ public final synchronized FSFile getFile(FSEntry entry) throws IOException { if (isClosed()) throw new IOException("FileSystem is closed"); FSFile file = files.get(entry); if (file == null) { file = createFile(entry); files.put(entry, file); } return file; } /** * Creates a new file from the entry * * @param entry * @return a new {@link FSFile} * * @throws IOException */ protected abstract FSFile createFile(FSEntry entry) throws IOException; /** * Save all unsaved files from entry cache. * * @throws IOException if error occurs during write of datas on the device. */ private final void flushFiles() throws IOException { log.info("flushing files ..."); for (FSFile f : files.values()) { if (log.isDebugEnabled()) { log.debug("flush: flushing file " + f); } f.flush(); } } /** * Gets the file for the given entry. * * @param entry * * @return the {@link FSDirectory} associated with this entry * * @throws IOException */ public final synchronized FSDirectory getDirectory(FSEntry entry) throws IOException { if (isClosed()) throw new IOException("FileSystem is closed"); FSDirectory dir = directories.get(entry); if (dir == null) { dir = createDirectory(entry); directories.put(entry, dir); } return dir; } /** * Creates a new directory from the entry * * @param entry * @return a new {@link FSDirectory} * * @throws IOException */ protected abstract FSDirectory createDirectory(FSEntry entry) throws IOException; /** * Save all unsaved files from entry cache. */ private final void flushDirectories() { log.info("flushing directories ..."); for (FSDirectory d : directories.values()) { if (log.isDebugEnabled()) { log.debug("flush: flushing directory " + d); } //TODO: uncomment this line //d.flush(); } } /** * Creates a new root entry * * @return {@link FSEntry} representing the new created root entry. * * @throws IOException file system doesn't allow to create a new root entry. */ protected abstract T createRootEntry() throws IOException; }