mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-22 15:12:38 +00:00 
			
		
		
		
	125606 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2)
      125515 slanglois: MNT-16155 Update source headers - add new Copyrights for Java and JSP source files + automatic check in the build
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@125788 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
	
		
			
				
	
	
		
			4473 lines
		
	
	
		
			182 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			4473 lines
		
	
	
		
			182 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * #%L
 | |
|  * Alfresco Repository
 | |
|  * %%
 | |
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | |
|  * %%
 | |
|  * This file is part of the Alfresco software. 
 | |
|  * If the software was purchased under a paid Alfresco license, the terms of 
 | |
|  * the paid license agreement will prevail.  Otherwise, the software is 
 | |
|  * provided under the following open source license terms:
 | |
|  * 
 | |
|  * 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 <http://www.gnu.org/licenses/>.
 | |
|  * #L%
 | |
|  */
 | |
| package org.alfresco.filesys.repo;
 | |
| 
 | |
| import java.io.FileNotFoundException;
 | |
| import java.io.IOException;
 | |
| import java.io.Serializable;
 | |
| import java.net.InetAddress;
 | |
| import java.util.Arrays;
 | |
| import java.util.Collections;
 | |
| import java.util.Date;
 | |
| import java.util.HashMap;
 | |
| import java.util.LinkedList;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| import java.util.Set;
 | |
| import java.util.TreeSet;
 | |
| import java.util.concurrent.Callable;
 | |
| import java.util.regex.Pattern;
 | |
| 
 | |
| import javax.transaction.UserTransaction;
 | |
| 
 | |
| import org.alfresco.error.AlfrescoRuntimeException;
 | |
| import org.alfresco.filesys.alfresco.AlfrescoContext;
 | |
| import org.alfresco.filesys.alfresco.AlfrescoNetworkFile;
 | |
| import org.alfresco.filesys.alfresco.AlfrescoTxDiskDriver;
 | |
| 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.AccessMode;
 | |
| 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.FileAttribute;
 | |
| import org.alfresco.jlan.server.filesys.FileExistsException;
 | |
| 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.FileSharingException;
 | |
| import org.alfresco.jlan.server.filesys.FileStatus;
 | |
| import org.alfresco.jlan.server.filesys.NetworkFile;
 | |
| 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.PseudoFileInterface;
 | |
| 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.SharingMode;
 | |
| import org.alfresco.jlan.smb.WinNT;
 | |
| import org.alfresco.jlan.smb.server.SMBServer;
 | |
| import org.alfresco.jlan.smb.server.SMBSrvSession;
 | |
| import org.alfresco.jlan.util.MemorySize;
 | |
| import org.alfresco.jlan.util.WildCard;
 | |
| import org.alfresco.model.ContentModel;
 | |
| import org.alfresco.repo.action.executer.ContentMetadataExtracter;
 | |
| import org.alfresco.repo.admin.SysAdminParams;
 | |
| import org.alfresco.repo.content.MimetypeMap;
 | |
| import org.alfresco.repo.node.archive.NodeArchiveService;
 | |
| import org.alfresco.repo.policy.BehaviourFilter;
 | |
| import org.alfresco.repo.security.authentication.AuthenticationContext;
 | |
| import org.alfresco.repo.security.authentication.AuthenticationUtil;
 | |
| import org.alfresco.repo.transaction.RetryingTransactionHelper;
 | |
| import org.alfresco.service.cmr.action.Action;
 | |
| import org.alfresco.service.cmr.action.ActionService;
 | |
| import org.alfresco.service.cmr.coci.CheckOutCheckInService;
 | |
| import org.alfresco.service.cmr.dictionary.DictionaryService;
 | |
| import org.alfresco.service.cmr.lock.LockService;
 | |
| import org.alfresco.service.cmr.lock.LockType;
 | |
| import org.alfresco.service.cmr.lock.NodeLockedException;
 | |
| import org.alfresco.service.cmr.model.FileFolderService;
 | |
| import org.alfresco.service.cmr.repository.AssociationRef;
 | |
| import org.alfresco.service.cmr.repository.ContentData;
 | |
| import org.alfresco.service.cmr.repository.ContentIOException;
 | |
| import org.alfresco.service.cmr.repository.ContentService;
 | |
| import org.alfresco.service.cmr.repository.InvalidNodeRefException;
 | |
| 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.AccessPermission;
 | |
| import org.alfresco.service.cmr.security.AccessStatus;
 | |
| import org.alfresco.service.cmr.security.AuthenticationService;
 | |
| import org.alfresco.service.cmr.security.OwnableService;
 | |
| import org.alfresco.service.cmr.security.PermissionService;
 | |
| import org.alfresco.service.namespace.NamespaceService;
 | |
| import org.alfresco.service.namespace.QName;
 | |
| import org.alfresco.util.Pair;
 | |
| import org.alfresco.util.PropertyCheck;
 | |
| import org.apache.commons.logging.Log;
 | |
| import org.apache.commons.logging.LogFactory;
 | |
| import org.springframework.extensions.config.ConfigElement;
 | |
| 
 | |
| /**
 | |
|  * Content repository filesystem driver class
 | |
|  * 
 | |
|  * <p>Provides a filesystem interface for various protocols such as SMB/CIFS and FTP.
 | |
|  * 
 | |
|  * @author gkspencer
 | |
|  */
 | |
| public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInterface, FileLockingInterface, OpLockInterface, DiskSizeInterface
 | |
