Commit 2e44129d authored by Tigran Mkrtchyan's avatar Tigran Mkrtchyan
Browse files

nfsv4: change AbstractNFSv4Operation interface

move exception handling into NFSServerV41 class
parent dc3137ae
......@@ -17,8 +17,11 @@
package org.dcache.chimera.nfs.v4;
import java.io.IOException;
import org.dcache.chimera.nfs.ChimeraNFSException;
import org.dcache.chimera.nfs.v4.xdr.nfs_argop4;
import org.dcache.chimera.nfs.v4.xdr.nfs_resop4;
import org.dcache.xdr.OncRpcException;
/**
*
......@@ -27,11 +30,9 @@ import org.dcache.chimera.nfs.v4.xdr.nfs_resop4;
*/
public abstract class AbstractNFSv4Operation {
protected final nfs_resop4 _result = new nfs_resop4();
protected final nfs_argop4 _args;
public AbstractNFSv4Operation(nfs_argop4 args, int opCode) {
_result.resop = opCode;
_args = args;
}
......@@ -39,5 +40,6 @@ public abstract class AbstractNFSv4Operation {
* Process current operation.
* @return result of operation
*/
public abstract nfs_resop4 process(CompoundContext context);
public abstract void process(CompoundContext context, nfs_resop4 result)
throws ChimeraNFSException, IOException, OncRpcException;
}
......@@ -105,24 +105,37 @@ public class NFSServerV41 extends nfs4_prot_NFS4_PROGRAM_ServerStub {
boolean retransmit = false;
for (nfs_argop4 op : arg1.argarray) {
context.nextOperation();
int position = context.getOperationPosition();
if (minorversion > 0) {
checkOpPosition(op.argop, position);
if (position == 1) {
/*
* at this point we already have to have a session
*/
List<nfs_resop4> cache = context.getCache();
if (cache != null) {
res.resarray.addAll(cache.subList(position, cache.size()));
res.status = statusOfLastOperation(cache);
retransmit = true;
break;
nfs_resop4 opResult = nfs_resop4.resopFor(op.argop);
try {
if (minorversion != 0) {
checkOpPosition(position, position);
if (position == 1) {
/*
* at this point we already have to have a session
*/
List<nfs_resop4> cache = context.getCache();
if (cache != null) {
res.resarray.addAll(cache.subList(position, cache.size()));
res.status = statusOfLastOperation(cache);
retransmit = true;
break;
}
}
}
_operationFactory.getOperation(op).process(context, opResult);
} catch (ChimeraNFSException e) {
opResult.setStatus(e.getStatus());
} catch (OncRpcException e) {
opResult.setStatus(nfsstat.NFSERR_BADXDR);
_log.warn("Bad xdr: {}: ", e.getMessage());
} catch (IOException e) {
opResult.setStatus(nfsstat.NFSERR_IO);
_log.warn("IO error: {}", e.getMessage());
} catch (Throwable e) {
opResult.setStatus(nfsstat.NFSERR_SERVERFAULT);
_log.error("General error: ", e);
}
nfs_resop4 opResult = _operationFactory.getOperation(op).process(context);
res.resarray.add(opResult);
res.status = opResult.getStatus();
if (res.status != nfsstat.NFS_OK) {
......
......@@ -17,6 +17,7 @@
package org.dcache.chimera.nfs.v4;
import java.io.IOException;
import org.dcache.chimera.nfs.nfsstat;
import org.dcache.chimera.nfs.v4.xdr.uint32_t;
import org.dcache.chimera.nfs.v4.xdr.nfs_argop4;
......@@ -34,79 +35,67 @@ import org.slf4j.LoggerFactory;
public class OperationACCESS extends AbstractNFSv4Operation {
private static final Logger _log = LoggerFactory.getLogger(OperationACCESS.class);
private static final Logger _log = LoggerFactory.getLogger(OperationACCESS.class);
OperationACCESS(nfs_argop4 args) {
super(args, nfs_opnum4.OP_ACCESS);
}
OperationACCESS(nfs_argop4 args) {
super(args, nfs_opnum4.OP_ACCESS);
}
@Override
public nfs_resop4 process(CompoundContext context) {
@Override
public void process(CompoundContext context, nfs_resop4 result)
throws ChimeraNFSException, IOException {
ACCESS4res res = new ACCESS4res();
final ACCESS4res res = result.opaccess;
_log.debug("NFS Request ACCESS uid: {}", context.getUser() );
_log.debug("NFS Request ACCESS uid: {}", context.getUser());
try {
int reqAccess = _args.opaccess.access.value;
Stat objStat = context.currentInode().statCache();
UnixAcl acl = new UnixAcl(objStat.getUid(), objStat.getGid(),objStat.getMode() & 0777 );
int reqAccess = _args.opaccess.access.value;
Stat objStat = context.currentInode().statCache();
UnixAcl acl = new UnixAcl(objStat.getUid(), objStat.getGid(), objStat.getMode() & 0777);
int realAccess = 0;
int realAccess = 0;
if( (reqAccess & nfs4_prot.ACCESS4_EXECUTE) == nfs4_prot.ACCESS4_EXECUTE ) {
if ( context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_EXECUTE ) ) {
realAccess |= nfs4_prot.ACCESS4_EXECUTE;
}
if ((reqAccess & nfs4_prot.ACCESS4_EXECUTE) == nfs4_prot.ACCESS4_EXECUTE) {
if (context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_EXECUTE)) {
realAccess |= nfs4_prot.ACCESS4_EXECUTE;
}
}
if( (reqAccess & nfs4_prot.ACCESS4_EXTEND) == nfs4_prot.ACCESS4_EXTEND ) {
if ( context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_INSERT ) ) {
realAccess |= nfs4_prot.ACCESS4_EXTEND;
}
if ((reqAccess & nfs4_prot.ACCESS4_EXTEND) == nfs4_prot.ACCESS4_EXTEND) {
if (context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_INSERT)) {
realAccess |= nfs4_prot.ACCESS4_EXTEND;
}
}
if( (reqAccess & nfs4_prot.ACCESS4_LOOKUP) == nfs4_prot.ACCESS4_LOOKUP ) {
if ( context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_LOOKUP ) ) {
realAccess |= nfs4_prot.ACCESS4_LOOKUP;
}
if ((reqAccess & nfs4_prot.ACCESS4_LOOKUP) == nfs4_prot.ACCESS4_LOOKUP) {
if (context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_LOOKUP)) {
realAccess |= nfs4_prot.ACCESS4_LOOKUP;
}
}
if( (reqAccess & nfs4_prot.ACCESS4_DELETE) == nfs4_prot.ACCESS4_DELETE ) {
if ( context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_DELETE ) ) {
realAccess |= nfs4_prot.ACCESS4_DELETE;
}
if ((reqAccess & nfs4_prot.ACCESS4_DELETE) == nfs4_prot.ACCESS4_DELETE) {
if (context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_DELETE)) {
realAccess |= nfs4_prot.ACCESS4_DELETE;
}
}
if( (reqAccess & nfs4_prot.ACCESS4_MODIFY) == nfs4_prot.ACCESS4_MODIFY ) {
if ( context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_WRITE ) ){
realAccess |= nfs4_prot.ACCESS4_MODIFY;
}
if ((reqAccess & nfs4_prot.ACCESS4_MODIFY) == nfs4_prot.ACCESS4_MODIFY) {
if (context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_WRITE)) {
realAccess |= nfs4_prot.ACCESS4_MODIFY;
}
}
if( (reqAccess & nfs4_prot.ACCESS4_READ) == nfs4_prot.ACCESS4_READ ) {
if ( context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_READ ) ) {
realAccess |= nfs4_prot.ACCESS4_READ;
}
if ((reqAccess & nfs4_prot.ACCESS4_READ) == nfs4_prot.ACCESS4_READ) {
if (context.getAclHandler().isAllowed(acl, context.getUser(), AclHandler.ACL_READ)) {
realAccess |= nfs4_prot.ACCESS4_READ;
}
res.resok4 = new ACCESS4resok();
res.resok4.access = new uint32_t( realAccess );
res.resok4.supported = new uint32_t( realAccess );
res.status = nfsstat.NFS_OK;
}catch(ChimeraNFSException he) {
_log.debug("ACCESS: {}", he.getMessage() );
res.status = he.getStatus();
}catch(Exception e) {
_log.error("ACCESS:", e);
res.status = nfsstat.NFSERR_RESOURCE;
}
_result.opaccess = res;
return _result;
}
res.resok4 = new ACCESS4resok();
res.resok4.access = new uint32_t(realAccess);
res.resok4.supported = new uint32_t(realAccess);
res.status = nfsstat.NFS_OK;
}
}
......@@ -37,32 +37,24 @@ public class OperationCLOSE extends AbstractNFSv4Operation {
}
@Override
public nfs_resop4 process(CompoundContext context) {
CLOSE4res res = new CLOSE4res();
public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNFSException {
final CLOSE4res res = result.opclose;
try {
Inode inode = context.currentInode();
Inode inode = context.currentInode();
if (context.getMinorversion() > 0) {
context.getSession().getClient().updateLeaseTime(NFSv4Defaults.NFS4_LEASE_TIME);
try {
context.getDeviceManager().layoutReturn(context, _args.opclose.open_stateid);
} catch (IOException e) {
_log.error("Failed to return a layout: {}", e.getMessage());
}
} else {
context.getStateHandler().updateClientLeaseTime(_args.opclose.open_stateid);
if (context.getMinorversion() > 0) {
context.getSession().getClient().updateLeaseTime(NFSv4Defaults.NFS4_LEASE_TIME);
try {
context.getDeviceManager().layoutReturn(context, _args.opclose.open_stateid);
} catch (IOException e) {
_log.error("Failed to return a layout: {}", e.getMessage());
}
} else {
context.getStateHandler().updateClientLeaseTime(_args.opclose.open_stateid);
}
res.open_stateid = Stateids.invalidStateId();
res.status = nfsstat.NFS_OK;
res.open_stateid = Stateids.invalidStateId();
res.status = nfsstat.NFS_OK;
} catch (ChimeraNFSException he) {
_log.debug("CLOSE: {}", he.getMessage());
res.status = he.getStatus();
}
_result.opclose = res;
return _result;
}
}
......@@ -20,7 +20,6 @@ package org.dcache.chimera.nfs.v4;
import org.dcache.chimera.nfs.nfsstat;
import org.dcache.chimera.nfs.v4.xdr.nfs_argop4;
import org.dcache.chimera.nfs.v4.xdr.nfs_opnum4;
import org.dcache.chimera.nfs.v4.xdr.COMMIT4res;
import org.dcache.chimera.nfs.v4.xdr.nfs_resop4;
public class OperationCOMMIT extends AbstractNFSv4Operation {
......@@ -30,9 +29,7 @@ public class OperationCOMMIT extends AbstractNFSv4Operation {
}
@Override
public nfs_resop4 process(CompoundContext context) {
_result.opcommit = new COMMIT4res();
_result.opcommit.status = nfsstat.NFSERR_NOTSUPP;
return _result;
public void process(CompoundContext context, nfs_resop4 result) {
result.opcommit.status = nfsstat.NFSERR_NOTSUPP;
}
}
......@@ -16,6 +16,7 @@
*/
package org.dcache.chimera.nfs.v4;
import java.io.IOException;
import org.dcache.chimera.nfs.nfsstat;
import org.dcache.chimera.nfs.v4.xdr.nfs_ftype4;
import org.dcache.chimera.nfs.v4.xdr.fattr4;
......@@ -33,6 +34,7 @@ import org.dcache.chimera.nfs.vfs.Inode;
import org.dcache.chimera.posix.AclHandler;
import org.dcache.chimera.posix.Stat;
import org.dcache.chimera.posix.UnixAcl;
import org.dcache.xdr.OncRpcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -45,109 +47,96 @@ public class OperationCREATE extends AbstractNFSv4Operation {
}
@Override
public nfs_resop4 process(CompoundContext context) {
public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNFSException, IOException, OncRpcException {
CREATE4res res = new CREATE4res();
final CREATE4res res = result.opcreate;
fattr4 objAttr = _args.opcreate.createattrs;
int type = _args.opcreate.objtype.type;
int type = _args.opcreate.objtype.type;
Inode inode = null;
String name = NameFilter.convert(_args.opcreate.objname.value.value.value);
Stat parentStat = context.currentInode().statCache();
UnixAcl fileAcl = new UnixAcl(parentStat.getUid(), parentStat.getGid(), parentStat.getMode() & 0777);
if (!context.getAclHandler().isAllowed(fileAcl, context.getUser(), AclHandler.ACL_INSERT)) {
throw new ChimeraNFSException(nfsstat.NFSERR_ACCESS, "Permission denied.");
}
if (name.length() == 0) {
throw new ChimeraNFSException(nfsstat.NFSERR_INVAL, "bad path name");
}
if (name.length() > NFSv4Defaults.NFS4_MAXFILENAME) {
throw new ChimeraNFSException(nfsstat.NFSERR_NAMETOOLONG, "name too long");
}
if (context.currentInode().type() != Inode.Type.DIRECTORY) {
throw new ChimeraNFSException(nfsstat.NFSERR_NOTDIR, "not a directory");
}
if (name.equals(".") || name.equals("..")) {
throw new ChimeraNFSException(nfsstat.NFSERR_BADNAME, "bad name '.' or '..'");
}
// TODO: this check have to be moved into JdbcFs
try {
inode = context.getFs().inodeOf(context.currentInode(), name);
throw new ChimeraNFSException(nfsstat.NFSERR_EXIST, "path already exist");
} catch (ChimeraFsException hfe) {
}
String name = NameFilter.convert(_args.opcreate.objname.value.value.value);
Stat parentStat = context.currentInode().statCache();
UnixAcl fileAcl = new UnixAcl(parentStat.getUid(), parentStat.getGid(), parentStat.getMode() & 0777);
if (!context.getAclHandler().isAllowed(fileAcl, context.getUser(), AclHandler.ACL_INSERT)) {
throw new ChimeraNFSException(nfsstat.NFSERR_ACCESS, "Permission denied.");
}
if (name.length() == 0) {
throw new ChimeraNFSException(nfsstat.NFSERR_INVAL, "bad path name");
}
if (name.length() > NFSv4Defaults.NFS4_MAXFILENAME) {
throw new ChimeraNFSException(nfsstat.NFSERR_NAMETOOLONG, "name too long");
}
if (context.currentInode().type() != Inode.Type.DIRECTORY) {
throw new ChimeraNFSException(nfsstat.NFSERR_NOTDIR, "not a directory");
}
if (name.equals(".") || name.equals("..")) {
throw new ChimeraNFSException(nfsstat.NFSERR_BADNAME, "bad name '.' or '..'");
}
// TODO: this check have to be moved into JdbcFs
try {
inode = context.getFs().inodeOf(context.currentInode(), name);
throw new ChimeraNFSException(nfsstat.NFSERR_EXIST, "path already exist");
} catch (ChimeraFsException hfe) {
}
switch (type) {
case nfs_ftype4.NF4DIR:
inode = context.getFs().mkdir(context.currentInode(), name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4LNK:
String linkDest = new String(_args.opcreate.objtype.linkdata.value.value.value);
inode = context.getFs().symlink(context.currentInode(), name, linkDest,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4BLK:
inode = context.getFs().create(context.currentInode(), Inode.Type.BLOCK, name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4CHR:
inode = context.getFs().create(context.currentInode(), Inode.Type.CHAR, name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4FIFO:
inode = context.getFs().create(context.currentInode(), Inode.Type.FIFO, name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4SOCK:
inode = context.getFs().create(context.currentInode(), Inode.Type.SOCK, name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4ATTRDIR:
case nfs_ftype4.NF4NAMEDATTR:
throw new ChimeraNFSException(nfsstat.NFSERR_NOTSUPP, "create of this type not supported");
// regular files handled by OPEN
case nfs_ftype4.NF4REG:
throw new ChimeraNFSException(nfsstat.NFSERR_BADTYPE, "create of regular files handled by OPEN");
default:
throw new ChimeraNFSException(nfsstat.NFSERR_BADTYPE, "bad file type");
}
inode.setGID(context.getUser().getGID());
inode.setUID(context.getUser().getUID());
res.status = nfsstat.NFS_OK;
res.resok4 = new CREATE4resok();
res.resok4.attrset = OperationSETATTR.setAttributes(objAttr, inode, context);
res.resok4.cinfo = new change_info4();
res.resok4.cinfo.atomic = true;
res.resok4.cinfo.before = new changeid4(new uint64_t(context.currentInode().statCache().getMTime()));
res.resok4.cinfo.after = new changeid4(new uint64_t(System.currentTimeMillis()));
context.currentInode(inode);
} catch (ChimeraNFSException he) {
_log.debug("CREATE: {}", he.getMessage());
res.status = he.getStatus();
} catch (Exception e) {
_log.error("CREATE: ", e);
res.status = nfsstat.NFSERR_SERVERFAULT;
switch (type) {
case nfs_ftype4.NF4DIR:
inode = context.getFs().mkdir(context.currentInode(), name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4LNK:
String linkDest = new String(_args.opcreate.objtype.linkdata.value.value.value);
inode = context.getFs().symlink(context.currentInode(), name, linkDest,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4BLK:
inode = context.getFs().create(context.currentInode(), Inode.Type.BLOCK, name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4CHR:
inode = context.getFs().create(context.currentInode(), Inode.Type.CHAR, name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4FIFO:
inode = context.getFs().create(context.currentInode(), Inode.Type.FIFO, name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4SOCK:
inode = context.getFs().create(context.currentInode(), Inode.Type.SOCK, name,
context.getUser().getUID(), context.getUser().getGID(), 777);
break;
case nfs_ftype4.NF4ATTRDIR:
case nfs_ftype4.NF4NAMEDATTR:
throw new ChimeraNFSException(nfsstat.NFSERR_NOTSUPP, "create of this type not supported");
// regular files handled by OPEN
case nfs_ftype4.NF4REG:
throw new ChimeraNFSException(nfsstat.NFSERR_BADTYPE, "create of regular files handled by OPEN");
default:
throw new ChimeraNFSException(nfsstat.NFSERR_BADTYPE, "bad file type");
}
_result.opcreate = res;
return _result;
inode.setGID(context.getUser().getGID());
inode.setUID(context.getUser().getUID());
res.status = nfsstat.NFS_OK;
res.resok4 = new CREATE4resok();
res.resok4.attrset = OperationSETATTR.setAttributes(objAttr, inode, context);
res.resok4.cinfo = new change_info4();
res.resok4.cinfo.atomic = true;
res.resok4.cinfo.before = new changeid4(new uint64_t(context.currentInode().statCache().getMTime()));
res.resok4.cinfo.after = new changeid4(new uint64_t(System.currentTimeMillis()));
context.currentInode(inode);
}
}
......@@ -36,128 +36,118 @@ public class OperationCREATE_SESSION extends AbstractNFSv4Operation {
* no nfs4_prot.CREATE_SESSION4_FLAG_CONN_RDMA;
*/
private final static int SUPPORTED_SESSION_FLAGS = nfs4_prot.CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
private static final Logger _log = LoggerFactory.getLogger(OperationCREATE_SESSION.class);
public OperationCREATE_SESSION(nfs_argop4 args) {
super(args, nfs_opnum4.OP_CREATE_SESSION);
}
@Override
public nfs_resop4 process(CompoundContext context) {
CREATE_SESSION4res res = new CREATE_SESSION4res();
Long clientId = Long.valueOf(_args.opcreate_session.csa_clientid.value.value);
try {
/*
* check for correct arguments
*/
if( _args.opcreate_session.csa_fore_chan_attrs.ca_rdma_ird.length > 1 ) {
throw new ChimeraNFSException(nfsstat.NFSERR_BADXDR, "bad size of rdma_ird");
}
/*
* check for correct flags
*/
if( ( _args.opcreate_session.csa_flags.value
& ~( nfs4_prot.CREATE_SESSION4_FLAG_PERSIST |nfs4_prot.CREATE_SESSION4_FLAG_CONN_RDMA |
nfs4_prot.CREATE_SESSION4_FLAG_CONN_BACK_CHAN)) != 0) {
throw new ChimeraNFSException(nfsstat.NFSERR_INVAL, "bad ceate_session flag");
}
NFS4Client client = context.getStateHandler().getClientByID(clientId);
/*
* Phase 1:
*
* Client record lookup. The server looks up the client ID in its client record table.
* If the server contains no records with client ID equal to clientid_arg,
* then most likely the client's state has been purged during a period of inactivity,
* possibly due to a loss of connectivity. NFS4ERR_STALE_CLIENTID is returned,
* and no changes are made to any client records on the server.
* Otherwise, the server goes to phase 2.
*/
if(client == null ) {
throw new ChimeraNFSException(nfsstat.NFSERR_STALE_CLIENTID, "client not known");
}
/*
* Phase 2:
*
* Sequence id processing. If csa_sequenceid is equal to the sequence id in the client ID's slot,
* then this is a replay of the previous CREATE_SESSION request, and the server returns
* the cached result. If csa_sequenceid is not equal to the sequence id in the slot,
* and is more than one greater (accounting for wraparound),
* then the server returns the error NFS4ERR_SEQ_MISORDERED, and does not change the slot.
* If csa_sequenceid is equal to the slot's sequence id + 1 (accounting for wraparound),
* then the slot's sequence id is set to csa_sequenceid, and the CREATE_SESSION processing
* goes to the next phase. A subsequent new CREATE_SESSION call MUST use a csa_sequence
* that is one greater than last successfully used.
*/
if( !client.principal().equals(Integer.toString(context.getUser().getUID())) && !client.isConfirmed() ) {
throw new ChimeraNFSException(nfsstat.NFSERR_CLID_INUSE, "client already in use: " + client.principal()+ " " + context.getUser().getUID());
}
NFSv41Session session = client.createSession(_args.opcreate_session.csa_sequence.value.value,
_args.opcreate_session.csa_fore_chan_attrs.ca_maxrequests.value.value);
_log.debug("adding new session [{}]", session);
context.getStateHandler().sessionById(session.id(), session);
/*
* if client supports call backs on the same channel make use of it.
*/
if( (_args.opcreate_session.csa_flags.value & nfs4_prot.CREATE_SESSION4_FLAG_CONN_BACK_CHAN) != 0) {
ClientCB cb = new ClientCB(
context.getRpcCall().getTransport().getPeerTransport(),
_args.opcreate_session.csa_cb_program.value,