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.OperationWRITE; import org.dcache.nfs.v4.Stateids; import org.dcache.nfs.v4.xdr.WRITE4res; import org.dcache.nfs.v4.xdr.WRITE4resok; import org.dcache.nfs.v4.xdr.count4; 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.v4.xdr.verifier4; import org.dcache.nfs.vfs.Inode; import org.dcache.nfs.vfs.VirtualFileSystem; public class ProxyIoWRITE extends AbstractNFSv4Operation { private static final Logger _log = LoggerFactory.getLogger(ProxyIoWRITE.class); private final ProxyIoFactory proxyIoFactory; public ProxyIoWRITE(nfs_argop4 args, ProxyIoFactory proxyIoFactory) { super(args, nfs_opnum4.OP_WRITE); this.proxyIoFactory = proxyIoFactory; } @Override public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNFSException { final WRITE4res res = result.opwrite; 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 write operation */ new OperationWRITE(_args).process(context, result); return; } long offset = _args.opwrite.offset.value; ByteBuffer data = _args.opwrite.data; stateid4 stateid = _args.opwrite.stateid; VirtualFileSystem.WriteResult writeResult; if (Stateids.isStateLess(stateid)) { /* * As there was no open, we have to check permissions. */ if (context.getFs().access(inode, nfs4_prot.ACCESS4_MODIFY | nfs4_prot.ACCESS4_EXTEND ) == 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, true)) { writeResult = oneUseProxyIoAdapter.write(data, offset); } } else { /** * NOTICE, we check for minor version here, even if 4.1 requests * should not reach this place. Just keep it consistent for the future. * is not supported */ 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 proxyIoAdapter = proxyIoFactory.getOrCreateProxy(inode, stateid, context, true); writeResult = proxyIoAdapter.write(data, offset); } res.status = nfsstat.NFS_OK; res.resok4 = new WRITE4resok(); res.resok4.count = new count4(writeResult.getBytesWritten()); res.resok4.committed = writeResult.getStabilityLevel().toStableHow(); res.resok4.writeverf = new verifier4(); res.resok4.writeverf.value = new byte[nfs4_prot.NFS4_VERIFIER_SIZE]; _log.debug("MOVER: {}@{} written.", writeResult.getBytesWritten(), offset); } catch (ChimeraNFSException e) { // NFS server will handle them throw e; }catch(IOException ioe) { _log.error("DSWRITE: {}", ioe.getMessage()); proxyIoFactory.shutdownAdapter(_args.opwrite.stateid); res.status = nfsstat.NFSERR_IO; }catch(Exception e) { _log.error("DSWRITE: ", e); res.status = nfsstat.NFSERR_SERVERFAULT; } finally { NDC.pop(); NDC.pop(); } } }