NFS server support added to filesystem server, includes mount and portmapper services plus an NFS v3 server.

Not enabled or wired in yet.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4742 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gary Spencer
2007-01-05 15:07:18 +00:00
parent 91ad3dde7d
commit 225f69de01
53 changed files with 16444 additions and 46 deletions

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
/**
* Bad Cookie Exception Class
*
* @author GKSpencer
*/
public class BadCookieException extends Exception {
// Object version id
private static final long serialVersionUID = -6689748925525944869L;
/**
* Default constructor
*/
public BadCookieException() {
super();
}
/**
* Class constructor
*
* @param msg String
*/
public BadCookieException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
/**
* Bad Handle Exception Class
*
* @author GKSpencer
*/
public class BadHandleException extends Exception {
// Object version id
private static final long serialVersionUID = 5928520475130958599L;
/**
* Default constructor
*/
public BadHandleException() {
super();
}
/**
* Class constructor
*
* @param msg String
*/
public BadHandleException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
import java.util.*;
/**
* File Id Cache Class
*
* <p>Converts a file/directory id to a share relative path.
*
* @author GKSpencer
*/
public class FileIdCache {
// File id to path cache
private Hashtable<Integer, String> m_idCache;
/**
* Default constructor
*/
public FileIdCache() {
m_idCache = new Hashtable<Integer, String>();
}
/**
* Add an entry to the cache
*
* @param fid int
* @param path String
*/
public final void addPath(int fid, String path) {
m_idCache.put(new Integer(fid), path);
}
/**
* Convert a file id to a path
*
* @param fid int
* @return String
*/
public final String findPath(int fid) {
return (String) m_idCache.get(new Integer(fid));
}
/**
* Delete an entry from the cache
*
* @param fid int
*/
public final void deletePath(int fid) {
m_idCache.remove(new Integer(fid));
}
}

View File

@@ -0,0 +1,267 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
/**
* NFS Server Constants Class
*
* @author GKSpencer
*/
public final class NFS {
// Default NFS server port
public static final int DefaultPort = 2049;
// Program and version id
public static final int ProgramId = 100003;
public static final int VersionId = 3;
// RPC procedure ids
public static final int ProcNull = 0;
public static final int ProcGetAttr = 1;
public static final int ProcSetAttr = 2;
public static final int ProcLookup = 3;
public static final int ProcAccess = 4;
public static final int ProcReadLink = 5;
public static final int ProcRead = 6;
public static final int ProcWrite = 7;
public static final int ProcCreate = 8;
public static final int ProcMkDir = 9;
public static final int ProcSymLink = 10;
public static final int ProcMkNode = 11;
public static final int ProcRemove = 12;
public static final int ProcRmDir = 13;
public static final int ProcRename = 14;
public static final int ProcLink = 15;
public static final int ProcReadDir = 16;
public static final int ProcReadDirPlus = 17;
public static final int ProcFsStat = 18;
public static final int ProcFsInfo = 19;
public static final int ProcPathConf = 20;
public static final int ProcCommit = 21;
public static final int ProcMax = 21;
// NFS server status codes
public static final int StsSuccess = 0;
public static final int StsPerm = 1;
public static final int StsNoEnt = 2;
public static final int StsIO = 5;
public static final int StsNxIO = 6;
public static final int StsAccess = 13;
public static final int StsExist = 17;
public static final int StsXDev = 18;
public static final int StsNoDev = 19;
public static final int StsNotDir = 20;
public static final int StsIsDir = 21;
public static final int StsInVal = 22;
public static final int StsFBig = 27;
public static final int StsNoSpc = 28;
public static final int StsROFS = 30;
public static final int StsMLink = 31;
public static final int StsNameTooLong = 63;
public static final int StsNotEmpty = 66;
public static final int StsDQuot = 69;
public static final int StsStale = 70;
public static final int StsRemote = 71;
public static final int StsBadHandle = 10001;
public static final int StsNotSync = 10002;
public static final int StsBadCookie = 10003;
public static final int StsNotSupp = 10004;
public static final int StsTooSmall = 10005;
public static final int StsServerFault = 10006;
public static final int StsBadType = 10007;
public static final int StsJukeBox = 10008;
// Data structure limits
public static final int FileHandleSize = 32; // can be 64 for NFS v3
public static final int WriteVerfSize = 8;
public static final int CreateVerfSize = 8;
public static final int CookieVerfSize = 8;
// File types
public static final int FileTypeReg = 1;
public static final int FileTypeDir = 2;
public static final int FileTypeBlk = 3;
public static final int FileTypeChr = 4;
public static final int FileTypeLnk = 5;
public static final int FileTypeSock = 6;
public static final int FileTypeFifo = 7;
// Filesystem properties
public static final int FileSysLink = 0x0001; // supports hard links
public static final int FileSysSymLink = 0x0002; // supports symbolic links
public static final int FileSysHomogeneuos = 0x0004; // PATHCONF valid for all files
public static final int FileSysCanSetTime = 0x0008; // can set time on server side
// Access mask
public static final int AccessRead = 0x0001;
public static final int AccessLookup = 0x0002;
public static final int AccessModify = 0x0004;
public static final int AccessExtend = 0x0008;
public static final int AccessDelete = 0x0010;
public static final int AccessExecute = 0x0020;
public static final int AccessAll = 0x003F;
// Create mode values
public static final int CreateUnchecked = 1;
public static final int CreateGuarded = 2;
public static final int CreateExclusive = 3;
// Write request stable values
public static final int WriteUnstable = 0;
public static final int WriteDataSync = 1;
public static final int WriteFileSync = 2;
// Set attributes file timestamp settings
public static final int DoNotSetTime = 0;
public static final int SetTimeServer = 1;
public static final int SetTimeClient = 2;
// RPC procedure names
private static final String[] _procNames = { "Null", "GetAttr", "SetAttr",
"Lookup", "Access", "ReadLink", "Read", "Write", "Create", "MkDir",
"SymLink", "MkNode", "Remove", "RmDir", "Rename", "Link",
"ReadDir", "ReadDirPlus", "FsStat", "FsInfo", "PathConf", "Commit" };
/**
* Return a procedure id as a name
*
* @param id
* int
* @return String
*/
public final static String getProcedureName(int id) {
if (id < 0 || id > ProcMax)
return null;
return _procNames[id];
}
/**
* Return an error status string for the specified status code
*
* @param sts
* int
* @return String
*/
public static final String getStatusString(int sts) {
String str = null;
switch (sts) {
case StsSuccess:
str = "Success status";
break;
case StsAccess:
str = "Access denied";
break;
case StsBadCookie:
str = "Bad cookie";
break;
case StsBadHandle:
str = "Bad handle";
break;
case StsBadType:
str = "Bad type";
break;
case StsDQuot:
str = "Quota exceeded";
break;
case StsPerm:
str = "No permission";
break;
case StsExist:
str = "Already exists";
break;
case StsFBig:
str = "File too large";
break;
case StsInVal:
str = "Invalid argument";
break;
case StsIO:
str = "I/O error";
break;
case StsIsDir:
str = "Is directory";
break;
case StsJukeBox:
str = "Jukebox";
break;
case StsMLink:
str = "Too many hard links";
break;
case StsNameTooLong:
str = "Name too long";
break;
case StsNoDev:
str = "No such device";
break;
case StsNoEnt:
str = "No entity";
break;
case StsNoSpc:
str = "No space left on device";
break;
case StsNotSync:
str = "Update synchronization mismatch";
break;
case StsNotDir:
str = "Not directory";
break;
case StsNotEmpty:
str = "Not empty";
break;
case StsNotSupp:
str = "Not supported";
break;
case StsNxIO:
str = "Nxio";
break;
case StsRemote:
str = "Too many levels of remote in path";
break;
case StsROFS:
str = "Readonly filesystem";
break;
case StsServerFault:
str = "Server fault";
break;
case StsStale:
str = "Stale";
break;
case StsTooSmall:
str = "Too small";
break;
case StsXDev:
str = "Cross device hard link attempted";
break;
}
return str;
}
}

View File

@@ -0,0 +1,417 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
import org.alfresco.filesys.server.oncrpc.RpcPacket;
import org.alfresco.filesys.util.DataPacker;
/**
* NFS Handle Class
*
* <p>Contains constants and static methods used with NFS handles.
*
* @author GKSpencer
*/
public class NFSHandle {
// Version
public static final byte VERSION = 1;
public static final byte MIN_VERSION = 1;
public static final byte MAX_VERSION = 1;
// Handle types
public static final byte TYPE_SHARE = 1;
public static final byte TYPE_DIR = 2;
public static final byte TYPE_FILE = 3;
// Offsets to fields within the handle
private static final int VERSION_OFFSET = 0;
private static final int TYPE_OFFSET = 1;
private static final int SHARE_OFFSET = 2;
private static final int DIR_OFFSET = 6;
private static final int FILE_OFFSET = 10;
private static final int NAME_OFFSET = 14;
/**
* Return the handle version
*
* @param handle byte[]
*/
public static final int isVersion(byte[] handle)
{
return (int) handle[0];
}
/**
* Return the handle type
*
* @param handle byte[]
* @return int
*/
public static final int isType(byte[] handle)
{
return (int) handle[1];
}
/**
* Check if the handle is a share type handle
*
* @param handle byte[]
* @return boolean
*/
public static final boolean isShareHandle(byte[] handle)
{
if (handle[1] == TYPE_SHARE)
return true;
return false;
}
/**
* Check if the handle is a directory type handle
*
* @param handle byte[]
* @return boolean
*/
public static final boolean isDirectoryHandle(byte[] handle)
{
if (handle[1] == TYPE_DIR)
return true;
return false;
}
/**
* Check if the handle is a file type handle
*
* @param handle byte[]
* @return boolean
*/
public static final boolean isFileHandle(byte[] handle)
{
if (handle[1] == TYPE_FILE)
return true;
return false;
}
/**
* Pack a share handle
*
* @param name String
* @param handle byte[]
*/
public static final void packShareHandle(String name, byte[] handle)
{
// Pack a share handle
handle[0] = VERSION;
handle[1] = TYPE_SHARE;
// Pack the hash code of the share name
DataPacker.putInt(name.hashCode(), handle, SHARE_OFFSET);
// Null pad the handle
int pos = SHARE_OFFSET + 4;
while (pos < handle.length)
handle[pos++] = 0;
}
/**
* Pack a share handle
*
* @param name String
* @param rpc RpcPacket
* @param hlen int
*/
public static final void packShareHandle(String name, RpcPacket rpc, int hlen)
{
// Pack a share handle
rpc.packInt(hlen);
rpc.packByte(VERSION);
rpc.packByte(TYPE_SHARE);
// Pack the hash code of the share name
rpc.packInt(name.hashCode());
// Null pad the handle
rpc.packNulls(hlen - 6);
}
/**
* Pack a directory handle
*
* @param shareId int
* @param dirId int
* @param handle byte[]
*/
public static final void packDirectoryHandle(int shareId, int dirId, byte[] handle)
{
// Pack a directory handle
handle[0] = VERSION;
handle[1] = TYPE_DIR;
DataPacker.putInt(shareId, handle, 2);
DataPacker.putInt(dirId, handle, 6);
// Null pad the handle
for (int i = 10; i < handle.length; i++)
handle[i] = 0;
}
/**
* Pack a directory handle
*
* @param shareId int
* @param dirId int
* @param rpc RpcPacket
* @param hlen int
*/
public static final void packDirectoryHandle(int shareId, int dirId, RpcPacket rpc, int hlen)
{
// Pack a directory handle
rpc.packInt(hlen);
rpc.packByte(VERSION);
rpc.packByte(TYPE_DIR);
rpc.packInt(shareId);
rpc.packInt(dirId);
// Null pad the handle
rpc.packNulls(hlen - 10);
}
/**
* Pack a file handle
*
* @param shareId int
* @param dirId int
* @param fileId int
* @param handle byte[]
*/
public static final void packFileHandle(int shareId, int dirId, int fileId, byte[] handle)
{
// Pack a directory handle
handle[0] = VERSION;
handle[1] = TYPE_FILE;
DataPacker.putInt(shareId, handle, 2);
DataPacker.putInt(dirId, handle, 6);
DataPacker.putInt(fileId, handle, 10);
// Null pad the handle
for (int i = 14; i < handle.length; i++)
handle[i] = 0;
}
/**
* Pack a file handle
*
* @param shareId int
* @param dirId int
* @param fileId int
* @param rpc RpcPacket
* @param hlen int
*/
public static final void packFileHandle(int shareId, int dirId, int fileId, RpcPacket rpc, int hlen)
{
// Pack a directory handle
rpc.packInt(hlen);
rpc.packByte(VERSION);
rpc.packByte(TYPE_FILE);
rpc.packInt(shareId);
rpc.packInt(dirId);
rpc.packInt(fileId);
// Null pad the handle
rpc.packNulls(hlen - 14);
}
/**
* Unpack a share id from a handle
*
* @param handle byte[]
* @return int
*/
public static final int unpackShareId(byte[] handle)
{
// Check if the handle is a share type handle
int shareId = -1;
if (handle[1] == TYPE_SHARE || handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
{
// Unpack the share id
shareId = DataPacker.getInt(handle, 2);
}
// Return the share id, or -1 if wrong handle type
return shareId;
}
/**
* Unpack a directory id from a handle
*
* @param handle byte[]
* @return int
*/
public static final int unpackDirectoryId(byte[] handle)
{
// Check if the handle is a directory or file type handle
int dirId = -1;
if (handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
{
// Unpack the directory id
dirId = DataPacker.getInt(handle, 6);
}
// Return the directory id, or -1 if wrong handle type
return dirId;
}
/**
* Unpack a file id from a handle
*
* @param handle byte[]
* @return int
*/
public static final int unpackFileId(byte[] handle)
{
// Check if the handle is a file type handle
int fileId = -1;
if (handle[1] == TYPE_FILE)
{
// Unpack the file id
fileId = DataPacker.getInt(handle, 10);
}
// Return the file id, or -1 if wrong handle type
return fileId;
}
/**
* Return an NFS handle as a string
*
* @param handle byte[]
* @return String
*/
public static final String asString(byte[] handle)
{
// Check if the handle is a valid type
StringBuffer str = new StringBuffer();
str.append("[");
switch (handle[1])
{
// Share/mountpoint type handle
case TYPE_SHARE:
str.append("Share:0x");
str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
break;
// Directory handle
case TYPE_DIR:
str.append("Dir:share=0x");
str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
str.append(",dir=0x");
str.append(Integer.toHexString(DataPacker.getInt(handle, 6)));
break;
// File handle
case TYPE_FILE:
str.append("File:share=0x");
str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
str.append(",dir=0x");
str.append(Integer.toHexString(DataPacker.getInt(handle, 6)));
str.append(",file=0x");
str.append(Integer.toHexString(DataPacker.getInt(handle, 10)));
break;
}
// Return the handle string
str.append("]");
return str.toString();
}
/**
* Check if a handle is valid
*
* @param handle byte[]
* @return boolean
*/
public static final boolean isValid(byte[] handle)
{
// Check if the version is valid
if (handle[0] < MIN_VERSION || handle[0] > MAX_VERSION)
return false;
// Check if the handle type is valid
if (handle[1] == TYPE_SHARE || handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
return true;
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
import java.util.*;
/**
* NFS Server Session Table Class
*
* @author GKSpencer
*/
public class NFSSessionTable {
// Session list
private Hashtable<Object, NFSSrvSession> m_sessions;
/**
* Class constructor
*/
public NFSSessionTable()
{
m_sessions = new Hashtable<Object, NFSSrvSession>();
}
/**
* Return the number of sessions in the list
*
* @return int
*/
public final int numberOfSessions()
{
return m_sessions.size();
}
/**
* Add a session to the list
*
* @param sess NFSSrvSession
*/
public final void addSession(NFSSrvSession sess)
{
m_sessions.put(sess.getAuthIdentifier(), sess);
}
/**
* Find the session using the authentication identifier
*
* @param authIdent Object
* @return NFSSrvSession
*/
public final NFSSrvSession findSession(Object authIdent)
{
return (NFSSrvSession) m_sessions.get(authIdent);
}
/**
* Remove a session from the list
*
* @param sess NFSSrvSession
* @return NFSSrvSession
*/
public final NFSSrvSession removeSession(NFSSrvSession sess)
{
return removeSession(sess.getAuthIdentifier());
}
/**
* Remove a session from the list
*
* @param authIdent Object
* @return NFSSrvSession
*/
public final NFSSrvSession removeSession(Object authIdent)
{
// Find the required session
NFSSrvSession sess = findSession(authIdent);
// Remove the session and return the removed session
m_sessions.remove(authIdent);
return sess;
}
/**
* Enumerate the session ids
*
* @return Enumeration
*/
public final Enumeration<Object> enumerate()
{
return m_sessions.keys();
}
}

View File

@@ -0,0 +1,471 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
import java.net.*;
import java.util.*;
import org.alfresco.filesys.server.NetworkServer;
import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.core.DeviceInterface;
import org.alfresco.filesys.server.filesys.SearchContext;
import org.alfresco.filesys.server.filesys.TreeConnection;
import org.alfresco.filesys.server.filesys.TreeConnectionHash;
import org.alfresco.filesys.server.oncrpc.Rpc;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* NFS Server Session Class
*
* @author GKSpencer
*/
public class NFSSrvSession extends SrvSession {
// Debug logging
private static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol");
// Default and maximum number of search slots
private static final int DefaultSearches = 32;
private static final int MaxSearches = 256;
// Remote address and port
private InetAddress m_remAddr;
private int m_remPort;
// Session type (TCP or UDP)
private int m_type;
// Authentication identifier
//
// Identifies this session uniquely within the authentication type being used by the client
private Object m_authIdentifier;
// Active tree connections
private TreeConnectionHash m_connections;
// Cache of currently open files
private NetworkFileCache m_fileCache;
// Last time the session was accessed. Used to determine when to expire UDP sessions.
private long m_lastAccess;
// Active search list for this session
private SearchContext[] m_search;
private int m_searchCount;
/**
* Class constructor
*
* @param srv NetworkServer
* @param addr InetAddress
* @param port int
* @param type int
*/
public NFSSrvSession(NetworkServer srv, InetAddress addr, int port, int type)
{
super(-1, srv, "NFS", null);
// Save the remove address/port and type
m_remAddr = addr;
m_remPort = port;
m_type = type;
// Create a unique id for the session from the remote address, port and type
StringBuffer str = new StringBuffer();
str.append(type == Rpc.TCP ? "T" : "U");
str.append(m_remAddr.getHostAddress());
str.append(":");
str.append(m_remPort);
setUniqueId(str.toString());
// Set the remote name
setRemoteName(m_remAddr.getHostAddress());
// Initialize the last access date/time
setLastAccess(System.currentTimeMillis());
}
/**
* Return the session type
*
* @return int
*/
public final int isType()
{
return m_type;
}
/**
* Return the open file cache
*
* @return NetworkFileCache
*/
public final NetworkFileCache getFileCache()
{
if (m_fileCache == null)
m_fileCache = new NetworkFileCache(getUniqueId());
return m_fileCache;
}
/**
* Determine if the session has an authentication identifier
*
* @return boolean
*/
public final boolean hasAuthIdentifier()
{
return m_authIdentifier != null ? true : false;
}
/**
* Return the authentication identifier
*
* @return Object
*/
public final Object getAuthIdentifier()
{
return m_authIdentifier;
}
/**
* Return the client network address
*
* @return InetAddress
*/
public InetAddress getRemoteAddress()
{
return m_remAddr;
}
/**
* Return the remote port
*
* @return int
*/
public final int getRemotePort()
{
return m_remPort;
}
/**
* Get the last access date/time for the session
*
* @return long
*/
public final long getLastAccess()
{
return m_lastAccess;
}
/**
* Find the tree connection for the specified share hash
*
* @param shareHash int
* @return TreeConnection
*/
public final TreeConnection findConnection(int shareHash)
{
if (m_connections == null)
return null;
return m_connections.findConnection(shareHash);
}
/**
* Add a new connection to the list of active tree connections for this session
*
* @param tree TreeConnection
*/
public final void addConnection(TreeConnection tree)
{
if (m_connections == null)
m_connections = new TreeConnectionHash();
m_connections.addConnection(tree);
}
/**
* Remove a connection from the list of active tree connections for this session
*
* @param tree TreeConnection
*/
public final void removeConnection(TreeConnection tree)
{
if (m_connections == null)
return;
m_connections.deleteConnection(tree.getSharedDevice().getName());
}
/**
* Set the authentication identifier
*
* @param authIdent Object
*/
public final void setAuthIdentifier(Object authIdent)
{
m_authIdentifier = authIdent;
}
/**
* Set the last access date/time for the session
*
* @param dateTime long
*/
public final void setLastAccess(long dateTime)
{
m_lastAccess = dateTime;
}
/**
* Set the last access date/time for the session
*/
public final void setLastAccess()
{
m_lastAccess = System.currentTimeMillis();
}
/**
* Close the session, cleanup any resources.
*/
public void closeSession()
{
// Cleanup open files, tree connections and searches
cleanupSession();
// Call the base class
super.closeSession();
}
/**
* Allocate a slot in the active searches list for a new search.
*
* @param search SearchContext
* @return int Search slot index, or -1 if there are no more search slots available.
*/
protected synchronized final int allocateSearchSlot(SearchContext search)
{
// Check if the search array has been allocated
if (m_search == null)
m_search = new SearchContext[DefaultSearches];
// Find a free slot for the new search
int idx = 0;
while (idx < m_search.length && m_search[idx] != null)
idx++;
// Check if we found a free slot
if (idx == m_search.length)
{
// The search array needs to be extended, check if we reached the limit.
if (m_search.length >= MaxSearches)
return -1;
// Extend the search array
SearchContext[] newSearch = new SearchContext[m_search.length * 2];
System.arraycopy(m_search, 0, newSearch, 0, m_search.length);
m_search = newSearch;
}
// If the search context is valid then store in the allocated slot
if (search != null)
m_search[idx] = search;
// Return the allocated search slot index
m_searchCount++;
return idx;
}
/**
* Deallocate the specified search context/slot.
*
* @param ctxId int
*/
protected synchronized final void deallocateSearchSlot(int ctxId)
{
// Check if the search array has been allocated and that the index is valid
if (m_search == null || ctxId >= m_search.length)
return;
// Close the search
if (m_search[ctxId] != null)
m_search[ctxId].closeSearch();
// Free the specified search context slot
m_searchCount--;
m_search[ctxId] = null;
}
/**
* Return the NFS server that the session is associated with
*
* @return NFSServer
*/
public final NFSServer getNFSServer()
{
return (NFSServer) getServer();
}
/**
* Return the search context for the specified search id.
*
* @return com.starla.smbsrv.SearchContext
* @param srchId int
*/
protected final SearchContext getSearchContext(int srchId)
{
// Check if the search array is valid and the search index is valid
if (m_search == null || srchId >= m_search.length)
return null;
// Return the required search context
return m_search[srchId];
}
/**
* Return the number of active tree searches.
*
* @return int
*/
public final int getSearchCount()
{
return m_searchCount;
}
/**
* Store the seach context in the specified slot.
*
* @param slot Slot to store the search context.
* @param srch com.starla.smbsrv.SearchContext
*/
protected final void setSearchContext(int slot, SearchContext srch)
{
// Check if the search slot id is valid
if (m_search == null || slot > m_search.length)
return;
// Store the context
m_search[slot] = srch;
}
/**
* Cleanup any resources owned by this session, close files, searches and change notification requests.
*/
protected final void cleanupSession()
{
// Debug
if (logger.isDebugEnabled() && hasDebug(NFSServer.DBG_SESSION))
logger.debug("NFS Cleanup session, searches=" + getSearchCount() + ", files="
+ (m_fileCache != null ? m_fileCache.numberOfEntries() : 0) + ", treeConns="
+ (m_connections != null ? m_connections.numberOfEntries() : 0));
// Check if there are any active searches
if (m_search != null)
{
// Close all active searches
for (int idx = 0; idx < m_search.length; idx++)
{
// Check if the current search slot is active
if (m_search[idx] != null)
deallocateSearchSlot(idx);
}
// Release the search context list, clear the search count
m_search = null;
m_searchCount = 0;
}
// Close any open files
if (m_fileCache != null)
m_fileCache.closeAllFiles();
// Check if there are open tree connections
if (m_connections != null && m_connections.numberOfEntries() > 0)
{
// Enumerate the active connections
Enumeration conns = m_connections.enumerateConnections();
while (conns.hasMoreElements())
{
// Get the current tree connection
TreeConnection tree = (TreeConnection) conns.nextElement();
tree.closeConnection(this);
// Inform the driver that the connection has been closed
DeviceInterface devIface = tree.getInterface();
if (devIface != null)
devIface.treeClosed(this, tree);
// Release the connection list
m_connections = null;
}
}
}
}

View File

@@ -0,0 +1,444 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
import java.util.*;
import java.io.*;
import org.alfresco.filesys.server.filesys.DiskInterface;
import org.alfresco.filesys.server.filesys.NetworkFile;
import org.alfresco.filesys.server.filesys.TreeConnection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Network File Cache Class
*
* <p>Caches the network files that are currently being accessed by the NFS server.
*
* @author GKSpencer
*/
public class NetworkFileCache {
// Debug logging
private static final Log logger = LogFactory.getLog(NetworkFileCache.class);
// Default file timeout
public static final long DefaultFileTimeout = 5000L; // 5 seconds
// Network file cache, key is the file id
private Hashtable<Integer, FileEntry> m_fileCache;
// File expiry thread
private FileExpiry m_expiryThread;
// File timeout
private long m_fileTmo = DefaultFileTimeout;
// Debug enable flag
private boolean m_debug = false;
/**
* File Entry Class
*/
protected class FileEntry {
// Network file
private NetworkFile m_file;
// Disk share connection
private TreeConnection m_conn;
// File timeout
private long m_timeout;
/**
* Class constructor
*
* @param file
* NetworkFile
* @param conn
* TreeConnection
*/
public FileEntry(NetworkFile file, TreeConnection conn) {
m_file = file;
m_conn = conn;
updateTimeout();
}
/**
* Return the file timeout
*
* @return long
*/
public final long getTimeout() {
return m_timeout;
}
/**
* Return the network file
*
* @return NetworkFile
*/
public final NetworkFile getFile() {
return m_file;
}
/**
* Return the disk share connection
*
* @return TreeConnection
*/
public final TreeConnection getConnection() {
return m_conn;
}
/**
* Update the file timeout
*/
public final void updateTimeout() {
m_timeout = System.currentTimeMillis() + m_fileTmo;
}
/**
* Update the file timeout
*
* @param tmo
* long
*/
public final void updateTimeout(long tmo) {
m_timeout = tmo;
}
};
/**
* File Expiry Thread Class
*/
protected class FileExpiry implements Runnable {
// Expiry thread
private Thread m_thread;
// Wakeup interval
private long m_wakeup;
// Shutdown flag
private boolean m_shutdown;
/**
* Class Constructor
*
* @param wakeup
* long
* @param name
* String
*/
public FileExpiry(long wakeup, String name) {
// Set the wakeup interval
m_wakeup = wakeup;
// Create and start the file expiry thread
m_thread = new Thread(this);
m_thread.setDaemon(true);
m_thread.setName("NFSFileExpiry_" + name);
m_thread.start();
}
/**
* Main thread method
*/
public void run() {
// Loop until shutdown
while (m_shutdown == false) {
// Sleep for a while
try {
Thread.sleep(m_wakeup);
} catch (InterruptedException ex) {
}
// Get the current system time
long timeNow = System.currentTimeMillis();
// Check for expired files
synchronized (m_fileCache) {
// Enumerate the cache entries
Enumeration enm = m_fileCache.keys();
while (enm.hasMoreElements()) {
// Get the current key
Integer fileId = (Integer) enm.nextElement();
// Get the file entry and check if it has expired
FileEntry fentry = (FileEntry) m_fileCache.get(fileId);
if (fentry != null && fentry.getTimeout() < timeNow) {
// Get the network file
NetworkFile netFile = fentry.getFile();
// Check if the file has an I/O request pending, if
// so then reset the file expiry time
// for the file
if (netFile.hasIOPending()) {
// Update the expiry time for the file entry
fentry.updateTimeout();
// DEBUG
if (logger.isDebugEnabled())
logger.debug("NFSFileExpiry: I/O pending file=" + fentry.getFile().getFullName() + ", fid=" + fileId);
} else {
// File entry has expired, remove it from the
// cache
m_fileCache.remove(fileId);
// Close the file via the disk interface
try {
// Get the disk interface
DiskInterface disk = (DiskInterface) fentry
.getConnection().getInterface();
// Close the file
disk.closeFile(null,
fentry.getConnection(), netFile);
} catch (IOException ex) {
}
// DEBUG
if (logger.isDebugEnabled())
logger.debug("NFSFileExpiry: Closed file=" + fentry.getFile().getFullName() + ", fid=" + fileId);
}
}
}
}
}
}
/**
* Request the file expiry thread to shutdown
*/
public final void requestShutdown() {
// Set the shutdown flag
m_shutdown = true;
// Wakeup the thread
try {
m_thread.interrupt();
} catch (Exception ex) {
}
// Wait for the expiry thread to complete
try {
m_thread.join(DefaultFileTimeout);
} catch (Exception ex) {
}
}
};
/**
* Class constructor
*
* @param name
* String
*/
public NetworkFileCache(String name) {
// Create the file cache
m_fileCache = new Hashtable<Integer, FileEntry>();
// Start the file expiry thread
m_expiryThread = new FileExpiry(DefaultFileTimeout / 4, name);
}
/**
* Determine if debug output is enabled
*
* @return boolean
*/
public final boolean hasDebug() {
return m_debug;
}
/**
* Add a file to the cache
*
* @param file
* NetworkFile
* @param conn
* TreeConnection
*/
public synchronized final void addFile(NetworkFile file, TreeConnection conn) {
synchronized (m_fileCache) {
m_fileCache.put(new Integer(file.getFileId()), new FileEntry(file,
conn));
}
}
/**
* Remove a file from the cache
*
* @param id
*/
public synchronized final void removeFile(int id) {
// Create the search key
Integer fileId = new Integer(id);
synchronized (m_fileCache) {
m_fileCache.remove(fileId);
}
}
/**
* Find a file via the file id
*
* @param id
* int
* @return NetworkFile
*/
public synchronized final NetworkFile findFile(int id) {
// Create the search key
Integer fileId = new Integer(id);
FileEntry fentry = null;
synchronized (m_fileCache) {
fentry = (FileEntry) m_fileCache.get(fileId);
}
// Return the file, or null if not found
if (fentry != null) {
// Update the file timeout and return the file
fentry.updateTimeout();
return fentry.getFile();
}
// Invalid file id
return null;
}
/**
* Return the count of entries in the cache
*
* @return int
*/
public final int numberOfEntries() {
return m_fileCache.size();
}
/**
* Close the expiry cache, close and remove all files from the cache and
* stop the expiry thread.
*/
public final void closeAllFiles() {
// Enumerate the cache entries
Enumeration keys = m_fileCache.keys();
while (keys.hasMoreElements()) {
// Get the current key and lookup the matching value
Integer key = (Integer) keys.nextElement();
FileEntry entry = (FileEntry) m_fileCache.get(key);
// Expire the file entry
entry.updateTimeout(0L);
}
// Shutdown the expiry thread, this should close the files
m_expiryThread.requestShutdown();
}
/**
* Dump the cache entries to the debug device
*/
public final void dumpCache() {
// Dump the count of entries in the cache
logger.debug("NetworkFileCache entries=" + numberOfEntries());
// Enumerate the cache entries
Enumeration keys = m_fileCache.keys();
while (keys.hasMoreElements()) {
// Get the current key and lookup the matching value
Integer key = (Integer) keys.nextElement();
FileEntry entry = (FileEntry) m_fileCache.get(key);
// Dump the entry details
logger.debug("fid=" + key + ": " + entry);
}
}
}

View File

@@ -0,0 +1,366 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
import org.alfresco.filesys.server.filesys.SearchContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Search Cache Class
*
* <p>Holds the details of the active searches for the NFS server
*
* @author GKSpencer
*/
public class SearchCache {
// Debug logging
private static final Log logger = LogFactory.getLog(SearchCache.class);
// Maximum number of active searches
public static final int MaximumSearches = 255;
// Default search timeout
public static final long DefaultSearchTimeout = 30000L; // 30 seconds
// Array of active searches, last allocated index
private SearchEntry[] m_searches;
private int m_lastIdx;
// Search timeout
private long m_searchTmo = DefaultSearchTimeout;
// Debug enable flag
private boolean m_debug = true;
/**
* Search Entry Class
*/
protected class SearchEntry
{
// Search context
private SearchContext m_search;
// Search timeout
private long m_timeout;
/**
* Class constructor
*
* @param search SearchContext
*/
public SearchEntry(SearchContext search)
{
m_search = search;
updateTimeout();
}
/**
* Return the search timeout
*
* @return long
*/
public final long getTimeout()
{
return m_timeout;
}
/**
* Return the search context
*
* @return SearchContext
*/
public final SearchContext getSearch()
{
return m_search;
}
/**
* Update the search timeout
*/
public final void updateTimeout()
{
m_timeout = System.currentTimeMillis() + m_searchTmo;
}
};
/**
* Search Expiry Thread Class
*/
protected class SearchExpiry implements Runnable
{
// Expiry thread
private Thread m_thread;
// Wakeup interval
private long m_wakeup;
/**
* Class Constructor
*
* @param wakeup long
*/
public SearchExpiry(long wakeup)
{
// Set the wakeup interval
m_wakeup = wakeup;
// Create and start the search expiry thread
m_thread = new Thread(this);
m_thread.setDaemon(true);
m_thread.setName("NFSSearchExpiry");
m_thread.start();
}
/**
* Main thread method
*/
public void run()
{
// Loop until shutdown
while (true)
{
// Sleep for a while
try
{
Thread.sleep(m_wakeup);
} catch (InterruptedException ex)
{
}
// Get the current system time
long timeNow = System.currentTimeMillis();
// Check for expired searches
synchronized (m_searches)
{
// Check all allocated slots
for (int i = 0; i < m_searches.length; i++)
{
// Check if the current slot has a valid entry
if (m_searches[i] != null && m_searches[i].getTimeout() < timeNow)
{
// Remove the current search entry
SearchEntry entry = m_searches[i];
m_searches[i] = null;
// Close the search
entry.getSearch().closeSearch();
// DEBUG
if (logger.isDebugEnabled())
logger.debug("NFSSearchExpiry: Closed search=" + entry.getSearch().getSearchString()
+ ", id=" + i);
}
}
}
}
}
};
/**
* Default constructor
*/
public SearchCache()
{
// Create the active search list
m_searches = new SearchEntry[MaximumSearches];
// Start the search expiry thread
new SearchExpiry(DefaultSearchTimeout / 2);
}
/**
* Determine if debug output is enabled
*
* @return boolean
*/
public final boolean hasDebug()
{
return m_debug;
}
/**
* Allocate a search slot
*
* @param search SearchContext
* @return int
*/
public final int allocateSearchId(SearchContext search)
{
synchronized (m_searches)
{
// Search for a free slot in the search list
int cnt = 0;
while (cnt < MaximumSearches)
{
// Check if the index has wrapped
if (m_lastIdx >= MaximumSearches)
m_lastIdx = 0;
// Check if the current slot is empty
if (m_searches[m_lastIdx] == null)
{
// Use this slot
SearchEntry entry = new SearchEntry(search);
m_searches[m_lastIdx] = entry;
return m_lastIdx++;
} else
m_lastIdx++;
// Update the slot count
cnt++;
}
}
// No empty search slot found
return -1;
}
/**
* Release a search slot
*
* @param id int
*/
public final void releaseSearchId(int id)
{
// Range check the id
if (id < 0 || id >= MaximumSearches)
return;
// Delete the search entry
synchronized (m_searches)
{
m_searches[id] = null;
}
}
/**
* Return the required search context
*
* @param id int
* @return SearchContext
*/
public final SearchContext getSearch(int id)
{
// Range check the id
if (id < 0 || id >= MaximumSearches)
return null;
// Get the search entry
SearchEntry entry = null;
synchronized (m_searches)
{
entry = m_searches[id];
}
// Return the search context, if valid
if (entry != null)
{
// Update the search timeout and return the search
entry.updateTimeout();
return entry.getSearch();
}
// Invalid search
return null;
}
/**
* Dump the active search list
*/
public final void dumpSearches()
{
synchronized (m_searches)
{
// Find all active searches in the list
for (int i = 0; i < m_searches.length; i++)
{
// Check if the current search slot is active
if (m_searches[i] != null)
{
// Get the search details
SearchEntry entry = m_searches[i];
logger.debug("" + i + ": " + entry.getSearch().toString());
}
}
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
/**
* Share Details Class
*
* <p>Contains the file id cache, active search cache and tree connection details
* of a shared filesystem.
*
* @author GKSpencer
*/
public class ShareDetails {
// Share name
private String m_name;
// File id to path conversion cache
private FileIdCache m_idCache;
// Flag to indicate if the filesystem driver for this share supports file id
// lookups
// via the FileIdInterface
private boolean m_fileIdLookup;
/**
* Class constructor
*
* @param name String
* @param fileIdSupport boolean
*/
public ShareDetails(String name, boolean fileIdSupport)
{
// Save the share name
m_name = name;
// Set the file id support flag
m_fileIdLookup = fileIdSupport;
// Create the file id and search caches
m_idCache = new FileIdCache();
}
/**
* Return the share name
*
* @return String
*/
public final String getName()
{
return m_name;
}
/**
* Return the file id cache
*
* @return FileIdCache
*/
public final FileIdCache getFileIdCache()
{
return m_idCache;
}
/**
* Determine if the filesystem driver for this share has file id support
*
* @return boolean
*/
public final boolean hasFileIdSupport()
{
return m_fileIdLookup;
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
import java.util.*;
/**
* Share Details Hash Class
*
* <p>Hashtable of ShareDetails for the available disk shared devices. ShareDetails are indexed using the
* hash of the share name to allow mounts to be persistent across server restarts.
*
* @author GKSpencer
*/
public class ShareDetailsHash {
// Share name hash to share details
private Hashtable<Integer, ShareDetails> m_details;
/**
* Class constructor
*/
public ShareDetailsHash()
{
m_details = new Hashtable<Integer, ShareDetails>();
}
/**
* Add share details to the list of available shares
*
* @param details ShareDetails
*/
public final void addDetails(ShareDetails details)
{
m_details.put(new Integer(details.getName().hashCode()), details);
}
/**
* Delete share details from the list
*
* @param shareName String
* @return ShareDetails
*/
public final ShareDetails deleteDetails(String shareName)
{
return (ShareDetails) m_details.get(new Integer(shareName.hashCode()));
}
/**
* Find share details for the specified share name
*
* @param shareName String
* @return ShareDetails
*/
public final ShareDetails findDetails(String shareName)
{
// Get the share details for the associated share name
ShareDetails details = (ShareDetails) m_details.get(new Integer(shareName.hashCode()));
// Return the share details
return details;
}
/**
* Find share details for the specified share name hash code
*
* @param hashCode int
* @return ShareDetails
*/
public final ShareDetails findDetails(int hashCode)
{
// Get the share details for the associated share name
ShareDetails details = (ShareDetails) m_details.get(new Integer(hashCode));
// Return the share details
return details;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.server.oncrpc.nfs;
/**
* Stale Handle Exception Class
*
* @author GKSpencer
*/
public class StaleHandleException extends Exception {
// Object version id
private static final long serialVersionUID = -8607694363687774475L;
/**
* Default constructor
*/
public StaleHandleException()
{
super();
}
/**
* Class constructor
*
* @param msg String
*/
public StaleHandleException(String msg)
{
super(msg);
}
}