| {
 | |
|     // Logging
 | |
|     private static final Log logger = LogFactory.getLog(ContentDiskDriver.class);
 | |
|     
 | |
|     // 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";
 | |
|     
 | |
|     // File status values used in the file state cache
 | |
|     
 | |
|     public static final int FileUnknown     = FileStatus.Unknown;
 | |
|     public static final int FileNotExist    = FileStatus.NotExist;
 | |
|     public static final int FileExists      = FileStatus.FileExists;
 | |
|     public static final int DirectoryExists = FileStatus.DirectoryExists;
 | |
|     
 | |
|     public static final int CustomFileStatus= FileStatus.MaxStatus + 1;
 | |
|     public static final int FileRenamed     = CustomFileStatus;
 | |
|     public static final int DeleteOnClose   = CustomFileStatus + 1;
 | |
|     
 | |
|     // File state attributes
 | |
|     
 | |
|     public static final String AttrLinkNode = "ContentLinkNode";
 | |
|     public static final String CanDeleteWithoutPerms = "CanDeleteWithoutPerms";
 | |
|         
 | |
|     // List of content properties to copy during rename
 | |
|     
 | |
|     private static QName[] _copyProperties = { ContentModel.PROP_AUTHOR, ContentModel.PROP_TITLE, ContentModel.PROP_DESCRIPTION };
 | |
| 
 | |
|     // List of property namespaces to exclude from copy during rename
 | |
|     
 | |
|     private static Set<String> _excludedNamespaces = new TreeSet<String>(Arrays.asList(new String[]
 | |
|     {
 | |
|         NamespaceService.CONTENT_MODEL_1_0_URI, NamespaceService.SYSTEM_MODEL_1_0_URI
 | |
|     }));
 | |
|     
 | |
|     // Disk sizing contants
 | |
|     
 | |
|     private static final int DiskBlockSize			= 512;	// bytes per block
 | |
|     private static final long DiskAllocationUnit	= 32 * MemorySize.KILOBYTE;
 | |
|     private static final long DiskBlocksPerUnit		= DiskAllocationUnit / DiskBlockSize;
 | |
|     
 | |
|     // Disk size returned in the content store does not support free/total size
 | |
|     
 | |
|     protected static final long DiskSizeDefault		= 1 * MemorySize.TERABYTE;
 | |
|     protected static final long DiskFreeDefault		= DiskSizeDefault / 2;
 | |
| 
 | |
|     private boolean isReadOnly;
 | |
|     private boolean isLockedFilesAsOffline;
 | |
|     // pattern for detect CSV files.
 | |
|     private Pattern renameCSVShufflePattern = Pattern.compile(".*[a-f0-9]{8}+$");
 | |
|     // Services and helpers
 | |
|     
 | |
|     private CifsHelper cifsHelper;
 | |
|     private NamespaceService namespaceService;
 | |
|     private NodeService nodeService;
 | |
|     private CheckOutCheckInService checkOutCheckInService;
 | |
|     private SearchService searchService;
 | |
|     private ContentService contentService;
 | |
|     private MimetypeService mimetypeService;
 | |
|     private PermissionService permissionService;
 | |
|     private FileFolderService fileFolderService;
 | |
|     private NodeArchiveService nodeArchiveService;
 | |
|     private LockService lockService;
 | |
|     private DictionaryService dictionaryService;
 | |
|     private OwnableService ownableService;
 | |
|     private ActionService actionService;
 | |
|     
 | |
|     private AuthenticationContext authContext;
 | |
|     private AuthenticationService authService;
 | |
|     private SysAdminParams sysAdminParams;
 | |
| 
 | |
|     private BehaviourFilter policyBehaviourFilter;
 | |
|     private NodeMonitorFactory m_nodeMonitorFactory;
 | |
|     
 | |
|     
 | |
|     /**
 | |
|      * Class constructor
 | |
|      * 
 | |
|      * @param cifsHelper to connect to the repository services
 | |
|      */
 | |
|     public ContentDiskDriver(CifsHelper cifsHelper)
 | |
|     {
 | |
|         this.cifsHelper = cifsHelper;
 | |
|     }
 | |
|     
 | |
|     public void init()
 | |
|     {
 | |
|         PropertyCheck.mandatory(this, "nodeService", nodeService);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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          service to provide information on check-in and check-out
 | |
|      */
 | |
|     public CheckOutCheckInService getCheckOutCheckInService()
 | |
|     {
 | |
|         return checkOutCheckInService;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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 node archive service
 | |
|      */
 | |
|     public final NodeArchiveService getNodeArchiveService() {
 | |
|     	return nodeArchiveService;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * 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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return the dictionary service
 | |
|      * 
 | |
|      * @return DictionaryService
 | |
|      */
 | |
|     public final DictionaryService getDictionaryService() {
 | |
|         return dictionaryService;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get the ownable service
 | |
|      * 
 | |
|      * @return OwnableService
 | |
|      */
 | |
|     public final OwnableService getOwnableService() {
 | |
|         return ownableService;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * @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 checkOutCheckInService            used to check for checked out nodes
 | |
|      */
 | |
|     public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService)
 | |
|     {
 | |
|         this.checkOutCheckInService = checkOutCheckInService;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @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;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Sets the sys admin params.
 | |
|      * 
 | |
|      * @param sysAdminParams
 | |
|      *            the sys admin params
 | |
|      */
 | |
|     public void setSysAdminParams(SysAdminParams sysAdminParams)
 | |
|     {
 | |
|         this.sysAdminParams = sysAdminParams;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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 node archive service
 | |
|      * 
 | |
|      * @param nodeArchiveService nodeArchiveService
 | |
|      */
 | |
|     public void setNodeArchiveService(NodeArchiveService nodeArchiveService) {
 | |
|     	this.nodeArchiveService = nodeArchiveService;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * 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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Set the dictionary service
 | |
|      * 
 | |
|      * @param dictionaryService DictionaryService
 | |
|      */
 | |
|     public void setDictionaryService(DictionaryService dictionaryService) {
 | |
|         this.dictionaryService = dictionaryService;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Set the ownable servive
 | |
|      * 
 | |
|      * @param ownableService OwnableService
 | |
|      */
 | |
|     public void setOwnableService(OwnableService ownableService) {
 | |
|         this.ownableService = ownableService;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Set the regular expression that will be applied to CSV files during renames.
 | |
|      * <b>MNT-211</b>
 | |
|      * 
 | |
|      * @param renameCSVShufflePattern      a regular expression CSV filename match
 | |
|      */
 | |
|     public void setRenameCSVShufflePattern(Pattern renameCSVShufflePattern)
 | |
|     {
 | |
|         this.renameCSVShufflePattern = renameCSVShufflePattern;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * 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.
 | |
|      * <p>
 | |
|      * WARNING: side effect, may commit or roll back current user transaction context.
 | |
|      * 
 | |
|      * @param deviceName The name of the device
 | |
|      * @param cfg ConfigElement the configuration of the device context.
 | |
|      * @return DeviceContext
 | |
|      * @exception DeviceContextException
 | |
|      */
 | |
|     //  MER TODO - transaction handling in registerContext needs changing
 | |
|     public DeviceContext createContext(String deviceName, ConfigElement cfg) throws DeviceContextException
 | |
|     {
 | |
|         ContentContext context = null;
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             // Get the store
 | |
|             ConfigElement storeElement = cfg.getChild(KEY_STORE);
 | |
|             if (storeElement == null || storeElement.getValue() == null || storeElement.getValue().length() == 0)
 | |
|             {
 | |
|                 throw new DeviceContextException("Device missing init value: " + KEY_STORE);
 | |
|             }
 | |
|             String storeValue = storeElement.getValue();
 | |
| 
 | |
|             // Get the root path
 | |
|             
 | |
|             ConfigElement rootPathElement = cfg.getChild(KEY_ROOT_PATH);
 | |
|             if (rootPathElement == null || rootPathElement.getValue() == null || rootPathElement.getValue().length() == 0)
 | |
|             {
 | |
|                 throw new DeviceContextException("Device missing init value: " + KEY_ROOT_PATH);
 | |
|             }
 | |
|             String rootPath = rootPathElement.getValue();
 | |
|             
 | |
| 
 | |
|             // Create the context
 | |
|             
 | |
|             context = new ContentContext();
 | |
|             context.setDeviceName(deviceName);
 | |
|             context.setStoreName(storeValue);
 | |
|             context.setRootPath(rootPath);
 | |
|             context.setSysAdminParams(this.sysAdminParams);
 | |
| 
 | |
|             // Check if a relative path has been specified
 | |
|             
 | |
|             ConfigElement relativePathElement = cfg.getChild(KEY_RELATIVE_PATH);
 | |
|             
 | |
|             if ( relativePathElement != null)
 | |
|             {
 | |
|                 // Make sure the path is in CIFS format
 | |
|                 
 | |
|                 String relPath = relativePathElement.getValue().replace( '/', FileName.DOS_SEPERATOR);
 | |
|                 context.setRelativePath(relPath);
 | |
|             }
 | |
|         }
 | |
|         /* 
 | |
|          * MER - I changed the code below - resulted in a NPE anyway 
 | |
|          * lower down
 | |
|          */
 | |
|         catch (DeviceContextException ex)
 | |
|         {
 | |
|             logger.error("Error during create context", ex);
 | |
|             throw ex;
 | |
|             
 | |
|         }
 | |
|           
 | |
|         // Check if URL link files are enabled
 | |
|         
 | |
|         ConfigElement urlFileElem = cfg.getChild( "urlFile");
 | |
|         if ( urlFileElem != null)
 | |
|         {
 | |
|             // Get the pseudo file name and web prefix path
 | |
|             
 | |
|             ConfigElement pseudoName = urlFileElem.getChild( "filename");
 | |
|             
 | |
|             if ( pseudoName != null)
 | |
|             {
 | |
|                 context.setURLFileName(pseudoName.getValue());
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Check if locked files should be marked as offline
 | |
|         
 | |
|         ConfigElement offlineFiles = cfg.getChild( "offlineFiles");
 | |
|         if ( offlineFiles != null)
 | |
|         {
 | |
|             context.setOfflineFiles(true);
 | |
|         }
 | |
|         
 | |
|         // Install the node service monitor
 | |
|         
 | |
|         // MER 01/03/2011 - I think one of these is the "wrong way round"
 | |
|         
 | |
|         if ( cfg.getChild("disableNodeMonitor") == null) {
 | |
|         	
 | |
|         	// Create the node monitor
 | |
|             context.setDisableNodeMonitor(true);
 | |
|         }
 | |
|         
 | |
|         // Check if oplocks are enabled, if so then enable oplocks in the lock manager
 | |
|         
 | |
|         if ( cfg.getChild("disableOplocks") != null) {
 | |
|         	context.setDisableOplocks( true);
 | |
|         }
 | |
|         
 | |
|         // Register the device context
 | |
|         
 | |
|         registerContext(context);
 | |
|         
 | |
|         // Return the context for this shared filesystem
 | |
|         
 | |
|         return context;
 | |
|     }
 | |
| 
 | |
|     
 | |
|     /**
 | |
|      * Registers a device context object for this instance
 | |
|      * of the shared device. The same DeviceInterface implementation may be used for multiple
 | |
|      * shares.
 | |
|      * 
 | |
|      * WARNING: side effect, will commit or roll back current user transaction context.
 | |
|      * 
 | |
|      * @param ctx the context
 | |
|      * @exception DeviceContextException
 | |
|      */
 | |
|     //  MER TODO - transaction handling in registerContext needs changing
 | |
|     @Override
 | |
|     public void registerContext(DeviceContext ctx) throws DeviceContextException
 | |
|     {
 | |
|         super.registerContext(ctx);
 | |
| 
 | |
|         ContentContext context = (ContentContext)ctx;
 | |
| 
 | |
|         // Wrap the initialization in a transaction
 | |
|         
 | |
|         UserTransaction tx = getTransactionService().getUserTransaction(true);
 | |
| 
 | |
|         try
 | |
|         {
 | |
|             // Use the system user as the authenticated context for the filesystem initialization
 | |
|             
 | |
|             AuthenticationUtil.pushAuthentication();
 | |
|             AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
 | |
|             
 | |
|             // Start the transaction
 | |
|             
 | |
|             if ( tx != null)
 | |
|                 tx.begin();
 | |
|             
 | |
|             // Get the store
 | |
|             String storeValue = context.getStoreName();
 | |
|             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);
 | |
|             
 | |
|             // Get the root path
 | |
|             String rootPath = context.getRootPath();
 | |
|             
 | |
|             // Find the root node for this device
 | |
|             
 | |
|             List<NodeRef> 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 a node
 | |
|                 
 | |
|                 rootNodeRef = nodeRefs.get(0);
 | |
|             }
 | |
| 
 | |
|             // Check if a relative path has been specified
 | |
|             
 | |
|             String relPath = context.getRelativePath();
 | |
|             
 | |
|             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");
 | |
|             }
 | |
|             
 | |
|             // Commit the transaction
 | |
|             
 | |
|             // MER 16/03/2010 - Why is this transaction management here?
 | |
|             tx.commit();
 | |
|             tx = null;
 | |
| 
 | |
|             // Record the root node ref
 | |
|             context.setRootNodeRef(rootNodeRef);
 | |
|         }
 | |
|         catch (Exception ex)
 | |
|         {
 | |
|             logger.error("Error during create context", ex);
 | |
|             
 | |
|             // MER BUGBUG Exception swallowed - will result in null pointer errors at best.
 | |
|             throw new DeviceContextException("unable to register context", ex);
 | |
|             // MER END
 | |
|         }
 | |
|         finally
 | |
|         {
 | |
|             // Restore authentication context
 | |
|             
 | |
|             AuthenticationUtil.popAuthentication();
 | |
|             
 | |
|             // If there is an active transaction then roll it back
 | |
|             
 | |
|             if ( tx != null)
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     tx.rollback();
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     logger.warn("Failed to rollback transaction", ex);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Check if locked files should be marked as offline
 | |
|         if ( context.getOfflineFiles() )
 | |
|         {
 | |
|             // Enable marking locked files as offline
 | |
|             isLockedFilesAsOffline = true;
 | |
|             
 | |
|             // Logging
 | |
|             
 | |
|             logger.info("Locked files will be marked as offline");
 | |
|         }
 | |
|                 
 | |
|         // Enable file state caching
 | |
|         
 | |
| //        context.enableStateCache(serverConfig, true);
 | |
|         
 | |
|         // Install the node service monitor
 | |
|         
 | |
|         if ( !context.getDisableNodeMonitor() && m_nodeMonitorFactory != null) {
 | |
|             
 | |
|             // Create the node monitor
 | |
| 
 | |
|             NodeMonitor nodeMonitor = m_nodeMonitorFactory.createNodeMonitor(context);
 | |
|             context.setNodeMonitor( nodeMonitor);
 | |
|         }
 | |
|         
 | |
|         // Check if oplocks are enabled
 | |
|         
 | |
|         if ( context.getDisableOplocks() == true)
 | |
|         	logger.warn("Oplock support disabled for filesystem " + ctx.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);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Check if pseudo file support is enabled
 | |
|      * 
 | |
|      * @param context ContentContext
 | |
|      * @return boolean
 | |
|      */
 | |
|     public final boolean hasPseudoFileInterface(ContentContext context)
 | |
|     {
 | |
|     	return context.hasPseudoFileInterface();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Return the pseudo file support implementation
 | |
|      *
 | |
|      * @param context ContentContext
 | |
|      * @return PseudoFileInterface
 | |
|      */
 | |
|     public final PseudoFileInterface getPseudoFileInterface(ContentContext context)
 | |
|     {
 | |
|         return context.getPseudoFileInterface();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * 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
 | |
|     {
 | |
|         return isReadOnly;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get the file information for the specified file.
 | |
|      * 
 | |
|      * @param session Server session
 | |
|      * @param tree Tree connection
 | |
|      * @param path 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);
 | |
|         }
 | |
|     	// Start a transaction
 | |
|     	
 | |
|     	beginReadTransaction( session);
 | |
|         
 | |
|         // Get the device root
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         NodeRef infoParentNodeRef = ctx.getRootNode();
 | |
|         
 | |
|         if ( path == null || path.length() == 0)
 | |
|             path = FileName.DOS_SEPERATOR_STR;
 | |
|         
 | |
|         String infoPath = path;
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             // Check if the path is to a pseudo file
 | |
| 
 | |
|             FileInfo finfo = null;
 | |
|             
 | |
|             if ( hasPseudoFileInterface(ctx))
 | |
|             {
 | |
|             	// Make sure the parent folder has a file state, and the path exists
 | |
|         		
 | |
|                 String[] paths = FileName.splitPath( path);
 | |
|                 FileState fstate = ctx.getStateCache().findFileState( paths[0]);
 | |
|                 
 | |
|                 if ( fstate == null)
 | |
|                 {
 | |
|                 	NodeRef nodeRef = getNodeForPath(tree, paths[0]);
 | |
|                         
 | |
|                     if ( nodeRef != null)
 | |
|                     {
 | |
|                         // Get the file information for the node
 | |
|                             
 | |
|                         finfo = cifsHelper.getFileInformation(nodeRef, isReadOnly, isLockedFilesAsOffline);
 | |
|                     }
 | |
|                         
 | |
|               		// Create the file state
 | |
|                 		
 | |
|                		fstate = ctx.getStateCache().findFileState( paths[0], true);
 | |
|                 		
 | |
|                		fstate.setFileStatus( DirectoryExists);
 | |
|    	                fstate.setFilesystemObject( nodeRef);
 | |
|                 		
 | |
|                		// Add pseudo files to the folder
 | |
|                 		
 | |
|                		getPseudoFileInterface( ctx).addPseudoFilesToFolder( session, tree, paths[0]);
 | |
|                 		
 | |
|                		// Debug
 | |
|                 		
 | |
|               		if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                			logger.debug( "Added file state for pseudo files folder (getinfo) - " + paths[0]);
 | |
|                 }
 | |
|                 else if ( fstate.hasPseudoFiles() == false)
 | |
|                 {
 | |
|             		// Make sure the file state has the node ref
 | |
|             		
 | |
|             		if ( fstate.hasFilesystemObject() == false)
 | |
|             		{
 | |
|     	                // Get the node for the folder path
 | |
|     	                
 | |
|     	                fstate.setFilesystemObject( getNodeForPath( tree, paths[0]));
 | |
|             		}
 | |
|             		
 | |
|                 	// Add pseudo files for the parent folder
 | |
|                 	
 | |
|             		getPseudoFileInterface( ctx).addPseudoFilesToFolder( session, tree, paths[0]);
 | |
|             		
 | |
|             		// Debug
 | |
|             		
 | |
|             		if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|             			logger.debug( "Added pseudo files for folder (exists) - " + paths[0]);
 | |
|                 }
 | |
|             	
 | |
|             	
 | |
|                 // Get the pseudo file
 | |
|                 
 | |
|                 PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( session, tree, path);
 | |
|                 if ( pfile != null)
 | |
|                 {
 | |
|                     // DEBUG
 | |
|                     if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                         logger.debug("getInfo using pseudo file info for " + path);
 | |
|                     
 | |
|                     FileInfo pseudoFileInfo = pfile.getFileInfo();
 | |
|                     if (isReadOnly)
 | |
|                     {
 | |
|                         int attr = pseudoFileInfo.getFileAttributes();
 | |
|                         if (( attr & FileAttribute.ReadOnly) == 0)
 | |
|                         {
 | |
|                             attr += FileAttribute.ReadOnly;
 | |
|                             pseudoFileInfo.setFileAttributes(attr);
 | |
|                         }
 | |
|                     }
 | |
|                     return pfile.getFileInfo();
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Get the node ref for the path, chances are there is a file state in the cache
 | |
|             
 | |
|             NodeRef nodeRef = getNodeForPath(tree, infoPath);
 | |
|             
 | |
|             if ( nodeRef != null)
 | |
|             {
 | |
|                 // Get the file information for the node
 | |
|                 
 | |
|                 finfo = cifsHelper.getFileInformation(nodeRef, isReadOnly, isLockedFilesAsOffline);
 | |
| 
 | |
|                 // DEBUG
 | |
|                 
 | |
|                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                     logger.debug("getInfo using cached noderef for path " + path);
 | |
|             }
 | |
|             
 | |
|             // If the required node was not in the state cache, the parent folder node might be
 | |
|             
 | |
|             
 | |
|             if ( finfo == null)
 | |
|             {
 | |
|                 String[] paths = FileName.splitPath( path);
 | |
|                 
 | |
|                 if ( paths[0] != null && paths[0].length() > 1)
 | |
|                 {
 | |
|                     // Find the node ref for the folder being searched
 | |
|                     
 | |
|                     nodeRef = getNodeForPath(tree, paths[0]);
 | |
|                     
 | |
|                     if ( nodeRef != null)
 | |
|                     {
 | |
|                         infoParentNodeRef = nodeRef;
 | |
|                         infoPath          = paths[1];
 | |
|                         
 | |
|                         // DEBUG
 | |
|                         
 | |
|                         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                             logger.debug("getInfo using cached noderef for parent " + path);
 | |
|                     }
 | |
|                 }
 | |
|             
 | |
|                 // Access the repository to get the file information
 | |
|                 
 | |
|                 finfo = cifsHelper.getFileInformation(infoParentNodeRef, infoPath, isReadOnly, isLockedFilesAsOffline);
 | |
|                 
 | |
|                 // DEBUG
 | |
|                 
 | |
|                 if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                     logger.debug("Getting file information: path=" + path + " file info: " + finfo);
 | |
|             }
 | |
| 
 | |
|             // Set the file id for the file using the relative path
 | |
|             
 | |
|             if ( finfo != null) {
 | |
|             	
 | |
|             	// Set the file id
 | |
|   
 | |
|                 long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID));
 | |
|                 finfo.setFileId((int) (id & 0xFFFFFFFFL));
 | |
| 
 | |
|             	// Copy cached file details, if available
 | |
|             	
 | |
|                 FileState fstate = getStateForPath(tree, infoPath);
 | |
|                 if ( fstate != null) {
 | |
|                     
 | |
|                     // Copy cached timestamps
 | |
|                     
 | |
| //                	if ( fstate.hasAccessDateTime())
 | |
| //                		finfo.setAccessDateTime(fstate.getAccessDateTime());
 | |
|                 	if ( fstate.hasChangeDateTime())
 | |
|                 		finfo.setChangeDateTime(fstate.getChangeDateTime());
 | |
|                 	if ( fstate.hasModifyDateTime())
 | |
|                 		finfo.setModifyDateTime(fstate.getModifyDateTime());
 | |
|                 	
 | |
|                 	// File allocation size
 | |
|                 	
 | |
|                 	if ( fstate.hasAllocationSize() && fstate.getAllocationSize() > finfo.getSize())
 | |
|                 	    finfo.setAllocationSize( fstate.getAllocationSize());
 | |
|                 }
 | |
|                 else {
 | |
|                 	
 | |
|                 	// Create a file state for the file/folder
 | |
|                 	
 | |
|                 	fstate = ctx.getStateCache().findFileState( path, true);
 | |
|                 	if ( finfo.isDirectory())
 | |
|                 	    fstate.setFileStatus( DirectoryExists);
 | |
|                 	else
 | |
|                 	    fstate.setFileStatus( FileExists);
 | |
|                 	fstate.setFilesystemObject( nodeRef);
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Return the file information
 | |
|             
 | |
|             return finfo;
 | |
|         }
 | |
|         catch (FileNotFoundException e)
 | |
|         {
 | |
|             // Debug
 | |
|         	
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                 logger.debug("Get file info - file not found, " + path);
 | |
|             throw e;
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                 logger.debug("Get file info - access denied, " + path);
 | |
|             
 | |
|             // Convert to a filesystem access denied status
 | |
|             
 | |
|             throw new AccessDeniedException("Get file information " + path);
 | |
|         }
 | |
|         catch (RuntimeException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                 logger.debug("Get file info error", ex);
 | |
|             
 | |
|             // Convert to a general I/O exception
 | |
|             
 | |
|             throw new IOException("Get file information " + path);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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 attributes 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 sess, TreeConnection tree, String searchPath, int attributes) throws FileNotFoundException
 | |
|     {
 | |
|         // Access the device context
 | |
|         if(logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("startSearch: "+ searchPath);
 | |
|         }
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
| 
 | |
|         try
 | |
|         {
 | |
|             String searchFileSpec = searchPath;
 | |
|             NodeRef searchRootNodeRef = ctx.getRootNode();
 | |
|             FileState searchFolderState = null;
 | |
|             
 | |
|             // Create the transaction
 | |
|             
 | |
|             beginReadTransaction( sess);
 | |
|             
 | |
|             // If the state table is available see if we can speed up the search using either cached
 | |
|             // file information or find the folder node to be searched without having to walk the path
 | |
| 
 | |
|             String[] paths = FileName.splitPath(searchPath);
 | |
|             
 | |
|             if ( ctx.hasStateCache())
 | |
|             {
 | |
|                 // See if the folder to be searched has a file state, we can avoid having to walk the path
 | |
|                 
 | |
|                 if ( paths[0] != null && paths[0].length() >= 1)
 | |
|                 {
 | |
|                     // Find the node ref for the folder being searched
 | |
|                     
 | |
|                     NodeRef nodeRef = getNodeForPath(tree, paths[0]);
 | |
|                     
 | |
|                     // Get the file state for the folder being searched
 | |
|                     
 | |
|                     searchFolderState = getStateForPath(tree, paths[0]);
 | |
|                     if ( searchFolderState == null)
 | |
|                     {
 | |
|                         // Create a file state for the folder
 | |
| 
 | |
|                         searchFolderState = ctx.getStateCache().findFileState( paths[0], true);
 | |
|                     }
 | |
|                     
 | |
|                     // Make sure the associated node is set
 | |
|                     
 | |
|                     if ( searchFolderState.hasFilesystemObject() == false)
 | |
|                     {
 | |
|                         // Set the associated node for the folder
 | |
|                         
 | |
|                         searchFolderState.setFilesystemObject( nodeRef);
 | |
|                     }
 | |
|                     
 | |
|                     // Add pseudo files to the folder being searched
 | |
| 
 | |
|                     if ( hasPseudoFileInterface(ctx))
 | |
|                         getPseudoFileInterface(ctx).addPseudoFilesToFolder( sess, tree, paths[0]);
 | |
| 
 | |
|                     // Set the search node and file spec
 | |
|                     
 | |
|                     if ( nodeRef != null)
 | |
|                     {
 | |
|                         searchRootNodeRef = nodeRef;
 | |
|                         searchFileSpec    = paths[1];
 | |
|                         
 | |
|                         // DEBUG
 | |
|                         
 | |
|                         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH))
 | |
|                             logger.debug("Search using cached noderef for path " + searchPath);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Convert the all files wildcard
 | |
|             
 | |
|             if ( searchFileSpec.equals( "*.*"))
 | |
|             	searchFileSpec = "*";
 | |
|             
 | |
|             // Debug
 | |
|             
 | |
|             long startTime = 0L;
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH))
 | |
|             	startTime = System.currentTimeMillis();
 | |
|             
 | |
|             // Perform the search
 | |
|             
 | |
|             List<NodeRef> results = cifsHelper.getNodeRefs(searchRootNodeRef, searchFileSpec);
 | |
| 
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH)) {
 | |
|             	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());
 | |
|             }
 | |
|             
 | |
|             // Check if there are any pseudo files for the folder being searched, for CIFS only
 | |
|             
 | |
|             PseudoFileList pseudoList = null;
 | |
|             
 | |
|             if ( sess instanceof SMBSrvSession && searchFolderState != null && searchFolderState.hasPseudoFiles())
 | |
|             {
 | |
|                 // If it is a wildcard search use all pseudo files
 | |
|                 
 | |
|                 if ( WildCard.containsWildcards(searchFileSpec))
 | |
|                 {
 | |
|                     // Get the list of pseudo files for the search path
 | |
|                     
 | |
|                     pseudoList = searchFolderState.getPseudoFileList();
 | |
|                     
 | |
|                     // Check if the wildcard is for all files or a subset
 | |
|                    
 | |
|                     if ( searchFileSpec.equals( "*") == false && pseudoList != null && pseudoList.numberOfFiles() > 0)
 | |
|                     {
 | |
|                         // Generate a subset of pseudo files that match the wildcard search pattern
 | |
|                         
 | |
|                         WildCard wildCard = new WildCard( searchFileSpec, false);
 | |
|                         PseudoFileList filterList = null;
 | |
|                         
 | |
|                         for ( int i = 0; i < pseudoList.numberOfFiles(); i++)
 | |
|                         {
 | |
|                             PseudoFile pseudoFile = pseudoList.getFileAt( i);
 | |
|                             if ( wildCard.matchesPattern( pseudoFile.getFileName()))
 | |
|                             {
 | |
|                                 // Add the pseudo file to the filtered list
 | |
|                                 
 | |
|                                 if ( filterList == null)
 | |
|                                     filterList = new PseudoFileList();
 | |
|                                 filterList.addFile( pseudoFile);
 | |
|                             }
 | |
|                         }
 | |
|                         
 | |
|                         // Use the filtered pseudo file list, or null if there were no matches
 | |
|                         
 | |
|                         pseudoList = filterList;
 | |
|                     }
 | |
|                 }
 | |
|                 else if ( results == null || results.size() == 0)
 | |
|                 {
 | |
|                     // Check if the required file is in the pseudo file list
 | |
|                     
 | |
|                     String fname = paths[1];
 | |
|                     
 | |
|                     if ( fname != null)
 | |
|                     {
 | |
|                         // Search for a matching pseudo file
 | |
|                         
 | |
|                         PseudoFile pfile = searchFolderState.getPseudoFileList().findFile( fname, true);
 | |
|                         if ( pfile != null)
 | |
|                         {
 | |
|                             // Create a file list with the required file
 | |
|                             
 | |
|                             pseudoList = new PseudoFileList();
 | |
|                             pseudoList.addFile( pfile);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Build the search context to store the results, use the cache lookup search for wildcard searches
 | |
|             
 | |
|             SearchContext searchCtx = null;
 | |
|             
 | |
|             if ( searchFileSpec.equals( "*"))
 | |
|             {
 | |
|             	// Use a cache lookup search context 
 | |
|                                                                                                                                                                                
 | |
|             	CacheLookupSearchContext cacheContext = new CacheLookupSearchContext(cifsHelper, results, searchFileSpec, pseudoList, paths[0], ctx.getStateCache(), isLockedFilesAsOffline);
 | |
|             	searchCtx = cacheContext;
 | |
|             	
 | |
|             	// Set the '.' and '..' pseudo file entry details
 | |
|             	
 | |
|             	if ( searchFolderState != null && searchFolderState.hasFilesystemObject())
 | |
|             	{
 | |
|             		// Get the '.' pseudo entry file details
 | |
|             	
 | |
|             		FileInfo finfo = cifsHelper.getFileInformation((NodeRef) searchFolderState.getFilesystemObject(), isReadOnly, isLockedFilesAsOffline);
 | |
|             		
 | |
|             		// Blend in any cached timestamps
 | |
|             		
 | |
|             		if ( searchFolderState != null) {
 | |
|             			if ( searchFolderState.hasAccessDateTime())
 | |
|             				finfo.setAccessDateTime( searchFolderState.getAccessDateTime());
 | |
|             			
 | |
|             			if ( searchFolderState.hasChangeDateTime())
 | |
|             				finfo.setChangeDateTime( searchFolderState.getChangeDateTime());
 | |
|             			
 | |
|             			if ( searchFolderState.hasModifyDateTime())
 | |
|             				finfo.setModifyDateTime( searchFolderState.getModifyDateTime());
 | |
|             		}
 | |
|             		
 | |
|             		// Set the '.' pseudo entry details
 | |
|             		
 | |
|             		cacheContext.setDotInfo( finfo);
 | |
|             		
 | |
|             		// Check if the search folder has a parent, if we are at the root of the filesystem then re-use
 | |
|             		// the file information
 | |
|             		
 | |
|             		if ( searchFolderState.getPath().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( finfo);
 | |
|             			cacheContext.setDotDotInfo( dotDotInfo);
 | |
|             		}
 | |
|             		else {
 | |
|             			
 | |
|             			// Get the parent folder path
 | |
|             			
 | |
|             			String parentPath = searchFolderState.getPath();
 | |
|             			if ( parentPath.endsWith( FileName.DOS_SEPERATOR_STR) && parentPath.length() > 1)
 | |
|             				parentPath = parentPath.substring(0, parentPath.length() - 1);
 | |
|             			
 | |
|             			int pos = parentPath.lastIndexOf( FileName.DOS_SEPERATOR_STR);
 | |
|             			if ( pos != -1)
 | |
|             				parentPath = parentPath.substring(0, pos + 1);
 | |
|             			
 | |
|             			// Get the file state for the parent path, if available
 | |
|             			
 | |
|             			FileState parentState = ctx.getStateCache().findFileState( parentPath);
 | |
|             			NodeRef parentNode = null;
 | |
|             			
 | |
|             			if ( parentState != null)
 | |
|             				parentNode = (NodeRef) parentState.getFilesystemObject();
 | |
|             			
 | |
|             			if ( parentState == null || parentNode == null)
 | |
|             				parentNode = getNodeForPath( tree, parentPath);
 | |
| 
 | |
|             			// Get the file information for the parent folder
 | |
|             			
 | |
|             			finfo = cifsHelper.getFileInformation( parentNode, isReadOnly, isLockedFilesAsOffline);
 | |
|             			
 | |
|             			// Blend in any cached timestamps
 | |
|             			
 | |
|                 		if ( parentState != null) {
 | |
|                 			if ( parentState.hasAccessDateTime())
 | |
|                 				finfo.setAccessDateTime( parentState.getAccessDateTime());
 | |
|                 			
 | |
|                 			if ( parentState.hasChangeDateTime())
 | |
|                 				finfo.setChangeDateTime( parentState.getChangeDateTime());
 | |
|                 			
 | |
|                 			if ( parentState.hasModifyDateTime())
 | |
|                 				finfo.setModifyDateTime( parentState.getModifyDateTime());
 | |
|                 		}
 | |
| 
 | |
|                 		// Set the '..' pseudo entry details
 | |
|                 		
 | |
|             			cacheContext.setDotDotInfo( finfo);
 | |
|             		}
 | |
|            		}
 | |
|             }
 | |
|             else {
 | |
|             	if ( ctx.hasStateCache())
 | |
|                 	searchCtx = new CacheLookupSearchContext(cifsHelper, results, searchFileSpec, pseudoList, paths[0], ctx.getStateCache(), isLockedFilesAsOffline);
 | |
|             	else
 | |
|                 	searchCtx = new ContentSearchContext(cifsHelper, results, searchFileSpec, pseudoList, paths[0], isLockedFilesAsOffline);
 | |
|             }
 | |
|             
 | |
|             // Debug
 | |
|             
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH))
 | |
|                 logger.debug("Started search: search path=" + searchPath + " attributes=" + attributes + ", ctx=" + searchCtx);
 | |
|             
 | |
|             // Return the search context
 | |
|             
 | |
|             return searchCtx;
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH))
 | |
|                 logger.debug("Start search - access denied, " + searchPath);
 | |
|             
 | |
|             // Convert to a file not found status
 | |
|             
 | |
|             throw new FileNotFoundException("Start search " + searchPath);
 | |
|         }
 | |
|         catch (RuntimeException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH))
 | |
|                 logger.debug("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.
 | |
|      * 
 | |
|      * <p>
 | |
|      * WARNING: side effect, commit or roll back current user transaction context.  Current transaction becomes read only.
 | |
|      * 
 | |
|      * @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 sess, TreeConnection tree, String name)
 | |
|     {
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         int status = FileStatus.Unknown;
 | |
|         FileState fstate = null;
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             // Check for a cached file state
 | |
|             
 | |
|             if ( ctx.hasStateCache())
 | |
|                 fstate = ctx.getStateCache().findFileState(name, true);
 | |
|             
 | |
|             if ( fstate != null && fstate.getFileStatus() != FileUnknown)
 | |
|             {
 | |
|                 status = fstate.getFileStatus();
 | |
|                 if ( status >= CustomFileStatus)
 | |
|                     status = FileNotExist;
 | |
| 
 | |
|                 // DEBUG
 | |
|                 
 | |
|                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                     logger.debug("Cache hit - fileExists() " + name + ", sts=" + status);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // Check if pseudo files are enabled
 | |
| 
 | |
|                 if ( hasPseudoFileInterface(ctx))
 | |
|                 {
 | |
|                 	// Check if the file name is a pseudo file name
 | |
|                 	
 | |
|                 	if ( getPseudoFileInterface( ctx).isPseudoFile(sess, tree, name)) {
 | |
|                 		
 | |
|     	            	// Make sure the parent folder has a file state, and the path exists
 | |
|                 		
 | |
|     	                String[] paths = FileName.splitPath( name);
 | |
|     	                fstate = ctx.getStateCache().findFileState( paths[0]);
 | |
|     	                
 | |
|     	                if ( fstate == null) {
 | |
| 
 | |
|     	                	// Check if the path exists
 | |
|     	                	
 | |
|     	                	if ( fileExists( sess, tree, paths[0]) == FileStatus.DirectoryExists)
 | |
|     	                	{
 | |
|     	                		// Create the file state
 | |
|     	                		
 | |
|     	                		fstate = ctx.getStateCache().findFileState( paths[0], true);
 | |
|     	                		
 | |
|     	                		fstate.setFileStatus( DirectoryExists);
 | |
|     	                		
 | |
|    	        	                // Get the node for the folder path
 | |
|     	        	                
 | |
|     	                		beginReadTransaction( sess);
 | |
|    	        	                fstate.setFilesystemObject( getNodeForPath( tree, paths[0]));
 | |
|     	                		
 | |
|     	                		// Add pseudo files to the folder
 | |
|     	                		
 | |
|     	                		getPseudoFileInterface( ctx).addPseudoFilesToFolder( sess, tree, paths[0]);
 | |
|     	                		
 | |
|     	                		// Debug
 | |
|     	                		
 | |
|     	                		if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO))
 | |
|     	                			logger.debug( "Added file state for pseudo files folder (exists) - " + paths[0]);
 | |
|     	                	}
 | |
|     	                }
 | |
|     	                else if ( fstate.hasPseudoFiles() == false)
 | |
|     	                {
 | |
| 	                		// Make sure the file state has the node ref
 | |
| 	                		
 | |
| 	                		if ( fstate.hasFilesystemObject() == false)
 | |
| 	                		{
 | |
| 	        	            	// Create the transaction
 | |
| 	        	                
 | |
| 	                			beginReadTransaction( sess);
 | |
| 	        	            
 | |
| 	        	                // Get the node for the folder path
 | |
| 	        	                
 | |
| 	        	                fstate.setFilesystemObject( getNodeForPath( tree, paths[0]));
 | |
| 	                		}
 | |
| 	                		
 | |
|     	                	// Add pseudo files for the parent folder
 | |
|     	                	
 | |
|                     		getPseudoFileInterface( ctx).addPseudoFilesToFolder( sess, tree, paths[0]);
 | |
|                     		
 | |
|                     		// Debug
 | |
|                     		
 | |
|                     		if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO))
 | |
|                     			logger.debug( "Added pseudo files for folder (exists) - " + paths[0]);
 | |
|     	                }
 | |
|     	            	
 | |
|     	                // Check if the path is to a pseudo file
 | |
|     	                
 | |
|     	                PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( sess, tree, name);
 | |
|     	                if ( pfile != null)
 | |
|     	                {
 | |
|     	                    // Indicate that the file exists
 | |
|     	                    
 | |
|     	                    status = FileStatus.FileExists;
 | |
|     	                }
 | |
|     	                else
 | |
|     	                {
 | |
|     	                	// Failed to find pseudo file
 | |
|     	                	
 | |
|     	                	if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO))
 | |
|     	                		logger.debug( "Failed to find pseudo file (exists) - " + name);
 | |
|     	                }
 | |
|                 	}
 | |
|                 }
 | |
| 
 | |
|                 // If the file is not a pseudo file then search for the file
 | |
|                 
 | |
|                 if ( status == FileStatus.Unknown) 
 | |
|                 {
 | |
| 	            	// Create the transaction
 | |
| 	                
 | |
|                 	beginReadTransaction( sess);
 | |
| 	                
 | |
| 	                // Get the file information to check if the file/folder exists
 | |
| 	                
 | |
|                     FileInfo info = getFileInformation(sess, tree, name);
 | |
|                     NodeRef nodeRef = getNodeOrNull(name, ctx, fstate);
 | |
|                     nodeRef = ((null == nodeRef) && (info instanceof ContentFileInfo)) ? (((ContentFileInfo) info).getNodeRef()) : (nodeRef);
 | |
| 
 | |
|                     if ((null == nodeRef) || !fileFolderService.exists(nodeRef))
 | |
|                     {
 | |
|                         status = FileStatus.NotExist;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
| 		                if (info.isDirectory())
 | |
| 		                {
 | |
| 		                    status = FileStatus.DirectoryExists;
 | |
| 		                }
 | |
| 		                else
 | |
| 		                {
 | |
| 		                    status = FileStatus.FileExists;
 | |
| 		                }
 | |
| 	                }
 | |
| 
 | |
| 	                // Update the file state status
 | |
| 
 | |
| 	                if ( fstate != null)
 | |
| 	                    fstate.setFileStatus( status);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         catch (FileNotFoundException e)
 | |
|         {
 | |
|             status = FileStatus.NotExist;
 | |
|             if ( fstate != null)
 | |
|                 fstate.setFileStatus( status);
 | |
|         }
 | |
|         catch (IOException e)
 | |
|         {
 | |
|             // Debug
 | |
| 
 | |
|         	if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|         		logger.debug("File exists error, " + name, e);
 | |
|             
 | |
|             status = FileStatus.NotExist;
 | |
|         }
 | |
| 
 | |
|         // Debug
 | |
|         
 | |
|         if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|             logger.debug("File status determined: name=" + name + " status=" + fileStatusString(fstate.getFileStatus()));
 | |
|         
 | |
|         // Return the file/folder status
 | |
|         
 | |
|         return status;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Open a file or folder
 | |
|      * 
 | |
|      * @param sess SrvSession
 | |
|      * @param tree TreeConnection
 | |
|      * @param params FileOpenParams
 | |
|      * @return NetworkFile
 | |
|      * @exception IOException
 | |
|      */
 | |
|     public NetworkFile openFile(SrvSession sess, TreeConnection tree, FileOpenParams params ) throws IOException
 | |
|     {
 | |
|         // Create the transaction
 | |
|         
 | |
|     	beginReadTransaction( sess);
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             // Check if pseudo files are enabled
 | |
|             
 | |
|             if ( hasPseudoFileInterface(ctx))
 | |
|             {
 | |
|             	// Check if the file name is a pseudo file name
 | |
|             	
 | |
|             	String path = params.getPath();
 | |
| 
 | |
|             	if ( getPseudoFileInterface( ctx).isPseudoFile(sess, tree, path)) {
 | |
|             		
 | |
| 	            	// Make sure the parent folder has a file state, and the path exists
 | |
| 	
 | |
| 	                String[] paths = FileName.splitPath( path);
 | |
| 	                FileState fstate = ctx.getStateCache().findFileState( paths[0]);
 | |
| 	                
 | |
| 	                if ( fstate == null) {
 | |
| 
 | |
| 	                	// Check if the path exists
 | |
| 	                	
 | |
| 	                	if ( fileExists( sess, tree, paths[0]) == FileStatus.DirectoryExists)
 | |
| 	                	{
 | |
| 	                		// Create the file state and add any pseudo files
 | |
| 	                		
 | |
| 	                		fstate = ctx.getStateCache().findFileState( paths[0], true);
 | |
| 	                		
 | |
| 	                		fstate.setFileStatus( DirectoryExists);
 | |
| 	                		getPseudoFileInterface( ctx).addPseudoFilesToFolder( sess, tree, paths[0]);
 | |
| 	                		
 | |
| 	                		// Debug
 | |
| 	                		
 | |
| 	                		if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO))
 | |
| 	                			logger.debug( "Added file state for pseudo files folder (open) - " + paths[0]);
 | |
| 	                	}
 | |
| 	                }
 | |
| 	                else if ( fstate.hasPseudoFiles() == false)
 | |
| 	                {
 | |
| 	                	// Add pseudo files for the parent folder
 | |
| 	                	
 | |
|                 		getPseudoFileInterface( ctx).addPseudoFilesToFolder( sess, tree, paths[0]);
 | |
|                 		
 | |
|                 		// Debug
 | |
|                 		
 | |
|                 		if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO))
 | |
|                 			logger.debug( "Added pseudo files for folder (open) - " + paths[0]);
 | |
| 	                }
 | |
| 	            	
 | |
| 	                // Check if the path is to a pseudo file
 | |
| 	                
 | |
| 	                PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( sess, tree, params.getPath());
 | |
| 	                if ( pfile != null)
 | |
| 	                {
 | |
| 	                    // Create a network file to access the pseudo file data
 | |
| 	                    
 | |
| 	                    return pfile.getFile( params.getPath());
 | |
| 	                }
 | |
| 	                else
 | |
| 	                {
 | |
| 	                	// Failed to find pseudo file
 | |
| 	                	
 | |
| 	                	if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO))
 | |
| 	                		logger.debug( "Failed to find pseudo file (open) - " + params.getPath());
 | |
| 	                }
 | |
|             	}
 | |
|             }
 | |
|             
 | |
|             // Not a pseudo file, try and open a normal file/folder node
 | |
|             
 | |
|             NodeRef nodeRef = getNodeForPath(tree, params.getPath());
 | |
|             
 | |
|             // Check permissions on the file/folder node
 | |
|             //
 | |
|             // Check for read access
 | |
|             
 | |
|             if ( params.hasAccessMode(AccessMode.NTRead) &&
 | |
|                     permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.DENIED)
 | |
|                 throw new AccessDeniedException("No read access to " + params.getFullPath());
 | |
|                 
 | |
|             // Check for write access
 | |
|             
 | |
|             if ( params.hasAccessMode(AccessMode.NTWrite) &&
 | |
|                     permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED)
 | |
|                 throw new AccessDeniedException("No write access to " + params.getFullPath());
 | |
|             
 | |
|             // Check for delete access
 | |
|             
 | |
| //            if ( params.hasAccessMode(AccessMode.NTDelete) &&
 | |
| //                    permissionService.hasPermission(nodeRef, PermissionService.DELETE) == AccessStatus.DENIED)
 | |
| //                throw new AccessDeniedException("No delete access to " + params.getFullPath());
 | |
| 
 | |
|             // Check if the file has a lock
 | |
|             
 | |
|             String lockTypeStr = (String) nodeService.getProperty( nodeRef, ContentModel.PROP_LOCK_TYPE);
 | |
|             
 | |
|             if ( params.hasAccessMode(AccessMode.NTWrite) && lockTypeStr != null)
 | |
|                 throw new AccessDeniedException("File is locked, no write access to " + params.getFullPath());
 | |
|             
 | |
|             //  Check if there is a file state for the file
 | |
| 
 | |
|             FileState fstate = null;
 | |
|             
 | |
|             if ( ctx.hasStateCache())
 | |
|             {
 | |
|                 // Check if there is a file state for the file
 | |
| 
 | |
|                 fstate = ctx.getStateCache().findFileState( params.getPath());
 | |
|             
 | |
|                 if ( fstate != null)
 | |
|                 {                
 | |
|                     // Check if the file exists
 | |
|                     
 | |
|                     if ( fstate.exists() == false)
 | |
|                         throw new FileNotFoundException();
 | |
|                 }
 | |
|                 else {
 | |
|                 	
 | |
|                 	// Create a file state for the path
 | |
|                 	
 | |
|                     fstate = ctx.getStateCache().findFileState( params.getPath(), true);
 | |
|                 }                	
 | |
|                     
 | |
|             	// Check if the current file open allows the required shared access
 | |
|             	
 | |
|             	boolean nosharing = false;
 | |
|             	String noshrReason = null;
 | |
|             	
 | |
|             	// TEST
 | |
|             	
 | |
|         		if ( params.getAccessMode() == AccessMode.NTFileGenericExecute && params.getPath().toLowerCase().endsWith( ".exe") == false) {
 | |
|         			
 | |
|             		// DEBUG
 | |
|             		
 | |
|             		if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) {
 | |
|             			logger.debug( "Execute access mode, path" + params.getPath());
 | |
|             			logger.debug( "  Fstate=" + fstate);
 | |
|             		}
 | |
|         		
 | |
|                     throw new AccessDeniedException("Invalid access mode");
 | |
|                 }
 | |
|                 
 | |
|                 if ( fstate.getOpenCount() > 0 && params.isAttributesOnlyAccess() == false) {
 | |
|                     
 | |
|     				// Check for impersonation security level from the original process that opened the file
 | |
|     				
 | |
|     				if ( params.getSecurityLevel() == WinNT.SecurityImpersonation && params.getProcessId() == fstate.getProcessId())
 | |
|     					nosharing = false;
 | |
|     	
 | |
|     				// Check if the caller wants read access, check the sharing mode
 | |
|     				// Check if the caller wants write access, check if the sharing mode allows write
 | |
|     				
 | |
|     		    	else if ( params.isReadOnlyAccess() && (fstate.getSharedAccess() & SharingMode.READ) != 0)
 | |
|     		    		nosharing = false;
 | |
|     				
 | |
|     				// Check if the caller wants write access, check the sharing mode
 | |
|     				
 | |
|     		    	else if (( params.isReadWriteAccess() || params.isWriteOnlyAccess()) && (fstate.getSharedAccess() & SharingMode.WRITE) == 0)
 | |
|     		    	{
 | |
|     					nosharing = true;
 | |
|     					noshrReason = "Sharing mode disallows write";
 | |
|     					
 | |
|     		    		// DEBUG
 | |
|     		    		
 | |
|                         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|     		    			logger.debug("Sharing mode disallows write access path=" + params.getPath());
 | |
|     		    	}
 | |
|     		    	
 | |
|     				// Check if the file has been opened for exclusive access
 | |
|     				
 | |
|     				else if ( fstate.getSharedAccess() == SharingMode.NOSHARING) {
 | |
|     					nosharing = true;
 | |
|     					noshrReason = "Sharing mode exclusive";
 | |
|     				}
 | |
|     				
 | |
|     				// Check if the required sharing mode is allowed by the current file open
 | |
|     				
 | |
|     				else if ((fstate.getSharedAccess() & params.getSharedAccess()) != params.getSharedAccess()) {
 | |
|     					nosharing = true;
 | |
|     					noshrReason = "Sharing mode mismatch";
 | |
|     					
 | |
|                         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|     		    			logger.debug("Local share mode=0x" + Integer.toHexString(fstate.getSharedAccess()) + ", params share mode=0x" + Integer.toHexString(params.getSharedAccess()));
 | |
|     				}
 | |
|     				
 | |
|     				// Check if the caller wants exclusive access to the file
 | |
|     				
 | |
|     		    	else if ( params.getSharedAccess() == SharingMode.NOSHARING) {
 | |
|     		    		nosharing = true;
 | |
|     		    		noshrReason = "Requestor wants exclusive mode";
 | |
|     		    	}
 | |
|                 }
 | |
|                 
 | |
|                 // Check if the file allows shared access
 | |
|                 
 | |
|                 if ( nosharing == true)
 | |
|                 {
 | |
|                     if ( params.getPath().equals( "\\") == false) {
 | |
|                         
 | |
|                         // DEBUG
 | |
|                         
 | |
|                         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                             logger.debug("Sharing violation path=" + params.getPath() + ", sharing=0x" + Integer.toHexString(fstate.getSharedAccess()) + ",reason=" + noshrReason);
 | |
|                     
 | |
|                         // File is locked by another user
 | |
|                         
 | |
|                         throw new FileSharingException("File already open, " + params.getPath());
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 // Update the file sharing mode and process id, if this is the first file open
 | |
|                 
 | |
|                 fstate.setSharedAccess( params.getSharedAccess());
 | |
|                 fstate.setProcessId( params.getProcessId());
 | |
|                 
 | |
|                 // DEBUG
 | |
|                 
 | |
|                 if ( logger.isDebugEnabled() && fstate.getOpenCount() == 0 && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                     logger.debug("Path " + params.getPath() + ", sharing=0x" + Integer.toHexString(params.getSharedAccess()) + ", PID=" + params.getProcessId());
 | |
|             }
 | |
|             
 | |
|             // Check if the node is a link node
 | |
|             
 | |
|             NodeRef linkRef = (NodeRef) nodeService.getProperty(nodeRef, ContentModel.PROP_LINK_DESTINATION);
 | |
|             AlfrescoNetworkFile netFile = null;
 | |
|             
 | |
|             if ( linkRef == null)
 | |
|             {
 | |
|                 // Check if the file is already opened by this client/process
 | |
|                 
 | |
|                 if ( tree.openFileCount() > 0 && params.isAttributesOnlyAccess() == false) {
 | |
|                 
 | |
|                     // Search the open file table for this session/virtual circuit
 | |
|                     
 | |
|                     int idx = 0;
 | |
|                     
 | |
|                     while ( idx < tree.getFileTableLength() && netFile == null) {
 | |
|                         
 | |
|                         // Get the current file from the open file table
 | |
|                         
 | |
|                         NetworkFile curFile = tree.findFile( idx);
 | |
|                         if ( curFile != null && curFile instanceof ContentNetworkFile) {
 | |
|                             
 | |
|                             // Check if the file is the same path and process id
 | |
|                             
 | |
|                             ContentNetworkFile contentFile = (ContentNetworkFile) curFile;
 | |
|                             if ( contentFile.getProcessId() == params.getProcessId() &&
 | |
|                                     contentFile.getFullName().equalsIgnoreCase( params.getFullPath())) {
 | |
|                                 
 | |
|                                 // Check that the access mode is the same
 | |
|                                 
 | |
|                                 if (( params.isReadWriteAccess() && contentFile.getGrantedAccess() == NetworkFile.READWRITE) ||
 | |
|                                         ( params.isReadOnlyAccess() && contentFile.getGrantedAccess() == NetworkFile.READONLY)) {
 | |
|                                     
 | |
|                                     // Found a match, re-use the open file
 | |
|                                     
 | |
|                                     netFile = contentFile;
 | |
|                                     
 | |
|                                     // Increment the file open count, last file close will actually close the file/stream
 | |
|                                     
 | |
|                                     contentFile.incrementOpenCount();
 | |
|     
 | |
|                                     // DEBUG
 | |
|                                     
 | |
|                                     if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                         logger.debug("Re-use existing file open Path " + params.getPath() + ", PID=" + params.getProcessId() + ", params=" +
 | |
|                                                 ( params.isReadOnlyAccess() ? "ReadOnly" : "Write") + ", file=" + 
 | |
|                                                 ( contentFile.getGrantedAccess() <= NetworkFile.READONLY ? "ReadOnly" : "Write"));
 | |
|                                 }
 | |
|                                 else if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                     logger.debug("Not re-using file path=" + params.getPath() + ", readWrite=" + (params.isReadWriteAccess() ? "true" : "false") +
 | |
|                                             ", readOnly=" + (params.isReadOnlyAccess() ? "true" : "false") +
 | |
|                                             ", grantedAccess=" + contentFile.getGrantedAccessAsString());
 | |
|                             }
 | |
|                         }
 | |
|                         
 | |
|                         // Update the file table index
 | |
|                         
 | |
|                         idx++;
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 // Create the network file, if we could not match an existing file open
 | |
|                 
 | |
|                 if ( netFile == null) {
 | |
|                     
 | |
|                     // Create a new network file for the open request
 | |
|                 
 | |
|                     netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, nodeRef, params.getPath(), params.isReadOnlyAccess(), params.isAttributesOnlyAccess(), sess);
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // Get the CIFS server name
 | |
|                 
 | |
|                 String srvName = null;
 | |
|                 SMBServer cifsServer = (SMBServer) sess.getServer().getConfiguration().findServer( "CIFS");
 | |
|                 
 | |
|                 if(sess instanceof SMBSrvSession)
 | |
|                 {
 | |
|                     SMBSrvSession smbSess = (SMBSrvSession)sess;
 | |
|                     srvName = smbSess.getShareHostName();
 | |
|                 }
 | |
|                 else if ( cifsServer != null)
 | |
|                 {
 | |
|                     // Use the CIFS server name in the URL
 | |
|                 
 | |
|                     srvName = cifsServer.getServerName();
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // Use the local server name in the URL
 | |
|                   
 | |
|                     srvName = InetAddress.getLocalHost().getHostName();
 | |
|                 }
 | |
|                 
 | |
|                 // Convert the target node to a path, convert to URL format
 | |
|                 
 | |
|                 String path = getPathForNode( tree, linkRef);
 | |
|                 path = path.replace( FileName.DOS_SEPERATOR, '/');
 | |
|                 
 | |
|                 // Build the URL file data
 | |
|                 
 | |
|                 StringBuilder urlStr = new StringBuilder();
 | |
|             
 | |
|                 urlStr.append("[InternetShortcut]\r\n");
 | |
|                 urlStr.append("URL=file://");
 | |
|                 urlStr.append( srvName);
 | |
|                 urlStr.append("/");
 | |
|                 urlStr.append( tree.getSharedDevice().getName());
 | |
|                 urlStr.append( path);
 | |
|                 urlStr.append("\r\n");
 | |
|     
 | |
|                 // Create the in memory pseudo file for the URL link
 | |
|                 
 | |
|                 byte[] urlData = urlStr.toString().getBytes();
 | |
|                 
 | |
|                 // Get the file information for the link node
 | |
|                 
 | |
|                 FileInfo fInfo = cifsHelper.getFileInformation( nodeRef, isReadOnly, isLockedFilesAsOffline);
 | |
| 
 | |
|                 // Set the file size to the actual data length
 | |
|                 
 | |
|                 fInfo.setFileSize( urlData.length);
 | |
|                 
 | |
|                 // Create the network file using the in-memory file data
 | |
|                 
 | |
|                 netFile = new LinkMemoryNetworkFile( fInfo.getFileName(), urlData, fInfo, nodeRef);
 | |
|                 netFile.setFullName( params.getPath());
 | |
|             }
 | |
|             
 | |
|             // Generate a file id for the file
 | |
|             
 | |
|             if ( netFile != null) {
 | |
|                 long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID));
 | |
|                 netFile.setFileId(( int) ( id & 0xFFFFFFFFL));
 | |
|                 
 | |
|                 // Indicate the file is open
 | |
|                 
 | |
|                 netFile.setClosed( false);
 | |
|             }
 | |
|             
 | |
|             // If the file has been opened for overwrite then truncate the file to zero length, this will
 | |
|             // also prevent the existing content data from being copied to the new version of the file
 | |
|             
 | |
|             if ( params.isOverwrite() && netFile != null)
 | |
|             {
 | |
|                 // Truncate the file to zero length
 | |
|                 
 | |
|                 netFile.truncateFile( 0L);
 | |
|             }
 | |
|             
 | |
|             // Create a file state for the open file
 | |
|             
 | |
|             if ( ctx.hasStateCache())
 | |
|             {
 | |
|                 if ( fstate  == null)
 | |
|                     fstate = ctx.getStateCache().findFileState(params.getPath(), true);
 | |
|             
 | |
|                 // Update the file state, cache the node
 | |
|                 
 | |
|                 if ( netFile.getGrantedAccess() > NetworkFile.ATTRIBUTESONLY)
 | |
|                 	fstate.incrementOpenCount();
 | |
|                 fstate.setFilesystemObject(nodeRef);
 | |
|                 
 | |
|                 // Store the state with the file
 | |
|                 
 | |
|                 netFile.setFileState( fstate);
 | |
|                 
 | |
|                 // Set the file access date/time, if available
 | |
|                 
 | |
| //                if ( fstate.hasAccessDateTime())
 | |
| //                    netFile.setAccessDate( fstate.getAccessDateTime());
 | |
|                 
 | |
|                 
 | |
|                // Set the live file size, if available
 | |
|                 
 | |
|                if ( fstate.hasFileSize())
 | |
|             	   netFile.setFileSize( fstate.getFileSize());
 | |
|             }
 | |
|             
 | |
|             // Debug
 | |
|             
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Opened network file: path=" + params.getPath() + " file open parameters=" + params + " network file=" + netFile);
 | |
| 
 | |
|             // Return the network file
 | |
|             
 | |
|             return netFile;
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Open file - access denied, " + params.getFullPath());
 | |
|             
 | |
|             // Convert to a filesystem access denied status
 | |
|             
 | |
|             throw new AccessDeniedException("Open file " + params.getFullPath());
 | |
|         }
 | |
|         catch (RuntimeException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Open file error", ex);
 | |
|             
 | |
|             // Convert to a general I/O exception
 | |
|             
 | |
|             throw new IOException("Open file " + params.getFullPath());
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Create a new file on the file system.
 | |
|      * 
 | |
|      * <p>
 | |
|      * WARNING : side effect - closes current transaction context.
 | |
|      * 
 | |
|      * @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
 | |
|     {
 | |
|         final ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         
 | |
|         try
 | |
|         {
 | |
| 
 | |
|             // Access the repository in a retryable write transaction
 | |
|             Pair<String, NodeRef> result = doInWriteTransaction(sess, new CallableIO<Pair<String, NodeRef>>(){
 | |
|                 public Pair<String, NodeRef> call() throws IOException
 | |
|                 {
 | |
|                     // Get the device root
 | |
| 
 | |
|                     NodeRef deviceRootNodeRef = ctx.getRootNode();
 | |
|                     
 | |
|                     String path = params.getPath();
 | |
|                     String parentPath = null;
 | |
| 
 | |
|                     // If the state table is available then try to find the parent folder node for the new file
 | |
|                     // to save having to walk the path
 | |
| 
 | |
|                     if (ctx.hasStateCache())
 | |
|                     {
 | |
|                         // See if the parent folder has a file state, we can avoid having to walk the path
 | |
|                         
 | |
|                         String[] paths = FileName.splitPath(path);
 | |
|                         if ( paths[0] != null && paths[0].length() > 1)
 | |
|                         {
 | |
|                             // Find the node ref for the folder being searched
 | |
|                             
 | |
|                             NodeRef nodeRef = getNodeForPath(tree, paths[0]);
 | |
|                             
 | |
|                             if ( nodeRef != null)
 | |
|                             {
 | |
|                                 deviceRootNodeRef = nodeRef;
 | |
|                                 path              = paths[1];
 | |
|                                 
 | |
|                                 // DEBUG
 | |
|                                 
 | |
|                                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                     logger.debug("Create file using cached noderef for path " + paths[0]);
 | |
|                             }
 | |
|                             
 | |
|                             parentPath = paths[0];
 | |
|                         }
 | |
|                     }
 | |
|                     
 | |
|                     // Create it - the path will be created, if necessary
 | |
|                     if(logger.isDebugEnabled())
 | |
|                     {
 | |
|                         logger.debug("create new file" + path);
 | |
|                     }
 | |
| 
 | |
| 
 | |
|                     NodeRef nodeRef = null;
 | |
| 
 | |
|                     try
 | |
|                     {
 | |
|                         nodeRef = cifsHelper.createNode(deviceRootNodeRef, path, ContentModel.TYPE_CONTENT);
 | |
|                         nodeService.addAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT, null);
 | |
|                     }
 | |
|                     catch ( FileExistsException ex)
 | |
|                     {
 | |
|                         nodeRef = cifsHelper.getNodeRef(deviceRootNodeRef, path);
 | |
|                         if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_SOFT_DELETE))
 | |
|                         {
 | |
|                             throw ex;
 | |
|                         }
 | |
|                     }
 | |
|                     
 | |
|                     return new Pair<String, NodeRef>(parentPath, nodeRef);
 | |
|                 }});
 | |
| 
 | |
|             // Get or create the file state for the parent folder
 | |
|             FileState parentState = null;
 | |
|             String parentPath = result.getFirst();
 | |
|             if (parentPath != null)
 | |
|             {
 | |
|                 parentState = getStateForPath(tree, parentPath);
 | |
|                 if (parentState == null && ctx.hasStateCache())
 | |
|                     parentState = ctx.getStateCache().findFileState(parentPath, true);
 | |
|             }
 | |
|             
 | |
|             NodeRef nodeRef = result.getSecond();
 | |
|             if (nodeRef != null && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_SOFT_DELETE))
 | |
|             {
 | |
|                 nodeService.removeAspect(nodeRef, ContentModel.ASPECT_SOFT_DELETE);
 | |
|             }
 | |
|   
 | |
|             // Create the network file
 | |
|             
 | |
|             ContentNetworkFile netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, result.getSecond(), params.getPath(), params.isReadOnlyAccess(), params.isAttributesOnlyAccess(), sess);
 | |
