First cut of the AVM filesystem driver for CIFS/FTP support.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@4388 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gary Spencer
2006-11-17 14:49:51 +00:00
parent 992bd82b9e
commit 10b48c7f2a
6 changed files with 2268 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.avm;
import org.alfresco.filesys.server.filesys.DiskDeviceContext;
import org.alfresco.filesys.server.filesys.FileSystem;
/**
* AVM Filesystem Context Class
*
* <p>Contains per filesystem context.
*
* @author GKSpencer
*/
public class AVMContext extends DiskDeviceContext {
// Constants
//
// Version id that indicates the head version
public static final int VERSION_HEAD = -1;
// Store, root path and version
private String m_storePath;
private int m_version = VERSION_HEAD;
/**
* Class constructor
*
* @param storePath String
* @param version int
*/
public AVMContext( String storePath, int version)
{
super( storePath + "(" + version + ")");
// Set the store root path, remove any trailing slash as relative paths will be appended to this value
m_storePath = storePath;
if ( m_storePath.endsWith( "/"))
m_storePath = m_storePath.substring(0, m_storePath.length() - 1);
// Set the store version to use
m_version = version;
}
/**
* Return the filesystem type, either FileSystem.TypeFAT or FileSystem.TypeNTFS.
*
* @return String
*/
public String getFilesystemType()
{
return FileSystem.TypeNTFS;
}
/**
* Return the store path
*
* @return String
*/
public final String getStorePath()
{
return m_storePath;
}
/**
* Return the version
*
* @return int
*/
public final int isVersion()
{
return m_version;
}
/**
* Close the filesystem context
*/
public void CloseContext() {
// Call the base class
super.CloseContext();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,493 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.avm;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.server.filesys.AccessDeniedException;
import org.alfresco.filesys.server.filesys.FileAttribute;
import org.alfresco.filesys.server.filesys.NetworkFile;
import org.alfresco.filesys.smb.SeekType;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* AVM Network File Class
*
* <p>Holds the details of an open file, and provides access to the file data.
*
* @author GKSpencer
*/
public class AVMNetworkFile extends NetworkFile {
// Logging
private static final Log logger = LogFactory.getLog(AVMNetworkFile.class);
// AVM service
private AVMService m_avmService;
// AVM path to the file/folder and store version
private String m_avmPath;
private int m_avmVersion;
// Flag to indicate if the file has been modified
private boolean m_modified;
// Access to the file data, flag to indicate if the file channel is writable
private FileChannel m_channel;
private boolean m_writable;
// Mime type, if a writer is opened
private String m_mimeType;
/**
* Class constructor
*
* @param details AVMNodeDescriptor
* @param avmPath String
* @param avmVersion int
* @param avmService AVMService
*/
public AVMNetworkFile( AVMNodeDescriptor details, String avmPath, int avmVersion, AVMService avmService)
{
super( details.getName());
// Save the service, apth and version
m_avmService = avmService;
m_avmPath = avmPath;
m_avmVersion = avmVersion;
// Copy the file details
setAccessDate( details.getAccessDate());
setCreationDate( details.getCreateDate());
setModifyDate( details.getModDate());
if ( details.isFile())
setFileSize( details.getLength());
else
setFileSize( 0L);
int attr = 0;
if ( details.isDirectory())
attr += FileAttribute.Directory;
if ( avmVersion != AVMContext.VERSION_HEAD)
attr += FileAttribute.ReadOnly;
setAttributes( attr);
}
/**
* Check if there is an open file channel to the content
*
* @return boolean
*/
public final boolean hasContentChannel()
{
return m_channel != null ? true : false;
}
/**
* Return the mime type
*
* @return String
*/
public final String getMimeType()
{
return m_mimeType;
}
/**
* Set the mime type
*
* @param mimeType String
*/
public final void setMimeType(String mimeType)
{
m_mimeType = mimeType;
}
/**
* Open the file
*
* @param createFlag boolean
* @exception IOException
*/
public void openFile(boolean createFlag)
throws IOException
{
// Nothing to do, content is opened on first read/write
}
/**
* Read from the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param fileOff long
* @return Length of data read.
* @exception IOException
*/
public int readFile(byte[] buf, int len, int pos, long fileOff)
throws java.io.IOException
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Read file " + getName() + ", len=" + len + ", offset=" + fileOff);
// Open the channel for reading
openContent(false, false);
// Read from the channel
ByteBuffer byteBuffer = ByteBuffer.wrap(buf, pos, len);
int count = m_channel.read(byteBuffer, fileOff);
if (count < 0)
{
// Return a zero count at end of file
count = 0;
}
// Return the length of data read
return count;
}
/**
* Write a block of data to the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param fileOff long
* @exception IOException
*/
public void writeFile(byte[] buf, int len, int pos, long fileOff)
throws java.io.IOException
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Write file " + getName() + ", len=" + len + ", offset=" + fileOff);
// Open the channel for writing
openContent(true, false);
// Write to the channel
ByteBuffer byteBuffer = ByteBuffer.wrap(buf, pos, len);
int count = m_channel.write(byteBuffer, fileOff);
// Set modification flag
m_modified = true;
// Update the current file size
setFileSize( m_channel.size());
}
/**
* Seek to the specified file position.
*
* @param pos long
* @param typ int
* @return int
* @exception IOException
*/
public long seekFile(long pos, int typ)
throws IOException
{
// Open the file, if not already open
openContent( false, false);
// Check if the current file position is the required file position
long curPos = m_channel.position();
switch (typ) {
// From start of file
case SeekType.StartOfFile :
if (curPos != pos)
m_channel.position( pos);
break;
// From current position
case SeekType.CurrentPos :
m_channel.position( curPos + pos);
break;
// From end of file
case SeekType.EndOfFile :
{
long newPos = m_channel.size() + pos;
m_channel.position(newPos);
}
break;
}
// Return the new file position
return m_channel.position();
}
/**
* Flush any buffered output to the file
*
* @throws IOException
*/
public void flushFile()
throws IOException
{
// If the file channel is open for write then flush the channel
if ( m_channel != null && m_writable)
m_channel.force( false);
}
/**
* Truncate the file to the specified file size
*
* @param siz long
* @exception IOException
*/
public void truncateFile(long siz)
throws IOException
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Truncate file " + getName() + ", size=" + siz);
// If the content data channel has not been opened yet and the requested size is zero
// then this is an open for overwrite so the existing content data is not copied
if ( m_channel == null && siz == 0L)
{
// Open content for overwrite, no need to copy existing content data
openContent(true, true);
}
else
{
// Normal open for write
openContent(true, false);
// Truncate or extend the channel
m_channel.truncate(siz);
}
// Set modification flag
m_modified = true;
}
/**
* Close the database file
*/
public void closeFile()
throws IOException
{
// If the file is a directory or the file channel has not been opened then there is nothing to do
if ( isDirectory() || m_channel == null)
return;
// Close the file channel
try
{
m_channel.close();
m_channel = null;
}
catch ( IOException ex)
{
logger.error("Failed to close file channel for " + getName(), ex);
}
}
/**
* Open a file channel to the file content, switching to a writable file channel if required.
*
* @param write boolean
* @param trunc boolean
* @throws AccessDeniedException If this network file is read only
* @throws AlfrescoRuntimeException If this network file represents a directory
*/
private void openContent(boolean write, boolean trunc)
throws AccessDeniedException, AlfrescoRuntimeException
{
// Check if this network file is a directory, no content to open
if ( isDirectory())
throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this);
// Check if write access is required and the current channel is read-only
long curPos = 0L;
if ( write && m_writable == false && m_channel != null)
{
// Close the existing read-only channel
try
{
// Save the current file position
curPos = m_channel.position();
// Close the read-only file channel
m_channel.close();
m_channel = null;
}
catch (IOException ex)
{
logger.error("Error closing read-only channel", ex);
}
// Debug
if ( logger.isDebugEnabled())
logger.debug("Switching to writable channel for " + getName());
}
else if ( m_channel != null)
{
// File channel already open
return;
}
// We need to create the channel
if (write && getGrantedAccess() == NetworkFile.READONLY)
throw new AccessDeniedException("The network file was created for read-only: " + this);
// Access the content data and get a file channel to the data
if ( write)
{
// Access the content data for write
ContentWriter cWriter = null;
try {
// Create a writer to access the file data
cWriter = m_avmService.createContentWriter( m_avmPath);
// Set the mime-type
cWriter.setMimetype( getMimeType());
}
catch (Exception ex) {
logger.debug( ex);
ex.printStackTrace();
}
// Indicate that we have a writable channel to the file
m_writable = true;
// Get the writable channel, do not copy existing content data if the file is to be truncated
m_channel = cWriter.getFileChannel( trunc);
// Reset the file position to match the read-only file channel position, unless we truncated the file
if ( curPos != 0L && trunc == false)
{
try
{
m_channel.position( curPos);
}
catch (IOException ex)
{
logger.error("Failed to set file position for " + getName(), ex);
}
}
}
else
{
// Access the content data for read
ContentReader cReader = m_avmService.getContentReader( m_avmVersion, m_avmPath);
// Indicate that we only have a read-only channel to the data
m_writable = false;
// Get the read-only channel
m_channel = cReader.getFileChannel();
}
}
/**
* Return the network file details as a string
*
* @return String
*/
public String toString()
{
StringBuilder str = new StringBuilder();
str.append( "[");
str.append( getName());
str.append( ":");
str.append( isDirectory() ? "Dir," : "File,");
str.append( getFileSize());
str.append( "-Channel=");
str.append( m_channel);
str.append( m_writable ? ",Write" : ",Read");
str.append( m_modified ? ",Modified" : "");
str.append( "]");
return str.toString();
}
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.avm;
import java.util.Collection;
import java.util.SortedMap;
import org.alfresco.filesys.server.filesys.FileAttribute;
import org.alfresco.filesys.server.filesys.FileInfo;
import org.alfresco.filesys.server.filesys.SearchContext;
import org.alfresco.filesys.util.WildCard;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
/**
* AVM Filesystem Search Context Class
*
* <p>Contains the details of a wildcard folder search.
*
* @author GKSpencer
*/
public class AVMSearchContext extends SearchContext {
// File list and current index
private AVMNodeDescriptor[] m_fileList;
private int m_fileIdx;
// File attributes
private int m_attrib;
// Optional wildcard filter
private WildCard m_filter;
/**
* Class constructor
*
* @param fileList SortedMap<String, AVMNodeDescriptor>
* @param attrib int
* @param filter WildCard
*/
public AVMSearchContext( AVMNodeDescriptor[] fileList, int attrib, WildCard filter)
{
m_attrib = attrib;
m_filter = filter;
m_fileList = fileList;
}
/**
* Determine if there are more files for the active search.
*
* @return boolean
*/
public boolean hasMoreFiles()
{
return m_fileIdx < m_fileList.length ? true : false;
}
/**
* Return file information for the next file in the active search. Returns false if the search
* is complete.
*
* @param info FileInfo to return the file information.
* @return true if the file information is valid, else false
*/
public boolean nextFileInfo(FileInfo info)
{
// Check if there is another file record to return
if ( m_fileIdx >= m_fileList.length)
return false;
// Search for the next valid file
boolean foundMatch = false;
AVMNodeDescriptor curFile = null;
while (foundMatch == false && m_fileIdx < m_fileList.length)
{
// Get the next file from the list
curFile = m_fileList[ m_fileIdx++];
// Check if the file name matches the search pattern
if ( m_filter.matchesPattern(curFile.getName()) == true)
{
// Check if the file matches the search attributes
if (FileAttribute.hasAttribute(m_attrib, FileAttribute.Directory) &&
curFile.isDirectory())
{
// Found a match
foundMatch = true;
}
else if ( curFile.isFile())
{
// Found a match
foundMatch = true;
}
// Check if we found a match
if ( foundMatch == false)
{
// Get the next file from the list
if ( ++m_fileIdx < m_fileList.length)
curFile = m_fileList[ m_fileIdx];
}
}
}
// If we found a match then fill in the file information
if ( foundMatch)
{
// Fill in the file information
info.setFileName( curFile.getName());
if ( curFile.isFile())
{
info.setFileSize( curFile.getLength());
info.setAllocationSize((curFile.getLength() + 512L) & 0xFFFFFFFFFFFFFE00L);
}
else
info.setFileSize( 0L);
info.setAccessDateTime( curFile.getAccessDate());
info.setCreationDateTime( curFile.getCreateDate());
info.setModifyDateTime( curFile.getModDate());
// Build the file attributes
int attr = 0;
if ( curFile.isDirectory())
attr += FileAttribute.Directory;
if ( curFile.getName().startsWith( ".") ||
curFile.getName().equalsIgnoreCase( "Desktop.ini") ||
curFile.getName().equalsIgnoreCase( "Thumbs.db"))
attr += FileAttribute.Hidden;
info.setFileAttributes( attr);
}
// Indicate if the file information is valid
return foundMatch;
}
/**
* Return the file name of the next file in the active search. Returns null is the search is
* complete.
*
* @return String
*/
public String nextFileName()
{
// Check if there is another file record to return
// Find the next matching file name
while ( m_fileIdx < m_fileList.length) {
// Check if the current file name matches the search pattern
String fname = m_fileList[m_fileIdx++].getName();
if ( m_filter.matchesPattern(fname))
return fname;
}
// No more matching file names
return null;
}
/**
* Return the total number of file entries for this search if known, else return -1
*
* @return int
*/
public int numberOfEntries()
{
return m_fileList.length;
}
/**
* Return the resume id for the current file/directory in the search.
*
* @return int
*/
public int getResumeId()
{
return m_fileIdx;
}
/**
* Restart a search at the specified resume point.
*
* @param resumeId Resume point id.
* @return true if the search can be restarted, else false.
*/
public boolean restartAt(int resumeId)
{
// Range check the resume id
if ( resumeId < 0 || resumeId >= m_fileList.length)
return false;
// Reset the current file index
m_fileIdx = resumeId;
return true;
}
/**
* Restart the current search at the specified file.
*
* @param info File to restart the search at.
* @return true if the search can be restarted, else false.
*/
public boolean restartAt(FileInfo info)
{
// Search backwards from the current file
int curFileIdx = m_fileIdx;
while ( m_fileIdx > 0) {
// Check if the current file is the required search restart point
if ( m_fileList[ m_fileIdx].getName().equals( info.getFileName()))
return true;
else
m_fileIdx--;
}
// Failed to find the restart file
m_fileIdx = curFileIdx;
return false;
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.avm;
import org.alfresco.filesys.server.filesys.FileAttribute;
import org.alfresco.filesys.server.filesys.FileInfo;
import org.alfresco.filesys.server.filesys.SearchContext;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
/**
* AVM Filesystem Single File Search Context Class
*
* <p>Contains the details of a non-wildcard file/folder search, where there is only one result to return.
*
* @author GKSpencer
*/
public class AVMSingleFileSearchContext extends SearchContext {
// Details of the single file/folder
private AVMNodeDescriptor m_fileDetails;
// Flag to indicate file details have been returned
private boolean m_endOfSearch;
/**
* Class constructor
*
* @param fileDetails AVMNodeDescriptor
*/
public AVMSingleFileSearchContext( AVMNodeDescriptor fileDetails)
{
m_fileDetails = fileDetails;
}
/**
* Determine if there are more files for the active search.
*
* @return boolean
*/
public boolean hasMoreFiles()
{
return m_endOfSearch == false ? true : false;
}
/**
* Return file information for the next file in the active search. Returns false if the search
* is complete.
*
* @param info FileInfo to return the file information.
* @return true if the file information is valid, else false
*/
public boolean nextFileInfo(FileInfo info)
{
// Check if the file details have been returned
if ( m_endOfSearch == true)
return false;
// Fill in the file information details
info.setFileName( m_fileDetails.getName());
if ( m_fileDetails.isFile())
{
info.setFileSize( m_fileDetails.getLength());
info.setAllocationSize((m_fileDetails.getLength() + 512L) & 0xFFFFFFFFFFFFFE00L);
}
else
info.setFileSize( 0L);
info.setAccessDateTime( m_fileDetails.getAccessDate());
info.setCreationDateTime( m_fileDetails.getCreateDate());
info.setModifyDateTime( m_fileDetails.getModDate());
// Build the file attributes
int attr = 0;
if ( m_fileDetails.isDirectory())
attr += FileAttribute.Directory;
if ( m_fileDetails.getName().startsWith( ".") ||
m_fileDetails.getName().equalsIgnoreCase( "Desktop.ini") ||
m_fileDetails.getName().equalsIgnoreCase( "Thumbs.db"))
attr += FileAttribute.Hidden;
info.setFileAttributes( attr);
// Set the end of search flag, indicate that the file informatin is valid
m_endOfSearch = true;
return true;
}
/**
* Return the file name of the next file in the active search. Returns null is the search is
* complete.
*
* @return String
*/
public String nextFileName()
{
// Check if the file details have been returned
if ( m_endOfSearch == true)
return null;
// Return the file/folder name, set the end of search flag
m_endOfSearch = true;
return m_fileDetails.getName();
}
/**
* Return the total number of file entries for this search if known, else return -1
*
* @return int
*/
public int numberOfEntries()
{
return 1;
}
/**
* Return the resume id for the current file/directory in the search.
*
* @return int
*/
public int getResumeId()
{
return 1;
}
/**
* Restart a search at the specified resume point.
*
* @param resumeId Resume point id.
* @return true if the search can be restarted, else false.
*/
public boolean restartAt(int resumeId)
{
// Validate the resume id and clear the end of search flag
if ( resumeId == 1)
m_endOfSearch = false;
else
return false;
return true;
}
/**
* Restart the current search at the specified file.
*
* @param info File to restart the search at.
* @return true if the search can be restarted, else false.
*/
public boolean restartAt(FileInfo info)
{
return true;
}
}