From a6eb590cc92121aa8ac2c125946054c7bfe058b8 Mon Sep 17 00:00:00 2001 From: Gary Spencer Date: Mon, 7 Sep 2009 13:04:53 +0000 Subject: [PATCH] Merge 3.2 to HEAD: 15128: Merge 3.1 to 3.2: 15114: Added support for impersonation level sharing mode check, to fix Office2007 file open issue. ETHREEOH-2320. 15130: Record-only 15340: Merge 3.1 to 3.2: 14359: Fixed native call being used when was configured. ETHREEOH-2105. (Record-only) 14484: Merged HEAD to v3.1: (Record-only) 13943 Added FTP IPv6 configuration, via the tag. Added the ftp.ipv6 property. MOB-714. 14523: Add trailing 'A' to CIFS server name, removed by recent checkin. (Record-only) 14561: Change the file server config bean to use the 'org.alfresco.fileserver' logging level. 14916: Fixes for local domain lookup when WINS is configured. ETHREEOH-2263. 14922: Merge HEAD to V3.1 14626: Fixes for the client side Windows desktop action application. part of ETHREEOH-401 15155: Fixes to client side desktop action exe handling of paths that are not mapped to the root of the Alfresco share. ETHREEOH-1613 15341: Record-only 15549: Check for null ClientInfo in the setCurrentUser() method and clear the auth context. Part of ETHREEOH-2538. 15550: Fixed performance issue in the continue search code, add warn level output of folder search timing. 15564: Merge 3.1 to 3.2: 14964: Port fix for convert content I/O exceptions to file server exceptions during write and truncate (part 2). ETWOTWO-1241 15233: Ignore nodes that no longer exist during the second stage of a file server folder search. 15234: Fixed incorrect length check when buffering MSOffice document writes. 15565: Record-only 15568: Fix for cut/paste file between folders on CIFS. ETHREEOH-2323 + ENH-515. 15569: Record-only 15644: Changed filesystem debug setting so it works with old and new config styles. 15786: Record-only 15787: Port of repo filesystem MS Office document locking fix. ETHREEOH-2579 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@16122 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/desktop/Alfresco.exe | Bin 393216 -> 393216 bytes source/cpp/CAlfrescoApp/CAlfrescoApp.cpp | 5 +- .../includes/alfresco/Alfresco.hpp | 4 +- .../CAlfrescoApp/source/alfresco/Alfresco.cpp | 26 +- .../AbstractServerConfigurationBean.java | 21 +- .../filesys/ServerConfigurationBean.java | 61 +++ .../filesys/alfresco/AlfrescoContext.java | 80 +++ .../auth/cifs/CifsAuthenticatorBase.java | 2 +- .../filesys/repo/ContentDiskDriver.java | 465 ++++++++++-------- .../filesys/repo/ContentNetworkFile.java | 67 ++- .../filesys/repo/ContentSearchContext.java | 83 +++- .../repo/MSOfficeContentNetworkFile.java | 2 +- .../org/alfresco/filesys/state/FileState.java | 31 +- 13 files changed, 585 insertions(+), 262 deletions(-) diff --git a/config/alfresco/desktop/Alfresco.exe b/config/alfresco/desktop/Alfresco.exe index 85a1b543eb7dacc3522fb3d19cd420a479a4ce38..1e4a82606d5d3680ed460ddcd9915644ee2fd30e 100644 GIT binary patch delta 40099 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�ihqhvgKO8hINr3(ZO> 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 XJQ1HMro?Ab;-lIZ^&NJ}Z%6+R=&xA` delta 40198 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 diff --git a/source/cpp/CAlfrescoApp/CAlfrescoApp.cpp b/source/cpp/CAlfrescoApp/CAlfrescoApp.cpp index 18221d552a..0e4f2a72d6 100644 --- a/source/cpp/CAlfrescoApp/CAlfrescoApp.cpp +++ b/source/cpp/CAlfrescoApp/CAlfrescoApp.cpp @@ -321,7 +321,7 @@ bool CAlfrescoApp::buildDesktopParameters( AlfrescoInterface& alfresco, StringLi // Convert the path to a UNC path String uncPath = alfresco.getRootPath(); - uncPath.append( curFile.substring(2)); + uncPath.append( curFile.substring(3)); curFile = uncPath; } @@ -356,7 +356,8 @@ bool CAlfrescoApp::buildDesktopParameters( AlfrescoInterface& alfresco, StringLi // If the path is to a file that is not on the Alfresco share the file will need to be copied, // after checking the status of a matching file in the Alfresco folder - if ( curFile.length() >= 3 && curFile.substring(1,3).equals( L":\\")) { + if ( curFile.length() >= 3 && curFile.startsWithIgnoreCase( alfresco.getDrivePath()) == false && + curFile.substring(1,3).equals( L":\\")) { // Check if the action supports local files diff --git a/source/cpp/CAlfrescoApp/includes/alfresco/Alfresco.hpp b/source/cpp/CAlfrescoApp/includes/alfresco/Alfresco.hpp index 273cca3825..57a1ded5ee 100644 --- a/source/cpp/CAlfrescoApp/includes/alfresco/Alfresco.hpp +++ b/source/cpp/CAlfrescoApp/includes/alfresco/Alfresco.hpp @@ -53,8 +53,8 @@ namespace Alfresco { #define FSCTL_ALFRESCO_FILESTS CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) // Version 1 FSCTL_ALFRESCO_CHECKOUT - 0x802 // Version 1 FSCTL_ALFRESCO_CHECKIN - 0x803 - #define FSCTL_ALFRESCO_GETACTIONINFO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x804, METHOD_BUFFERED, FILE_WRITE_DATA) - #define FSCTL_ALFRESCO_RUNACTION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x805, METHOD_BUFFERED, FILE_WRITE_DATA) + #define FSCTL_ALFRESCO_GETACTIONINFO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS) + #define FSCTL_ALFRESCO_RUNACTION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_ALFRESCO_GETAUTHTICKET CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS) // Request signature bytes diff --git a/source/cpp/CAlfrescoApp/source/alfresco/Alfresco.cpp b/source/cpp/CAlfrescoApp/source/alfresco/Alfresco.cpp index b221122c2f..c81d0dabc7 100644 --- a/source/cpp/CAlfrescoApp/source/alfresco/Alfresco.cpp +++ b/source/cpp/CAlfrescoApp/source/alfresco/Alfresco.cpp @@ -330,12 +330,16 @@ bool AlfrescoInterface::setRootPath( const wchar_t* rootPath) { if ( m_handle != INVALID_HANDLE_VALUE) CloseHandle(m_handle); + // Clear the root path + + m_rootPath = ""; + // Check if the path is to a mapped drive String path = rootPath; String alfPath = rootPath; - if ( alfPath.length() >= 3 && alfPath.substring(1,3).equals( L":\\")) { + if ( alfPath.length() >= 2 && alfPath.charAt(1) == ':') { // Try and convert the local path to a UNC path @@ -353,6 +357,10 @@ bool AlfrescoInterface::setRootPath( const wchar_t* rootPath) { if ( alfPath.endsWith( PathSeperator) == false) alfPath.append( PathSeperator); + m_rootPath = alfPath; + + // Build the full UNC path to the target + if ( path.length() > 3) alfPath.append( path.substring( 3)); } @@ -395,13 +403,15 @@ bool AlfrescoInterface::setRootPath( const wchar_t* rootPath) { // Set the root path - int pos = m_uncPath.indexOf( PathSeperator, 2); - if ( pos != -1) { - pos = m_uncPath.indexOf( PathSeperator, pos + 1); - if ( pos == -1) - m_rootPath = m_uncPath; - else - m_rootPath = m_uncPath.substring(0, pos); + if ( m_rootPath.length() == 0) { + int pos = m_uncPath.indexOf( PathSeperator, 2); + if ( pos != -1) { + pos = m_uncPath.indexOf( PathSeperator, pos + 1); + if ( pos == -1) + m_rootPath = m_uncPath; + else + m_rootPath = m_uncPath.substring(0, pos); + } } } diff --git a/source/java/org/alfresco/filesys/AbstractServerConfigurationBean.java b/source/java/org/alfresco/filesys/AbstractServerConfigurationBean.java index b8a80959c3..8ecfa044a2 100644 --- a/source/java/org/alfresco/filesys/AbstractServerConfigurationBean.java +++ b/source/java/org/alfresco/filesys/AbstractServerConfigurationBean.java @@ -34,8 +34,6 @@ import java.util.Enumeration; import java.util.Locale; import java.util.StringTokenizer; -import net.sf.acegisecurity.AuthenticationManager; - import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.alfresco.AlfrescoClientInfoFactory; import org.alfresco.filesys.alfresco.ExtendedDiskInterface; @@ -430,6 +428,23 @@ public abstract class AbstractServerConfigurationBean extends ServerConfiguratio ClientInfo.setFactory( new AlfrescoClientInfoFactory()); + // We need to check for a WINS server configuration in the CIFS server config section to initialize + // the NetBIOS name lookups to use WINS rather broadcast lookups, which may be used to get the local + // domain + + try { + + // Get the CIFS server config section and extract the WINS server config, if available + + processWINSServerConfig(); + } + catch (Exception ex) { + + // Configuration error + + logger.error("File server configuration error (WINS), " + ex.getMessage(), ex); + } + // Initialize the filesystems try @@ -531,6 +546,8 @@ public abstract class AbstractServerConfigurationBean extends ServerConfiguratio protected abstract void processNFSServerConfig(); protected abstract void processFTPServerConfig(); + + protected void processWINSServerConfig() {} /** * Close the configuration bean diff --git a/source/java/org/alfresco/filesys/ServerConfigurationBean.java b/source/java/org/alfresco/filesys/ServerConfigurationBean.java index 07cd4f7b17..faa14f2e6f 100644 --- a/source/java/org/alfresco/filesys/ServerConfigurationBean.java +++ b/source/java/org/alfresco/filesys/ServerConfigurationBean.java @@ -1880,6 +1880,20 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean { boolean changeNotify = elem.getChild("disableChangeNotification") == null ? true : false; + // Check if filesyststem debug flags are enabled + + ConfigElement filesysDbgElem = elem.getChild("debug"); + if (filesysDbgElem != null) + { + // Check for filesystem debug flags + + String flags = filesysDbgElem.getAttribute("flags"); + + // Set the filesystem debug flags + + filesysContext.setDebug( flags); + } + // Create the shared filesystem filesys = new DiskSharedDevice(filesysName, filesysDriver, filesysContext); @@ -2624,4 +2638,51 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean { return desktopActions; } + /** + * Parse the CIFS server config section to get the WINS server details, if available + * + * @param config Config + */ + protected void processWINSServerConfig( Config config) + { + // Check if WINS servers are configured + + ConfigElement elem = config.getConfigElement("WINS"); + + if (elem != null) + { + + // Get the primary WINS server + + ConfigElement priWinsElem = elem.getChild("primary"); + + if (priWinsElem == null || priWinsElem.getValue().length() == 0) + throw new AlfrescoRuntimeException("No primary WINS server configured"); + + // Validate the WINS server address + + InetAddress primaryWINS = null; + + try + { + primaryWINS = InetAddress.getByName(priWinsElem.getValue()); + } + catch (UnknownHostException ex) + { + throw new AlfrescoRuntimeException("Invalid primary WINS server address, " + priWinsElem.getValue()); + } + + // Pass the setting to the NetBIOS session class + + NetBIOSSession.setDefaultWINSServer(primaryWINS); + } + } + + /** + * Parse the CIFS server config section to get the WINS server details, if available + */ + protected void processWINSServerConfig() + { + processWINSServerConfig(m_configService.getConfig(ConfigCIFS, configCtx)); + } } diff --git a/source/java/org/alfresco/filesys/alfresco/AlfrescoContext.java b/source/java/org/alfresco/filesys/alfresco/AlfrescoContext.java index 4b0efcaa15..45e5568e48 100644 --- a/source/java/org/alfresco/filesys/alfresco/AlfrescoContext.java +++ b/source/java/org/alfresco/filesys/alfresco/AlfrescoContext.java @@ -27,6 +27,7 @@ package org.alfresco.filesys.alfresco; import java.net.InetAddress; import java.util.Enumeration; import java.util.List; +import java.util.StringTokenizer; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.config.GlobalDesktopActionConfigBean; @@ -52,6 +53,20 @@ public abstract class AlfrescoContext extends DiskDeviceContext private static final String TokenLocalName = "${localname}"; + // Debug levels + + public final static int DBG_FILE = 0x00000001; // file/folder create/delete + public final static int DBG_FILEIO = 0x00000002; // file read/write/truncate + public final static int DBG_SEARCH = 0x00000004; // folder search + public final static int DBG_INFO = 0x00000008; // file/folder information + public final static int DBG_LOCK = 0x00000010; // file byte range locking + public final static int DBG_PSEUDO = 0x00000020; // pseudo files/folders + public final static int DBG_RENAME = 0x00000040; // rename file/folder + + // Filesystem debug flag strings + + private static final String m_filesysDebugStr[] = { "FILE", "FILEIO", "SEARCH", "INFO", "LOCK", "PSEUDO", "RENAME" }; + // File state table and associated file state reaper private FileStateTable m_stateTable; @@ -76,6 +91,12 @@ public abstract class AlfrescoContext extends DiskDeviceContext private IOControlHandler m_ioHandler; + // Debug flags + // + // Requires the logger to be enabled for debug output + + public int m_debug; + public AlfrescoContext() { // Default the filesystem to look like an 80Gb sized disk with 90% free space @@ -456,6 +477,65 @@ public abstract class AlfrescoContext extends DiskDeviceContext m_ioHandler = ioctlHandler; } + /** + * Set the debug flags, also requires the logger to be enabled for debug output + * + * @param dbg int + */ + public final void setDebug(String flagsStr) + { + int filesysDbg = 0; + + if (flagsStr != null) + { + // Parse the flags + + StringTokenizer token = new StringTokenizer(flagsStr.toUpperCase(), ","); + + while (token.hasMoreTokens()) + { + // Get the current debug flag token + + String dbg = token.nextToken().trim(); + + // Find the debug flag name + + int idx = 0; + boolean match = false; + + while (idx < m_filesysDebugStr.length && match == false) + { + if ( m_filesysDebugStr[idx].equalsIgnoreCase(dbg) == true) + match = true; + else + idx++; + } + + if (match == false) + throw new AlfrescoRuntimeException("Invalid filesystem debug flag, " + dbg); + + // Set the debug flag + + filesysDbg += 1 << idx; + } + + // Set the debug flags + + m_debug = filesysDbg; + } + } + + /** + * Check if a debug flag is enabled + * + * @param flg int + * @return boolean + */ + public final boolean hasDebug(int flg) + { + return (m_debug & flg) != 0 ? true : false; + } + /** * Close the filesystem context */ diff --git a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java index f53df4d83a..a6a4e60f72 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java +++ b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java @@ -413,7 +413,7 @@ public abstract class CifsAuthenticatorBase extends CifsAuthenticator implements // Check the account type and setup the authentication context - if (client.isNullSession()) + if (client == null || client.isNullSession()) { // Clear the authentication, null user should not be allowed to do any service calls diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java index 71c6f6bb60..6d5338ed83 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java @@ -34,6 +34,7 @@ import javax.transaction.UserTransaction; import org.alfresco.config.ConfigElement; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.filesys.alfresco.AlfrescoContext; import org.alfresco.filesys.alfresco.AlfrescoDiskDriver; import org.alfresco.filesys.alfresco.AlfrescoNetworkFile; import org.alfresco.filesys.state.FileState; @@ -64,6 +65,7 @@ import org.alfresco.jlan.server.filesys.pseudo.PseudoNetworkFile; import org.alfresco.jlan.server.locking.FileLockingInterface; import org.alfresco.jlan.server.locking.LockManager; 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.WildCard; @@ -701,7 +703,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Debug - if ( logger.isDebugEnabled()) + 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) @@ -721,7 +723,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug( "Added pseudo files for folder (exists) - " + paths[0]); } @@ -732,7 +734,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa if ( pfile != null) { // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("getInfo using pseudo file info for " + path); FileInfo pseudoFileInfo = pfile.getFileInfo(); @@ -761,7 +763,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("getInfo using cached noderef for path " + path); } @@ -785,7 +787,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("getInfo using cached noderef for parent " + path); } } @@ -796,12 +798,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if (logger.isDebugEnabled()) - { - logger.debug("Getting file information: \n" + - " path: " + path + "\n" + - " file info: " + finfo); - } + 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 @@ -833,16 +831,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if (logger.isDebugEnabled()) - logger.debug("Getting file information - File not found: \n" + - " path: " + path); + 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()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("Get file info - access denied, " + path); // Convert to a filesystem access denied status @@ -853,7 +850,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("Get file info error", ex); // Convert to a general I/O exception @@ -875,12 +872,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa */ public SearchContext startSearch(SrvSession sess, TreeConnection tree, String searchPath, int attributes) throws FileNotFoundException { + // Access the device context + + ContentContext ctx = (ContentContext) tree.getContext(); + try { - // Access the device context - - ContentContext ctx = (ContentContext) tree.getContext(); - String searchFileSpec = searchPath; NodeRef searchRootNodeRef = ctx.getRootNode(); FileState searchFolderState = null; @@ -937,7 +934,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH)) logger.debug("Search using cached noderef for path " + searchPath); } } @@ -948,10 +945,25 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa if ( searchFileSpec.equals( "*.*")) searchFileSpec = "*"; + // Debug + + long startTime = 0L; + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH)) + startTime = System.currentTimeMillis(); + // Perform the search List 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; @@ -1021,19 +1033,16 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Debug - if (logger.isDebugEnabled()) - { - logger.debug("Started search: \n" + - " search path: " + searchPath + "\n" + - " attributes: " + attributes); - } + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH)) + logger.debug("Started search: search path=" + searchPath + " attributes=" + attributes); + return searchCtx; } catch (org.alfresco.repo.security.permissions.AccessDeniedException ex) { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH)) logger.debug("Start search - access denied, " + searchPath); // Convert to a file not found status @@ -1044,7 +1053,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_SEARCH)) logger.debug("Start search", ex); // Convert to a file not found status @@ -1064,14 +1073,13 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa */ public int fileExists(SrvSession sess, TreeConnection tree, String name) { - + ContentContext ctx = (ContentContext) tree.getContext(); int status = FileStatus.Unknown; try { // Check for a cached file state - ContentContext ctx = (ContentContext) tree.getContext(); FileState fstate = null; if ( ctx.hasStateTable()) @@ -1090,7 +1098,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("Cache hit - fileExists() " + name + ", sts=" + status); } else @@ -1131,7 +1139,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO)) logger.debug( "Added file state for pseudo files folder (exists) - " + paths[0]); } } @@ -1156,7 +1164,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO)) logger.debug( "Added pseudo files for folder (exists) - " + paths[0]); } @@ -1173,7 +1181,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Failed to find pseudo file - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO)) logger.debug( "Failed to find pseudo file (exists) - " + name); } } @@ -1208,20 +1216,17 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa catch (IOException e) { // Debug - - logger.debug("File exists error, " + name, e); + + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) + logger.debug("File exists error, " + name, e); status = FileStatus.NotExist; } // Debug - if (logger.isDebugEnabled()) - { - logger.debug("File status determined: \n" + - " name: " + name + "\n" + - " status: " + status); - } + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) + logger.debug("File status determined: name=" + name + " status=" + FileStatus.asString(status)); // Return the file/folder status @@ -1242,13 +1247,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Create the transaction beginReadTransaction( sess); + ContentContext ctx = (ContentContext) tree.getContext(); try { - // Get the node for the path - - ContentContext ctx = (ContentContext) tree.getContext(); - // Check if pseudo files are enabled if ( hasPseudoFileInterface(ctx)) @@ -1279,7 +1281,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO)) logger.debug( "Added file state for pseudo files folder (open) - " + paths[0]); } } @@ -1291,7 +1293,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO)) logger.debug( "Added pseudo files for folder (open) - " + paths[0]); } @@ -1308,7 +1310,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Failed to find pseudo file - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_PSEUDO)) logger.debug( "Failed to find pseudo file (open) - " + params.getPath()); } } @@ -1361,46 +1363,102 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa if ( fstate.exists() == false) throw new FileNotFoundException(); - - // Check the file sharing mode - - if ( fstate != null) { - - // Check if the current file open allows the required shared access - - boolean nosharing = false; - - if ( fstate.getOpenCount() > 0) { - - // Check if the file has been opened for exclusive access - - if ( fstate.getSharedAccess() == SharingMode.NOSHARING) - nosharing = true; - - // Check if the required sharing mode is allowed by the current file open - - else if ( ( fstate.getSharedAccess() & params.getSharedAccess()) != params.getSharedAccess()) - nosharing = true; - - // Check if the caller wants exclusive access to the file - - else if ( params.getSharedAccess() == SharingMode.NOSHARING) - nosharing = true; - } - - // Check if the file allows shared access - - if ( nosharing == true) - { - if ( params.getPath().equals( "\\") == false) - throw new FileSharingException("File already open, " + params.getPath()); - } - - // Update the file sharing mode, if this is the first file open - - fstate.setSharedAccess( params.getSharedAccess()); - } } + else { + + // Create a file state for the path + + fstate = ctx.getStateTable().findFileState( params.getPath(), false, true); + } + + // Check if the current file open allows the required shared access + + boolean nosharing = false; + + // 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) { + + // 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) + { + // DEBUG + + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Sharing mode disallows write access path=" + params.getPath()); + + // Access not allowed + + throw new AccessDeniedException( "Sharing mode (write)"); + } + + // Check if the file has been opened for exclusive access + + else if ( fstate.getSharedAccess() == SharingMode.NOSHARING) + nosharing = true; + + // Check if the required sharing mode is allowed by the current file open + + else if ( ( fstate.getSharedAccess() & params.getSharedAccess()) != params.getSharedAccess()) + nosharing = true; + + // Check if the caller wants exclusive access to the file + + else if ( params.getSharedAccess() == SharingMode.NOSHARING) + nosharing = true; + + } + + // 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())); + + // 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 @@ -1508,13 +1566,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Debug - if (logger.isDebugEnabled()) - { - logger.debug("Opened network file: \n" + - " path: " + params.getPath() + "\n" + - " file open parameters: " + params + "\n" + - " network file: " + netFile); - } + 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 @@ -1524,7 +1577,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Open file - access denied, " + params.getFullPath()); // Convert to a filesystem access denied status @@ -1535,7 +1588,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Open file error", ex); // Convert to a general I/O exception @@ -1558,14 +1611,13 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Create the transaction beginWriteTransaction( sess); + ContentContext ctx = (ContentContext) tree.getContext(); try { // Get the device root - ContentContext ctx = (ContentContext) tree.getContext(); NodeRef deviceRootNodeRef = ctx.getRootNode(); - String path = params.getPath(); // If the state table is available then try to find the parent folder node for the new file @@ -1589,7 +1641,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Create file using cached noderef for path " + paths[0]); } } @@ -1626,6 +1678,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // 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 @@ -1639,20 +1692,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Create file, state=" + fstate); } } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Created file: \n" + - " path: " + path + "\n" + - " file open parameters: " + params + "\n" + - " node: " + nodeRef + "\n" + - " network file: " + netFile); - } + // Debug + + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Created file: path=" + path + " file open parameters=" + params + " node=" + nodeRef + " network file=" + netFile); return netFile; } @@ -1660,7 +1708,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Create file - access denied, " + params.getFullPath()); // Convert to a filesystem access denied status @@ -1671,7 +1719,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + 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 @@ -1682,7 +1730,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Create file error", ex); // Convert to a general I/O exception @@ -1705,12 +1753,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Create the transaction beginWriteTransaction( sess); + ContentContext ctx = (ContentContext) tree.getContext(); try { // get the device root - ContentContext ctx = (ContentContext) tree.getContext(); NodeRef deviceRootNodeRef = ctx.getRootNode(); String path = params.getPath(); @@ -1736,7 +1784,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Create file using cached noderef for path " + paths[0]); } } @@ -1760,25 +1808,21 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Creaste folder, state=" + fstate); } } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Created directory: \n" + - " path: " + path + "\n" + - " file open params: " + params + "\n" + - " node: " + nodeRef); - } + // Debug + + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Created directory: path=" + path + " file open params=" + params + " node=" + nodeRef); } catch (org.alfresco.repo.security.permissions.AccessDeniedException ex) { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Create directory - access denied, " + params.getFullPath()); // Convert to a filesystem access denied status @@ -1789,7 +1833,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Create directory error", ex); // Convert to a general I/O exception @@ -1841,28 +1885,23 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa throw new DirectoryNotEmptyException( dir); } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Deleted directory: \n" + - " directory: " + dir + "\n" + - " node: " + nodeRef); - } + // Debug + + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Deleted directory: directory=" + dir + " node=" + nodeRef); } catch (FileNotFoundException e) { - // already gone - if (logger.isDebugEnabled()) - { - logger.debug("Deleted directory : \n" + - " directory: " + dir); - } + // 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()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Delete directory - access denied, " + dir); // Convert to a filesystem access denied status @@ -1873,7 +1912,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Delete directory", ex); // Convert to a general I/O exception @@ -1892,6 +1931,13 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa */ 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(); @@ -1956,7 +2002,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa } catch ( Exception ex) { - if ( logger.isWarnEnabled()) + if ( logger.isWarnEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.warn("Error during delete on close, " + file.getFullName(), ex); } @@ -1982,7 +2028,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Delete on close - access denied, " + file.getFullName()); // Convert to a filesystem access denied exception @@ -2007,12 +2053,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if (logger.isDebugEnabled()) - { - logger.debug("Closed file: \n" + - " network file: " + file + "\n" + - " deleted on close: " + file.hasDeleteOnClose()); - } + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Closed file: network file=" + file + " delete on close=" + file.hasDeleteOnClose()); } /** @@ -2048,15 +2090,16 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa ctx.getStateTable().removeFileState(name); } - // done - if (logger.isDebugEnabled()) - logger.debug("Deleted file: " + name + ", node: " + nodeRef); + // Debug + + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Deleted file: " + name + ", node=" + nodeRef); } catch (NodeLockedException ex) { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Delete file - access denied (locked)"); // Convert to a filesystem access denied status @@ -2067,7 +2110,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Delete file - access denied"); // Convert to a filesystem access denied status @@ -2078,7 +2121,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.debug("Delete file error", ex); // Convert to a general I/O exception @@ -2102,12 +2145,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa beginWriteTransaction( sess); + // Get the device context + + ContentContext ctx = (ContentContext) tree.getContext(); + try { - // Get the device context - - ContentContext ctx = (ContentContext) tree.getContext(); - // Get the file/folder to move NodeRef nodeToMoveRef = getNodeForPath(tree, oldName); @@ -2156,7 +2199,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug(" Found rename state, relinking, " + renState); // Relink the new version of the file data to the previously renamed node so that it @@ -2186,7 +2229,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug(" Found delete on close state, restore and relink, " + renState); // Restore the deleted node so we can relink the new version to the old history/properties @@ -2195,7 +2238,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug(" Found archived node + " + archivedNode); if ( archivedNode != null && getNodeService().exists( archivedNode)) @@ -2206,7 +2249,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug(" Restored node " + restoredNode); if ( restoredNode != null) @@ -2232,7 +2275,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug(" Swapped content to restored node, delete " + oldName + ", nodeRef=" + nodeToMoveRef); // Link the node ref for the associated rename state @@ -2295,7 +2338,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug("Added Temporary aspect to renamed file " + newName); } @@ -2315,14 +2358,14 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug("Removed Temporary aspect from renamed file " + newName); } // DEBUG - if ( logger.isDebugEnabled()) - logger.debug("Renamed file: from: " + oldName + " to: " + newName); + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) + logger.debug("Renamed file: from=" + oldName + " to=" + newName); } else { @@ -2332,15 +2375,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) - logger.debug("Moved file: from: " + oldName + " to: " + newName); + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) + logger.debug("Moved file: from=" + oldName + " to=" + newName); } // Check if we renamed a file, if so then cache the rename details for a short period // in case another file renamed to the old name. MS Word uses renames to move a new // version of a document into place so we need to reconnect the version history. - if ( !cifsHelper.isDirectory(nodeToMoveRef)) + if ( !cifsHelper.isDirectory(nodeToMoveRef) && sameFolder == true) { // Get or create a new file state for the old file path @@ -2369,7 +2412,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) { logger.debug("Cached rename state for " + oldName + ", state=" + fstate); logger.debug(" new name " + newName + ", state=" + newState); @@ -2379,14 +2422,14 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if (logger.isDebugEnabled()) - logger.debug("Moved node: from: " + oldName + " to: " + newName); + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) + logger.debug("Moved node: from=" + oldName + " to=" + newName); } catch (org.alfresco.repo.security.permissions.AccessDeniedException ex) { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug("Rename file - access denied, " + oldName); // Convert to a filesystem access denied status @@ -2397,7 +2440,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug("Rename file", ex); // Convert to an filesystem access denied exception @@ -2408,7 +2451,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug("Rename file", ex); // Convert to a general I/O exception @@ -2428,12 +2471,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa */ public void setFileInformation(SrvSession sess, TreeConnection tree, String name, FileInfo info) throws IOException { + // Get the device context + + ContentContext ctx = (ContentContext) tree.getContext(); + try { - // Get the device context - - ContentContext ctx = (ContentContext) tree.getContext(); - // Check if pseudo files are enabled if ( hasPseudoFileInterface(ctx) && @@ -2496,7 +2539,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("Set deleteOnClose=true file=" + name); } @@ -2520,7 +2563,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("Set creationDate=" + createDate + " file=" + name); } @@ -2544,7 +2587,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("Set modifyDate=" + modifyDate + " file=" + name); } } @@ -2552,7 +2595,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("Set file information - access denied, " + name); // Convert to a filesystem access denied status @@ -2563,7 +2606,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Debug - if ( logger.isDebugEnabled()) + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_INFO)) logger.debug("Open file error", ex); // Convert to a general I/O exception @@ -2587,13 +2630,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa file.truncateFile(size); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Truncated file: \n" + - " network file: " + file + "\n" + - " size: " + size); - } + // Debug + + ContentContext ctx = (ContentContext) tree.getContext(); + + if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILEIO)) + logger.debug("Truncated file: network file=" + file + " size=" + size); } /** @@ -2639,17 +2681,14 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa count = 0; } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Read bytes from file: \n" + - " network file: " + file + "\n" + - " buffer size: " + buffer.length + "\n" + - " buffer pos: " + bufferPosition + "\n" + - " size: " + size + "\n" + - " file offset: " + fileOffset + "\n" + - " bytes read: " + count); - } + // 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; } @@ -2712,15 +2751,13 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa file.writeFile(buffer, size, bufferOffset, fileOffset); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Wrote bytes to file: \n" + - " network file: " + file + "\n" + - " buffer size: " + buffer.length + "\n" + - " size: " + size + "\n" + - " file offset: " + fileOffset); - } + // Debug + + ContentContext ctx = (ContentContext) tree.getContext(); + + 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; } diff --git a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java index 4d6671b618..de14cd1527 100644 --- a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java +++ b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java @@ -47,6 +47,7 @@ import org.alfresco.repo.content.encoding.ContentCharsetFinder; import org.alfresco.repo.content.filestore.FileContentReader; import org.alfresco.service.cmr.repository.ContentAccessor; import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -426,25 +427,38 @@ public class ContentNetworkFile extends NodeRefNetworkFile public void truncateFile(long size) throws IOException { - // If the content data channel has not been opened yet and the requested size is zero - // then this is an open for overwrite so the existing content data is not copied - - if ( hasContent() == false && size == 0L) - { - // Open content for overwrite, no need to copy existing content data + try { + // If the content data channel has not been opened yet and the requested size is zero + // then this is an open for overwrite so the existing content data is not copied - openContent(true, true); - } - else - { - // Normal open for write - - openContent(true, false); + if ( hasContent() == false && size == 0L) + { + // Open content for overwrite, no need to copy existing content data + + openContent(true, true); + } + else + { + // Normal open for write + + openContent(true, false); - // Truncate or extend the channel - - channel.truncate(size); - } + // Truncate or extend the channel + + channel.truncate(size); + } + } + catch ( ContentIOException ex) { + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Error opening file " + getFullName() + " for write", ex); + + // Convert to a file server I/O error + + throw new DiskFullException("Failed to open " + getFullName() + " for write"); + } // Set modification flag @@ -477,9 +491,22 @@ public class ContentNetworkFile extends NodeRefNetworkFile public void writeFile(byte[] buffer, int length, int position, long fileOffset) throws IOException { - // Open the channel for writing - - openContent(true, false); + try { + // Open the channel for writing + + openContent(true, false); + } + catch ( ContentIOException ex) { + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Error opening file " + getFullName() + " for write", ex); + + // Convert to a file server I/O error + + throw new DiskFullException("Failed to open " + getFullName() + " for write"); + } // Write to the channel diff --git a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java index 4aa6b7d80c..02d27838c0 100644 --- a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java +++ b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java @@ -34,6 +34,7 @@ import org.alfresco.jlan.server.filesys.FileType; import org.alfresco.jlan.server.filesys.SearchContext; import org.alfresco.jlan.server.filesys.pseudo.PseudoFile; import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -76,6 +77,10 @@ public class ContentSearchContext extends SearchContext private String m_relPath; + // Keep track of the last file name returned for fast restartAt processing + + private String m_lastFileName; + /** * Class constructor * @@ -111,10 +116,18 @@ public class ContentSearchContext extends SearchContext public String toString() { StringBuilder sb = new StringBuilder(60); - sb.append("ContentSearchContext") - .append("[ searchStr=").append(getSearchString()) - .append(", resultCount=").append(results.size()) - .append("]"); + + sb.append("[ContentSearchContext searchStr="); + sb.append(getSearchString()); + sb.append(", resultCount="); + sb.append(results.size()); + sb.append(", pseudoList="); + if ( pseudoList != null) + sb.append( pseudoList.numberOfFiles()); + else + sb.append("NULL"); + sb.append("]"); + return sb.toString(); } @@ -205,17 +218,46 @@ public class ContentSearchContext extends SearchContext } } - // Get the next file info from the node search - - NodeRef nextNodeRef = results.get(index); + // Return the next available file information for a real file/folder try { - // Get the file information and copy across to the callers file info - - ContentFileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, ""); - info.copyFrom(nextInfo); + // Loop until we get a valid node, might have been deleted since the initial folder search + + ContentFileInfo nextInfo = null; + + while ( nextInfo == null && index < results.size()) + { + // Get the next node from the search + + NodeRef nextNodeRef = results.get(index); + try { + + // Get the file information and copy across to the callers file info + + nextInfo = cifsHelper.getFileInformation(nextNodeRef, ""); + info.copyFrom(nextInfo); + } + catch ( InvalidNodeRefException ex) { + + // Log a warning + + if ( logger.isWarnEnabled()) + logger.warn("Noderef " + nextNodeRef + " no longer valid, ignoring"); + + // Update the node index, node no longer exists, try the next node in the search + + index++; + resumeId++; + } + } + + // Check if we have finished returning file info + + if ( nextInfo == null) + return false; + // Generate a file id for the current file StringBuilder pathStr = new StringBuilder( m_relPath); @@ -243,6 +285,10 @@ public class ContentSearchContext extends SearchContext else info.setFileType( FileType.RegularFile); + // Keep track of the last file name returned + + m_lastFileName = info.getFileName(); + // Indicate that the file information is valid return true; @@ -313,6 +359,10 @@ public class ContentSearchContext extends SearchContext // Get the file information and copy across to the callers file info FileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, ""); + + // Keep track of the last file name returned + + m_lastFileName = nextInfo.getFileName(); // Indicate that the file information is valid @@ -360,6 +410,17 @@ public class ContentSearchContext extends SearchContext } } + // Check if the resume file name is the last file returned, no need to reposition the file index + + if ( m_lastFileName != null && info.getFileName().equalsIgnoreCase( m_lastFileName)) { + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Fast search restart - " + m_lastFileName); + return true; + } + // Check if the resume file is in the main file list if ( results != null) diff --git a/source/java/org/alfresco/filesys/repo/MSOfficeContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/MSOfficeContentNetworkFile.java index ce21966123..eabccdc860 100644 --- a/source/java/org/alfresco/filesys/repo/MSOfficeContentNetworkFile.java +++ b/source/java/org/alfresco/filesys/repo/MSOfficeContentNetworkFile.java @@ -132,7 +132,7 @@ public class MSOfficeContentNetworkFile extends ContentNetworkFile { // Buffer the write, looks like a file open update. Do not buffer zero length writes. - if ( length == 0) { + if ( length != 0) { byte[] data = new byte[ length]; System.arraycopy(buffer, position, data, 0, length); diff --git a/source/java/org/alfresco/filesys/state/FileState.java b/source/java/org/alfresco/filesys/state/FileState.java index 76facf3b61..9a940b3d66 100644 --- a/source/java/org/alfresco/filesys/state/FileState.java +++ b/source/java/org/alfresco/filesys/state/FileState.java @@ -75,9 +75,10 @@ public class FileState private int m_openCount; - // Sharing mode + // Sharing mode and PID of first process to open the file private int m_sharedAccess = SharingMode.READWRITE + SharingMode.DELETE; + private int m_pid = -1; // File lock list, allocated once there are active locks on this file @@ -203,6 +204,16 @@ public class FileState return m_sharedAccess; } + /** + * Return the PID of the first process to open the file, or -1 if the file is not open + * + * @return int + */ + public final int getProcessId() + { + return m_pid; + } + /** * Check if there are active locks on this file * @@ -279,6 +290,11 @@ public class FileState else m_openCount--; + // Clear the PID if the file is no longer open + + if ( m_openCount == 0) + m_pid = -1; + return m_openCount; } @@ -430,6 +446,17 @@ public class FileState m_sharedAccess = mode; } + /** + * Set the PID of the process opening the file + * + * @param pid int + */ + public final void setProcessId(int pid) + { + if ( getOpenCount() == 0) + m_pid = pid; + } + /** * Set the file path * @@ -703,6 +730,8 @@ public class FileState str.append(getFileStatus()); str.append(":Opn="); str.append(getOpenCount()); + str.append("/"); + str.append(getProcessId()); str.append(",Expire="); str.append(getSecondsToExpire(System.currentTimeMillis()));