|             
 | |
|             // Always allow write access to a newly created file
 | |
|             
 | |
|             netFile.setGrantedAccess(NetworkFile.READWRITE);
 | |
|             
 | |
|             // Set the owner process id for this open file
 | |
|             
 | |
|             netFile.setProcessId( params.getProcessId());
 | |
|             
 | |
|             // Truncate the file so that the content stream is created
 | |
|             
 | |
|             netFile.truncateFile( 0L);
 | |
| 
 | |
|             // Indicate the file is open
 | |
|             
 | |
|             netFile.setClosed( false);
 | |
|             
 | |
|             // Generate a file id for the file
 | |
|             
 | |
|             if ( netFile != null) {
 | |
|                 long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(netFile.getNodeRef(), ContentModel.PROP_NODE_DBID));
 | |
|                 netFile.setFileId((int) (id & 0xFFFFFFFFL));
 | |
|             }
 | |
|             
 | |
|             // Add a file state for the new file/folder
 | |
|             
 | |
|             if ( ctx.hasStateCache())
 | |
|             {
 | |
|                 FileState fstate = ctx.getStateCache().findFileState(params.getPath(), true);
 | |
|                 if ( fstate != null)
 | |
|                 {
 | |
|                     // Save the file sharing mode, needs to be done before the open count is incremented
 | |
|                     
 | |
|                     fstate.setSharedAccess( params.getSharedAccess());
 | |
|                     fstate.setProcessId( params.getProcessId());
 | |
|                     
 | |
|                     // Indicate that the file is open
 | |
|     
 | |
|                     fstate.setFileStatus( FileExists);
 | |
|                     fstate.incrementOpenCount();
 | |
|                     fstate.setFilesystemObject(result.getSecond());
 | |
|                     
 | |
|                     // Track the intial allocation size
 | |
|                     
 | |
|                     fstate.setAllocationSize( params.getAllocationSize());
 | |
|                     
 | |
|                     // Store the file state with the file
 | |
|                     
 | |
|                     netFile.setFileState( fstate);
 | |
|                     
 | |
|                     // DEBUG
 | |
|                     
 | |
|                     if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                         logger.debug("Create file, state=" + fstate);
 | |
|                 }
 | |
|                 
 | |
|                 // Update the parent folder file state
 | |
|                 
 | |
|                 if ( parentState != null)
 | |
|                     parentState.updateModifyDateTime();
 | |
|             }
 | |
