/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see .
*/
package org.alfresco.filesys.repo;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.alfresco.AlfrescoContext;
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
import org.alfresco.filesys.alfresco.ExtendedDiskInterface;
import org.alfresco.filesys.alfresco.PseudoFileOverlayImpl;
import org.alfresco.filesys.alfresco.RepositoryDiskInterface;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.core.DeviceContext;
import org.alfresco.jlan.server.core.DeviceContextException;
import org.alfresco.jlan.server.filesys.AccessDeniedException;
import org.alfresco.jlan.server.filesys.DirectoryNotEmptyException;
import org.alfresco.jlan.server.filesys.DiskDeviceContext;
import org.alfresco.jlan.server.filesys.DiskFullException;
import org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.jlan.server.filesys.DiskSizeInterface;
import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.FileOpenParams;
import org.alfresco.jlan.server.filesys.FileStatus;
import org.alfresco.jlan.server.filesys.IOControlNotImplementedException;
import org.alfresco.jlan.server.filesys.IOCtlInterface;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.server.filesys.PermissionDeniedException;
import org.alfresco.jlan.server.filesys.SearchContext;
import org.alfresco.jlan.server.filesys.SrvDiskInfo;
import org.alfresco.jlan.server.filesys.TreeConnection;
import org.alfresco.jlan.server.filesys.cache.FileState;
import org.alfresco.jlan.server.filesys.pseudo.MemoryNetworkFile;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFile;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList;
import org.alfresco.jlan.server.filesys.pseudo.PseudoNetworkFile;
import org.alfresco.jlan.server.filesys.quota.QuotaManager;
import org.alfresco.jlan.server.filesys.quota.QuotaManagerException;
import org.alfresco.jlan.server.locking.FileLockingInterface;
import org.alfresco.jlan.server.locking.LockManager;
import org.alfresco.jlan.server.locking.OpLockInterface;
import org.alfresco.jlan.server.locking.OpLockManager;
import org.alfresco.jlan.smb.SMBException;
import org.alfresco.jlan.smb.server.SMBServer;
import org.alfresco.jlan.smb.server.SMBSrvSession;
import org.alfresco.jlan.util.DataBuffer;
import org.alfresco.jlan.util.MemorySize;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.encoding.ContentCharsetFinder;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.repo.model.filefolder.HiddenAspect;
import org.alfresco.repo.node.archive.NodeArchiveService;
import org.alfresco.repo.node.archive.RestoreNodeReport;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.NodeLockedException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.config.ConfigElement;
/**
* Alfresco Content repository filesystem driver class
*
* Provides a JLAN ContentDiskDriver for various JLAN protocols
* such as SMB/CIFS, NFS and FTP.
*
*/
public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedDiskInterface,
DiskInterface,
DiskSizeInterface,
IOCtlInterface,
RepositoryDiskInterface,
OpLockInterface,
FileLockingInterface
{
// Logging
private static final Log logger = LogFactory.getLog(ContentDiskDriver2.class);
private static final Log readLogger = LogFactory.getLog("org.alfresco.filesys.repo.ContentDiskDriver2.Read");
private static final Log writeLogger = LogFactory.getLog("org.alfresco.filesys.repo.ContentDiskDriver2.Write");
// Services and helpers
private CifsHelper cifsHelper;
private NamespaceService namespaceService;
private NodeService nodeService;
private SearchService searchService;
private ContentService contentService;
private MimetypeService mimetypeService;
private PermissionService permissionService;
private FileFolderService fileFolderService;
private LockService lockService;
private CheckOutCheckInService checkOutCheckInService;
private AuthenticationContext authContext;
private AuthenticationService authService;
private BehaviourFilter policyBehaviourFilter;
private NodeMonitorFactory m_nodeMonitorFactory;
private ContentComparator contentComparator;
private NodeArchiveService nodeArchiveService;
private HiddenAspect hiddenAspect;
private LockKeeper lockKeeper;
// TODO Should not be here - should be specific to a context.
private boolean isLockedFilesAsOffline;
/**
*
*/
public void init()
{
PropertyCheck.mandatory(this, "checkOutCheckInService", checkOutCheckInService);
PropertyCheck.mandatory(this, "cifsHelper", cifsHelper);
PropertyCheck.mandatory(this, "namespaceService", namespaceService);
PropertyCheck.mandatory(this, "nodeService", nodeService);
PropertyCheck.mandatory(this, "searchService", searchService);
PropertyCheck.mandatory(this, "contentService", contentService);
PropertyCheck.mandatory(this, "mimetypeService", mimetypeService);
PropertyCheck.mandatory(this, "permissionService", permissionService);
PropertyCheck.mandatory(this, "fileFolderService", fileFolderService);
PropertyCheck.mandatory(this, "lockService",lockService);
PropertyCheck.mandatory(this, "authContext", authContext);
PropertyCheck.mandatory(this, "authService", authService);
PropertyCheck.mandatory(this, "policyBehaviourFilter", policyBehaviourFilter);
PropertyCheck.mandatory(this, "m_nodeMonitorFactory", m_nodeMonitorFactory);
PropertyCheck.mandatory(this, "ioControlHandler", ioControlHandler);
PropertyCheck.mandatory(this, "contentComparator", getContentComparator());
PropertyCheck.mandatory(this, "nodeArchiveService", nodeArchiveService);
PropertyCheck.mandatory(this, "hiddenAspect", hiddenAspect);
PropertyCheck.mandatory(this, "lockKeeper", lockKeeper);
PropertyCheck.mandatory(this, "deletePseudoFileCache", deletePseudoFileCache);
}
/**
* Return the CIFS helper
*
* @return CifsHelper
*/
public final CifsHelper getCifsHelper()
{
return this.cifsHelper;
}
/**
* Return the authentication service
*
* @return AuthenticationService
*/
public final AuthenticationService getAuthenticationService()
{
return authService;
}
/**
* Return the authentication context
*
* @return AuthenticationContext
*/
public final AuthenticationContext getAuthenticationContext() {
return authContext;
}
/**
* Return the node service
*
* @return NodeService
*/
public final NodeService getNodeService()
{
return this.nodeService;
}
/**
* Return the content service
*
* @return ContentService
*/
public final ContentService getContentService()
{
return this.contentService;
}
/**
* Return the namespace service
*
* @return NamespaceService
*/
public final NamespaceService getNamespaceService()
{
return this.namespaceService;
}
/**
* Return the search service
*
* @return SearchService
*/
public final SearchService getSearchService(){
return this.searchService;
}
/**
* Return the file folder service
*
* @return FileFolderService
*/
public final FileFolderService getFileFolderService() {
return this.fileFolderService;
}
/**
* Return the permission service
*
* @return PermissionService
*/
public final PermissionService getPermissionService() {
return this.permissionService;
}
/**
* Return the lock service
*
* @return LockService
*/
public final LockService getLockService() {
return lockService;
}
/**
* Get the policy behaviour filter, used to inhibit versioning on a per transaction basis
*/
public BehaviourFilter getPolicyFilter()
{
return policyBehaviourFilter;
}
/**
* @param contentService the content service
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* @param namespaceService the namespace service
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param nodeService the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param searchService the search service
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* Set the permission service
*
* @param permissionService PermissionService
*/
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* Set the authentication context
*
* @param authContext AuthenticationContext
*/
public void setAuthenticationContext(AuthenticationContext authContext)
{
this.authContext = authContext;
}
/**
* Set the authentication service
*
* @param authService AuthenticationService
*/
public void setAuthenticationService(AuthenticationService authService)
{
this.authService = authService;
}
/**
* Set the file folder service
*
* @param fileService FileFolderService
*/
public void setFileFolderService(FileFolderService fileService)
{
fileFolderService = fileService;
}
/**
* @param mimetypeService service for helping with mimetypes and encoding
*/
public void setMimetypeService(MimetypeService mimetypeService)
{
this.mimetypeService = mimetypeService;
}
/**
* Set the node monitor factory
*
* @param nodeMonitorFactory NodeMonitorFactory
*/
public void setNodeMonitorFactory(NodeMonitorFactory nodeMonitorFactory) {
m_nodeMonitorFactory = nodeMonitorFactory;
}
/**
* Set the lock service
*
* @param lockService LockService
*/
public void setLockService(LockService lockService) {
this.lockService = lockService;
}
/**
* Set the policy behaviour filter, used to inhibit versioning on a per transaction basis
*
* @param policyFilter PolicyBehaviourFilter
*/
public void setPolicyFilter(BehaviourFilter policyFilter)
{
this.policyBehaviourFilter = policyFilter;
}
/**
* @param hiddenAspect
*/
public void setHiddenAspect(HiddenAspect hiddenAspect)
{
this.hiddenAspect = hiddenAspect;
}
/**
* @param hiddenAspect
*/
public void setAlfrescoLockKeeper(LockKeeper lockKeeper)
{
this.lockKeeper = lockKeeper;
}
// Configuration key names
private static final String KEY_STORE = "store";
private static final String KEY_ROOT_PATH = "rootPath";
private static final String KEY_RELATIVE_PATH = "relativePath";
/**
* Parse and validate the parameter string and create a device context object for this instance
* of the shared device. The same DeviceInterface implementation may be used for multiple
* shares.
*
* @deprecated - no longer used. Construction of context is via spring now.
* @param deviceName The name of the device
* @param cfg ConfigElement the configuration of the device context.
* @return DeviceContext
* @exception DeviceContextException
*/
public DeviceContext createContext(String deviceName, ConfigElement cfg) throws DeviceContextException
{
logger.error("Obsolete method called");
throw new DeviceContextException("Obsolete Method called");
}
/*
* Register context implementation
*
* Results in various obscure bits and pieces being initialised, most importantly the
* calculation of the root node ref.
*
* There's a load of initialisation that needs to be moved out of this method, like the
* instantiation of the lock manager, quota manager and node monitor.
*/
public void registerContext(DeviceContext ctx) throws DeviceContextException
{
logger.debug("registerContext");
super.registerContext(ctx);
final ContentContext context = (ContentContext)ctx;
final String rootPath = context.getRootPath();
final String storeValue = context.getStoreName();
/**
* Work using the repo needs to run as system.
*/
RunAsWork runAsSystem = new RunAsWork()
{
@Override
public Void doWork() throws Exception
{
StoreRef storeRef = new StoreRef(storeValue);
// Connect to the repo and ensure that the store exists
if (! nodeService.exists(storeRef))
{
throw new DeviceContextException("Store not created prior to application startup: " + storeRef);
}
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
// Find the root node for this device
List nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPath, null, namespaceService, false);
NodeRef rootNodeRef = null;
if (nodeRefs.size() > 1)
{
throw new DeviceContextException("Multiple possible roots for device: \n" +
" root path: " + rootPath + "\n" +
" results: " + nodeRefs);
}
else if (nodeRefs.size() == 0)
{
// Nothing found
throw new DeviceContextException("No root found for device: \n" +
" root path: " + rootPath);
}
else
{
// We found the root node ref
rootNodeRef = nodeRefs.get(0);
}
// Check if a relative path has been specified
String relPath = context.getRelativePath();
try
{
if ( relPath != null && relPath.length() > 0)
{
// Find the node and validate that the relative path is to a folder
NodeRef relPathNode = cifsHelper.getNodeRef( rootNodeRef, relPath);
if ( cifsHelper.isDirectory( relPathNode) == false)
{
throw new DeviceContextException("Relative path is not a folder, " + relPath);
}
// Use the relative path node as the root of the filesystem
rootNodeRef = relPathNode;
}
else
{
// Make sure the default root node is a folder
if ( cifsHelper.isDirectory( rootNodeRef) == false)
{
throw new DeviceContextException("Root node is not a folder type node");
}
}
}
catch (Exception ex)
{
if(logger.isDebugEnabled())
{
logger.debug("Error during create context", ex);
}
throw new DeviceContextException("Unable to find root node.", ex);
}
// Record the root node ref
if(logger.isDebugEnabled())
{
logger.debug("set root node ref:" + rootNodeRef);
}
context.setRootNodeRef(rootNodeRef);
return null;
}
};
/**
* Run the above code as system - in particular resolves root node ref.
*/
AuthenticationUtil.runAs(runAsSystem, AuthenticationUtil.getSystemUserName());
/*
* Now we have some odds and ends below that should really be configured elsewhere
*/
// Check if locked files should be marked as offline
if ( context.getOfflineFiles() )
{
// Enable marking locked files as offline
isLockedFilesAsOffline = true;
logger.info("Locked files will be marked as offline");
}
// Enable file state caching
// context.enableStateCache(serverConfig, true);
// context.getStateCache().setCaseSensitive( false);
logger.debug("initialise the node monitor");
// Install the node service monitor
if ( !context.getDisableNodeMonitor() && m_nodeMonitorFactory != null)
{
NodeMonitor nodeMonitor = m_nodeMonitorFactory.createNodeMonitor(context);
context.setNodeMonitor( nodeMonitor);
}
logger.debug("initialise the file state lock manager");
// Check if oplocks are enabled
if ( context.getDisableOplocks() == true)
{
logger.warn("Oplock support disabled for filesystem " + context.getDeviceName());
}
// Start the quota manager, if enabled
if ( context.hasQuotaManager())
{
try
{
// Start the quota manager
context.getQuotaManager().startManager( this, context);
logger.info("Quota manager enabled for filesystem");
}
catch ( QuotaManagerException ex)
{
logger.error("Failed to start quota manager", ex);
}
}
// TODO mode to spring
PseudoFileOverlayImpl ps = new PseudoFileOverlayImpl();
ps.setContext(context);
ps.setNodeService(nodeService);
ps.setSysAdminParams(context.getSysAdminParams());
ps.setDeletePseudoFileCache(deletePseudoFileCache);
context.setPseudoFileOverlay(ps);
ps.init();
}
/**
* Determine if the disk device is read-only.
*
* @param sess Server session
* @param ctx Device context
* @return boolean
* @exception java.io.IOException If an error occurs.
*/
public boolean isReadOnly(SrvSession sess, DeviceContext ctx) throws IOException
{
if(logger.isDebugEnabled())
{
logger.debug("isReadOnly");
}
return !m_transactionService.getAllowWrite();
}
/**
* Get the file information for the specified file.
*
* @param sess Server session
* @param tree Tree connection
* @param name File name/path that information is required for.
* @return File information if valid, else null
* @exception java.io.IOException The exception description.
*/
public FileInfo getFileInformation(SrvSession session, TreeConnection tree, String path) throws IOException
{
if(logger.isDebugEnabled())
{
logger.debug("getFileInformation:" + path + ", session:" + session.getUniqueId());
}
ContentContext ctx = (ContentContext) tree.getContext();
boolean readOnly = !m_transactionService.getAllowWrite();
if ( path == null || path.length() == 0)
{
path = FileName.DOS_SEPERATOR_STR;
}
String infoPath = path;
try
{
FileInfo finfo = null;
// Is the node a pseudo file ?
if(session.isPseudoFilesEnabled() && ctx.isPseudoFilesEnabled())
{
String[] paths = FileName.splitPath(path);
// lookup parent directory
NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
// Check whether we are opening a pseudo file
if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1]))
{
PseudoFile pfile = ctx.getPseudoFileOverlay().getPseudoFile(dirNodeRef, paths[1]);
if(logger.isDebugEnabled())
{
if (pfile != null)
{
logger.debug("returning psuedo file details:" + pfile);
}
else
{
logger.debug("Try to return deleted pseudo file :" + paths[1]);
}
}
if (pfile != null)
{
return pfile.getFileInfo();
}
else
{
throw new FileNotFoundException("The pseudo file was deleted");
}
}
}
// no - this is not a specially named pseudo file.
NodeRef nodeRef = getNodeForPath(tree, infoPath);
if ( nodeRef != null)
{
// Get the file information for the node
finfo = getCifsHelper().getFileInformation(nodeRef, readOnly, isLockedFilesAsOffline);
/**
* Special processing for root node
*/
if(path.equals(FileName.DOS_SEPERATOR_STR))
{
finfo.setFileName("");
}
// DEBUG
if ( logger.isDebugEnabled())
{
logger.debug("getFileInformation found nodeRef for nodeRef :"+ nodeRef + ", path: " + path);
}
// Moved to CIFS Helper
// // Set the file id from the node's DBID
// long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID));
// finfo.setFileId((int) (id & 0xFFFFFFFFL));
}
// Return the file information or null if the node ref does not exist
return finfo;
}
catch (FileNotFoundException e)
{
// Debug
if (logger.isDebugEnabled())
{
// exception not logged - cifs does lots of these
logger.debug("Get file info - file not found, " + path);
}
throw e;
}
catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("Get file info - access denied, " + path, ex);
}
// Convert to a filesystem access denied status
throw new AccessDeniedException("Get file information " + path);
}
catch (AlfrescoRuntimeException ex)
{
if ( logger.isDebugEnabled())
{
logger.debug("Get file info error" + path, ex);
}
// Convert to a general I/O exception
throw new IOException("Get file information " + path, ex);
}
}
/**
* Start a new search on the filesystem using the specified searchPath that may contain
* wildcards.
*
* @param sess Server session
* @param tree Tree connection
* @param searchPath File(s) to search for, may include wildcards.
* @param attrib Attributes of the file(s) to search for, see class SMBFileAttribute.
* @return SearchContext
* @exception java.io.FileNotFoundException If the search could not be started.
*/
public SearchContext startSearch(SrvSession session, TreeConnection tree, String searchPath, int attributes) throws FileNotFoundException
{
if(logger.isDebugEnabled())
{
logger.debug("startSearch: "+ searchPath + ", session:" + session.getUniqueId());
}
// Access the device context
ContentContext ctx = (ContentContext) tree.getContext();
try
{
String searchFileSpec = searchPath;
NodeRef searchRootNodeRef = ctx.getRootNode();
String[] paths = FileName.splitPath(searchPath);
String dotPath = paths[0];
// lookup parent directory
NodeRef dirNodeRef = getNodeForPath(tree, dotPath);
if(dirNodeRef != null)
{
searchRootNodeRef = dirNodeRef;
searchFileSpec = paths[1];
}
// Convert the all files wildcard
if ( searchFileSpec.equals( "*.*"))
{
searchFileSpec = "*";
}
// Debug
long startTime = 0L;
if ( logger.isDebugEnabled())
{
startTime = System.currentTimeMillis();
}
// Perform the search
logger.debug("Call repo to do search");
List results = getCifsHelper().getNodeRefs(searchRootNodeRef, searchFileSpec);
// Debug
if ( logger.isDebugEnabled())
{
long endTime = System.currentTimeMillis();
if (( endTime - startTime) > 500)
{
logger.debug("Search for searchPath=" + searchPath + ", searchSpec=" + searchFileSpec + ", searchRootNode=" + searchRootNodeRef + " took "
+ ( endTime - startTime) + "ms results=" + results.size());
}
}
/**
* Search pseudo files if they are enabled
*/
PseudoFileList pseudoList = null;
if(session.isPseudoFilesEnabled() && ctx.isPseudoFilesEnabled())
{
logger.debug("search pseudo files");
pseudoList = ctx.getPseudoFileOverlay().searchPseudoFiles(dirNodeRef, searchFileSpec);
}
DotDotContentSearchContext searchCtx = new DotDotContentSearchContext(getCifsHelper(), results, searchFileSpec, pseudoList, paths[0], isLockedFilesAsOffline);
FileInfo dotInfo = getCifsHelper().getFileInformation(searchRootNodeRef, false, isLockedFilesAsOffline);
if ( searchPath.equals( FileName.DOS_SEPERATOR_STR)) {
// Searching the root folder, re-use the search folder file information for the '..' pseudo entry
FileInfo dotDotInfo = new FileInfo();
dotDotInfo.copyFrom(dotInfo);
searchCtx.setDotInfo(dotInfo);
searchCtx.setDotDotInfo( dotDotInfo);
}
else
{
String[] parent = FileName.splitPath(dotPath);
NodeRef parentNodeRef = getNodeForPath(tree, parent[0]);
if(parentNodeRef != null)
{
FileInfo dotDotInfo = getCifsHelper().getFileInformation(parentNodeRef, false, isLockedFilesAsOffline);
searchCtx.setDotDotInfo(dotDotInfo);
}
// Searching a normal, non root, folder
// Need to set dot and dotdot
searchCtx.setDotInfo(dotInfo);
}
// Debug
if (logger.isDebugEnabled())
{
logger.debug("Started search: search path=" + searchPath + " attributes=" + attributes + ", ctx=" + searchCtx);
}
// TODO --
// Need to resolve the file info here so it's within the transaction boundary.
return searchCtx;
}
catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("Start search - access denied, " + searchPath);
}
// Convert to a file not found status
throw new FileNotFoundException("Start search " + searchPath);
}
catch (AlfrescoRuntimeException ex)
{
// This is an error even though we "handle" it here.
if ( logger.isErrorEnabled())
{
logger.error("Exception in Start search", ex);
}
// Convert to a file not found status
throw new FileNotFoundException("Start search " + searchPath);
}
}
/**
* Check if the specified file exists, and whether it is a file or directory.
*
*
* @param sess Server session
* @param tree Tree connection
* @param name the path of the file
* @return FileStatus (0: NotExist, 1 : FileExist, 2: DirectoryExists)
* @see FileStatus
*/
public int fileExists(SrvSession session, TreeConnection tree, String name)
{
if(logger.isDebugEnabled())
{
logger.debug("fileExists:" + name + ", session:" + session.getUniqueId());
}
ContentContext ctx = (ContentContext) tree.getContext();
int status = FileStatus.Unknown;
try
{
if(session.isPseudoFilesEnabled() && ctx.isPseudoFilesEnabled())
{
String[] paths = FileName.splitPath(name);
// lookup parent directory
NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
// Check whether we are opening a pseudo file
if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1]))
{
return FileStatus.FileExists;
}
}
// Get the file information to check if the file/folder exists
FileInfo info = getFileInformation(session, tree, name);
if (info.isDirectory())
{
if(logger.isDebugEnabled())
{
logger.debug("is directory");
}
status = FileStatus.DirectoryExists;
}
else
{
if(logger.isDebugEnabled())
{
logger.debug("is file");
}
status = FileStatus.FileExists;
}
if (logger.isDebugEnabled())
{
logger.debug("File status determined: name=" + name + " status=" + status);
}
return status;
}
catch (FileNotFoundException e)
{
if(logger.isDebugEnabled())
{
logger.debug("file does not exist");
}
status = FileStatus.NotExist;
return status;
}
catch (IOException e)
{
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("File exists error, " + name, e);
}
status = FileStatus.NotExist;
return status;
}
}
/**
* Open a file or folder - obsolete implementation.
*
* @param sess SrvSession
* @param tree TreeConnection
* @param params FileOpenParams
* @return NetworkFile
* @exception IOException
*/
public NetworkFile openFile(SrvSession session, TreeConnection tree, FileOpenParams params) throws IOException
{
// obsolete
logger.error("Obsolete method called");
throw new AlfrescoRuntimeException("obsolete method called");
}
/**
* Create a new file on the file system.
*
* @param sess Server session
* @param tree Tree connection
* @param params File create parameters
* @return NetworkFile
* @exception java.io.IOException If an error occurs.
*/
public NetworkFile createFile(SrvSession sess, final TreeConnection tree, final FileOpenParams params) throws IOException
{
// Obsolete
logger.error("Obsolete method called");
throw new AlfrescoRuntimeException("obsolete method called");
}
/**
* Create a new directory on this file system.
*
*
* @param sess Server session
* @param tree Tree connection.
* @param params Directory create parameters
* @exception java.io.IOException If an error occurs.
*/
public void createDirectory(SrvSession sess, final TreeConnection tree, final FileOpenParams params) throws IOException
{
final ContentContext ctx = (ContentContext) tree.getContext();
if (logger.isDebugEnabled())
{
logger.debug("createDirectory :" + params);
}
try
{
NodeRef dirNodeRef;
String folderName;
String path = params.getPath();
String[] paths = FileName.splitPath(path);
if (paths[0] != null && paths[0].length() > 1)
{
// lookup parent directory
dirNodeRef = getNodeForPath(tree, paths[0]);
folderName = paths[1];
}
else
{
dirNodeRef = ctx.getRootNode();
folderName = path;
}
if(dirNodeRef == null)
{
throw new IOException("Create directory parent folder not found" + params.getFullPath());
}
NodeRef nodeRef = getCifsHelper().createNode(dirNodeRef, folderName, ContentModel.TYPE_FOLDER);
if (logger.isDebugEnabled())
{
logger.debug("Created directory: path=" + params.getPath() + " file open params=" + params + " node=" + nodeRef);
}
// void return
}
catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("Create directory - access denied, " + params.getFullPath());
}
// Convert to a filesystem access denied status
throw new AccessDeniedException("Create directory " + params.getFullPath());
}
catch (AlfrescoRuntimeException ex)
{
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("Create directory error", ex);
}
// Convert to a general I/O exception
throw new IOException("Create directory " + params.getFullPath(), ex);
}
}
/**
* Delete the directory from the filesystem.
*
* The directory must be empty in order to be able to delete ity
*
* @param sess Server session
* @param tree Tree connection
* @param dir Directory name.
* @exception java.io.IOException The exception description.
*/
public void deleteDirectory(SrvSession session, TreeConnection tree, final String dir) throws IOException
{
// get the device root
if (logger.isDebugEnabled())
{
logger.debug("deleteDirectory: " + dir + ", session:" + session.getUniqueId());
}
ContentContext ctx = (ContentContext) tree.getContext();
final NodeRef deviceRootNodeRef = ctx.getRootNode();
try
{
// Get the node for the folder
NodeRef nodeRef = getCifsHelper().getNodeRef(deviceRootNodeRef, dir);
if (fileFolderService.exists(nodeRef))
{
// Check if the folder is empty
// Get pseudo files
PseudoFileList pseudoFileList = new PseudoFileList();
if (session.isPseudoFilesEnabled())
{
pseudoFileList = ctx.getPseudoFileOverlay().searchPseudoFiles(nodeRef, "*");
}
if (getCifsHelper().isFolderEmpty(nodeRef) && pseudoFileList.isEmpty())
{
// Delete the folder node
fileFolderService.delete(nodeRef);
}
else
{
throw new DirectoryNotEmptyException( dir);
}
}
// Debug
if (logger.isDebugEnabled())
{
logger.debug("Deleted directory: directory=" + dir + " node=" + nodeRef);
}
// void return
}
catch (FileNotFoundException e)
{
// Debug
if (logger.isDebugEnabled())
{
logger.debug("Delete directory - file not found, " + dir);
}
}
catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("Delete directory - access denied, " + dir);
}
// Convert to a filesystem access denied status
throw new AccessDeniedException("Delete directory, access denied :" + dir);
}
catch (AlfrescoRuntimeException ex)
{
// Debug
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
logger.debug("Delete directory", ex);
// Convert to a general I/O exception
throw new IOException("Delete directory " + dir, ex);
}
}
/**
* Flush any buffered output for the specified file.
*
* @param sess Server session
* @param tree Tree connection
* @param file Network file context.
* @exception java.io.IOException The exception description.
*/
public void flushFile(SrvSession session, TreeConnection tree, NetworkFile file) throws IOException
{
// Debug
ContentContext ctx = (ContentContext) tree.getContext();
if ( logger.isDebugEnabled())
{
logger.debug("Flush file=" + file.getFullName()+ ", session:" + session.getUniqueId());
}
// Flush the file data
file.flushFile();
}
/**
* Close the file.
*
* @param sess Server session
* @param tree Tree connection.
* @param param Network file context.
*
* @exception java.io.IOException If an error occurs.
*/
public void closeFile(SrvSession session, TreeConnection tree, final NetworkFile file) throws IOException
{
throw new AlfrescoRuntimeException("obsolete method called");
}
public void deleteFile(final SrvSession session, final TreeConnection tree, final String name) throws IOException
{
throw new AlfrescoRuntimeException("obsolete method called");
}
/**
* Delete the specified file.
*
* @param sess Server session
* @param tree Tree connection
* @param file NetworkFile
* @exception java.io.IOException The exception description.
* @return NodeRef of deletedFile
*/
public NodeRef deleteFile2(final SrvSession session, final TreeConnection tree, NodeRef rootNode, String path) throws IOException
{
// Get the device context
final ContentContext ctx = (ContentContext) tree.getContext();
if(logger.isDebugEnabled())
{
logger.debug("deleteFile:" + path + ", session:" + session.getUniqueId());
}
try
{
if(session.isPseudoFilesEnabled() && ctx.isPseudoFilesEnabled())
{
String[] paths = FileName.splitPath(path);
// lookup parent directory
NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
// Check whether we are closing a pseudo file
if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1]))
{
// pseudo delete a pseudo file
ctx.getPseudoFileOverlay().delete(dirNodeRef, paths[1]);
return null;
}
}
// Check if there is a quota manager enabled, if so then we need to save the current file size
final QuotaManager quotaMgr = ctx.getQuotaManager();
// Get the node and delete it
final NodeRef nodeRef = getNodeForPath(tree, path);
if (fileFolderService.exists(nodeRef))
{
lockKeeper.removeLock(nodeRef);
// Get the size of the file being deleted
final FileInfo fInfo = quotaMgr == null ? null : getFileInformation(session, tree, path);
if(logger.isDebugEnabled())
{
logger.debug("deleted file" + path);
}
fileFolderService.delete(nodeRef);
//TODO Needs to be post-commit
if (quotaMgr != null)
{
quotaMgr.releaseSpace(session, tree, fInfo.getFileId(), path, fInfo.getSize());
}
// Debug
if (logger.isDebugEnabled())
{
logger.debug("Deleted file: " + path + ", nodeRef=" + nodeRef);
}
// void return
return nodeRef;
}
}
catch (NodeLockedException ex)
{
if ( logger.isDebugEnabled())
{
logger.debug("Delete file - access denied (locked)", ex);
}
// Convert to a filesystem access denied status
throw new AccessDeniedException("Unable to delete " + path);
}
catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("Delete file - access denied", ex);
}
// Convert to a filesystem access denied status
throw new AccessDeniedException("Unable to delete " + path);
}
catch (IOException ex)
{
// Allow I/O Exceptions to pass through
if ( logger.isDebugEnabled())
{
logger.debug("Delete file error - pass through IO Exception", ex);
}
throw ex;
}
catch (Exception ex)
{
// Debug
if ( logger.isDebugEnabled())
{
logger.debug("Delete file error", ex);
}
// Convert to a general I/O exception
IOException ioe = new IOException("Delete file " + path);
ioe.initCause(ex);
throw ioe;
}
return null;
}
public void renameFile(final SrvSession session, final TreeConnection tree, final String oldName, final String newName)
throws IOException
{
throw new AlfrescoRuntimeException("obsolete method called");
}
/**
* Rename the specified file.
*
* @param rootNode
* @param oldName path/name of old file
* @param newName path/name of new file
* @exception java.io.IOException The exception description.
*/
public void renameFile(NodeRef rootNode, final String oldName, final String newName, boolean soft, boolean moveAsSystem)
throws IOException
{
if (logger.isDebugEnabled())
{
logger.debug("RenameFile oldName=" + oldName + ", newName=" + newName + ", soft" + soft);
}
try
{
// Get the file/folder to move
final NodeRef nodeToMoveRef = getCifsHelper().getNodeRef(rootNode, oldName);
// Check if the node is a link node
if ( nodeToMoveRef != null && nodeService.getProperty(nodeToMoveRef, ContentModel.PROP_LINK_DESTINATION) != null)
{
throw new AccessDeniedException("Cannot rename link nodes");
}
// Get the new target folder - it must be a folder
String[] splitPaths = FileName.splitPath(newName);
String[] oldPaths = FileName.splitPath(oldName);
final NodeRef targetFolderRef = getCifsHelper().getNodeRef(rootNode, splitPaths[0]);
final NodeRef sourceFolderRef = getCifsHelper().getNodeRef(rootNode, oldPaths[0]);
final String name = splitPaths[1];
// Check if this is a rename within the same folder
final boolean sameFolder = splitPaths[0].equalsIgnoreCase(oldPaths[0]);
// Check if we are renaming a folder, or the rename is to a different folder
boolean isFolder = getCifsHelper().isDirectory(nodeToMoveRef);
if ( isFolder == true || sameFolder == false)
{
// Rename or move the file/folder to another folder
if (sameFolder == true)
{
fileFolderService.rename(nodeToMoveRef, name);
if (logger.isDebugEnabled())
{
logger.debug(" Renamed " + (isFolder ? "folder" : "file") + ":" +
" Old name: " + oldName + "\n" +
" New name: " + newName + "\n" );
}
}
else
{
if (moveAsSystem)
{
if (logger.isDebugEnabled())
{
logger.debug("Run move as System for: " + oldName);
}
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork