From 1b7cfc83039981c49cf7a06236e723394b15f633 Mon Sep 17 00:00:00 2001 From: Gary Spencer Date: Fri, 12 Jan 2007 15:04:47 +0000 Subject: [PATCH] Updates to repo filesystem to add support for NFS, plus various updates/fixes to NFS. Removed synchronization from content network file methods, synchronization is done in the protocol layer. Compacted content network file debug output. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4810 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/filesys/server/SrvSession.java | 2 +- .../server/auth/AlfrescoRpcAuthenticator.java | 16 +- .../server/config/ServerConfiguration.java | 1 - .../filesys/server/oncrpc/nfs/NFSServer.java | 31 +- .../server/oncrpc/nfs/NFSSrvSession.java | 2 +- .../server/oncrpc/nfs/NetworkFileCache.java | 111 +++++-- .../smb/server/repo/ContentDiskDriver.java | 62 +++- .../smb/server/repo/ContentNetworkFile.java | 273 ++++++++++++------ .../smb/server/repo/ContentSearchContext.java | 32 +- 9 files changed, 371 insertions(+), 159 deletions(-) diff --git a/source/java/org/alfresco/filesys/server/SrvSession.java b/source/java/org/alfresco/filesys/server/SrvSession.java index c20d316104..dd138755de 100644 --- a/source/java/org/alfresco/filesys/server/SrvSession.java +++ b/source/java/org/alfresco/filesys/server/SrvSession.java @@ -529,7 +529,7 @@ public abstract class SrvSession tx.rollback(); } } - catch ( SystemException ex) + catch ( Exception ex) { } diff --git a/source/java/org/alfresco/filesys/server/auth/AlfrescoRpcAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/AlfrescoRpcAuthenticator.java index b06ae9ea97..037a83c37c 100644 --- a/source/java/org/alfresco/filesys/server/auth/AlfrescoRpcAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/AlfrescoRpcAuthenticator.java @@ -28,6 +28,7 @@ import org.alfresco.filesys.server.oncrpc.Rpc; import org.alfresco.filesys.server.oncrpc.RpcAuthenticationException; import org.alfresco.filesys.server.oncrpc.RpcAuthenticator; import org.alfresco.filesys.server.oncrpc.RpcPacket; +import org.alfresco.filesys.server.oncrpc.nfs.NFS; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.transaction.TransactionService; import org.apache.commons.logging.Log; @@ -91,6 +92,14 @@ public class AlfrescoRpcAuthenticator implements RpcAuthenticator { if ( logger.isDebugEnabled()) logger.debug( "RpcAuth: Type=Unix uid=" + uid + ", gid=" + gid); + // Check that there is a user name mapping for the uid/gid + + Integer idKey = new Integer((gid << 16) + uid); + String userName = m_idMap.get( idKey); + + if ( userName == null) + throw new RpcAuthenticationException( NFS.StsAccess); + // Check if the Unix authentication session table is valid sessKey = new Long((((long) rpc.getClientAddress().hashCode()) << 32) + (gid << 16) + uid); @@ -142,7 +151,8 @@ public class AlfrescoRpcAuthenticator implements RpcAuthenticator { * @param rpc RpcPacket * @return ClientInfo */ - public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc) { + public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc) + { // Create a client information object to hold the client details @@ -197,7 +207,7 @@ public class AlfrescoRpcAuthenticator implements RpcAuthenticator { cInfo.setClientAddress( clientAddr); cInfo.setUid( uid); cInfo.setGid( gid); - + cInfo.setGroupsList(groups); } @@ -240,7 +250,7 @@ public class AlfrescoRpcAuthenticator implements RpcAuthenticator { // Check the account type and setup the authentication context - if ( client == null || client.isNullSession() || client.hasAuthenticationToken() == false) + 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/server/config/ServerConfiguration.java b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java index 14398335db..ea54f9e379 100644 --- a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java +++ b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java @@ -579,7 +579,6 @@ public class ServerConfiguration extends AbstractLifecycleBean // Create the configuration context ConfigLookupContext configCtx = new ConfigLookupContext(ConfigArea); - configCtx.setIncludeGlobalSection( false); // Set the platform type diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java index e7ac903aec..3e86d41271 100644 --- a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java +++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java @@ -1778,8 +1778,7 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { // Get the disk interface from the disk driver - DiskInterface disk = (DiskInterface) conn.getSharedDevice() - .getInterface(); + DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface(); // Get the pre-operation state for the parent directory @@ -1846,8 +1845,7 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { if (finfo.isDirectory()) packDirectoryHandle(shareId, finfo.getFileId(), rpc); else - packFileHandle(shareId, getFileIdForHandle(handle), - finfo.getFileId(), rpc); + packFileHandle(shareId, getFileIdForHandle(handle), finfo.getFileId(), rpc); // Pack the file attributes @@ -1856,19 +1854,17 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { // Add a cache entry for the path ShareDetails details = m_shareDetails.findDetails(shareId); - details.getFileIdCache().addPath(finfo.getFileId(), - filePath); + details.getFileIdCache().addPath(finfo.getFileId(), filePath); // Add a cache entry for the network file - sess.getFileCache().addFile(netFile, conn); + sess.getFileCache().addFile(netFile, conn, sess); // Pack the wcc data structure for the directory packPreOpAttr(sess, preInfo, rpc); - FileInfo postInfo = disk.getFileInformation(sess, conn, - path); + FileInfo postInfo = disk.getFileInformation(sess, conn, path); packPostOpAttr(sess, postInfo, shareId, rpc); // DEBUG @@ -1879,8 +1875,7 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { // Notify change listeners that a new file has been created - DiskDeviceContext diskCtx = (DiskDeviceContext) conn - .getContext(); + DiskDeviceContext diskCtx = (DiskDeviceContext) conn.getContext(); if (diskCtx.hasChangeHandler()) diskCtx.getChangeHandler().notifyFileChanged( NotifyChange.ActionAdded, filePath); @@ -2320,14 +2315,13 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { // Add a cache entry for the network file - sess.getFileCache().addFile(netFile, conn); + sess.getFileCache().addFile(netFile, conn, sess); // Pack the wcc data structure for the directory packPreOpAttr(sess, preInfo, rpc); - FileInfo postInfo = disk.getFileInformation(sess, conn, - path); + FileInfo postInfo = disk.getFileInformation(sess, conn, path); packPostOpAttr(sess, postInfo, shareId, rpc); // DEBUG @@ -3036,7 +3030,7 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { // Generate the search path - String searchPath = generatePath(path, "*.*"); + String searchPath = generatePath(path, "*"); // DEBUG @@ -4611,8 +4605,7 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { * @exception StaleHandleException * If the file id cannot be converted to a path */ - protected final NetworkFile getNetworkFileForHandle(NFSSrvSession sess, - byte[] handle, TreeConnection conn, boolean readOnly) + protected final NetworkFile getNetworkFileForHandle(NFSSrvSession sess, byte[] handle, TreeConnection conn, boolean readOnly) throws BadHandleException, StaleHandleException { // Check if the handle is a file handle @@ -4633,7 +4626,7 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { // Check the file cache, file may already be open - file = fileCache.findFile(fileId); + file = fileCache.findFile(fileId, sess); if (file == null) { // Get the path for the file @@ -4656,7 +4649,7 @@ public class NFSServer extends RpcNetworkServer implements RpcProcessor { // Add the file to the active file cache if (file != null) - fileCache.addFile(file, conn); + fileCache.addFile(file, conn, sess); } catch (AccessDeniedException ex) { if (hasDebug()) diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSSrvSession.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSSrvSession.java index 32381944e7..13be2c92d8 100644 --- a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSSrvSession.java +++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSSrvSession.java @@ -133,7 +133,7 @@ public class NFSSrvSession extends SrvSession { public final NetworkFileCache getFileCache() { if (m_fileCache == null) - m_fileCache = new NetworkFileCache(getUniqueId()); + m_fileCache = new NetworkFileCache(getUniqueId(), getNFSServer().getRpcAuthenticator()); return m_fileCache; } diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NetworkFileCache.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NetworkFileCache.java index 8b3d2aadd0..73abc6f7b3 100644 --- a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NetworkFileCache.java +++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NetworkFileCache.java @@ -17,11 +17,12 @@ package org.alfresco.filesys.server.oncrpc.nfs; import java.util.*; -import java.io.*; +import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.filesys.DiskInterface; import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.server.filesys.TreeConnection; +import org.alfresco.filesys.server.oncrpc.RpcAuthenticator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -40,7 +41,7 @@ public class NetworkFileCache { // Default file timeout - public static final long DefaultFileTimeout = 5000L; // 5 seconds + public static final long DefaultFileTimeout = 5000L; // 5 seconds // Network file cache, key is the file id @@ -50,6 +51,10 @@ public class NetworkFileCache { private FileExpiry m_expiryThread; + // RPC authenticator + + private RpcAuthenticator m_rpcAuthenticator; + // File timeout private long m_fileTmo = DefaultFileTimeout; @@ -74,18 +79,23 @@ public class NetworkFileCache { // File timeout private long m_timeout; + + // Session that last accessed the file + + private SrvSession m_sess; /** * Class constructor * - * @param file - * NetworkFile - * @param conn - * TreeConnection + * @param file NetworkFile + * @param conn TreeConnection + * @param sess SrvSession */ - public FileEntry(NetworkFile file, TreeConnection conn) { + public FileEntry(NetworkFile file, TreeConnection conn, SrvSession sess) { m_file = file; m_conn = conn; + setSession(sess); + updateTimeout(); } @@ -116,6 +126,16 @@ public class NetworkFileCache { return m_conn; } + /** + * Get the session that last accessed the file + * + * @return SrvSession + */ + public final SrvSession getSession() + { + return m_sess; + } + /** * Update the file timeout */ @@ -132,6 +152,16 @@ public class NetworkFileCache { public final void updateTimeout(long tmo) { m_timeout = tmo; } + + /** + * Set the session that last accessed the file + * + * @param sess SrvSession + */ + public final void setSession( SrvSession sess) + { + m_sess = sess; + } }; /** @@ -242,22 +272,31 @@ public class NetworkFileCache { try { + // Set the current user using the session that last accessed the file + + if ( m_rpcAuthenticator != null) + m_rpcAuthenticator.setCurrentUser( fentry.getSession(), fentry.getSession().getClientInformation()); + // Get the disk interface - DiskInterface disk = (DiskInterface) fentry - .getConnection().getInterface(); + DiskInterface disk = (DiskInterface) fentry.getConnection().getInterface(); // Close the file - disk.closeFile(null, - fentry.getConnection(), netFile); - } catch (IOException ex) { + disk.closeFile( fentry.getSession(), fentry.getConnection(), netFile); + + // Commit any transactions + + fentry.getSession().endTransaction(); + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("NFSFileExpiry: Closed file=" + fentry.getFile().getFullName() + ", fid=" + fileId); + } + catch (Exception ex) { + logger.error( "File expiry exception", ex); } - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug("NFSFileExpiry: Closed file=" + fentry.getFile().getFullName() + ", fid=" + fileId); } } } @@ -293,10 +332,10 @@ public class NetworkFileCache { /** * Class constructor * - * @param name - * String + * @param name String + * @param rpcAuth RpcAuthenticator */ - public NetworkFileCache(String name) { + public NetworkFileCache(String name, RpcAuthenticator rpcAuth) { // Create the file cache @@ -305,6 +344,10 @@ public class NetworkFileCache { // Start the file expiry thread m_expiryThread = new FileExpiry(DefaultFileTimeout / 4, name); + + // Set the RPC authenticator + + m_rpcAuthenticator = rpcAuth; } /** @@ -319,16 +362,22 @@ public class NetworkFileCache { /** * Add a file to the cache * - * @param file - * NetworkFile - * @param conn - * TreeConnection + * @param file NetworkFile + * @param conn TreeConnection + * @param sess SrvSession */ - public synchronized final void addFile(NetworkFile file, TreeConnection conn) { + public synchronized final void addFile(NetworkFile file, TreeConnection conn, SrvSession sess) { + + // Add the file id mapping + synchronized (m_fileCache) { - m_fileCache.put(new Integer(file.getFileId()), new FileEntry(file, - conn)); + m_fileCache.put(new Integer(file.getFileId()), new FileEntry(file, conn, sess)); } + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Added file " + file.getName() + ", fid=" + file.getFileId()); } /** @@ -350,11 +399,11 @@ public class NetworkFileCache { /** * Find a file via the file id * - * @param id - * int + * @param id int + * @param sess SrvSession * @return NetworkFile */ - public synchronized final NetworkFile findFile(int id) { + public synchronized final NetworkFile findFile(int id, SrvSession sess) { // Create the search key @@ -372,6 +421,8 @@ public class NetworkFileCache { // Update the file timeout and return the file fentry.updateTimeout(); + fentry.setSession(sess); + return fentry.getFile(); } diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java index 187faf02f1..5585c52ac7 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java @@ -26,6 +26,7 @@ import javax.transaction.UserTransaction; import org.alfresco.config.ConfigElement; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.alfresco.AlfrescoDiskDriver; +import org.alfresco.filesys.avm.AVMNetworkFile; import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.core.DeviceContext; import org.alfresco.filesys.server.core.DeviceContextException; @@ -682,6 +683,11 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa } } + // Set the file id for the file using the relative path + + if ( finfo != null) + finfo.setFileId( path.hashCode()); + // Return the file information return finfo; @@ -748,13 +754,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // 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 = null; + String[] paths = FileName.splitPath(searchPath); if ( ctx.hasStateTable()) { // See if the folder to be searched has a file state, we can avoid having to walk the path - paths = FileName.splitPath(searchPath); if ( paths[0] != null && paths[0].length() > 1) { // Find the node ref for the folder being searched @@ -842,7 +847,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Build the search context to store the results - SearchContext searchCtx = new ContentSearchContext(cifsHelper, results, searchFileSpec, pseudoList); + SearchContext searchCtx = new ContentSearchContext(cifsHelper, results, searchFileSpec, pseudoList, paths[0]); // Debug @@ -1199,6 +1204,11 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa NetworkFile netFile = ContentNetworkFile.createFile(transactionService, nodeService, contentService, cifsHelper, nodeRef, params); + // Generate a file id for the file + + if ( netFile != null) + netFile.setFileId( params.getPath().hashCode()); + // Create a file state for the open file if ( ctx.hasStateTable()) @@ -1315,9 +1325,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa NodeRef nodeRef = cifsHelper.createNode(deviceRootNodeRef, path, true); - // create the network file + // Create the network file + NetworkFile netFile = ContentNetworkFile.createFile(transactionService, nodeService, contentService, cifsHelper, nodeRef, params); + // Generate a file id for the file + + if ( netFile != null) + netFile.setFileId( params.getPath().hashCode()); + // Add a file state for the new file/folder if ( ctx.hasStateTable()) @@ -2027,6 +2043,16 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa 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) + sess.beginReadTransaction( transactionService); + } + // Read a block of data from the file int count = file.readFile(buffer, size, bufferPosition, fileOffset); @@ -2064,7 +2090,21 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa */ public long seekFile(SrvSession sess, TreeConnection tree, NetworkFile file, long pos, int typ) throws IOException { - throw new UnsupportedOperationException("Unsupported: " + file + " (seek)"); + // 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) + sess.beginReadTransaction( transactionService); + + // Set the file position + + return file.seekFile(pos, typ); } /** @@ -2083,7 +2123,17 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa public int writeFile(SrvSession sess, TreeConnection tree, NetworkFile file, byte[] buffer, int bufferOffset, int size, long fileOffset) throws IOException { - // Write to the file + // 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) + sess.beginWriteTransaction( transactionService); + } + + // Write to the file file.writeFile(buffer, size, bufferOffset, fileOffset); diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentNetworkFile.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentNetworkFile.java index 86c17659e3..1ee735c41d 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentNetworkFile.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentNetworkFile.java @@ -27,11 +27,10 @@ import org.alfresco.filesys.server.filesys.FileAttribute; import org.alfresco.filesys.server.filesys.FileInfo; import org.alfresco.filesys.server.filesys.FileOpenParams; import org.alfresco.filesys.server.filesys.NetworkFile; +import org.alfresco.filesys.smb.SeekType; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.filestore.FileContentReader; -import org.alfresco.repo.transaction.TransactionUtil; -import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; import org.alfresco.service.cmr.repository.ContentAccessor; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; @@ -59,11 +58,17 @@ public class ContentNetworkFile extends NetworkFile private NodeService nodeService; private ContentService contentService; private NodeRef nodeRef; - /** keeps track of the read/write access */ + + // File channel to file content + private FileChannel channel; - /** the original content opened */ + + // File content + private ContentAccessor content; - /** keeps track of any writes */ + + // Indicate if file has been written to or truncated/resized + private boolean modified; // Flag to indicate if the file channel is writable @@ -86,9 +91,12 @@ public class ContentNetworkFile extends NetworkFile // Check write access // TODO: Check access writes and compare to write requirements - // create the file + // Create the file + ContentNetworkFile netFile = new ContentNetworkFile(transactionService, nodeService, contentService, nodeRef, path); - // set relevant parameters + + // Set relevant parameters + if (params.isReadOnlyAccess()) { netFile.setGrantedAccess(NetworkFile.READONLY); @@ -98,7 +106,8 @@ public class ContentNetworkFile extends NetworkFile netFile.setGrantedAccess(NetworkFile.READWRITE); } - // check the type + // Check the type + FileInfo fileInfo; try { @@ -108,6 +117,7 @@ public class ContentNetworkFile extends NetworkFile { throw new AlfrescoRuntimeException("File not found when creating network file: " + nodeRef, e); } + if (fileInfo.isDirectory()) { netFile.setAttributes(FileAttribute.Directory); @@ -139,14 +149,13 @@ public class ContentNetworkFile extends NetworkFile if ( netFile.isReadOnly()) netFile.setGrantedAccess(NetworkFile.READONLY); - // done + // DEBUG + if (logger.isDebugEnabled()) - { - logger.debug("Created network file: \n" + - " node: " + nodeRef + "\n" + - " param: " + params + "\n" + - " netfile: " + netFile); - } + logger.debug("Create file node=" + nodeRef + ", param=" + params + ", netfile=" + netFile); + + // Return the network file + return netFile; } @@ -181,16 +190,19 @@ public class ContentNetworkFile extends NetworkFile */ public String toString() { - StringBuilder sb = new StringBuilder(50); - sb.append("ContentNetworkFile:") - .append("[ node=").append(nodeRef) - .append(", channel=").append(channel) - .append(writableChannel ? "(Write)" : "(Read)") - .append(", writable=").append(isWritable()) - .append(", content=").append(content) - .append(", modified=").append(modified) - .append("]"); - return sb.toString(); + StringBuilder str = new StringBuilder(); + + str.append( "["); + str.append( nodeRef.getId()); + str.append( ",channel="); + str.append( channel); + if ( channel != null) + str.append( writableChannel ? "(Write)" : "(Read)"); + if ( modified) + str.append( ",modified"); + str.append( "]"); + + return str.toString(); } /** @@ -211,7 +223,8 @@ public class ContentNetworkFile extends NetworkFile */ private boolean isWritable() { - // check that we are allowed to write + // Check that we are allowed to write + int access = getGrantedAccess(); return (access == NetworkFile.READWRITE || access == NetworkFile.WRITEONLY); } @@ -241,8 +254,11 @@ public class ContentNetworkFile extends NetworkFile * @see NetworkFile#WRITEONLY * @see NetworkFile#READWRITE */ - private synchronized void openContent(boolean write, boolean trunc) throws AccessDeniedException, AlfrescoRuntimeException + private void openContent(boolean write, boolean trunc) + throws AccessDeniedException, AlfrescoRuntimeException { + // Check if the file is a directory + if (isDirectory()) { throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this); @@ -271,11 +287,13 @@ public class ContentNetworkFile extends NetworkFile } else if (channel != null) { - // already have channel open + // Already have channel open + return; } - // we need to create the channel + // We need to create the channel + if (write && !isWritable()) { throw new AccessDeniedException("The network file was created for read-only: " + this); @@ -284,6 +302,8 @@ public class ContentNetworkFile extends NetworkFile content = null; if (write) { + // Get a writeable channel to the content + content = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, false); // Indicate that we have a writable channel to the file @@ -296,8 +316,12 @@ public class ContentNetworkFile extends NetworkFile } else { + // Get a read-only channel to the content + content = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); - // ensure that the content we are going to read is valid + + // Ensure that the content we are going to read is valid + content = FileContentReader.getSafeContentReader( (ContentReader) content, I18NUtil.getMessage(FileContentReader.MSG_MISSING_CONTENT), @@ -307,7 +331,8 @@ public class ContentNetworkFile extends NetworkFile writableChannel = false; - // get the read-only channel + // Get the read-only channel + channel = ((ContentReader) content).getFileChannel(); } } @@ -317,48 +342,44 @@ public class ContentNetworkFile extends NetworkFile * * @exception IOException */ - public synchronized void closeFile() throws IOException + public void closeFile() + throws IOException { - if (isDirectory()) // ignore if this is a directory + // Check if this is a directory + + if (isDirectory()) { + // Nothing to do + return; } - else if (channel == null) // ignore if the channel hasn't been opened + else if (channel == null) { + // File was not read/written so channel was not opened + return; } + // Check if the file has been modified - if (modified) // file was modified + if (modified) { - // execute the close (with possible replication listeners, etc) and the - // node update in the same transaction. A transaction will be started - // by the nodeService anyway, so it is merely widening the transaction - // boundaries. - TransactionWork closeWork = new TransactionWork() - { - public Object doWork() throws Exception - { - // close the channel - // close it - channel.close(); - channel = null; - // update node properties - ContentData contentData = content.getContentData(); - nodeService.setProperty(nodeRef, ContentModel.PROP_CONTENT, contentData); - // done - return null; - } - }; - TransactionUtil.executeInUserTransaction(transactionService, closeWork); + // Close the channel + + channel.close(); + channel = null; + + // Update node properties + + ContentData contentData = content.getContentData(); + nodeService.setProperty(nodeRef, ContentModel.PROP_CONTENT, contentData); } else { - // close it - it was not modified + // Close it - it was not modified + channel.close(); channel = null; - // no transaction used here. Any listener operations against this (now unused) content - // are irrelevant. } } @@ -368,7 +389,8 @@ public class ContentNetworkFile extends NetworkFile * @param size long * @exception IOException */ - public synchronized void truncateFile(long size) throws IOException + 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 @@ -394,14 +416,10 @@ public class ContentNetworkFile extends NetworkFile modified = true; - // Debug + // DEBUG if (logger.isDebugEnabled()) - { - logger.debug("Truncated channel: " + - " net file: " + this + "\n" + - " size: " + size); - } + logger.debug("Truncate file=" + this + ", size=" + size); } /** @@ -413,7 +431,8 @@ public class ContentNetworkFile extends NetworkFile * @param fileOff long * @exception IOException */ - public synchronized void writeFile(byte[] buffer, int length, int position, long fileOffset) throws IOException + public void writeFile(byte[] buffer, int length, int position, long fileOffset) + throws IOException { // Open the channel for writing @@ -435,11 +454,7 @@ public class ContentNetworkFile extends NetworkFile // DEBUG if (logger.isDebugEnabled()) - { - logger.debug("Wrote to channel: " + - " net file: " + this + "\n" + - " written: " + count); - } + logger.debug("Write file=" + this + ", size=" + count); } /** @@ -452,53 +467,119 @@ public class ContentNetworkFile extends NetworkFile * @return Length of data read. * @exception IOException */ - public synchronized int readFile(byte[] buffer, int length, int position, long fileOffset) throws IOException + public int readFile(byte[] buffer, int length, int position, long fileOffset) + throws IOException { // Open the channel for reading openContent(false, false); - // read from the channel + // Read from the channel + ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, position, length); int count = channel.read(byteBuffer, fileOffset); if (count < 0) { count = 0; // doesn't obey the same rules, i.e. just returns the bytes read } - // done + + // DEBUG + if (logger.isDebugEnabled()) - { - logger.debug("Read from channel: " + - " net file: " + this + "\n" + - " read: " + count); - } + logger.debug("Read file=" + this + " read=" + count); + + // Return the actual count of bytes read + return count; } + /** + * Open the file + * + * @param createFlag boolean + * @exception IOException + */ @Override - public synchronized void openFile(boolean createFlag) throws IOException + public void openFile(boolean createFlag) + throws IOException { - throw new UnsupportedOperationException(); + // Wait for read/write before opening the content channel } + /** + * Seek to a new position in the file + * + * @param pos long + * @param typ int + * @return long + */ @Override - public synchronized long seekFile(long pos, int typ) throws IOException + public long seekFile(long pos, int typ) + throws IOException { - throw new UnsupportedOperationException(); - } + // Open the file, if not already open - @Override - public synchronized void flushFile() throws IOException - { - // open the channel for writing - openContent(true, false); - // flush the channel - metadata flushing is not important - channel.force(false); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Flushed channel: " + - " net file: " + this); + openContent( false, false); + + // Check if the current file position is the required file position + + long curPos = channel.position(); + + switch (typ) { + + // From start of file + + case SeekType.StartOfFile : + if (curPos != pos) + channel.position( pos); + break; + + // From current position + + case SeekType.CurrentPos : + channel.position( curPos + pos); + break; + + // From end of file + + case SeekType.EndOfFile : + { + long newPos = channel.size() + pos; + channel.position(newPos); + } + break; } + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("Seek file=" + this + ", pos=" + pos + ", type=" + typ); + + // Return the new file position + + return channel.position(); + } + + /** + * Flush and buffered data for this file + * + * @exception IOException + */ + @Override + public void flushFile() + throws IOException + { + // Open the channel for writing + + openContent(true, false); + + // Flush the channel - metadata flushing is not important + + channel.force(false); + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("Flush file=" + this); } } diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentSearchContext.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentSearchContext.java index b64770332d..4a8898dbc2 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentSearchContext.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentSearchContext.java @@ -20,6 +20,7 @@ import java.io.FileNotFoundException; import java.util.List; import org.alfresco.filesys.server.filesys.FileInfo; +import org.alfresco.filesys.server.filesys.FileName; import org.alfresco.filesys.server.filesys.SearchContext; import org.alfresco.filesys.server.pseudo.PseudoFile; import org.alfresco.filesys.server.pseudo.PseudoFileList; @@ -51,6 +52,10 @@ public class ContentSearchContext extends SearchContext private int resumeId; + // Relative path being searched + + private String m_relPath; + /** * Class constructor * @@ -58,18 +63,24 @@ public class ContentSearchContext extends SearchContext * @param results List of file/folder nodes that match the search pattern * @param searchStr Search path * @param pseudoList List of pseudo files to be blended into the returned list of files + * @param relPath Relative path being searched */ protected ContentSearchContext( CifsHelper cifsHelper, List results, String searchStr, - PseudoFileList pseudoList) + PseudoFileList pseudoList, + String relPath) { super(); super.setSearchString(searchStr); this.cifsHelper = cifsHelper; this.results = results; this.pseudoList = pseudoList; + + m_relPath = relPath; + if ( m_relPath != null && m_relPath.endsWith( FileName.DOS_SEPERATOR_STR) == false) + m_relPath = m_relPath + FileName.DOS_SEPERATOR_STR; } /** @@ -147,6 +158,16 @@ public class ContentSearchContext extends SearchContext info.copyFrom( pinfo); + // Generate a file id for the current file + + if ( info != null && info.getFileId() == -1) + { + StringBuilder pathStr = new StringBuilder( m_relPath); + pathStr.append ( info.getFileName()); + + info.setFileId( pathStr.toString().hashCode()); + } + // Check if we have finished with the pseudo file list, switch to the normal file list if ( index == (pseudoList.numberOfFiles() - 1)) @@ -175,7 +196,14 @@ public class ContentSearchContext extends SearchContext FileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, ""); info.copyFrom(nextInfo); - // Indicate that the file information is valid + // Generate a file id for the current file + + StringBuilder pathStr = new StringBuilder( m_relPath); + pathStr.append ( info.getFileName()); + + info.setFileId( pathStr.toString().hashCode()); + + // Indicate that the file information is valid return true; }