|             
 | |
|             // Debug
 | |
|             
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Created file: path=" + params.getPath() + " file open parameters=" + params + " node=" + result.getSecond() + " network file=" + netFile);
 | |
| 
 | |
|             // Return the new network file
 | |
|             
 | |
|             return netFile;
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Create file - access denied, " + params.getFullPath());
 | |
|             
 | |
|             // Convert to a filesystem access denied status
 | |
|             
 | |
|             throw new AccessDeniedException("Create file " + params.getFullPath());
 | |
|         }
 | |
|         catch (ContentIOException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Create file - content I/O error, " + params.getFullPath());
 | |
|             
 | |
|             // Convert to a filesystem disk full status
 | |
|             
 | |
|             throw new DiskFullException("Create file " + params.getFullPath());
 | |
|         }
 | |
|         catch (RuntimeException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Create file error", ex);
 | |
|             
 | |
|             // Convert to a general I/O exception
 | |
|             
 | |
|             throw new IOException("Create file " + params.getFullPath(), ex);
 | |
|         }
 | |
|         
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create a new directory on this file system.
 | |
|      *
 | |
|      * <p>
 | |
|      * WARNING : side effect - closes current transaction context.
 | |
|      *
 | |
|      * @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();
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             // Access the repository in a retryable write transaction
 | |
