package org.dcache.chimera.nfsv41.mover; import com.google.common.primitives.Longs; import java.io.IOException; import java.nio.ByteBuffer; import org.dcache.nfs.ChimeraNFSException; import org.dcache.nfs.nfsstat; import org.dcache.nfs.status.NfsIoException; import org.dcache.nfs.v3.HimeraNfsUtils; import org.dcache.nfs.v3.xdr.READ3args; import org.dcache.nfs.v3.xdr.READ3res; import org.dcache.nfs.v3.xdr.READ3resfail; import org.dcache.nfs.v3.xdr.READ3resok; import org.dcache.nfs.v3.xdr.WRITE3args; import org.dcache.nfs.v3.xdr.WRITE3res; import org.dcache.nfs.v3.xdr.WRITE3resfail; import org.dcache.nfs.v3.xdr.WRITE3resok; import org.dcache.nfs.v3.xdr.count3; import org.dcache.nfs.v3.xdr.nfs3_prot; import org.dcache.nfs.v3.xdr.stable_how; import org.dcache.nfs.v3.xdr.uint32; import org.dcache.nfs.v3.xdr.writeverf3; import org.dcache.nfs.v4.xdr.stateid4; import org.dcache.pool.repository.RepositoryChannel; import org.dcache.xdr.OncRpcException; import org.dcache.xdr.RpcCall; import org.dcache.xdr.RpcDispatchable; import org.dcache.xdr.XdrVoid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class EmbeddedV3 implements RpcDispatchable { private static final Logger _log = LoggerFactory.getLogger(EmbeddedV3.class); private final writeverf3 writeVerifier = new writeverf3(Longs.toByteArray(System.currentTimeMillis())); private final NFSv4MoverHandler _moverHandler; public EmbeddedV3(NFSv4MoverHandler moverHandler) { this._moverHandler = moverHandler; } @Override public void dispatchOncRpcCall(RpcCall call) throws OncRpcException, IOException { int procedure = call.getProcedure(); switch (procedure) { case nfs3_prot.NFSPROC3_NULL_3: call.retrieveCall(XdrVoid.XDR_VOID); call.reply(XdrVoid.XDR_VOID); break; case nfs3_prot.NFSPROC3_READ_3: { READ3args args$ = new READ3args(); call.retrieveCall(args$); READ3res result$ = NFSPROC3_READ_3(call, args$); call.reply(result$); break; } case nfs3_prot.NFSPROC3_WRITE_3: { WRITE3args args$ = new WRITE3args(); call.retrieveCall(args$); WRITE3res result$ = NFSPROC3_WRITE_3(call, args$); call.reply(result$); break; } default: call.failProcedureUnavailable(); } } private READ3res NFSPROC3_READ_3(RpcCall call, READ3args args$) { READ3res res = new READ3res(); try { long offset = args$.offset.value.value; int count = args$.count.value.value; stateid4 stateid = new stateid4(args$.file.data, 0); NfsMover mover = _moverHandler.getOrCreateMover(call.getTransport().getRemoteSocketAddress(), stateid, args$.file.data); if (mover == null) { /* * return IO error instead of BadStateidException to avoid state recovery. * The client will fall back to IO through MDS. */ throw new NfsIoException("No mover associated with given stateid: " + stateid); } ByteBuffer bb = ByteBuffer.allocate(count); RepositoryChannel fc = mover.getMoverChannel(); bb.rewind(); int bytesRead = fc.read(bb, offset); res.status = nfsstat.NFS_OK; res.resok = new READ3resok(); res.resok.count = new count3(new uint32(bytesRead)); res.resok.data = bb.array(); res.resok.file_attributes = HimeraNfsUtils.defaultPostOpAttr(); if (bytesRead == -1 || offset + bytesRead == fc.size()) { res.resok.eof = true; } _log.debug("MOVER: {}@{} read, {} requested.", bytesRead, offset, count); } catch (ChimeraNFSException e) { res.status = e.getStatus(); res.resfail = new READ3resfail(); res.resfail.file_attributes = HimeraNfsUtils.defaultPostOpAttr(); _log.debug(e.getMessage()); } catch (Exception e) { _log.error("DSREAD3: ", e); res.status = nfsstat.NFSERR_SERVERFAULT; res.resfail = new READ3resfail(); res.resfail.file_attributes = HimeraNfsUtils.defaultPostOpAttr(); _log.error("internal server error", e); } return res; } private WRITE3res NFSPROC3_WRITE_3(RpcCall call, WRITE3args args$) { WRITE3res res = new WRITE3res(); try { long offset = args$.offset.value.value; int count = args$.count.value.value; stateid4 stateid = new stateid4(args$.file.data, 0); NfsMover mover = _moverHandler.getOrCreateMover(call.getTransport().getRemoteSocketAddress(), stateid, args$.file.data); if (mover == null) { /* * return IO error instead of BadStateidException to avoid state recovery. * The client will fall back to IO through MDS. */ throw new NfsIoException("No mover associated with given stateid: " + stateid); } ByteBuffer bb = ByteBuffer.wrap(args$.data); RepositoryChannel fc = mover.getMoverChannel(); bb.limit(count); int bytesWritten = fc.write(bb, offset); res.status = nfsstat.NFS_OK; res.resok = new WRITE3resok(); res.resok.count = new count3(new uint32(bytesWritten)); res.resok.file_wcc = HimeraNfsUtils.defaultWccData(); res.resok.committed = stable_how.FILE_SYNC; res.resok.verf = writeVerifier; _log.debug("MOVER: {}@{} written, {} requested.", bytesWritten, offset, count); } catch (ChimeraNFSException e) { res.status = e.getStatus(); res.resfail = new WRITE3resfail(); res.resfail.file_wcc = HimeraNfsUtils.defaultWccData(); _log.debug(e.getMessage()); } catch (Exception e) { _log.error("DSWRIRE3: ", e); res.status = nfsstat.NFSERR_SERVERFAULT; res.resfail = new WRITE3resfail(); res.resfail.file_wcc = HimeraNfsUtils.defaultWccData(); _log.error("internal server error", e); } return res; } }