package org.dcache.chimera.nfsv41.door.proxy; import com.google.common.io.BaseEncoding; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.ByteBuffer; import org.dcache.util.NDC; import org.dcache.nfs.ChimeraNFSException; import org.dcache.nfs.nfsstat; import org.dcache.nfs.status.AccessException; import org.dcache.nfs.v4.AbstractNFSv4Operation; import org.dcache.nfs.v4.CompoundContext; import org.dcache.nfs.v4.OperationREAD; import org.dcache.nfs.v4.Stateids; import org.dcache.nfs.v4.xdr.READ4res; import org.dcache.nfs.v4.xdr.READ4resok; import org.dcache.nfs.v4.xdr.nfs4_prot; import org.dcache.nfs.v4.xdr.nfs_argop4; import org.dcache.nfs.v4.xdr.nfs_opnum4; import org.dcache.nfs.v4.xdr.nfs_resop4; import org.dcache.nfs.v4.xdr.stateid4; import org.dcache.nfs.vfs.Inode; public class ProxyIoREAD extends AbstractNFSv4Operation { private static final Logger _log = LoggerFactory.getLogger(ProxyIoREAD.class); private final ProxyIoFactory proxyIoFactory; public ProxyIoREAD(nfs_argop4 args, ProxyIoFactory proxyIoFactory) { super(args, nfs_opnum4.OP_READ); this.proxyIoFactory = proxyIoFactory; } @Override public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNFSException { final READ4res res = result.opread; Inode inode = context.currentInode(); NDC.push(context.getRpcCall().getTransport().getRemoteSocketAddress().toString()); NDC.push(BaseEncoding.base16().upperCase().encode(inode.getFileId())); try { if (!context.getFs().hasIOLayout(inode)) { /* * if we have a special file, then fall back to regular read operation */ new OperationREAD(_args).process(context, result); return; } long offset = _args.opread.offset.value; int count = _args.opread.count.value; stateid4 stateid = _args.opread.stateid; ProxyIoAdapter.ReadResult readResult; ProxyIoAdapter proxyIoAdapter; ByteBuffer bb = ByteBuffer.allocate(count); if (Stateids.isStateLess(stateid)) { /* * As there was no open, we have to check permissions. */ if (context.getFs().access(inode, nfs4_prot.ACCESS4_READ) == 0) { throw new AccessException(); } /* * use try-with-resource as wee need to close adapter on each request */ try (ProxyIoAdapter oneUseProxyIoAdapter = proxyIoFactory.createIoAdapter(inode, stateid, context, false)) { proxyIoAdapter = oneUseProxyIoAdapter; readResult = oneUseProxyIoAdapter.read(bb, offset); } } else { if (context.getMinorversion() == 0) { /* * The NFSv4.0 spec requires to update lease time as long as client * needs the file. This is done through READ, WRITE and RENEW * opertations. With introduction of sessions in v4.1 update of the * lease time done through SEQUENCE operation. */ context.getStateHandler().updateClientLeaseTime(stateid); } proxyIoAdapter = proxyIoFactory.getOrCreateProxy(inode, stateid, context, false); readResult = proxyIoAdapter.read(bb, offset); } res.status = nfsstat.NFS_OK; res.resok4 = new READ4resok(); res.resok4.data = bb; res.resok4.eof = readResult.isEof(); _log.debug("MOVER: {}@{} readed, {} requested.", readResult.isEof(), offset, _args.opread.count.value); } catch (ChimeraNFSException e) { // NFS server will handle them throw e; }catch(IOException ioe) { _log.error("DSREAD: ", ioe); proxyIoFactory.shutdownAdapter(_args.opread.stateid); res.status = nfsstat.NFSERR_IO; }catch(Exception e) { _log.error("DSREAD: ", e); res.status = nfsstat.NFSERR_SERVERFAULT; } finally { NDC.pop(); NDC.pop(); } } }