|             Pair<String, NodeRef> result = doInWriteTransaction(sess, new CallableIO<Pair<String, NodeRef>>()
 | |
|             {
 | |
| 
 | |
|                 public Pair<String, NodeRef> call() throws IOException
 | |
|                 {
 | |
|                     // get the device root
 | |
| 
 | |
|                     NodeRef deviceRootNodeRef = ctx.getRootNode();
 | |
| 
 | |
|                     String path = params.getPath();
 | |
|                     String parentPath = null;
 | |
| 
 | |
|                     // If the state table is available then try to find the parent folder node for the new folder
 | |
|                     // to save having to walk the path
 | |
| 
 | |
|                     if ( ctx.hasStateCache())
 | |
|                     {
 | |
|                         // See if the parent folder has a file state, we can avoid having to walk the path
 | |
| 
 | |
|                         String[] paths = FileName.splitPath(path);
 | |
|                         if (paths[0] != null && paths[0].length() > 1)
 | |
|                         {
 | |
|                             // Find the node ref for the folder being searched
 | |
| 
 | |
|                             NodeRef nodeRef = getNodeForPath(tree, paths[0]);
 | |
| 
 | |
|                             if (nodeRef != null)
 | |
|                             {
 | |
|                                 deviceRootNodeRef = nodeRef;
 | |
|                                 path = paths[1];
 | |
| 
 | |
|                                 // DEBUG
 | |
| 
 | |
|                                 if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                     logger.debug("Create file using cached noderef for path " + paths[0]);
 | |
|                             }
 | |
|                             
 | |
|                             parentPath = paths[0];                            
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     // Create it - the path will be created, if necessary
 | |
| 
 | |
|                     NodeRef nodeRef = cifsHelper.createNode(deviceRootNodeRef, path, ContentModel.TYPE_FOLDER);
 | |
|                     
 | |
|                     return new Pair<String, NodeRef>(parentPath, nodeRef);                    
 | |
|                 }
 | |
|             });
 | |
| 
 | |
|             // Get or create the file state for the parent folder
 | |
|             FileState parentState = null;
 | |
|             String parentPath = result.getFirst();
 | |
|             if (parentPath != null)
 | |
|             {
 | |
|                 parentState = getStateForPath(tree, parentPath);
 | |
|                 if (parentState == null && ctx.hasStateCache())
 | |
|                     parentState = ctx.getStateCache().findFileState(parentPath, true);
 | |
|             }
 | |
|             
 | |
|             // Add a file state for the new folder
 | |
|             
 | |
|             if ( ctx.hasStateCache())
 | |
|             {
 | |
|                 FileState fstate = ctx.getStateCache().findFileState( params.getPath(), true);
 | |
|                 if ( fstate != null)
 | |
|                 {
 | |
|                     // Indicate that the file is open
 | |
|     
 | |
|                     fstate.setFileStatus( DirectoryExists);
 | |
|                     fstate.setFilesystemObject(result.getSecond());
 | |
|                     
 | |
|                     // DEBUG
 | |
|                     
 | |
|                     if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                         logger.debug("Create folder, state=" + fstate); 
 | |
|                 }
 | |
|                 
 | |
|                 // Update the parent folder file state
 | |
|                 
 | |
|                 if ( parentState != null)
 | |
|                     parentState.updateModifyDateTime();
 | |
|             }
 | |
|             
 | |
|             // Debug
 | |
|             
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Created directory: path=" + params.getPath() + " file open params=" + params + " node=" + result.getSecond());
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Create directory - access denied, " + params.getFullPath());
 | |
|             
 | |
|             // Convert to a filesystem access denied status
 | |
|             
 | |
|             throw new AccessDeniedException("Create directory " + params.getFullPath());
 | |
|         }
 | |
|         catch (RuntimeException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 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.
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection
 | |
|      * @param dir Directory name.
 | |
|      * @exception java.io.IOException The exception description.
 | |
|      */
 | |
|     public void deleteDirectory(SrvSession sess, TreeConnection tree, final String dir) throws IOException
 | |
|     {
 | |
|         // get the device root
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         final NodeRef deviceRootNodeRef = ctx.getRootNode();
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             NodeRef nodeRef = doInWriteTransaction(sess, new CallableIO<NodeRef>(){
 | |
| 
 | |
|                 public NodeRef call() throws IOException
 | |
|                 {
 | |
|                     // Get the node for the folder                    
 | |
|             
 | |
|                     NodeRef nodeRef = cifsHelper.getNodeRef(deviceRootNodeRef, dir);
 | |
|                     if (fileFolderService.exists(nodeRef))
 | |
|                     {
 | |
|                         // Check if the folder is empty                        
 | |
|                 
 | |
|                         if ( cifsHelper.isFolderEmpty( nodeRef))
 | |
|                         {                            
 | |
|                             // Delete the folder node           
 | |
|     
 | |
|                             fileFolderService.delete(nodeRef);
 | |
|                             return nodeRef;
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             throw new DirectoryNotEmptyException( dir);                            
 | |
|                         }
 | |
|                     }
 | |
|                     return null;
 | |
|                 }});
 | |
|             if (nodeRef != null && ctx.hasStateCache())
 | |
|             {
 | |
|                 // Remove the file state
 | |
|             
 | |
|                 ctx.getStateCache().removeFileState(dir);
 | |
|             
 | |
|                 // Update, or create, a parent folder file state
 | |
|                 
 | |
|                 String[] paths = FileName.splitPath(dir);
 | |
|                 if ( paths[0] != null && paths[0].length() > 1)
 | |
|                 {
 | |
|                     // Get the file state for the parent folder
 | |
|                     
 | |
|                     FileState parentState = getStateForPath(tree, paths[0]);
 | |
|                             if ( parentState == null && ctx.hasStateCache())
 | |
|                                 parentState = ctx.getStateCache().findFileState( paths[0], true);
 | |
| 
 | |
|                     // Update the modification timestamp
 | |
|                     
 | |
|                     parentState.updateModifyDateTime();
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Debug
 | |
|             
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Deleted directory: directory=" + dir + " node=" + nodeRef);
 | |
|         }
 | |
|         catch (FileNotFoundException e)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Delete directory - file not found, " + dir);
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Delete directory - access denied, " + dir);
 | |
|             
 | |
|             // Convert to a filesystem access denied status
 | |
|             
 | |
|             throw new AccessDeniedException("Delete directory " + dir);
 | |
|         }
 | |
|         catch (RuntimeException 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);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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 sess, TreeConnection tree, NetworkFile file) throws IOException
 | |
|     {
 | |
|         // Debug
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         
 | |
|         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILEIO))
 | |
|             logger.debug("Flush file=" + file.getFullName());
 | |
|         
 | |
|         // Flush the file data
 | |
|         
 | |
|         file.flushFile();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Close the file.
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection.
 | |
|      * @param file Network file context.
 | |
|      * @exception java.io.IOException If an error occurs.
 | |
|      */
 | |
|     public void closeFile(SrvSession sess, TreeConnection tree, final NetworkFile file) throws IOException
 | |
|     {  
 | |
|         if (logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("Close file: file" + file);
 | |
|         }
 | |
|         
 | |
|         // Get the associated file state
 | |
|         
 | |
|         final ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         FileState toUpdate = null;
 | |
| 
 | |
|         // Check for a content file
 | |
|         
 | |
|         if ( file instanceof ContentNetworkFile) {
 | |
|             
 | |
|             // Update the file state
 | |
|             
 | |
|             if ( ctx.hasStateCache())
 | |
|             {
 | |
|                 FileState fstate = ctx.getStateCache().findFileState(file.getFullName());
 | |
|                 if ( fstate != null) {
 | |
|                     
 | |
|                     // If the file open count is now zero then reset the stored sharing mode
 | |
|                     
 | |
|                     if ( file.getGrantedAccess() > NetworkFile.ATTRIBUTESONLY && fstate.decrementOpenCount() == 0)
 | |
|                         fstate.setSharedAccess( SharingMode.READWRITE + SharingMode.DELETE);
 | |
|                     
 | |
|                     // Check if there is an oplock on the file
 | |
|                     
 | |
|                     if ( file.hasOpLock()) {
 | |
|                     	
 | |
|                     	// Release the oplock
 | |
|                     	
 | |
|                         OpLockInterface flIface = (OpLockInterface) this;
 | |
|                         OpLockManager oplockMgr = flIface.getOpLockManager(sess, tree);
 | |
|                         
 | |
|                         oplockMgr.releaseOpLock( file.getOpLock().getPath());
 | |
| 
 | |
|                         //  DEBUG
 | |
|                         
 | |
|                         if ( logger.isDebugEnabled())
 | |
|                           logger.debug("Released oplock for closed file, file=" + file.getFullName());
 | |
|                     }
 | |
|                     
 | |
|                     // Check if there is a cached modification timestamp to be written out
 | |
|                     
 | |
|                     if ( file.hasDeleteOnClose() == false && fstate.hasModifyDateTime() && fstate.hasFilesystemObject() && fstate.isDirectory() == false) {
 | |
|                         
 | |
|                         // Update the modification date on the file/folder node
 | |
|                         toUpdate = fstate;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Decrement the file open count
 | |
|             
 | |
|             ContentNetworkFile contentFile = (ContentNetworkFile) file;
 | |
|             
 | |
|             if ( contentFile.decrementOpenCount() > 0) {
 | |
|                 
 | |
|                 // DEBUG
 | |
|                 
 | |
|                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                     logger.debug("Deferred file close, path=" + file.getFullName() + ", openCount=" + contentFile.getOpenCount());
 | |
|                 
 | |
|                 // Defer the file close to the last reference
 | |
|                 
 | |
|                 return;
 | |
|             }
 | |
|             else if ( logger.isDebugEnabled())
 | |
|                 logger.debug("Last reference to file, closing, path=" + file.getFullName() + ", access=" + file.getGrantedAccessAsString() + ", fid=" + file.getProtocolId() +
 | |
|                         ", modified=" + contentFile.isModified());
 | |
|         }
 | |
|         
 | |
|         // Check if there is a quota manager enabled
 | |
|         
 | |
|         long fileSize = 0L;
 | |
|         
 | |
|         if ( ctx.hasQuotaManager() && file.hasDeleteOnClose()) {
 | |
|             
 | |
|             // Make sure the content stream has been opened, to get the current file size
 | |
|             
 | |
|             if ( file instanceof ContentNetworkFile) {
 | |
|                 ContentNetworkFile contentFile = (ContentNetworkFile) file;
 | |
|                 if ( contentFile.hasContent() == false)
 | |
|                     contentFile.openContent( false, false);
 | |
|                 
 | |
|                 // Save the current file size
 | |
|                 
 | |
|                 fileSize = contentFile.getFileSize();
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Depending on whether the node has the NO_CONTENT aspect, we may have to wipe it out on error
 | |
|         final CallableIO<Void> errorHandler = new CallableIO<Void>()
 | |
|         {
 | |
|             public Void call() throws IOException
 | |
|             {
 | |
|                 if (file instanceof NodeRefNetworkFile)
 | |
|                 {
 | |
|                     NodeRef nodeRef = ((NodeRefNetworkFile) file).getNodeRef();
 | |
|                     if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT))
 | |
|                     {
 | |
|                         logger.debug("No content - delete");
 | |
|                         fileFolderService.delete(nodeRef);
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 return null;
 | |
|             }
 | |
|         };
 | |
|         try
 | |
|         {
 | |
|             // Perform repository updates in a retryable write transaction
 | |
|             final FileState finalFileState = toUpdate;
 | |
|             Pair<NodeRef, Boolean> result = doInWriteTransaction(sess, new CallableIO<Pair<NodeRef, Boolean>>()
 | |
|             {
 | |
|                 public Pair<NodeRef, Boolean> call() throws IOException
 | |
|                 {
 | |
|                     // Check if the file is an OpenOffice document and hte truncation flag is set
 | |
|                     //
 | |
|                     // Note: Check before the timestamp update
 | |
|                     
 | |
|                     if ( file instanceof OpenOfficeContentNetworkFile) {
 | |
|                         OpenOfficeContentNetworkFile ooFile = (OpenOfficeContentNetworkFile) file;
 | |
|                         if ( ooFile.truncatedToZeroLength()) {
 | |
|                             
 | |
|                             // Inhibit versioning for this transaction
 | |
|                             
 | |
|                             getPolicyFilter().disableBehaviour( ContentModel.ASPECT_VERSIONABLE);
 | |
|     
 | |
|                             // Debug
 | |
|                             
 | |
|                             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                 logger.debug("OpenOffice file truncation update only, inhibit versioning, " + file.getFullName());
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     // Update the modification date on the file/folder node
 | |
|                     if (finalFileState != null && file instanceof ContentNetworkFile)
 | |
|                     {
 | |
|                         NodeRef nodeRef = (NodeRef) finalFileState.getFilesystemObject();
 | |
|     
 | |
|                         // Check if the file data has been updated, if not then inhibit versioning for this txn
 | |
|                         // so the timestamp update does not generate a new file version
 | |
|                         
 | |
|                         ContentNetworkFile contentFile = (ContentNetworkFile) file;
 | |
|                         if ( contentFile.isModified() == false && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) {
 | |
|     
 | |
|                             // Stop a new file version being generated
 | |
|                             
 | |
|                             getPolicyFilter().disableBehaviour( ContentModel.ASPECT_VERSIONABLE);
 | |
|     
 | |
|                             // Debug
 | |
|                             
 | |
|                             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                 logger.debug("Timestamp update only, inhibit versioning, " + file.getFullName());
 | |
|                         }
 | |
|     
 | |
|                         // Update the modification timestamp
 | |
|                         
 | |
|                         getPolicyFilter().disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
 | |
|     
 | |
|                         if (permissionService.hasPermission((NodeRef) finalFileState.getFilesystemObject(), PermissionService.WRITE_PROPERTIES) == AccessStatus.ALLOWED)
 | |
|                         { 
 | |
|                             nodeService.setProperty(nodeRef, ContentModel.PROP_MODIFIER, authService.getCurrentUserName());
 | |
|                             Date modifyDate = new Date(finalFileState.getModifyDateTime());
 | |
|                             nodeService.setProperty(nodeRef, ContentModel.PROP_MODIFIED, modifyDate);
 | |
|                             
 | |
|                             // Debug
 | |
|                             
 | |
|                             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                 logger.debug("Updated modification timestamp, " + file.getFullName() + ", modTime=" + modifyDate);
 | |
|                         }
 | |
|                     }
 | |
|                 
 | |
|                     // Defer to the network file to close the stream and remove the content
 | |
|                        
 | |
|                     file.close();
 | |
|                     
 | |
|                     // Remove the node if marked for delete
 | |
|                     
 | |
|                     if (file.hasDeleteOnClose())
 | |
|                     {
 | |
|                         logger.debug("File has delete on close");
 | |
|                         // Check if the file is a noderef based file
 | |
|                         
 | |
|                         if ( file instanceof NodeRefNetworkFile)
 | |
|                         {
 | |
|                             NodeRefNetworkFile nodeNetFile = (NodeRefNetworkFile) file;
 | |
|                             final NodeRef nodeRef = nodeNetFile.getNodeRef();
 | |
|                             
 | |
|                             // We don't know how long the network file has had the reference, so check for existence
 | |
|                             
 | |
|                             if (fileFolderService.exists(nodeRef))
 | |
|                             {
 | |
|                                 try
 | |
|                                 {
 | |
|                                    boolean isVersionable = nodeService.hasAspect( nodeRef, ContentModel.ASPECT_VERSIONABLE);
 | |
|                            
 | |
|                                     try
 | |
|                                     {
 | |
|                                         // Delete the file                                    
 | |
|                                     	FileState fileState = ctx.getStateCache().findFileState(file.getFullName());
 | |
|                                     	if (fileState!= null && fileState.findAttribute(CanDeleteWithoutPerms) != null)
 | |
|                                     	{
 | |
|                                     	    //Has CanDeleteWithoutPerms attribute, so delete from system user
 | |
|                                             AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
 | |
|                                             {
 | |
|                                                 @Override
 | |
|                                                 public Object doWork() throws Exception 
 | |
|                                                 {
 | |
|                                                     logger.debug("delete as system" + nodeRef);
 | |
|                                                     fileFolderService.delete(nodeRef);
 | |
|                                                     return null;
 | |
|                                                 }
 | |
|                                 			
 | |
|                                             }, AuthenticationUtil.getSystemUserName());
 | |
|                                
 | |
|                                         }
 | |
|                                     	else
 | |
|                                     	{
 | |
|                                     	    logger.debug("delete nodeRef:" + nodeRef);
 | |
|                                     		fileFolderService.delete(nodeRef);
 | |
|                                     	}
 | |
|                                     }
 | |
|                                     catch ( Exception ex)
 | |
|                                     {
 | |
|                                         logger.debug("on delete on close", ex);
 | |
|                                         // Propagate retryable errors. Log the rest.
 | |
|                                         if (RetryingTransactionHelper.extractRetryCause(ex) != null)
 | |
|                                         {
 | |
|                                             if (ex instanceof RuntimeException)
 | |
|                                             {
 | |
|                                                 throw (RuntimeException)ex;
 | |
|                                             }
 | |
|                                             else
 | |
|                                             {
 | |
|                                                 throw new AlfrescoRuntimeException("Error during delete on close, " + file.getFullName(), ex);
 | |
|                                             }
 | |
|                                         }
 | |
|                                         if ( logger.isWarnEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                             logger.warn("Error during delete on close, " + file.getFullName(), ex);
 | |
|                                     }
 | |
|                                     
 | |
|                                     // Return a node ref to update in the state table
 | |
|                                     return new Pair<NodeRef, Boolean>(nodeRef, isVersionable);            
 | |
|                                 }
 | |
|                                 catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|                                 {
 | |
|                                     // Debug
 | |
|                                     
 | |
|                                     if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                                         logger.debug("Delete on close - access denied, " + file.getFullName());
 | |
|                                     
 | |
|                                     // Convert to a filesystem access denied exception
 | |
|                                     
 | |
|                                     throw new AccessDeniedException("Delete on close " + file.getFullName());
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|     
 | |
|                     return null;
 | |
|                 }});
 | |
|     
 | |
|             if (result != null)
 | |
|             {
 | |
|                 // Check if there is a quota manager enabled, release space back to the user quota
 | |
|                 
 | |
|                 if ( ctx.hasQuotaManager())
 | |
|                     ctx.getQuotaManager().releaseSpace(sess, tree, file.getFileId(), file.getFullName(), fileSize);
 | |
|     
 | |
|                 // Set the file state to indicate a delete on close
 | |
|     
 | |
|                 if (ctx.hasStateCache())
 | |
|                 {
 | |
|                     if (result.getSecond())
 | |
|                     {
 | |
|     
 | |
|                         // Get, or create, the file state
 | |
|     
 | |
|                         FileState fState = ctx.getStateCache().findFileState(file.getFullName(), true);
 | |
|     
 | |
|                         // Indicate that the file was deleted via a delete on close request
 | |
|     
 | |
|                         fState.setFileStatus(DeleteOnClose);
 | |
|     
 | |
|                         // Make sure the file state is cached for a short while, save the noderef details
 | |
|     
 | |
|                         fState.setExpiryTime(System.currentTimeMillis() + FileState.RenameTimeout);
 | |
|                         fState.setFilesystemObject(result.getFirst());
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|     
 | |
|                         // Remove the file state
 | |
|     
 | |
|                         ctx.getStateCache().removeFileState(file.getFullName());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else if (file.hasDeleteOnClose() && (file instanceof PseudoNetworkFile || file instanceof MemoryNetworkFile)
 | |
|                     && hasPseudoFileInterface(ctx))
 | |
|             {
 | |
|                 // Delete the pseudo file
 | |
|     
 | |
|                 getPseudoFileInterface(ctx).deletePseudoFile(sess, tree, file.getFullName());
 | |
|     
 | |
|             }
 | |
|             
 | |
|             // DEBUG
 | |
|             
 | |
|             if (logger.isDebugEnabled() && (ctx.hasDebug(AlfrescoContext.DBG_FILE) || ctx.hasDebug(AlfrescoContext.DBG_RENAME))) {
 | |
|                 logger.debug("Closed file: network file=" + file + " delete on close=" + file.hasDeleteOnClose());
 | |
|                 if ( file.hasDeleteOnClose() == false && file instanceof ContentNetworkFile) {
 | |
|                     ContentNetworkFile cFile = (ContentNetworkFile) file;
 | |
|                     logger.debug("  File " + file.getFullName() + ", version=" + nodeService.getProperty( cFile.getNodeRef(), ContentModel.PROP_VERSION_LABEL));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         // Make sure we clean up before propagating exceptions
 | |
|         catch (IOException e)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 doInWriteTransaction(sess, errorHandler);
 | |
|             }
 | |
|             catch (Throwable t)
 | |
|             {
 | |
|                 logger.error(t.getMessage(), t);
 | |
|             }
 | |
|             throw e;
 | |
| 
 | |
|         }
 | |
|         catch (RuntimeException e)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 doInWriteTransaction(sess, errorHandler);
 | |
|             }
 | |
|             catch (Throwable t)
 | |
|             {
 | |
|                 logger.error(t.getMessage(), t);
 | |
|             }
 | |
|             throw e;
 | |
|         }
 | |
|         catch (Error e)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 doInWriteTransaction(sess, errorHandler);
 | |
|             }
 | |
|             catch (Throwable t)
 | |
|             {
 | |
|                 logger.error(t.getMessage(), t);
 | |
|             }
 | |
|             throw e;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Delete the specified file.
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection
 | |
|      * @param name NetworkFile
 | |
|      * @exception java.io.IOException The exception description.
 | |
|      */
 | |
|     public void deleteFile(final SrvSession sess, final TreeConnection tree, final String name) throws IOException
 | |
|     {
 | |
|         // Get the device context
 | |
|         
 | |
|         if ( logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("Delete file - " + name);
 | |
|         }
 | |
|         
 | |
|         final ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             // Check if pseudo files are enabled
 | |
|             
 | |
|             if ( hasPseudoFileInterface(ctx))
 | |
|             {
 | |
|                 // Check if the file name is a pseudo file name
 | |
|                 
 | |
|                 if ( getPseudoFileInterface( ctx).isPseudoFile(sess, tree, name)) {
 | |
|                     
 | |
|                     // Make sure the parent folder has a file state, and the path exists
 | |
|     
 | |
|                     String[] paths = FileName.splitPath( name);
 | |
|                     FileState fstate = ctx.getStateCache().findFileState( paths[0]);
 | |
|                     
 | |
|                     if ( fstate != null) {
 | |
| 
 | |
|                         // Check if the path is to a pseudo file
 | |
|                         
 | |
|                         PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( sess, tree, name);
 | |
|                         if ( pfile != null)
 | |
|                         {
 | |
|                             // Delete the pseudo file
 | |
| 
 | |
|                             getPseudoFileInterface( ctx).deletePseudoFile( sess, tree, name);
 | |
|                             return;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Check if there is a quota manager enabled, if so then we need to save the current file size
 | |
|             
 | |
|             final QuotaManager quotaMgr = ctx.getQuotaManager();
 | |
| 
 | |
|             // Perform repository updates in a retryable write transaction
 | |
|             Callable<Void> postTxn = doInWriteTransaction(sess, new CallableIO<Callable<Void>>()
 | |
|             {
 | |
|                 public Callable<Void> call() throws IOException
 | |
|                 {
 | |
|                     // Get the node and delete it
 | |
|                     final NodeRef nodeRef = getNodeForPath(tree, name);
 | |
|                     
 | |
|                     Callable<Void> result = null;
 | |
|                     if (fileFolderService.exists(nodeRef))
 | |
|                     {
 | |
|                         // Get the size of the file being deleted
 | |
|                         
 | |
|                         final FileInfo fInfo = quotaMgr == null ? null : getFileInformation(sess, tree, name);
 | |
| 
 | |
|                         // Check if the node is versionable
 | |
| 
 | |
|                         final boolean isVersionable = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE);
 | |
| 
 | |
|                         if(logger.isDebugEnabled())
 | |
|                         {
 | |
|                             logger.debug("deleted file" + name);
 | |
|                         }
 | |
|                         fileFolderService.delete(nodeRef);
 | |
| 
 | |
|                         // Return the operations to perform when the transaction succeeds
 | |
|                         
 | |
|                         result = new Callable<Void>()
 | |
|                         {
 | |
| 
 | |
|                             public Void call() throws Exception
 | |
|                             {
 | |
|                                 // Remove the file state
 | |
| 
 | |
|                                 if (ctx.hasStateCache())
 | |
|                                 {
 | |
|                                     // Check if the node is versionable, cache the node details for a short while
 | |
| 
 | |
|                                     if (isVersionable == true)
 | |
|                                     {
 | |
| 
 | |
|                                         // Make sure the file state is cached for a short while, a new file may be
 | |
|                                         // renamed to the same name
 | |
|                                         // in which case we can connect the file to the previous version history
 | |
| 
 | |
|                                         FileState delState = ctx.getStateCache().findFileState(name, true);
 | |
| 
 | |
|                                         if(logger.isDebugEnabled())
 | |
|                                         {
 | |
|                                             logger.debug("set delete on close" + name);
 | |
|                                         }
 | |
|                                         delState.setExpiryTime(System.currentTimeMillis() + FileState.DeleteTimeout);
 | |
|                                         delState.setFileStatus(DeleteOnClose);
 | |
|                                         delState.setFilesystemObject(nodeRef);
 | |
|                                     }
 | |
|                                     else
 | |
|                                     {
 | |
| 
 | |
|                                         // Remove the file state
 | |
| 
 | |
|                                         ctx.getStateCache().removeFileState(name);
 | |
|                                     }
 | |
| 
 | |
|                                     // Update, or create, a parent folder file state
 | |
| 
 | |
|                                     String[] paths = FileName.splitPath(name);
 | |
|                                     if (paths[0] != null && paths[0].length() > 1)
 | |
|                                     {
 | |
|                                         // Get the file state for the parent folder
 | |
| 
 | |
|                                         FileState parentState = getStateForPath(tree, paths[0]);
 | |
|                                         if (parentState == null && ctx.hasStateCache())
 | |
|                                             parentState = ctx.getStateCache().findFileState(paths[0], true);
 | |
| 
 | |
|                                         // Update the modification timestamp
 | |
| 
 | |
|                                         parentState.updateModifyDateTime();
 | |
|                                     }
 | |
|                                 }
 | |
| 
 | |
|                                 // Release the space back to the users quota
 | |
| 
 | |
|                                 if (quotaMgr != null)
 | |
|                                     quotaMgr.releaseSpace(sess, tree, fInfo.getFileId(), name, fInfo.getSize());
 | |
|                                 
 | |
|                                 return null;
 | |
|                             }
 | |
|                         };
 | |
|                     }
 | |
| 
 | |
|                     // Debug
 | |
|                     
 | |
|                     if (logger.isDebugEnabled() && (ctx.hasDebug(AlfrescoContext.DBG_FILE) || ctx.hasDebug(AlfrescoContext.DBG_RENAME)))
 | |
|                         logger.debug("Deleted file: " + name + ", node=" + nodeRef);
 | |
| 
 | |
|                     return result;
 | |
|                 }
 | |
|             });
 | |
|             
 | |
|             // Perform state updates after the transaction succeeds
 | |
|             postTxn.call();
 | |
|         }
 | |
|         catch (NodeLockedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Delete file - access denied (locked)");
 | |
|             
 | |
|             // Convert to a filesystem access denied status
 | |
|             
 | |
|             throw new AccessDeniedException("Delete " + name);
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Delete file - access denied");
 | |
|             
 | |
|             // Convert to a filesystem access denied status
 | |
|             
 | |
|             throw new AccessDeniedException("Delete " + name);
 | |
|         }
 | |
|         catch (IOException ex)
 | |
|         {
 | |
|             // Allow I/O Exceptions to pass through
 | |
|             throw ex;
 | |
|         }
 | |
|         catch (Exception ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
 | |
|                 logger.debug("Delete file error", ex);
 | |
|             
 | |
|             // Convert to a general I/O exception
 | |
|             
 | |
|             IOException ioe = new IOException("Delete file " + name);
 | |
|             ioe.initCause(ex);
 | |
|             throw ioe;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Rename the specified file.
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection
 | |
|      * @param oldName java.lang.String
 | |
|      * @param newName java.lang.String
 | |
|      * @exception java.io.IOException The exception description.
 | |
|      */
 | |
|     public void renameFile(final SrvSession sess, final TreeConnection tree, final String oldName, final String newName)
 | |
|             throws IOException
 | |
|     {
 | |
|         // Create the transaction (initially read-only)
 | |
| 
 | |
|         beginReadTransaction(sess);
 | |
| 
 | |
|         // Get the device context
 | |
| 
 | |
|         final ContentContext ctx = (ContentContext) tree.getContext();
 | |
| 
 | |
|         // DEBUG
 | |
| 
 | |
|         if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|             logger.debug("Rename oldName=" + oldName + ", newName=" + newName);
 | |
| 
 | |
|         try
 | |
|         {
 | |
|             // Get the file/folder to move
 | |
| 
 | |
|             final NodeRef nodeToMoveRef = getNodeForPath(tree, 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 = getNodeForPath(tree, splitPaths[0]);
 | |
|             final NodeRef sourceFolderRef = getNodeForPath(tree, 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]);
 | |
| 
 | |
|             // Get the file state for the old file, if available
 | |
| 
 | |
|             final FileState oldState = ctx.getStateCache().findFileState(oldName, true);
 | |
| 
 | |
|             // Check if we are renaming a folder, or the rename is to a different folder
 | |
| 
 | |
|             boolean isFolder = cifsHelper.isDirectory(nodeToMoveRef);
 | |
| 
 | |
|             if ( isFolder == true || sameFolder == false) {
 | |
|                 
 | |
|                 // Rename or move the file/folder
 | |
| 
 | |
|                 doInWriteTransaction(sess, new CallableIO<Void>()
 | |
|                 {
 | |
| 
 | |
|                     public Void call() throws IOException
 | |
|                     {
 | |
|                         if (sameFolder == true)
 | |
|                             cifsHelper.rename(nodeToMoveRef, name);
 | |
|                         else
 | |
|                             cifsHelper.move(nodeToMoveRef, sourceFolderRef, targetFolderRef, name);
 | |
|                         return null;
 | |
|                     }
 | |
|                 });
 | |
| 
 | |
|                 // Update the old file state
 | |
| 
 | |
|                 if (oldState != null)
 | |
|                 {
 | |
|                     // Update the file state index to use the new name
 | |
| 
 | |
|                     ctx.getStateCache().renameFileState(newName, oldState, isFolder);
 | |
|                 }
 | |
| 
 | |
|                 // DEBUG
 | |
| 
 | |
|                 if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                     logger.debug("  Renamed " + (isFolder ? "folder" : "file") + " using "
 | |
|                             + (sameFolder ? "rename" : "move"));
 | |
|             }
 | |
|             else 
 | |
|             {
 | |
|                 
 | |
|                 // Rename a file within the same folder
 | |
|                 //
 | |
|                 // Check if the target file already exists
 | |
| 
 | |
|                 final int newExists = fileExists(sess, tree, newName);
 | |
|                 final FileState newState = ctx.getStateCache().findFileState(newName, true);
 | |
| 
 | |
|                 List<Runnable> postTxn = doInWriteTransaction(sess, new CallableIO<List<Runnable>>()
 | |
|                 {
 | |
| 
 | |
|                     public List<Runnable> call() throws IOException
 | |
|                     {
 | |
|                         List<Runnable> postTxn = new LinkedList<Runnable>();
 | |
| 
 | |
|                         NodeRef targetNodeRef = null;
 | |
| 
 | |
|                         boolean isFromVersionable = nodeService.hasAspect( nodeToMoveRef, ContentModel.ASPECT_VERSIONABLE);
 | |
|                         boolean typesCompatible = true;
 | |
|                         
 | |
|                         // HACK ALF-3856: Version History lost when Versionable Content renamed via CIFS
 | |
|                         //                This code will move into the repo layer (or just above it)
 | |
|                         //                and this complexity removed from here.
 | |
|                         //          Attempt to detect normal renames.  Hack alert!
 | |
|                         Pattern renameShufflePattern = ctx.getRenameShufflePattern();
 | |
|                         boolean renameShuffle = isRenameShuffle(oldName, newName, renameShufflePattern);
 | |
|                         if (logger.isDebugEnabled())
 | |
|                         {
 | |
|                             logger.debug(
 | |
|                                     "Rename file: \n" +
 | |
|                                     "   Old name:      " + oldName + "\n" +
 | |
|                                     "   New name:      " + newName + "\n" +
 | |
|                                     "   Pattern:       " + renameShufflePattern.pattern() + "\n" +
 | |
|                                     "   Is shuffle:    " + renameShuffle + "\n" +
 | |
|                                     "   Source folder: " + sourceFolderRef + "\n" +
 | |
|                                     "   Target folder: " + targetFolderRef + "\n" +
 | |
|                                     "   Node:          " + nodeToMoveRef + "\n" +
 | |
|                                     "   Aspects:       " + nodeService.getAspects(nodeToMoveRef));
 | |
|                                     
 | |
|                         }
 | |
|                         
 | |
|                         if ( newExists == FileStatus.FileExists)
 | |
|                         {
 | |
|                             
 | |
|                             // Use the existing file as the target node
 | |
|                             
 | |
|                             targetNodeRef = getNodeForPath( tree, newName);
 | |
|                         }
 | |
|                         else if (renameShuffle)
 | |
|                         {
 | |
|                             logger.debug("is rename shuffle");
 | |
|                             // Check if the target has a renamed or delete-on-close state
 | |
|                             
 | |
|                             if ( newState.getFileStatus() == FileRenamed) 
 | |
|                             {
 | |
|                                 logger.debug("file status == FileRenamed");
 | |
|                                 
 | |
|                                 
 | |
|                                 // DEBUG
 | |
|                                 
 | |
|                                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                     logger.debug("  Using renamed node, " + newState);
 | |
|                                 
 | |
|                                 NodeRef newStateNode = (NodeRef)newState.getFilesystemObject();
 | |
|                                 QName oldType = nodeService.getType(nodeToMoveRef); 
 | |
|                                 QName newType = nodeService.getType(newStateNode);
 | |
|                                 if (oldType.equals(newType)) {
 | |
| 
 | |
|                                     // Use the renamed node to clone aspects/state if it is of the correct type
 | |
|                                     
 | |
|                                     cloneNode(name, newStateNode, nodeToMoveRef, ctx);
 | |
|                                 }
 | |
|                                 else
 | |
|                                 {
 | |
|                                     logger.debug("not renamed, must create new node");
 | |
|                                     // Otherwise we must create a node of the correct type
 | |
|                                     targetNodeRef = cifsHelper.createNode(ctx.getRootNode(), newName, newType);
 | |
|                                     
 | |
|                                     // Force a copy to this target
 | |
|                                     typesCompatible = false;
 | |
|                                     
 | |
|                                     // DEBUG
 | |
|                                     
 | |
|                                     if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                         logger.debug("  Created new node for " + newName + " type " + newType + ", isFromVersionable=false");
 | |
|             
 | |
|                                     // Copy aspects from the original state
 | |
|                                     
 | |
|                                     cloneNode( name, newStateNode, targetNodeRef, ctx);
 | |
|                                 }
 | |
| 
 | |
|                                 //Change state for tmp node to allow delete it without special permission
 | |
|                                 String newStateNodeName = (String) nodeService.getProperty(newStateNode, ContentModel.PROP_NAME);
 | |
|                                 FileState stateForTmp = ctx.getStateCache().findFileState(newName.substring(0,newName.lastIndexOf("\\")) +"\\"+ newStateNodeName, true);
 | |
|                                 stateForTmp.addAttribute(CanDeleteWithoutPerms, true);
 | |
|                                 stateForTmp.setFileStatus(FileStatus.FileExists); 
 | |
|                                 stateForTmp.setExpiryTime(System.currentTimeMillis() + FileState.DeleteTimeout);
 | |
|                                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                     logger.debug("  Set CanDeleteWithoutPerms=true for " + stateForTmp);
 | |
|                                 
 | |
|                             }
 | |
|                             else if ( newState.getFileStatus() == DeleteOnClose) 
 | |
|                             {
 | |
|                                 logger.debug("file state is delete on close");
 | |
|                                 
 | |
|                                 // DEBUG
 | |
|                                 
 | |
|                                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                     logger.debug("  Restoring delete-on-close node, " + newState);
 | |
|                                 
 | |
|                                 // Restore the deleted node so we can relink the new version to the old history/properties
 | |
|                                 
 | |
|                                 NodeRef archivedNode = getNodeArchiveService().getArchivedNode((NodeRef) newState.getFilesystemObject());
 | |
|                                 
 | |
|                                 // DEBUG
 | |
|                                 
 | |
|                                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                     logger.debug("  Found archived node " + archivedNode);
 | |
|                                 
 | |
|                                 if ( archivedNode != null && getNodeService().exists(archivedNode) )
 | |
|                                 {
 | |
|                                     // Restore the node
 | |
|                                     
 | |
|                                     targetNodeRef = getNodeService().restoreNode( archivedNode, null, null, null);
 | |
|                                     
 | |
|                                     // DEBUG
 | |
|                                     
 | |
|                                     if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                         logger.debug("  Restored node " + targetNodeRef + ", version=" + nodeService.getProperty( targetNodeRef, ContentModel.PROP_VERSION_LABEL));
 | |
|                                     
 | |
|                                     // Check if the deleted file had a linked node, due to a rename
 | |
|                                     
 | |
|                                     NodeRef linkNode = (NodeRef) newState.findAttribute( AttrLinkNode);
 | |
|                                     
 | |
|                                     if ( linkNode != null && nodeService.exists( linkNode)) {
 | |
|                                         
 | |
|                                         // Clone aspects from the linked node onto the restored node
 | |
|                                         
 | |
|                                         cloneNode( name, linkNode, targetNodeRef, ctx);
 | |
| 
 | |
|                                         // DEBUG
 | |
|                                         
 | |
|                                         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) {
 | |
|                                             logger.debug("  Moved aspects from linked node " + linkNode);
 | |
|         
 | |
|                                             // Check if the node is a working copy
 | |
|                                             
 | |
|                                             NodeRef mainNodeRef = checkOutCheckInService.getCheckedOut(targetNodeRef);
 | |
|                                             if ( mainNodeRef != null)
 | |
|                                             {
 | |
|                                                 // Check if the main document is still locked
 | |
|                                                 LockType lockTyp = lockService.getLockType( mainNodeRef);
 | |
|                                                 logger.debug("  Main node ref lock type = " + lockTyp);
 | |
|                                             }
 | |
|                                         }
 | |
|                                     }
 | |
|                                 }
 | |
|                             }
 | |
| 
 | |
|                             // Check if the node being renamed is versionable
 | |
| 
 | |
|                             else if ( isFromVersionable == true) 
 | |
|                             {
 | |
|                                 logger.debug("from node is versionable");
 | |
|                                 
 | |
|                                 // Create a new node for the target
 | |
|                                 
 | |
|                                 targetNodeRef = cifsHelper.createNode(ctx.getRootNode(), newName, nodeService.getType(nodeToMoveRef));
 | |
|                                 
 | |
|                                 // DEBUG
 | |
|                                 
 | |
|                                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                     logger.debug("  Created new node for " + newName + ", isFromVersionable=true");
 | |
|         
 | |
|                                 // Copy aspects from the original file
 | |
|                                 
 | |
|                                 cloneNode( name, nodeToMoveRef, targetNodeRef, ctx);
 | |
|                                 
 | |
|                                 //Change state for tmp node to allow delete it without special permission
 | |
|                                 FileState stateForTmp = ctx.getStateCache().findFileState(newName, true);
 | |
|                                 stateForTmp.addAttribute(CanDeleteWithoutPerms, true);
 | |
|                                 stateForTmp.setFileStatus(FileStatus.FileExists); 
 | |
|                                 stateForTmp.setExpiryTime(System.currentTimeMillis() + FileState.DeleteTimeout);
 | |
|                                 if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                     logger.debug("  Set CanDeleteWithoutPerms=true for " + stateForTmp);
 | |
|                             }
 | |
|                         }
 | |
| 
 | |
|                         // If the original or target nodes are not versionable and types are compatible then just use a standard rename of the node
 | |
|                         if (    !renameShuffle ||
 | |
|                                 (   !isFromVersionable &&
 | |
|                                     typesCompatible &&
 | |
|                                     ( targetNodeRef == null || nodeService.hasAspect( targetNodeRef, ContentModel.ASPECT_VERSIONABLE) == false)))
 | |
|                         {
 | |
|                             logger.debug("do simple rename");
 | |
| 
 | |
|                             // Rename the file/folder
 | |
| 
 | |
|                             cifsHelper.rename(nodeToMoveRef, name);
 | |
| 
 | |
|                             postTxn.add(new Runnable()
 | |
|                             {
 | |
|                                 public void run()
 | |
|                                 {
 | |
|                                     // Mark the new file as existing
 | |
| 
 | |
|                                     newState.setFileStatus(FileExists);
 | |
|                                     newState.setFilesystemObject(nodeToMoveRef);
 | |
|                                     newState.setFileSize(oldState.getFileSize());
 | |
| 
 | |
|                                     // the date is updated to be properly saved when the document is closed, see MNT-214
 | |
|                                     newState.updateModifyDateTime(oldState.getModifyDateTime());
 | |
| 
 | |
|                                     // Make sure the old file state is cached for a short while, the file may not be open so the
 | |
|                                     // file state could be expired
 | |
| 
 | |
|                                     oldState.setExpiryTime(System.currentTimeMillis() + FileState.DeleteTimeout);
 | |
| 
 | |
|                                     // Indicate that this is a renamed file state, set the node ref of the file that was renamed
 | |
| 
 | |
|                                     oldState.setFileStatus(FileRenamed);
 | |
|                                     oldState.setFilesystemObject(nodeToMoveRef);
 | |
|                                 }
 | |
|                             });
 | |
| 
 | |
|                             // DEBUG
 | |
| 
 | |
|                             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                 logger.debug("  Use standard rename for " + name + "(versionable=" + isFromVersionable + ", targetNodeRef=" + targetNodeRef + ")");
 | |
|                         }
 | |
|                         else 
 | |
|                         {
 | |
|                             
 | |
|                             // Make sure we have a valid target node
 | |
|                             
 | |
|                             if ( targetNodeRef == null) {
 | |
|                                 
 | |
|                                 // DEBUG
 | |
| 
 | |
|                                 if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                                     logger.debug("  No target node for rename");
 | |
| 
 | |
|                                 // Throw an error
 | |
| 
 | |
|                                 throw new AccessDeniedException("No target node for file rename");
 | |
|                             }
 | |
| 
 | |
|                             // Copy content data from the old file to the new file
 | |
| 
 | |
|                             copyContentData(sess, tree, nodeToMoveRef, targetNodeRef, newName);
 | |
| 
 | |
|                             final NodeRef finalTargetNodeRef = targetNodeRef;
 | |
|                             postTxn.add(new Runnable()
 | |
|                             {
 | |
| 
 | |
|                                 public void run()
 | |
|                                 {
 | |
|                                     // Mark the new file as existing
 | |
| 
 | |
|                                     newState.setFileStatus(FileExists);
 | |
|                                     newState.setFilesystemObject(finalTargetNodeRef);
 | |
|                                     newState.setFileSize(oldState.getFileSize());
 | |
| 
 | |
|                                     // the date is updated to be properly saved when the document is closed, see MNT-214
 | |
|                                     newState.updateModifyDateTime(oldState.getModifyDateTime());
 | |
| 
 | |
|                                     // Make sure the old file state is cached for a short while, the file may not be open so the
 | |
|                                     // file state could be expired
 | |
|                                     
 | |
|                                     oldState.setExpiryTime(System.currentTimeMillis() + FileState.DeleteTimeout);
 | |
|                                     
 | |
|                                     // Indicate that this is a deleted file state, set the node ref of the file that was renamed
 | |
|                                     
 | |
|                                     oldState.setFileStatus( DeleteOnClose);
 | |
|                                     oldState.setFilesystemObject(nodeToMoveRef);
 | |
|                                     
 | |
|                                     // Link to the new node, a new file may be renamed into place, we need to transfer aspect/locks
 | |
|                                     
 | |
|                                     oldState.addAttribute( AttrLinkNode, finalTargetNodeRef);
 | |
|                                     
 | |
|                                     // DEBUG
 | |
|                                     
 | |
|                                     if ( logger.isDebugEnabled() && ctx.hasDebug( AlfrescoContext.DBG_RENAME))
 | |
|                                         logger.debug("  Cached delete state for " + oldName);
 | |
|                                 }
 | |
|                             });
 | |
|                             
 | |
|                             logger.debug("delete the old file");
 | |
| 
 | |
|                             // Delete the old file
 | |
|                             if (renameShuffle && isFromVersionable && permissionService.hasPermission(nodeToMoveRef, PermissionService.EDITOR) == AccessStatus.ALLOWED)
 | |
|                             {
 | |
|                                 AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
 | |
|                                 {
 | |
|                                     @Override
 | |
|                                     public Object doWork() throws Exception
 | |
|                                     {
 | |
|                                         if (logger.isDebugEnabled())
 | |
|                                         {
 | |
|                                             logger.debug("Rename shuffle for versioning content is assumed. Deleting " + nodeToMoveRef + " as system user");
 | |
|                                         }
 | |
|                                         if (renameCSVShufflePattern.matcher(newName.toLowerCase()).matches())
 | |
|                                         {
 | |
|                                             Map<QName, Serializable> props = Collections.emptyMap();
 | |
|                                             nodeService.addAspect(nodeToMoveRef, ContentModel.ASPECT_SOFT_DELETE, props);
 | |
|                                         }
 | |
|                                         else
 | |
|                                         {
 | |
|                                             nodeService.deleteNode(nodeToMoveRef);
 | |
|                                         }
 | |
|                                         return null;
 | |
|                                     }
 | |
| 
 | |
|                                 }, AuthenticationUtil.getSystemUserName());
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 if (logger.isDebugEnabled())
 | |
|                                 {
 | |
|                                     logger.debug("Deleting " + nodeToMoveRef + " as user: " + AuthenticationUtil.getFullyAuthenticatedUser());
 | |
|                                 }
 | |
|                                 nodeService.deleteNode(nodeToMoveRef);
 | |
|                             }
 | |
| 
 | |
|                         }
 | |
| 
 | |
|                         return postTxn;
 | |
|                     }
 | |
|                 });
 | |
| 
 | |
|                 logger.debug("running post txns");
 | |
|                 // Run the required state-changing logic once the retrying transaction has completed successfully
 | |
|                 for (Runnable runnable : postTxn)
 | |
|                 {
 | |
|                     runnable.run();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
| 
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                 logger.debug("Rename file - access denied, " + oldName);
 | |
| 
 | |
|             // Convert to a filesystem access denied status
 | |
| 
 | |
|             throw new AccessDeniedException("Rename file " + oldName);
 | |
|         }
 | |
|         catch (NodeLockedException ex)
 | |
|         {
 | |
|             // Debug
 | |
| 
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                 logger.debug("Rename file", ex);
 | |
| 
 | |
|             // Convert to an filesystem access denied exception
 | |
| 
 | |
|             throw new AccessDeniedException("Node locked " + oldName);
 | |
|         }
 | |
|         catch (InvalidNodeRefException ex)
 | |
|         {
 | |
|             // Debug
 | |
| 
 | |
|             if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                 logger.debug("Rename file - file doesn't exist, " + oldName, ex);
 | |
| 
 | |
|             // Convert to a filesystem access denied status
 | |
| 
 | |
|             throw new FileNotFoundException("File doesn't exist " + oldName);	
 | |
|         }
 | |
|         catch (RuntimeException ex)
 | |
|         {
 | |
|             // Unexpected Exception being consumed here - hence the error logging.
 | |
|             logger.error("Unable to rename file" + oldName, ex);
 | |
|             
 | |
|             // Convert to a general I/O exception
 | |
| 
 | |
|             throw new AccessDeniedException("Rename file " + oldName);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Set file information
 | |
|      * 
 | |
|      * @param sess SrvSession
 | |
|      * @param tree TreeConnection
 | |
|      * @param name String
 | |
|      * @param info FileInfo
 | |
|      * @exception IOException
 | |
|      */
 | |
|     public void setFileInformation(SrvSession sess, final TreeConnection tree, final String name , final FileInfo info) throws IOException
 | |
|     {
 | |
|         // Get the device context
 | |
|         
 | |
|         final ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             // Check if pseudo files are enabled
 | |
|             
 | |
|             if ( hasPseudoFileInterface(ctx) &&
 | |
|                     getPseudoFileInterface(ctx).isPseudoFile( sess, tree, name))
 | |
|             {
 | |
|                 // Allow the file information to be changed
 | |
|                 
 | |
|                 return;
 | |
|             }
 | |
|             
 | |
|             final FileState fstate = getStateForPath(tree, name);
 | |
| 
 | |
|             doInWriteTransaction(sess, new CallableIO<Pair<Boolean, Boolean>>(){
 | |
| 
 | |
|                 public Pair<Boolean, Boolean> call() throws IOException
 | |
|                 {
 | |
|                     // Get the file/folder node
 | |
|                     
 | |
|                     NodeRef nodeRef = getNodeForPath(tree, name);
 | |
|                     
 | |
|                     // Check permissions on the file/folder node
 | |
|                     
 | |
|                     if ( permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED)
 | |
|                     {
 | |
|                         throw new AccessDeniedException("No write access to " + name);
 | |
|                     }
 | |
| 
 | |
|                     // Inhibit versioning for this transaction
 | |
|                     
 | |
|                     getPolicyFilter().disableBehaviour( ContentModel.ASPECT_VERSIONABLE);
 | |
|                     
 | |
|                     // Check if the file is being marked for deletion, if so then check if the file is locked
 | |
|                     
 | |
|                     if ( info.hasSetFlag(FileInfo.SetDeleteOnClose) && info.hasDeleteOnClose())
 | |
|                     {
 | |
|                         // Check deleting permissions on the node if action of deleting was configured only
 | |
|                         if ((AccessStatus.DENIED == permissionService.hasPermission(nodeRef, PermissionService.DELETE))
 | |
|                                 && ((null == fstate) || (null == fstate.findAttribute(CanDeleteWithoutPerms))))
 | |
|                         {
 | |
|                             throw new org.alfresco.repo.security.permissions.AccessDeniedException("No delete access to " + name);
 | |
|                         }
 | |
| 
 | |
|                         // Check if the node is locked
 | |
|                         
 | |
|                         if ( nodeService.hasAspect( nodeRef, ContentModel.ASPECT_LOCKABLE))
 | |
|                         {
 | |
|                             // Get the lock type, if any
 | |
|                             
 | |
|                             String lockTypeStr = (String) nodeService.getProperty( nodeRef, ContentModel.PROP_LOCK_TYPE);
 | |
|                             
 | |
|                             if ( lockTypeStr != null)
 | |
|                                 throw new org.alfresco.repo.security.permissions.AccessDeniedException("Node locked, cannot mark for delete");
 | |
|                         }
 | |
|                         
 | |
|                         // Get the node for the folder
 | |
|                         
 | |
|                         if (fileFolderService.exists(nodeRef))
 | |
|                         {
 | |
|                             // Check if it is a folder that is being deleted, make sure it is empty
 | |
|                             
 | |
|                             boolean isFolder = true;
 | |
|                             
 | |
|                             if ( fstate != null)
 | |
|                                 isFolder = fstate.isDirectory();
 | |
|                             else {
 | |
|                                 ContentFileInfo cInfo = cifsHelper.getFileInformation( nodeRef, isReadOnly, isLockedFilesAsOffline);
 | |
|                                 if ( cInfo != null && cInfo.isDirectory() == false)
 | |
|                                     isFolder = false;
 | |
|                             }
 | |
| 
 | |
|                             // Check if the folder is empty
 | |
|                             
 | |
|                             if ( isFolder == true && cifsHelper.isFolderEmpty( nodeRef) == false)
 | |
|                                 throw new DirectoryNotEmptyException( name);
 | |
|                         }
 | |
|                                                 
 | |
|                         // DEBUG
 | |
|                         
 | |
|                         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                             logger.debug("Set deleteOnClose=true file=" + name);
 | |
|                     }
 | |
|                     
 | |
|                     // Set the creation and modified date/time
 | |
|                     Map<QName, Serializable> auditableProps = new HashMap<QName, Serializable>(5);
 | |
|                     
 | |
|                     if ( info.hasSetFlag(FileInfo.SetCreationDate))
 | |
|                     {
 | |
|                         // Set the creation date on the file/folder node
 | |
|                         Date createDate = new Date( info.getCreationDateTime());
 | |
|                         auditableProps.put(ContentModel.PROP_CREATED, createDate);
 | |
|                     }
 | |
|                     if ( info.hasSetFlag(FileInfo.SetModifyDate)) {
 | |
|                         
 | |
|                         // Set the modification date on the file/folder node
 | |
|                         Date modifyDate = new Date( info.getModifyDateTime());
 | |
|                         auditableProps.put(ContentModel.PROP_MODIFIED, modifyDate);
 | |
|                     }
 | |
| 
 | |
|                     // Did we have any cm:auditable properties?
 | |
|                     if (auditableProps.size() > 0)
 | |
|                     {
 | |
|                         getPolicyFilter().disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
 | |
|                         nodeService.addProperties(nodeRef, auditableProps);
 | |
|                         
 | |
|                         // DEBUG
 | |
|                         if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                             logger.debug("Set auditable props: " + auditableProps + " file=" + name);
 | |
|                     }
 | |
|                     
 | |
|                     return null;
 | |
|                 }});
 | |
|             // Check if the file is being marked for deletion, if so then check if the file is locked
 | |
| 
 | |
|             // Update the change date/time
 | |
|             if (fstate != null)
 | |
|             {
 | |
|                 if (info.hasSetFlag(FileInfo.SetDeleteOnClose) && info.hasDeleteOnClose()
 | |
|                         || info.hasSetFlag(FileInfo.SetCreationDate))
 | |
|                 {
 | |
| 
 | |
|                     fstate.updateChangeDateTime();
 | |
|                 }
 | |
| 
 | |
|                 // Set the modification date/time
 | |
| 
 | |
|                 if (info.hasSetFlag(FileInfo.SetModifyDate))
 | |
|                 {
 | |
|                     // Update the change date/time, clear the cached modification date/time
 | |
|                     fstate.updateChangeDateTime();
 | |
|                     Date modifyDate = new Date( info.getModifyDateTime());
 | |
|                     fstate.updateModifyDateTime(modifyDate.getTime()); 
 | |
|                 }                
 | |
|             }
 | |
|         }
 | |
|         catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                 logger.debug("Set file information - access denied, " + name, ex);
 | |
|             
 | |
|             // Convert to a filesystem access denied status
 | |
|             
 | |
|             throw new AccessDeniedException("Set file information " + name);
 | |
|         }
 | |
|         catch (RuntimeException ex)
 | |
|         {
 | |
|             // Debug
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO))
 | |
|                 logger.debug("Open file error", ex);
 | |
|             
 | |
|             // Convert to a general I/O exception
 | |
|             
 | |
|             throw new IOException("Set file information " + name);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Truncate a file to the specified size
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection
 | |
|      * @param file Network file details
 | |
|      * @param size New file length
 | |
|      * @exception java.io.IOException The exception description.
 | |
|      */
 | |
|     public void truncateFile(SrvSession sess, TreeConnection tree, NetworkFile file, long size) throws IOException
 | |
|     {
 | |
|         //  Keep track of the allocation/release size in case the file resize fails
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         
 | |
|         long allocSize   = 0L;
 | |
|         long releaseSize = 0L;
 | |
|         
 | |
|         //  Check if there is a quota manager
 | |
| 
 | |
|         QuotaManager quotaMgr = ctx.getQuotaManager();
 | |
|               
 | |
|         if ( ctx.hasQuotaManager()) {
 | |
|           
 | |
|             // Check if the file content has been opened, we need the content to be opened to get the
 | |
|             // current file size
 | |
|             
 | |
|             if ( file instanceof ContentNetworkFile) {
 | |
|                 ContentNetworkFile contentFile = (ContentNetworkFile) file;
 | |
|                 if ( contentFile.hasContent() == false)
 | |
|                     contentFile.openContent( false, false);
 | |
|             }
 | |
|             else
 | |
|                 throw new IOException("Invalid file class type, " + file.getClass().getName());
 | |
|             
 | |
|             //  Determine if the new file size will release space or require space allocating
 | |
|           
 | |
|             if ( size > file.getFileSize()) {
 | |
|             
 | |
|                 //  Calculate the space to be allocated
 | |
|             
 | |
|                 allocSize = size - file.getFileSize();
 | |
|             
 | |
|                 //  Allocate space to extend the file
 | |
|             
 | |
|                 quotaMgr.allocateSpace(sess, tree, file, allocSize);
 | |
|             }
 | |
|             else {
 | |
|             
 | |
|               //  Calculate the space to be released as the file is to be truncated, release the space if
 | |
|               //  the file truncation is successful
 | |
|             
 | |
|               releaseSize = file.getFileSize() - size;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         //  Check if this is a file extend, update the cached allocation size if necessary
 | |
|         
 | |
|         if ( file instanceof ContentNetworkFile) {
 | |
|             
 | |
|             // Get the cached state for the file
 | |
|         
 | |
|             ContentNetworkFile contentFile = (ContentNetworkFile) file;
 | |
|             FileState fstate = contentFile.getFileState();
 | |
|             if ( fstate != null && size > fstate.getAllocationSize())
 | |
|                 fstate.setAllocationSize( size);
 | |
|         }
 | |
|         
 | |
|         //  Set the file length
 | |
| 
 | |
|         try {
 | |
|             file.truncateFile(size);
 | |
|         }
 | |
|         catch (IOException ex) {
 | |
|           
 | |
|             //  Check if we allocated space to the file
 | |
|           
 | |
|             if ( allocSize > 0 && quotaMgr != null)
 | |
|                 quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, allocSize);
 | |
| 
 | |
|             //  Rethrow the exception
 | |
|           
 | |
|             throw ex;       
 | |
|         }
 | |
|         
 | |
|         //  Check if space has been released by the file resizing
 | |
|         
 | |
|         if ( releaseSize > 0 && quotaMgr != null)
 | |
|             quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, releaseSize);
 | |
| 
 | |
|         // Debug
 | |
| 
 | |
|         if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILEIO))
 | |
|             logger.debug("Truncated file: network file=" + file + " size=" + size);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Read a block of data from the specified file.
 | |
|      * 
 | |
|      * @param sess Session details
 | |
|      * @param tree Tree connection
 | |
|      * @param file Network file
 | |
|      * @param buffer Buffer to return data to
 | |
|      * @param bufferPosition Starting position in the return buffer
 | |
|      * @param size Maximum size of data to return
 | |
|      * @param fileOffset File offset to read data
 | |
|      * @return Number of bytes read
 | |
|      * @exception java.io.IOException The exception description.
 | |
|      */
 | |
|     public int readFile(
 | |
|             SrvSession sess, TreeConnection tree, NetworkFile file,
 | |
|             byte[] buffer, int bufferPosition, int size, long fileOffset) throws IOException
 | |
|     {
 | |
|         // Check if the file is a directory
 | |
|         
 | |
|         if(file.isDirectory())
 | |
|             throw new AccessDeniedException();
 | |
|             
 | |
|         // If the content channel is not open for the file then start a transaction
 | |
|         
 | |
|         if ( file instanceof ContentNetworkFile)
 | |
|         {
 | |
|             ContentNetworkFile contentFile = (ContentNetworkFile) file;
 | |
|             
 | |
|             if ( contentFile.hasContent() == false)
 | |
|                 beginReadTransaction( sess);
 | |
|         }
 | |
|         
 | |
|         // Read a block of data from the file
 | |
|         
 | |
|         int count = file.readFile(buffer, size, bufferPosition, fileOffset);
 | |
|         
 | |
|         if ( count == -1)
 | |
|         {
 | |
|             // Read count of -1 indicates a read past the end of file
 | |
|             
 | |
|             count = 0;
 | |
|         }
 | |
|         
 | |
|         // Debug
 | |
| 
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         
 | |
|         if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILEIO))
 | |
|             logger.debug("Read bytes from file: network file=" + file + " buffer size=" + buffer.length + " buffer pos=" + bufferPosition +
 | |
|                     " size=" + size + " file offset=" + fileOffset + " bytes read=" + count);
 | |
|         
 | |
|         return count;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Seek to the specified file position.
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection
 | |
|      * @param file Network file.
 | |
|      * @param pos Position to seek to.
 | |
|      * @param typ Seek type.
 | |
|      * @return New file position, relative to the start of file.
 | |
|      */
 | |
|     public long seekFile(SrvSession sess, TreeConnection tree, NetworkFile file, long pos, int typ) throws IOException
 | |
|     {
 | |
|         // Check if the file is a directory
 | |
|         
 | |
|         if ( file.isDirectory())
 | |
|             throw new AccessDeniedException();
 | |
|       
 | |
|         // If the content channel is not open for the file then start a transaction
 | |
|         
 | |
|         ContentNetworkFile contentFile = (ContentNetworkFile) file;
 | |
|         
 | |
|         if ( contentFile.hasContent() == false)
 | |
|             beginReadTransaction( sess);
 | |
|         
 | |
|         // Set the file position
 | |
| 
 | |
|         return file.seekFile(pos, typ);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Write a block of data to the file.
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection
 | |
|      * @param file Network file details
 | |
|      * @param buffer byte[] Data to be written
 | |
|      * @param bufferOffset Offset within the buffer that the data starts
 | |
|      * @param size int Data length
 | |
|      * @param fileOffset Position within the file that the data is to be written.
 | |
|      * @return Number of bytes actually written
 | |
|      * @exception java.io.IOException The exception description.
 | |
|      */
 | |
|     public int writeFile(SrvSession sess, TreeConnection tree, NetworkFile file,
 | |
|             byte[] buffer, int bufferOffset, int size, long fileOffset) throws IOException
 | |
|     {
 | |
|         // If the content channel is not open for the file then start a transaction
 | |
|         
 | |
|         if ( file instanceof ContentNetworkFile)
 | |
|         {
 | |
|             ContentNetworkFile contentFile = (ContentNetworkFile) file;
 | |
|             
 | |
|             if ( contentFile.hasContent() == false)
 | |
|                 beginReadTransaction( sess);
 | |
|         }
 | |
| 
 | |
|         //  Check if there is a quota manager
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         QuotaManager quotaMgr = ctx.getQuotaManager();
 | |
|         long curSize = file.getFileSize();
 | |
|         
 | |
|         if ( quotaMgr != null) {
 | |
|           
 | |
|           //  Check if the file requires extending
 | |
|           
 | |
|           long extendSize = 0L;
 | |
|           long endOfWrite = fileOffset + size;
 | |
|           
 | |
|           if ( endOfWrite > curSize) {
 | |
|             
 | |
|             //  Calculate the amount the file must be extended
 | |
| 
 | |
|             extendSize = endOfWrite - file.getFileSize();
 | |
|             
 | |
|             //  Allocate space for the file extend
 | |
|             
 | |
|             quotaMgr.allocateSpace(sess, tree, file, extendSize);
 | |
|           }
 | |
|         }
 | |
|         
 | |
|         // Write to the file
 | |
|         
 | |
|         file.writeFile(buffer, size, bufferOffset, fileOffset);
 | |
| 
 | |
|         // Check if the file size was reduced by the write, may have been extended previously
 | |
|         
 | |
|         if ( quotaMgr != null) {
 | |
|             
 | |
|             // Check if the file size reduced
 | |
|             
 | |
|             if ( file.getFileSize() < curSize) {
 | |
|                 
 | |
|                 // Release space that was freed by the write
 | |
|                 
 | |
|                 quotaMgr.releaseSpace( sess, tree, file.getFileId(), file.getFullName(), curSize - file.getFileSize());
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Debug
 | |
| 
 | |
|         if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILEIO))
 | |
|             logger.debug("Wrote bytes to file: network file=" + file + " buffer size=" + buffer.length + " size=" + size + " file offset=" + fileOffset);
 | |
| 
 | |
|         return size;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the node for the specified path
 | |
|      * 
 | |
|      * @param tree TreeConnection
 | |
|      * @param path String
 | |
|      * @return NodeRef
 | |
|      * @exception FileNotFoundException
 | |
|      */
 | |
|     public NodeRef getNodeForPath(TreeConnection tree, String path)
 | |
|         throws FileNotFoundException
 | |
|     {
 | |
|         // Check if there is a cached state for the path
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         
 | |
|         NodeRef result = null;
 | |
|         if ( ctx.hasStateCache())
 | |
|         {
 | |
|             // Try and get the node ref from an in memory file state
 | |
|             FileState fstate = ctx.getStateCache().findFileState(path);
 | |
|             if (null != (result = getNodeOrNull(path, ctx, fstate)))
 | |
|             {
 | |
|                 return result;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Search the repository for the node
 | |
|         
 | |
|         return cifsHelper.getNodeRef(ctx.getRootNode(), path);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Determines current existence state of the file object from the cache.
 | |
|      * Updates state of the object in the cache according to existence state: increasing expiration time if
 | |
|      * object exists or removing object from the cache if it does not exist
 | |
|      * 
 | |
|      * @param path - {@link String} value which contains relative path to the file object
 | |
|      * @param ctx - {@link ContentContext} instance of the current {@link TreeConnection}
 | |
|      * @param fstate - {@link FileState} instance which represents current state of the cached object
 | |
|      * @return {@link NodeRef} instance of existent file object or <code>null</code> if object is not exist
 | |
|      */
 | |
|     private NodeRef getNodeOrNull(String path, ContentContext ctx, FileState fstate)
 | |
|     {
 | |
|         if ((null != fstate) && fstate.hasFilesystemObject() && fstate.exists())
 | |
|         {
 | |
|             // Check that the node exists
 | |
|             if (fileFolderService.exists((NodeRef) fstate.getFilesystemObject()))
 | |
|             {
 | |
|                 // Bump the file states expiry time
 | |
|                 fstate.setExpiryTime(System.currentTimeMillis() + FileState.DefTimeout);
 | |
| 
 | |
|                 // Return the cached noderef
 | |
|                 return (NodeRef) fstate.getFilesystemObject();
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 ctx.getStateCache().removeFileState(path);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Convert a node into a share relative path
 | |
|      */
 | |
|     public String getPathForNode( TreeConnection tree, NodeRef nodeRef)
 | |
|         throws FileNotFoundException
 | |
|     {
 | |
|         // Convert the target node to a path
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         List<org.alfresco.service.cmr.model.FileInfo> linkPaths = null;
 | |
|         
 | |
|         try {
 | |
|             linkPaths = fileFolderService.getNamePath( ctx.getRootNode(), nodeRef);
 | |
|         }
 | |
|         catch ( org.alfresco.service.cmr.model.FileNotFoundException ex)
 | |
|         {
 | |
|             throw new FileNotFoundException();
 | |
|         }
 | |
| 
 | |
|         // Build the share relative path to the node
 | |
|         
 | |
|         StringBuilder pathStr = new StringBuilder();
 | |
|         
 | |
|         for ( org.alfresco.service.cmr.model.FileInfo fInfo : linkPaths) {
 | |
|             pathStr.append( FileName.DOS_SEPERATOR);
 | |
|             pathStr.append( fInfo.getName());
 | |
|         }
 | |
|         
 | |
|         // Return the share relative path
 | |
|         
 | |
|         return pathStr.toString();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get the file state for the specified path
 | |
|      * 
 | |
|      * @param tree TreeConnection
 | |
|      * @param path String
 | |
|      * @return FileState
 | |
|      * @exception FileNotFoundException
 | |
|      */
 | |
|     public FileState getStateForPath(TreeConnection tree, String path)
 | |
|         throws FileNotFoundException
 | |
|     {
 | |
|         // Check if there is a cached state for the path
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         FileState fstate = null;
 | |
|         
 | |
|         if ( ctx.hasStateCache())
 | |
|         {
 | |
|             // Get the file state for a file/folder
 | |
|             
 | |
|             fstate = ctx.getStateCache().findFileState(path);
 | |
|         }
 | |
|         
 | |
|         // Return the file state
 | |
|         
 | |
|         return fstate;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Connection opened to this disk device
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection
 | |
|      */
 | |
|     public void treeClosed(SrvSession sess, TreeConnection tree)
 | |
|     {
 | |
|         // Nothing to do
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Connection closed to this device
 | |
|      * 
 | |
|      * @param sess Server session
 | |
|      * @param tree Tree connection
 | |
|      */
 | |
|     public void treeOpened(SrvSession sess, TreeConnection tree)
 | |
|     {
 | |
|         // Nothing to do
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return the lock manager used by this filesystem
 | |
|      * 
 | |
|      * @param sess SrvSession
 | |
|      * @param tree TreeConnection
 | |
|      * @return LockManager
 | |
|      */
 | |
|     public LockManager getLockManager(SrvSession sess, TreeConnection tree) {
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         return ctx.getLockManager();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Return the oplock manager implementation associated with this virtual filesystem
 | |
|      * 
 | |
|      * @param sess SrvSession
 | |
|      * @param tree TreeConnection
 | |
|      * @return OpLockManager
 | |
|      */
 | |
|     public OpLockManager getOpLockManager(SrvSession sess, TreeConnection tree) {
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         return ctx.getLockManager();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Enable/disable oplock support
 | |
|      * 
 | |
|      * @param sess SrvSession
 | |
|      * @param tree TreeConnection
 | |
|      * @return boolean
 | |
|      */
 | |
|     public boolean isOpLocksEnabled(SrvSession sess, TreeConnection tree) {
 | |
|         
 | |
|         // Check if oplocks are enabled
 | |
|         
 | |
|         ContentContext ctx = (ContentContext) tree.getContext();
 | |
|         return ctx.getDisableOplocks() ? false : true;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Copy content data from file to file
 | |
|      * 
 | |
|      * @param sess SrvSession
 | |
|      * @param tree TreeConnection
 | |
|      * @param fromNode NodeRef
 | |
|      * @param toNode NodeRef
 | |
|      * @param newName String
 | |
|      */
 | |
|     private void copyContentData( SrvSession sess, TreeConnection tree, NodeRef fromNode, NodeRef toNode, String newName)
 | |
|     {
 | |
|         ContentData content = (ContentData) nodeService.getProperty(fromNode, ContentModel.PROP_CONTENT);
 | |
|         if ( newName != null)
 | |
|             content = ContentData.setMimetype( content, mimetypeService.guessMimetype( newName));
 | |
|         nodeService.setProperty(toNode, ContentModel.PROP_CONTENT, content);
 | |
|     }
 | |
|     
 | |
|     
 | |
|     /**
 | |
|      * Clone/move aspects/properties between nodes
 | |
|      * 
 | |
|      * @param newName new name of the file
 | |
|      * @param fromNode NodeRef the node to copy from
 | |
|      * @param toNode NodeRef the node to copy to
 | |
|      * @param ctx Filesystem Context
 | |
|      */
 | |
|     private void cloneNodeAspects( String newName, NodeRef fromNode, NodeRef toNode, ContentContext ctx)
 | |
|     {
 | |
|         // We need to remove various aspects/properties from the original file, and move them to the new file
 | |
|         //
 | |
|         // Check for the lockable aspect
 | |
|         
 | |
|         if ( nodeService.hasAspect( fromNode, ContentModel.ASPECT_LOCKABLE)) {
 | |
|             
 | |
|             // Remove the lockable aspect from the old working copy, add it to the new file
 | |
|             
 | |
|             nodeService.removeAspect( fromNode, ContentModel.ASPECT_LOCKABLE);
 | |
|             nodeService.addAspect( toNode, ContentModel.ASPECT_LOCKABLE, null);
 | |
| 
 | |
|             // DEBUG
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                 logger.debug("  Moved aspect " + ContentModel.ASPECT_LOCKABLE + " to new document");
 | |
|         }
 | |
|         
 | |
|         // Check for the working copy aspect
 | |
|         
 | |
|         if ( nodeService.hasAspect( fromNode, ContentModel.ASPECT_WORKING_COPY)) {
 | |
|             
 | |
|             // Add the working copy aspect to the new file
 | |
|             
 | |
|             Map<QName, Serializable> workingCopyProperties = new HashMap<QName, Serializable>(1);
 | |
|             workingCopyProperties.put(ContentModel.PROP_WORKING_COPY_OWNER, nodeService.getProperty( fromNode, ContentModel.PROP_WORKING_COPY_OWNER));
 | |
|             
 | |
|             nodeService.addAspect( toNode, ContentModel.ASPECT_WORKING_COPY, workingCopyProperties);
 | |
|             
 | |
|             // Remove the working copy aspect from old working copy file 
 | |
|             
 | |
|             nodeService.removeAspect( fromNode, ContentModel.ASPECT_WORKING_COPY);
 | |
| 
 | |
|             // DEBUG
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                 logger.debug("  Moved aspect " + ContentModel.ASPECT_WORKING_COPY + " to new document");
 | |
|         }
 | |
|         
 | |
|         // Check for the copied from aspect
 | |
|         
 | |
|         if ( nodeService.hasAspect( fromNode, ContentModel.ASPECT_COPIEDFROM)) {
 | |
|             
 | |
|             // Add the copied from aspect to the new file
 | |
|             List<AssociationRef> assocs = nodeService.getSourceAssocs(fromNode, ContentModel.ASSOC_ORIGINAL);
 | |
|             if (assocs.size() > 0)
 | |
|             {
 | |
|                 AssociationRef assoc = assocs.get(0);
 | |
|                 NodeRef originalNodeRef = assoc.getTargetRef();
 | |
|                 nodeService.createAssociation(toNode, originalNodeRef, ContentModel.ASSOC_ORIGINAL);
 | |
|             }
 | |
|             
 | |
|             // Remove the copied from aspect from old working copy file 
 | |
|             
 | |
|             nodeService.removeAspect( fromNode, ContentModel.ASPECT_COPIEDFROM);
 | |
| 
 | |
|             // DEBUG
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                 logger.debug("  Moved aspect " + ContentModel.ASPECT_COPIEDFROM + " to new document");
 | |
|             
 | |
| //            // Check if the original node is locked
 | |
| //            
 | |
| //            if ( lockService.getLockType( copiedFromNode) == null) {
 | |
| //                
 | |
| //                // Add the lock back onto the original file
 | |
| //                
 | |
| //                lockService.lock( copiedFromNode, LockType.READ_ONLY_LOCK);
 | |
| //
 | |
| //                // DEBUG
 | |
| //                
 | |
| //                if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
| //                    logger.debug("  Re-locked copied from node " + copiedFromNode);
 | |
| //            }
 | |
|         }
 | |
|         
 | |
|         // Copy over all aspects from non-system namespaces (we will copy their properties later)
 | |
| 
 | |
|         for (QName aspectName : nodeService.getAspects(fromNode))
 | |
|         {
 | |
|             if (!_excludedNamespaces.contains(aspectName.getNamespaceURI()))
 | |
|             {
 | |
|                 nodeService.addAspect(toNode, aspectName, null);                
 | |
|             }            
 | |
|         }
 | |
| 
 | |
|         // Copy over all other properties from non system namespaces
 | |
|         Map<QName, Serializable> fromProps = nodeService.getProperties(fromNode);
 | |
|         for ( Map.Entry<QName, Serializable> entry : fromProps.entrySet()) {
 | |
|             QName propName = entry.getKey();
 | |
|             if (!_excludedNamespaces.contains(propName.getNamespaceURI()))
 | |
|             {
 | |
|                 nodeService.setProperty( toNode, propName, entry.getValue());                
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Check if the new file name is a temporary file, remove any versionable aspect from it
 | |
|         
 | |
|         String newNameNorm = newName.toLowerCase();
 | |
|         
 | |
|         if ( newNameNorm.endsWith( ".tmp") || newNameNorm.endsWith( ".temp")) {
 | |
|             
 | |
|             // Remove the versionable aspect
 | |
|             
 | |
|             if ( nodeService.hasAspect( toNode, ContentModel.ASPECT_VERSIONABLE))
 | |
|                 nodeService.removeAspect( toNode, ContentModel.ASPECT_VERSIONABLE);
 | |
| 
 | |
|             // Add the temporary aspect, also prevents versioning
 | |
|             
 | |
|             nodeService.addAspect( toNode, ContentModel.ASPECT_TEMPORARY, null);
 | |
| 
 | |
|             // DEBUG
 | |
|             
 | |
|             if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
 | |
|                 logger.debug("  Removed versionable aspect from temp file");
 | |
|         }
 | |
|         
 | |
|         // Copy over various properties
 | |
|         
 | |
|         for ( QName propName : _copyProperties) {
 | |
|             Serializable nodeProp = nodeService.getProperty( fromNode, propName);
 | |
|             if ( nodeProp != null)
 | |
|                 nodeService.setProperty( toNode, propName, nodeProp);
 | |
|         }
 | |
|         
 | |
|     } 
 | |
|     
 | |
|     /**
 | |
|      * Clone node
 | |
|      * 
 | |
|      * @param newName the new name of the node
 | |
|      * @param fromNode the node to copy from
 | |
|      * @param toNode the node to copy to
 | |
|      * @param ctx
 | |
|      */
 | |
|     private void cloneNode(String newName, NodeRef fromNode, NodeRef toNode, ContentContext ctx) 
 | |
|     {
 | |
|         if(logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("clone node from fromNode:" + fromNode + "toNode:" + toNode);
 | |
|         }
 | |
|         cloneNodeAspects(newName, fromNode, toNode, ctx);
 | |
| 
 | |
|         // copy over the node creator and owner properties
 | |
|         // need to disable the auditable aspect first to prevent default audit behaviour
 | |
|         policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE);
 | |
|         try
 | |
|         {
 | |
|         	nodeService.setProperty(toNode, ContentModel.PROP_CREATOR, nodeService.getProperty(fromNode, ContentModel.PROP_CREATOR));
 | |
|         	ownableService.setOwner(toNode, ownableService.getOwner(fromNode));
 | |
|         }
 | |
|         finally
 | |
|         {
 | |
|             policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE);
 | |
|         }
 | |
|         
 | |
|         Set<AccessPermission> permissions = permissionService.getAllSetPermissions(fromNode);
 | |
|         boolean inheritParentPermissions = permissionService.getInheritParentPermissions(fromNode);
 | |
|         permissionService.deletePermissions(fromNode);
 | |
|         
 | |
|         permissionService.setInheritParentPermissions(toNode, inheritParentPermissions);        
 | |
|         for(AccessPermission permission : permissions)
 | |
|         {
 | |
|             permissionService.setPermission(toNode, permission.getAuthority(), permission.getPermission(), (permission.getAccessStatus() == AccessStatus.ALLOWED));
 | |
|         }
 | |
|         
 | |
|         // Need to take a new guess at the mimetype based upon the new file name.
 | |
|         ContentData content = (ContentData)nodeService.getProperty(toNode, ContentModel.PROP_CONTENT);
 | |
|             
 | |
|         // Take a guess at the mimetype (if it has not been set by something already)
 | |
|         if (content != null && (content.getMimetype() == null || content.getMimetype().equals(MimetypeMap.MIMETYPE_BINARY)))
 | |
|         {
 | |
|             String mimetype = mimetypeService.guessMimetype(newName);
 | |
|             if(logger.isDebugEnabled())
 | |
|             {
 | |
|                 logger.debug("set new mimetype to:" + mimetype);
 | |
|             }
 | |
|             ContentData replacement = ContentData.setMimetype(content, mimetype);
 | |
|             nodeService.setProperty(toNode, ContentModel.PROP_CONTENT, replacement);
 | |
|         }
 | |
| 
 | |
|         // Extract metadata pending change for ALF-5082
 | |
|         Action action = getActionService().createAction(ContentMetadataExtracter.EXECUTOR_NAME);
 | |
|         if(action != null)
 | |
|         {
 | |
|             getActionService().executeAction(action, toNode);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Return the file state status as a string
 | |
|      * 
 | |
|      * @param sts int
 | |
|      * @return String
 | |
|      */
 | |
|     private final String fileStatusString( int sts) {
 | |
|         String fstatus = "Unknown";
 | |
|         
 | |
|         switch ( sts) {
 | |
|             case FileUnknown:
 | |
|                 fstatus = "Unknown";
 | |
|                 break;
 | |
|             case FileNotExist:
 | |
|                 fstatus = "NotExist";
 | |
|                 break;
 | |
|             case FileExists:
 | |
|                 fstatus = "FileExists";
 | |
|                 break;
 | |
|             case DirectoryExists:
 | |
|                 fstatus = "DirectoryExists";
 | |
|                 break;
 | |
|             case FileRenamed:
 | |
|                 fstatus = "FileRenamed";
 | |
|                 break;
 | |
|             case DeleteOnClose:
 | |
|                 fstatus = "DeleteOnClose";
 | |
|                 break;
 | |
|         }
 | |
|         
 | |
|         return fstatus;
 | |
|     }
 | |
|     
 | |
|     private boolean isRenameShuffle(String oldFilename, String newFilename, Pattern renameShufflePattern) 
 | |
|     {
 | |
|     	boolean renameShuffle = false;
 | |
|         String oldNameLower = oldFilename.toLowerCase();
 | |
|         String newNameLower = newFilename.toLowerCase();
 | |
|         renameShuffle = renameShuffle || renameShufflePattern.matcher(oldNameLower).matches();
 | |
|         renameShuffle = renameShuffle || renameShufflePattern.matcher(newNameLower).matches();
 | |
| 
 | |
|     	return renameShuffle;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get the disk information for this shared disk device.
 | |
|      *
 | |
|      * @param ctx       DiskDeviceContext
 | |
|      * @param diskDev   SrvDiskInfo
 | |
|      * @exception IOException
 | |
|      */
 | |
|     public void getDiskInformation(DiskDeviceContext ctx, SrvDiskInfo diskDev)
 | |
|       throws IOException {
 | |
|         
 | |
|         // Set the block size and blocks per allocation unit
 | |
|         
 | |
|         diskDev.setBlockSize( DiskBlockSize);
 | |
|         diskDev.setBlocksPerAllocationUnit( DiskBlocksPerUnit);
 | |
|         
 | |
|         // Get the free and total disk size in bytes from the content store
 | |
|         
 | |
|         long freeSpace = contentService.getStoreFreeSpace();
 | |
|         long totalSpace= contentService.getStoreTotalSpace();
 | |
|         
 | |
|         if ( totalSpace == -1L) {
 | |
|             
 | |
|             // Use a fixed value for the total space, content store does not support size information
 | |
|             
 | |
|             totalSpace = DiskSizeDefault;
 | |
|             freeSpace  = DiskFreeDefault;
 | |
|         }
 | |
| 
 | |
|         // Convert the total/free space values to allocation units
 | |
|         
 | |
|         diskDev.setTotalUnits( totalSpace / DiskAllocationUnit);
 | |
|         diskDev.setFreeUnits( freeSpace / DiskAllocationUnit);
 | |
|     }
 | |
| 
 | |
|     public void setActionService(ActionService actionService)
 | |
|     {
 | |
|         this.actionService = actionService;
 | |
|     }
 | |
| 
 | |
|     public ActionService getActionService()
 | |
|     {
 | |
|         return actionService;
 | |
|     }
 | |
| }
 |