diff --git a/source/java/org/alfresco/filesys/NFSServer.java b/source/java/org/alfresco/filesys/NFSServer.java
new file mode 100644
index 0000000000..286aa3d0d9
--- /dev/null
+++ b/source/java/org/alfresco/filesys/NFSServer.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+
+package org.alfresco.filesys;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.SocketException;
+import java.util.Vector;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.filesys.server.NetworkServer;
+import org.alfresco.filesys.server.config.ServerConfiguration;
+import org.alfresco.filesys.server.oncrpc.mount.MountServer;
+import org.alfresco.filesys.server.oncrpc.portmap.PortMapperServer;
+import org.alfresco.util.AbstractLifecycleBean;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * NFS Server Class
+ *
+ *
Create and start the various server components required to run the NFS server.
+ *
+ * @author GKSpencer
+ */
+public class NFSServer extends AbstractLifecycleBean
+{
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog("org.alfresco.nfs.server");
+
+ // Server configuration
+
+ private ServerConfiguration m_filesysConfig;
+
+ // List of NFS server components
+
+ private Vector m_serverList = new Vector();
+
+ /**
+ * Class constructor
+ *
+ * @param serverConfig ServerConfiguration
+ */
+ public NFSServer(ServerConfiguration serverConfig)
+ {
+ m_filesysConfig = serverConfig;
+ }
+
+ /**
+ * Return the server configuration
+ *
+ * @return ServerConfiguration
+ */
+ public final ServerConfiguration getConfiguration()
+ {
+ return m_filesysConfig;
+ }
+
+ /**
+ * Check if the server is started/enabled
+ *
+ * @return Returns true if the server started up without any errors
+ */
+ public boolean isStarted()
+ {
+ return (m_filesysConfig != null && m_filesysConfig.isNFSServerEnabled());
+ }
+
+ /**
+ * Start the NFS server components
+ *
+ * @exception SocketException If a network error occurs
+ * @exception IOException If an I/O error occurs
+ */
+ public final void startServer() throws SocketException, IOException
+ {
+ try
+ {
+ // Create the NFS, mount and portmapper servers, if enabled
+
+ if (m_filesysConfig.isNFSServerEnabled())
+ {
+ // Create the portmapper server, if enabled
+
+ if (m_filesysConfig.hasNFSPortMapper())
+ m_serverList.add(new PortMapperServer(m_filesysConfig));
+
+ // Create the mount and main NFS servers
+
+ m_serverList.add(new MountServer(m_filesysConfig));
+ m_serverList.add(new org.alfresco.filesys.server.oncrpc.nfs.NFSServer(m_filesysConfig));
+
+ // Add the servers to the configuration
+
+ for (NetworkServer server : m_serverList)
+ {
+ m_filesysConfig.addServer(server);
+ }
+ }
+
+ // Start the server(s)
+
+ for (NetworkServer server : m_serverList)
+ {
+ if (logger.isInfoEnabled())
+ logger.info("Starting server " + server.getProtocolName() + " ...");
+
+ // Start the server
+
+ server.startServer();
+ }
+ }
+ catch (Throwable e)
+ {
+ m_filesysConfig = null;
+ throw new AlfrescoRuntimeException("Failed to start NFS Server", e);
+ }
+ }
+
+ /**
+ * Stop the NFS server components
+ */
+ public final void stopServer()
+ {
+ if (m_filesysConfig == null)
+ {
+ // initialisation failed
+ return;
+ }
+
+ // Shutdown the NFS server components, in reverse order
+
+ for ( int i = m_serverList.size() - 1; i >= 0; i--)
+ {
+ // Get the current server from the list
+
+ NetworkServer server = m_serverList.get( i);
+ if (logger.isInfoEnabled())
+ logger.info("Shutting server " + server.getProtocolName() + " ...");
+
+ // Stop the server
+
+ server.shutdownServer(false);
+
+ // Remove the server from the global list
+
+ getConfiguration().removeServer(server.getProtocolName());
+ }
+
+ // Clear the server list and configuration
+
+ m_serverList.clear();
+ m_filesysConfig = null;
+ }
+
+ /**
+ * Runs the NFS server directly
+ *
+ * @param args String[]
+ */
+ public static void main(String[] args)
+ {
+ PrintStream out = System.out;
+
+ out.println("NFS Server Test");
+ out.println("----------------");
+
+ try
+ {
+ // Create the configuration service in the same way that Spring creates it
+
+ ApplicationContext ctx = new ClassPathXmlApplicationContext("alfresco/application-context.xml");
+
+ // Get the NFS server bean
+
+ NFSServer server = (NFSServer) ctx.getBean("nfsServer");
+ if (server == null)
+ {
+ throw new AlfrescoRuntimeException("Server bean 'nfsServer' not defined");
+ }
+
+ // Stop the FTP server, if running
+
+ server.getConfiguration().setFTPServerEnabled(false);
+
+ NetworkServer srv = server.getConfiguration().findServer("FTP");
+ if ( srv != null)
+ srv.shutdownServer(true);
+
+ // Stop the CIFS server, if running
+
+ server.getConfiguration().setSMBServerEnabled(false);
+
+ srv = server.getConfiguration().findServer("SMB");
+ if ( srv != null)
+ srv.shutdownServer(true);
+
+ // Only wait for shutdown if the NFS server is enabled
+
+ if ( server.getConfiguration().isNFSServerEnabled())
+ {
+
+ // NFS server should have automatically started
+ // Wait for shutdown via the console
+
+ out.println("Enter 'x' to shutdown ...");
+ boolean shutdown = false;
+
+ // Wait while the server runs, user may stop the server by typing a key
+
+ while (shutdown == false)
+ {
+
+ // Wait for the user to enter the shutdown key
+
+ int ch = System.in.read();
+
+ if (ch == 'x' || ch == 'X')
+ shutdown = true;
+
+ synchronized (server)
+ {
+ server.wait(20);
+ }
+ }
+
+ // Stop the server
+
+ server.stopServer();
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.exit(1);
+ }
+
+ @Override
+ protected void onBootstrap(ApplicationEvent event)
+ {
+ try
+ {
+ startServer();
+ }
+ catch (SocketException e)
+ {
+ throw new AlfrescoRuntimeException("Failed to start NFS server", e);
+ }
+ catch (IOException e)
+ {
+ throw new AlfrescoRuntimeException("Failed to start NFS server", e);
+ }
+ }
+
+ @Override
+ protected void onShutdown(ApplicationEvent event)
+ {
+ stopServer();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/DatagramSessionHandler.java b/source/java/org/alfresco/filesys/server/DatagramSessionHandler.java
new file mode 100644
index 0000000000..3f2c412b99
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/DatagramSessionHandler.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server;
+
+import java.io.*;
+import java.net.*;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Datagram Session Handler Class
+ *
+ *
Implementation of a session handler that uses a Java datagram socket to listen for incoming requests.
+ *
+ * @author GKSpencer
+ */
+public abstract class DatagramSessionHandler implements SessionHandlerInterface, Runnable {
+
+ // Debug logging
+
+ protected static final Log logger = LogFactory.getLog(DatagramSessionHandler.class);
+
+ // Server that the handler is associated with
+
+ private NetworkServer m_server;
+
+ // Address/port to use
+
+ private int m_port;
+
+ private InetAddress m_bindAddr;
+
+ // Datagram socket to listen for incoming requests
+
+ private DatagramSocket m_srvSock;
+
+ // Maximum datagram size
+
+ private int m_maxDgramSize;
+
+ // Session id
+
+ private int m_sessId;
+
+ // Session handler name, protocol name
+
+ private String m_name;
+
+ private String m_protocol;
+
+ // Shutdown request flag
+
+ private boolean m_shutdown;
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ * @param protocol String
+ * @param server NetworkServer
+ * @param addr InetAddress
+ * @param port int
+ */
+ protected DatagramSessionHandler(String name, String protocol, NetworkServer server, InetAddress addr, int port)
+ {
+ m_name = name;
+ m_protocol = protocol;
+ m_server = server;
+
+ m_bindAddr = addr;
+ m_port = port;
+ }
+
+ /**
+ * Return the maximum datagram size allowed
+ *
+ * @return int
+ */
+ public final int getMaximumDatagramSize()
+ {
+ return m_maxDgramSize;
+ }
+
+ /**
+ * Return the session handler name
+ *
+ * @return String
+ */
+ public final String getHandlerName()
+ {
+ return m_name;
+ }
+
+ /**
+ * Return the short protocol name
+ *
+ * @return String
+ */
+ public final String getProtocolName()
+ {
+ return m_protocol;
+ }
+
+ /**
+ * Check if the server should bind to a specific network address
+ *
+ * @return boolean
+ */
+ public final boolean hasBindAddress()
+ {
+ return m_bindAddr != null ? true : false;
+ }
+
+ /**
+ * Return the network address that the server should bind to
+ *
+ * @return InetAddress
+ */
+ public final InetAddress getBindAddres()
+ {
+ return m_bindAddr;
+ }
+
+ /**
+ * Return the port that the server should bind to
+ *
+ * @return int
+ */
+ public final int getPort()
+ {
+ return m_port;
+ }
+
+ /**
+ * Clear the shutdown flag
+ */
+ protected final void clearShutdown()
+ {
+ m_shutdown = false;
+ }
+
+ /**
+ * Determine if the shutdown flag has been set
+ *
+ * @return boolean
+ */
+ protected final boolean hasShutdown()
+ {
+ return m_shutdown;
+ }
+
+ /**
+ * Get the next available session id
+ *
+ * @return int
+ */
+ protected synchronized int getNextSessionId()
+ {
+ return m_sessId++;
+ }
+
+ /**
+ * Set the local port that the datagram handler is using
+ *
+ * @param port int
+ */
+ protected final void setPort(int port)
+ {
+ m_port = port;
+ }
+
+ /**
+ * Return the datagrma socket
+ *
+ * @return DatagramSocket
+ */
+ protected final DatagramSocket getDatagramSocket()
+ {
+ return m_srvSock;
+ }
+
+ /**
+ * Initialize the session handler
+ *
+ * @param server NetworkServer
+ */
+ public void initializeSessionHandler(NetworkServer server)
+ throws IOException
+ {
+
+ // Open the server socket
+
+ if (hasBindAddress())
+ m_srvSock = new DatagramSocket(getPort(), getBindAddres());
+ else
+ m_srvSock = new DatagramSocket(getPort());
+
+ // Set the datagram receive buffer size
+
+ if (m_srvSock.getReceiveBufferSize() < getMaximumDatagramSize())
+ m_srvSock.setReceiveBufferSize(getMaximumDatagramSize());
+
+ // Set the allocated port
+
+ if (getPort() == 0)
+ setPort(m_srvSock.getLocalPort());
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ {
+ String bindAddr = hasBindAddress() ? getBindAddres().getHostAddress() : "ALL";
+ logger.debug("[" + getProtocolName() + "] Binding " + getHandlerName() + " session handler to address : " + bindAddr);
+ }
+ }
+
+ /**
+ * Close the session handler
+ *
+ * @param server NetworkServer
+ */
+ public void closeSessionHandler(NetworkServer server)
+ {
+
+ // Request the main listener thread shutdown
+
+ m_shutdown = true;
+
+ // Close the server socket to release any pending listen
+
+ if (m_srvSock != null)
+ m_srvSock.close();
+ }
+
+ /**
+ * Set the maximum datagram size
+ *
+ * @param maxSize int
+ */
+ protected final void setMaximumDatagramSize(int maxSize)
+ {
+ m_maxDgramSize = maxSize;
+ }
+
+ /**
+ * Process a received datagram packet
+ *
+ * @param pkt DatagramPacket
+ * @return boolean Return true to reuse the DatagramPacket, else false to allocate a new packet
+ * @exception IOException
+ */
+ protected abstract boolean processDatagram(DatagramPacket pkt)
+ throws IOException;
+
+ /**
+ * Allocate a buffer for the datagram receive
+ *
+ * @param bufSize int
+ * @return byte[]
+ */
+ protected byte[] allocateBuffer(int bufSize)
+ {
+
+ // Allocate a buffer for the datagram
+
+ return new byte[bufSize];
+ }
+
+ /**
+ * Send a datagram
+ *
+ * @param pkt DatagramPacket
+ * @exception IOException
+ */
+ protected void sendDatagram(DatagramPacket pkt)
+ throws IOException
+ {
+
+ // Check if the datagram socket is valid
+
+ if (m_srvSock == null)
+ throw new IOException("Datagram socket is null");
+
+ // Default implementation sends the datagram immediately via the datagram socket
+
+ m_srvSock.send(pkt);
+ }
+
+ /**
+ * Socket listener thread
+ */
+ public void run()
+ {
+
+ try
+ {
+
+ // Set the thread name
+
+ Thread.currentThread().setName(getProtocolName() + "_" + getHandlerName());
+
+ // Clear the shutdown flag
+
+ clearShutdown();
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] Waiting for datagrams ...");
+
+ // Wait for incoming connection requests
+
+ byte[] buf = null;
+ DatagramPacket pkt = null;
+ boolean reusePkt = false;
+
+ while (hasShutdown() == false)
+ {
+
+ // Allocate the datagram buffer and packet
+
+ if (reusePkt == false)
+ {
+
+ // Allocate a new datagram packet and buffer
+
+ buf = allocateBuffer(getMaximumDatagramSize());
+ if (pkt == null)
+ {
+
+ // Allocate the datagram packet
+
+ pkt = new DatagramPacket(buf, buf.length);
+ } else
+ {
+
+ // Re-use the existing datagram packet
+
+ pkt.setData(buf, 0, buf.length);
+ }
+ } else
+ {
+
+ // Re-use the existing datagram packet and buffer.
+ //
+ // Reset to use our buffer as the datagram packet may have been reused to send a response.
+
+ pkt.setData(buf, 0, buf.length);
+ }
+
+ // Wait for an incoming datagram
+
+ m_srvSock.receive(pkt);
+
+ try
+ {
+
+ // Process the datagram packet
+
+ reusePkt = processDatagram(pkt);
+ } catch (Exception ex)
+ {
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] Error processing datagram, " + ex.toString());
+ }
+ }
+ } catch (SocketException ex)
+ {
+
+ // Do not report an error if the server has shutdown, closing the server socket
+ // causes an exception to be thrown.
+
+ if (hasShutdown() == false)
+ {
+ logger.debug("[" + getProtocolName() + "] Socket error : " + ex.toString());
+ logger.debug(ex);
+ }
+ } catch (Exception ex)
+ {
+
+ // Do not report an error if the server has shutdown, closing the server socket
+ // causes an exception to be thrown.
+
+ if (hasShutdown() == false)
+ {
+ logger.debug("[" + getProtocolName() + "] Server error : " + ex.toString());
+ logger.debug(ex);
+ }
+ }
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] " + getHandlerName() + " session handler closed");
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/PacketHandlerInterface.java b/source/java/org/alfresco/filesys/server/PacketHandlerInterface.java
new file mode 100644
index 0000000000..4250b9275c
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/PacketHandlerInterface.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server;
+
+import java.io.*;
+
+/**
+ * Packet Handler Interface
+ *
+ *
Implemented by classes that read/write request packets to a network connection.
+ *
+ * @author GKSpencer
+ */
+public interface PacketHandlerInterface {
+
+ /**
+ * Return the protocol name
+ *
+ * @return String
+ */
+ public String getProtocolName();
+
+ /**
+ * Return the number of bytes available for reading without blocking
+ *
+ * @return int
+ * @exception IOException
+ */
+ public int availableBytes()
+ throws IOException;
+
+ /**
+ * Read a packet of data
+ *
+ * @param pkt byte[]
+ * @param offset int
+ * @param maxLen int
+ * @return int
+ * @exception IOException
+ */
+ public int readPacket(byte[] pkt, int offset, int maxLen)
+ throws IOException;
+
+ /**
+ * Write a packet of data
+ *
+ * @param pkt byte[]
+ * @param offset int
+ * @param len int
+ * @exception IOException
+ */
+ public void writePacket(byte[] pkt, int offset, int len)
+ throws IOException;
+
+ /**
+ * Close the packet handler
+ */
+ public void closePacketHandler();
+}
diff --git a/source/java/org/alfresco/filesys/server/PacketHandlerList.java b/source/java/org/alfresco/filesys/server/PacketHandlerList.java
new file mode 100644
index 0000000000..c9bbddbec8
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/PacketHandlerList.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server;
+
+import java.util.*;
+
+/**
+ * Packet Handler List Class
+ *
+ * @author GKSpencer
+ */
+public class PacketHandlerList {
+
+ // List of session handlers
+
+ private Vector m_handlers;
+
+ /**
+ * Default constructor
+ */
+ public PacketHandlerList() {
+ m_handlers = new Vector();
+ }
+
+ /**
+ * Add a handler to the list
+ *
+ * @param handler PacketHandlerInterface
+ */
+ public final void addHandler(PacketHandlerInterface handler) {
+ m_handlers.addElement(handler);
+ }
+
+ /**
+ * Return the number of handlers in the list
+ *
+ * @return int
+ */
+ public final int numberOfHandlers() {
+ return m_handlers.size();
+ }
+
+ /**
+ * Return the specified handler
+ *
+ * @param idx int
+ * @return PacketHandlerInterface
+ */
+ public final PacketHandlerInterface getHandlerAt(int idx) {
+
+ // Range check the index
+
+ if (idx < 0 || idx >= m_handlers.size())
+ return null;
+ return (PacketHandlerInterface) m_handlers.elementAt(idx);
+ }
+
+ /**
+ * Remove a handler from the list
+ *
+ * @param idx int
+ * @return PacketHandlerInterface
+ */
+ public final PacketHandlerInterface remoteHandler(int idx) {
+
+ // Range check the index
+
+ if (idx < 0 || idx >= m_handlers.size())
+ return null;
+
+ // Remove the handler, and return it
+
+ PacketHandlerInterface handler = (PacketHandlerInterface) m_handlers.elementAt(idx);
+ m_handlers.removeElementAt(idx);
+ return handler;
+ }
+
+ /**
+ * Remove all handlers from the list
+ */
+ public final void removeAllHandlers() {
+ m_handlers.removeAllElements();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/SessionHandlerInterface.java b/source/java/org/alfresco/filesys/server/SessionHandlerInterface.java
new file mode 100644
index 0000000000..ebaf8f9764
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/SessionHandlerInterface.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server;
+
+import java.io.*;
+
+/**
+ * Session Handler Interface
+ *
+ *
Implemented by classes that wait for an incoming session request.
+ *
+ * @author GKSpencer
+ */
+public interface SessionHandlerInterface
+{
+ /**
+ * Return the protocol name
+ *
+ * @return String
+ */
+ public String getHandlerName();
+
+ /**
+ * Initialize the session handler
+ *
+ * @param server
+ * NetworkServer
+ * @exception IOException
+ */
+ public void initializeSessionHandler(NetworkServer server)
+ throws IOException;
+
+ /**
+ * Close the session handler
+ */
+ public void closeSessionHandler(NetworkServer server);
+}
diff --git a/source/java/org/alfresco/filesys/server/SessionHandlerList.java b/source/java/org/alfresco/filesys/server/SessionHandlerList.java
new file mode 100644
index 0000000000..f5810528b3
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/SessionHandlerList.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server;
+
+import java.util.*;
+
+/**
+ * Session Handler List Class
+ *
+ * @author GKSpencer
+ */
+public class SessionHandlerList {
+
+ // List of session handlers
+
+ private Vector m_handlers;
+
+ /**
+ * Default constructor
+ */
+ public SessionHandlerList()
+ {
+ m_handlers = new Vector();
+ }
+
+ /**
+ * Add a handler to the list
+ *
+ * @param handler SessionHandlerInterface
+ */
+ public final void addHandler(SessionHandlerInterface handler)
+ {
+ m_handlers.addElement(handler);
+ }
+
+ /**
+ * Return the number of handlers in the list
+ *
+ * @return int
+ */
+ public final int numberOfHandlers()
+ {
+ return m_handlers.size();
+ }
+
+ /**
+ * Return the specified handler
+ *
+ * @param idx int
+ * @return SessionHandlerInterface
+ */
+ public final SessionHandlerInterface getHandlerAt(int idx)
+ {
+
+ // Range check the index
+
+ if (idx < 0 || idx >= m_handlers.size())
+ return null;
+ return (SessionHandlerInterface) m_handlers.elementAt(idx);
+ }
+
+ /**
+ * Find the required handler by name
+ *
+ * @param name String
+ * @return SessionHandlerInterface
+ */
+ public final SessionHandlerInterface findHandler(String name)
+ {
+
+ // Search for the required handler
+
+ for (int i = 0; i < m_handlers.size(); i++)
+ {
+
+ // Get the current handler
+
+ SessionHandlerInterface handler = (SessionHandlerInterface) m_handlers.elementAt(i);
+
+ if (handler.getHandlerName().equals(name))
+ return handler;
+ }
+
+ // Handler not found
+
+ return null;
+ }
+
+ /**
+ * Remove a handler from the list
+ *
+ * @param idx int
+ * @return SessionHandlerInterface
+ */
+ public final SessionHandlerInterface remoteHandler(int idx)
+ {
+
+ // Range check the index
+
+ if (idx < 0 || idx >= m_handlers.size())
+ return null;
+
+ // Remove the handler, and return it
+
+ SessionHandlerInterface handler = (SessionHandlerInterface) m_handlers.elementAt(idx);
+ m_handlers.removeElementAt(idx);
+ return handler;
+ }
+
+ /**
+ * Remove a handler from the list
+ *
+ * @param name String
+ * @return SessionHandlerInterface
+ */
+ public final SessionHandlerInterface remoteHandler(String name)
+ {
+
+ // Search for the required handler
+
+ for (int i = 0; i < m_handlers.size(); i++)
+ {
+
+ // Get the current handler
+
+ SessionHandlerInterface handler = (SessionHandlerInterface) m_handlers.elementAt(i);
+
+ if (handler.getHandlerName().equals(name))
+ {
+
+ // Remove the handler from the list
+
+ m_handlers.removeElementAt(i);
+ return handler;
+ }
+ }
+
+ // Handler not found
+
+ return null;
+ }
+
+ /**
+ * Remove all handlers from the list
+ */
+ public final void removeAllHandlers()
+ {
+ m_handlers.removeAllElements();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/SocketPacketHandler.java b/source/java/org/alfresco/filesys/server/SocketPacketHandler.java
new file mode 100644
index 0000000000..8dd18e57a6
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/SocketPacketHandler.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * Java Socket Based Packet Handler Class
+ *
+ * @author GKSpencer
+ */
+public abstract class SocketPacketHandler implements PacketHandlerInterface {
+
+ // Socket to read/write to/from
+
+ private Socket m_socket;
+
+ // Input/output streams for receiving/sending data
+
+ private DataInputStream m_in;
+ private DataOutputStream m_out;
+
+ /**
+ * Class constructor
+ *
+ * @param socket Socket
+ * @param protocol String
+ * @exception IOException
+ */
+ protected SocketPacketHandler(Socket socket) throws IOException {
+ m_socket = socket;
+
+ // Open the input/output streams
+
+ m_in = new DataInputStream(m_socket.getInputStream());
+ m_out = new DataOutputStream(m_socket.getOutputStream());
+ }
+
+ /**
+ * Return the protocol name
+ *
+ * @return String
+ */
+ public abstract String getProtocolName();
+
+ /**
+ * Return the number of bytes available for reading without blocking
+ *
+ * @return int
+ * @exception IOException
+ */
+ public int availableBytes() throws IOException {
+ if (m_in != null)
+ return m_in.available();
+ return 0;
+ }
+
+ /**
+ * Read a packet of data
+ *
+ * @param pkt byte[]
+ * @param offset int
+ * @param maxLen int
+ * @return int
+ * @exception IOException
+ */
+ public int readPacket(byte[] pkt, int offset, int maxLen) throws IOException {
+
+ // Read a packet of data
+
+ if (m_in != null)
+ return m_in.read(pkt, offset, maxLen);
+ return 0;
+ }
+
+ /**
+ * Write a packet of data
+ *
+ * @param pkt byte[]
+ * @param offset int
+ * @param len int
+ * @exception IOException
+ */
+ public void writePacket(byte[] pkt, int offset, int len) throws IOException {
+
+ // Output the raw packet
+
+ if (m_out != null) {
+
+ synchronized (m_out) {
+ m_out.write(pkt, offset, len);
+ }
+ }
+ }
+
+ /**
+ * Close the packet handler
+ */
+ public void closePacketHandler() {
+
+ // Close the socket
+
+ if (m_socket != null) {
+ try {
+ m_socket.close();
+ } catch (Exception ex) {
+ }
+ m_socket = null;
+ }
+
+ // Close the input stream
+
+ if (m_in != null) {
+ try {
+ m_in.close();
+ } catch (Exception ex) {
+ }
+ m_in = null;
+ }
+
+ // Close the output stream
+
+ if (m_out != null) {
+ try {
+ m_out.close();
+ } catch (Exception ex) {
+ }
+ m_out = null;
+ }
+ }
+
+ /**
+ * Return the socket
+ *
+ * @return Socket
+ */
+ protected final Socket getSocket() {
+ return m_socket;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/SocketSessionHandler.java b/source/java/org/alfresco/filesys/server/SocketSessionHandler.java
new file mode 100644
index 0000000000..edaeb8dba1
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/SocketSessionHandler.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server;
+
+import java.io.*;
+import java.net.*;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Socket Session Handler Class
+ *
+ *
Implementation of a session handler that uses a Java socket to listen for incoming session requests.
+ *
+ * @author GKSpencer
+ */
+public abstract class SocketSessionHandler implements SessionHandlerInterface, Runnable {
+
+ // Debug logging
+
+ protected static final Log logger = LogFactory.getLog(SocketSessionHandler.class);
+
+ // Constants
+ //
+ // Default socket listen back log limit
+
+ public static final int ListenBacklog = 10;
+
+ // Server that the handler is associated with
+
+ private NetworkServer m_server;
+
+ // Address/port to use
+
+ private int m_port;
+ private InetAddress m_bindAddr;
+
+ // Socket listen back log limit
+
+ private int m_backLog = ListenBacklog;
+
+ // Server socket to listen for incoming connections
+
+ private ServerSocket m_srvSock;
+
+ // Session id
+
+ private int m_sessId;
+
+ // Session handler name, protocol name
+
+ private String m_name;
+ private String m_protocol;
+
+ // Shutdown request flag
+
+ private boolean m_shutdown;
+
+ // Debug enable
+
+ private boolean m_debug;
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ * @param protocol String
+ * @param server NetworkServer
+ * @param addr InetAddress
+ * @param port int
+ */
+ public SocketSessionHandler(String name, String protocol, NetworkServer server, InetAddress addr, int port) {
+ m_name = name;
+ m_protocol = protocol;
+ m_server = server;
+
+ m_bindAddr = addr;
+ m_port = port;
+ }
+
+ /**
+ * Return the session handler name
+ *
+ * @return String
+ */
+ public final String getHandlerName() {
+ return m_name;
+ }
+
+ /**
+ * Return the short protocol name
+ *
+ * @return String
+ */
+ public final String getProtocolName() {
+ return m_protocol;
+ }
+
+ /**
+ * Check if the server should bind to a specific network address
+ *
+ * @return boolean
+ */
+ public final boolean hasBindAddress() {
+ return m_bindAddr != null ? true : false;
+ }
+
+ /**
+ * Return the network address that the server should bind to
+ *
+ * @return InetAddress
+ */
+ public final InetAddress getBindAddress() {
+ return m_bindAddr;
+ }
+
+ /**
+ * Return the port that the server should bind to
+ *
+ * @return int
+ */
+ public final int getPort() {
+ return m_port;
+ }
+
+ /**
+ * Return the socket listen backlog limit
+ *
+ * @return int
+ */
+ public final int getListenBacklog() {
+ return m_backLog;
+ }
+
+ /**
+ * Determine if debug output is enabled
+ *
+ * @return boolean
+ */
+ public final boolean hasDebug() {
+ return m_debug;
+ }
+
+ /**
+ * Clear the shutdown flag
+ */
+ protected final void clearShutdown() {
+ m_shutdown = false;
+ }
+
+ /**
+ * Determine if the shutdown flag has been set
+ *
+ * @return boolean
+ */
+ protected final boolean hasShutdown() {
+ return m_shutdown;
+ }
+
+ /**
+ * Get the next available session id
+ *
+ * @return int
+ */
+ protected synchronized int getNextSessionId() {
+ return m_sessId++;
+ }
+
+ /**
+ * Enable/disable debug output
+ *
+ * @param dbg boolean
+ */
+ public final void setDebug(boolean dbg) {
+ m_debug = dbg;
+ }
+
+ /**
+ * Set the local port that the session handler is using
+ *
+ * @param port int
+ */
+ protected final void setPort(int port) {
+ m_port = port;
+ }
+
+ /**
+ * Initialize the session handler
+ *
+ * @param server NetworkServer
+ */
+ public void initializeSessionHandler(NetworkServer server) throws IOException {
+
+ // Open the server socket
+
+ if (hasBindAddress())
+ m_srvSock = new ServerSocket(getPort(), getListenBacklog(), getBindAddress());
+ else
+ m_srvSock = new ServerSocket(getPort(), getListenBacklog());
+
+ // Set the allocated port
+
+ if (getPort() == 0)
+ setPort(m_srvSock.getLocalPort());
+
+ // DEBUG
+
+ if (logger.isDebugEnabled()) {
+ String bindAddr = hasBindAddress() ? getBindAddress().getHostAddress() : "ALL";
+ logger.debug("[" + getProtocolName() + "] Binding " + getHandlerName() + " session handler to address : " + bindAddr);
+ }
+ }
+
+ /**
+ * Close the session handler
+ *
+ * @param server NetworkServer
+ */
+ public void closeSessionHandler(NetworkServer server) {
+
+ // Request the main listener thread shutdown
+
+ m_shutdown = true;
+
+ try {
+
+ // Close the server socket to release any pending listen
+
+ if (m_srvSock != null)
+ m_srvSock.close();
+ } catch (SocketException ex) {
+ } catch (Exception ex) {
+ }
+ }
+
+ /**
+ * Accept a new connection on the specified socket
+ *
+ * @param sock Socket
+ */
+ protected abstract void acceptConnection(Socket sock);
+
+ /**
+ * Socket listener thread
+ */
+ public void run() {
+
+ try {
+
+ // Clear the shutdown flag
+
+ clearShutdown();
+
+ // Wait for incoming connection requests
+
+ while (hasShutdown() == false) {
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] Waiting for session request ...");
+
+ // Wait for a connection
+
+ Socket sessSock = m_srvSock.accept();
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] Session request received from "
+ + sessSock.getInetAddress().getHostAddress());
+
+ try {
+
+ // Process the new connection request
+
+ acceptConnection(sessSock);
+ }
+ catch (Exception ex) {
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] Failed to create session, " + ex.toString());
+ }
+ }
+ }
+ catch (SocketException ex) {
+
+ // Do not report an error if the server has shutdown, closing the server socket
+ // causes an exception to be thrown.
+
+ if (hasShutdown() == false) {
+ logger.debug("[" + getProtocolName() + "] Socket error : " + ex.toString());
+ logger.debug(ex);
+ }
+ }
+ catch (Exception ex) {
+
+ // Do not report an error if the server has shutdown, closing the server socket
+ // causes an exception to be thrown.
+
+ if (hasShutdown() == false) {
+ logger.debug("[" + getProtocolName() + "] Server error : " + ex.toString());
+ logger.debug(ex);
+ }
+ }
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] " + getHandlerName() + " session handler closed");
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/SrvSession.java b/source/java/org/alfresco/filesys/server/SrvSession.java
index 662da219b3..c20d316104 100644
--- a/source/java/org/alfresco/filesys/server/SrvSession.java
+++ b/source/java/org/alfresco/filesys/server/SrvSession.java
@@ -27,6 +27,7 @@ import org.alfresco.filesys.server.auth.AuthContext;
import org.alfresco.filesys.server.auth.ClientInfo;
import org.alfresco.filesys.server.core.SharedDevice;
import org.alfresco.filesys.server.core.SharedDeviceList;
+import org.alfresco.filesys.server.filesys.FilesysTransaction;
import org.alfresco.service.transaction.TransactionService;
/**
@@ -64,7 +65,6 @@ public abstract class SrvSession
// Debug flags for this session
private int m_debug;
- private String m_dbgPrefix;
// Session shutdown flag
@@ -92,8 +92,10 @@ public abstract class SrvSession
// Active transaction and read/write flag
- private UserTransaction m_transaction;
- private boolean m_readOnlyTrans;
+ private ThreadLocal m_tx = new ThreadLocal();
+
+// UserTransaction m_transaction;
+// private boolean m_readOnlyTrans;
// Request and transaction counts
@@ -373,16 +375,6 @@ public abstract class SrvSession
m_debug = flgs;
}
- /**
- * Set the debug output prefix for this session
- *
- * @param prefix String
- */
- public final void setDebugPrefix(String prefix)
- {
- m_dbgPrefix = prefix;
- }
-
/**
* Set the logged on/validated status for the session
*
@@ -507,22 +499,34 @@ public abstract class SrvSession
{
boolean created = false;
+ // Get the filesystem transaction
+
+ FilesysTransaction filesysTx = m_tx.get();
+ if ( filesysTx == null)
+ {
+ filesysTx = new FilesysTransaction();
+ m_tx.set( filesysTx);
+ }
+
// If there is an active transaction check that it is the required type
- if ( m_transaction != null)
+ if ( filesysTx.hasTransaction())
{
+ // Get the active transaction
+
+ UserTransaction tx = filesysTx.getTransaction();
+
// Check if the current transaction is marked for rollback
try
{
-
- if ( m_transaction.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
- m_transaction.getStatus() == Status.STATUS_ROLLEDBACK ||
- m_transaction.getStatus() == Status.STATUS_ROLLING_BACK)
+ if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
+ tx.getStatus() == Status.STATUS_ROLLEDBACK ||
+ tx.getStatus() == Status.STATUS_ROLLING_BACK)
{
// Rollback the current transaction
- m_transaction.rollback();
+ tx.rollback();
}
}
catch ( SystemException ex)
@@ -531,13 +535,13 @@ public abstract class SrvSession
// Check if the transaction is a write transaction, if write has been requested
- if ( readOnly == false && m_readOnlyTrans == true)
+ if ( readOnly == false && filesysTx.isReadOnly() == true)
{
// Commit the read-only transaction
try
{
- m_transaction.commit();
+ tx.commit();
m_transConvCount++;
}
catch ( Exception ex)
@@ -548,24 +552,25 @@ public abstract class SrvSession
{
// Clear the active transaction
- m_transaction = null;
+ filesysTx.clearTransaction();
}
}
}
// Create the transaction
- if ( m_transaction == null)
+ if ( filesysTx.hasTransaction() == false)
{
try
{
- m_transaction = transService.getUserTransaction(readOnly);
- m_transaction.begin();
+ UserTransaction userTrans = transService.getUserTransaction(readOnly);
+ userTrans.begin();
created = true;
+
+ // Store the transaction
- m_readOnlyTrans = readOnly;
-
+ filesysTx.setTransaction( userTrans, readOnly);
m_transCount++;
}
catch (Exception ex)
@@ -585,25 +590,33 @@ public abstract class SrvSession
public final void endTransaction()
throws AlfrescoRuntimeException
{
+ // Get the filesystem transaction
+
+ FilesysTransaction filesysTx = m_tx.get();
+
// Check if there is an active transaction
- if ( m_transaction != null)
+ if ( filesysTx != null && filesysTx.hasTransaction())
{
+ // Get the active transaction
+
+ UserTransaction tx = filesysTx.getTransaction();
+
try
{
// Commit or rollback the transaction
- if ( m_transaction.getStatus() == Status.STATUS_MARKED_ROLLBACK)
+ if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
{
// Transaction is marked for rollback
- m_transaction.rollback();
+ tx.rollback();
}
else
{
// Commit the transaction
- m_transaction.commit();
+ tx.commit();
}
}
catch ( Exception ex)
@@ -614,7 +627,7 @@ public abstract class SrvSession
{
// Clear the current transaction
- m_transaction = null;
+ filesysTx.clearTransaction();
}
}
@@ -626,7 +639,12 @@ public abstract class SrvSession
*/
public final boolean hasUserTransaction()
{
- return m_transaction != null ? true : false;
+ // Get the filesystem transaction
+
+ FilesysTransaction filesysTx = m_tx.get();
+ if ( filesysTx != null)
+ return filesysTx.hasTransaction();
+ return false;
}
/**
@@ -636,8 +654,17 @@ public abstract class SrvSession
*/
public final UserTransaction getUserTransaction()
{
- UserTransaction trans = m_transaction;
- m_transaction = null;
- return trans;
+ // Get the filesystem transaction
+
+ UserTransaction userTrans = null;
+ FilesysTransaction filesysTx = m_tx.get();
+
+ if ( filesysTx != null)
+ {
+ userTrans = filesysTx.getTransaction();
+ filesysTx.clearTransaction();
+ }
+
+ return userTrans;
}
}
diff --git a/source/java/org/alfresco/filesys/server/auth/AlfrescoRpcAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/AlfrescoRpcAuthenticator.java
new file mode 100644
index 0000000000..b06ae9ea97
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/auth/AlfrescoRpcAuthenticator.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2005-2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.auth;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.alfresco.config.ConfigElement;
+import org.alfresco.filesys.server.SrvSession;
+import org.alfresco.filesys.server.config.InvalidConfigurationException;
+import org.alfresco.filesys.server.config.ServerConfiguration;
+import org.alfresco.filesys.server.oncrpc.AuthType;
+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.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.service.transaction.TransactionService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Alfresco RPC Authenticator Class
+ *
+ *
Provides authentication support for the NFS server.
+ *
+ * @author gkspencer
+ */
+public class AlfrescoRpcAuthenticator implements RpcAuthenticator {
+
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol.auth");
+
+ // Authentication types aupported by this implementation
+
+ private int[] _authTypes = { AuthType.Null, AuthType.Unix };
+
+ // UID/GID to username conversions
+
+ private HashMap m_idMap;
+
+ // Authentication component and services
+
+ private AuthenticationComponent m_authComponent;
+ private TransactionService m_transService;
+
+ /**
+ * Authenticate an RPC client and create a unique session id key.
+ *
+ * @param authType int
+ * @param rpc RpcPacket
+ * @return Object
+ * @throws RpcAuthenticationException
+ */
+ public Object authenticateRpcClient(int authType, RpcPacket rpc)
+ throws RpcAuthenticationException {
+
+ // Create a unique session key depending on the authentication type
+
+ Object sessKey = null;
+
+ if (authType == AuthType.Unix) {
+
+ // Get the gid and uid from the credentials data in the request
+
+ rpc.positionAtCredentialsData();
+ rpc.skipBytes(4);
+ int nameLen = rpc.unpackInt();
+ rpc.skipBytes(nameLen);
+
+ int gid = rpc.unpackInt();
+ int uid = rpc.unpackInt();
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug( "RpcAuth: Type=Unix uid=" + uid + ", gid=" + gid);
+
+ // Check if the Unix authentication session table is valid
+
+ sessKey = new Long((((long) rpc.getClientAddress().hashCode()) << 32) + (gid << 16) + uid);
+ }
+ else if ( authType == AuthType.Null)
+ {
+ // Set the session key for the null authentication
+
+ sessKey = new Integer(rpc.getClientAddress().hashCode());
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug( "RpcAuth: Type=Null client=" + rpc.getClientAddress());
+ }
+
+ // Check if the session key is valid, if not then the authentication
+ // type is unsupported
+
+ if (sessKey == null)
+ throw new RpcAuthenticationException(Rpc.AuthBadCred, "Unsupported auth type, " + authType);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcAuth: RPC from " + rpc.getClientDetails()
+ + ", authType=" + AuthType.getTypeAsString(authType)
+ + ", sessKey=" + sessKey);
+
+ // Return the session key
+
+ return sessKey;
+ }
+
+ /**
+ * Return the authentication types that are supported by this
+ * implementation.
+ *
+ * @return int[]
+ */
+ public int[] getRpcAuthenticationTypes() {
+ return _authTypes;
+ }
+
+ /**
+ * Return the client information for the specified RPC request
+ *
+ * @param sessKey Object
+ * @param rpc RpcPacket
+ * @return ClientInfo
+ */
+ public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc) {
+
+ // Create a client information object to hold the client details
+
+ ClientInfo cInfo = null;
+
+ // Get the authentication type
+
+ int authType = rpc.getCredentialsType();
+
+ // Unpack the client details from the RPC request
+
+ if ( authType == AuthType.Unix) {
+
+ // Unpack the credentials data
+
+ rpc.positionAtCredentialsData();
+ rpc.skipBytes(4); // stamp id
+
+ String clientAddr = rpc.unpackString();
+ int uid = rpc.unpackInt();
+ int gid = rpc.unpackInt();
+
+ // Check for an additional groups list
+
+ int grpLen = rpc.unpackInt();
+ int[] groups = null;
+
+ if (grpLen > 0) {
+ groups = new int[grpLen];
+ rpc.unpackIntArray(groups);
+ }
+
+ // Get the user name mapping for the uid/gid and authenticate
+
+ Integer idKey = new Integer((gid << 16) + uid);
+ String userName = m_idMap.get( idKey);
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug( "RpcClientInfo: username=" + userName + ", uid=" + uid + ", gid=" + gid);
+
+ // Create the client information if there is a valid mapping
+
+ if ( userName != null)
+ {
+ // Create the client information and fill in relevant fields
+
+ cInfo = new ClientInfo( userName, null);
+
+ cInfo.setNFSAuthenticationType( authType);
+ cInfo.setClientAddress( clientAddr);
+ cInfo.setUid( uid);
+ cInfo.setGid( gid);
+
+ cInfo.setGroupsList(groups);
+ }
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", name="
+ + clientAddr + ", uid=" + uid + ", gid=" + gid + ", groups=" + grpLen);
+ }
+ else if ( authType == AuthType.Null)
+ {
+ // Create the client information
+
+ cInfo = new ClientInfo( "", null);
+ cInfo.setClientAddress(rpc.getClientAddress().getHostAddress());
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", addr="
+ + rpc.getClientAddress().getHostAddress());
+ }
+
+ // Return the client information
+
+ return cInfo;
+ }
+
+ /**
+ * Set the current authenticated user context for this thread
+ *
+ * @param sess SrvSession
+ * @param client ClientInfo
+ */
+ public void setCurrentUser( SrvSession sess, ClientInfo client)
+ {
+ // Start a transaction
+
+ sess.beginReadTransaction( m_transService);
+
+ // Check the account type and setup the authentication context
+
+ if ( client == null || client.isNullSession() || client.hasAuthenticationToken() == false)
+ {
+ // Clear the authentication, null user should not be allowed to do any service calls
+
+ m_authComponent.clearCurrentSecurityContext();
+ }
+ else if ( client.isGuest() == false)
+ {
+ // Check if the authentication token has been set for the client
+
+ if ( client.hasAuthenticationToken() == false)
+ {
+ // Set the current user and retrieve the authentication token
+
+ m_authComponent.setCurrentUser( client.getUserName());
+ client.setAuthenticationToken( m_authComponent.getCurrentAuthentication());
+ }
+ else
+ {
+ // Set the authentication context for the request
+
+ m_authComponent.setCurrentAuthentication( client.getAuthenticationToken());
+ }
+ }
+ else
+ {
+ // Enable guest access for the request
+
+ m_authComponent.setGuestUserAsCurrentUser();
+ }
+ }
+
+ /**
+ * Initialize the RPC authenticator
+ *
+ * @param config ServerConfiguration
+ * @param params NameValueList
+ * @throws InvalidConfigurationException
+ */
+ public void initialize(ServerConfiguration config, ConfigElement params)
+ throws InvalidConfigurationException {
+
+ // Get the configured authentication component and transaction service
+
+ m_authComponent = config.getAuthenticationComponent();
+ m_transService = config.getTransactionService();
+
+ // Check for the user mappings
+
+ ConfigElement userMappings = params.getChild("userMappings");
+ if ( userMappings != null)
+ {
+ // Allocate the id mappings table
+
+ m_idMap = new HashMap();
+
+ // Get the user map elements
+
+ List userMaps = userMappings.getChildren();
+
+ // Process the user list
+
+ for ( ConfigElement userElem : userMaps)
+ {
+ // Validate the element type
+
+ if ( userElem.getName().equalsIgnoreCase( "user"))
+ {
+ // Get the user name, user id and group id
+
+ String userName = userElem.getAttribute("name");
+ String uidStr = userElem.getAttribute("uid");
+ String gidStr = userElem.getAttribute("gid");
+
+ if ( userName == null || userName.length() == 0)
+ throw new InvalidConfigurationException("Empty user name, or name not specified");
+
+ if ( uidStr == null || uidStr.length() == 0)
+ throw new InvalidConfigurationException("Invalid uid, or uid not specified, for user " + userName);
+
+ if ( gidStr == null || gidStr.length() == 0)
+ throw new InvalidConfigurationException("Invalid gid, or gid not specified, for user " + userName);
+
+ // Parse the uid/gid
+
+ int uid = -1;
+ int gid = -1;
+
+ try
+ {
+ uid = Integer.parseInt( uidStr);
+ }
+ catch ( NumberFormatException ex)
+ {
+ throw new InvalidConfigurationException("Invalid uid value, " + uidStr + " for user " + userName);
+ }
+
+ try
+ {
+ gid = Integer.parseInt( gidStr);
+ }
+ catch ( NumberFormatException ex)
+ {
+ throw new InvalidConfigurationException("Invalid gid value, " + gidStr + " for user " + userName);
+ }
+
+ // Check if the mapping already exists
+
+ Integer idKey = new Integer(( gid << 16) + uid);
+ if ( m_idMap.containsKey( idKey) == false)
+ {
+ // Add the username uid/gid mapping
+
+ m_idMap.put( idKey, userName);
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Added RPC user mapping for user " + userName + " uid=" + uid + ", gid=" + gid);
+ }
+ else if ( logger.isDebugEnabled())
+ {
+ // DEBUG
+
+ logger.debug("Ignored duplicate mapping for uid=" + uid + ", gid=" + gid);
+ }
+ }
+ else
+ throw new InvalidConfigurationException( "Invalid user mapping, " + userElem.getName());
+ }
+ }
+
+ // Make sure there are some user mappings
+
+ if ( m_idMap == null || m_idMap.size() == 0)
+ throw new InvalidConfigurationException("No user mappings for RPC authenticator");
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java
index e78e98cb3c..34f14524a6 100644
--- a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java
+++ b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java
@@ -58,6 +58,7 @@ import org.alfresco.filesys.netbios.RFCNetBIOSProtocol;
import org.alfresco.filesys.netbios.win32.Win32NetBIOS;
import org.alfresco.filesys.server.NetworkServer;
import org.alfresco.filesys.server.NetworkServerList;
+import org.alfresco.filesys.server.auth.AlfrescoRpcAuthenticator;
import org.alfresco.filesys.server.auth.CifsAuthenticator;
import org.alfresco.filesys.server.auth.acl.ACLParseException;
import org.alfresco.filesys.server.auth.acl.AccessControl;
@@ -75,8 +76,8 @@ import org.alfresco.filesys.server.core.SharedDeviceList;
import org.alfresco.filesys.server.filesys.DefaultShareMapper;
import org.alfresco.filesys.server.filesys.DiskInterface;
import org.alfresco.filesys.server.filesys.DiskSharedDevice;
-//import org.alfresco.filesys.server.oncrpc.DefaultRpcAuthenticator;
-//import org.alfresco.filesys.server.oncrpc.RpcAuthenticator;
+import org.alfresco.filesys.server.oncrpc.DefaultRpcAuthenticator;
+import org.alfresco.filesys.server.oncrpc.RpcAuthenticator;
import org.alfresco.filesys.smb.ServerType;
import org.alfresco.filesys.smb.TcpipSMB;
import org.alfresco.filesys.smb.server.repo.ContentContext;
@@ -357,7 +358,7 @@ public class ServerConfiguration extends AbstractLifecycleBean
// RPC authenticator implementation
-// private RpcAuthenticator m_rpcAuthenticator;
+ private RpcAuthenticator m_rpcAuthenticator;
// --------------------------------------------------------------------------------
// Global server configuration
@@ -1699,7 +1700,6 @@ public class ServerConfiguration extends AbstractLifecycleBean
*/
private final void processNFSServerConfig(Config config)
{
-/**
// If the configuration section is not valid then NFS is disabled
if ( config == null)
@@ -1856,8 +1856,26 @@ public class ServerConfiguration extends AbstractLifecycleBean
// Create the RPC authenticator
- m_rpcAuthenticator = new DefaultRpcAuthenticator();
-**/
+ elem = config.getConfigElement("rpcAuthenticator");
+ if ( elem != null)
+ {
+ // Create the RPC authenticator
+
+ m_rpcAuthenticator = new AlfrescoRpcAuthenticator();
+
+ try
+ {
+ // Initialize the RPC authenticator
+
+ m_rpcAuthenticator.initialize( this, elem);
+ }
+ catch (InvalidConfigurationException ex)
+ {
+ throw new AlfrescoRuntimeException( ex.getMessage());
+ }
+ }
+ else
+ throw new AlfrescoRuntimeException("RPC authenticator configuration missing, require user mappings");
}
/**
@@ -2181,8 +2199,8 @@ public class ServerConfiguration extends AbstractLifecycleBean
{
// Check if the appropriate authentication component type is configured
- if ( ntlmMode != NTLMMode.NONE)
- throw new AlfrescoRuntimeException("Wrong authentication setup for passthru authenticator (can only be used with LDAP/JAAS auth component)");
+// if ( ntlmMode != NTLMMode.NONE)
+// throw new AlfrescoRuntimeException("Wrong authentication setup for passthru authenticator (can only be used with LDAP/JAAS auth component)");
// Load the passthru authenticator dynamically
@@ -3845,12 +3863,11 @@ public class ServerConfiguration extends AbstractLifecycleBean
*
* @return RpcAuthenticator
*/
-/**
public final RpcAuthenticator getRpcAuthenticator()
{
return m_rpcAuthenticator;
}
-**/
+
/**
* Close the server configuration, used to close various components that are shared between protocol
* handlers.
diff --git a/source/java/org/alfresco/filesys/server/filesys/FilesysTransaction.java b/source/java/org/alfresco/filesys/server/filesys/FilesysTransaction.java
new file mode 100644
index 0000000000..3258aa7068
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/filesys/FilesysTransaction.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2005-2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.filesys;
+
+import javax.transaction.UserTransaction;
+
+/**
+ * Filesystem Transaction Class
+ *
+ *
Holds the details of a transaction used during a batch of filesystem driver requests.
+ *
+ * @author gkspencer
+ */
+public class FilesysTransaction {
+
+ // Transaction
+
+ private UserTransaction m_transaction;
+
+ // Flag to indicate read-only or writeable transaction
+
+ private boolean m_readOnly;
+
+ /**
+ * Default constructor
+ */
+ public FilesysTransaction()
+ {
+ }
+
+ /**
+ * Check if the transaction is valid
+ *
+ * @return boolean
+ */
+ public final boolean hasTransaction()
+ {
+ return m_transaction != null ? true : false;
+ }
+
+ /**
+ * Check if the transaction is read-only
+ *
+ * @return boolean
+ */
+ public final boolean isReadOnly()
+ {
+ return m_readOnly;
+ }
+
+ /**
+ * Return the active transaction
+ *
+ * @return UserTransaction
+ */
+ public final UserTransaction getTransaction()
+ {
+ return m_transaction;
+ }
+
+ /**
+ * Set the transaction
+ *
+ * @param trans UserTransaction
+ * @param readOnly boolean
+ */
+ public final void setTransaction( UserTransaction trans, boolean readOnly)
+ {
+ m_transaction = trans;
+ m_readOnly = readOnly;
+ }
+
+ /**
+ * Clear the transaction
+ */
+ public final void clearTransaction()
+ {
+ m_transaction = null;
+ m_readOnly = true;
+ }
+
+ /**
+ * Return the transaction details as a string
+ *
+ * @return String
+ */
+ public String toString()
+ {
+ StringBuilder str = new StringBuilder();
+
+ str.append( "[");
+ str.append( m_transaction);
+ str.append( isReadOnly() ? ",Read" : ",Write");
+ str.append( "]");
+
+ return str.toString();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/filesys/SymbolicLinkInterface.java b/source/java/org/alfresco/filesys/server/filesys/SymbolicLinkInterface.java
new file mode 100644
index 0000000000..f526e907e7
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/filesys/SymbolicLinkInterface.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.filesys;
+
+import org.alfresco.filesys.server.SrvSession;
+
+/**
+ * Symbolic Link Interface
+ *
+ *
Optional interface that a filesystem driver can implement to indicate that symbolic links are supported.
+ */
+public interface SymbolicLinkInterface {
+
+ /**
+ * Read the link data for a symbolic link
+ *
+ * @param sess SrvSession
+ * @param tree TreeConnection
+ * @param path String
+ * @return String
+ * @exception AccessDeniedException
+ */
+ public String readSymbolicLink( SrvSession sess, TreeConnection tree, String path)
+ throws AccessDeniedException;
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/AuthType.java b/source/java/org/alfresco/filesys/server/oncrpc/AuthType.java
new file mode 100644
index 0000000000..f09bc44963
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/AuthType.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+/**
+ * Authentication Types Class
+ *
+ * @author GKSpencer
+ */
+public final class AuthType {
+
+ // Authentication type contants
+
+ public static final int Null = 0;
+ public static final int Unix = 1;
+ public static final int Short = 2;
+ public static final int DES = 3;
+
+ // Authentication type strings
+
+ private static final String[] _authTypes = { "Null", "Unix", "Short", "DES" };
+
+ /**
+ * Return the authentication type as string
+ *
+ * @param type int
+ * @return String
+ */
+ public static final String getTypeAsString(int type) {
+ if ( type < 0 || type >= _authTypes.length)
+ return "" + type;
+ return _authTypes[type];
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/DefaultRpcAuthenticator.java b/source/java/org/alfresco/filesys/server/oncrpc/DefaultRpcAuthenticator.java
new file mode 100644
index 0000000000..098ac01a62
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/DefaultRpcAuthenticator.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import org.alfresco.config.ConfigElement;
+import org.alfresco.filesys.server.SrvSession;
+import org.alfresco.filesys.server.auth.ClientInfo;
+import org.alfresco.filesys.server.config.InvalidConfigurationException;
+import org.alfresco.filesys.server.config.ServerConfiguration;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Default RPC Authenticator Class
+ *
+ *
RPC authenticator implementation that allows any client to access the RPC servers.
+ *
+ * @author GKSpencer
+ */
+public class DefaultRpcAuthenticator implements RpcAuthenticator {
+
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol.auth");
+
+ // Authentication types aupported by this implementation
+
+ private int[] _authTypes = { AuthType.Null, AuthType.Unix };
+
+ /**
+ * Authenticate an RPC client and create a unique session id key.
+ *
+ * @param authType int
+ * @param rpc RpcPacket
+ * @return Object
+ * @throws RpcAuthenticationException
+ */
+ public Object authenticateRpcClient(int authType, RpcPacket rpc)
+ throws RpcAuthenticationException {
+
+ // Create a unique session key depending on the authentication type
+
+ Object sessKey = null;
+
+ switch (authType) {
+
+ // Null authentication
+
+ case AuthType.Null:
+ sessKey = new Integer(rpc.getClientAddress().hashCode());
+ break;
+
+ // Unix authentication
+
+ case AuthType.Unix:
+
+ // Get the gid and uid from the credentials data in the request
+
+ rpc.positionAtCredentialsData();
+ rpc.skipBytes(4);
+ int nameLen = rpc.unpackInt();
+ rpc.skipBytes(nameLen);
+
+ int gid = rpc.unpackInt();
+ int uid = rpc.unpackInt();
+
+ // Check if the Unix authentication session table is valid
+
+ sessKey = new Long((((long) rpc.getClientAddress().hashCode()) << 32) + (gid << 16) + uid);
+ break;
+ }
+
+ // Check if the session key is valid, if not then the authentication
+ // type is unsupported
+
+ if (sessKey == null)
+ throw new RpcAuthenticationException(Rpc.AuthBadCred, "Unsupported auth type, " + authType);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcAuth: RPC from " + rpc.getClientDetails()
+ + ", authType=" + AuthType.getTypeAsString(authType)
+ + ", sessKey=" + sessKey);
+
+ // Return the session key
+
+ return sessKey;
+ }
+
+ /**
+ * Return the authentication types that are supported by this
+ * implementation.
+ *
+ * @return int[]
+ */
+ public int[] getRpcAuthenticationTypes() {
+ return _authTypes;
+ }
+
+ /**
+ * Return the client information for the specified RPC request
+ *
+ * @param sessKey
+ * Object
+ * @param rpc
+ * RpcPacket
+ * @return ClientInfo
+ */
+ public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc) {
+
+ // Create a client information object to hold the client details
+
+ ClientInfo cInfo = new ClientInfo("", null);
+
+ // Get the authentication type
+
+ int authType = rpc.getCredentialsType();
+ cInfo.setNFSAuthenticationType(authType);
+
+ // Unpack the client details from the RPC request
+
+ switch (authType) {
+
+ // Null authentication
+
+ case AuthType.Null:
+ cInfo.setClientAddress(rpc.getClientAddress().getHostAddress());
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", addr="
+ + rpc.getClientAddress().getHostAddress());
+ break;
+
+ // Unix authentication
+
+ case AuthType.Unix:
+
+ // Unpack the credentials data
+
+ rpc.positionAtCredentialsData();
+ rpc.skipBytes(4); // stamp id
+
+ cInfo.setClientAddress(rpc.unpackString());
+ cInfo.setUid(rpc.unpackInt());
+ cInfo.setGid(rpc.unpackInt());
+
+ // Check for an additional groups list
+
+ int grpLen = rpc.unpackInt();
+ if (grpLen > 0) {
+ int[] groups = new int[grpLen];
+ rpc.unpackIntArray(groups);
+
+ cInfo.setGroupsList(groups);
+ }
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", name="
+ + cInfo.getClientAddress() + ", uid=" + cInfo.getUid() + ", gid=" + cInfo.getGid() + ", groups=" + grpLen);
+ break;
+ }
+
+ // Return the client information
+
+ return cInfo;
+ }
+
+ /**
+ * Initialize the RPC authenticator
+ *
+ * @param config ServerConfiguration
+ * @param params NameValueList
+ * @throws InvalidConfigurationException
+ */
+ public void initialize(ServerConfiguration config, ConfigElement params)
+ throws InvalidConfigurationException {
+ }
+
+ /**
+ * Set the current authenticated user context for this thread
+ *
+ * @param sess SrvSession
+ * @param client ClientInfo
+ */
+ public void setCurrentUser( SrvSession sess, ClientInfo client)
+ {
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedTcpRpcPacketHandler.java b/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedTcpRpcPacketHandler.java
new file mode 100644
index 0000000000..0ae4562ed2
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedTcpRpcPacketHandler.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * Multi-Threaded Tcp Rpc Packet Handler Class
+ *
+ *
Adds multi-threaded processing of RPC requests to the standard TCP RPC handler.
+ *
+ * @author GKSpencer
+ */
+public class MultiThreadedTcpRpcPacketHandler extends TcpRpcPacketHandler implements RpcPacketHandler {
+
+ /**
+ * Class constructor to create a TCP RPC handler for a server.
+ *
+ * @param handler TcpRpcSessionHandler
+ * @param sessId int
+ * @param server RpcProcessor
+ * @param socket Socket
+ * @param maxRpcSize int
+ * @throws IOException
+ */
+ public MultiThreadedTcpRpcPacketHandler(TcpRpcSessionHandler handler, int sessId, RpcProcessor server,
+ Socket socket, int maxRpcSize) throws IOException
+ {
+ super(handler, sessId, server, socket, maxRpcSize);
+ }
+
+ /**
+ * Return the multi-threaded RPC session handler
+ *
+ * @return MultiThreadedTcpRpcSessionHandler
+ */
+ protected final MultiThreadedTcpRpcSessionHandler getSessionHandler()
+ {
+ return (MultiThreadedTcpRpcSessionHandler) getHandler();
+ }
+
+ /**
+ * Allocate an RPC packet from the packet pool
+ *
+ * @param maxSize int
+ * @return RpcPacket
+ */
+ protected RpcPacket allocateRpcPacket(int maxSize)
+ {
+
+ // Use the session handler to allocate the RPC packet
+
+ return getSessionHandler().allocateRpcPacket(maxSize);
+ }
+
+ /**
+ * Deallocate an RPC packet, return the packet to the pool.
+ *
+ * @param pkt RpcPacket
+ */
+ protected void deallocateRpcPacket(RpcPacket pkt)
+ {
+
+ // Return the packet to the pool
+
+ if (pkt.isAllocatedFromPool())
+ pkt.getOwnerPacketPool().releasePacket(pkt);
+ }
+
+ /**
+ * Process an RPC request by passing the request to a pool of worker threads.
+ *
+ * @param rpc RpcPacket
+ * @throws IOException
+ */
+ protected void processRpc(RpcPacket rpc)
+ throws IOException
+ {
+
+ // Link the RPC request to this handler
+
+ rpc.setPacketHandler(this);
+
+ // Queue the RPC request to the session handlers thread pool for processing
+
+ getSessionHandler().queueRpcRequest(rpc);
+ }
+
+ /**
+ * Send an RPC response using the TCP socket connection
+ *
+ * @param rpc RpcPacket
+ * @throws IOException
+ */
+ public void sendRpcResponse(RpcPacket rpc)
+ throws IOException
+ {
+
+ // Send the RPC response
+
+ sendRpc(rpc);
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedTcpRpcSessionHandler.java b/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedTcpRpcSessionHandler.java
new file mode 100644
index 0000000000..f345a53f42
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedTcpRpcSessionHandler.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+
+import org.alfresco.filesys.server.NetworkServer;
+
+/**
+ * Multi-threaded TCP RPC Session Handler Class
+ *
+ *
Extend the basic TCP RPC handler class to process RPC requests using a thread pool.
+ *
+ * @author GKSpencer
+ */
+public class MultiThreadedTcpRpcSessionHandler extends TcpRpcSessionHandler {
+
+ // Constants
+ //
+ // Default packet pool size
+
+ public static final int DefaultPacketPoolSize = 50;
+ public static final int DefaultSmallPacketSize = 512;
+
+ // RPC packet pool
+
+ private RpcPacketPool m_packetPool;
+
+ // Request handler thread pool
+
+ private RpcRequestThreadPool m_threadPool;
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ * @param protocol String
+ * @param rpcServer RpcProcessor
+ * @param server NetworkServer
+ * @param addr InetAddress
+ * @param port int
+ * @param maxSize int
+ */
+ public MultiThreadedTcpRpcSessionHandler(String name, String protocol, RpcProcessor rpcServer,
+ NetworkServer server, InetAddress addr, int port, int maxSize)
+ {
+ super(name, protocol, rpcServer, server, addr, port, maxSize);
+ }
+
+ /**
+ * Initialize the session socket handler
+ *
+ * @param server
+ * @throws IOException
+ */
+ public void initializeSessionHandler(NetworkServer server)
+ throws IOException
+ {
+
+ // If the packet pool has not been created, create a default packet pool
+
+ if (m_packetPool == null)
+ m_packetPool = new RpcPacketPool(DefaultSmallPacketSize, DefaultPacketPoolSize, getMaximumRpcSize(),
+ DefaultPacketPoolSize);
+
+ // Create the RPC request handling thread pool, if not already created
+
+ if (m_threadPool == null)
+ m_threadPool = new RpcRequestThreadPool(getHandlerName(), getRpcProcessor());
+
+ // Call the base class initialization
+
+ super.initializeSessionHandler(server);
+ }
+
+ /**
+ * Allocate an RPC packet from the packet pool
+ *
+ * @param size int
+ * @return RpcPacket
+ */
+ protected final RpcPacket allocateRpcPacket(int size)
+ {
+
+ // Allocate an RPC packet from the packet pool
+
+ return m_packetPool.allocatePacket(size);
+ }
+
+ /**
+ * Queue an RPC request to the thread pool for processing
+ *
+ * @param rpc RpcPacket
+ */
+ protected final void queueRpcRequest(RpcPacket rpc)
+ {
+
+ // DEBUG
+
+ // Debug.println("MTRpcSessHandler Queue rpc=" + rpc.toString());
+
+ // Queue the RPC request to the thread pool for processing
+
+ m_threadPool.queueRpcRequest(rpc);
+ }
+
+ /**
+ * Create a multi-threaded packet handler for the new session
+ *
+ * @param sessId int
+ * @param sock Socket
+ * @return TcpRpcPacketHandler
+ * @throws IOException
+ */
+ protected TcpRpcPacketHandler createPacketHandler(int sessId, Socket sock)
+ throws IOException
+ {
+
+ // Create a multi-threaded packet handler to use the session handlers thread pool to
+ // process the RPC requests
+
+ return new MultiThreadedTcpRpcPacketHandler(this, sessId, getRpcProcessor(), sock, getMaximumRpcSize());
+ }
+
+ /**
+ * Set the packet pool size
+ *
+ * @param smallSize int
+ * @param smallPool int
+ * @param largeSize int
+ * @param largePool int
+ */
+ public final void setPacketPool(int smallSize, int smallPool, int largeSize, int largePool)
+ {
+
+ // Create the packet pool, if not already initialized
+
+ if (m_packetPool == null)
+ {
+
+ // Create the packet pool
+
+ m_packetPool = new RpcPacketPool(smallSize, smallPool, largeSize, largePool);
+ }
+ }
+
+ /**
+ * Set the packet pool size
+ *
+ * @param poolSize int
+ */
+ public final void setPacketPool(int poolSize)
+ {
+
+ // Create the packet pool, if not already initialized
+
+ if (m_packetPool == null)
+ {
+
+ // Create the packet pool
+
+ m_packetPool = new RpcPacketPool(DefaultSmallPacketSize, poolSize, getMaximumRpcSize(), poolSize);
+ }
+ }
+
+ /**
+ * Set the packet pool
+ *
+ * @param pktPool RpcPacketPool
+ */
+ public final void setPacketPool(RpcPacketPool pktPool)
+ {
+
+ // Set the packet pool, if not already initialized
+
+ if (m_packetPool == null)
+ m_packetPool = pktPool;
+ }
+
+ /**
+ * Set the thread pool size
+ *
+ * @param numThreads int
+ */
+ public final void setThreadPool(int numThreads)
+ {
+
+ // Create the thread pool, if not already initialized
+
+ if (m_threadPool == null)
+ {
+
+ // Create the thread pool
+
+ m_threadPool = new RpcRequestThreadPool(getHandlerName(), numThreads, getRpcProcessor());
+ }
+ }
+
+ /**
+ * Set the thread pool
+ *
+ * @param threadPool RpcRequestThreadPool
+ */
+ public final void setThreadPool(RpcRequestThreadPool threadPool)
+ {
+
+ // Set the thread pool, if not already initialized
+
+ if (m_threadPool == null)
+ m_threadPool = threadPool;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedUdpRpcDatagramHandler.java b/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedUdpRpcDatagramHandler.java
new file mode 100644
index 0000000000..d5391b96f0
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/MultiThreadedUdpRpcDatagramHandler.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+
+import org.alfresco.filesys.server.NetworkServer;
+
+/**
+ * Multi-Threaded UDP RPC Datagram Handler Class
+ *
+ *
Extend the basic UDP RPC handler class to process RPC requests using a thread pool.
+ *
+ * @author GKSpencer
+ */
+public class MultiThreadedUdpRpcDatagramHandler extends UdpRpcDatagramHandler implements RpcPacketHandler {
+
+ // Constants
+ //
+ // Default packet pool size
+
+ public static final int DefaultPacketPoolSize = 50;
+ public static final int DefaultSmallPacketSize = 512;
+
+ // RPC packet pool
+
+ private RpcPacketPool m_packetPool;
+
+ // Request handler thread pool
+
+ private RpcRequestThreadPool m_threadPool;
+
+ // RPC response queue
+
+ private RpcRequestQueue m_txQueue;
+
+ // Datagram sender thread
+
+ private DatagramSender m_txThread;
+
+ // Current receive RPC packet
+
+ private RpcPacket m_rxPkt;
+
+ /**
+ * Datagram Sender Thread Inner Class
+ */
+ protected class DatagramSender implements Runnable
+ {
+
+ // Worker thread
+
+ private Thread mi_thread;
+
+ // RPC sender thread datagram packet
+
+ private DatagramPacket mi_txPkt;
+
+ // Shutdown flag
+
+ private boolean mi_shutdown = false;
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ */
+ public DatagramSender(String name)
+ {
+
+ // Create the worker thread
+
+ mi_thread = new Thread(this);
+ mi_thread.setName(name);
+ mi_thread.setDaemon(true);
+ mi_thread.start();
+ }
+
+ /**
+ * Request the worker thread to shutdown
+ */
+ public final void shutdownRequest()
+ {
+ mi_shutdown = true;
+ try
+ {
+ mi_thread.interrupt();
+ } catch (Exception ex)
+ {
+ }
+ }
+
+ /**
+ * Run the thread
+ */
+ public void run()
+ {
+
+ // Allocate the datagram packet for sending the RPC responses
+
+ mi_txPkt = new DatagramPacket(new byte[4], 4);
+
+ // Loop until shutdown
+
+ RpcPacket rpc = null;
+
+ while (mi_shutdown == false)
+ {
+
+ try
+ {
+
+ // Wait for an RPC response to be queued
+
+ rpc = m_txQueue.removeRequest();
+ } catch (InterruptedException ex)
+ {
+
+ // Check for shutdown
+
+ if (mi_shutdown == true)
+ break;
+ }
+
+ // If the request is valid process it
+
+ if (rpc != null)
+ {
+
+ try
+ {
+
+ // Initialize the datagram packet for this response
+
+ mi_txPkt.setAddress(rpc.getClientAddress());
+ mi_txPkt.setPort(rpc.getClientPort());
+ mi_txPkt.setData(rpc.getBuffer(), rpc.getOffset(), rpc.getLength());
+
+ // Send the RPC response
+
+ getDatagramSocket().send(mi_txPkt);
+ } catch (Throwable ex)
+ {
+
+ // Do not display errors if shutting down
+
+ if (mi_shutdown == false)
+ {
+ logger.debug("DatagramSender " + Thread.currentThread().getName() + ":");
+ logger.debug(ex);
+ }
+ } finally
+ {
+
+ // Release the RPC packet back to the packet pool
+
+ if (rpc.isAllocatedFromPool())
+ rpc.getOwnerPacketPool().releasePacket(rpc);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ * @param protocol String
+ * @param rpcServer RpcProcessor
+ * @param server NetworkServer
+ * @param addr InetAddress
+ * @param port int
+ * @param maxSize int
+ */
+ public MultiThreadedUdpRpcDatagramHandler(String name, String protocol, RpcProcessor rpcServer,
+ NetworkServer server, InetAddress addr, int port, int maxSize)
+ {
+ super(name, protocol, rpcServer, server, addr, port, maxSize);
+ }
+
+ /**
+ * Initialize the session handler
+ *
+ * @param server NetworkServer
+ * @throws IOException
+ */
+ public void initializeSessionHandler(NetworkServer server)
+ throws IOException
+ {
+
+ // Create the RPC response queue
+
+ m_txQueue = new RpcRequestQueue();
+
+ // Create the datagram sender thread
+
+ m_txThread = new DatagramSender("UDP_Tx_" + getProtocolName());
+
+ // If the packet pool has not been created, create a default packet pool
+
+ if (m_packetPool == null)
+ m_packetPool = new RpcPacketPool(DefaultSmallPacketSize, DefaultPacketPoolSize, getMaximumDatagramSize(),
+ DefaultPacketPoolSize);
+
+ // Create the RPC request handling thread pool, if not already created
+
+ if (m_threadPool == null)
+ m_threadPool = new RpcRequestThreadPool(getHandlerName(), getRpcProcessor());
+
+ // Call the base class initialization
+
+ super.initializeSessionHandler(server);
+ }
+
+ /**
+ * Process the RPC request
+ *
+ * @param pkt DatagramPacket
+ * @return boolean
+ * @throws IOException
+ */
+ protected boolean processDatagram(DatagramPacket pkt)
+ throws IOException
+ {
+
+ // Make sure that the received data is using the same buffer that we allocated in the
+ // allocateBuffer() method, if not the buffer did not come from the packet pool.
+
+ if (pkt.getData() != m_rxPkt.getBuffer())
+ throw new IOException("Received datagram is not in expected buffer");
+
+ // Update the RPC packet details
+
+ m_rxPkt.setBuffer(pkt.getData(), 0, pkt.getLength());
+
+ // Set the client details
+
+ m_rxPkt.setClientDetails(pkt.getAddress(), pkt.getPort(), Rpc.UDP);
+
+ // Set the packet handler interface to be used to send the RPC reply
+
+ m_rxPkt.setPacketHandler(this);
+
+ // Queue the request to the thread pool for processing
+
+ queueRpcRequest(m_rxPkt);
+
+ // Indicate that the datagram buffer cannot be re-used, the main datagram receiving thread must
+ // allocate a new buffer for the next request.
+
+ return false;
+ }
+
+ /**
+ * Queue an RPC request to the thread pool for processing
+ *
+ * @param rpc RpcPacket
+ */
+ protected final void queueRpcRequest(RpcPacket rpc)
+ {
+
+ // Queue the RPC request to the thread pool for processing
+
+ m_threadPool.queueRpcRequest(rpc);
+ }
+
+ /**
+ * Allocate a buffer for the next datagram
+ *
+ * @param bufSize int
+ * @return byte[]
+ */
+ protected byte[] allocateBuffer(int bufSize)
+ {
+
+ // Allocate an RPC packet from the packet pool
+
+ m_rxPkt = m_packetPool.allocatePacket(bufSize);
+
+ // Return the buffer from the RPC packet
+
+ return m_rxPkt.getBuffer();
+ }
+
+ /**
+ * Send an RPC response using the datagram socket
+ *
+ * @param rpc RpcPacket
+ * @throws IOException
+ */
+ public void sendRpcResponse(RpcPacket rpc)
+ throws IOException
+ {
+
+ // Queue the RPC response to the datagram sender thread
+
+ m_txQueue.addRequest(rpc);
+ }
+
+ /**
+ * Set the packet pool size
+ *
+ * @param smallSize int
+ * @param smallPool int
+ * @param largeSize int
+ * @param largePool int
+ */
+ public final void setPacketPool(int smallSize, int smallPool, int largeSize, int largePool)
+ {
+
+ // Create the packet pool, if not already initialized
+
+ if (m_packetPool == null)
+ {
+
+ // Create the packet pool
+
+ m_packetPool = new RpcPacketPool(smallSize, smallPool, largeSize, largePool);
+ }
+ }
+
+ /**
+ * Set the packet pool size
+ *
+ * @param poolSize int
+ */
+ public final void setPacketPool(int poolSize)
+ {
+
+ // Create the packet pool, if not already initialized
+
+ if (m_packetPool == null)
+ {
+
+ // Create the packet pool
+
+ m_packetPool = new RpcPacketPool(DefaultSmallPacketSize, poolSize, getMaximumDatagramSize(), poolSize);
+ }
+ }
+
+ /**
+ * Set the packet pool
+ *
+ * @param pktPool RpcPacketPool
+ */
+ public final void setPacketPool(RpcPacketPool pktPool)
+ {
+
+ // Set the packet pool, if not already initialized
+
+ if (m_packetPool == null)
+ m_packetPool = pktPool;
+ }
+
+ /**
+ * Set the thread pool size
+ *
+ * @param numThreads int
+ */
+ public final void setThreadPool(int numThreads)
+ {
+
+ // Create the thread pool, if not already initialized
+
+ if (m_threadPool == null)
+ {
+
+ // Create the thread pool
+
+ m_threadPool = new RpcRequestThreadPool(getHandlerName(), numThreads, getRpcProcessor());
+ }
+ }
+
+ /**
+ * Set the thread pool
+ *
+ * @param threadPool RpcRequestThreadPool
+ */
+ public final void setThreadPool(RpcRequestThreadPool threadPool)
+ {
+
+ // Set the thread pool, if not already initialized
+
+ if (m_threadPool == null)
+ m_threadPool = threadPool;
+ }
+
+ /**
+ * Close the session handler
+ *
+ * @param server NetworkServer
+ */
+ public void closeSessionHandler(NetworkServer server)
+ {
+
+ // Shutdown the datagram sender thread
+
+ m_txThread.shutdownRequest();
+
+ // Call the base class
+
+ super.closeSessionHandler(server);
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/PortMapping.java b/source/java/org/alfresco/filesys/server/oncrpc/PortMapping.java
new file mode 100644
index 0000000000..6b658b792c
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/PortMapping.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+/**
+ * Port Details Class
+ *
+ *
Contains the details of an RPC service registered with the PortMapper service.
+ *
+ * @author GKSpencer
+ */
+public class PortMapping {
+
+ // Program id and version
+
+ private int m_programId;
+ private int m_versionId;
+
+ // Protocol type (UDP or TCP)
+
+ private int m_protocol;
+
+ // Port
+
+ private int m_port;
+
+ /**
+ * Class constructor
+ *
+ * @param progId int
+ * @param verId int
+ * @param protocol int
+ * @param port int
+ */
+ public PortMapping(int progId, int verId, int protocol, int port)
+ {
+ m_programId = progId;
+ m_versionId = verId;
+ m_protocol = protocol;
+ m_port = port;
+ }
+
+ /**
+ * Return the program id
+ *
+ * @return int
+ */
+ public final int getProgramId()
+ {
+ return m_programId;
+ }
+
+ /**
+ * Return the version id
+ *
+ * @return int
+ */
+ public final int getVersionId()
+ {
+ return m_versionId;
+ }
+
+ /**
+ * Return the protocol type
+ *
+ * @return int
+ */
+ public final int getProtocol()
+ {
+ return m_protocol;
+ }
+
+ /**
+ * Return the port number
+ *
+ * @return int
+ */
+ public final int getPort()
+ {
+ return m_port;
+ }
+
+ /**
+ * Return a hash code for the port mapping
+ *
+ * @return int
+ */
+ public int hashCode()
+ {
+
+ // Create a hash code from the program id + version + protocol
+
+ return generateHashCode(m_programId, m_versionId, m_protocol);
+ }
+
+ /**
+ * Generate a hash code for the specified program, version and protocol
+ *
+ * @param progId int
+ * @param verId int
+ * @param proto int
+ * @return int
+ */
+ public final static int generateHashCode(int progId, int verId, int proto)
+ {
+
+ // Create a hash code from the program id + version + protocol
+
+ return (progId << 16) + (verId << 8) + proto;
+ }
+
+ /**
+ * Return the port details as a string
+ *
+ * @return String
+ */
+ public String toString()
+ {
+ StringBuffer str = new StringBuffer(64);
+
+ str.append("[");
+ str.append(getProgramId());
+ str.append(":");
+ str.append(getVersionId());
+ str.append(getProtocol() == Rpc.TCP ? ",TCP," : ",UDP,");
+ str.append(getPort());
+ str.append("]");
+
+ return str.toString();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/Rpc.java b/source/java/org/alfresco/filesys/server/oncrpc/Rpc.java
new file mode 100644
index 0000000000..f172fc4aed
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/Rpc.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+/**
+ * ONC/RPC Constants Class
+ *
+ * @author GKSpencer
+ */
+public class Rpc {
+
+ // RPC length/flags
+
+ public static final int LastFragment = 0x80000000;
+ public static final int LengthMask = 0x7FFFFFFF;
+
+ // RPC message types
+
+ public static final int Call = 0;
+ public static final int Reply = 1;
+
+ // Call status
+
+ public static final int CallAccepted = 0;
+ public static final int CallDenied = 1;
+
+ // Required RPC version
+
+ public static final int RpcVersion = 2;
+
+ // Call accepted status codes
+
+ public static final int StsSuccess = 0; // RPC executed successfully
+ public static final int StsProgUnavail = 1; // program not available
+ public static final int StsProgMismatch = 2; // program version mismatch
+ public static final int StsProcUnavail = 3; // program does not support procedure
+ public static final int StsBadArgs = 4; // bad arguments in request
+
+ // Call rejected status codes
+
+ public static final int StsRpcMismatch = 0; // RPC version number does not equal 2
+ public static final int StsAuthError = 1; // authentication error
+
+ // Authentication failure status codes
+
+ public static final int AuthBadCred = 1; // bad credentials
+ public static final int AuthRejectCred = 2; // client must begin new session
+ public static final int AuthBadVerf = 3; // bad verifier
+ public static final int AuthRejectedVerf = 4; // verifier rejected or replayed
+ public static final int AuthTooWeak = 5; // rejected for security reasons
+
+ // True/false values
+
+ public static final int True = 1;
+ public static final int False = 0;
+
+ // Protocol ids
+
+ public static final int TCP = 6;
+ public static final int UDP = 17;
+
+ /**
+ * Return a program id as a service name
+ *
+ * @param progId int
+ * @return String
+ */
+ public final static String getServiceName(int progId)
+ {
+ String svcName = null;
+
+ switch (progId)
+ {
+ case 100005:
+ svcName = "Mount";
+ break;
+ case 100003:
+ svcName = "NFS";
+ break;
+ case 100000:
+ svcName = "Portmap";
+ break;
+ default:
+ svcName = "" + progId;
+ break;
+ }
+
+ return svcName;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcAuthenticationException.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcAuthenticationException.java
new file mode 100644
index 0000000000..1882e80b34
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcAuthenticationException.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+/**
+ * RPC Authentication Exception Class
+ *
+ * @author GKSpencer
+ */
+public class RpcAuthenticationException extends Exception {
+
+ // Object version id
+
+ private static final long serialVersionUID = 7599358351809146330L;
+
+ // Authentication failure error code
+
+ private int m_authError;
+
+ /**
+ * Class constructor
+ *
+ * @param authError int
+ */
+ public RpcAuthenticationException(int authError)
+ {
+ m_authError = authError;
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param authError int
+ * @param msg String
+ */
+ public RpcAuthenticationException(int authError, String msg)
+ {
+ super(msg);
+ m_authError = authError;
+ }
+
+ /**
+ * Get the authentication error code
+ *
+ * @return int
+ */
+ public final int getAuthenticationErrorCode()
+ {
+ return m_authError;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcAuthenticator.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcAuthenticator.java
new file mode 100644
index 0000000000..8b6f6cce73
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcAuthenticator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import org.alfresco.config.ConfigElement;
+import org.alfresco.filesys.server.SrvSession;
+import org.alfresco.filesys.server.auth.ClientInfo;
+import org.alfresco.filesys.server.config.InvalidConfigurationException;
+import org.alfresco.filesys.server.config.ServerConfiguration;
+
+/**
+ * RPC Authenticator Interface
+ *
+ *
Provides authentication support for ONC/RPC requests.
+ *
+ * @author GKSpencer
+ */
+public interface RpcAuthenticator
+{
+ /**
+ * Initialize the RPC authenticator
+ *
+ * @param config ServerConfiguration
+ * @param params NameValueList
+ * @exception InvalidConfigurationException
+ */
+ public void initialize(ServerConfiguration config, ConfigElement params)
+ throws InvalidConfigurationException;
+
+ /**
+ * Authenticate an RPC client using the credentials within the RPC request.
+ * The object that is returned is used as the key to find the associated
+ * session object.
+ *
+ * @param authType int
+ * @param rpc RpcPacket
+ * @return Object
+ * @exception RpcAuthenticationException
+ */
+ public Object authenticateRpcClient(int authType, RpcPacket rpc)
+ throws RpcAuthenticationException;
+
+ /**
+ * Get RPC client information from the RPC request.
+ *
+ *
+ * This method is called when a new session object is created by an RPC
+ * server.
+ *
+ * @param sessKey Object
+ * @param rpc RpcPacket
+ * @return ClientInfo
+ */
+ public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc);
+
+ /**
+ * Return a list of the authentication types that the RPC authenticator
+ * implementation supports. The authentication types are specified in the
+ * AuthType class.
+ *
+ * @return int[]
+ */
+ public int[] getRpcAuthenticationTypes();
+
+ /**
+ * Set the current authenticated user context for this thread
+ *
+ * @param sess SrvSession
+ * @param client ClientInfo
+ */
+ public void setCurrentUser( SrvSession sess, ClientInfo client);
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcClient.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcClient.java
new file mode 100644
index 0000000000..cc3b1a0f98
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcClient.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * RPC Client Class
+ *
+ *
Provides either a socket or datagram connection to an RPC server.
+ *
+ * @author GKSpencer
+ */
+public abstract class RpcClient {
+
+ // Network address and port to connect to on the remote RPC server
+
+ private InetAddress m_server;
+ private int m_port;
+
+ // Protocol type
+
+ private int m_protocol;
+
+ // Maximum RPC size to send/receive
+
+ private int m_maxRpcSize;
+
+ /**
+ * Class constructor
+ *
+ * @param addr InetAddress
+ * @param port int
+ * @param proto int
+ * @param maxRpcSize int
+ * @throws IOException
+ * @throws SocketException
+ */
+ protected RpcClient(InetAddress addr, int port, int proto, int maxRpcSize) throws IOException, SocketException
+ {
+
+ // Save the server address, port and the protocol type
+
+ m_server = addr;
+ m_port = port;
+
+ m_protocol = proto;
+
+ // Set the maximum RPC size to send/recieve
+
+ m_maxRpcSize = maxRpcSize;
+ }
+
+ /**
+ * Return the maximum RPC size
+ *
+ * @return int
+ */
+ public final int getMaximumRpcSize()
+ {
+ return m_maxRpcSize;
+ }
+
+ /**
+ * Return the server address
+ *
+ * @return InetAddress
+ */
+ public final InetAddress getServerAddress()
+ {
+ return m_server;
+ }
+
+ /**
+ * Return the server port
+ *
+ * @return int
+ */
+ public final int getServerPort()
+ {
+ return m_port;
+ }
+
+ /**
+ * Return the protocol type
+ *
+ * @return int
+ */
+ public final int isProtocol()
+ {
+ return m_protocol;
+ }
+
+ /**
+ * Send an RPC request to the server
+ *
+ * @param rpc RpcPacket
+ * @param rxRpc RpcPacket
+ * @return RpcPacket
+ * @throws IOException
+ */
+ public abstract RpcPacket sendRPC(RpcPacket rpc, RpcPacket rxRpc)
+ throws IOException;
+
+ /**
+ * Close the connection to the remote RPC server
+ */
+ public abstract void closeConnection();
+
+ /**
+ * Return the RPC connection details as a string
+ *
+ * @return String
+ */
+ public String toString()
+ {
+ StringBuffer str = new StringBuffer();
+
+ str.append("[");
+ str.append(isProtocol() == Rpc.TCP ? "TCP:" : "UDP:");
+ str.append(getServerAddress().getHostAddress());
+ str.append(":");
+ str.append(getServerPort());
+
+ str.append(",");
+ str.append(getMaximumRpcSize());
+ str.append("]");
+
+ return str.toString();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcNetworkServer.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcNetworkServer.java
new file mode 100644
index 0000000000..de38833e8d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcNetworkServer.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+
+import org.alfresco.filesys.server.NetworkServer;
+import org.alfresco.filesys.server.config.ServerConfiguration;
+import org.alfresco.filesys.server.oncrpc.portmap.PortMapper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * RPC Network Server Abstract Class
+ *
+ *
Provides the base class for RPC servers (such as mount and NFS).
+ *
+ * @author GKSpencer
+ */
+public abstract class RpcNetworkServer extends NetworkServer implements RpcProcessor {
+
+ // Debug logging
+
+ protected static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol");
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ * @param config ServerConfiguration
+ */
+ public RpcNetworkServer(String name, ServerConfiguration config)
+ {
+ super(name, config);
+ }
+
+ /**
+ * Register a port/protocol for the RPC server
+ *
+ * @param mapping PortMapping
+ * @throws IOException
+ */
+ protected final void registerRPCServer(PortMapping mapping)
+ throws IOException
+ {
+
+ // Call the main registration method
+
+ PortMapping[] mappings = new PortMapping[1];
+ mappings[0] = mapping;
+
+ registerRPCServer(mappings);
+ }
+
+ /**
+ * Register a set of ports/protocols for the RPC server
+ *
+ * @param mappings PortMapping[]
+ * @throws IOException
+ */
+ protected final void registerRPCServer(PortMapping[] mappings)
+ throws IOException
+ {
+
+ // Connect to the local portmapper service to register the RPC service
+
+ InetAddress localHost = InetAddress.getByName("127.0.0.1");
+
+ TcpRpcClient rpcClient = new TcpRpcClient(localHost, PortMapper.DefaultPort, 512);
+
+ // Allocate RPC request and response packets
+
+ RpcPacket setPortRpc = new RpcPacket(512);
+ RpcPacket rxRpc = new RpcPacket(512);
+
+ // Loop through the port mappings and register each port with the portmapper service
+
+ for (int i = 0; i < mappings.length; i++)
+ {
+
+ // Build the RPC request header
+
+ setPortRpc.buildRequestHeader(PortMapper.ProgramId, PortMapper.VersionId, PortMapper.ProcSet, 0, null, 0,
+ null);
+
+ // Pack the request parameters and set the request length
+
+ setPortRpc.packPortMapping(mappings[i]);
+ setPortRpc.setLength();
+
+ // Send the RPC request and receive a response
+
+ rxRpc = rpcClient.sendRPC(setPortRpc, rxRpc);
+ }
+ }
+
+ /**
+ * Unregister a port/protocol for the RPC server
+ *
+ * @param mapping PortMapping
+ * @throws IOException
+ */
+ protected final void unregisterRPCServer(PortMapping mapping)
+ throws IOException
+ {
+
+ // Call the main unregister ports method
+
+ PortMapping[] mappings = new PortMapping[1];
+ mappings[0] = mapping;
+
+ unregisterRPCServer(mappings);
+ }
+
+ /**
+ * Unregister a set of ports/protocols for the RPC server
+ *
+ * @param mappings PortMapping[]
+ * @throws IOException
+ */
+ protected final void unregisterRPCServer(PortMapping[] mappings)
+ throws IOException
+ {
+
+ // Connect to the local portmapper service to unregister the RPC service
+
+ InetAddress localHost = InetAddress.getByName("127.0.0.1");
+
+ TcpRpcClient rpcClient = new TcpRpcClient(localHost, PortMapper.DefaultPort, 512);
+
+ // Allocate RPC request and response packets
+
+ RpcPacket setPortRpc = new RpcPacket(512);
+ RpcPacket rxRpc = new RpcPacket(512);
+
+ // Loop through the port mappings and unregister each port with the portmapper service
+
+ for (int i = 0; i < mappings.length; i++)
+ {
+
+ // Build the RPC request header
+
+ setPortRpc.buildRequestHeader(PortMapper.ProgramId, PortMapper.VersionId, PortMapper.ProcUnSet, 0, null, 0,
+ null);
+
+ // Pack the request parameters and set the request length
+
+ setPortRpc.packPortMapping(mappings[i]);
+ setPortRpc.setLength();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] UnRegister server RPC " + setPortRpc.toString());
+
+ // Send the RPC request and receive a response
+
+ rxRpc = rpcClient.sendRPC(setPortRpc, rxRpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] UnRegister response " + rxRpc.toString());
+ }
+ }
+
+ /**
+ * Start the RPC server
+ */
+ public abstract void startServer();
+
+ /**
+ * Shutdown the RPC server
+ *
+ * @param immediate boolean
+ */
+ public abstract void shutdownServer(boolean immediate);
+
+ /**
+ * Process an RPC request
+ *
+ * @param rpc RpcPacket
+ * @return RpcPacket
+ * @throws IOException
+ */
+ public abstract RpcPacket processRpc(RpcPacket rpc)
+ throws IOException;
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcPacket.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcPacket.java
new file mode 100644
index 0000000000..2edf6ded2b
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcPacket.java
@@ -0,0 +1,1319 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.net.*;
+
+import org.alfresco.filesys.util.DataPacker;
+
+/**
+ * ONC/RPC Request/Response Packet Class
+ *
+ * @author GKSpencer
+ */
+public class RpcPacket {
+
+ // Constants
+ //
+ // Default buffer size to allocate
+
+ private static final int DefaultBufferSize = 8192;
+
+ // Fragment header length
+
+ public static final int FragHeaderLen = 4;
+
+ // Fixed packet lengths
+
+ public static final int ResponseMismatchLen = 24;
+ public static final int ResponseAuthFailLen = 20;
+
+ // RPC data buffer
+
+ private byte[] m_buffer;
+ private int m_offset;
+
+ // Current buffer pack/unpack position and end of buffer position
+
+ private int m_pos;
+ private int m_endPos;
+
+ // Callers address, port and protocol
+
+ private InetAddress m_clientAddr;
+ private int m_clientPort;
+ private int m_protocol;
+
+ // RPC packet handler interface used to send an RPC response
+
+ private RpcPacketHandler m_pktHandler;
+
+ // Packet pool that owns this packet, if allocated from a pool
+
+ private RpcPacketPool m_ownerPool;
+
+ /**
+ * Default constructor
+ */
+ public RpcPacket()
+ {
+ // Allocate the RPC buffer
+
+ m_buffer = new byte[DefaultBufferSize];
+ m_offset = FragHeaderLen;
+
+ m_pos = FragHeaderLen;
+ m_endPos = m_buffer.length;
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param len int
+ */
+ public RpcPacket(int len)
+ {
+ // Allocate the RPC buffer
+
+ m_buffer = new byte[len + FragHeaderLen];
+ m_offset = FragHeaderLen;
+
+ m_pos = FragHeaderLen;
+ m_endPos = m_buffer.length;
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param len int
+ * @param owner RpcPacketPool
+ */
+ protected RpcPacket(int len, RpcPacketPool owner)
+ {
+ this(len);
+
+ // Set the owner
+
+ setOwnerPacketPool(owner);
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param buf byte[]
+ */
+ public RpcPacket(byte[] buf)
+ {
+ m_buffer = buf;
+ m_offset = FragHeaderLen;
+ m_pos = FragHeaderLen;
+ m_endPos = buf.length;
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param buf byte[]
+ * @param offset int
+ * @param len int
+ */
+ public RpcPacket(byte[] buf, int offset, int len)
+ {
+ m_buffer = buf;
+ m_offset = offset;
+ m_pos = offset;
+ m_endPos = offset + len;
+ }
+
+ /**
+ * Determine if the packet handler is valid
+ *
+ * @return boolean
+ */
+ public final boolean hasPacketHandler()
+ {
+ return m_pktHandler != null ? true : false;
+ }
+
+ /**
+ * Return the packet handler interface used to send/receive a packet
+ *
+ * @return RpcPacketHandler
+ */
+ public final RpcPacketHandler getPacketHandler()
+ {
+ return m_pktHandler;
+ }
+
+ /**
+ * Detemrine if the packet is allocated from a packet pool
+ *
+ * @return boolean
+ */
+ public final boolean isAllocatedFromPool()
+ {
+ return m_ownerPool != null ? true : false;
+ }
+
+ /**
+ * Return the packet pool that owns this packet
+ *
+ * @return RpcPacketPool
+ */
+ public final RpcPacketPool getOwnerPacketPool()
+ {
+ return m_ownerPool;
+ }
+
+ /**
+ * Determine if the client address has been set
+ *
+ * @return boolean
+ */
+ public final boolean hasClientAddress()
+ {
+ return m_clientAddr != null ? true : false;
+ }
+
+ /**
+ * Return the client network address
+ *
+ * @return InetAddress
+ */
+ public final InetAddress getClientAddress()
+ {
+ return m_clientAddr;
+ }
+
+ /**
+ * Return the client port
+ *
+ * @return int
+ */
+ public final int getClientPort()
+ {
+ return m_clientPort;
+ }
+
+ /**
+ * Return the client protocol
+ *
+ * @return int
+ */
+ public final int getClientProtocol()
+ {
+ return m_protocol;
+ }
+
+ /**
+ * Return the client details as a string
+ *
+ * @return String
+ */
+ public final String getClientDetails()
+ {
+ if (hasClientAddress() == false)
+ return "";
+
+ StringBuffer str = new StringBuffer(32);
+ str.append(getClientProtocol() == Rpc.TCP ? "T" : "U");
+ str.append(getClientAddress().getHostAddress());
+ str.append(":");
+ str.append(getClientPort());
+
+ return str.toString();
+ }
+
+ /**
+ * Return the current buffer position
+ *
+ * @return int
+ */
+ public final int getPosition()
+ {
+ return m_pos;
+ }
+
+ /**
+ * Return the buffer
+ *
+ * @return byte[]
+ */
+ public final byte[] getBuffer()
+ {
+ return m_buffer;
+ }
+
+ /**
+ * Return the available buffer size
+ *
+ * @return int
+ */
+ public final int getAvailableLength()
+ {
+ return m_buffer.length - m_pos;
+ }
+
+ /**
+ * Return the used buffer length
+ *
+ * @return int
+ */
+ public final int getLength()
+ {
+ return m_endPos - m_offset;
+ }
+
+ /**
+ * Return the RPC + fragment header length
+ *
+ * @return int
+ */
+ public final int getTxLength()
+ {
+ if (m_offset == 0)
+ return m_endPos;
+ else
+ return (m_endPos - m_offset) + FragHeaderLen;
+ }
+
+ /**
+ * Return the start of data offset
+ *
+ * @return int
+ */
+ public final int getOffset()
+ {
+ return m_offset;
+ }
+
+ /**
+ * Return the message type
+ *
+ * @return int
+ */
+ public final int getMessageType()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + 4);
+ }
+
+ /**
+ * Return the RPC version
+ *
+ * @return int
+ */
+ public final int getRpcVersion()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + 8);
+ }
+
+ /**
+ * Return the program id
+ *
+ * @return int
+ */
+ public final int getProgramId()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + 12);
+ }
+
+ /**
+ * Return the program version
+ *
+ * @return int
+ */
+ public final int getProgramVersion()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + 16);
+ }
+
+ /**
+ * Return the procedure id
+ *
+ * @return int
+ */
+ public final int getProcedureId()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + 20);
+ }
+
+ /**
+ * Return the credentials type
+ *
+ * @return int
+ */
+ public final int getCredentialsType()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + 24);
+ }
+
+ /**
+ * Return the credentials length
+ *
+ * @return int
+ */
+ public final int getCredentialsLength()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + 28);
+ }
+
+ /**
+ * Return the verifier type
+ *
+ * @return int
+ */
+ public final int getVerifierType()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + getCredentialsLength() + 32);
+ }
+
+ /**
+ * Return the verifier length
+ *
+ * @return int
+ */
+ public final int getVerifierLength()
+ {
+ return DataPacker.getInt(m_buffer, m_offset + getCredentialsLength() + 36);
+ }
+
+ /**
+ * Return the buffer offset to the verifier
+ *
+ * @return int
+ */
+ public final int getVerifierOffset()
+ {
+ return m_offset + getCredentialsLength() + 40;
+ }
+
+ /**
+ * Return the procedure specific parameters offset
+ *
+ * @return int
+ */
+ public final int getProcedureParameterOffset()
+ {
+ return m_offset + getCredentialsLength() + getVerifierLength() + 40;
+ }
+
+ /**
+ * Return the procedure parameters length
+ *
+ * @return int
+ */
+ public final int getProcedureParameterLength()
+ {
+ return m_endPos - getProcedureParameterOffset();
+ }
+
+ /**
+ * Return the XID
+ *
+ * @return int
+ */
+ public final int getXID()
+ {
+ return DataPacker.getInt(m_buffer, m_offset);
+ }
+
+ /**
+ * Check if the response has a success status
+ *
+ * @return boolean
+ */
+ public final boolean hasSuccessStatus()
+ {
+ return getAcceptStatus() == Rpc.StsSuccess ? true : false;
+ }
+
+ /**
+ * Return the reply state
+ *
+ * @return int
+ */
+ public final int getReplyState()
+ {
+ return DataPacker.getInt(m_buffer, 8);
+ }
+
+ /**
+ * Return the reject reply status
+ *
+ * @return int
+ */
+ public final int getRejectStatus()
+ {
+ return DataPacker.getInt(m_buffer, 12);
+ }
+
+ /**
+ * Return the version mismatch low version
+ *
+ * @return int
+ */
+ public final int getMismatchVersionLow()
+ {
+ return DataPacker.getInt(m_buffer, 16);
+ }
+
+ /**
+ * Return the version mismatch high version
+ *
+ * @return int
+ */
+ public final int getMismatchVersionHigh()
+ {
+ return DataPacker.getInt(m_buffer, 20);
+ }
+
+ /**
+ * Return the authentication failure status
+ *
+ * @return int
+ */
+ public final int getAuthFailStatus()
+ {
+ return DataPacker.getInt(m_buffer, 16);
+ }
+
+ /**
+ * Return the accept status for the RPC response
+ *
+ * @return int
+ */
+ public final int getAcceptStatus()
+ {
+ int pos = DataPacker.getInt(m_buffer, 16) + 20;
+ return DataPacker.getInt(m_buffer, pos);
+ }
+
+ /**
+ * Align the buffer position on a longword/32bit boundary
+ *
+ * @param ival
+ */
+ protected final void alignPosition()
+ {
+
+ // Align the buffer position on the required boundary
+
+ m_pos = (m_pos + 3) & 0xFFFFFFFC;
+ }
+
+ /**
+ * Pack a byte value
+ *
+ * @param bval int
+ */
+ public final void packByte(int bval)
+ {
+ m_buffer[m_pos++] = (byte) (bval & 0xFF);
+ }
+
+ /**
+ * Pack nulls
+ *
+ * @param len int
+ */
+ public final void packNulls(int len)
+ {
+ for (int i = 0; i < len; i++)
+ m_buffer[m_pos++] = (byte) 0;
+ }
+
+ /**
+ * Pack an integer value
+ *
+ * @param ival int
+ */
+ public final void packInt(int ival)
+ {
+ DataPacker.putInt(ival, m_buffer, m_pos);
+ m_pos += 4;
+ }
+
+ /**
+ * Pack a long value
+ *
+ * @param lval long
+ */
+ public final void packLong(long lval)
+ {
+ DataPacker.putLong(lval, m_buffer, m_pos);
+ m_pos += 8;
+ }
+
+ /**
+ * Pack a byte array with a length
+ *
+ * @param buf byte[]
+ */
+ public final void packByteArrayWithLength(byte[] buf)
+ {
+ DataPacker.putInt(buf.length, m_buffer, m_pos);
+ m_pos += 4;
+ System.arraycopy(buf, 0, m_buffer, m_pos, buf.length);
+ m_pos += buf.length;
+ alignPosition();
+ }
+
+ /**
+ * Pack a byte array
+ *
+ * @param buf byte[]
+ */
+ public final void packByteArray(byte[] buf)
+ {
+ System.arraycopy(buf, 0, m_buffer, m_pos, buf.length);
+ m_pos += buf.length;
+ alignPosition();
+ }
+
+ /**
+ * Pack an integer array
+ *
+ * @param iarray int[]
+ */
+ public final void packIntArrayWithLength(int[] iarray)
+ {
+ DataPacker.putInt(iarray.length, m_buffer, m_pos);
+ m_pos += 4;
+ for (int i = 0; i < iarray.length; i++)
+ {
+ DataPacker.putInt(iarray[i], m_buffer, m_pos);
+ m_pos += 4;
+ }
+ }
+
+ /**
+ * Pack a string
+ *
+ * @param str String
+ */
+ public final void packString(String str)
+ {
+ DataPacker.putInt(str != null ? str.length() : 0, m_buffer, m_pos);
+ m_pos += 4;
+ if (str != null)
+ {
+ m_pos = DataPacker.putString(str, m_buffer, m_pos, false);
+ alignPosition();
+ }
+ }
+
+ /**
+ * Pack a port mapping structure
+ *
+ * @param portMap PortMapping
+ */
+ public final void packPortMapping(PortMapping portMap)
+ {
+ DataPacker.putInt(portMap.getProgramId(), m_buffer, m_pos);
+ DataPacker.putInt(portMap.getVersionId(), m_buffer, m_pos + 4);
+ DataPacker.putInt(portMap.getProtocol(), m_buffer, m_pos + 8);
+ DataPacker.putInt(portMap.getPort(), m_buffer, m_pos + 12);
+
+ m_pos += 16;
+ }
+
+ /**
+ * Unpack an integer value
+ *
+ * @return int
+ */
+ public final int unpackInt()
+ {
+ int val = DataPacker.getInt(m_buffer, m_pos);
+ m_pos += 4;
+ return val;
+ }
+
+ /**
+ * Unpack a long value
+ *
+ * @return long
+ */
+ public final long unpackLong()
+ {
+ long val = DataPacker.getLong(m_buffer, m_pos);
+ m_pos += 8;
+ return val;
+ }
+
+ /**
+ * Unpack a string
+ *
+ * @return String
+ */
+ public final String unpackString()
+ {
+ int len = unpackInt();
+
+ String str = "";
+ if (len > 0)
+ {
+ str = DataPacker.getString(m_buffer, m_pos, len);
+ m_pos += len;
+ alignPosition();
+ }
+
+ return str;
+ }
+
+ /**
+ * Unpack a byte array with a length
+ *
+ * @param buf byte[]
+ */
+ public final void unpackByteArrayWithLength(byte[] buf)
+ {
+ int len = DataPacker.getInt(m_buffer, m_pos);
+ m_pos += 4;
+ if (len > 0)
+ {
+ System.arraycopy(m_buffer, m_pos, buf, 0, len);
+ m_pos += len;
+ }
+ alignPosition();
+ }
+
+ /**
+ * Unpack a byte array, using the buffer length
+ *
+ * @param buf byte[]
+ */
+ public final void unpackByteArray(byte[] buf)
+ {
+ System.arraycopy(m_buffer, m_pos, buf, 0, buf.length);
+ m_pos += buf.length;
+ alignPosition();
+ }
+
+ /**
+ * Unpack an integer array, using the buffer length
+ *
+ * @param buf int[]
+ */
+ public final void unpackIntArray(int[] buf)
+ {
+ for (int i = 0; i < buf.length; i++)
+ buf[i] = unpackInt();
+ }
+
+ /**
+ * Position the read pointer at the credentials data
+ */
+ public final void positionAtCredentialsData()
+ {
+ m_pos = m_offset + 32;
+ }
+
+ /**
+ * Position the read pointer at the verifier data
+ */
+ public final void positionAtVerifierData()
+ {
+ m_pos = getVerifierOffset();
+ }
+
+ /**
+ * Position the read pointer at the procedure specific parameters
+ */
+ public final void positionAtParameters()
+ {
+ m_pos = getProcedureParameterOffset();
+ }
+
+ /**
+ * Skip a number of bytes in the buffer, rounded to the next int boundary
+ *
+ * @param cnt int
+ */
+ public final void skipBytes(int cnt)
+ {
+ m_pos += (cnt + 3) & 0xFFFC;
+ }
+
+ /**
+ * Set the client details
+ *
+ * @param addr InetAddress
+ * @param port int
+ * @param protocol int
+ */
+ public final void setClientDetails(InetAddress addr, int port, int protocol)
+ {
+ m_clientAddr = addr;
+ m_clientPort = port;
+ m_protocol = protocol;
+ }
+
+ /**
+ * Reset the buffer details
+ *
+ * @param buf byte[]
+ * @param offset int
+ * @param len int
+ */
+ public final void setBuffer(byte[] buf, int offset, int len)
+ {
+ m_buffer = buf;
+ m_offset = offset;
+ m_pos = offset;
+ m_endPos = offset + len;
+ }
+
+ /**
+ * Reset the buffer details
+ *
+ * @param offset int
+ * @param len int
+ */
+ public final void setBuffer(int offset, int len)
+ {
+ m_offset = offset;
+ m_pos = offset;
+ m_endPos = offset + len;
+ }
+
+ /**
+ * Set the used buffer length
+ *
+ * @param len int
+ */
+ public final void setLength(int len)
+ {
+ m_endPos = len + m_offset;
+
+ // Set the fragment header, if the offset is non-zero
+
+ if (m_offset == FragHeaderLen)
+ DataPacker.putInt(getLength() + Rpc.LastFragment, m_buffer, 0);
+ }
+
+ /**
+ * Set the used buffer length
+ */
+ public final void setLength()
+ {
+ m_endPos = m_pos;
+
+ // Set the fragment header, if the offset is non-zero
+
+ if (m_offset == FragHeaderLen)
+ DataPacker.putInt(getLength() + Rpc.LastFragment, m_buffer, 0);
+ }
+
+ /**
+ * Set the buffer position
+ *
+ * @param pos int
+ */
+ public final void setPosition(int pos)
+ {
+ m_pos = pos;
+ }
+
+ /**
+ * Set the message type
+ *
+ * @param msgType int
+ */
+ public final void setMessageType(int msgType)
+ {
+ DataPacker.putInt(msgType, m_buffer, m_offset + 4);
+ }
+
+ /**
+ * Set the RPC version
+ *
+ * @param rpcVer int
+ */
+ public final void setRpcVersion(int rpcVer)
+ {
+ DataPacker.putInt(rpcVer, m_buffer, m_offset + 8);
+ }
+
+ /**
+ * Set the program id
+ *
+ * @param progId int
+ */
+ public final void setProgramId(int progId)
+ {
+ DataPacker.putInt(progId, m_buffer, m_offset + 12);
+ }
+
+ /**
+ * Set the program version
+ *
+ * @param progVer int
+ */
+ public final void setProgramVersion(int progVer)
+ {
+ DataPacker.putInt(progVer, m_buffer, m_offset + 16);
+ }
+
+ /**
+ * Set the procedure id
+ *
+ * @param procId int
+ */
+ public final void setProcedureId(int procId)
+ {
+ DataPacker.putInt(procId, m_buffer, m_offset + 20);
+ }
+
+ /**
+ * Set the credentials type
+ *
+ * @param credtype int
+ */
+ public final void setCredentialsType(int credtype)
+ {
+ DataPacker.putInt(credtype, m_buffer, m_offset + 24);
+ }
+
+ /**
+ * Set the credentials length
+ *
+ * @param credlen int
+ */
+ public final void setCredentialsLength(int credlen)
+ {
+ DataPacker.putInt(credlen, m_buffer, m_offset + 28);
+ }
+
+ /**
+ * Set the reply state
+ *
+ * @param replySts int
+ */
+ public final void setReplyState(int replySts)
+ {
+ DataPacker.putInt(replySts, m_buffer, m_offset + 8);
+ }
+
+ /**
+ * Set the reject status
+ *
+ * @param rejSts int
+ */
+ public final void setRejectStatus(int rejSts)
+ {
+ DataPacker.putInt(rejSts, m_buffer, m_offset + 8);
+ }
+
+ /**
+ * Set the RPC mismatch values
+ *
+ * @param rpcLow int
+ * @param rpcHigh int
+ */
+ public final void setRpcMismatch(int rpcLow, int rpcHigh)
+ {
+ DataPacker.putInt(rpcLow, m_buffer, m_offset + 12);
+ DataPacker.putInt(rpcHigh, m_buffer, m_offset + 16);
+ }
+
+ /**
+ * Set the authentication failure status
+ *
+ * @param authSts int
+ */
+ public final void setAuthFailStatus(int authSts)
+ {
+ DataPacker.putInt(authSts, m_buffer, m_offset + 8);
+ }
+
+ /**
+ * Set the verifier type
+ *
+ * @param verftype int
+ */
+ public final void setVerifierType(int verftype)
+ {
+ DataPacker.putInt(verftype, m_buffer, m_offset + getCredentialsLength() + 32);
+ }
+
+ /**
+ * Set the verifier length
+ *
+ * @param verflen int
+ */
+ public final void setVerifierLength(int verflen)
+ {
+ DataPacker.putInt(verflen, m_buffer, m_offset + getCredentialsLength() + 36);
+ }
+
+ /**
+ * Set the associated packet handler interface for the packet
+ *
+ * @param pktHandler RpcPacketHandler
+ */
+ public final void setPacketHandler(RpcPacketHandler pktHandler)
+ {
+ m_pktHandler = pktHandler;
+ }
+
+ /**
+ * Set the XID
+ *
+ * @param xid int
+ */
+ public final void setXID(int xid)
+ {
+ DataPacker.putInt(xid, m_buffer, m_offset);
+ }
+
+ /**
+ * Set the owner packet pool, if the packet was allocated from a pool
+ *
+ * @param pool RpcPacketPool
+ */
+ protected final void setOwnerPacketPool(RpcPacketPool pool)
+ {
+ m_ownerPool = pool;
+ }
+
+ /**
+ * Build an RPC request header, and set the buffer pointer ready to stream data into the parameter
+ * area of the request
+ *
+ * @param progId int
+ * @param verId int
+ * @param procId int
+ * @param credType int
+ * @param cred byte[]
+ * @param verfType int
+ * @param verf byte[]
+ */
+ public final void buildRequestHeader(int progId, int verId, int procId, int credType, byte[] cred, int verfType,
+ byte[] verf)
+ {
+
+ // Generate an id for the request
+
+ setXID((int) (System.currentTimeMillis() & 0xFFFFFFFFL));
+
+ // Set the message type and RPC version (always version 2)
+
+ setMessageType(Rpc.Call);
+ setRpcVersion(Rpc.RpcVersion);
+
+ // Set the request details
+
+ setProgramId(progId);
+ setProgramVersion(verId);
+ setProcedureId(procId);
+
+ // Set the credentials type, length and value
+
+ setCredentialsType(credType);
+ setCredentialsLength(cred != null ? cred.length : 0);
+ if (cred != null)
+ System.arraycopy(cred, 0, m_buffer, m_offset + 32, cred.length);
+
+ // Set the verifier type, length and value
+
+ setVerifierType(verfType);
+ setVerifierLength(verf != null ? verf.length : 0);
+ if (verf != null)
+ {
+ int pos = getVerifierOffset();
+ System.arraycopy(verf, 0, m_buffer, pos, verf.length);
+ }
+
+ // Position the buffer pointer at the request parameter area
+
+ positionAtParameters();
+ }
+
+ /**
+ * Build a response header for a valid RPC response and set the buffer pointer ready to stream data
+ * into the parameter area of the response.
+ */
+ public final void buildResponseHeader()
+ {
+ setMessageType(Rpc.Reply);
+ setReplyState(Rpc.CallAccepted);
+
+ // Copy the verifier from the request
+
+ DataPacker.putInt(getVerifierType(), m_buffer, m_offset + 12);
+
+ int verfLen = getVerifierLength();
+ DataPacker.putInt(verfLen, m_buffer, m_offset + 16);
+
+ if (verfLen > 0)
+ System.arraycopy(m_buffer, getVerifierOffset(), m_buffer, m_offset + 20, verfLen);
+
+ // Indicate a success status
+
+ DataPacker.putInt(Rpc.StsSuccess, m_buffer, m_offset + 20 + verfLen);
+
+ // Set the buffer pointer for streaming the response parameters
+
+ m_pos = m_offset + 24 + verfLen;
+ setLength();
+ }
+
+ /**
+ * Build an error response packet where the RPC has been accepted but returns a status code in the parameter area.
+ *
+ * @param stsCode int
+ */
+ public final void buildErrorResponse(int stsCode)
+ {
+
+ // Check if the RPC is a request or reply
+
+ boolean isReply = getMessageType() == Rpc.Reply;
+
+ // Set the reply header
+
+ setMessageType(Rpc.Reply);
+ setReplyState(Rpc.CallAccepted);
+
+ // Copy the verifier from the request
+
+ int verfLen = 0;
+
+ if (isReply == false)
+ {
+ DataPacker.putInt(getVerifierType(), m_buffer, m_offset + 12);
+
+ verfLen = getVerifierLength();
+ DataPacker.putInt(verfLen, m_buffer, m_offset + 16);
+
+ if (verfLen > 0)
+ System.arraycopy(m_buffer, getVerifierOffset(), m_buffer, m_offset + 20, verfLen);
+ } else
+ {
+
+ // Get the verifier length from the reply
+
+ verfLen = DataPacker.getInt(m_buffer, m_offset + 16);
+ }
+
+ // Indicate a success status
+
+ DataPacker.putInt(Rpc.StsSuccess, m_buffer, m_offset + 20 + verfLen);
+
+ // Set the buffer pointer for streaming the response parameters
+
+ m_pos = m_offset + 24 + verfLen;
+
+ // Pack the service status code
+
+ DataPacker.putInt(stsCode, m_buffer, m_pos);
+ m_pos += 4;
+ setLength();
+ }
+
+ /**
+ * Build an RPC version mismatch response
+ */
+ public final void buildRpcMismatchResponse()
+ {
+ setMessageType(Rpc.Reply);
+ setReplyState(Rpc.CallDenied);
+ setRejectStatus(Rpc.StsRpcMismatch);
+ setRpcMismatch(Rpc.RpcVersion, Rpc.RpcVersion);
+
+ setLength(ResponseMismatchLen);
+ }
+
+ /**
+ * Build an RPC authentication failure response
+ *
+ * @param stsCode int
+ */
+ public final void buildAuthFailResponse(int stsCode)
+ {
+ setMessageType(Rpc.Reply);
+ setReplyState(Rpc.CallDenied);
+ setRejectStatus(Rpc.StsAuthError);
+ setAuthFailStatus(stsCode);
+
+ setLength(ResponseAuthFailLen);
+ }
+
+ /**
+ * Build an RPC accept error response
+ *
+ * @param stsCode int
+ */
+ public final void buildAcceptErrorResponse(int stsCode)
+ {
+ setMessageType(Rpc.Reply);
+ setReplyState(Rpc.CallAccepted);
+
+ // Copy the verifier from the request
+
+ DataPacker.putInt(getVerifierType(), m_buffer, m_offset + 12);
+
+ int verfLen = getVerifierLength();
+ DataPacker.putInt(verfLen, m_buffer, m_offset + 16);
+
+ if (verfLen > 0)
+ System.arraycopy(m_buffer, getVerifierOffset(), m_buffer, m_offset + 20, verfLen);
+
+ // Pack the status code
+
+ DataPacker.putInt(stsCode, m_buffer, m_offset + 20 + verfLen);
+
+ // Set the response length
+
+ setLength(m_offset + 24 + verfLen);
+ }
+
+ /**
+ * Build a program mismatch error response
+ *
+ * @param verLow int
+ * @param verHigh int
+ */
+ public final void buildProgramMismatchResponse(int verLow, int verHigh)
+ {
+ setMessageType(Rpc.Reply);
+ setReplyState(Rpc.CallAccepted);
+
+ // Copy the verifier from the request
+
+ DataPacker.putInt(getVerifierType(), m_buffer, m_offset + 12);
+
+ int verfLen = getVerifierLength();
+ DataPacker.putInt(verfLen, m_buffer, m_offset + 16);
+
+ if (verfLen > 0)
+ System.arraycopy(m_buffer, getVerifierOffset(), m_buffer, m_offset + 20, verfLen);
+
+ // Pack the status code, and low/high version numbers
+
+ int pos = m_offset + 20 + verfLen;
+ DataPacker.putInt(Rpc.StsProgMismatch, m_buffer, pos);
+ DataPacker.putInt(verLow, m_buffer, pos + 4);
+ DataPacker.putInt(verHigh, m_buffer, pos + 8);
+
+ // Set the response length
+
+ setLength(pos + 12);
+ }
+
+ /**
+ * Return the RPC packet as a string
+ *
+ * @return String
+ */
+ public String toString()
+ {
+ StringBuffer str = new StringBuffer(128);
+
+ // Dump the client details
+
+ str.append("[");
+ if (hasClientAddress())
+ {
+ str.append(getClientProtocol() == Rpc.TCP ? "T" : "U");
+ str.append(getClientAddress().getHostAddress());
+ str.append(":");
+ str.append(getClientPort());
+ } else
+ str.append("");
+
+ // Dump the call/response header
+
+ if (getMessageType() == Rpc.Call)
+ {
+
+ // Request packet
+
+ str.append("-Call,XID=0x");
+ str.append(Integer.toHexString(getXID()));
+
+ str.append(",RpcVer=");
+ str.append(getRpcVersion());
+
+ str.append(",ProgId=");
+ str.append(getProgramId());
+ str.append(",ProgVer=");
+ str.append(getProgramVersion());
+
+ str.append(",Proc=");
+ str.append(getProcedureId());
+
+ str.append(",CredType=");
+ str.append(getCredentialsType());
+ str.append(",CredLen=");
+ str.append(getCredentialsLength());
+
+ str.append(",VerfType");
+ str.append(getVerifierType());
+ str.append(",VerfLen=");
+ str.append(getVerifierLength());
+
+ str.append(",ParamLen=");
+ str.append(getProcedureParameterLength());
+ } else
+ {
+
+ // Response packet
+
+ str.append("-Reply,XID=0x");
+ str.append(Integer.toHexString(getXID()));
+
+ if (getReplyState() == Rpc.CallAccepted)
+ {
+
+ // Request accepted response
+
+ str.append(",Accepted");
+ } else
+ {
+
+ // Request denied response
+
+ str.append(",Denied");
+
+ if (getRejectStatus() == Rpc.StsRpcMismatch)
+ {
+ str.append(",RpcMismatch, Low=");
+ str.append(getMismatchVersionLow());
+ str.append("/High=");
+ str.append(getMismatchVersionHigh());
+ } else
+ {
+ str.append(",AuthError, Status=");
+ ;
+ str.append(getAuthFailStatus());
+ }
+ }
+ }
+
+ // Check if the packet is allocated from a pool
+
+ if (isAllocatedFromPool())
+ str.append(",Pool");
+ str.append("]");
+
+ // Return the string
+
+ return str.toString();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcPacketHandler.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcPacketHandler.java
new file mode 100644
index 0000000000..440dc7051d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcPacketHandler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+
+/**
+ * RPC Packet Handler Interface
+ *
+ *
Interface used by an RpcPacket to send a response RPC via either TCP or UDP.
+ *
+ * @author GKSpencer
+ */
+public interface RpcPacketHandler {
+
+ /**
+ * Send an RPC response
+ *
+ * @param rpc RpcPacket
+ * @exception IOException
+ */
+ public void sendRpcResponse(RpcPacket rpc)
+ throws IOException;
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcPacketPool.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcPacketPool.java
new file mode 100644
index 0000000000..97a3a20d31
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcPacketPool.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.util.*;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Rpc Packet Pool Class
+ *
+ *
Contains a pool of small and large RpcPacket objects for use by multi-threaded RPC servers.
+ *
+ * @author GKSpencer
+ */
+public class RpcPacketPool {
+
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog(RpcPacketPool.class);
+
+ // Constants
+ //
+ // Default small/large packet sizes
+
+ public static final int DefaultSmallSize = 512;
+ public static final int DefaultLargeSize = 32768;
+ public static final int DefaultSmallLimit = -1; // no allocation limit
+ public static final int DefaultLargeLimit = -1; // " " "
+
+ // Small/large packet lists
+
+ private Vector m_smallPackets;
+ private Vector m_largePackets;
+
+ // Small packet size and maximum allowed packets
+
+ private int m_smallPktSize;
+ private int m_smallPktLimit;
+
+ // Large packet size and maximum allowed packets
+
+ private int m_largePktSize;
+ private int m_largePktLimit;
+
+ // Count of allocated small/large packets
+
+ private int m_smallPktCount;
+ private int m_largePktCount;
+
+ /**
+ * Default constructor
+ */
+ public RpcPacketPool()
+ {
+
+ // Create the small/large packet lists
+
+ m_smallPackets = new Vector();
+ m_largePackets = new Vector();
+
+ // Set the packet sizes/limits
+
+ m_smallPktSize = DefaultSmallSize;
+ m_smallPktLimit = DefaultSmallLimit;
+
+ m_largePktSize = DefaultLargeSize;
+ m_largePktLimit = DefaultLargeLimit;
+
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param smallSize int
+ * @param smallLimit int
+ * @param largeSize int
+ * @param largeLimit int
+ */
+ public RpcPacketPool(int smallSize, int smallLimit, int largeSize, int largeLimit)
+ {
+
+ // Create the small/large packet lists
+
+ m_smallPackets = new Vector();
+ m_largePackets = new Vector();
+
+ // Save the packet sizes/limits
+
+ m_smallPktSize = smallSize;
+ m_smallPktLimit = smallLimit;
+
+ m_largePktSize = largeSize;
+ m_largePktLimit = largeLimit;
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param largeSize int
+ * @param largeLimit int
+ */
+ public RpcPacketPool(int largeSize, int largeLimit)
+ {
+
+ // Create the small/large packet lists
+
+ m_smallPackets = new Vector();
+ m_largePackets = new Vector();
+
+ // Save the packet sizes/limits
+
+ m_smallPktSize = DefaultSmallSize;
+ m_smallPktLimit = largeLimit;
+
+ m_largePktSize = largeSize;
+ m_largePktLimit = largeLimit;
+ }
+
+ /**
+ * Return the small packet size
+ *
+ * @return int
+ */
+ public final int getSmallPacketSize()
+ {
+ return m_smallPktSize;
+ }
+
+ /**
+ * Return the count of allocated small packets
+ *
+ * @return int
+ */
+ public final int getSmallPacketCount()
+ {
+ return m_smallPktCount;
+ }
+
+ /**
+ * Return the small packet allocation limit
+ *
+ * @return int
+ */
+ public final int getSmallPacketAllocationLimit()
+ {
+ return m_smallPktLimit;
+ }
+
+ /**
+ * Return the count of available large packets
+ *
+ * @return int
+ */
+ public final int availableLargePackets()
+ {
+ return m_largePackets.size();
+ }
+
+ /**
+ * Return the large packet size
+ *
+ * @return int
+ */
+ public final int getLargePacketSize()
+ {
+ return m_largePktSize;
+ }
+
+ /**
+ * Return the count of allocated large packets
+ *
+ * @return int
+ */
+ public final int getLargePacketCount()
+ {
+ return m_largePktCount;
+ }
+
+ /**
+ * Return the large packet allocation limit
+ *
+ * @return int
+ */
+ public final int getLargePacketAllocationLimit()
+ {
+ return m_largePktLimit;
+ }
+
+ /**
+ * Return the count of available small packets
+ *
+ * @return int
+ */
+ public final int availableSmallPackets()
+ {
+ return m_smallPackets.size();
+ }
+
+ /**
+ * Allocate a packet from the packet pool
+ *
+ * @param reqSize int
+ * @return RpcPacket
+ */
+ public final RpcPacket allocatePacket(int reqSize)
+ {
+
+ // Check if the packet should come from the small or large packet list
+
+ RpcPacket pkt = null;
+
+ if (reqSize <= m_smallPktSize)
+ {
+
+ // Allocate a packet from the small packet list
+
+ pkt = allocateSmallPacket();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcPacketPool Allocated (small) " + pkt.getBuffer() + ", len=" + pkt.getBuffer().length
+ + ", list=" + m_smallPackets.size() + "/" + m_smallPktLimit);
+ } else
+ {
+
+ // Allocate a packet from the large packet list
+
+ pkt = allocateLargePacket();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcPacketPool Allocated (large) " + pkt.getBuffer() + ", len=" + pkt.getBuffer().length
+ + ", list=" + m_largePackets.size() + "/" + m_largePktLimit);
+ }
+
+ // Return the allocated packet
+
+ return pkt;
+ }
+
+ /**
+ * Release an RPC packet back to the pool
+ *
+ * @param pkt RpcPacket
+ */
+ public final void releasePacket(RpcPacket pkt)
+ {
+
+ // Check if the packet should be released to the small or large list
+
+ if (pkt.getBuffer().length >= m_largePktSize)
+ {
+
+ // Release the packet to the large packet list
+
+ synchronized (m_largePackets)
+ {
+
+ // Add the packet back to the free list
+
+ m_largePackets.addElement(pkt);
+
+ // Signal any waiting threads that there are packets available
+
+ m_largePackets.notify();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcPacketPool Released (large) " + pkt.getBuffer() + ", len="
+ + pkt.getBuffer().length + ", list=" + m_largePackets.size());
+ }
+ } else
+ {
+
+ // Release the packet to the small packet list
+
+ synchronized (m_smallPackets)
+ {
+
+ // Add the packet back to the free list
+
+ m_smallPackets.addElement(pkt);
+
+ // Signal any waiting threads that there are packets available
+
+ m_smallPackets.notify();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("RpcPacketPool Released (small) " + pkt.getBuffer() + ", len="
+ + pkt.getBuffer().length);
+ }
+ }
+ }
+
+ /**
+ * Allocate, or create, a small RPC packet
+ *
+ * @return RpcPacket
+ */
+ private final RpcPacket allocateSmallPacket()
+ {
+
+ RpcPacket pkt = null;
+
+ synchronized (m_smallPackets)
+ {
+
+ // Check if there is a packet available from the small packet list
+
+ if (m_smallPackets.size() > 0)
+ {
+
+ // Remove a packet from the head of the free list
+
+ pkt = (RpcPacket) m_smallPackets.elementAt(0);
+ m_smallPackets.removeElementAt(0);
+ } else if (m_smallPktLimit == -1 || m_smallPktCount < m_smallPktLimit)
+ {
+
+ // Allocate a new packet
+
+ pkt = new RpcPacket(m_smallPktSize, this);
+ m_smallPktCount++;
+ } else
+ {
+
+ // Wait for a packet to be released to the small packet list
+
+ try
+ {
+
+ // Wait for a packet
+
+ m_smallPackets.wait();
+
+ // Try to get the packet from the small packet list again
+
+ if (m_smallPackets.size() > 0)
+ {
+
+ // Remove a packet from the head of the free list
+
+ pkt = (RpcPacket) m_smallPackets.elementAt(0);
+ m_smallPackets.removeElementAt(0);
+ }
+ } catch (InterruptedException ex)
+ {
+ }
+ }
+ }
+
+ // Return the allocated packet
+
+ return pkt;
+ }
+
+ /**
+ * Allocate, or create, a large RPC packet
+ *
+ * @return RpcPacket
+ */
+ private final RpcPacket allocateLargePacket()
+ {
+
+ RpcPacket pkt = null;
+
+ synchronized (m_largePackets)
+ {
+
+ // Check if there is a packet available from the large packet list
+
+ if (m_largePackets.size() > 0)
+ {
+
+ // Remove a packet from the head of the free list
+
+ pkt = (RpcPacket) m_largePackets.elementAt(0);
+ m_largePackets.removeElementAt(0);
+ } else if (m_largePktLimit == -1 || m_largePktCount < m_largePktLimit)
+ {
+
+ // Allocate a new packet
+
+ pkt = new RpcPacket(m_largePktSize, this);
+ m_largePktCount++;
+ } else
+ {
+
+ // Wait for a packet to be released to the large packet list
+
+ try
+ {
+
+ // Wait for a packet
+
+ m_largePackets.wait();
+
+ // Try to get the packet from the large packet list again
+
+ if (m_largePackets.size() > 0)
+ {
+
+ // Remove a packet from the head of the free list
+
+ pkt = (RpcPacket) m_largePackets.elementAt(0);
+ m_largePackets.removeElementAt(0);
+ }
+ } catch (InterruptedException ex)
+ {
+ }
+ }
+ }
+
+ // Return the allocated packet
+
+ return pkt;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcProcessor.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcProcessor.java
new file mode 100644
index 0000000000..6848445ab6
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcProcessor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+
+/**
+ * RPC Processor Interface
+ *
+ * @author GKSpencer
+ */
+public interface RpcProcessor {
+
+ /**
+ * Process an RPC request
+ *
+ * @param rpc RpcPacket
+ * @return RpcPacket
+ * @throws IOException
+ */
+ public RpcPacket processRpc(RpcPacket rpc)
+ throws IOException;
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcRequestQueue.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcRequestQueue.java
new file mode 100644
index 0000000000..89119b63ff
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcRequestQueue.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.util.*;
+
+/**
+ * RPC Request Queue Class
+ *
+ *
Provides a request queue for a thread pool of worker threads.
+ *
+ * @author GKSpencer
+ */
+public class RpcRequestQueue {
+
+ // List of RPC requests
+
+ private LinkedList m_queue;
+
+ /**
+ * Class constructor
+ */
+ public RpcRequestQueue()
+ {
+ m_queue = new LinkedList();
+ }
+
+ /**
+ * Return the number of requests in the queue
+ *
+ * @return int
+ */
+ public final synchronized int numberOfRequests()
+ {
+ return m_queue.size();
+ }
+
+ /**
+ * Add a request to the queue
+ *
+ * @param req RpcPacket
+ */
+ public final synchronized void addRequest(RpcPacket req)
+ {
+
+ // Add the request to the queue
+
+ m_queue.add(req);
+
+ // Notify workers that there is a request to process
+
+ notifyAll();
+ }
+
+ /**
+ * Remove a request from the head of the queue
+ *
+ * @return RpcPacket
+ * @exception InterruptedException
+ */
+ public final synchronized RpcPacket removeRequest()
+ throws InterruptedException
+ {
+
+ // Wait until there is a request
+
+ waitWhileEmpty();
+
+ // Get the request from the head of the queue
+
+ return (RpcPacket) m_queue.removeFirst();
+ }
+
+ /**
+ * Wait for a request to be added to the queue
+ *
+ * @exception InterruptedException
+ */
+ public final synchronized void waitWhileEmpty()
+ throws InterruptedException
+ {
+
+ // Wait until some work arrives on the queue
+
+ while (m_queue.size() == 0)
+ wait();
+ }
+
+ /**
+ * Wait for the request queue to be emptied
+ *
+ * @exception InterruptedException
+ */
+ public final synchronized void waitUntilEmpty()
+ throws InterruptedException
+ {
+
+ // Wait until the request queue is empty
+
+ while (m_queue.size() != 0)
+ wait();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/RpcRequestThreadPool.java b/source/java/org/alfresco/filesys/server/oncrpc/RpcRequestThreadPool.java
new file mode 100644
index 0000000000..71e1dcf15f
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/RpcRequestThreadPool.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * ONC/RPC Request Thread Pool Class
+ *
+ *
Processes RPC requests using a pool of worker threads.
+ *
+ * @author GKSpencer
+ */
+public class RpcRequestThreadPool {
+
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog(RpcRequestThreadPool.class);
+
+ // Default/minimum/maximum number of worker threads to use
+
+ public static final int DefaultWorkerThreads = 8;
+ public static final int MinimumWorkerThreads = 4;
+ public static final int MaximumWorkerThreads = 50;
+
+ // Queue of RPC requests
+
+ private RpcRequestQueue m_queue;
+
+ // Worker threads
+
+ private ThreadWorker[] m_workers;
+
+ // RPC dispatcher
+
+ private RpcProcessor m_rpcProcessor;
+
+ /**
+ * Thread Worker Inner Class
+ */
+ protected class ThreadWorker implements Runnable
+ {
+
+ // Worker thread
+
+ private Thread mi_thread;
+
+ // Worker unique id
+
+ private int mi_id;
+
+ // Shutdown flag
+
+ private boolean mi_shutdown = false;
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ * @param id int
+ */
+ public ThreadWorker(String name, int id)
+ {
+
+ // Save the thread id
+
+ mi_id = id;
+
+ // Create the worker thread
+
+ mi_thread = new Thread(this);
+ mi_thread.setName(name);
+ mi_thread.setDaemon(true);
+ mi_thread.start();
+ }
+
+ /**
+ * Request the worker thread to shutdown
+ */
+ public final void shutdownRequest()
+ {
+ mi_shutdown = true;
+ try
+ {
+ mi_thread.interrupt();
+ } catch (Exception ex)
+ {
+ }
+ }
+
+ /**
+ * Run the thread
+ */
+ public void run()
+ {
+
+ // Loop until shutdown
+
+ RpcPacket rpc = null;
+ RpcPacket response = null;
+
+ while (mi_shutdown == false)
+ {
+
+ try
+ {
+
+ // Wait for an RPC request to be queued
+
+ rpc = m_queue.removeRequest();
+ } catch (InterruptedException ex)
+ {
+
+ // Check for shutdown
+
+ if (mi_shutdown == true)
+ break;
+ }
+
+ // If the request is valid process it
+
+ if (rpc != null)
+ {
+
+ try
+ {
+
+ // Process the request
+
+ response = m_rpcProcessor.processRpc(rpc);
+ if (response != null)
+ response.getPacketHandler().sendRpcResponse(response);
+ } catch (Throwable ex)
+ {
+
+ // Do not display errors if shutting down
+
+ if (mi_shutdown == false)
+ {
+ if ( logger.isDebugEnabled()) {
+ logger.debug("Worker " + Thread.currentThread().getName() + ":");
+ logger.debug(ex);
+ }
+ }
+ } finally
+ {
+
+ // Release the RPC packet(s) back to the packet pool
+
+ if (rpc.getClientProtocol() == Rpc.TCP && rpc.isAllocatedFromPool())
+ rpc.getOwnerPacketPool().releasePacket(rpc);
+
+ if (response != null && response.getClientProtocol() == Rpc.TCP
+ && response.getBuffer() != rpc.getBuffer() && response.isAllocatedFromPool())
+ response.getOwnerPacketPool().releasePacket(response);
+
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Class constructor
+ *
+ * @param threadName String
+ * @param rpcServer RpcProcessor
+ * @param pktHandler PacketHandlerInterface
+ */
+ public RpcRequestThreadPool(String threadName, RpcProcessor rpcServer)
+ {
+ this(threadName, DefaultWorkerThreads, rpcServer);
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param threadName String
+ * @param poolSize int
+ * @param rpcServer RpcProcessor
+ */
+ public RpcRequestThreadPool(String threadName, int poolSize, RpcProcessor rpcServer)
+ {
+
+ // Save the RPC handler
+
+ m_rpcProcessor = rpcServer;
+
+ // Create the request queue
+
+ m_queue = new RpcRequestQueue();
+
+ // Check that we have at least minimum worker threads
+
+ if (poolSize < MinimumWorkerThreads)
+ poolSize = MinimumWorkerThreads;
+
+ // Create the worker threads
+
+ m_workers = new ThreadWorker[poolSize];
+
+ for (int i = 0; i < m_workers.length; i++)
+ m_workers[i] = new ThreadWorker(threadName + (i + 1), i);
+ }
+
+ /**
+ * Return the number of requests in the queue
+ *
+ * @return int
+ */
+ public final int getNumberOfRequests()
+ {
+ return m_queue.numberOfRequests();
+ }
+
+ /**
+ * Queue an RPC request to the thread pool for processing
+ *
+ * @param rpc RpcPacket
+ */
+ public final void queueRpcRequest(RpcPacket pkt)
+ {
+ m_queue.addRequest(pkt);
+ }
+
+ /**
+ * Shutdown the thread pool and release all resources
+ */
+ public void shutdownThreadPool()
+ {
+
+ // Shutdown the worker threads
+
+ if (m_workers != null)
+ {
+ for (int i = 0; i < m_workers.length; i++)
+ m_workers[i].shutdownRequest();
+ }
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcClient.java b/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcClient.java
new file mode 100644
index 0000000000..97ff3322c5
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcClient.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * TCP RPC Client Connection Class
+ *
+ * @author GKSpencer
+ */
+public class TcpRpcClient extends RpcClient {
+
+ // TCP RPC client connection
+
+ private TcpRpcPacketHandler m_client;
+
+ /**
+ * Class constructor
+ *
+ * @param addr InetAddress
+ * @param port int
+ * @param maxRpcSize int
+ * @throws IOException
+ */
+ public TcpRpcClient(InetAddress addr, int port, int maxRpcSize) throws IOException
+ {
+ super(addr, port, Rpc.TCP, maxRpcSize);
+
+ // Connect a socket to the remote server
+
+ Socket sock = new Socket(getServerAddress(), getServerPort());
+
+ // Create the TCP RPC packet handler for the client connection
+
+ m_client = new TcpRpcPacketHandler(sock, maxRpcSize);
+ }
+
+ /**
+ * Send an RPC request using the socket connection, and receive a response
+ *
+ * @param rpc RpcPacket
+ * @param rxRpc RpcPacket
+ * @return RpcPacket
+ * @throws IOException
+ */
+ public RpcPacket sendRPC(RpcPacket rpc, RpcPacket rxRpc)
+ throws IOException
+ {
+
+ // Use the TCP packet handler to send the RPC
+
+ m_client.sendRpc(rpc);
+
+ // Receive a response RPC
+
+ RpcPacket rxPkt = rxRpc;
+ if (rxPkt == null)
+ rxPkt = new RpcPacket(getMaximumRpcSize());
+
+ m_client.receiveRpc(rxPkt);
+
+ // Return the RPC response
+
+ return rxPkt;
+ }
+
+ /**
+ * Close the connection to the remote RPC server
+ */
+ public void closeConnection()
+ {
+
+ // Close the packet handler
+
+ if (m_client != null)
+ {
+ m_client.closePacketHandler();
+ m_client = null;
+ }
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcPacketHandler.java b/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcPacketHandler.java
new file mode 100644
index 0000000000..680f7ac68d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcPacketHandler.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+
+import org.alfresco.filesys.server.SocketPacketHandler;
+import org.alfresco.filesys.util.DataPacker;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * TCP RPC Packet Handler Class
+ *
+ *
Processes RPC requests received via TCP session.
+ *
+ * @author GKSpencer
+ */
+public class TcpRpcPacketHandler extends SocketPacketHandler implements Runnable {
+
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog(TcpRpcPacketHandler.class);
+
+ // Session handler that owns this session
+
+ private TcpRpcSessionHandler m_handler;
+
+ // RPC server implementation used to process the requests
+
+ private RpcProcessor m_rpcProcessor;
+
+ // Session id
+
+ private int m_sessId;
+
+ // RPC processing thread shutdown flag
+
+ private boolean m_shutdown;
+
+ // Maximum RPC size accepted
+
+ private int m_maxRpcSize;
+
+ // Packet buffer for receiving incoming RPC requests
+
+ private RpcPacket m_rxPkt;
+
+ // Fragment header buffer
+
+ private byte[] m_fragBuf;
+
+ /**
+ * Class constructor to create a TCP RPC handler for a server.
+ *
+ * @param handler TcpRpcSessionHandler
+ * @param sessId int
+ * @param server RpcProcessor
+ * @param socket Socket
+ * @param maxRpcSize int
+ * @throws IOException
+ */
+ public TcpRpcPacketHandler(TcpRpcSessionHandler handler, int sessId, RpcProcessor server, Socket socket,
+ int maxRpcSize) throws IOException
+ {
+ super(socket);
+
+ // Set the session handler that owns this session
+
+ m_handler = handler;
+
+ // set the session id
+
+ m_sessId = sessId;
+
+ // Set the RPC server to be used to process requests
+
+ m_rpcProcessor = server;
+
+ // Set the maximum RPC size accepted
+
+ m_maxRpcSize = maxRpcSize;
+
+ // Allocate the RPC fragment header buffer
+
+ m_fragBuf = new byte[4];
+
+ // Create a thread to run the RPC processing for this session
+
+ Thread th = new Thread(this);
+ th.setName(handler.getProtocolName() + "_" + getSessionId());
+ th.start();
+ }
+
+ /**
+ * Class constructor to create a TCP RPC handler for a client.
+ *
+ * @param socket Socket
+ * @param maxRpcSize int
+ * @throws IOException
+ */
+ public TcpRpcPacketHandler(Socket socket, int maxRpcSize) throws IOException
+ {
+ super(socket);
+
+ // Allocate the RPC fragment header buffer
+
+ m_maxRpcSize = maxRpcSize;
+ m_fragBuf = new byte[4];
+ }
+
+ /**
+ * Return the protocol name
+ *
+ * @return String
+ */
+ public String getProtocolName()
+ {
+ return "TCP RPC";
+ }
+
+ /**
+ * Return the session id
+ *
+ * @return int
+ */
+ public final int getSessionId()
+ {
+ return m_sessId;
+ }
+
+ /**
+ * Return the maximum RPC size accepted
+ *
+ * @return int
+ */
+ public final int getMaximumRpcSize()
+ {
+ return m_maxRpcSize;
+ }
+
+ /**
+ * Return the associated session handler
+ *
+ * @return TcpRpcSessionHandler
+ */
+ protected final TcpRpcSessionHandler getHandler()
+ {
+ return m_handler;
+ }
+
+ /**
+ * Thread to read and process the RPC requests for this session
+ */
+ public void run()
+ {
+
+ // Loop until shutdown
+
+ int rxLen = 0;
+ RpcPacket rpcPkt = null;
+
+ while (m_shutdown == false)
+ {
+
+ try
+ {
+
+ // allocate an RPC packet to receive an incoming request
+
+ rpcPkt = allocateRpcPacket(getMaximumRpcSize());
+
+ // Read an RPC request
+
+ rxLen = receiveRpc(rpcPkt);
+
+ if (rxLen == -1)
+ {
+
+ // Release the packet
+
+ deallocateRpcPacket(rpcPkt);
+
+ // Receive error, client has closed the socket
+
+ m_handler.closeSession(getSessionId());
+ break;
+ }
+ } catch (SocketException ex)
+ {
+
+ // Release the packet
+
+ if (rpcPkt != null)
+ deallocateRpcPacket(rpcPkt);
+
+ // Socket error, close the session
+
+ m_handler.closeSession(getSessionId());
+ break;
+ } catch (IOException ex)
+ {
+
+ // Only dump errors if not shutting down
+
+ if (m_shutdown == false)
+ logger.debug(ex);
+ }
+
+ // Process the RPC request
+
+ try
+ {
+
+ // Validate the RPC header
+
+ if (rpcPkt.getRpcVersion() != Rpc.RpcVersion)
+ {
+
+ // Build/send an error response
+
+ rpcPkt.buildRpcMismatchResponse();
+ sendRpc(rpcPkt);
+ } else
+ {
+
+ // Process the RPC request
+
+ processRpc(rpcPkt);
+ }
+ } catch (IOException ex)
+ {
+
+ // Only dump errors if not shutting down
+
+ if (m_shutdown == false)
+ logger.debug(ex);
+ }
+ }
+ }
+
+ /**
+ * Close the session
+ */
+ public void closePacketHandler()
+ {
+
+ // Request the RPC processing thread to shutdown
+
+ m_shutdown = true;
+
+ // Close the input/output streams and socket
+
+ super.closePacketHandler();
+ }
+
+ /**
+ * Send an RPC request/response packet
+ *
+ * @param rpc RpcPacket
+ * @exception IOException
+ */
+ protected final void sendRpc(RpcPacket rpc)
+ throws IOException
+ {
+
+ // Write the RPC response, this includes the fragment header
+ //
+ // If the fragment header is written seperately to the main RPC response packet trace tools
+ // such as Ethereal will not display the details properly.
+
+ writePacket(rpc.getBuffer(), 0, rpc.getTxLength());
+ }
+
+ /**
+ * Read an RPC request/response
+ *
+ * @param rpc RpcPacket
+ * @return int
+ * @throws IOException
+ */
+ protected final int receiveRpc(RpcPacket rpc)
+ throws IOException
+ {
+
+ // Use the main receive method
+
+ int rxLen = receiveRpc(rpc.getBuffer(), RpcPacket.FragHeaderLen, rpc.getBuffer().length
+ - RpcPacket.FragHeaderLen);
+ if (rxLen > 0)
+ {
+
+ // Set the received length
+
+ rpc.setBuffer(RpcPacket.FragHeaderLen, rxLen + RpcPacket.FragHeaderLen);
+
+ // Set the client details
+
+ rpc.setClientDetails(getSocket().getInetAddress(), getSocket().getPort(), Rpc.TCP);
+ }
+
+ // Return the received data length
+
+ return rxLen;
+ }
+
+ /**
+ * Read an RPC request/response
+ *
+ * @param buffer byte[]
+ * @param offset int
+ * @param maxLen int
+ * @return int
+ * @throws IOException
+ */
+ protected final int receiveRpc(byte[] buffer, int offset, int maxLen)
+ throws IOException
+ {
+
+ // Fill the buffer until the last fragment is received
+
+ int rxLen = 0;
+ int totLen = 0;
+ int rxOffset = offset;
+ int fragLen = 0;
+ boolean lastFrag = false;
+
+ while (lastFrag == false)
+ {
+
+ // Read in a header to get the fragment length
+
+ rxLen = readPacket(m_fragBuf, 0, 4);
+ if (rxLen == -1)
+ return rxLen;
+
+ // Check if we received the last fragment
+
+ fragLen = DataPacker.getInt(m_fragBuf, 0);
+
+ if ((fragLen & Rpc.LastFragment) != 0)
+ {
+ lastFrag = true;
+ fragLen = fragLen & Rpc.LengthMask;
+ }
+
+ // Check if the buffer is large enough to receive the request
+
+ if (fragLen > (buffer.length - rxOffset))
+ throw new IOException("Receive RPC buffer overflow, fragment len = " + fragLen);
+
+ // Read the data part of the packet into the users buffer, this may take
+ // several reads
+
+ while (fragLen > 0)
+ {
+
+ // Read the data
+
+ rxLen = readPacket(buffer, offset, fragLen);
+
+ // Check if the connection has been closed
+
+ if (rxLen == -1)
+ return -1;
+
+ // Update the received length and remaining data length
+
+ totLen += rxLen;
+ fragLen -= rxLen;
+
+ // Update the user buffer offset as more reads will be required
+ // to complete the data read
+
+ offset += rxLen;
+
+ } // end while reading data
+
+ } // end while fragments
+
+ // Return the total length read
+
+ return totLen;
+ }
+
+ /**
+ * Allocate an RPC packet for receiving an incoming request. This method must be overridden for
+ * multi-threaded implementations.
+ *
+ * @param maxSize int
+ * @return RpcPacket
+ */
+ protected RpcPacket allocateRpcPacket(int maxSize)
+ {
+
+ // Check if the receive packet has been allocated
+
+ if (m_rxPkt == null)
+ m_rxPkt = new RpcPacket(maxSize);
+
+ // Return the RPC receive packet
+
+ return m_rxPkt;
+ }
+
+ /**
+ * Deallocate an RPC packet, default method does nothing but a pooled implementation may
+ * return the packet to the pool.
+ *
+ * @param pkt RpcPacket
+ */
+ protected void deallocateRpcPacket(RpcPacket pkt)
+ {
+ }
+
+ /**
+ * Process an RPC request. This method must be overridden for multi-threaded implementations.
+ *
+ * @param rpc RpcPacket
+ * @exception IOException
+ */
+ protected void processRpc(RpcPacket rpc)
+ throws IOException
+ {
+
+ // Process the RPC request in the current thread
+
+ RpcPacket response = m_rpcProcessor.processRpc(rpc);
+
+ // Send the RPC response
+
+ if (response != null)
+ sendRpc(response);
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcSessionHandler.java b/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcSessionHandler.java
new file mode 100644
index 0000000000..b70d3b6d23
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/TcpRpcSessionHandler.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import org.alfresco.filesys.server.NetworkServer;
+import org.alfresco.filesys.server.PacketHandlerInterface;
+import org.alfresco.filesys.server.SocketSessionHandler;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * TCP RPC Session Handler Class
+ *
+ *
Receives session requests via a TCP socketRPC requests via a datagram and passes the request to the registered RPC server.
+ *
+ * @author GKSpencer
+ */
+public class TcpRpcSessionHandler extends SocketSessionHandler {
+
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog(TcpRpcSessionHandler.class);
+
+ // RPC server implementation that handles the RPC processing
+
+ private RpcProcessor m_rpcProcessor;
+
+ // Maximum request size allowed
+
+ private int m_maxRpcSize;
+
+ // List of active sessions
+
+ private Hashtable m_sessions;
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ * @param protocol String
+ * @param rpcServer RpcProcessor
+ * @param server NetworkServer
+ * @param addr InetAddress
+ * @param port int
+ * @param maxSize int
+ */
+ public TcpRpcSessionHandler(String name, String protocol, RpcProcessor rpcServer, NetworkServer server,
+ InetAddress addr, int port, int maxSize)
+ {
+ super(name, protocol, server, addr, port);
+
+ // Set the RPC server implementation that will handle the actual requests
+
+ m_rpcProcessor = rpcServer;
+
+ // Set the maximum RPC request size allowed
+
+ m_maxRpcSize = maxSize;
+
+ // Create the active session list
+
+ m_sessions = new Hashtable();
+ }
+
+ /**
+ * Return the maximum RPC size allowed
+ *
+ * @return int
+ */
+ protected int getMaximumRpcSize()
+ {
+ return m_maxRpcSize;
+ }
+
+ /**
+ * Return the RPC server used to process the requests
+ *
+ * @return RpcProcessor
+ */
+ protected final RpcProcessor getRpcProcessor()
+ {
+ return m_rpcProcessor;
+ }
+
+ /**
+ * Accept an incoming session request
+ *
+ * @param sock Socket
+ */
+ protected void acceptConnection(Socket sock)
+ {
+
+ try
+ {
+
+ // Set the socket for no delay
+
+ sock.setTcpNoDelay(true);
+
+ // Create a packet handler for the new session and add to the active session list
+
+ int sessId = getNextSessionId();
+ TcpRpcPacketHandler pktHandler = createPacketHandler(sessId, sock);
+
+ // Add the packet handler to the active session table
+
+ m_sessions.put(new Integer(sessId), pktHandler);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("[" + getProtocolName() + "] Created new session id = " + sessId + ", from = "
+ + sock.getInetAddress().getHostAddress() + ":" + sock.getPort());
+ } catch (IOException ex)
+ {
+ }
+ }
+
+ /**
+ * Remove a session from the active session list
+ *
+ * @param sessId int
+ */
+ protected final void closeSession(int sessId)
+ {
+
+ // Remove the specified session from the active session table
+
+ PacketHandlerInterface pktHandler = (PacketHandlerInterface) m_sessions.remove(new Integer(sessId));
+ if (pktHandler != null)
+ {
+
+ // Close the session
+
+ pktHandler.closePacketHandler();
+ }
+ }
+
+ /**
+ * Close the session handler, close all active sessions.
+ *
+ * @param server NetworkServer
+ */
+ public void closeSessionHandler(NetworkServer server)
+ {
+ super.closeSessionHandler(server);
+
+ // Close all active sessions
+
+ if (m_sessions.size() > 0)
+ {
+
+ // Enumerate the sessions
+
+ Enumeration enm = m_sessions.elements();
+
+ while (enm.hasMoreElements())
+ {
+
+ // Get the current packet handler
+
+ PacketHandlerInterface handler = (PacketHandlerInterface) enm.nextElement();
+ handler.closePacketHandler();
+ }
+
+ // Clear the session list
+
+ m_sessions.clear();
+ }
+ }
+
+ /**
+ * Create a packet handler for a new session
+ *
+ * @param sessId int
+ * @param sock Socket
+ * @return TcpRpcPacketHandler
+ * @exception IOException
+ */
+ protected TcpRpcPacketHandler createPacketHandler(int sessId, Socket sock)
+ throws IOException
+ {
+
+ // Create a single threaded TCP RPC packet handler
+
+ return new TcpRpcPacketHandler(this, sessId, m_rpcProcessor, sock, getMaximumRpcSize());
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/UdpRpcDatagramHandler.java b/source/java/org/alfresco/filesys/server/oncrpc/UdpRpcDatagramHandler.java
new file mode 100644
index 0000000000..376f82735e
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/UdpRpcDatagramHandler.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc;
+
+import java.io.*;
+import java.net.*;
+
+import org.alfresco.filesys.server.DatagramSessionHandler;
+import org.alfresco.filesys.server.NetworkServer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * UDP RPC Datagram Handler Class
+ *
+ *
Receives RPC requests via a datagram and passes the request to the registered RPC server.
+ *
+ * @author GKSpencer
+ */
+public class UdpRpcDatagramHandler extends DatagramSessionHandler
+{
+ // Debug logging
+
+ protected static final Log logger = LogFactory.getLog(UdpRpcDatagramHandler.class);
+
+ // RPC server implementation that handles the RPC processing
+
+ private RpcProcessor m_rpcProcessor;
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ * @param protocol String
+ * @param rpcServer RpcProcessor
+ * @param server NetworkServer
+ * @param addr InetAddress
+ * @param port int
+ * @param maxSize int
+ */
+ public UdpRpcDatagramHandler(String name, String protocol, RpcProcessor rpcServer, NetworkServer server,
+ InetAddress addr, int port, int maxSize)
+ {
+ super(name, protocol, server, addr, port);
+
+ // Set the RPC server implementation that will handle the actual requests
+
+ m_rpcProcessor = rpcServer;
+
+ // Set the maximum RPC request size allowed
+
+ setMaximumDatagramSize(maxSize);
+ }
+
+ /**
+ * Return the RPC server used to process the requests
+ *
+ * @return RpcProcessor
+ */
+ protected final RpcProcessor getRpcProcessor()
+ {
+ return m_rpcProcessor;
+ }
+
+ /**
+ * Process the RPC datagram
+ *
+ * @param pkt DatagramPacket
+ * @return boolean
+ * @throws IOException
+ */
+ protected boolean processDatagram(DatagramPacket pkt)
+ throws IOException
+ {
+
+ // The default implementation processes the RPC immediately then returns to the main datagram handler
+ // to wait for the next datagram to be received. In this case the datagram packet can be re-used as
+ // processing is done sequentially.
+
+ // Wrap the datagram data up as an RPC request
+
+ RpcPacket rpcPkt = new RpcPacket(pkt.getData(), 0, pkt.getLength());
+
+ // Set the client details
+
+ rpcPkt.setClientDetails(pkt.getAddress(), pkt.getPort(), Rpc.UDP);
+
+ // Validate the RPC header
+
+ if (rpcPkt.getRpcVersion() != Rpc.RpcVersion)
+ {
+
+ // Build/send an error response
+
+ rpcPkt.buildRpcMismatchResponse();
+ pkt.setData(rpcPkt.getBuffer(), rpcPkt.getOffset(), RpcPacket.ResponseMismatchLen);
+
+ sendDatagram(pkt);
+ }
+ else
+ {
+
+ // Pass the request to the registered RPC server to process
+
+ RpcPacket response = m_rpcProcessor.processRpc(rpcPkt);
+
+ // Send the RPC response
+
+ if (response != null)
+ {
+ pkt.setData(response.getBuffer(), response.getOffset(), response.getLength());
+ sendDatagram(pkt);
+ }
+ }
+
+ // Indicate that the existing datagram packet can be re-used for the next request
+
+ return true;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/mount/Mount.java b/source/java/org/alfresco/filesys/server/oncrpc/mount/Mount.java
new file mode 100644
index 0000000000..798d1c7c10
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/mount/Mount.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.mount;
+
+/**
+ * Mount Server Constants Class
+ *
+ * @author GKSpencer
+ */
+public final class Mount {
+
+ // Program and version id
+
+ public static final int ProgramId = 100005;
+ public static final int VersionId1 = 1;
+ public static final int VersionId3 = 3;
+
+ // RPC procedure ids (version 1)
+
+ public static final int ProcNull1 = 0;
+ public static final int ProcMnt1 = 1;
+ public static final int ProcDump1 = 2;
+ public static final int ProcUMnt1 = 3;
+ public static final int ProcUMntAll1 = 4;
+ public static final int ProcExport1 = 5;
+ public static final int ProcExportAll1 = 6;
+ public static final int ProcMax1 = 6;
+
+ // RPC procedure ids (version 3)
+
+ public static final int ProcNull3 = 0;
+ public static final int ProcMnt3 = 1;
+ public static final int ProcDump3 = 2;
+ public static final int ProcUMnt3 = 3;
+ public static final int ProcUMntAll3 = 4;
+ public static final int ProcExport3 = 5;
+ public static final int ProcMax3 = 5;
+
+ // Mount server status codes
+
+ public static final int StsSuccess = 0;
+ public static final int StsPerm = 1;
+ public static final int StsNoEnt = 2;
+ public static final int StsIO = 5;
+ public static final int StsAccess = 13;
+ public static final int StsNotDir = 20;
+ public static final int StsInval = 22;
+ public static final int StsNameTooLong = 63;
+ public static final int StsNotSupp = 10004;
+ public static final int StsServerFault = 10006;
+
+ // Data structure limits
+
+ public static final int FileHandleSize1 = 32;
+ public static final int FileHandleSize3 = 32; // can be 64 for v3
+
+ // RPC procedure names
+
+ private static final String[] _procNames = { "Null", "Mount", "Dump",
+ "UnMount", "UnMountAll", "Export", "ExportAll" };
+
+ /**
+ * Return a procedure id as a name
+ *
+ * @param id
+ * int
+ * @return String
+ */
+ public final static String getProcedureName(int id) {
+ if (id < 0 || id > ProcMax1)
+ return null;
+ return _procNames[id];
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/mount/MountEntry.java b/source/java/org/alfresco/filesys/server/oncrpc/mount/MountEntry.java
new file mode 100644
index 0000000000..3635488dcc
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/mount/MountEntry.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.mount;
+
+/**
+ * Mount Entry Class
+ *
+ *
Contains the details of an active NFS mount.
+ *
+ * @author GKSpencer
+ */
+public class MountEntry {
+
+ // Remote host name/address
+
+ private String m_host;
+
+ // Mount path
+
+ private String m_path;
+
+ /**
+ * Class constructor
+ *
+ * @param host String
+ * @param path String
+ */
+ public MountEntry(String host, String path) {
+ m_host = host;
+ m_path = path;
+ }
+
+ /**
+ * Return the host name/address
+ *
+ * @return String
+ */
+ public final String getHost() {
+ return m_host;
+ }
+
+ /**
+ * Return the mount path
+ *
+ * @return String
+ */
+ public final String getPath() {
+ return m_path;
+ }
+
+ /**
+ * Return the mount entry as a string
+ *
+ * @return String
+ */
+ public String toString() {
+ StringBuffer str = new StringBuffer();
+
+ str.append("[");
+ str.append(getHost());
+ str.append(":");
+ str.append(getPath());
+ str.append("]");
+
+ return str.toString();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/mount/MountEntryList.java b/source/java/org/alfresco/filesys/server/oncrpc/mount/MountEntryList.java
new file mode 100644
index 0000000000..38d2cb2152
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/mount/MountEntryList.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.mount;
+
+import java.util.*;
+
+/**
+ * Mount Entry List Class
+ *
+ *
Contains a list of active mount entries.
+ *
+ * @author GKSpencer
+ */
+public class MountEntryList {
+
+ // Mount entry list
+
+ private Vector m_mounts;
+
+ /**
+ * Default constructor
+ */
+ public MountEntryList() {
+ m_mounts = new Vector();
+ }
+
+ /**
+ * Ad an entry to the list
+ *
+ * @param entry MountEntry
+ */
+ public synchronized final void addEntry(MountEntry entry) {
+ m_mounts.addElement(entry);
+ }
+
+ /**
+ * Return the number of entries in the list
+ *
+ * @return int
+ */
+ public synchronized final int numberOfEntries() {
+ return m_mounts.size();
+ }
+
+ /**
+ * Return the specified entry
+ *
+ * @param idx
+ * @return MountEntry
+ */
+ public synchronized final MountEntry getEntryAt(int idx) {
+ if ( idx < 0 || idx >= m_mounts.size())
+ return null;
+ return (MountEntry) m_mounts.elementAt(idx);
+ }
+
+ /**
+ * Find an entry in the list
+ *
+ * @param path String
+ * @param host String
+ * @return MountEntry
+ */
+ public synchronized final MountEntry findEntry(String path, String host) {
+ for ( int i = 0; i < m_mounts.size(); i++) {
+ MountEntry entry = (MountEntry) m_mounts.elementAt(i);
+
+ if ( host.compareTo(entry.getHost()) == 0 && path.compareTo(entry.getPath()) == 0)
+ return entry;
+ }
+ return null;
+ }
+
+ /**
+ * Remove an entry from the list
+ *
+ * @param path String
+ * @param host String
+ * @return MountEntry
+ */
+ public synchronized final MountEntry removeEntry(String path, String host) {
+ for ( int i = 0; i < m_mounts.size(); i++) {
+ MountEntry entry = (MountEntry) m_mounts.elementAt(i);
+
+ if ( host.compareTo(entry.getHost()) == 0 && path.compareTo(entry.getPath()) == 0) {
+ m_mounts.removeElementAt(i);
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Remove all entries from the list for the specified host
+ *
+ * @param host String
+ */
+ public synchronized final void removeHostEntries(String host) {
+ for ( int i = 0; i < m_mounts.size(); i++) {
+ MountEntry entry = (MountEntry) m_mounts.elementAt(i);
+
+ if ( host.compareTo(entry.getHost()) == 0)
+ m_mounts.removeElementAt(i);
+ }
+ }
+
+ /**
+ * Find all items for the specified host and return as a new list
+ *
+ * @param host String
+ * @return MountEntryList
+ */
+ public synchronized final MountEntryList findSessionEntries(String host) {
+
+ // Allocate the list to hold the matching entries
+
+ MountEntryList list = new MountEntryList();
+
+ // Find the matching entries
+
+ for ( int i = 0; i < m_mounts.size(); i++) {
+ MountEntry entry = (MountEntry) m_mounts.elementAt(i);
+ if ( host.compareTo(entry.getHost()) == 0)
+ list.addEntry(entry);
+ }
+
+ // Check if the list is empty, return the list
+
+ if ( list.numberOfEntries() == 0)
+ list = null;
+ return list;
+ }
+
+ /**
+ * Remote all entries from the list
+ */
+ public synchronized final void removeAllItems() {
+ m_mounts.removeAllElements();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/mount/MountServer.java b/source/java/org/alfresco/filesys/server/oncrpc/mount/MountServer.java
new file mode 100644
index 0000000000..8b85e0c04d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/mount/MountServer.java
@@ -0,0 +1,885 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.mount;
+
+import java.io.*;
+import java.util.*;
+
+import org.alfresco.filesys.server.ServerListener;
+import org.alfresco.filesys.server.auth.acl.AccessControl;
+import org.alfresco.filesys.server.auth.acl.AccessControlManager;
+import org.alfresco.filesys.server.config.ServerConfiguration;
+import org.alfresco.filesys.server.core.ShareType;
+import org.alfresco.filesys.server.core.SharedDevice;
+import org.alfresco.filesys.server.core.SharedDeviceList;
+import org.alfresco.filesys.server.filesys.DiskInterface;
+import org.alfresco.filesys.server.filesys.FileInfo;
+import org.alfresco.filesys.server.filesys.TreeConnection;
+import org.alfresco.filesys.server.filesys.TreeConnectionHash;
+import org.alfresco.filesys.server.oncrpc.PortMapping;
+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.RpcNetworkServer;
+import org.alfresco.filesys.server.oncrpc.RpcPacket;
+import org.alfresco.filesys.server.oncrpc.RpcProcessor;
+import org.alfresco.filesys.server.oncrpc.TcpRpcSessionHandler;
+import org.alfresco.filesys.server.oncrpc.UdpRpcDatagramHandler;
+import org.alfresco.filesys.server.oncrpc.nfs.NFSHandle;
+import org.alfresco.filesys.server.oncrpc.nfs.NFSSrvSession;
+
+/**
+ * Mount Server Class
+ *
+ *
Contains the NFS mount server.
+ *
+ * @author GKSpencer
+ */
+public class MountServer extends RpcNetworkServer implements RpcProcessor {
+
+ // Constants
+ //
+ // Maximum request size to accept
+
+ public final static int MaxRequestSize = 8192;
+
+ // Unix path seperator
+
+ public static final String UNIX_SEPERATOR = "/";
+ public static final char UNIX_SEPERATOR_CHAR = '/';
+ public static final String DOS_SEPERATOR = "\\";
+ public static final char DOS_SEPERATOR_CHAR = '\\';
+
+ // Incoming datagram handler for UDP requests
+
+ private UdpRpcDatagramHandler m_udpHandler;
+
+ // Incoming session handler for TCP requests
+
+ private TcpRpcSessionHandler m_tcpHandler;
+
+ // Tree connection hash
+
+ private TreeConnectionHash m_connections;
+
+ // List of active mounts
+
+ private MountEntryList m_mounts;
+
+ // Port number to listen on (UDP and TCP)
+
+ private int m_port;
+
+ /**
+ * Class constructor
+ *
+ * @param config
+ * ServerConfiguration
+ */
+ public MountServer(ServerConfiguration config) {
+ super("Mount", config);
+
+ // Enable/disable debug output
+
+ setDebug(config.hasMountServerDebug());
+
+ // Set the port to bind the server to
+
+ setPort(config.getMountServerPort());
+ }
+
+ /**
+ * Return the port to bind to
+ *
+ * @return int
+ */
+ public final int getPort() {
+ return m_port;
+ }
+
+ /**
+ * Set the port to use
+ *
+ * @param port
+ * int
+ */
+ public final void setPort(int port) {
+ m_port = port;
+ }
+
+ /**
+ * Start the mount server
+ */
+ public void startServer() {
+
+ try {
+
+ // Create the UDP handler for accepting incoming requests
+
+ m_udpHandler = new UdpRpcDatagramHandler("Mountd", "Mnt", this, this, null, getPort(), MaxRequestSize);
+ m_udpHandler.initializeSessionHandler(this);
+
+ // Start the UDP request listener is a seperate thread
+
+ Thread udpThread = new Thread(m_udpHandler);
+ udpThread.setName("Mountd_UDP");
+ udpThread.start();
+
+ // Create the TCP handler for accepting incoming requests
+
+ m_tcpHandler = new TcpRpcSessionHandler("Mountd", "Mnt", this, this, null, getPort(), MaxRequestSize);
+ m_tcpHandler.initializeSessionHandler(this);
+
+ // Start the UDP request listener is a seperate thread
+
+ Thread tcpThread = new Thread(m_tcpHandler);
+ tcpThread.setName("Mountd_TCP");
+ tcpThread.start();
+
+ // Register the mount server with the portmapper
+
+ PortMapping[] mappings = new PortMapping[4];
+ mappings[0] = new PortMapping(Mount.ProgramId, Mount.VersionId1, Rpc.UDP, m_udpHandler.getPort());
+ mappings[1] = new PortMapping(Mount.ProgramId, Mount.VersionId3, Rpc.UDP, m_udpHandler.getPort());
+ mappings[2] = new PortMapping(Mount.ProgramId, Mount.VersionId1, Rpc.TCP, m_tcpHandler.getPort());
+ mappings[3] = new PortMapping(Mount.ProgramId, Mount.VersionId3, Rpc.TCP, m_tcpHandler.getPort());
+
+ registerRPCServer(mappings);
+ }
+ catch (Exception ex) {
+ logger.error(ex);
+ }
+
+ // Allocate the tree connection hash list and populate with the
+ // available share names
+
+ m_connections = new TreeConnectionHash();
+
+ SharedDeviceList shareList = getConfiguration().getShareMapper()
+ .getShareList(getConfiguration().getServerName(), null, false);
+ Enumeration shares = shareList.enumerateShares();
+
+ while (shares.hasMoreElements()) {
+
+ // Get the shared device
+
+ SharedDevice share = (SharedDevice) shares.nextElement();
+
+ // Check if it is a disk type shared device, if so then add a
+ // connection to the tree connection hash
+
+ if (share != null && share.getType() == ShareType.DISK)
+ m_connections.addConnection(new TreeConnection(share));
+ }
+
+ // Allocate the active mount list
+
+ m_mounts = new MountEntryList();
+ }
+
+ /**
+ * Shutdown the mount server
+ *
+ * @param immediate
+ * boolean
+ */
+ public void shutdownServer(boolean immediate) {
+
+ // Unregister the mount server with the portmapper
+
+ try {
+ PortMapping[] mappings = new PortMapping[4];
+ mappings[0] = new PortMapping(Mount.ProgramId, Mount.VersionId1,
+ Rpc.UDP, m_udpHandler.getPort());
+ mappings[1] = new PortMapping(Mount.ProgramId, Mount.VersionId3,
+ Rpc.UDP, m_udpHandler.getPort());
+ mappings[2] = new PortMapping(Mount.ProgramId, Mount.VersionId1,
+ Rpc.TCP, m_tcpHandler.getPort());
+ mappings[3] = new PortMapping(Mount.ProgramId, Mount.VersionId3,
+ Rpc.TCP, m_tcpHandler.getPort());
+
+ unregisterRPCServer(mappings);
+ }
+ catch (IOException ex) {
+ logger.debug(ex);
+ }
+
+ // Stop the RPC handlers
+
+ if (m_udpHandler != null) {
+ m_udpHandler.closeSessionHandler(this);
+ m_udpHandler = null;
+ }
+
+ if (m_tcpHandler != null) {
+ m_tcpHandler.closeSessionHandler(this);
+ m_tcpHandler = null;
+ }
+
+ // Fire a shutdown notification event
+
+ fireServerEvent(ServerListener.ServerShutdown);
+ }
+
+ /**
+ * Process an RPC request
+ *
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ * @throws IOException
+ */
+ public RpcPacket processRpc(RpcPacket rpc) throws IOException {
+
+ // Validate the request
+
+ int version = rpc.getProgramVersion();
+
+ if (rpc.getProgramId() != Mount.ProgramId) {
+
+ // Request is not for us
+
+ rpc.buildAcceptErrorResponse(Rpc.StsProgUnavail);
+ return rpc;
+ }
+ else if (version != Mount.VersionId1 && version != Mount.VersionId3) {
+
+ // Request is not for this version of mount
+
+ rpc.buildProgramMismatchResponse(Mount.VersionId1, Mount.VersionId3);
+ return rpc;
+ }
+
+ // Authenticate the request
+
+ NFSSrvSession sess = null;
+
+ try {
+
+ // Create a temporary session for the request
+
+ sess = createTemporarySession(rpc);
+ }
+ catch (RpcAuthenticationException ex) {
+
+ // Failed to authenticate the RPC client
+
+ rpc.buildAuthFailResponse(ex.getAuthenticationErrorCode());
+ return rpc;
+ }
+
+ // Position the RPC buffer pointer at the start of the call parameters
+
+ rpc.positionAtParameters();
+
+ // Process the RPC request
+
+ RpcPacket response = null;
+
+ if (version == Mount.VersionId1) {
+
+ // Version 1 requests
+
+ switch (rpc.getProcedureId()) {
+
+ // Null request
+
+ case Mount.ProcNull1:
+ response = procNull(rpc);
+ break;
+
+ // Mount request
+
+ case Mount.ProcMnt1:
+ response = procMount(sess, rpc, version);
+ break;
+
+ // Dump request
+
+ case Mount.ProcDump1:
+ response = procDump(sess, rpc, version);
+ break;
+
+ // Unmount request
+
+ case Mount.ProcUMnt1:
+ response = procUnMount(sess, rpc, version);
+ break;
+
+ // Unmount all request
+
+ case Mount.ProcUMntAll1:
+ response = procUnMountAll(sess, rpc, version);
+ break;
+
+ // Export request
+
+ case Mount.ProcExport1:
+ response = procExport(sess, rpc, version);
+ break;
+
+ // Export all request
+
+ case Mount.ProcExportAll1:
+ response = procExportAll(sess, rpc);
+ break;
+ }
+ } else if (version == Mount.VersionId3) {
+
+ // Version 1 requests
+
+ switch (rpc.getProcedureId()) {
+
+ // Null request
+
+ case Mount.ProcNull3:
+ response = procNull(rpc);
+ break;
+
+ // Mount request
+
+ case Mount.ProcMnt3:
+ response = procMount(sess, rpc, version);
+ break;
+
+ // Dump request
+
+ case Mount.ProcDump3:
+ response = procDump(sess, rpc, version);
+ break;
+
+ // Unmount request
+
+ case Mount.ProcUMnt3:
+ response = procUnMount(sess, rpc, version);
+ break;
+
+ // Unmount all request
+
+ case Mount.ProcUMntAll3:
+ response = procUnMountAll(sess, rpc, version);
+ break;
+
+ // Export request
+
+ case Mount.ProcExport3:
+ response = procExport(sess, rpc, version);
+ break;
+ }
+ }
+
+ // Return the RPC response
+
+ return response;
+ }
+
+ /**
+ * Process the null request
+ *
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procNull(RpcPacket rpc) {
+
+ // Build the response
+
+ rpc.buildResponseHeader();
+ return rpc;
+ }
+
+ /**
+ * Process the mount request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @param version
+ * int
+ * @return RpcPacket
+ */
+ private final RpcPacket procMount(NFSSrvSession sess, RpcPacket rpc,
+ int version) {
+
+ // Get the request parameters
+
+ String mountPath = rpc.unpackString();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("[Mount] Mount request from " + rpc.getClientDetails() + " path=" + mountPath);
+
+ // Allocate the file handle
+
+ byte[] handle = allocateFileHandle(version);
+
+ // Mount the path
+
+ int sts = mountPath(sess, mountPath, handle);
+
+ // Pack mount the response
+
+ rpc.buildResponseHeader();
+
+ // Pack the file handle status structure, version 1 format
+
+ if (version == 1) {
+
+ // Version 1 response format
+
+ rpc.packInt(sts);
+ if (sts == Mount.StsSuccess)
+ rpc.packByteArray(handle);
+ }
+ else if (version == 3) {
+
+ // Version 3 response format
+
+ rpc.packInt(sts);
+ if (sts == Mount.StsSuccess)
+ rpc.packByteArrayWithLength(handle);
+
+ // Create an authentication flavours array
+
+ rpc.packIntArrayWithLength(getConfiguration().getRpcAuthenticator()
+ .getRpcAuthenticationTypes());
+ }
+
+ // Return the mount response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the dump request, return the list of active mounts
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @param version
+ * int
+ * @return RpcPacket
+ */
+ private final RpcPacket procDump(NFSSrvSession sess, RpcPacket rpc,
+ int version) {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("[Mount] Dump request from " + rpc.getClientDetails());
+
+ // Take a snapshot of the active mount list
+
+ MountEntryList activeList = null;
+
+ synchronized (m_mounts) {
+
+ // Check if there are active mounts, if so then copy the mount list
+
+ if (m_mounts.numberOfEntries() > 0) {
+ activeList = new MountEntryList();
+ for (int i = 0; i < m_mounts.numberOfEntries(); i++)
+ activeList.addEntry(m_mounts.getEntryAt(i));
+ }
+ }
+
+ // Build the response header
+
+ rpc.buildResponseHeader();
+
+ // Pack the mount list structures into the response
+
+ if (activeList != null) {
+
+ // Pack the active mount entry details
+
+ for (int i = 0; i < activeList.numberOfEntries(); i++) {
+
+ // Get the current entry
+
+ MountEntry mntEntry = activeList.getEntryAt(i);
+
+ rpc.packInt(Rpc.True);
+ rpc.packString(mntEntry.getPath());
+ rpc.packString(mntEntry.getHost());
+ }
+ }
+
+ // Mark the end of the mount list and set the response length
+
+ rpc.packInt(Rpc.False);
+ rpc.setLength();
+
+ // Return the RPC response
+
+ return rpc;
+ }
+
+ /**
+ * Process the unmount request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @param version
+ * int
+ * @return RpcPacket
+ */
+ private final RpcPacket procUnMount(NFSSrvSession sess, RpcPacket rpc,
+ int version) {
+
+ // Get the request parameters
+
+ String mountPath = rpc.unpackString();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("[Mount] UnMount request from " + rpc.getClientDetails() + " path=" + mountPath);
+
+ // Remove the mount details from the active mount list
+
+ m_mounts.removeEntry(mountPath, sess.getRemoteName());
+
+ // Build the RPC response
+
+ rpc.buildResponseHeader();
+
+ // Return the RPC response
+
+ return rpc;
+ }
+
+ /**
+ * Process the unmoount all request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @param version
+ * int
+ * @return RpcPacket
+ */
+ private final RpcPacket procUnMountAll(NFSSrvSession sess, RpcPacket rpc,
+ int version) {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("[Mount] UnMountAll request from " + rpc.getClientDetails());
+
+ // Remove all the mount details for the specified host
+
+ m_mounts.removeHostEntries(sess.getRemoteName());
+
+ // Build the RPC response
+
+ rpc.buildResponseHeader();
+
+ // Return the RPC response
+
+ return rpc;
+ }
+
+ /**
+ * Process the export request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @param version
+ * int
+ * @return RpcPacket
+ */
+ private final RpcPacket procExport(NFSSrvSession sess, RpcPacket rpc,
+ int version) {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("[Mount] Export request from " + rpc.getClientDetails());
+
+ // Get the share list from the server
+
+ SharedDeviceList shareList = sess.getServer().getShareMapper()
+ .getShareList(getConfiguration().getServerName(), sess, false);
+
+ // Check if there is an access control manager configured
+
+ if (sess.getServer().hasAccessControlManager()) {
+
+ // Filter the list of available shares by applying any access
+ // control rules
+
+ AccessControlManager aclMgr = sess.getServer().getAccessControlManager();
+
+ shareList = aclMgr.filterShareList(sess, shareList);
+ }
+
+ // Build the response header
+
+ rpc.buildResponseHeader();
+
+ // Add the visible shares to the export list
+
+ Enumeration enm = shareList.enumerateShares();
+
+ while (enm.hasMoreElements()) {
+
+ // Get the current share
+
+ SharedDevice share = (SharedDevice) enm.nextElement();
+
+ // Add to the list of exports if it is a disk type share
+
+ if (share.getType() == ShareType.DISK) {
+
+ // Pack the share details
+
+ rpc.packInt(Rpc.True);
+ rpc.packString("/" + share.getName());
+
+ // No group information
+
+ rpc.packInt(Rpc.False);
+ }
+ }
+
+ // Mark the end of the list
+
+ rpc.packInt(Rpc.False);
+ rpc.setLength();
+
+ // Return the response
+
+ return rpc;
+ }
+
+ /**
+ * Process the export all request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procExportAll(NFSSrvSession sess, RpcPacket rpc) {
+ return null;
+ }
+
+ /**
+ * Create a temporary session for a request. There is no need to cache
+ * sessions in the mount server as usually only single requests are made.
+ *
+ * @param rpc
+ * RpcPacket
+ * @return NFSSrvSession
+ * @exception RpcAuthenticationException
+ */
+ private final NFSSrvSession createTemporarySession(RpcPacket rpc)
+ throws RpcAuthenticationException {
+
+ // Authenticate the request
+
+ RpcAuthenticator rpcAuth = getConfiguration().getRpcAuthenticator();
+ Object sessKey = rpcAuth.authenticateRpcClient( rpc.getCredentialsType(), rpc);
+
+ // Create an NFS session for the request
+
+ NFSSrvSession nfsSess = new NFSSrvSession(this, rpc.getClientAddress(), rpc.getClientPort(), rpc.getClientProtocol());
+
+ // Set the client information for the request
+
+ nfsSess.setClientInformation(rpcAuth.getRpcClientInformation(sessKey, rpc));
+
+ // Return the session
+
+ return nfsSess;
+ }
+
+ /**
+ * Mount a path. Used by the mount server to validate a path and initialize
+ * any NFS resources
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param path
+ * String
+ * @param handle
+ * byte[]
+ * @return int
+ */
+ protected final int mountPath(NFSSrvSession sess, String path, byte[] handle) {
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ logger.debug("MountPath path=" + path);
+
+ // Parse the path into share and additional path components
+
+ if (path.startsWith(UNIX_SEPERATOR) && path.length() >= 2) {
+
+ // Split the path into share and any additional path
+
+ String shareName = null;
+ String extraPath = null;
+ int shareId = -1;
+
+ int pos = path.indexOf(UNIX_SEPERATOR, 1);
+ if (pos != -1) {
+ shareName = path.substring(1, pos);
+ extraPath = path.substring(pos);
+ } else {
+ shareName = path.substring(1);
+ }
+
+ // Search for a share with the specified name
+
+ SharedDevice share = null;
+
+ try {
+ share = getConfiguration().getShareMapper().findShare(
+ getConfiguration().getServerName(), shareName,
+ ShareType.DISK, sess, false);
+ }
+ catch (Exception ex) {
+ }
+
+ // Check if the share exists
+
+ if (share != null) {
+
+ // Check if there is an access control manager configured
+
+ if (getConfiguration().hasAccessControlManager()) {
+
+ // Check the access control to the shared filesystem
+
+ AccessControlManager aclMgr = getConfiguration().getAccessControlManager();
+
+ if (aclMgr.checkAccessControl(sess, share) == AccessControl.NoAccess) {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to mount path=" + path + ", access denied");
+
+ // Return a does not exist type error
+
+ return Mount.StsNoEnt;
+ }
+ }
+
+ // The share id is the hash of the share name
+
+ shareId = shareName.hashCode();
+
+ // Check if there is an extra path to validate
+
+ if (extraPath != null) {
+
+ // Convert the path to an SMB share relative path
+
+ extraPath = extraPath.replace(UNIX_SEPERATOR_CHAR, DOS_SEPERATOR_CHAR);
+ if (extraPath.endsWith(DOS_SEPERATOR))
+ extraPath = extraPath.substring(0, extraPath.length() - 2);
+
+ try {
+
+ // Get the disk shared device
+
+ TreeConnection conn = m_connections.findConnection(shareId);
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Validate the path
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, extraPath);
+
+ if (finfo == null)
+ return Mount.StsNoEnt;
+ else if (finfo.isDirectory() == false)
+ return Mount.StsNotDir;
+
+ // Fill in the handle for the directory
+
+ NFSHandle.packDirectoryHandle(shareId, finfo.getFileId(), handle);
+ }
+ catch (Exception ex) {
+ }
+ }
+ else {
+
+ // Fill in the handle using a share type handle
+
+ NFSHandle.packShareHandle(share.getName(), handle);
+ }
+
+ // Add a new entry to the active mount list
+
+ m_mounts.addEntry(new MountEntry(sess.getRemoteName(), path));
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("Mounted path=" + path + ", handle=" + NFSHandle.asString(handle));
+
+ // Return a success status
+
+ return Mount.StsSuccess;
+ } else {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to mount path=" + path);
+
+ // Indicate that the share does not exist
+
+ return Mount.StsNoEnt;
+ }
+ }
+
+ // Return an invalid path error
+
+ return Mount.StsNoEnt;
+ }
+
+ /**
+ * Allocate a buffer for a file handle, the size depends on the RPC version
+ *
+ * @param version
+ * int
+ * @return byte[]
+ */
+ private final byte[] allocateFileHandle(int version) {
+ byte[] handle = null;
+ if (version == 1)
+ handle = new byte[Mount.FileHandleSize1];
+ else if (version == 3)
+ handle = new byte[Mount.FileHandleSize3];
+ return handle;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/BadCookieException.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/BadCookieException.java
new file mode 100644
index 0000000000..f6d3f0dadb
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/BadCookieException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.nfs;
+
+/**
+ * Bad Cookie Exception Class
+ *
+ * @author GKSpencer
+ */
+public class BadCookieException extends Exception {
+
+ // Object version id
+
+ private static final long serialVersionUID = -6689748925525944869L;
+
+ /**
+ * Default constructor
+ */
+ public BadCookieException() {
+ super();
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param msg String
+ */
+ public BadCookieException(String msg) {
+ super(msg);
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/BadHandleException.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/BadHandleException.java
new file mode 100644
index 0000000000..be00afd65d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/BadHandleException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.nfs;
+
+/**
+ * Bad Handle Exception Class
+ *
+ * @author GKSpencer
+ */
+public class BadHandleException extends Exception {
+
+ // Object version id
+
+ private static final long serialVersionUID = 5928520475130958599L;
+
+ /**
+ * Default constructor
+ */
+ public BadHandleException() {
+ super();
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param msg String
+ */
+ public BadHandleException(String msg) {
+ super(msg);
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/FileIdCache.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/FileIdCache.java
new file mode 100644
index 0000000000..a377d9416a
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/FileIdCache.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.nfs;
+
+import java.util.*;
+
+/**
+ * File Id Cache Class
+ *
+ *
Converts a file/directory id to a share relative path.
+ *
+ * @author GKSpencer
+ */
+public class FileIdCache {
+
+ // File id to path cache
+
+ private Hashtable m_idCache;
+
+ /**
+ * Default constructor
+ */
+ public FileIdCache() {
+ m_idCache = new Hashtable();
+ }
+
+ /**
+ * Add an entry to the cache
+ *
+ * @param fid int
+ * @param path String
+ */
+ public final void addPath(int fid, String path) {
+ m_idCache.put(new Integer(fid), path);
+ }
+
+ /**
+ * Convert a file id to a path
+ *
+ * @param fid int
+ * @return String
+ */
+ public final String findPath(int fid) {
+ return (String) m_idCache.get(new Integer(fid));
+ }
+
+ /**
+ * Delete an entry from the cache
+ *
+ * @param fid int
+ */
+ public final void deletePath(int fid) {
+ m_idCache.remove(new Integer(fid));
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFS.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFS.java
new file mode 100644
index 0000000000..9aebf21619
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFS.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.nfs;
+
+/**
+ * NFS Server Constants Class
+ *
+ * @author GKSpencer
+ */
+public final class NFS {
+
+ // Default NFS server port
+
+ public static final int DefaultPort = 2049;
+
+ // Program and version id
+
+ public static final int ProgramId = 100003;
+ public static final int VersionId = 3;
+
+ // RPC procedure ids
+
+ public static final int ProcNull = 0;
+ public static final int ProcGetAttr = 1;
+ public static final int ProcSetAttr = 2;
+ public static final int ProcLookup = 3;
+ public static final int ProcAccess = 4;
+ public static final int ProcReadLink = 5;
+ public static final int ProcRead = 6;
+ public static final int ProcWrite = 7;
+ public static final int ProcCreate = 8;
+ public static final int ProcMkDir = 9;
+ public static final int ProcSymLink = 10;
+ public static final int ProcMkNode = 11;
+ public static final int ProcRemove = 12;
+ public static final int ProcRmDir = 13;
+ public static final int ProcRename = 14;
+ public static final int ProcLink = 15;
+ public static final int ProcReadDir = 16;
+ public static final int ProcReadDirPlus = 17;
+ public static final int ProcFsStat = 18;
+ public static final int ProcFsInfo = 19;
+ public static final int ProcPathConf = 20;
+ public static final int ProcCommit = 21;
+ public static final int ProcMax = 21;
+
+ // NFS server status codes
+
+ public static final int StsSuccess = 0;
+ public static final int StsPerm = 1;
+ public static final int StsNoEnt = 2;
+ public static final int StsIO = 5;
+ public static final int StsNxIO = 6;
+ public static final int StsAccess = 13;
+ public static final int StsExist = 17;
+ public static final int StsXDev = 18;
+ public static final int StsNoDev = 19;
+ public static final int StsNotDir = 20;
+ public static final int StsIsDir = 21;
+ public static final int StsInVal = 22;
+ public static final int StsFBig = 27;
+ public static final int StsNoSpc = 28;
+ public static final int StsROFS = 30;
+ public static final int StsMLink = 31;
+ public static final int StsNameTooLong = 63;
+ public static final int StsNotEmpty = 66;
+ public static final int StsDQuot = 69;
+ public static final int StsStale = 70;
+ public static final int StsRemote = 71;
+ public static final int StsBadHandle = 10001;
+ public static final int StsNotSync = 10002;
+ public static final int StsBadCookie = 10003;
+ public static final int StsNotSupp = 10004;
+ public static final int StsTooSmall = 10005;
+ public static final int StsServerFault = 10006;
+ public static final int StsBadType = 10007;
+ public static final int StsJukeBox = 10008;
+
+ // Data structure limits
+
+ public static final int FileHandleSize = 32; // can be 64 for NFS v3
+ public static final int WriteVerfSize = 8;
+ public static final int CreateVerfSize = 8;
+ public static final int CookieVerfSize = 8;
+
+ // File types
+
+ public static final int FileTypeReg = 1;
+ public static final int FileTypeDir = 2;
+ public static final int FileTypeBlk = 3;
+ public static final int FileTypeChr = 4;
+ public static final int FileTypeLnk = 5;
+ public static final int FileTypeSock = 6;
+ public static final int FileTypeFifo = 7;
+
+ // Filesystem properties
+
+ public static final int FileSysLink = 0x0001; // supports hard links
+ public static final int FileSysSymLink = 0x0002; // supports symbolic links
+ public static final int FileSysHomogeneuos = 0x0004; // PATHCONF valid for all files
+ public static final int FileSysCanSetTime = 0x0008; // can set time on server side
+
+ // Access mask
+
+ public static final int AccessRead = 0x0001;
+ public static final int AccessLookup = 0x0002;
+ public static final int AccessModify = 0x0004;
+ public static final int AccessExtend = 0x0008;
+ public static final int AccessDelete = 0x0010;
+ public static final int AccessExecute = 0x0020;
+ public static final int AccessAll = 0x003F;
+
+ // Create mode values
+
+ public static final int CreateUnchecked = 1;
+ public static final int CreateGuarded = 2;
+ public static final int CreateExclusive = 3;
+
+ // Write request stable values
+
+ public static final int WriteUnstable = 0;
+ public static final int WriteDataSync = 1;
+ public static final int WriteFileSync = 2;
+
+ // Set attributes file timestamp settings
+
+ public static final int DoNotSetTime = 0;
+ public static final int SetTimeServer = 1;
+ public static final int SetTimeClient = 2;
+
+ // RPC procedure names
+
+ private static final String[] _procNames = { "Null", "GetAttr", "SetAttr",
+ "Lookup", "Access", "ReadLink", "Read", "Write", "Create", "MkDir",
+ "SymLink", "MkNode", "Remove", "RmDir", "Rename", "Link",
+ "ReadDir", "ReadDirPlus", "FsStat", "FsInfo", "PathConf", "Commit" };
+
+ /**
+ * Return a procedure id as a name
+ *
+ * @param id
+ * int
+ * @return String
+ */
+ public final static String getProcedureName(int id) {
+ if (id < 0 || id > ProcMax)
+ return null;
+ return _procNames[id];
+ }
+
+ /**
+ * Return an error status string for the specified status code
+ *
+ * @param sts
+ * int
+ * @return String
+ */
+ public static final String getStatusString(int sts) {
+ String str = null;
+
+ switch (sts) {
+ case StsSuccess:
+ str = "Success status";
+ break;
+ case StsAccess:
+ str = "Access denied";
+ break;
+ case StsBadCookie:
+ str = "Bad cookie";
+ break;
+ case StsBadHandle:
+ str = "Bad handle";
+ break;
+ case StsBadType:
+ str = "Bad type";
+ break;
+ case StsDQuot:
+ str = "Quota exceeded";
+ break;
+ case StsPerm:
+ str = "No permission";
+ break;
+ case StsExist:
+ str = "Already exists";
+ break;
+ case StsFBig:
+ str = "File too large";
+ break;
+ case StsInVal:
+ str = "Invalid argument";
+ break;
+ case StsIO:
+ str = "I/O error";
+ break;
+ case StsIsDir:
+ str = "Is directory";
+ break;
+ case StsJukeBox:
+ str = "Jukebox";
+ break;
+ case StsMLink:
+ str = "Too many hard links";
+ break;
+ case StsNameTooLong:
+ str = "Name too long";
+ break;
+ case StsNoDev:
+ str = "No such device";
+ break;
+ case StsNoEnt:
+ str = "No entity";
+ break;
+ case StsNoSpc:
+ str = "No space left on device";
+ break;
+ case StsNotSync:
+ str = "Update synchronization mismatch";
+ break;
+ case StsNotDir:
+ str = "Not directory";
+ break;
+ case StsNotEmpty:
+ str = "Not empty";
+ break;
+ case StsNotSupp:
+ str = "Not supported";
+ break;
+ case StsNxIO:
+ str = "Nxio";
+ break;
+ case StsRemote:
+ str = "Too many levels of remote in path";
+ break;
+ case StsROFS:
+ str = "Readonly filesystem";
+ break;
+ case StsServerFault:
+ str = "Server fault";
+ break;
+ case StsStale:
+ str = "Stale";
+ break;
+ case StsTooSmall:
+ str = "Too small";
+ break;
+ case StsXDev:
+ str = "Cross device hard link attempted";
+ break;
+ }
+
+ return str;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSHandle.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSHandle.java
new file mode 100644
index 0000000000..ccb7a11f6f
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSHandle.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.nfs;
+
+import org.alfresco.filesys.server.oncrpc.RpcPacket;
+import org.alfresco.filesys.util.DataPacker;
+
+/**
+ * NFS Handle Class
+ *
+ *
Contains constants and static methods used with NFS handles.
+ *
+ * @author GKSpencer
+ */
+public class NFSHandle {
+
+ // Version
+
+ public static final byte VERSION = 1;
+ public static final byte MIN_VERSION = 1;
+ public static final byte MAX_VERSION = 1;
+
+ // Handle types
+
+ public static final byte TYPE_SHARE = 1;
+ public static final byte TYPE_DIR = 2;
+ public static final byte TYPE_FILE = 3;
+
+ // Offsets to fields within the handle
+
+ private static final int VERSION_OFFSET = 0;
+ private static final int TYPE_OFFSET = 1;
+ private static final int SHARE_OFFSET = 2;
+ private static final int DIR_OFFSET = 6;
+ private static final int FILE_OFFSET = 10;
+ private static final int NAME_OFFSET = 14;
+
+ /**
+ * Return the handle version
+ *
+ * @param handle byte[]
+ */
+ public static final int isVersion(byte[] handle)
+ {
+ return (int) handle[0];
+ }
+
+ /**
+ * Return the handle type
+ *
+ * @param handle byte[]
+ * @return int
+ */
+ public static final int isType(byte[] handle)
+ {
+ return (int) handle[1];
+ }
+
+ /**
+ * Check if the handle is a share type handle
+ *
+ * @param handle byte[]
+ * @return boolean
+ */
+ public static final boolean isShareHandle(byte[] handle)
+ {
+ if (handle[1] == TYPE_SHARE)
+ return true;
+ return false;
+ }
+
+ /**
+ * Check if the handle is a directory type handle
+ *
+ * @param handle byte[]
+ * @return boolean
+ */
+ public static final boolean isDirectoryHandle(byte[] handle)
+ {
+ if (handle[1] == TYPE_DIR)
+ return true;
+ return false;
+ }
+
+ /**
+ * Check if the handle is a file type handle
+ *
+ * @param handle byte[]
+ * @return boolean
+ */
+ public static final boolean isFileHandle(byte[] handle)
+ {
+ if (handle[1] == TYPE_FILE)
+ return true;
+ return false;
+ }
+
+ /**
+ * Pack a share handle
+ *
+ * @param name String
+ * @param handle byte[]
+ */
+ public static final void packShareHandle(String name, byte[] handle)
+ {
+
+ // Pack a share handle
+
+ handle[0] = VERSION;
+ handle[1] = TYPE_SHARE;
+
+ // Pack the hash code of the share name
+
+ DataPacker.putInt(name.hashCode(), handle, SHARE_OFFSET);
+
+ // Null pad the handle
+
+ int pos = SHARE_OFFSET + 4;
+
+ while (pos < handle.length)
+ handle[pos++] = 0;
+ }
+
+ /**
+ * Pack a share handle
+ *
+ * @param name String
+ * @param rpc RpcPacket
+ * @param hlen int
+ */
+ public static final void packShareHandle(String name, RpcPacket rpc, int hlen)
+ {
+
+ // Pack a share handle
+
+ rpc.packInt(hlen);
+
+ rpc.packByte(VERSION);
+ rpc.packByte(TYPE_SHARE);
+
+ // Pack the hash code of the share name
+
+ rpc.packInt(name.hashCode());
+
+ // Null pad the handle
+
+ rpc.packNulls(hlen - 6);
+ }
+
+ /**
+ * Pack a directory handle
+ *
+ * @param shareId int
+ * @param dirId int
+ * @param handle byte[]
+ */
+ public static final void packDirectoryHandle(int shareId, int dirId, byte[] handle)
+ {
+
+ // Pack a directory handle
+
+ handle[0] = VERSION;
+ handle[1] = TYPE_DIR;
+
+ DataPacker.putInt(shareId, handle, 2);
+ DataPacker.putInt(dirId, handle, 6);
+
+ // Null pad the handle
+
+ for (int i = 10; i < handle.length; i++)
+ handle[i] = 0;
+ }
+
+ /**
+ * Pack a directory handle
+ *
+ * @param shareId int
+ * @param dirId int
+ * @param rpc RpcPacket
+ * @param hlen int
+ */
+ public static final void packDirectoryHandle(int shareId, int dirId, RpcPacket rpc, int hlen)
+ {
+
+ // Pack a directory handle
+
+ rpc.packInt(hlen);
+
+ rpc.packByte(VERSION);
+ rpc.packByte(TYPE_DIR);
+
+ rpc.packInt(shareId);
+ rpc.packInt(dirId);
+
+ // Null pad the handle
+
+ rpc.packNulls(hlen - 10);
+ }
+
+ /**
+ * Pack a file handle
+ *
+ * @param shareId int
+ * @param dirId int
+ * @param fileId int
+ * @param handle byte[]
+ */
+ public static final void packFileHandle(int shareId, int dirId, int fileId, byte[] handle)
+ {
+
+ // Pack a directory handle
+
+ handle[0] = VERSION;
+ handle[1] = TYPE_FILE;
+
+ DataPacker.putInt(shareId, handle, 2);
+ DataPacker.putInt(dirId, handle, 6);
+ DataPacker.putInt(fileId, handle, 10);
+
+ // Null pad the handle
+
+ for (int i = 14; i < handle.length; i++)
+ handle[i] = 0;
+ }
+
+ /**
+ * Pack a file handle
+ *
+ * @param shareId int
+ * @param dirId int
+ * @param fileId int
+ * @param rpc RpcPacket
+ * @param hlen int
+ */
+ public static final void packFileHandle(int shareId, int dirId, int fileId, RpcPacket rpc, int hlen)
+ {
+
+ // Pack a directory handle
+
+ rpc.packInt(hlen);
+
+ rpc.packByte(VERSION);
+ rpc.packByte(TYPE_FILE);
+
+ rpc.packInt(shareId);
+ rpc.packInt(dirId);
+ rpc.packInt(fileId);
+
+ // Null pad the handle
+
+ rpc.packNulls(hlen - 14);
+ }
+
+ /**
+ * Unpack a share id from a handle
+ *
+ * @param handle byte[]
+ * @return int
+ */
+ public static final int unpackShareId(byte[] handle)
+ {
+
+ // Check if the handle is a share type handle
+
+ int shareId = -1;
+
+ if (handle[1] == TYPE_SHARE || handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
+ {
+
+ // Unpack the share id
+
+ shareId = DataPacker.getInt(handle, 2);
+ }
+
+ // Return the share id, or -1 if wrong handle type
+
+ return shareId;
+ }
+
+ /**
+ * Unpack a directory id from a handle
+ *
+ * @param handle byte[]
+ * @return int
+ */
+ public static final int unpackDirectoryId(byte[] handle)
+ {
+
+ // Check if the handle is a directory or file type handle
+
+ int dirId = -1;
+
+ if (handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
+ {
+
+ // Unpack the directory id
+
+ dirId = DataPacker.getInt(handle, 6);
+ }
+
+ // Return the directory id, or -1 if wrong handle type
+
+ return dirId;
+ }
+
+ /**
+ * Unpack a file id from a handle
+ *
+ * @param handle byte[]
+ * @return int
+ */
+ public static final int unpackFileId(byte[] handle)
+ {
+
+ // Check if the handle is a file type handle
+
+ int fileId = -1;
+
+ if (handle[1] == TYPE_FILE)
+ {
+
+ // Unpack the file id
+
+ fileId = DataPacker.getInt(handle, 10);
+ }
+
+ // Return the file id, or -1 if wrong handle type
+
+ return fileId;
+ }
+
+ /**
+ * Return an NFS handle as a string
+ *
+ * @param handle byte[]
+ * @return String
+ */
+ public static final String asString(byte[] handle)
+ {
+
+ // Check if the handle is a valid type
+
+ StringBuffer str = new StringBuffer();
+ str.append("[");
+
+ switch (handle[1])
+ {
+
+ // Share/mountpoint type handle
+
+ case TYPE_SHARE:
+ str.append("Share:0x");
+ str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
+ break;
+
+ // Directory handle
+
+ case TYPE_DIR:
+ str.append("Dir:share=0x");
+ str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
+ str.append(",dir=0x");
+ str.append(Integer.toHexString(DataPacker.getInt(handle, 6)));
+ break;
+
+ // File handle
+
+ case TYPE_FILE:
+ str.append("File:share=0x");
+ str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
+ str.append(",dir=0x");
+ str.append(Integer.toHexString(DataPacker.getInt(handle, 6)));
+ str.append(",file=0x");
+ str.append(Integer.toHexString(DataPacker.getInt(handle, 10)));
+ break;
+ }
+
+ // Return the handle string
+
+ str.append("]");
+ return str.toString();
+ }
+
+ /**
+ * Check if a handle is valid
+ *
+ * @param handle byte[]
+ * @return boolean
+ */
+ public static final boolean isValid(byte[] handle)
+ {
+
+ // Check if the version is valid
+
+ if (handle[0] < MIN_VERSION || handle[0] > MAX_VERSION)
+ return false;
+
+ // Check if the handle type is valid
+
+ if (handle[1] == TYPE_SHARE || handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
+ return true;
+ return false;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java
new file mode 100644
index 0000000000..e7ac903aec
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java
@@ -0,0 +1,5104 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.nfs;
+
+import java.io.*;
+import java.util.*;
+
+import org.alfresco.filesys.server.ServerListener;
+import org.alfresco.filesys.server.SrvSession;
+import org.alfresco.filesys.server.auth.acl.AccessControl;
+import org.alfresco.filesys.server.auth.acl.AccessControlManager;
+import org.alfresco.filesys.server.config.ServerConfiguration;
+import org.alfresco.filesys.server.core.InvalidDeviceInterfaceException;
+import org.alfresco.filesys.server.core.ShareType;
+import org.alfresco.filesys.server.core.SharedDevice;
+import org.alfresco.filesys.server.core.SharedDeviceList;
+import org.alfresco.filesys.server.filesys.AccessDeniedException;
+import org.alfresco.filesys.server.filesys.AccessMode;
+import org.alfresco.filesys.server.filesys.DiskDeviceContext;
+import org.alfresco.filesys.server.filesys.DiskFullException;
+import org.alfresco.filesys.server.filesys.DiskInterface;
+import org.alfresco.filesys.server.filesys.DiskSizeInterface;
+import org.alfresco.filesys.server.filesys.FileAction;
+import org.alfresco.filesys.server.filesys.FileAttribute;
+import org.alfresco.filesys.server.filesys.FileExistsException;
+import org.alfresco.filesys.server.filesys.FileIdInterface;
+import org.alfresco.filesys.server.filesys.FileInfo;
+import org.alfresco.filesys.server.filesys.FileName;
+import org.alfresco.filesys.server.filesys.FileOpenParams;
+import org.alfresco.filesys.server.filesys.FileStatus;
+import org.alfresco.filesys.server.filesys.FileType;
+import org.alfresco.filesys.server.filesys.NetworkFile;
+import org.alfresco.filesys.server.filesys.NotifyChange;
+import org.alfresco.filesys.server.filesys.SearchContext;
+import org.alfresco.filesys.server.filesys.SrvDiskInfo;
+import org.alfresco.filesys.server.filesys.SymbolicLinkInterface;
+import org.alfresco.filesys.server.filesys.TreeConnection;
+import org.alfresco.filesys.server.filesys.TreeConnectionHash;
+import org.alfresco.filesys.server.oncrpc.AuthType;
+import org.alfresco.filesys.server.oncrpc.MultiThreadedTcpRpcSessionHandler;
+import org.alfresco.filesys.server.oncrpc.MultiThreadedUdpRpcDatagramHandler;
+import org.alfresco.filesys.server.oncrpc.PortMapping;
+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.RpcNetworkServer;
+import org.alfresco.filesys.server.oncrpc.RpcPacket;
+import org.alfresco.filesys.server.oncrpc.RpcPacketPool;
+import org.alfresco.filesys.server.oncrpc.RpcProcessor;
+import org.alfresco.filesys.server.oncrpc.RpcRequestThreadPool;
+
+/**
+ * NFS Server Class
+ *
+ *
Contains the main NFS server.
+ *
+ * @author GKSpencer
+ */
+public class NFSServer extends RpcNetworkServer implements RpcProcessor {
+
+ // Constants
+ //
+ // Debug flags
+
+ public static final int DBG_RXDATA = 0x00000001; // Received data
+ public static final int DBG_TXDATA = 0x00000002; // Transmit data
+ public static final int DBG_DUMPDATA = 0x00000004; // Dump data packets
+ public static final int DBG_SEARCH = 0x00000008; // File/directory search
+ public static final int DBG_INFO = 0x00000010; // Information requests
+ public static final int DBG_FILE = 0x00000020; // File open/close/info
+ public static final int DBG_FILEIO = 0x00000040; // File read/write
+ public static final int DBG_ERROR = 0x00000080; // Errors
+ public static final int DBG_TIMING = 0x00000100; // Time packet processing
+ public static final int DBG_DIRECTORY = 0x00000200; // Directory commands
+ public static final int DBG_SESSION = 0x00000400; // Session creation/deletion
+
+ // Unix path seperator
+
+ public static final String UNIX_SEPERATOR = "/";
+ public static final char UNIX_SEPERATOR_CHAR = '/';
+ public static final String DOS_SEPERATOR = "\\";
+ public static final char DOS_SEPERATOR_CHAR = '\\';
+
+ // Unix file modes
+
+ public static final int MODE_STFILE = 0100000;
+ public static final int MODE_STDIR = 0040000;
+ public static final int MODE_STREAD = 0000555;
+ public static final int MODE_STWRITE = 0000333;
+ public static final int MODE_DIR_DEFAULT = MODE_STDIR + (MODE_STREAD | MODE_STWRITE);
+ public static final int MODE_FILE_DEFAULT = MODE_STFILE + (MODE_STREAD | MODE_STWRITE);
+
+ // Readdir/Readdirplus cookie masks/shift
+ //
+ // 32bit cookies (required by Solaris)
+
+ public static final long COOKIE_RESUMEID_MASK = 0x00FFFFFFL;
+ public static final long COOKIE_SEARCHID_MASK = 0xFF000000L;
+ public static final int COOKIE_SEARCHID_SHIFT = 24;
+
+ // Cookie ids for . and .. directory entries
+
+ public static final long COOKIE_DOT_DIRECTORY = 0x00FFFFFFL;
+ public static final long COOKIE_DOTDOT_DIRECTORY = 0x00FFFFFEL;
+
+ // ReadDir and ReadDirPlus reply header and per file fixed structure
+ // lengths.
+ //
+ // Add file name length rounded to 4 byte boundary to the per file structure
+ // length to get the actual length.
+
+ public final static int READDIRPLUS_HEADER_LENGTH = 108;
+ public final static int READDIRPLUS_ENTRY_LENGTH = 200;
+ public final static int READDIR_HEADER_LENGTH = 108;
+ public final static int READDIR_ENTRY_LENGTH = 24;
+
+ // File id offset
+
+ public static final long FILE_ID_OFFSET = 2L;
+
+ // Maximum request size to accept
+
+ public final static int MaxRequestSize = 0xFFFF;
+
+ // Filesystem limits
+
+ public static final int MaxReadSize = MaxRequestSize;
+ public static final int PrefReadSize = MaxRequestSize;
+ public static final int MultReadSize = 4096;
+ public static final int MaxWriteSize = MaxRequestSize;
+ public static final int PrefWriteSize = MaxRequestSize;
+ public static final int MultWriteSize = 4096;
+ public static final int PrefReadDirSize = 8192;
+ public static final long MaxFileSize = 0x01FFFFFFF000L;
+
+ // Thread pool and packet pool defaults
+
+ private static final int DefaultThreadPoolSize = 8;
+ private static final int DefaultPacketPoolSize = 50;
+
+ // Incoming datagram handler for UDP requests
+
+ private MultiThreadedUdpRpcDatagramHandler m_udpHandler;
+
+ // Incoming session handler for TCP requests
+
+ private MultiThreadedTcpRpcSessionHandler m_tcpHandler;
+
+ // Share details hash
+
+ private ShareDetailsHash m_shareDetails;
+
+ // Tree connection hash
+
+ private TreeConnectionHash m_connections;
+
+ // Session tables for the various authentication types
+
+ private NFSSessionTable m_sessAuthNull;
+
+ private NFSSessionTable m_sessAuthUnix;
+
+ // Session id generator
+
+ private int m_sessId = 1;
+
+ // Port to bind the NFS server to (UDP and TCP)
+
+ private int m_port;
+
+ // Shared thread pool, used by TCP and UDP request handlers
+
+ private RpcRequestThreadPool m_threadPool;
+
+ // Shared packet pool, usd by TCP and UDP request handlers
+
+ private RpcPacketPool m_packetPool;
+
+ // RPC authenticator, from the main server configuration
+
+ private RpcAuthenticator m_rpcAuthenticator;
+
+ // Write verifier, generated from the server start time
+
+ private long m_writeVerifier;
+
+ /**
+ * Class constructor
+ *
+ * @param config
+ * ServerConfiguration
+ */
+ public NFSServer(ServerConfiguration config) {
+ super("NFS", config);
+
+ // Set the debug flags
+
+ setDebugFlags(config.getNFSDebug());
+
+ // Set the port to bind the server to
+
+ if (config.getNFSServerPort() != 0)
+ setPort(config.getNFSServerPort());
+ else
+ setPort(NFS.DefaultPort);
+
+ // Set the RPC authenticator
+
+ m_rpcAuthenticator = config.getRpcAuthenticator();
+
+ // Generate the write verifier
+
+ m_writeVerifier = System.currentTimeMillis();
+ }
+
+ /**
+ * Return the port to bind to
+ *
+ * @return int
+ */
+ public final int getPort() {
+ return m_port;
+ }
+
+ /**
+ * Set the port to use
+ *
+ * @param port
+ * int
+ */
+ public final void setPort(int port) {
+ m_port = port;
+ }
+
+ /**
+ * Start the NFS server
+ */
+ public void startServer() {
+
+ try {
+
+ // Allocate the share detail hash list and tree connection list, and
+ // populate with the available share details
+
+ m_shareDetails = new ShareDetailsHash();
+ m_connections = new TreeConnectionHash();
+
+ checkForNewShares();
+
+ // Get the thread pool and packet pool sizes
+
+ int threadPoolSize = DefaultThreadPoolSize;
+
+ if (getConfiguration().getNFSThreadPoolSize() > 0)
+ threadPoolSize = getConfiguration().getNFSThreadPoolSize();
+
+ int packetPoolSize = DefaultPacketPoolSize;
+
+ if (getConfiguration().getNFSPacketPoolSize() > 0)
+ packetPoolSize = getConfiguration().getNFSPacketPoolSize();
+
+ // Create the share thread pool for RPC processing
+
+ m_threadPool = new RpcRequestThreadPool("NFS", threadPoolSize, this);
+
+ // Create the shared packet pool
+
+ m_packetPool = new RpcPacketPool(MaxRequestSize, packetPoolSize);
+
+ // Create the UDP handler for accepting incoming requests
+
+ m_udpHandler = new MultiThreadedUdpRpcDatagramHandler("Nfsd", "Nfs", this, this, null, getPort(), MaxRequestSize);
+
+ // Use the shared thread pool and packet pool
+
+ m_udpHandler.setThreadPool(m_threadPool);
+ m_udpHandler.setPacketPool(m_packetPool);
+
+ m_udpHandler.initializeSessionHandler(this);
+
+ // Start the UDP request listener is a seperate thread
+
+ Thread udpThread = new Thread(m_udpHandler);
+ udpThread.setName("NFS_UDP");
+ udpThread.start();
+
+ // Create the TCP handler for accepting incoming requests
+
+ m_tcpHandler = new MultiThreadedTcpRpcSessionHandler("Nfsd", "Nfs",
+ this, this, null, getPort(), MaxRequestSize);
+
+ // Use the shared thread pool and packet pool
+
+ m_tcpHandler.setThreadPool(m_threadPool);
+ m_tcpHandler.setPacketPool(m_packetPool);
+
+ m_tcpHandler.initializeSessionHandler(this);
+
+ // Start the UDP request listener is a seperate thread
+
+ Thread tcpThread = new Thread(m_tcpHandler);
+ tcpThread.setName("NFS_TCP");
+ tcpThread.start();
+
+ // Register the NFS server with the portmapper
+
+ PortMapping[] mappings = new PortMapping[2];
+ mappings[0] = new PortMapping(NFS.ProgramId, NFS.VersionId, Rpc.UDP, m_udpHandler.getPort());
+ mappings[1] = new PortMapping(NFS.ProgramId, NFS.VersionId, Rpc.TCP, m_tcpHandler.getPort());
+
+ registerRPCServer(mappings);
+ }
+ catch (Exception ex) {
+ logger.error(ex);
+ }
+ }
+
+ /**
+ * Shutdown the NFS server
+ *
+ * @param immediate
+ * boolean
+ */
+ public void shutdownServer(boolean immediate) {
+
+ // Unregister the NFS server with the portmapper
+
+ try {
+ PortMapping[] mappings = new PortMapping[2];
+ mappings[0] = new PortMapping(NFS.ProgramId, NFS.VersionId, Rpc.UDP, m_udpHandler.getPort());
+ mappings[1] = new PortMapping(NFS.ProgramId, NFS.VersionId, Rpc.TCP, m_tcpHandler.getPort());
+
+ unregisterRPCServer(mappings);
+ }
+ catch (IOException ex) {
+ logger.error(ex);
+ }
+
+ // Stop the RPC handlers
+
+ if (m_udpHandler != null) {
+ m_udpHandler.closeSessionHandler(this);
+ m_udpHandler = null;
+ }
+
+ if (m_tcpHandler != null) {
+ m_tcpHandler.closeSessionHandler(this);
+ m_tcpHandler = null;
+ }
+
+ // Stop the thread pool
+
+ m_threadPool.shutdownThreadPool();
+
+ // Fire a shutdown notification event
+
+ fireServerEvent(ServerListener.ServerShutdown);
+ }
+
+ /**
+ * Process an RPC request to the NFS or mount server
+ *
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ * @throws IOException
+ */
+ public RpcPacket processRpc(RpcPacket rpc) throws IOException {
+
+ // Dump the request data
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_DUMPDATA))
+ logger.debug("NFS Req=" + rpc.toString());
+
+ // Validate the request
+
+ int version = rpc.getProgramVersion();
+
+ if (rpc.getProgramId() != NFS.ProgramId) {
+
+ // Request is not for us
+
+ rpc.buildAcceptErrorResponse(Rpc.StsProgUnavail);
+ return rpc;
+ }
+ else if (version != NFS.VersionId) {
+
+ // Request is not for this version of NFS
+
+ rpc.buildProgramMismatchResponse(NFS.VersionId, NFS.VersionId);
+ return rpc;
+ }
+
+ // Find the associated session object for the request, or create a new
+ // session
+
+ NFSSrvSession nfsSess = null;
+
+ try {
+
+ // Find the associated session, or create a new session
+
+ nfsSess = findSessionForRequest(rpc);
+ }
+ catch (RpcAuthenticationException ex) {
+
+ // Failed to authenticate the RPC client
+
+ rpc.buildAuthFailResponse(ex.getAuthenticationErrorCode());
+ return rpc;
+ }
+
+ // Position the RPC buffer pointer at the start of the call parameters
+
+ rpc.positionAtParameters();
+
+ // Process the RPC request
+
+ RpcPacket response = null;
+
+ try
+ {
+ switch (rpc.getProcedureId()) {
+
+ // Null request
+
+ case NFS.ProcNull:
+ response = procNull(nfsSess, rpc);
+ break;
+
+ // Get attributes request
+
+ case NFS.ProcGetAttr:
+ response = procGetAttr(nfsSess, rpc);
+ break;
+
+ // Set attributes request
+
+ case NFS.ProcSetAttr:
+ response = procSetAttr(nfsSess, rpc);
+ break;
+
+ // Lookup request
+
+ case NFS.ProcLookup:
+ response = procLookup(nfsSess, rpc);
+ break;
+
+ // Access request
+
+ case NFS.ProcAccess:
+ response = procAccess(nfsSess, rpc);
+ break;
+
+ // Read symbolic link request
+
+ case NFS.ProcReadLink:
+ response = procReadLink(nfsSess, rpc);
+ break;
+
+ // Read file request
+
+ case NFS.ProcRead:
+ response = procRead(nfsSess, rpc);
+ break;
+
+ // Write file request
+
+ case NFS.ProcWrite:
+ response = procWrite(nfsSess, rpc);
+ break;
+
+ // Create file request
+
+ case NFS.ProcCreate:
+ response = procCreate(nfsSess, rpc);
+ break;
+
+ // Create directory request
+
+ case NFS.ProcMkDir:
+ response = procMkDir(nfsSess, rpc);
+ break;
+
+ // Create symbolic link request
+
+ case NFS.ProcSymLink:
+ response = procSymLink(nfsSess, rpc);
+ break;
+
+ // Create special device request
+
+ case NFS.ProcMkNode:
+ response = procMkNode(nfsSess, rpc);
+ break;
+
+ // Delete file request
+
+ case NFS.ProcRemove:
+ response = procRemove(nfsSess, rpc);
+ break;
+
+ // Delete directory request
+
+ case NFS.ProcRmDir:
+ response = procRmDir(nfsSess, rpc);
+ break;
+
+ // Rename request
+
+ case NFS.ProcRename:
+ response = procRename(nfsSess, rpc);
+ break;
+
+ // Create hard link request
+
+ case NFS.ProcLink:
+ response = procLink(nfsSess, rpc);
+ break;
+
+ // Read directory request
+
+ case NFS.ProcReadDir:
+ response = procReadDir(nfsSess, rpc);
+ break;
+
+ // Read directory plus request
+
+ case NFS.ProcReadDirPlus:
+ response = procReadDirPlus(nfsSess, rpc);
+ break;
+
+ // Filesystem status request
+
+ case NFS.ProcFsStat:
+ response = procFsStat(nfsSess, rpc);
+ break;
+
+ // Filesystem information request
+
+ case NFS.ProcFsInfo:
+ response = procFsInfo(nfsSess, rpc);
+ break;
+
+ // Retrieve POSIX information request
+
+ case NFS.ProcPathConf:
+ response = procPathConf(nfsSess, rpc);
+ break;
+
+ // Commit request
+
+ case NFS.ProcCommit:
+ response = procCommit(nfsSess, rpc);
+ break;
+ }
+
+ // Dump the response
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_DUMPDATA))
+ logger.debug("NFS Resp=" + (rpc != null ? rpc.toString() : ""));
+
+ // Commit, or rollback, any active user transaction
+
+ try
+ {
+ // Commit or rollback the transaction
+
+ nfsSess.endTransaction();
+ }
+ catch ( Exception ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Error committing transaction", ex);
+ }
+ }
+ finally
+ {
+ // If there is an active transaction then roll it back
+
+ if ( nfsSess.hasUserTransaction())
+ {
+ try
+ {
+ nfsSess.getUserTransaction().rollback();
+ }
+ catch (Exception ex)
+ {
+ logger.warn("Failed to rollback transaction", ex);
+ }
+ }
+ }
+
+ // Return the RPC response
+
+ return response;
+ }
+
+ /**
+ * Process the null request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procNull(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Build the response
+
+ rpc.buildResponseHeader();
+ return rpc;
+ }
+
+ /**
+ * Process the get attributes request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procGetAttr(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Get the handle from the request
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("GetAttr request from " + rpc.getClientDetails()
+ + ", handle=" + NFSHandle.asString(handle));
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+
+ // Return an error status
+
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Build the response header
+
+ rpc.buildResponseHeader();
+
+ // Check if this is a share handle
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ // Call the disk share driver to get the file information for the path
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the path from the handle
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Get the file information for the specified path
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, path);
+ if (finfo != null) {
+
+ // Pack the file information into the NFS attributes structure
+
+ rpc.packInt(NFS.StsSuccess);
+ packAttributes3(rpc, finfo, shareId);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("GetAttr path=" + path + ", info="
+ + finfo);
+ }
+ } catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ } catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ } catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ } catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("GetAttr Exception: " + ex.toString());
+ logger.debug(ex);
+ }
+
+ // Error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("GetAttr error="
+ + NFS.getStatusString(errorSts));
+ }
+
+ // Return the attributes
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the set attributes request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procSetAttr(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the set attributes parameters
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("SetAttr request from " + rpc.getClientDetails());
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Check if this is a share handle
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ // Call the disk share driver to get the file information for the path
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasWriteAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the path from the handle
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Get the current file information
+
+ FileInfo oldInfo = disk.getFileInformation(sess, conn, path);
+
+ // Get the values to be set for the file/folder
+
+ int setFlags = 0;
+ int gid = -1;
+ int uid = -1;
+ int mode = -1;
+ long fsize = -1L;
+ long atime = -1L;
+ long mtime = -1L;
+
+ // Check if the file mode has been specified
+
+ if (rpc.unpackInt() == Rpc.True) {
+ mode = rpc.unpackInt();
+ setFlags += FileInfo.SetMode;
+ }
+
+ // Check if the file owner uid has been specified
+
+ if (rpc.unpackInt() == Rpc.True) {
+ uid = rpc.unpackInt();
+ setFlags += FileInfo.SetUid;
+ }
+
+ // Check if the file group gid has been specified
+
+ if (rpc.unpackInt() == Rpc.True) {
+ gid = rpc.unpackInt();
+ setFlags += FileInfo.SetGid;
+ }
+
+ // Check if a new file size has been specified
+
+ if (rpc.unpackInt() == Rpc.True) {
+ fsize = rpc.unpackLong();
+ setFlags += FileInfo.SetFileSize;
+ }
+
+ // Check if the access date/time should be set. It may be set to a
+ // client specified time
+ // or using the server time
+
+ int setTime = rpc.unpackInt();
+
+ if (setTime == NFS.SetTimeClient) {
+ atime = (long) rpc.unpackInt();
+ atime *= 1000L;
+ rpc.skipBytes(4); // nanoseconds
+ setFlags += FileInfo.SetAccessDate;
+ } else if (setTime == NFS.SetTimeServer) {
+ atime = System.currentTimeMillis();
+ setFlags += FileInfo.SetAccessDate;
+ }
+
+ // Check if the modify date/time should be set. It may be set to a
+ // client specified time
+ // or using the server time
+
+ setTime = rpc.unpackInt();
+
+ if (setTime == NFS.SetTimeClient) {
+ mtime = (long) rpc.unpackInt();
+ mtime *= 1000L;
+ rpc.skipBytes(4); // nanoseconds
+ setFlags += FileInfo.SetModifyDate;
+ } else if (setTime == NFS.SetTimeServer) {
+ mtime = System.currentTimeMillis();
+ setFlags += FileInfo.SetModifyDate;
+ }
+
+ // Check if any of the file times should be updated
+
+ if (setFlags != 0) {
+
+ // Set the file access/modify date/times
+
+ FileInfo finfo = new FileInfo();
+ finfo.setFileInformationFlags(setFlags);
+
+ if (atime != -1L)
+ finfo.setAccessDateTime(atime);
+
+ if (mtime != -1L)
+ finfo.setModifyDateTime(mtime);
+
+ // Check if the group id should be set
+
+ if (gid != -1) {
+
+ // Set the group id in the file information
+
+ finfo.setGid(gid);
+ }
+
+ // Check if the user id should be set
+
+ if (uid != -1) {
+
+ // Set the user id in the file information
+
+ finfo.setUid(uid);
+ }
+
+ // Check if the mode should be set
+
+ if (mode != -1) {
+
+ // Set the mode in the file information
+
+ finfo.setMode(mode);
+ }
+
+ // Set the file information
+
+ disk.setFileInformation(sess, conn, path, finfo);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("SetAttr handle="
+ + NFSHandle.asString(handle) + ", accessTime="
+ + finfo.getAccessDateTime() + ", modifyTime="
+ + finfo.getModifyDateTime() + ", mode=" + mode
+ + ", gid/uid=" + gid + "/" + uid);
+ }
+
+ // Check if the file size should be updated
+
+ if (fsize != -1L) {
+
+ // Open the file, may be cached
+
+ NetworkFile netFile = getNetworkFileForHandle(sess, handle,
+ conn, false);
+
+ synchronized (netFile) {
+
+ // Open the network file
+
+ netFile.openFile(false);
+
+ // Change the file size
+
+ disk.truncateFile(sess, conn, netFile, fsize);
+
+ // Close the file
+
+ netFile.close();
+ }
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("SetAttr handle=" + NFSHandle.asString(handle) + ", newSize=" + fsize);
+ }
+
+ // Get the updated file information
+
+ FileInfo newInfo = disk.getFileInformation(sess, conn, path);
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packWccData(rpc, oldInfo);
+ packPostOpAttr(sess, newInfo, shareId, rpc);
+ } catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ } catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ } catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ } catch (DiskFullException ex) {
+ errorSts = NFS.StsDQuot;
+ } catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("SetAttr Exception: " + ex.toString());
+ }
+
+ // Check for a failure status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packWccData(rpc, null);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("SetAttr error="
+ + NFS.getStatusString(errorSts));
+ }
+
+ // Return a the set status
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the lookup request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procLookup(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the lookup arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ String fileName = rpc.unpackString();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("Lookup request from " + rpc.getClientDetails() + ", handle=" + NFSHandle.asString(handle) + ", name=" + fileName);
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Call the disk share driver to get the file information for the path
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the path from the handle
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Build the full path string
+
+ String lookupPath = generatePath(path, fileName);
+
+ // Check if the file/directory exists
+
+ if (disk.fileExists(sess, conn, lookupPath) != FileStatus.NotExist) {
+
+ // Get file information for the path
+
+ FileInfo finfo = disk
+ .getFileInformation(sess, conn, lookupPath);
+
+ if (finfo != null) {
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ // Pack the file handle
+
+ if (finfo.isDirectory())
+ NFSHandle.packDirectoryHandle(shareId, finfo.getFileId(), rpc, NFS.FileHandleSize);
+ else
+ NFSHandle.packFileHandle(shareId, getFileIdForHandle(handle), finfo.getFileId(), rpc, NFS.FileHandleSize);
+
+ // Pack the file attributes
+
+ packPostOpAttr(sess, finfo, shareId, rpc);
+
+ // Add a cache entry for the path
+
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+
+ details.getFileIdCache().addPath(finfo.getFileId(),
+ lookupPath);
+
+ // Check if the file path is a file name only, if so then
+ // get the parent directory details
+
+ if (pathHasDirectories(fileName) == false
+ || fileName.equals("..")) {
+
+ // Get the parent directory file information
+
+ FileInfo dirInfo = disk.getFileInformation(sess, conn,
+ path);
+ packPostOpAttr(sess, dirInfo, shareId, rpc);
+
+ // Add the path to the file id cache, if the filesystem
+ // does not support id lookups
+
+ if (details.hasFileIdSupport() == false)
+ details.getFileIdCache().addPath(dirInfo.getFileId(), path);
+ }
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("Lookup path=" + lookupPath + ", finfo=" + finfo.toString());
+ }
+ } else {
+
+ // File does not exist
+
+ errorSts = NFS.StsNoEnt;
+ }
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Lookup Exception: " + ex.toString());
+ }
+
+ // Check if an error is being returned
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the response
+
+ rpc.buildErrorResponse(errorSts);
+ packPostOpAttr(sess, null, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Lookup error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the access request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procAccess(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Get the parameters from the request
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ int accessMode = rpc.unpackInt();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("Access request from " + rpc.getClientDetails()
+ + ", handle=" + NFSHandle.asString(handle) + ", access=0x"
+ + Integer.toHexString(accessMode));
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+
+ // Return an error status
+
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Check if this is a share handle
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ // Call the disk share driver to get the file information for the path
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+ path = getPathForHandle(sess, handle, conn);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Get the file information for the specified path
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, path);
+ if (finfo != null) {
+
+ // Check the access that the session has to the filesystem
+
+ int mask = 0;
+
+ if (conn.hasWriteAccess()) {
+
+ // Set the mask to allow all operations
+
+ mask = NFS.AccessAll;
+ }
+ else if (conn.hasReadAccess()) {
+
+ // Set the mask for read-only operations
+
+ mask = NFS.AccessRead + NFS.AccessLookup + NFS.AccessExecute;
+ }
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packPostOpAttr(sess, finfo, shareId, rpc);
+ rpc.packInt(accessMode & mask);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("Access path=" + path + ", info=" + finfo);
+ } else {
+
+ // Return an error status
+
+ errorSts = NFS.StsNoEnt;
+ }
+ } catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ } catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ } catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Access3 Exception: " + ex.toString());
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+ rpc.buildErrorResponse(errorSts);
+ packPostOpAttr(sess, null, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Access error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the read link request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procReadLink(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the read link arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Build the response header
+
+ rpc.buildResponseHeader();
+
+ // Call the disk share driver to read the symbolic link data
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+
+ TreeConnection conn = getTreeConnection(sess, shareId);
+ path = getPathForHandle(sess, handle, conn);
+
+ // Check if the filesystem supports symbolic links
+
+ if ((conn.getInterface() instanceof SymbolicLinkInterface) == false) {
+
+ // Symbolic links not supported on this filesystem
+
+ rpc.buildErrorResponse(NFS.StsNotSupp);
+ packPostOpAttr(sess, null, 0, rpc);
+ packWccData(rpc, null);
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Get the file information for the symbolic link
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, path);
+ if (finfo != null && finfo.isFileType() == FileType.SymbolicLink) {
+
+ // Get the symbolic link data
+
+ SymbolicLinkInterface symLinkInterface = (SymbolicLinkInterface) disk;
+ String linkData = symLinkInterface.readSymbolicLink(sess, conn,
+ path);
+
+ // Pack the read link response
+
+ rpc.packInt(NFS.StsSuccess);
+ packPostOpAttr(sess, finfo, shareId, rpc);
+ rpc.packString(linkData);
+ } else {
+
+ // Return an error status, not a symbolic link
+
+ errorSts = NFS.StsInVal;
+ }
+
+ } catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ } catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ } catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ } catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("ReadLink Exception: " + ex.toString());
+ }
+
+ // Error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("ReadLink error="
+ + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the read file request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procRead(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the read parameters
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ long offset = rpc.unpackLong();
+ int count = rpc.unpackInt();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILEIO))
+ logger.debug("[NFS] Read request " + rpc.getClientDetails()
+ + ", count=" + count + ", pos=" + offset);
+
+ // Call the disk share driver to read the file
+
+ int shareId = -1;
+ NetworkFile netFile = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and associated shared device
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the network file, it may be cached
+
+ netFile = getNetworkFileForHandle(sess, handle, conn, true);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Pack the start of the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ // Get file information for the path and pack into the reply
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, netFile.getFullName());
+ packPostOpAttr(sess, finfo, shareId, rpc);
+
+ // Save the current position in the response buffer to fill in the
+ // length and end of file flag after
+ // the read.
+
+ int bufPos = rpc.getPosition();
+
+ // Read the network file
+
+ int rdlen = -1;
+
+ synchronized (netFile) {
+
+ // Make sure the network file is open
+
+ if (netFile.isClosed())
+ netFile.openFile(false);
+
+ // Read a block of data from the file
+
+ rdlen = disk.readFile(sess, conn, netFile, rpc.getBuffer(),
+ bufPos + 12, count, offset);
+ }
+
+ // Set the read length and end of file flag
+
+ rpc.packInt(rdlen);
+ rpc.packInt(rdlen < count ? Rpc.True : Rpc.False);
+ rpc.packInt(rdlen);
+
+ // Set the response length
+
+ rpc.setLength(bufPos + 12 + ((rdlen + 3) & 0xFFFFFFFC));
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILEIO))
+ logger.debug("Read fid=" + netFile.getFileId() + ", name=" + netFile.getName() + ", rdlen=" + rdlen);
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR)) {
+ logger.debug("Read Exception: netFile=" + netFile + ", cache=" + sess.getFileCache().numberOfEntries());
+ logger.debug(ex);
+ }
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packPostOpAttr(sess, null, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Read error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ return rpc;
+ }
+
+ /**
+ * Process the write file request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procWrite(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the read parameters
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ long offset = rpc.unpackLong();
+ int count = rpc.unpackInt();
+ int stable = rpc.unpackInt();
+
+ // Skip the second write length, position at the start of the data to
+ // write
+
+ rpc.skipBytes(4);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILEIO))
+ logger.debug("Write request from " + rpc.getClientDetails() + " , count=" + count + ", offset=" + offset);
+
+ // Call the disk share driver to write to the file
+
+ int shareId = -1;
+ String path = null;
+ NetworkFile netFile = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and associated shared device
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasWriteAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the network file, it may be cached
+
+ netFile = getNetworkFileForHandle(sess, handle, conn, false);
+
+ // Get the file path
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Check if threaded writes should be used
+
+ FileInfo preInfo = null;
+
+ synchronized (netFile) {
+
+ // Make sure the network file is open
+
+ if (netFile.isClosed())
+ netFile.openFile(false);
+
+ // Get the pre-operation file details
+
+ preInfo = disk.getFileInformation(sess, conn, path);
+
+ // Write to the network file
+
+ disk.writeFile(sess, conn, netFile, rpc.getBuffer(), rpc
+ .getPosition(), count, offset);
+ }
+
+ // Get file information for the path and pack the response
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, path);
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packPreOpAttr(sess, preInfo, rpc);
+ packPostOpAttr(sess, finfo, shareId, rpc);
+
+ rpc.packInt(count);
+ rpc.packInt(stable);
+ rpc.packLong(m_writeVerifier); // verifier
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILEIO))
+ logger.debug("Write fid=" + netFile.getFileId() + ", name=" + netFile.getName() + ", wrlen=" + count);
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (DiskFullException ex) {
+ errorSts = NFS.StsNoSpc;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR)) {
+ logger.debug("Write Exception: netFile=" + netFile + ", cache=" + sess.getFileCache().numberOfEntries());
+ logger.debug(ex);
+ }
+ }
+
+ // Check for a failure status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packWccData(rpc, null); // before attributes
+ packWccData(rpc, null); // after attributes
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Write error="
+ + NFS.getStatusString(errorSts));
+ }
+
+ // Return the write response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the create file request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procCreate(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the create arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ String fileName = rpc.unpackString();
+
+ int createMode = rpc.unpackInt();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE))
+ logger.debug("Create request from " + rpc.getClientDetails() + ", name=" + fileName);
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Call the disk share driver to create the new file
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+
+ TreeConnection conn = getTreeConnection(sess, shareId);
+ path = getPathForHandle(sess, handle, conn);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasWriteAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Get the pre-operation state for the parent directory
+
+ FileInfo preInfo = disk.getFileInformation(sess, conn, path);
+
+ // Build the full path string
+
+ StringBuffer str = new StringBuffer();
+ str.append(path);
+
+ if (path.endsWith("\\") == false)
+ str.append("\\");
+ str.append(fileName);
+
+ String filePath = str.toString();
+
+ // Check if the file exists
+
+ int existSts = disk.fileExists(sess, conn, filePath);
+ if (existSts == FileStatus.FileExists) {
+ errorSts = NFS.StsExist;
+ }
+ else if (existSts == FileStatus.DirectoryExists) {
+ errorSts = NFS.StsIsDir;
+ }
+ else {
+
+ // Get the file permissions
+
+ int gid = -1;
+ int uid = -1;
+ int mode = -1;
+
+ if (rpc.unpackInt() == Rpc.True)
+ mode = rpc.unpackInt();
+
+ if (rpc.unpackInt() == Rpc.True)
+ uid = rpc.unpackInt();
+
+ if (rpc.unpackInt() == Rpc.True)
+ gid = rpc.unpackInt();
+
+ // Create a new file
+
+ FileOpenParams params = new FileOpenParams(filePath, FileAction.CreateNotExist, AccessMode.ReadWrite, 0, gid, uid, mode);
+ NetworkFile netFile = disk.createFile(sess, conn, params);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE))
+ logger.debug(" Create file params=" + params);
+
+ // Get file information for the path
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, filePath);
+
+ if (finfo != null) {
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ if (finfo.isDirectory())
+ packDirectoryHandle(shareId, finfo.getFileId(), rpc);
+ else
+ packFileHandle(shareId, getFileIdForHandle(handle),
+ finfo.getFileId(), rpc);
+
+ // Pack the file attributes
+
+ packPostOpAttr(sess, finfo, shareId, rpc);
+
+ // Add a cache entry for the path
+
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+ details.getFileIdCache().addPath(finfo.getFileId(),
+ filePath);
+
+ // Add a cache entry for the network file
+
+ sess.getFileCache().addFile(netFile, conn);
+
+ // Pack the wcc data structure for the directory
+
+ packPreOpAttr(sess, preInfo, rpc);
+
+ FileInfo postInfo = disk.getFileInformation(sess, conn,
+ path);
+ packPostOpAttr(sess, postInfo, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE))
+ logger.debug("Create path=" + filePath
+ + ", finfo=" + finfo.toString());
+
+ // Notify change listeners that a new file has been created
+
+ DiskDeviceContext diskCtx = (DiskDeviceContext) conn
+ .getContext();
+
+ if (diskCtx.hasChangeHandler())
+ diskCtx.getChangeHandler().notifyFileChanged( NotifyChange.ActionAdded, filePath);
+ }
+ }
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Create Exception: " + ex.toString());
+ }
+
+ // Check for a failure status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packWccData(rpc, null); // before attributes
+ packWccData(rpc, null); // after attributes
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Create error="
+ + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the create directory request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procMkDir(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the mkdir arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ String dirName = rpc.unpackString();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_DIRECTORY))
+ logger.debug("MkDir request from " + rpc.getClientDetails() + ", name=" + dirName);
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Call the disk share driver to create the new directory
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+ path = getPathForHandle(sess, handle, conn);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasWriteAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Get the pre-operation state for the parent directory
+
+ FileInfo preInfo = disk.getFileInformation(sess, conn, path);
+
+ // Build the full path string
+
+ StringBuffer str = new StringBuffer();
+ str.append(path);
+ if (path.endsWith("\\") == false)
+ str.append("\\");
+ str.append(dirName);
+ String dirPath = str.toString();
+
+ // Check if the file exists
+
+ int existSts = disk.fileExists(sess, conn, dirPath);
+ if (existSts != FileStatus.NotExist) {
+ errorSts = NFS.StsExist;
+ } else {
+
+ // Get the user id, group id and mode for the new directory
+
+ int gid = -1;
+ int uid = -1;
+ int mode = -1;
+
+ if (rpc.unpackInt() == Rpc.True)
+ mode = rpc.unpackInt();
+
+ if (rpc.unpackInt() == Rpc.True)
+ uid = rpc.unpackInt();
+
+ if (rpc.unpackInt() == Rpc.True)
+ gid = rpc.unpackInt();
+
+ // Directory creation parameters
+
+ FileOpenParams params = new FileOpenParams(dirPath, FileAction.CreateNotExist, AccessMode.ReadWrite,
+ FileAttribute.NTDirectory, gid, uid, mode);
+
+ // Create a new directory
+
+ disk.createDirectory(sess, conn, params);
+
+ // Get file information for the new directory
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, dirPath);
+
+ if (finfo != null) {
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packDirectoryHandle(shareId, finfo.getFileId(), rpc);
+
+ // Pack the file attributes
+
+ packPostOpAttr(sess, finfo, shareId, rpc);
+
+ // Add a cache entry for the path
+
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+
+ details.getFileIdCache().addPath(finfo.getFileId(), dirPath);
+
+ // Pack the post operation details for the parent directory
+
+ packWccData(rpc, preInfo);
+ packPostOpAttr(sess, conn, handle, rpc);
+
+ // Notify change listeners that a new directory has been
+ // created
+
+ DiskDeviceContext diskCtx = (DiskDeviceContext) conn
+ .getContext();
+
+ if (diskCtx.hasChangeHandler())
+ diskCtx.getChangeHandler().notifyFileChanged( NotifyChange.ActionAdded, dirPath);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_DIRECTORY))
+ logger.debug("Mkdir path=" + dirPath + ", finfo=" + finfo.toString());
+ }
+ }
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Mkdir Exception: " + ex.toString());
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packWccData(rpc, null);
+ packWccData(rpc, null);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Mkdir error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the create symbolic link request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procSymLink(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the create symbolic link arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ String fileName = rpc.unpackString();
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Call the disk share driver to create the symbolic link
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+
+ TreeConnection conn = getTreeConnection(sess, shareId);
+ path = getPathForHandle(sess, handle, conn);
+
+ // Check if the filesystem supports symbolic links
+
+ if ((conn.getInterface() instanceof SymbolicLinkInterface) == false) {
+
+ // Symbolic links not supported on this filesystem
+
+ rpc.buildErrorResponse(NFS.StsNotSupp);
+ packPostOpAttr(sess, null, 0, rpc);
+ packWccData(rpc, null);
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasWriteAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the symbolic link attributes
+
+ int setFlags = 0;
+ int gid = -1;
+ int uid = -1;
+ int mode = -1;
+ long fsize = -1L;
+ long atime = -1L;
+ long mtime = -1L;
+
+ // Check if the file mode has been specified
+
+ if (rpc.unpackInt() == Rpc.True) {
+ mode = rpc.unpackInt();
+ setFlags += FileInfo.SetMode;
+ }
+
+ // Check if the file owner uid has been specified
+
+ if (rpc.unpackInt() == Rpc.True) {
+ uid = rpc.unpackInt();
+ setFlags += FileInfo.SetUid;
+ }
+
+ // Check if the file group gid has been specified
+
+ if (rpc.unpackInt() == Rpc.True) {
+ gid = rpc.unpackInt();
+ setFlags += FileInfo.SetGid;
+ }
+
+ // Check if a new file size has been specified
+
+ if (rpc.unpackInt() == Rpc.True) {
+ fsize = rpc.unpackLong();
+ setFlags += FileInfo.SetFileSize;
+ }
+
+ // Check if the access date/time should be set. It may be set to a
+ // client specified time
+ // or using the server time
+
+ int setTime = rpc.unpackInt();
+
+ if (setTime == NFS.SetTimeClient) {
+ atime = (long) rpc.unpackInt();
+ atime *= 1000L;
+ rpc.skipBytes(4); // nanoseconds
+ setFlags += FileInfo.SetAccessDate;
+ }
+ else if (setTime == NFS.SetTimeServer) {
+ atime = System.currentTimeMillis();
+ setFlags += FileInfo.SetAccessDate;
+ }
+
+ // Check if the modify date/time should be set. It may be set to a
+ // client specified time
+ // or using the server time
+
+ setTime = rpc.unpackInt();
+
+ if (setTime == NFS.SetTimeClient) {
+ mtime = (long) rpc.unpackInt();
+ mtime *= 1000L;
+ rpc.skipBytes(4); // nanoseconds
+ setFlags += FileInfo.SetModifyDate;
+ }
+ else if (setTime == NFS.SetTimeServer) {
+ mtime = System.currentTimeMillis();
+ setFlags += FileInfo.SetModifyDate;
+ }
+
+ // Get the symbolic link name
+
+ String linkName = rpc.unpackString();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE))
+ logger.debug("Symbolic link request from " + rpc.getClientDetails() + ", name=" + fileName + ", link=" + linkName);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Get the pre-operation state for the parent directory
+
+ FileInfo preInfo = disk.getFileInformation(sess, conn, path);
+
+ // Build the full path string
+
+ StringBuffer str = new StringBuffer();
+ str.append(path);
+
+ if (path.endsWith("\\") == false)
+ str.append("\\");
+ str.append(fileName);
+
+ String filePath = str.toString();
+
+ // Check if the file exists
+
+ int existSts = disk.fileExists(sess, conn, filePath);
+ if (existSts == FileStatus.FileExists) {
+ errorSts = NFS.StsExist;
+ }
+ else if (existSts == FileStatus.DirectoryExists) {
+ errorSts = NFS.StsIsDir;
+ }
+ else {
+
+ // Create a new symbolic
+
+ FileOpenParams params = new FileOpenParams(filePath, FileAction.CreateNotExist, AccessMode.ReadWrite, 0, gid, uid, mode);
+ params.setSymbolicLink(linkName);
+
+ NetworkFile netFile = disk.createFile(sess, conn, params);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE))
+ logger.debug(" Symbolic link params=" + params);
+
+ // Get file information for the path
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, filePath);
+
+ if (finfo != null) {
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packFileHandle(shareId, getFileIdForHandle(handle), finfo
+ .getFileId(), rpc);
+
+ // Pack the file attributes
+
+ packPostOpAttr(sess, finfo, shareId, rpc);
+
+ // Add a cache entry for the path
+
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+ details.getFileIdCache().addPath(finfo.getFileId(), filePath);
+
+ // Add a cache entry for the network file
+
+ sess.getFileCache().addFile(netFile, conn);
+
+ // Pack the wcc data structure for the directory
+
+ packPreOpAttr(sess, preInfo, rpc);
+
+ FileInfo postInfo = disk.getFileInformation(sess, conn,
+ path);
+ packPostOpAttr(sess, postInfo, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE))
+ logger.debug("Symbolic link path=" + filePath + ", finfo=" + finfo.toString());
+ }
+ }
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("SymbolicLink Exception: " + ex.toString());
+ }
+
+ // Error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("SymLink error="
+ + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the make special device request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procMkNode(NFSSrvSession sess, RpcPacket rpc) {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_DIRECTORY))
+ logger.debug("MkNode request from " + rpc.getClientDetails());
+
+ // Return an error status
+
+ rpc.buildErrorResponse(NFS.StsNotSupp);
+ packPostOpAttr(sess, null, 0, rpc);
+ packWccData(rpc, null);
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the delete file request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procRemove(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the remove arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ String fileName = rpc.unpackString();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE))
+ logger.debug("Remove request from " + rpc.getClientDetails() + ", name=" + fileName);
+
+ // Call the disk share driver to delete the file
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasWriteAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Get the pre-operation details for the directory
+
+ FileInfo preInfo = disk.getFileInformation(sess, conn, path);
+
+ // Build the full path string
+
+ StringBuffer str = new StringBuffer();
+ str.append(path);
+ if (path.endsWith("\\") == false)
+ str.append("\\");
+ str.append(fileName);
+ String delPath = str.toString();
+
+ // Check if the file exists
+
+ int existSts = disk.fileExists(sess, conn, delPath);
+ if (existSts == FileStatus.NotExist) {
+ errorSts = NFS.StsNoEnt;
+ }
+ else if (existSts == FileStatus.DirectoryExists) {
+ errorSts = NFS.StsIsDir;
+ }
+ else {
+
+ // Get the file information for the file to be deleted
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, delPath);
+
+ // Delete the file
+
+ disk.deleteFile(sess, conn, delPath);
+
+ // Remove the path from the cache
+
+ if (finfo != null)
+ details.getFileIdCache().deletePath(finfo.getFileId());
+
+ // Get the post-operation details for the directory
+
+ FileInfo postInfo = disk.getFileInformation(sess, conn, path);
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packPreOpAttr(sess, preInfo, rpc);
+ packPostOpAttr(sess, postInfo, shareId, rpc);
+
+ // Check if there are any file/directory change notify requests
+ // active
+
+ DiskDeviceContext diskCtx = (DiskDeviceContext) conn
+ .getContext();
+ if (diskCtx.hasChangeHandler())
+ diskCtx.getChangeHandler().notifyFileChanged( NotifyChange.ActionRemoved, delPath);
+ }
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (SecurityException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("GetAttr Exception: " + ex.toString());
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packWccData(rpc, null);
+ packWccData(rpc, null);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Remove error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the remove repsonse
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the delete directory request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procRmDir(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the rmdir arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ String dirName = rpc.unpackString();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_DIRECTORY))
+ logger.debug("RmDir request from " + rpc.getClientDetails() + ", name=" + dirName);
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasWriteAccess() == false)
+ throw new AccessDeniedException();
+
+ // Build the pre-operation part of the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ // Pack the pre operation attributes for the parent directory
+
+ packPreOpAttr(sess, conn, handle, rpc);
+
+ // Get the path to be removed
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Build the full path string
+
+ StringBuffer str = new StringBuffer();
+ str.append(path);
+
+ if (path.endsWith("\\") == false)
+ str.append("\\");
+ str.append(dirName);
+
+ String delPath = str.toString();
+
+ // Check if the file exists
+
+ int existSts = disk.fileExists(sess, conn, delPath);
+ if (existSts == FileStatus.NotExist) {
+ errorSts = NFS.StsNoEnt;
+ }
+ else if (existSts == FileStatus.FileExists) {
+ errorSts = NFS.StsNoEnt;
+ }
+ else {
+
+ // Get the file information for the directory to be deleted
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, delPath);
+
+ // Delete the directory
+
+ disk.deleteDirectory(sess, conn, delPath);
+
+ // Remove the path from the cache
+
+ if (finfo != null)
+ details.getFileIdCache().deletePath(finfo.getFileId());
+
+ // Pack the post operation attributes for the parent directory
+
+ packPostOpAttr(sess, conn, handle, rpc);
+
+ // Check if there are any file/directory change notify requests
+ // active
+
+ DiskDeviceContext diskCtx = (DiskDeviceContext) conn.getContext();
+ if (diskCtx.hasChangeHandler())
+ diskCtx.getChangeHandler().notifyFileChanged( NotifyChange.ActionRemoved, delPath);
+ }
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (SecurityException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Rmdir Exception: " + ex.toString());
+ }
+
+ // Check if an error status is being returned
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packWccData(rpc, null);
+ packWccData(rpc, null);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Rmdir error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the rename file request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procRename(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the rename arguments
+
+ byte[] fromHandle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(fromHandle);
+
+ String fromName = rpc.unpackString();
+
+ byte[] toHandle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(toHandle);
+
+ String toName = rpc.unpackString();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE)) {
+ logger.debug("Rename request from " + rpc.getClientDetails() + ", fromHandle=" + NFSHandle.asString(fromHandle)
+ + ", fromname=" + fromName);
+ logger.debug(" tohandle=" + NFSHandle.asString(toHandle) + ", toname=" + toName);
+ }
+
+ // Call the disk share driver to rename the file/directory
+
+ int shareId = -1;
+ String fromPath = null;
+ String toPath = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(fromHandle);
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasWriteAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get paths from the handles
+
+ fromPath = getPathForHandle(sess, fromHandle, conn);
+ toPath = getPathForHandle(sess, toHandle, conn);
+
+ // Build the full path string for the old name
+
+ StringBuffer str = new StringBuffer();
+ str.append(fromPath);
+
+ if (fromPath.endsWith("\\") == false)
+ str.append("\\");
+ str.append(fromName);
+
+ String oldPath = str.toString();
+
+ // Build the full path string for the new name
+
+ str.setLength(0);
+ str.append(toPath);
+
+ if (toPath.endsWith("\\") == false)
+ str.append("\\");
+ str.append(toName);
+
+ String newPath = str.toString();
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice()
+ .getInterface();
+
+ // Get the pre-operation details for the parent directories
+
+ FileInfo preFromInfo = disk.getFileInformation(sess, conn, fromPath);
+ FileInfo preToInfo = null;
+
+ if (NFSHandle.unpackDirectoryId(fromHandle) == NFSHandle.unpackDirectoryId(toHandle))
+ preToInfo = preFromInfo;
+ else
+ preToInfo = disk.getFileInformation(sess, conn, toPath);
+
+ // Check if the from path exists
+
+ int existSts = disk.fileExists(sess, conn, oldPath);
+
+ if (existSts == FileStatus.NotExist) {
+ errorSts = NFS.StsNoEnt;
+ } else {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILE))
+ logger.debug("Rename from=" + oldPath + ", to=" + newPath);
+
+ // Get the file details for the file/folder being renamed
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, oldPath);
+
+ // Rename the file/directory
+
+ disk.renameFile(sess, conn, oldPath, newPath);
+
+ // Remove the original path from the cache
+
+ if (finfo != null && finfo.getFileId() != -1)
+ details.getFileIdCache().deletePath(finfo.getFileId());
+
+ // Get the file id for the new file/directory
+
+ finfo = disk.getFileInformation(sess, conn, newPath);
+ if (finfo != null)
+ details.getFileIdCache().addPath(finfo.getFileId(), newPath);
+
+ // Check if there are any file/directory change notify requests
+ // active
+
+ DiskDeviceContext diskCtx = (DiskDeviceContext) conn.getContext();
+ if (diskCtx.hasChangeHandler())
+ diskCtx.getChangeHandler().notifyRename(oldPath, newPath);
+
+ // Get the post-operation details for the parent directories
+
+ FileInfo postFromInfo = disk.getFileInformation(sess, conn, fromPath);
+ FileInfo postToInfo = null;
+
+ if (NFSHandle.unpackDirectoryId(fromHandle) == NFSHandle.unpackDirectoryId(toHandle))
+ postToInfo = postFromInfo;
+ else
+ postToInfo = disk.getFileInformation(sess, conn, toPath);
+
+ // Pack the rename response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packWccData(rpc, preFromInfo);
+ packPostOpAttr(sess, postFromInfo, shareId, rpc);
+
+ packWccData(rpc, preToInfo);
+ packPostOpAttr(sess, postToInfo, shareId, rpc);
+ }
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (SecurityException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (FileExistsException ex) {
+ errorSts = NFS.StsExist;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Rename Exception: " + ex.toString());
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+
+ // Pack the from dir WCC data
+
+ packWccData(rpc, null);
+ packWccData(rpc, null);
+
+ // Pack the to dir WCC data
+
+ packWccData(rpc, null);
+ packWccData(rpc, null);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Rename error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the rename response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the create hard link request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procLink(NFSSrvSession sess, RpcPacket rpc) {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_RXDATA))
+ logger.debug("Link request from " + rpc.getClientDetails());
+
+ // Return an error status
+
+ rpc.buildErrorResponse(NFS.StsAccess);
+ packPostOpAttr(sess, null, 0, rpc);
+ packWccData(rpc, null);
+ packWccData(rpc, null);
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the read directory request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procReadDir(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the read directory arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ long cookie = rpc.unpackLong();
+ long cookieVerf = rpc.unpackLong();
+
+ int maxCount = rpc.unpackInt();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDir request from " + rpc.getClientDetails()
+ + " handle=" + NFSHandle.asString(handle) + ", count="
+ + maxCount);
+
+ // Check if this is a share handle
+
+ int shareId = -1;
+ String path = null;
+
+ int errorSts = NFS.StsSuccess;
+
+ // Call the disk share driver to get the file information for the path
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Get the path from the handle
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // If the filesystem driver cannot convert file ids to relative
+ // paths we need to build a relative path for
+ // every file and sub-directory in the search
+
+ StringBuffer pathBuf = null;
+ int pathLen = 0;
+ FileIdCache fileCache = details.getFileIdCache();
+
+ if (details.hasFileIdSupport() == false) {
+
+ // Allocate the buffer for building the relative paths
+
+ pathBuf = new StringBuffer(256);
+ pathBuf.append(path);
+ if (path.endsWith("\\") == false)
+ pathBuf.append("\\");
+
+ // Set the length of the search path portion of the string
+
+ pathLen = pathBuf.length();
+ }
+
+ // Build the response header
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ // Get the root directory information
+
+ FileInfo dinfo = disk.getFileInformation(sess, conn, path);
+ packPostOpAttr(sess, dinfo, shareId, rpc);
+
+ // Generate the search path
+
+ String searchPath = generatePath(path, "*.*");
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDir searchPath=" + searchPath + ", cookie=" + cookie);
+
+ // Check if this is the start of a search
+
+ SearchContext search = null;
+ long searchId = -1;
+
+ if (cookie == 0) {
+
+ // Start a new search, allocate a search id
+
+ search = disk.startSearch(sess, conn, searchPath, FileAttribute.Directory + FileAttribute.Normal);
+
+ // Allocate a search id for the new search
+
+ searchId = sess.allocateSearchSlot(search);
+
+ // Set the cookie verifier
+
+ cookieVerf = dinfo.getModifyDateTime();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDir allocated searchId=" + searchId);
+ } else {
+
+ // Check if the cookie verifier is valid
+
+ if (cookieVerf != 0L && cookieVerf != dinfo.getModifyDateTime())
+ throw new BadCookieException();
+
+ // Retrieve the search from the active search cache
+
+ searchId = (cookie & COOKIE_SEARCHID_MASK) >> COOKIE_SEARCHID_SHIFT;
+
+ // Get the active search
+
+ search = sess.getSearchContext((int) searchId);
+
+ // Check if the search has been closed, if so then restart the
+ // search
+
+ if (search == null) {
+
+ // Restart the search
+
+ search = disk.startSearch(sess, conn, searchPath, FileAttribute.Directory + FileAttribute.Normal);
+
+ // Allocate a search id for the new search
+
+ searchId = sess.allocateSearchSlot(search);
+
+ // Set the cookie verifier
+
+ cookieVerf = dinfo.getModifyDateTime();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDir restarted search, searchId=" + searchId);
+ }
+
+ // Check if the search is at the required restart point
+
+ int resumeId = (int) (cookie & COOKIE_RESUMEID_MASK);
+ if (search.getResumeId() != resumeId)
+ search.restartAt(resumeId);
+ }
+
+ // Pack the cookie verifier
+
+ rpc.packLong(cookieVerf);
+
+ // Check if the search id is valid
+
+ if (searchId == -1)
+ throw new Exception("Bad search id");
+
+ // Search id is masked into the top of the file index to make the
+ // resume cookie
+
+ long searchMask = ((long) searchId) << COOKIE_SEARCHID_SHIFT;
+
+ // Build the return file list
+
+ int entCnt = 0;
+
+ // Loop until the return buffer is full or there are no more files
+
+ FileInfo finfo = new FileInfo();
+
+ // Check if this is the start of a search, if so then add the '.'
+ // and '..' entries
+
+ if (cookie == 0) {
+
+ // Add the search directory details, the '.' directory
+
+ rpc.packInt(Rpc.True);
+ rpc.packLong(dinfo.getFileIdLong() + FILE_ID_OFFSET);
+ rpc.packString(".");
+ rpc.packLong(COOKIE_DOT_DIRECTORY);
+
+ // Get the file information for the parent directory
+
+ String parentPath = generatePath(path, "..");
+ FileInfo parentInfo = disk.getFileInformation(sess, conn,
+ parentPath);
+
+ // Add the parent of the search directory, the '..' directory
+
+ rpc.packInt(Rpc.True);
+ rpc.packLong(parentInfo.getFileIdLong() + FILE_ID_OFFSET);
+ rpc.packString("..");
+ rpc.packLong(COOKIE_DOTDOT_DIRECTORY);
+
+ // Update the entry count and current used reply buffer count
+
+ entCnt = 2;
+ }
+
+ // Add file/sub-directory entries until there are no more entries or
+ // the buffer is full
+
+ boolean replyFull = false;
+
+ while (entCnt++ < maxCount && replyFull == false && search.nextFileInfo(finfo)) {
+
+ // Check if the new file entry will fit into the reply buffer
+ // without exceeding the clients maximum
+ // reply size
+
+ int entryLen = READDIR_ENTRY_LENGTH + ((finfo.getFileName().length() + 3) & 0xFFFFFFFC);
+
+ if (entryLen > rpc.getAvailableLength() || (rpc.getPosition() + entryLen > maxCount)) {
+ replyFull = true;
+ break;
+ }
+
+ // Fill in the entry details
+
+ rpc.packInt(Rpc.True);
+ rpc.packLong(finfo.getFileIdLong() + FILE_ID_OFFSET);
+ rpc.packString(finfo.getFileName());
+ rpc.packLong(search.getResumeId() + searchMask);
+
+ // Check if the relative path should be added to the file id
+ // cache
+
+ if (details.hasFileIdSupport() == false && fileCache.findPath(finfo.getFileId()) == null) {
+
+ // Create a relative path for the current file/sub-directory
+ // and add to the file id cache
+
+ pathBuf.setLength(pathLen);
+ pathBuf.append(finfo.getFileName());
+
+ fileCache.addPath(finfo.getFileId(), pathBuf.toString());
+ }
+ }
+
+ // Indicate no more file entries in this response
+
+ rpc.packInt(Rpc.False);
+
+ // Check if the search is complete
+
+ if (search.hasMoreFiles()) {
+
+ // Indicate that there are more files to be returned
+
+ rpc.packInt(Rpc.False);
+ }
+ else {
+
+ // Set the end of search flag
+
+ rpc.packInt(Rpc.True);
+
+ // Close the search, release the search slot
+
+ search.closeSearch();
+ sess.deallocateSearchSlot((int) searchId);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDir released searchId=" + searchId);
+ }
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDir return entries=" + entCnt + ", eof="
+ + search.hasMoreFiles());
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (BadCookieException ex) {
+ errorSts = NFS.StsBadCookie;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR)) {
+ logger.debug("ReadDir Exception: " + ex.toString());
+ logger.debug(ex);
+ }
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packPostOpAttr(sess, null, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("ReadDir error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the read directory response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the read directory plus request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procReadDirPlus(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the read directory arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ long cookie = rpc.unpackLong();
+ long cookieVerf = rpc.unpackLong();
+
+ int maxDir = rpc.unpackInt();
+ int maxCount = rpc.unpackInt();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDir request from " + rpc.getClientDetails()
+ + " handle=" + NFSHandle.asString(handle) + ", dir="
+ + maxDir + ", count=" + maxCount);
+
+ // Check if this is a share handle
+
+ int shareId = -1;
+ String path = null;
+
+ int errorSts = NFS.StsSuccess;
+
+ // Call the disk share driver to get the file information for the path
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ ShareDetails details = m_shareDetails.findDetails(shareId);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Get the path from the handle
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // If the filesystem driver cannot convert file ids to relative
+ // paths we need to build a relative path for
+ // every file and sub-directory in the search
+
+ StringBuffer pathBuf = null;
+ int pathLen = 0;
+ FileIdCache fileCache = details.getFileIdCache();
+
+ if (details.hasFileIdSupport() == false) {
+
+ // Allocate the buffer for building the relative paths
+
+ pathBuf = new StringBuffer(256);
+ pathBuf.append(path);
+ if (path.endsWith("\\") == false)
+ pathBuf.append("\\");
+
+ // Set the length of the search path portion of the string
+
+ pathLen = pathBuf.length();
+ }
+
+ // Build the response header
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ // Get the root directory information
+
+ FileInfo dinfo = disk.getFileInformation(sess, conn, path);
+ packPostOpAttr(sess, dinfo, shareId, rpc);
+
+ // Generate the search path
+
+ String searchPath = generatePath(path, "*.*");
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDirPlus searchPath=" + searchPath
+ + ", cookie=" + cookie);
+
+ // Check if this is the start of a search
+
+ SearchContext search = null;
+ long searchId = -1;
+
+ if (cookie == 0L) {
+
+ // Start a new search, allocate a search id
+
+ search = disk.startSearch(sess, conn, searchPath, FileAttribute.Directory + FileAttribute.Normal);
+
+ // Allocate a search id for the new search
+
+ searchId = sess.allocateSearchSlot(search);
+
+ // Set the cookie verifier
+
+ cookieVerf = dinfo.getModifyDateTime();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDirPlus allocated searchId="
+ + searchId);
+ }
+ else {
+
+ // Check if the cookie verifier is valid
+
+ if (cookieVerf != 0L && cookieVerf != dinfo.getModifyDateTime()) {
+ logger.debug("Bad cookie verifier, verf=0x"
+ + Long.toHexString(cookieVerf) + ", modTime=0x"
+ + Long.toHexString(dinfo.getModifyDateTime()));
+ throw new BadCookieException();
+ }
+
+ // Retrieve the search from the active search cache
+
+ searchId = (cookie & COOKIE_SEARCHID_MASK) >> COOKIE_SEARCHID_SHIFT;
+
+ // Get the active search
+
+ search = sess.getSearchContext((int) searchId);
+
+ // Check if the search has been closed, if so then restart the
+ // search
+
+ if (search == null) {
+
+ // Restart the search
+
+ search = disk.startSearch(sess, conn, searchPath, FileAttribute.Directory + FileAttribute.Normal);
+
+ // Allocate a search id for the new search
+
+ searchId = sess.allocateSearchSlot(search);
+
+ // Set the cookie verifier
+
+ cookieVerf = dinfo.getModifyDateTime();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDirPlus restarted search, searchId=" + searchId);
+ }
+
+ // Get the search resume id from the cookie
+
+ int resumeId = (int) (cookie & COOKIE_RESUMEID_MASK);
+ if (search != null && search.getResumeId() != resumeId)
+ search.restartAt(resumeId);
+ }
+
+ // Pack the cookie verifier
+
+ rpc.packLong(cookieVerf);
+
+ // Check if the search id is valid
+
+ if (searchId == -1)
+ throw new Exception("Bad search id");
+
+ // Search id is masked into the top of the file index to make the
+ // resume cookie
+
+ long searchMask = ((long) searchId) << COOKIE_SEARCHID_SHIFT;
+
+ // Build the return file list
+
+ int entCnt = 0;
+
+ // Loop until the return buffer is full or there are no more files
+
+ FileInfo finfo = new FileInfo();
+
+ // Check if this is the start of a search, if so then add the '.'
+ // and '..' entries
+
+ if (cookie == 0) {
+
+ // Add the search directory details, the '.' directory
+
+ rpc.packInt(Rpc.True);
+ rpc.packLong(dinfo.getFileIdLong() + FILE_ID_OFFSET);
+ rpc.packString(".");
+ rpc.packLong(COOKIE_DOT_DIRECTORY);
+
+ // Fill in the file attributes
+
+ rpc.packInt(Rpc.True);
+ packAttributes3(rpc, dinfo, shareId);
+
+ // Fill in the file handle
+
+ packDirectoryHandle(shareId, dinfo.getFileId(), rpc);
+
+ // Get the file information for the parent directory
+
+ String parentPath = generatePath(path, "..");
+ FileInfo parentInfo = disk.getFileInformation(sess, conn,
+ parentPath);
+
+ // Add the parent of the search directory, the '..' directory
+
+ rpc.packInt(Rpc.True);
+ rpc.packLong(parentInfo.getFileIdLong() + FILE_ID_OFFSET);
+ rpc.packString("..");
+ rpc.packLong(COOKIE_DOTDOT_DIRECTORY);
+
+ // Fill in the file attributes
+
+ rpc.packInt(Rpc.True);
+ packAttributes3(rpc, parentInfo, shareId);
+
+ // Fill in the file handle
+
+ packDirectoryHandle(shareId, parentInfo.getFileId(), rpc);
+
+ // Update the entry count and current used reply buffer count
+
+ entCnt = 2;
+ }
+
+ // Pack the file entries
+
+ boolean replyFull = false;
+
+ while (entCnt++ < maxDir && replyFull == false && search.nextFileInfo(finfo)) {
+
+ // Check if the new file entry will fit into the reply buffer
+ // without exceeding the clients maximum
+ // reply size
+
+ int entryLen = READDIRPLUS_ENTRY_LENGTH + ((finfo.getFileName().length() + 3) & 0xFFFFFFFC);
+
+ if (entryLen > rpc.getAvailableLength() || (rpc.getPosition() + entryLen > maxCount)) {
+ replyFull = true;
+ break;
+ }
+
+ // Fill in the entry details
+
+ rpc.packInt(Rpc.True);
+ rpc.packLong(finfo.getFileIdLong() + FILE_ID_OFFSET);
+ rpc.packString(finfo.getFileName());
+ rpc.packLong(search.getResumeId() + searchMask);
+
+ // Fill in the file attributes
+
+ rpc.packInt(Rpc.True);
+ packAttributes3(rpc, finfo, shareId);
+
+ // Fill in the file or directory handle
+
+ if (finfo.isDirectory())
+ packDirectoryHandle(shareId, finfo.getFileId(), rpc);
+ else
+ packFileHandle(shareId, dinfo.getFileId(), finfo.getFileId(), rpc);
+
+ // Check if the relative path should be added to the file id
+ // cache
+
+ if (details.hasFileIdSupport() == false && fileCache.findPath(finfo.getFileId()) == null) {
+
+ // Create a relative path for the current file/sub-directory
+ // and add to the file id cache
+
+ pathBuf.setLength(pathLen);
+ pathBuf.append(finfo.getFileName());
+
+ fileCache.addPath(finfo.getFileId(), pathBuf.toString());
+ }
+
+ // Reset the file type
+
+ finfo.setFileType(FileType.RegularFile);
+ }
+
+ // Indicate that there are no more file entries in this response
+
+ rpc.packInt(Rpc.False);
+
+ // Check if the search is complete
+
+ if (search.hasMoreFiles()) {
+
+ // Indicate that there are more files to be returned
+
+ rpc.packInt(Rpc.False);
+ } else {
+
+ // Set the end of search flag
+
+ rpc.packInt(Rpc.True);
+
+ // Close the search, release the search slot
+
+ search.closeSearch();
+ sess.deallocateSearchSlot((int) searchId);
+ }
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("ReadDirPlus return entries=" + entCnt + ", eof=" + (search.hasMoreFiles() ? false : true));
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (BadCookieException ex) {
+ errorSts = NFS.StsBadCookie;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR)) {
+ logger.debug("ReadDirPlus Exception: " + ex.toString());
+ logger.debug(ex);
+ }
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packPostOpAttr(sess, null, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("ReadDir error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the read directory plus response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the filesystem status request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procFsStat(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Get the handle from the request
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("FsInfo request from " + rpc.getClientDetails());
+
+ // Call the disk share driver to get the disk size information
+
+ int shareId = -1;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id
+
+ shareId = getShareIdFromHandle(handle);
+
+ // Get the required disk driver/tree connection
+
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the static disk information from the context, if available
+
+ DiskDeviceContext diskCtx = (DiskDeviceContext) conn.getContext();
+ SrvDiskInfo diskInfo = diskCtx.getDiskInformation();
+
+ // If we did not get valid disk information from the device context
+ // check
+ // if the driver implements the
+ // disk sizing interface
+
+ if (diskInfo == null)
+ diskInfo = new SrvDiskInfo();
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Check if the driver implements the dynamic sizing interface to
+ // get
+ // realtime disk size information
+
+ if (disk instanceof DiskSizeInterface) {
+
+ // Get the dynamic disk sizing information
+
+ DiskSizeInterface sizeInterface = (DiskSizeInterface) disk;
+ sizeInterface.getDiskInformation(diskCtx, diskInfo);
+ }
+
+ // Calculate the disk size information
+
+ // int unitSize = diskInfo.getBlockSize() *
+ // diskInfo.getBlocksPerAllocationUnit();
+
+ // Get the file details for the root directory
+
+ String rootPath = getPathForHandle(sess, handle, conn);
+ FileInfo rootInfo = disk.getFileInformation(sess, conn, rootPath);
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packPostOpAttr(sess, rootInfo, shareId, rpc);
+
+ // Calculate the total/free disk space in bytes
+
+ long totalSize = ((long) diskInfo.getDiskSizeKb()) * 1024L;
+ long freeSize = ((long) diskInfo.getDiskFreeSizeKb()) * 1024L;
+
+ // Pack the total size, free size and space available to the user
+
+ rpc.packLong(totalSize);
+ rpc.packLong(freeSize);
+ rpc.packLong(freeSize);
+
+ // Total/free file slots in the file system, assume one file per 1Kb
+ // of
+ // space
+
+ long totalSlots = diskInfo.getDiskSizeKb();
+ long freeSlots = diskInfo.getDiskFreeSizeKb();
+
+ // Pack the total slots, free slots and user slots available
+
+ rpc.packLong(totalSlots);
+ rpc.packLong(freeSlots);
+ rpc.packLong(freeSlots);
+
+ // Pack the number of seconds for which the file system in not
+ // expected to
+ // change
+
+ rpc.packInt(0);
+ }
+ catch (SecurityException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packPostOpAttr(sess, null, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("FsStat error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the filesystem information request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procFsInfo(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Get the handle from the request
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_INFO))
+ logger.debug("[NFS] FsInfo request from " + rpc.getClientDetails());
+
+ // Check if the handle is valid
+
+ if (NFSHandle.isValid(handle) == false) {
+
+ // Return an error status
+
+ rpc.buildErrorResponse(NFS.StsBadHandle);
+ return rpc;
+ }
+
+ // Build the response header
+
+ rpc.buildResponseHeader();
+
+ // Check if this is a share handle
+
+ int shareId = -1;
+ int errorSts = NFS.StsSuccess;
+
+ // Pack the filesystem information for the filesystem
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Pack the status code and post op attributes
+
+ rpc.packInt(NFS.StsSuccess);
+ packPostOpAttr(sess, conn, handle, rpc);
+
+ // Pack the filesystem information
+ //
+ // Maximum/preferred read request supported by the server
+
+ rpc.packInt(MaxReadSize);
+ rpc.packInt(PrefReadSize);
+ rpc.packInt(MultReadSize);
+
+ // Maximum/preferred write request supported by the server
+
+ rpc.packInt(MaxWriteSize);
+ rpc.packInt(PrefWriteSize);
+ rpc.packInt(MultWriteSize);
+
+ // Preferred READDIR request size
+
+ rpc.packInt(PrefReadDirSize);
+
+ // Maximum file size supported
+
+ rpc.packLong(MaxFileSize);
+
+ // Server time resolution, indicate to nearest second
+
+ rpc.packInt(1); // seconds
+ rpc.packInt(0); // nano-seconds
+
+ // Server properties, check if the filesystem supports symbolic
+ // links
+
+ int fileSysProps = NFS.FileSysHomogeneuos + NFS.FileSysCanSetTime;
+ if (conn.getInterface() instanceof SymbolicLinkInterface)
+ fileSysProps += NFS.FileSysSymLink;
+
+ rpc.packInt(fileSysProps);
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Access Exception: " + ex.toString());
+ }
+
+ // Check for an error status
+
+ if (errorSts != NFS.StsSuccess) {
+ rpc.buildErrorResponse(errorSts);
+ packPostOpAttr(sess, null, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Access error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Process the retrieve POSIX information request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procPathConf(NFSSrvSession sess, RpcPacket rpc) {
+
+ // Unpack the pathconf arguments
+
+ byte[] handle = new byte[NFS.FileHandleSize];
+ rpc.unpackByteArrayWithLength(handle);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("PathConf request from " + rpc.getClientDetails()
+ + " handle=" + NFSHandle.asString(handle));
+
+ // Call the disk share driver to get the file information for the path
+
+ int shareId = -1;
+ String path = null;
+ int errorSts = NFS.StsSuccess;
+
+ try {
+
+ // Get the share id and path
+
+ shareId = getShareIdFromHandle(handle);
+ TreeConnection conn = getTreeConnection(sess, shareId);
+
+ // Check if the session has the required access to the shared
+ // filesystem
+
+ if (conn.hasReadAccess() == false)
+ throw new AccessDeniedException();
+
+ // Get the path from the handle
+
+ path = getPathForHandle(sess, handle, conn);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Check if the file/directory exists
+
+ if (disk.fileExists(sess, conn, path) != FileStatus.NotExist) {
+
+ // Get file information for the path
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, path);
+
+ // Build the response
+
+ rpc.buildResponseHeader();
+ rpc.packInt(NFS.StsSuccess);
+
+ packPostOpAttr(sess, finfo, shareId, rpc);
+
+ // Pack the filesystem options
+
+ rpc.packInt(32767);
+ rpc.packInt(255);
+
+ rpc.packInt(Rpc.True); // truncate over size names
+ rpc.packInt(Rpc.True); // chown restricted
+ rpc.packInt(Rpc.True); // case insensitive
+ rpc.packInt(Rpc.True); // case preserving
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SEARCH))
+ logger.debug("Pathconf path=" + path + ", finfo=" + (finfo != null ? finfo.toString() : ""));
+ }
+ else {
+
+ // File does not exist
+
+ errorSts = NFS.StsNoEnt;
+ }
+ }
+ catch (BadHandleException ex) {
+ errorSts = NFS.StsBadHandle;
+ }
+ catch (StaleHandleException ex) {
+ errorSts = NFS.StsStale;
+ }
+ catch (AccessDeniedException ex) {
+ errorSts = NFS.StsAccess;
+ }
+ catch (Exception ex) {
+ errorSts = NFS.StsServerFault;
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Pathconf Exception: " + ex.toString());
+ }
+
+ // Check if an error is being returned
+
+ if (errorSts != NFS.StsSuccess) {
+
+ // Pack the error response
+
+ rpc.buildErrorResponse(errorSts);
+ packPostOpAttr(sess, null, shareId, rpc);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_ERROR))
+ logger.debug("Pathconf error=" + NFS.getStatusString(errorSts));
+ }
+
+ // Return the path information response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Commit request
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param rpc
+ * RpcPacket
+ * @return RpcPacket
+ */
+ private final RpcPacket procCommit(NFSSrvSession sess, RpcPacket rpc) {
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_FILEIO))
+ logger.debug("Commit request from " + rpc.getClientDetails());
+
+ // Pack the response
+
+ rpc.buildResponseHeader();
+
+ rpc.packInt(NFS.StsSuccess);
+ packWccData(rpc, null);
+ packPostOpAttr(sess, null, 0, rpc);
+
+ // Pack the write verifier, indicates if the server has been restarted
+ // since the file write requests
+
+ rpc.packLong(m_writeVerifier);
+
+ // Return the response
+
+ rpc.setLength();
+ return rpc;
+ }
+
+ /**
+ * Find, or create, the session for the specified RPC request.
+ *
+ * @param rpc
+ * RpcPacket
+ * @return NFSSrvSession
+ * @exception RpcAuthenticationException
+ */
+ private final NFSSrvSession findSessionForRequest(RpcPacket rpc)
+ throws RpcAuthenticationException {
+
+ // Check the authentication type and search the appropriate session
+ // table for an existing session
+
+ int authType = rpc.getCredentialsType();
+
+ // Authenticate the request
+
+ Object sessKey = getRpcAuthenticator().authenticateRpcClient(authType, rpc);
+
+ NFSSrvSession sess = null;
+
+ switch (authType) {
+
+ // Null authentication
+
+ case AuthType.Null:
+ sess = findAuthNullSession(rpc, sessKey);
+ break;
+
+ // Unix authentication
+
+ case AuthType.Unix:
+ sess = findAuthUnixSession(rpc, sessKey);
+ break;
+ }
+
+ // Setup the authentication context for the request
+
+ getRpcAuthenticator().setCurrentUser( sess, sess.getClientInformation());
+
+ // Return the server session
+
+ return sess;
+ }
+
+ /**
+ * Find, or create, a null authentication session for the specified request
+ *
+ * @param rpc
+ * RpcPacket
+ * @param sessKey
+ * Object
+ * @return NFSSrvSession
+ */
+ private final NFSSrvSession findAuthNullSession(RpcPacket rpc,
+ Object sessKey) {
+
+ // Check if the null authentication session table is valid
+
+ NFSSrvSession sess = null;
+
+ if (m_sessAuthNull != null) {
+
+ // Search for the required session using the client IP address
+
+ sess = m_sessAuthNull.findSession(sessKey);
+ }
+ else {
+
+ // Allocate the null authentication session table
+
+ m_sessAuthNull = new NFSSessionTable();
+ }
+
+ // Check if we found the required session object
+
+ if (sess == null) {
+
+ // Create a new session for the request
+
+ sess = new NFSSrvSession(this, rpc.getClientAddress(), rpc.getClientPort(), rpc.getClientProtocol());
+ sess.setAuthIdentifier(sessKey);
+
+ // Get the client information from the RPC
+
+ sess.setClientInformation(getRpcAuthenticator().getRpcClientInformation(sessKey, rpc));
+
+ // Add the new session to the session table
+
+ m_sessAuthNull.addSession(sess);
+
+ // Set the session id and debug output prefix
+
+ sess.setUniqueId("" + sessKey.hashCode());
+ sess.setDebug(getConfiguration().getNFSDebug());
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SESSION))
+ logger.debug("[NFS] Added Null session " + sess.getUniqueId());
+ }
+
+ // Return the session
+
+ return sess;
+ }
+
+ /**
+ * Find, or create, a Unix authentication session for the specified request
+ *
+ * @param rpc
+ * RpcPacket
+ * @param sessKey
+ * Object
+ * @return NFSSrvSession
+ */
+ private final NFSSrvSession findAuthUnixSession(RpcPacket rpc,
+ Object sessKey) {
+
+ // Check if the Unix authentication session table is valid
+
+ NFSSrvSession sess = null;
+
+ if (m_sessAuthUnix != null) {
+
+ // Search for the required session using the client IP address + gid
+ // + uid
+
+ sess = m_sessAuthUnix.findSession(sessKey);
+ }
+ else {
+
+ // Allocate the Unix authentication session table
+
+ m_sessAuthUnix = new NFSSessionTable();
+ }
+
+ // Check if we found the required session object
+
+ if (sess == null) {
+
+ // Create a new session for the request
+
+ sess = new NFSSrvSession(this, rpc.getClientAddress(), rpc.getClientPort(), rpc.getClientProtocol());
+ sess.setAuthIdentifier(sessKey);
+
+ // Set the session id and debug output prefix
+
+ sess.setUniqueId("" + sessKey.hashCode());
+ sess.setDebug(getConfiguration().getNFSDebug());
+
+ // Get the client information from the RPC
+
+ sess.setClientInformation(getRpcAuthenticator().getRpcClientInformation(sessKey, rpc));
+
+ // Add the new session to the session table
+
+ m_sessAuthUnix.addSession(sess);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled() && hasDebugFlag(DBG_SESSION))
+ logger.debug("[NFS] Added Unix session " + sess.getUniqueId());
+ }
+
+ // Return the session
+
+ return sess;
+ }
+
+ /**
+ * Pack the NFS v3 file attributes structure using the file information
+ *
+ * @param rpc
+ * RpcPacket
+ * @param finfo
+ * FileInfo
+ * @param fileSysId
+ * int
+ */
+ protected final void packAttributes3(RpcPacket rpc, FileInfo finfo,
+ int fileSysId) {
+
+ // Pack the NFS format file attributes
+
+ if (finfo.isDirectory()) {
+
+ // Pack the directory information
+
+ rpc.packInt(NFS.FileTypeDir);
+ if (finfo.hasMode())
+ rpc.packInt(finfo.getMode());
+ else
+ rpc.packInt(MODE_DIR_DEFAULT);
+ } else {
+
+ // Pack the file information
+
+ if (finfo.isFileType() == FileType.SymbolicLink)
+ rpc.packInt(NFS.FileTypeLnk);
+ else
+ rpc.packInt(NFS.FileTypeReg);
+
+ if (finfo.hasMode())
+ rpc.packInt(finfo.getMode());
+ else
+ rpc.packInt(MODE_FILE_DEFAULT);
+ }
+
+ // Set various Unix fields
+
+ rpc.packInt(1); // number of links
+
+ rpc.packInt(finfo.hasUid() ? finfo.getUid() : 0);
+ rpc.packInt(finfo.hasGid() ? finfo.getGid() : 0);
+
+ // Set the size for the file
+
+ if (finfo.isDirectory()) {
+
+ // Pack the directory size/allocation
+
+ rpc.packLong(512L);
+ rpc.packLong(1024L);
+ } else {
+
+ // Pack the file size/allocation
+
+ rpc.packLong(finfo.getSize());
+ if (finfo.getAllocationSize() != 0)
+ rpc.packLong(finfo.getAllocationSize());
+ else
+ rpc.packLong(finfo.getSize());
+ }
+
+ // Pack the rdev field
+
+ rpc.packInt(0); // specdata1
+ rpc.packInt(0); // specdata2
+
+ // Pack the file id
+
+ long fid = ((long) finfo.getFileId()) & 0x0FFFFFFFFL;
+ fid += FILE_ID_OFFSET;
+
+ rpc.packLong(fileSysId);
+ rpc.packLong(fid); // fid
+
+ // Pack the file times
+
+ if (finfo.hasAccessDateTime()) {
+ rpc.packInt((int) (finfo.getAccessDateTime() / 1000L));
+ rpc.packInt(0);
+ } else
+ rpc.packLong(0);
+
+ if (finfo.hasModifyDateTime()) {
+ rpc.packInt((int) (finfo.getModifyDateTime() / 1000L));
+ rpc.packInt(0);
+ } else
+ rpc.packLong(0);
+
+ if (finfo.hasChangeDateTime()) {
+ rpc.packInt((int) (finfo.getChangeDateTime() / 1000L));
+ rpc.packInt(0);
+ } else
+ rpc.packLong(0);
+ }
+
+ /**
+ * Pack a share handle
+ *
+ * @param shareName
+ * String
+ * @param rpc
+ * RpcPacket
+ */
+ protected final void packShareHandle(String shareName, RpcPacket rpc) {
+
+ // Indicate that a handle follows, pack the handle
+
+ rpc.packInt(Rpc.True);
+ NFSHandle.packShareHandle(shareName, rpc, NFS.FileHandleSize);
+ }
+
+ /**
+ * Pack a directory handle
+ *
+ * @param shareId
+ * int
+ * @param dirId
+ * int
+ * @param rpc
+ * RpcPacket
+ */
+ protected final void packDirectoryHandle(int shareId, int dirId,
+ RpcPacket rpc) {
+
+ // Indicate that a handle follows, pack the handle
+
+ rpc.packInt(Rpc.True);
+ NFSHandle.packDirectoryHandle(shareId, dirId, rpc, NFS.FileHandleSize);
+ }
+
+ /**
+ * Pack a directory handle
+ *
+ * @param shareId
+ * int
+ * @param dirId
+ * int
+ * @param fileId
+ * int
+ * @param rpc
+ * RpcPacket
+ */
+ protected final void packFileHandle(int shareId, int dirId, int fileId,
+ RpcPacket rpc) {
+
+ // Indicate that a handle follows, pack the handle
+
+ rpc.packInt(Rpc.True);
+ NFSHandle.packFileHandle(shareId, dirId, fileId, rpc,
+ NFS.FileHandleSize);
+ }
+
+ /**
+ * Get the share id from the specified handle
+ *
+ * @param handle
+ * byte[]
+ * @return int
+ * @exception BadHandleException
+ */
+ protected final int getShareIdFromHandle(byte[] handle)
+ throws BadHandleException {
+
+ // Check if this is a share handle
+
+ int shareId = NFSHandle.unpackShareId(handle);
+
+ // Check if the share id is valid
+
+ if (shareId == -1)
+ throw new BadHandleException();
+
+ // Return the share id
+
+ return shareId;
+ }
+
+ /**
+ * Get the path for the specified handle
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param handle
+ * byte[]
+ * @param tree
+ * TreeConnection
+ * @return String
+ * @exception BadHandleException
+ * @exception StaleHandleException
+ */
+ protected final String getPathForHandle(NFSSrvSession sess, byte[] handle,
+ TreeConnection tree) throws BadHandleException,
+ StaleHandleException {
+
+ // Get the share details via the share id hash
+
+ ShareDetails details = m_shareDetails
+ .findDetails(getShareIdFromHandle(handle));
+
+ // Check if this is a share handle
+
+ String path = null;
+
+ int dirId = -1;
+ int fileId = -1;
+
+ if (NFSHandle.isShareHandle(handle)) {
+
+ // Use the root path
+
+ path = "\\";
+ } else if (NFSHandle.isDirectoryHandle(handle)) {
+
+ // Get the directory id from the handle and get the associated path
+
+ dirId = NFSHandle.unpackDirectoryId(handle);
+ path = details.getFileIdCache().findPath(dirId);
+ } else if (NFSHandle.isFileHandle(handle)) {
+
+ // Get the file id from the handle and get the associated path
+
+ fileId = NFSHandle.unpackFileId(handle);
+ path = details.getFileIdCache().findPath(fileId);
+ } else
+ throw new BadHandleException();
+
+ // Check if the path is valid. The path may not be valid if the server has
+ // been restarted as the file id cache will not contain the required path.
+
+ if (path == null) {
+
+ // Check if the filesystem driver supports converting file ids to
+ // paths
+
+ if (details.hasFileIdSupport()) {
+
+ // Get the file and directory ids from the handle
+
+ dirId = NFSHandle.unpackDirectoryId(handle);
+ fileId = NFSHandle.unpackFileId(handle);
+
+ // If the file id is not valid the handle is to a directory, use the
+ // directory id as the file id
+
+ if (fileId == -1) {
+ fileId = dirId;
+ dirId = -1;
+ }
+
+ // Convert the file id to a path
+
+ FileIdInterface fileIdInterface = (FileIdInterface) tree
+ .getInterface();
+ try {
+
+ // Convert the file id to a path
+
+ path = fileIdInterface.buildPathForFileId(sess, tree,
+ dirId, fileId);
+
+ // Add the path to the cache
+
+ details.getFileIdCache().addPath(fileId, path);
+ } catch (FileNotFoundException ex) {
+ }
+ } else if (NFSHandle.isDirectoryHandle(handle) && dirId == 0) {
+
+ // Path is the root directory
+
+ path = "\\";
+
+ // Add an entry to the cache
+
+ details.getFileIdCache().addPath(dirId, path);
+ }
+ }
+
+ // Check if the path is valid, filesystem driver may not support converting
+ // file ids to paths or the file/directory may have been deleted.
+
+ if (path == null)
+ throw new StaleHandleException();
+
+ // Return the path
+
+ return path;
+ }
+
+ /**
+ * Get the file id from the specified handle
+ *
+ * @param handle
+ * byte[]
+ * @return String
+ * @exception BadHandleException
+ */
+ protected final int getFileIdForHandle(byte[] handle)
+ throws BadHandleException {
+
+ // Check the handle type
+
+ int fileId = -1;
+
+ if (NFSHandle.isShareHandle(handle)) {
+
+ // Root file id
+
+ fileId = 0;
+ }
+ else if (NFSHandle.isDirectoryHandle(handle)) {
+
+ // Get the directory id from the handle
+
+ fileId = NFSHandle.unpackDirectoryId(handle);
+ }
+ else if (NFSHandle.isFileHandle(handle)) {
+
+ // Get the file id from the handle
+
+ fileId = NFSHandle.unpackFileId(handle);
+ }
+
+ // Check if the file id is valid
+
+ if (fileId == -1)
+ throw new BadHandleException();
+
+ // Return the file id
+
+ return fileId;
+ }
+
+ /**
+ * Find, or open, the required network file using the file handle
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param handle
+ * byte[]
+ * @param conn
+ * TreeConnection
+ * @param readOnly
+ * boolean
+ * @return NetworkFile
+ * @exception BadHandleException
+ * If the handle is not valid
+ * @exception StaleHandleException
+ * If the file id cannot be converted to a path
+ */
+ protected final NetworkFile getNetworkFileForHandle(NFSSrvSession sess,
+ byte[] handle, TreeConnection conn, boolean readOnly)
+ throws BadHandleException, StaleHandleException {
+
+ // Check if the handle is a file handle
+
+ if (NFSHandle.isFileHandle(handle) == false)
+ throw new BadHandleException("Not a file handle");
+
+ // Get the file id from the handle
+
+ int fileId = getFileIdForHandle(handle);
+
+ // Get the per session network file cache, use this to synchronize
+
+ NetworkFileCache fileCache = sess.getFileCache();
+ NetworkFile file = null;
+
+ synchronized (fileCache) {
+
+ // Check the file cache, file may already be open
+
+ file = fileCache.findFile(fileId);
+ if (file == null) {
+
+ // Get the path for the file
+
+ String path = getPathForHandle(sess, handle, conn);
+ if (path == null)
+ throw new StaleHandleException();
+
+ try {
+
+ // Get the disk interface from the connection
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Open the network file
+
+ FileOpenParams params = new FileOpenParams(path, FileAction.OpenIfExists, AccessMode.ReadWrite, 0);
+ file = disk.openFile(sess, conn, params);
+
+ // Add the file to the active file cache
+
+ if (file != null)
+ fileCache.addFile(file, conn);
+ }
+ catch (AccessDeniedException ex) {
+ if (hasDebug())
+ logger.debug(ex);
+ }
+ catch (Exception ex) {
+ logger.debug(ex);
+ }
+ }
+ }
+
+ // Return the network file
+
+ return file;
+ }
+
+ /**
+ * Return the tree connection for the specified share index
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param shareId
+ * int
+ * @return TreeConnection
+ * @exception BadHandleException
+ */
+ protected final TreeConnection getTreeConnection(NFSSrvSession sess,
+ int shareId) throws BadHandleException {
+
+ // Get the required tree connection from the session
+
+ TreeConnection conn = sess.findConnection(shareId);
+ if (conn == null) {
+
+ // Get a template tree connection from the global list
+
+ TreeConnection template = m_connections.findConnection(shareId);
+ if (template == null) {
+
+ // Check if any new shares have been added and try to find the
+ // required connection again
+
+ if (checkForNewShares() > 0)
+ template = m_connections.findConnection(shareId);
+ }
+
+ // Matching tree connection not found, handle is not valid
+
+ if (template == null)
+ throw new BadHandleException();
+
+ // Check if there is an access control manager configured
+
+ if (hasAccessControlManager()) {
+
+ // Check if the session has access to the shared filesystem
+
+ AccessControlManager aclMgr = getAccessControlManager();
+
+ int sharePerm = aclMgr.checkAccessControl(sess, template
+ .getSharedDevice());
+
+ if (sharePerm == AccessControl.NoAccess) {
+
+ // Session does not have access to the shared filesystem,
+ // mount should
+ // have failed or permissions may have changed.
+
+ throw new BadHandleException();
+ } else if (sharePerm == AccessControl.Default)
+ sharePerm = AccessControl.ReadWrite;
+
+ // Create a new tree connection from the template
+
+ conn = new TreeConnection(template.getSharedDevice());
+ conn.setPermission(sharePerm);
+
+ // Add the tree connection to the active list for the session
+
+ sess.addConnection(conn);
+ }
+ }
+
+ // Return the tree connection
+
+ return conn;
+ }
+
+ /**
+ * Pack a weak cache consistency structure
+ *
+ * @param rpc
+ * RpcPacket
+ * @param finfo
+ * FileInfo
+ */
+ protected final void packWccData(RpcPacket rpc, FileInfo finfo) {
+
+ // Pack the weak cache consistency data
+
+ if (finfo != null) {
+
+ // Indicate that data follows
+
+ rpc.packInt(Rpc.True);
+
+ // Pack the file size
+
+ if (finfo.isDirectory())
+ rpc.packLong(512L);
+ else
+ rpc.packLong(finfo.getSize());
+
+ // Pack the file times
+
+ if (finfo.hasModifyDateTime()) {
+ rpc.packInt((int) (finfo.getModifyDateTime() / 1000L));
+ rpc.packInt(0);
+ } else
+ rpc.packLong(0);
+
+ if (finfo.hasChangeDateTime()) {
+ rpc.packInt((int) (finfo.getChangeDateTime() / 1000L));
+ rpc.packInt(0);
+ } else
+ rpc.packLong(0);
+ } else
+ rpc.packInt(Rpc.False);
+ }
+
+ /**
+ * Check if a file path contains any directory components
+ *
+ * @param fpath
+ * String
+ * @return boolean
+ */
+ protected final boolean pathHasDirectories(String fpath) {
+
+ // Check if the file path is valid
+
+ if (fpath == null || fpath.length() == 0)
+ return false;
+
+ // Check if the file path starts with a directory component
+
+ if (fpath.startsWith("\\") || fpath.startsWith("/")
+ || fpath.startsWith(".."))
+ return true;
+
+ // Check if the file path contains directory components
+
+ if (fpath.indexOf("\\") != -1 || fpath.indexOf("/") != -1)
+ return true;
+
+ // File path does not have any directory components
+
+ return false;
+ }
+
+ /**
+ * Pack the pre operation weak cache consistency data for the specified
+ * file/directory
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param finfo
+ * FileInfo
+ * @param rpc
+ * RpcPacket
+ */
+ protected final void packPreOpAttr(NFSSrvSession sess, FileInfo finfo,
+ RpcPacket rpc) {
+
+ // Pack the file information
+
+ if (finfo != null)
+ packWccData(rpc, finfo);
+ else
+ rpc.packInt(Rpc.False);
+ }
+
+ /**
+ * Pack the pre operation weak cache consistency data for the specified
+ * file/directory
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param conn
+ * TreeConnection
+ * @param fhandle
+ * byte[]
+ * @param rpc
+ * RpcPacket
+ * @throws BadHandleException
+ * @throws StaleHandleException
+ * @throws InvalidDeviceInterfaceException
+ * @throws IOException
+ */
+ protected final void packPreOpAttr(NFSSrvSession sess, TreeConnection conn,
+ byte[] fhandle, RpcPacket rpc) throws BadHandleException,
+ StaleHandleException, InvalidDeviceInterfaceException, IOException {
+
+ // Get the path
+
+ String path = getPathForHandle(sess, fhandle, conn);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Get the file information for the path
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, path);
+
+ // Pack the file information
+
+ packWccData(rpc, finfo);
+ }
+
+ /**
+ * Pack the post operation weak cache consistency data for the specified
+ * file/directory
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param conn
+ * TreeConnection
+ * @param fhandle
+ * byte[]
+ * @param rpc
+ * RpcPacket
+ * @throws BadHandleException
+ * @throws StaleHandleException
+ * @throws InvalidDeviceInterfaceException
+ * @throws IOException
+ */
+ protected final void packPostOpAttr(NFSSrvSession sess,
+ TreeConnection conn, byte[] fhandle, RpcPacket rpc)
+ throws BadHandleException, StaleHandleException,
+ InvalidDeviceInterfaceException, IOException {
+
+ // Get the path
+
+ String path = getPathForHandle(sess, fhandle, conn);
+
+ // Get the disk interface from the disk driver
+
+ DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
+
+ // Get the file information for the path
+
+ FileInfo finfo = disk.getFileInformation(sess, conn, path);
+
+ // Pack the file information
+
+ if (finfo != null) {
+ rpc.packInt(Rpc.True);
+ packAttributes3(rpc, finfo, getShareIdFromHandle(fhandle));
+ } else
+ rpc.packInt(Rpc.False);
+ }
+
+ /**
+ * Pack the post operation weak cache consistency data for the specified
+ * file/directory
+ *
+ * @param sess
+ * NFSSrvSession
+ * @param finfo
+ * FileInfo
+ * @param fileSysId
+ * int
+ * @param rpc
+ * RpcPacket
+ */
+ protected final void packPostOpAttr(NFSSrvSession sess, FileInfo finfo,
+ int fileSysId, RpcPacket rpc) {
+
+ // Pack the file information
+
+ if (finfo != null) {
+
+ // Pack the post operation attributes
+
+ rpc.packInt(Rpc.True);
+ packAttributes3(rpc, finfo, fileSysId);
+ } else
+ rpc.packInt(Rpc.False);
+ }
+
+ /**
+ * Generate a share relative path from the directory path and argument path.
+ * The argument path may contain the value '..' in which case the directory
+ * path will be stipped back one level.
+ *
+ * @param dirPath
+ * String
+ * @param argPath
+ * String
+ * @return String
+ */
+ protected final String generatePath(String dirPath, String argPath) {
+
+ // If the argument path is '..', if so then strip the directory path
+ // back a
+ // level
+
+ StringBuffer pathBuf = new StringBuffer();
+
+ if (argPath.equals("..")) {
+
+ // Split the path into component directories
+
+ String[] dirs = FileName.splitAllPaths(dirPath);
+
+ // Rebuild the path without the last directory
+
+ pathBuf.append("\\");
+ int dirCnt = dirs.length - 1;
+
+ if (dirCnt > 0) {
+
+ // Add the paths
+
+ for (int i = 0; i < dirCnt; i++) {
+ pathBuf.append(dirs[i]);
+ pathBuf.append("\\");
+ }
+ }
+
+ // Remove the trailing slash
+
+ if (pathBuf.length() > 1)
+ pathBuf.setLength(pathBuf.length() - 1);
+ } else {
+
+ // Add the share relative path
+
+ pathBuf.append(dirPath);
+ if (dirPath.endsWith("\\") == false)
+ pathBuf.append("\\");
+ pathBuf.append(argPath);
+ }
+
+ // Return the path
+
+ return pathBuf.toString();
+ }
+
+ /**
+ * Check for new shared devices and add them to the share and tree
+ * connection lists
+ *
+ * @return int
+ */
+ protected final int checkForNewShares() {
+
+ // Scan the shared device list and check for new shared devices
+
+ SharedDeviceList shareList = getConfiguration().getShareMapper().getShareList(getConfiguration().getServerName(), null, false);
+ Enumeration shares = shareList.enumerateShares();
+
+ int newShares = 0;
+
+ while (shares.hasMoreElements()) {
+
+ // Get the shared device
+
+ SharedDevice share = (SharedDevice) shares.nextElement();
+
+ // Check if it is a disk type shared device, if so then add a connection
+ // to the tree connection hash
+
+ if (share != null && share.getType() == ShareType.DISK) {
+
+ // Check if the filesystem driver has file id support
+
+ boolean fileIdSupport = false;
+ try {
+ if (share.getInterface() instanceof FileIdInterface)
+ fileIdSupport = true;
+ } catch (InvalidDeviceInterfaceException ex) {
+ }
+
+ // Check if the share is already in the share/tree connection
+ // lists
+
+ if (m_shareDetails.findDetails(share.getName()) == null) {
+
+ // Add the new share details
+
+ m_shareDetails.addDetails(new ShareDetails(share.getName(), fileIdSupport));
+ m_connections.addConnection(new TreeConnection(share));
+
+ // Update the new share count
+
+ newShares++;
+ }
+ }
+ }
+
+ // Return the count of new shares added
+
+ return newShares;
+ }
+
+ /**
+ * Return the next session id
+ *
+ * @return int
+ */
+ protected final synchronized int getNextSessionId() {
+ return m_sessId++;
+ }
+
+ /**
+ * Return the configured RPC authenticator
+ *
+ * @return RpcAuthenticator
+ */
+ protected final RpcAuthenticator getRpcAuthenticator() {
+ return m_rpcAuthenticator;
+ }
+
+ /**
+ * Inform session listeners that a new session has been created
+ *
+ * @param sess
+ * SrvSession
+ */
+ protected final void fireSessionOpened(SrvSession sess) {
+ fireSessionOpenEvent(sess);
+ }
+
+ /**
+ * Inform session listeners that a session has been closed
+ *
+ * @param sess
+ * SrvSession
+ */
+ protected final void fireSessionClosed(SrvSession sess) {
+ fireSessionClosedEvent(sess);
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSSessionTable.java b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSSessionTable.java
new file mode 100644
index 0000000000..20e2e3820e
--- /dev/null
+++ b/source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSSessionTable.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.filesys.server.oncrpc.nfs;
+
+import java.util.*;
+
+/**
+ * NFS Server Session Table Class
+ *
+ * @author GKSpencer
+ */
+public class NFSSessionTable {
+
+ // Session list
+
+ private Hashtable