Skip to content

Commit

Permalink
nfsv42: initial support for intra-server copy
Browse files Browse the repository at this point in the history
Motivation:
the newer libc make use of copy_file_range function, that directly
mapped to NFSv4.2 COPY request.

Modification:
Add initial implementation on OperationCOPY that synchronously copies
the data between two open files.

Result:
initial step to support intra-server copy.

Acked-by: Paul Millar
Target: master
  • Loading branch information
kofemann committed Aug 31, 2021
1 parent b1956be commit 91b31e0
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 - 2020 Deutsches Elektronen-Synchroton,
* Copyright (c) 2009 - 2021 Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
*
* This library is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -159,8 +159,9 @@ protected AbstractNFSv4Operation getOperation(nfs_argop4 op) {
return new OperationLISTXATTRS(op);
case nfs_opnum4.OP_REMOVEXATTR:
return new OperationREMOVEXATTR(op);
case nfs_opnum4.OP_ALLOCATE:
case nfs_opnum4.OP_COPY:
return new OperationCOPY(op);
case nfs_opnum4.OP_ALLOCATE:
case nfs_opnum4.OP_COPY_NOTIFY:
case nfs_opnum4.OP_DEALLOCATE:
case nfs_opnum4.OP_IO_ADVISE:
Expand Down
121 changes: 121 additions & 0 deletions core/src/main/java/org/dcache/nfs/v4/OperationCOPY.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright (c) 2021 Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.nfs.v4;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import com.google.common.annotations.Beta;
import com.google.common.util.concurrent.Uninterruptibles;
import org.dcache.nfs.ChimeraNFSException;
import org.dcache.nfs.nfsstat;
import org.dcache.nfs.status.NotSuppException;
import org.dcache.nfs.status.OpenModeException;
import org.dcache.nfs.v4.xdr.COPY4res;
import org.dcache.nfs.v4.xdr.COPY4resok;
import org.dcache.nfs.v4.xdr.copy_requirements4;
import org.dcache.nfs.v4.xdr.length4;
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.stable_how4;
import org.dcache.nfs.v4.xdr.stateid4;
import org.dcache.nfs.v4.xdr.write_response4;
import org.dcache.nfs.vfs.Inode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Beta
/**
* NFSv4.2 operations that handles server side copy as specified in rfc7862#section-4.
*/
public class OperationCOPY extends AbstractNFSv4Operation {

private final static Logger LOGGER = LoggerFactory.getLogger(OperationCOPY.class);

public OperationCOPY(nfs_argop4 args) {
super(args, nfs_opnum4.OP_COPY);
}

@Override
public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNFSException, IOException {

final COPY4res res = result.opcopy;

// inter server copy is not supported
if (_args.opcopy.ca_source_server.length > 0) {
throw new NotSuppException("Inter-server copy is not supported");
}

// only synchronous mode is supported (for now)
if (!_args.opcopy.ca_consecutive || !_args.opcopy.ca_synchronous) {
throw new NotSuppException();
}

Inode srcInode = context.savedInode();
Inode dstInode = context.currentInode();

long srcPos = _args.opcopy.ca_src_offset.value;
long dstPos = _args.opcopy.ca_dst_offset.value;
long len = _args.opcopy.ca_count.value;

NFS4Client client = context.getSession().getClient();

int srcAccess = context.getStateHandler().getFileTracker().getShareAccess(client, srcInode, _args.opcopy.ca_src_stateid);
int dstAccess = context.getStateHandler().getFileTracker().getShareAccess(client, dstInode, _args.opcopy.ca_dst_stateid);

if ((srcAccess & nfs4_prot.OPEN4_SHARE_ACCESS_READ) == 0) {
throw new OpenModeException("Invalid source inode open mode (required read)");
}

if ((dstAccess & nfs4_prot.OPEN4_SHARE_ACCESS_WRITE) == 0) {
throw new OpenModeException("Invalid destination inode open mode (required write)");
}

Future<Long> copyFuture = context.getFs().copyFileRange(srcInode, srcPos, dstInode, dstPos, len);

try {
long n = Uninterruptibles.getUninterruptibly(copyFuture);

res.cr_resok4 = new COPY4resok();
res.cr_resok4.cr_response = new write_response4();
res.cr_resok4.cr_response.wr_callback_id = new stateid4[0];
res.cr_resok4.cr_response.wr_committed = stable_how4.FILE_SYNC4;
res.cr_resok4.cr_response.wr_count = new length4(n);
res.cr_resok4.cr_response.wr_writeverf = context.getRebootVerifier();

res.cr_resok4.cr_requirements = new copy_requirements4();
res.cr_resok4.cr_requirements.cr_consecutive = true;
res.cr_resok4.cr_requirements.cr_synchronous = true;

res.cr_status = nfsstat.NFS_OK;
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof ChimeraNFSException) {
res.cr_status = ((ChimeraNFSException) cause).getStatus();
} else {
LOGGER.warn("Unexpected error during copyFileRange", e);
res.cr_status = nfsstat.NFSERR_IO;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 - 2020 Deutsches Elektronen-Synchroton,
* Copyright (c) 2009 - 2021 Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
*
* This library is free software; you can redistribute it and/or modify
Expand All @@ -21,6 +21,7 @@

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import javax.security.auth.Subject;
import org.dcache.nfs.v4.NfsIdMapping;
import org.dcache.nfs.v4.xdr.nfsace4;
Expand Down Expand Up @@ -200,4 +201,9 @@ public String[] listXattrs(Inode inode) throws IOException {
public void removeXattr(Inode inode, String attr) throws IOException {
delegate().removeXattr(inode, attr);
}

@Override
public CompletableFuture<Long> copyFileRange(Inode src, long srcPos, Inode dst, long dstPos, long len) {
return delegate().copyFileRange(src, srcPos, dst, dstPos, len);
}
}
21 changes: 20 additions & 1 deletion core/src/main/java/org/dcache/nfs/vfs/VirtualFileSystem.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 - 2020 Deutsches Elektronen-Synchroton,
* Copyright (c) 2009 - 2021 Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
*
* This library is free software; you can redistribute it and/or modify
Expand All @@ -21,7 +21,10 @@

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import javax.security.auth.Subject;

import com.google.common.annotations.Beta;
import org.dcache.nfs.status.NotSuppException;
import org.dcache.nfs.v4.NfsIdMapping;
import org.dcache.nfs.v4.xdr.nfsace4;
Expand Down Expand Up @@ -501,4 +504,20 @@ default void removeXattr(Inode inode, String attr) throws IOException {
throw new NotSuppException();
}

/**
* Performs an in-filesystem copy between two open files. The result of the successfully completed {@link CompletableFuture}
* corresponds to the number of copied bytes. If {@code copyFileRange} operation fails, the returned CompletableFuture
* with complete exceptionally with corresponding error.
* @param src inode of the source file.
* @param srcPos starting position in the source file.
* @param dst inode of the destination file.
* @param dstPos starting position in the destination file.
* @param len number of bytes to copy.
* @return a {@link CompletableFuture} representing pending completion of the copy request
* @since 0.23
*/
@Beta
default CompletableFuture<Long> copyFileRange(Inode src, long srcPos, Inode dst, long dstPos, long len) {
return CompletableFuture.failedFuture(new NotSuppException());
}
}

0 comments on commit 91b31e0

Please sign in to comment.