/* * Copyright (C) 2006-2008 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.jlan.smb.server; import java.util.Enumeration; import java.util.Hashtable; import org.alfresco.jlan.debug.Debug; import org.alfresco.jlan.server.SrvSession; import org.alfresco.jlan.server.auth.ClientInfo; import org.alfresco.jlan.server.core.DeviceInterface; import org.alfresco.jlan.server.core.SharedDevice; import org.alfresco.jlan.server.filesys.DiskInterface; import org.alfresco.jlan.server.filesys.NetworkFile; import org.alfresco.jlan.server.filesys.SearchContext; import org.alfresco.jlan.server.filesys.TooManyConnectionsException; import org.alfresco.jlan.server.filesys.TreeConnection; /** * Virtual Circuit Class * * <p>Represents an authenticated circuit on an SMB/CIFS session. There may be multiple virtual circuits opened * on a single session/socket connection. * * @author gkspencer */ public class VirtualCircuit { // 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<Integer, TreeConnection> 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; // Flag to indicate if the virtual circuit is logged on/off private boolean m_loggedOn; /** * Class constructor * * @param vcNum int * @param cInfo ClientInfo */ public VirtualCircuit( int vcNum, ClientInfo cInfo) { m_vcNum = vcNum; m_clientInfo = cInfo; m_loggedOn = true; } /** * 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<Integer, TreeConnection>(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 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 sess 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 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 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 (Debug.EnableInfo && sess.hasDebug(SMBSrvSession.DBG_STATE)) sess.debugPrintln("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<TreeConnection> enm = m_connections.elements(); while ( enm.hasMoreElements()) { // Get the current tree connection TreeConnection tree = 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(); } } } /** * Check if the virtual circuit has a valid user logged on * * @return boolean */ public final boolean isLoggedOn() { return m_loggedOn; } /** * Set the logged on status for the virtual circuit * * @param loggedOn boolean */ public final void setLoggedOn(boolean loggedOn) { m_loggedOn = loggedOn; } /** * 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(); } }