/* * 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.smb.server; import java.util.Enumeration; import java.util.Hashtable; import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.auth.ClientInfo; import org.alfresco.filesys.server.core.DeviceInterface; import org.alfresco.filesys.server.core.SharedDevice; import org.alfresco.filesys.server.filesys.DiskInterface; import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.server.filesys.SearchContext; import org.alfresco.filesys.server.filesys.TooManyConnectionsException; import org.alfresco.filesys.server.filesys.TreeConnection; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Virtual Circuit Class * *

* Represents an authenticated circuit on an SMB/CIFS session. There may be * multiple virtual circuits opened on a single session/socket connection. */ public class VirtualCircuit { // Debug logging private static Log logger = LogFactory.getLog("org.alfresco.smb.protocol"); // Default and maximum number of connection slots public static final int DefaultConnections = 4; public static final int MaxConnections = 16; // Tree ids are 16bit values private static final int TreeIdMask = 0x0000FFFF; // Default and maximum number of search slots private static final int DefaultSearches = 8; private static final int MaxSearches = 256; // Invalid UID value public static final int InvalidUID = -1; // Virtual circuit UID value // // Allocated by the server and sent by the client to identify the virtual circuit private int m_uid = -1; // Virtual circuit number private int m_vcNum; // Client information for this virtual circuit private ClientInfo m_clientInfo; // Active tree connections private Hashtable m_connections; private int m_treeId; // List of active searches private SearchContext[] m_search; private int m_searchCount; // Active transaction details private SrvTransactBuffer m_transact; /** * Class constructor * * @param vcNum * int * @param cInfo * ClientInfo */ public VirtualCircuit(int vcNum, ClientInfo cInfo) { m_vcNum = vcNum; m_clientInfo = cInfo; } /** * Return the virtual circuit UID * * @return int */ public final int getUID() { return m_uid; } /** * Return the virtual circuit number * * @return int */ public final int getVCNumber() { return m_vcNum; } /** * Return the client information * * @return ClientInfo */ public final ClientInfo getClientInformation() { return m_clientInfo; } /** * Add a new connection to this virtual circuit. Return the allocated tree * id for the new connection. * * @param shrDev SharedDevice * @return int Allocated tree id (connection id). */ public int addConnection(SharedDevice shrDev) throws TooManyConnectionsException { // Check if the connection array has been allocated if (m_connections == null) m_connections = new Hashtable(DefaultConnections); // Allocate an id for the tree connection int treeId = 0; synchronized (m_connections) { // Check if the tree connection table is full if (m_connections.size() == MaxConnections) throw new TooManyConnectionsException(); // Find a free slot in the connection array treeId = (m_treeId++ & TreeIdMask); Integer key = new Integer(treeId); while (m_connections.contains(key)) { // Try another tree id for the new connection treeId = (m_treeId++ & TreeIdMask); key = new Integer(treeId); } // Store the new tree connection m_connections.put(key, new TreeConnection(shrDev)); } // Return the allocated tree id return treeId; } /** * Return the tree connection details for the specified tree id. * * @return com.starla.smbsrv.TreeConnection * @param treeId * int */ public final TreeConnection findConnection(int treeId) { // Check if the tree id and connection array are valid if (m_connections == null) return null; // Get the required tree connection details return m_connections.get(new Integer(treeId)); } /** * Remove the specified tree connection from the active connection list. * * @param treeId * int * @param srvSession * SrvSession */ protected void removeConnection(int treeId, SrvSession sess) { // Check if the tree id is valid if (m_connections == null) return; // Close the connection and remove from the connection list synchronized (m_connections) { // Get the connection Integer key = new Integer(treeId); TreeConnection tree = m_connections.get(key); // Close the connection, release resources if (tree != null) { // Close the connection tree.closeConnection(sess); // Remove the connection from the connection list m_connections.remove(key); } } } /** * Return the active tree connection count * * @return int */ public final int getConnectionCount() { return m_connections != null ? m_connections.size() : 0; } /** * Allocate a slot in the active searches list for a new search. * * @return int Search slot index, or -1 if there are no more search slots * available. */ public final int allocateSearchSlot() { // Check if the search array has been allocated if (m_search == null) m_search = new SearchContext[DefaultSearches]; // Find a free slot for the new search int idx = 0; while (idx < m_search.length && m_search[idx] != null) idx++; // Check if we found a free slot if (idx == m_search.length) { // The search array needs to be extended, check if we reached the // limit. if (m_search.length >= MaxSearches) return -1; // Extend the search array SearchContext[] newSearch = new SearchContext[m_search.length * 2]; System.arraycopy(m_search, 0, newSearch, 0, m_search.length); m_search = newSearch; } // Return the allocated search slot index m_searchCount++; return idx; } /** * Deallocate the specified search context/slot. * * @param ctxId * int */ public final void deallocateSearchSlot(int ctxId) { // Check if the search array has been allocated and that the index is // valid if (m_search == null || ctxId >= m_search.length) return; // Close the search if (m_search[ctxId] != null) m_search[ctxId].closeSearch(); // Free the specified search context slot m_searchCount--; m_search[ctxId] = null; } /** * Return the search context for the specified search id. * * @return com.starla.smbsrv.SearchContext * @param srchId * int */ public final SearchContext getSearchContext(int srchId) { // Check if the search array is valid and the search index is valid if (m_search == null || srchId >= m_search.length) return null; // Return the required search context return m_search[srchId]; } /** * Store the seach context in the specified slot. * * @param slot * Slot to store the search context. * @param srch * com.starla.smbsrv.SearchContext */ public final void setSearchContext(int slot, SearchContext srch) { // Check if the search slot id is valid if (m_search == null || slot > m_search.length) return; // Store the context m_search[slot] = srch; } /** * Return the number of active tree searches. * * @return int */ public final int getSearchCount() { return m_searchCount; } /** * Check if there is an active transaction * * @return boolean */ public final boolean hasTransaction() { return m_transact != null ? true : false; } /** * Return the active transaction buffer * * @return TransactBuffer */ public final SrvTransactBuffer getTransaction() { return m_transact; } /** * Set the active transaction buffer * * @param buf * TransactBuffer */ public final void setTransaction(SrvTransactBuffer buf) { m_transact = buf; } /** * Set the UID for the circuit * * @param uid * int */ public final void setUID(int uid) { m_uid = uid; } /** * Close the virtual circuit, close active tree connections * * @param sess * SrvSession */ public final void closeCircuit(SrvSession sess) { // Debug if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_STATE)) logger.debug("Cleanup vc=" + getVCNumber() + ", UID=" + getUID() + ", searches=" + getSearchCount() + ", treeConns=" + getConnectionCount()); // Check if there are any active searches if (m_search != null) { // Close all active searches for (int idx = 0; idx < m_search.length; idx++) { // Check if the current search slot is active if (m_search[idx] != null) deallocateSearchSlot(idx); } // Release the search context list, clear the search count m_search = null; m_searchCount = 0; } // Check if there are open tree connections if (m_connections != null) { synchronized (m_connections) { // Close all active tree connections Enumeration enm = m_connections.elements(); while (enm.hasMoreElements()) { // Get the current tree connection TreeConnection tree = (TreeConnection) enm.nextElement(); DeviceInterface devIface = tree.getInterface(); // Check if there are open files on the share if (tree.openFileCount() > 0) { // Close the open files, release locks for (int i = 0; i < tree.getFileTableLength(); i++) { // Get an open file NetworkFile curFile = tree.findFile(i); if (curFile != null && devIface instanceof DiskInterface) { // Access the disk share interface DiskInterface diskIface = (DiskInterface) devIface; try { // Remove the file from the tree connection list tree.removeFile(i, sess); // Close the file diskIface.closeFile(sess, tree, curFile); } catch (Exception ex) { } } } } // Inform the driver that the connection has been closed if (devIface != null) devIface.treeClosed(sess, tree); } // Clear the tree connection list m_connections.clear(); } } } /** * Return the virtual circuit details as a string * * @return String */ public String toString() { StringBuffer str = new StringBuffer(); str.append("["); str.append(getVCNumber()); str.append(":"); str.append(getUID()); str.append(","); str.append(getClientInformation()); str.append(",Tree="); str.append(getConnectionCount()); str.append(",Searches="); str.append(getSearchCount()); str.append("]"); return str.toString(); } }