/* * 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.oncrpc.nfs; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Enumeration; import org.alfresco.jlan.debug.Debug; import org.alfresco.jlan.oncrpc.AuthType; import org.alfresco.jlan.oncrpc.MultiThreadedTcpRpcSessionHandler; import org.alfresco.jlan.oncrpc.MultiThreadedUdpRpcDatagramHandler; import org.alfresco.jlan.oncrpc.PortMapping; import org.alfresco.jlan.oncrpc.Rpc; import org.alfresco.jlan.oncrpc.RpcAuthenticationException; import org.alfresco.jlan.oncrpc.RpcAuthenticator; import org.alfresco.jlan.oncrpc.RpcNetworkServer; import org.alfresco.jlan.oncrpc.RpcPacket; import org.alfresco.jlan.oncrpc.RpcPacketPool; import org.alfresco.jlan.oncrpc.RpcProcessor; import org.alfresco.jlan.oncrpc.RpcRequestThreadPool; import org.alfresco.jlan.server.ServerListener; import org.alfresco.jlan.server.SrvSession; import org.alfresco.jlan.server.Version; import org.alfresco.jlan.server.auth.acl.AccessControl; import org.alfresco.jlan.server.auth.acl.AccessControlManager; import org.alfresco.jlan.server.config.ServerConfiguration; import org.alfresco.jlan.server.core.InvalidDeviceInterfaceException; import org.alfresco.jlan.server.core.ShareType; import org.alfresco.jlan.server.core.SharedDevice; import org.alfresco.jlan.server.core.SharedDeviceList; import org.alfresco.jlan.server.filesys.AccessDeniedException; import org.alfresco.jlan.server.filesys.AccessMode; import org.alfresco.jlan.server.filesys.DiskDeviceContext; import org.alfresco.jlan.server.filesys.DiskFullException; import org.alfresco.jlan.server.filesys.DiskInterface; import org.alfresco.jlan.server.filesys.DiskSizeInterface; import org.alfresco.jlan.server.filesys.FileAction; import org.alfresco.jlan.server.filesys.FileAttribute; import org.alfresco.jlan.server.filesys.FileExistsException; import org.alfresco.jlan.server.filesys.FileIdInterface; import org.alfresco.jlan.server.filesys.FileInfo; import org.alfresco.jlan.server.filesys.FileName; import org.alfresco.jlan.server.filesys.FileOpenParams; import org.alfresco.jlan.server.filesys.FileStatus; import org.alfresco.jlan.server.filesys.FileType; import org.alfresco.jlan.server.filesys.NetworkFile; import org.alfresco.jlan.server.filesys.NotifyChange; import org.alfresco.jlan.server.filesys.SearchContext; import org.alfresco.jlan.server.filesys.SrvDiskInfo; import org.alfresco.jlan.server.filesys.SymbolicLinkInterface; import org.alfresco.jlan.server.filesys.TreeConnection; import org.alfresco.jlan.server.filesys.TreeConnectionHash; import org.alfresco.jlan.util.HexDump; /** * NFS Server Class * * <p>Contains the main NFS server. * * @author gkspencer */ public class NFSServer extends RpcNetworkServer implements RpcProcessor { // Constants // // Server version private static final String ServerVersion = Version.NFSServerVersion; // 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 = '\\'; // Constants // // 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; // Configuration sections private NFSConfigSection m_nfsConfig; // 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 server version setVersion(ServerVersion); // Get the NFS configuration m_nfsConfig = (NFSConfigSection) config.getConfigSection( NFSConfigSection.SectionName); if ( m_nfsConfig != null) { // Set the debug flags setDebugFlags( getNFSConfiguration().getNFSDebug()); // Set the port to bind the server to if ( getNFSConfiguration().getNFSServerPort() != 0) setPort( getNFSConfiguration().getNFSServerPort()); else setPort(NFS.DefaultPort); // Set the RPC authenticator m_rpcAuthenticator = getNFSConfiguration().getRpcAuthenticator(); // Generate the write verifier m_writeVerifier = System.currentTimeMillis(); } else setEnabled( false); } /** * Return the port to bind to * * @return int */ public final int getPort() { return m_port; } /** * Return the NFS configuration section * * @return NFSConfigSection */ protected final NFSConfigSection getNFSConfiguration() { return m_nfsConfig; } /** * 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 ( getNFSConfiguration().getNFSThreadPoolSize() > 0) threadPoolSize = getNFSConfiguration().getNFSThreadPoolSize(); int packetPoolSize = DefaultPacketPoolSize; if ( getNFSConfiguration().getNFSPacketPoolSize() > 0) packetPoolSize = getNFSConfiguration().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) { Debug.println(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) { // DEBUG if ( hasDebugFlag(DBG_ERROR)) Debug.println( 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 (Debug.EnableInfo && hasDebugFlag(DBG_DUMPDATA)) Debug.println("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; 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; } // Commit/rollback a transaction that the filesystem driver may have stored in the session nfsSess.endTransaction(); // Dump the response if (Debug.EnableInfo && hasDebugFlag(DBG_DUMPDATA)) { Debug.println("NFS Resp=" + (rpc != null ? rpc.toString() : "<Null>")); HexDump.Dump(rpc.getBuffer(), rpc.getLength(), 0); } // 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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("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 (Debug.EnableError && hasDebugFlag(DBG_ERROR)) { sess.debugPrintln("GetAttr Exception: " + ex.toString()); sess.debugPrintln(ex); } } // Error status if (errorSts != NFS.StsSuccess) { // Pack the error response rpc.buildErrorResponse(errorSts); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) Debug.println("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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("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 (Debug.EnableError && hasDebugFlag(DBG_ERROR)) { sess.debugPrintln("Access3 Exception: " + ex.toString()); sess.debugPrintln( ex); } } // Check for an error status if (errorSts != NFS.StsSuccess) { rpc.buildErrorResponse(errorSts); packPostOpAttr(sess, null, shareId, rpc); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 boolean symLinks = false; if ( conn.getInterface() instanceof SymbolicLinkInterface) { // Check if symbolic links are enabled in the filesystem SymbolicLinkInterface symLinkIface = (SymbolicLinkInterface) conn.getInterface(); symLinks = symLinkIface.hasSymbolicLinksEnabled(sess, conn); } // Check if symbolic links are not supported or not enabled if ( symLinks == 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 (Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("ReadLink Exception: " + ex.toString()); } // Error status if (errorSts != NFS.StsSuccess) { // Pack the error response rpc.buildErrorResponse(errorSts); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_FILEIO)) sess.debugPrintln("[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 (Debug.EnableInfo && hasDebugFlag(DBG_FILEIO)) sess.debugPrintln("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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) { sess.debugPrintln("Read Exception: netFile=" + netFile + ", cache=" + sess.getFileCache().numberOfEntries()); Debug.println(ex); } } // Check for an error status if ( errorSts != NFS.StsSuccess) { // Pack the error response rpc.buildErrorResponse(errorSts); packPostOpAttr(sess, null, shareId, rpc); // DEBUG if ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_FILEIO)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_FILEIO)) sess.debugPrintln("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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) { sess.debugPrintln("Write Exception: netFile=" + netFile + ", cache=" + sess.getFileCache().numberOfEntries()); sess.debugPrintln(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 ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) sess.debugPrintln("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, 0); NetworkFile netFile = disk.createFile(sess, conn, params); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) sess.debugPrintln(" 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, sess); // 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 (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) sess.debugPrintln("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 (Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_DIRECTORY)) sess.debugPrintln("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, 0); // 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 (Debug.EnableInfo && hasDebugFlag(DBG_DIRECTORY)) sess.debugPrintln("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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 boolean symLinks = false; if ( conn.getInterface() instanceof SymbolicLinkInterface) { // Check if symbolic links are enabled in the filesystem SymbolicLinkInterface symLinkIface = (SymbolicLinkInterface) conn.getInterface(); symLinks = symLinkIface.hasSymbolicLinksEnabled(sess, conn); } // Check if symbolic links are not supported or not enabled if ( symLinks == 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 (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) sess.debugPrintln("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, 0); params.setSymbolicLink( linkName); NetworkFile netFile = disk.createFile(sess, conn, params); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) sess.debugPrintln(" 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, sess); // 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 (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) sess.debugPrintln("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 (Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("SymbolicLink Exception: " + ex.toString()); } // Error status if (errorSts != NFS.StsSuccess) { // Pack the error response rpc.buildErrorResponse(errorSts); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_DIRECTORY)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) sess.debugPrintln("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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_DIRECTORY)) sess.debugPrintln("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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) { sess.debugPrintln("Rename request from " + rpc.getClientDetails() + ", fromHandle=" + NFSHandle.asString(fromHandle) + ", fromname=" + fromName); sess.debugPrintln(" 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 (Debug.EnableInfo && hasDebugFlag(DBG_FILE)) sess.debugPrintln("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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_RXDATA)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("ReadDir released searchId=" + searchId); } // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("ReadDir return entries=" + (entCnt - 1) + ", 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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) { sess.debugPrintln("ReadDir Exception: " + ex.toString()); sess.debugPrintln(ex); } } // Check for an error status if ( errorSts != NFS.StsSuccess) { // Pack the error response rpc.buildErrorResponse(errorSts); packPostOpAttr(sess, null, shareId, rpc); // DEBUG if ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("ReadDirPlus allocated searchId=" + searchId); } else { // Check if the cookie verifier is valid if ( cookieVerf != 0L && cookieVerf != dinfo.getModifyDateTime()) { sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("ReadDirPlus return entries=" + (entCnt - 1) + ", 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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) { sess.debugPrintln("ReadDirPlus Exception: " + ex.toString()); sess.debugPrintln(ex); } } // Check for an error status if ( errorSts != NFS.StsSuccess) { // Pack the error response rpc.buildErrorResponse(errorSts); packPostOpAttr(sess, null, shareId, rpc); // DEBUG if ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("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 = diskInfo.getDiskSizeKb() * 1024L; long freeSize = 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 (Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_INFO)) sess.debugPrintln("[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) { // Check if symbolic links are enabled SymbolicLinkInterface symLinkIface = (SymbolicLinkInterface) conn.getInterface(); if ( symLinkIface.hasSymbolicLinksEnabled(sess, conn)) 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 (Debug.EnableError && hasDebugFlag(DBG_ERROR)) { sess.debugPrintln("FsInfo Exception: " + ex.toString()); sess.debugPrintln(ex); } } // Check for an error status if (errorSts != NFS.StsSuccess) { rpc.buildErrorResponse(errorSts); packPostOpAttr(sess, null, shareId, rpc); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("FsInfo 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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_SEARCH)) sess.debugPrintln("Pathconf path=" + path + ", finfo=" + (finfo != null ? finfo.toString() : "<null>")); } 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 ( Debug.EnableError && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 ( Debug.EnableInfo && hasDebugFlag(DBG_ERROR)) sess.debugPrintln("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 (Debug.EnableInfo && hasDebugFlag(DBG_FILEIO)) sess.debugPrintln("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; } // DEBUG if ( Debug.EnableDbg && hasDebugFlag( DBG_SESSION)) Debug.println("[NFS] Found session " + sess); // Setup the authentication context for the request try { getRpcAuthenticator().setCurrentUser( sess, sess.getClientInformation()); } catch ( Throwable ex) { sess = null; // DEBUG if (Debug.EnableError && hasDebugFlag(DBG_ERROR)) Debug.println("[NFS] RPC Authencation Exception: " + ex.toString()); } // Check if the session is valid if ( sess == null) throw new RpcAuthenticationException(Rpc.AuthBadCred); // 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.setDebugPrefix("[NFS_AN_" + getNextSessionId() + "] "); sess.setDebug(getNFSConfiguration().getNFSDebug()); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_SESSION)) Debug.println("[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.setDebugPrefix("[NFS_AU_" + getNextSessionId() + "] "); sess.setDebug(getNFSConfiguration().getNFSDebug()); // Get the client information from the RPC sess.setNFSClientInformation(getRpcAuthenticator().getRpcClientInformation(sessKey, rpc)); sess.setClientInformation( sess.getNFSClientInformation()); // Add the new session to the session table m_sessAuthUnix.addSession(sess); // DEBUG if (Debug.EnableInfo && hasDebugFlag(DBG_SESSION)) Debug.println("[NFS] Added Unix session " + sess.getUniqueId()); } else { // Set the thread local client information sess.setClientInformation( sess.getNFSClientInformation()); } // 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, sess); 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, 0); file = disk.openFile(sess, conn, params); // Add the file to the active file cache if (file != null) fileCache.addFile(file, conn, sess); } catch (AccessDeniedException ex) { if ( hasDebug()) Debug.println(ex); } catch (Exception ex) { Debug.println(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); } else { // No access control manager, allow full access to the filesystem conn = new TreeConnection(template.getSharedDevice()); conn.setPermission(AccessControl.ReadWrite); // 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 = getShareMapper().getShareList(getConfiguration().getServerName(), null, false); Enumeration<SharedDevice> shares = shareList.enumerateShares(); int newShares = 0; while (shares.hasMoreElements()) { // Get the shared device SharedDevice share = 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); } }