Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hdfs.nfs.nfs3; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.DirectoryListingStartAfterNotFoundException; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileSystem.Statistics; import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.fs.FsStatus; import org.apache.hadoop.fs.Options; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.DFSClient; import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.client.HdfsDataOutputStream; import org.apache.hadoop.hdfs.protocol.DirectoryListing; import org.apache.hadoop.hdfs.protocol.HdfsConstants; import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; import org.apache.hadoop.hdfs.server.namenode.NameNode; import org.apache.hadoop.ipc.RemoteException; import org.apache.hadoop.nfs.AccessPrivilege; import org.apache.hadoop.nfs.NfsExports; import org.apache.hadoop.nfs.NfsFileType; import org.apache.hadoop.nfs.NfsTime; import org.apache.hadoop.nfs.nfs3.FileHandle; import org.apache.hadoop.nfs.nfs3.IdUserGroup; import org.apache.hadoop.nfs.nfs3.Nfs3Constant; import org.apache.hadoop.nfs.nfs3.Nfs3Constant.NFSPROC3; import org.apache.hadoop.nfs.nfs3.Nfs3Constant.WriteStableHow; import org.apache.hadoop.nfs.nfs3.Nfs3FileAttributes; import org.apache.hadoop.nfs.nfs3.Nfs3Interface; import org.apache.hadoop.nfs.nfs3.Nfs3Status; import org.apache.hadoop.nfs.nfs3.request.ACCESS3Request; import org.apache.hadoop.nfs.nfs3.request.COMMIT3Request; import org.apache.hadoop.nfs.nfs3.request.CREATE3Request; import org.apache.hadoop.nfs.nfs3.request.FSINFO3Request; import org.apache.hadoop.nfs.nfs3.request.FSSTAT3Request; import org.apache.hadoop.nfs.nfs3.request.GETATTR3Request; import org.apache.hadoop.nfs.nfs3.request.LOOKUP3Request; import org.apache.hadoop.nfs.nfs3.request.MKDIR3Request; import org.apache.hadoop.nfs.nfs3.request.PATHCONF3Request; import org.apache.hadoop.nfs.nfs3.request.READ3Request; import org.apache.hadoop.nfs.nfs3.request.READDIR3Request; import org.apache.hadoop.nfs.nfs3.request.READDIRPLUS3Request; import org.apache.hadoop.nfs.nfs3.request.READLINK3Request; import org.apache.hadoop.nfs.nfs3.request.REMOVE3Request; import org.apache.hadoop.nfs.nfs3.request.RENAME3Request; import org.apache.hadoop.nfs.nfs3.request.RMDIR3Request; import org.apache.hadoop.nfs.nfs3.request.SETATTR3Request; import org.apache.hadoop.nfs.nfs3.request.SYMLINK3Request; import org.apache.hadoop.nfs.nfs3.request.SetAttr3; import org.apache.hadoop.nfs.nfs3.request.SetAttr3.SetAttrField; import org.apache.hadoop.nfs.nfs3.request.WRITE3Request; import org.apache.hadoop.nfs.nfs3.response.ACCESS3Response; import org.apache.hadoop.nfs.nfs3.response.COMMIT3Response; import org.apache.hadoop.nfs.nfs3.response.CREATE3Response; import org.apache.hadoop.nfs.nfs3.response.FSINFO3Response; import org.apache.hadoop.nfs.nfs3.response.FSSTAT3Response; import org.apache.hadoop.nfs.nfs3.response.GETATTR3Response; import org.apache.hadoop.nfs.nfs3.response.LOOKUP3Response; import org.apache.hadoop.nfs.nfs3.response.MKDIR3Response; import org.apache.hadoop.nfs.nfs3.response.NFS3Response; import org.apache.hadoop.nfs.nfs3.response.PATHCONF3Response; import org.apache.hadoop.nfs.nfs3.response.READ3Response; import org.apache.hadoop.nfs.nfs3.response.READDIR3Response; import org.apache.hadoop.nfs.nfs3.response.READDIR3Response.DirList3; import org.apache.hadoop.nfs.nfs3.response.READDIR3Response.Entry3; import org.apache.hadoop.nfs.nfs3.response.READDIRPLUS3Response; import org.apache.hadoop.nfs.nfs3.response.READDIRPLUS3Response.DirListPlus3; import org.apache.hadoop.nfs.nfs3.response.READLINK3Response; import org.apache.hadoop.nfs.nfs3.response.REMOVE3Response; import org.apache.hadoop.nfs.nfs3.response.RENAME3Response; import org.apache.hadoop.nfs.nfs3.response.RMDIR3Response; import org.apache.hadoop.nfs.nfs3.response.SETATTR3Response; import org.apache.hadoop.nfs.nfs3.response.SYMLINK3Response; import org.apache.hadoop.nfs.nfs3.response.WRITE3Response; import org.apache.hadoop.nfs.nfs3.response.WccAttr; import org.apache.hadoop.nfs.nfs3.response.WccData; import org.apache.hadoop.oncrpc.RpcAcceptedReply; import org.apache.hadoop.oncrpc.RpcCall; import org.apache.hadoop.oncrpc.RpcCallCache; import org.apache.hadoop.oncrpc.RpcDeniedReply; import org.apache.hadoop.oncrpc.RpcInfo; import org.apache.hadoop.oncrpc.RpcProgram; import org.apache.hadoop.oncrpc.RpcReply; import org.apache.hadoop.oncrpc.RpcResponse; import org.apache.hadoop.oncrpc.RpcUtil; import org.apache.hadoop.oncrpc.XDR; import org.apache.hadoop.oncrpc.security.Credentials; import org.apache.hadoop.oncrpc.security.CredentialsSys; import org.apache.hadoop.oncrpc.security.RpcAuthInfo.AuthFlavor; import org.apache.hadoop.oncrpc.security.SecurityHandler; import org.apache.hadoop.oncrpc.security.SysSecurityHandler; import org.apache.hadoop.oncrpc.security.Verifier; import org.apache.hadoop.oncrpc.security.VerifierNone; import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.security.UserGroupInformation; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.EnumSet; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NFS_KEYTAB_FILE_KEY; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NFS_USER_NAME_KEY; /** * RPC program corresponding to nfs daemon. See {@link Nfs3}. */ public class RpcProgramNfs3 extends RpcProgram implements Nfs3Interface { public static final int DEFAULT_UMASK = 0022; public static final FsPermission umask = new FsPermission((short) DEFAULT_UMASK); static final Log LOG = LogFactory.getLog(RpcProgramNfs3.class); private final Configuration config = new Configuration(); private final WriteManager writeManager; private final IdUserGroup iug; private final DFSClientCache clientCache; private final NfsExports exports; /** * superUserClient should always impersonate HDFS file system owner to send * requests which requires supergroup privilege. This requires the same user * to start HDFS and NFS. */ private final DFSClient superUserClient; private final short replication; private final long blockSize; private final int bufferSize; private Statistics statistics; private String writeDumpDir; // The dir save dump files private final RpcCallCache rpcCallCache; public RpcProgramNfs3(Configuration config) throws IOException { super("NFS3", "localhost", config.getInt(Nfs3Constant.NFS3_SERVER_PORT, Nfs3Constant.NFS3_SERVER_PORT_DEFAULT), Nfs3Constant.PROGRAM, Nfs3Constant.VERSION, Nfs3Constant.VERSION); config.set(FsPermission.UMASK_LABEL, "000"); iug = new IdUserGroup(); exports = NfsExports.getInstance(config); writeManager = new WriteManager(iug, config); clientCache = new DFSClientCache(config); superUserClient = new DFSClient(NameNode.getAddress(config), config); replication = (short) config.getInt(DFSConfigKeys.DFS_REPLICATION_KEY, DFSConfigKeys.DFS_REPLICATION_DEFAULT); blockSize = config.getLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, DFSConfigKeys.DFS_BLOCK_SIZE_DEFAULT); bufferSize = config.getInt(CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_KEY, CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_DEFAULT); writeDumpDir = config.get(Nfs3Constant.FILE_DUMP_DIR_KEY, Nfs3Constant.FILE_DUMP_DIR_DEFAULT); boolean enableDump = config.getBoolean(Nfs3Constant.ENABLE_FILE_DUMP_KEY, Nfs3Constant.ENABLE_FILE_DUMP_DEFAULT); UserGroupInformation.setConfiguration(config); SecurityUtil.login(config, DFS_NFS_KEYTAB_FILE_KEY, DFS_NFS_USER_NAME_KEY); if (!enableDump) { writeDumpDir = null; } else { clearDirectory(writeDumpDir); } rpcCallCache = new RpcCallCache("NFS3", 256); } private void clearDirectory(String writeDumpDir) throws IOException { File dumpDir = new File(writeDumpDir); if (dumpDir.exists()) { LOG.info("Delete current dump directory " + writeDumpDir); if (!(FileUtil.fullyDelete(dumpDir))) { throw new IOException("Cannot remove current dump directory: " + dumpDir); } } LOG.info("Create new dump directory " + writeDumpDir); if (!dumpDir.mkdirs()) { throw new IOException("Cannot create dump directory " + dumpDir); } } @Override public void startDaemons() { writeManager.startAsyncDataSerivce(); } /** * *************************************************** * RPC call handlers * **************************************************** */ @Override public NFS3Response nullProcedure() { if (LOG.isDebugEnabled()) { LOG.debug("NFS NULL"); } return new NFS3Response(Nfs3Status.NFS3_OK); } @Override public GETATTR3Response getattr(XDR xdr, SecurityHandler securityHandler, InetAddress client) { GETATTR3Response response = new GETATTR3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } GETATTR3Request request = null; try { request = new GETATTR3Request(xdr); } catch (IOException e) { LOG.error("Invalid GETATTR request"); response.setStatus(Nfs3Status.NFS3ERR_INVAL); return response; } FileHandle handle = request.getHandle(); if (LOG.isTraceEnabled()) { LOG.trace("GETATTR for fileId: " + handle.getFileId()); } Nfs3FileAttributes attrs = null; try { attrs = writeManager.getFileAttr(dfsClient, handle, iug); } catch (IOException e) { LOG.info("Can't get file attribute, fileId=" + handle.getFileId(), e); response.setStatus(Nfs3Status.NFS3ERR_IO); return response; } if (attrs == null) { LOG.error("Can't get path for fileId:" + handle.getFileId()); response.setStatus(Nfs3Status.NFS3ERR_STALE); return response; } response.setPostOpAttr(attrs); return response; } // Set attribute, don't support setting "size". For file/dir creation, mode is // set during creation and setMode should be false here. private void setattrInternal(DFSClient dfsClient, String fileIdPath, SetAttr3 newAttr, boolean setMode) throws IOException { EnumSet<SetAttrField> updateFields = newAttr.getUpdateFields(); if (setMode && updateFields.contains(SetAttrField.MODE)) { if (LOG.isDebugEnabled()) { LOG.debug("set new mode:" + newAttr.getMode()); } dfsClient.setPermission(fileIdPath, new FsPermission((short) (newAttr.getMode()))); } if (updateFields.contains(SetAttrField.UID) || updateFields.contains(SetAttrField.GID)) { String uname = updateFields.contains(SetAttrField.UID) ? iug.getUserName(newAttr.getUid(), Nfs3Constant.UNKNOWN_USER) : null; String gname = updateFields.contains(SetAttrField.GID) ? iug.getGroupName(newAttr.getGid(), Nfs3Constant.UNKNOWN_GROUP) : null; dfsClient.setOwner(fileIdPath, uname, gname); } long atime = updateFields.contains(SetAttrField.ATIME) ? newAttr.getAtime().getMilliSeconds() : -1; long mtime = updateFields.contains(SetAttrField.MTIME) ? newAttr.getMtime().getMilliSeconds() : -1; if (atime != -1 || mtime != -1) { if (LOG.isDebugEnabled()) { LOG.debug("set atime:" + +atime + " mtime:" + mtime); } dfsClient.setTimes(fileIdPath, mtime, atime); } } @Override public SETATTR3Response setattr(XDR xdr, SecurityHandler securityHandler, InetAddress client) { SETATTR3Response response = new SETATTR3Response(Nfs3Status.NFS3_OK); DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } SETATTR3Request request = null; try { request = new SETATTR3Request(xdr); } catch (IOException e) { LOG.error("Invalid SETATTR request"); response.setStatus(Nfs3Status.NFS3ERR_INVAL); return response; } FileHandle handle = request.getHandle(); if (LOG.isDebugEnabled()) { LOG.debug("NFS SETATTR fileId: " + handle.getFileId()); } if (request.getAttr().getUpdateFields().contains(SetAttrField.SIZE)) { LOG.error("Setting file size is not supported when setattr, fileId: " + handle.getFileId()); response.setStatus(Nfs3Status.NFS3ERR_INVAL); return response; } String fileIdPath = Nfs3Utils.getFileIdPath(handle); Nfs3FileAttributes preOpAttr = null; try { preOpAttr = Nfs3Utils.getFileAttr(dfsClient, fileIdPath, iug); if (preOpAttr == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); response.setStatus(Nfs3Status.NFS3ERR_STALE); return response; } WccAttr preOpWcc = Nfs3Utils.getWccAttr(preOpAttr); if (request.isCheck()) { if (!preOpAttr.getCtime().equals(request.getCtime())) { WccData wccData = new WccData(preOpWcc, preOpAttr); return new SETATTR3Response(Nfs3Status.NFS3ERR_NOT_SYNC, wccData); } } // check the write access privilege if (!checkAccessPrivilege(client, AccessPrivilege.READ_WRITE)) { return new SETATTR3Response(Nfs3Status.NFS3ERR_ACCES, new WccData(preOpWcc, preOpAttr)); } setattrInternal(dfsClient, fileIdPath, request.getAttr(), true); Nfs3FileAttributes postOpAttr = Nfs3Utils.getFileAttr(dfsClient, fileIdPath, iug); WccData wccData = new WccData(preOpWcc, postOpAttr); return new SETATTR3Response(Nfs3Status.NFS3_OK, wccData); } catch (IOException e) { LOG.warn("Exception ", e); WccData wccData = null; try { wccData = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(preOpAttr), dfsClient, fileIdPath, iug); } catch (IOException e1) { LOG.info("Can't get postOpAttr for fileIdPath: " + fileIdPath, e1); } if (e instanceof AccessControlException) { return new SETATTR3Response(Nfs3Status.NFS3ERR_ACCES, wccData); } else { return new SETATTR3Response(Nfs3Status.NFS3ERR_IO, wccData); } } } @Override public LOOKUP3Response lookup(XDR xdr, SecurityHandler securityHandler, InetAddress client) { LOOKUP3Response response = new LOOKUP3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } LOOKUP3Request request = null; try { request = new LOOKUP3Request(xdr); } catch (IOException e) { LOG.error("Invalid LOOKUP request"); return new LOOKUP3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle dirHandle = request.getHandle(); String fileName = request.getName(); if (LOG.isDebugEnabled()) { LOG.debug("NFS LOOKUP dir fileId: " + dirHandle.getFileId() + " name: " + fileName); } try { String dirFileIdPath = Nfs3Utils.getFileIdPath(dirHandle); Nfs3FileAttributes postOpObjAttr = writeManager.getFileAttr(dfsClient, dirHandle, fileName); if (postOpObjAttr == null) { if (LOG.isDebugEnabled()) { LOG.debug("NFS LOOKUP fileId: " + dirHandle.getFileId() + " name:" + fileName + " does not exist"); } Nfs3FileAttributes postOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); return new LOOKUP3Response(Nfs3Status.NFS3ERR_NOENT, null, null, postOpDirAttr); } Nfs3FileAttributes postOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); if (postOpDirAttr == null) { LOG.info("Can't get path for dir fileId:" + dirHandle.getFileId()); return new LOOKUP3Response(Nfs3Status.NFS3ERR_STALE); } FileHandle fileHandle = new FileHandle(postOpObjAttr.getFileId()); return new LOOKUP3Response(Nfs3Status.NFS3_OK, fileHandle, postOpObjAttr, postOpDirAttr); } catch (IOException e) { LOG.warn("Exception ", e); return new LOOKUP3Response(Nfs3Status.NFS3ERR_IO); } } @Override public ACCESS3Response access(XDR xdr, SecurityHandler securityHandler, InetAddress client) { ACCESS3Response response = new ACCESS3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } ACCESS3Request request = null; try { request = new ACCESS3Request(xdr); } catch (IOException e) { LOG.error("Invalid ACCESS request"); return new ACCESS3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle handle = request.getHandle(); Nfs3FileAttributes attrs; if (LOG.isDebugEnabled()) { LOG.debug("NFS ACCESS fileId: " + handle.getFileId()); } try { // HDFS-5804 removed supserUserClient access attrs = writeManager.getFileAttr(dfsClient, handle, iug); if (attrs == null) { LOG.error("Can't get path for fileId:" + handle.getFileId()); return new ACCESS3Response(Nfs3Status.NFS3ERR_STALE); } int access = Nfs3Utils.getAccessRightsForUserGroup(securityHandler.getUid(), securityHandler.getGid(), attrs); return new ACCESS3Response(Nfs3Status.NFS3_OK, attrs, access); } catch (IOException e) { LOG.warn("Exception ", e); return new ACCESS3Response(Nfs3Status.NFS3ERR_IO); } } public READLINK3Response readlink(XDR xdr, SecurityHandler securityHandler, InetAddress client) { READLINK3Response response = new READLINK3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } READLINK3Request request = null; try { request = new READLINK3Request(xdr); } catch (IOException e) { LOG.error("Invalid READLINK request"); return new READLINK3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle handle = request.getHandle(); if (LOG.isDebugEnabled()) { LOG.debug("NFS READLINK fileId: " + handle.getFileId()); } String fileIdPath = Nfs3Utils.getFileIdPath(handle); try { String target = dfsClient.getLinkTarget(fileIdPath); Nfs3FileAttributes postOpAttr = Nfs3Utils.getFileAttr(dfsClient, fileIdPath, iug); if (postOpAttr == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); return new READLINK3Response(Nfs3Status.NFS3ERR_STALE); } if (postOpAttr.getType() != NfsFileType.NFSLNK.toValue()) { LOG.error("Not a symlink, fileId:" + handle.getFileId()); return new READLINK3Response(Nfs3Status.NFS3ERR_INVAL); } if (target == null) { LOG.error("Symlink target should not be null, fileId:" + handle.getFileId()); return new READLINK3Response(Nfs3Status.NFS3ERR_SERVERFAULT); } int rtmax = config.getInt(Nfs3Constant.MAX_READ_TRANSFER_SIZE_KEY, Nfs3Constant.MAX_READ_TRANSFER_SIZE_DEFAULT); if (rtmax < target.getBytes().length) { LOG.error("Link size: " + target.getBytes().length + " is larger than max transfer size: " + rtmax); return new READLINK3Response(Nfs3Status.NFS3ERR_IO, postOpAttr, new byte[0]); } return new READLINK3Response(Nfs3Status.NFS3_OK, postOpAttr, target.getBytes()); } catch (IOException e) { LOG.warn("Readlink error: " + e.getClass(), e); if (e instanceof FileNotFoundException) { return new READLINK3Response(Nfs3Status.NFS3ERR_STALE); } else if (e instanceof AccessControlException) { return new READLINK3Response(Nfs3Status.NFS3ERR_ACCES); } return new READLINK3Response(Nfs3Status.NFS3ERR_IO); } } @Override public READ3Response read(XDR xdr, SecurityHandler securityHandler, InetAddress client) { READ3Response response = new READ3Response(Nfs3Status.NFS3_OK); final String userName = securityHandler.getUser(); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(userName); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } READ3Request request = null; try { request = new READ3Request(xdr); } catch (IOException e) { LOG.error("Invalid READ request"); return new READ3Response(Nfs3Status.NFS3ERR_INVAL); } long offset = request.getOffset(); int count = request.getCount(); FileHandle handle = request.getHandle(); if (LOG.isDebugEnabled()) { LOG.debug("NFS READ fileId: " + handle.getFileId() + " offset: " + offset + " count: " + count); } Nfs3FileAttributes attrs; boolean eof; if (count == 0) { // Only do access check. try { // Don't read from cache. Client may not have read permission. attrs = Nfs3Utils.getFileAttr(dfsClient, Nfs3Utils.getFileIdPath(handle), iug); } catch (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug("Get error accessing file, fileId:" + handle.getFileId(), e); } return new READ3Response(Nfs3Status.NFS3ERR_IO); } if (attrs == null) { if (LOG.isDebugEnabled()) { LOG.debug("Can't get path for fileId:" + handle.getFileId()); } return new READ3Response(Nfs3Status.NFS3ERR_NOENT); } int access = Nfs3Utils.getAccessRightsForUserGroup(securityHandler.getUid(), securityHandler.getGid(), attrs); if ((access & Nfs3Constant.ACCESS3_READ) != 0) { eof = offset < attrs.getSize() ? false : true; return new READ3Response(Nfs3Status.NFS3_OK, attrs, 0, eof, ByteBuffer.wrap(new byte[0])); } else { return new READ3Response(Nfs3Status.NFS3ERR_ACCES); } } // In case there is buffered data for the same file, flush it. This can be // optimized later by reading from the cache. int ret = writeManager.commitBeforeRead(dfsClient, handle, offset + count); if (ret != Nfs3Status.NFS3_OK) { LOG.warn("commitBeforeRead didn't succeed with ret=" + ret + ". Read may not get most recent data."); } try { int rtmax = config.getInt(Nfs3Constant.MAX_READ_TRANSFER_SIZE_KEY, Nfs3Constant.MAX_READ_TRANSFER_SIZE_DEFAULT); int buffSize = Math.min(rtmax, count); byte[] readbuffer = new byte[buffSize]; int readCount = 0; /** * Retry exactly once because the DFSInputStream can be stale. */ for (int i = 0; i < 1; ++i) { FSDataInputStream fis = clientCache.getDfsInputStream(userName, Nfs3Utils.getFileIdPath(handle)); try { readCount = fis.read(offset, readbuffer, 0, count); } catch (IOException e) { // TODO: A cleaner way is to throw a new type of exception // which requires incompatible changes. if (e.getMessage() == "Stream closed") { clientCache.invalidateDfsInputStream(userName, Nfs3Utils.getFileIdPath(handle)); continue; } else { throw e; } } } attrs = Nfs3Utils.getFileAttr(dfsClient, Nfs3Utils.getFileIdPath(handle), iug); if (readCount < count) { LOG.info("Partical read. Asked offset:" + offset + " count:" + count + " and read back:" + readCount + "file size:" + attrs.getSize()); } // HDFS returns -1 for read beyond file size. if (readCount < 0) { readCount = 0; } eof = (offset + readCount) < attrs.getSize() ? false : true; return new READ3Response(Nfs3Status.NFS3_OK, attrs, readCount, eof, ByteBuffer.wrap(readbuffer)); } catch (IOException e) { LOG.warn("Read error: " + e.getClass() + " offset: " + offset + " count: " + count, e); return new READ3Response(Nfs3Status.NFS3ERR_IO); } } @Override public WRITE3Response write(XDR xdr, Channel channel, int xid, SecurityHandler securityHandler, InetAddress client) { WRITE3Response response = new WRITE3Response(Nfs3Status.NFS3_OK); DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } WRITE3Request request = null; try { request = new WRITE3Request(xdr); } catch (IOException e) { LOG.error("Invalid WRITE request"); return new WRITE3Response(Nfs3Status.NFS3ERR_INVAL); } long offset = request.getOffset(); int count = request.getCount(); WriteStableHow stableHow = request.getStableHow(); byte[] data = request.getData().array(); if (data.length < count) { LOG.error("Invalid argument, data size is less than count in request"); return new WRITE3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle handle = request.getHandle(); if (LOG.isDebugEnabled()) { LOG.debug("NFS WRITE fileId: " + handle.getFileId() + " offset: " + offset + " length:" + count + " stableHow:" + stableHow.getValue() + " xid:" + xid); } Nfs3FileAttributes preOpAttr = null; try { preOpAttr = writeManager.getFileAttr(dfsClient, handle, iug); if (preOpAttr == null) { LOG.error("Can't get path for fileId:" + handle.getFileId()); return new WRITE3Response(Nfs3Status.NFS3ERR_STALE); } if (!checkAccessPrivilege(client, AccessPrivilege.READ_WRITE)) { return new WRITE3Response(Nfs3Status.NFS3ERR_ACCES, new WccData(Nfs3Utils.getWccAttr(preOpAttr), preOpAttr), 0, stableHow, Nfs3Constant.WRITE_COMMIT_VERF); } if (LOG.isDebugEnabled()) { LOG.debug("requesed offset=" + offset + " and current filesize=" + preOpAttr.getSize()); } writeManager.handleWrite(dfsClient, request, channel, xid, preOpAttr); } catch (IOException e) { LOG.info("Error writing to fileId " + handle.getFileId() + " at offset " + offset + " and length " + data.length, e); // Try to return WccData Nfs3FileAttributes postOpAttr = null; try { postOpAttr = writeManager.getFileAttr(dfsClient, handle, iug); } catch (IOException e1) { LOG.info("Can't get postOpAttr for fileId: " + handle.getFileId(), e1); } WccAttr attr = preOpAttr == null ? null : Nfs3Utils.getWccAttr(preOpAttr); WccData fileWcc = new WccData(attr, postOpAttr); return new WRITE3Response(Nfs3Status.NFS3ERR_IO, fileWcc, 0, request.getStableHow(), Nfs3Constant.WRITE_COMMIT_VERF); } return null; } @Override public CREATE3Response create(XDR xdr, SecurityHandler securityHandler, InetAddress client) { CREATE3Response response = new CREATE3Response(Nfs3Status.NFS3_OK); DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } CREATE3Request request = null; try { request = new CREATE3Request(xdr); } catch (IOException e) { LOG.error("Invalid CREATE request"); return new CREATE3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle dirHandle = request.getHandle(); String fileName = request.getName(); if (LOG.isDebugEnabled()) { LOG.debug("NFS CREATE dir fileId: " + dirHandle.getFileId() + " filename: " + fileName); } int createMode = request.getMode(); if ((createMode != Nfs3Constant.CREATE_EXCLUSIVE) && request.getObjAttr().getUpdateFields().contains(SetAttrField.SIZE) && request.getObjAttr().getSize() != 0) { LOG.error("Setting file size is not supported when creating file: " + fileName + " dir fileId:" + dirHandle.getFileId()); return new CREATE3Response(Nfs3Status.NFS3ERR_INVAL); } HdfsDataOutputStream fos = null; String dirFileIdPath = Nfs3Utils.getFileIdPath(dirHandle); Nfs3FileAttributes preOpDirAttr = null; Nfs3FileAttributes postOpObjAttr = null; FileHandle fileHandle = null; WccData dirWcc = null; try { preOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); if (preOpDirAttr == null) { LOG.error("Can't get path for dirHandle:" + dirHandle); return new CREATE3Response(Nfs3Status.NFS3ERR_STALE); } if (!checkAccessPrivilege(client, AccessPrivilege.READ_WRITE)) { return new CREATE3Response(Nfs3Status.NFS3ERR_ACCES, null, preOpDirAttr, new WccData(Nfs3Utils.getWccAttr(preOpDirAttr), preOpDirAttr)); } String fileIdPath = Nfs3Utils.getFileIdPath(dirHandle) + "/" + fileName; SetAttr3 setAttr3 = request.getObjAttr(); assert (setAttr3 != null); FsPermission permission = setAttr3.getUpdateFields().contains(SetAttrField.MODE) ? new FsPermission((short) setAttr3.getMode()) : FsPermission.getDefault().applyUMask(umask); EnumSet<CreateFlag> flag = (createMode != Nfs3Constant.CREATE_EXCLUSIVE) ? EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE) : EnumSet.of(CreateFlag.CREATE); fos = new HdfsDataOutputStream(dfsClient.create(fileIdPath, permission, flag, false, replication, blockSize, null, bufferSize, null), statistics); if ((createMode == Nfs3Constant.CREATE_UNCHECKED) || (createMode == Nfs3Constant.CREATE_GUARDED)) { // Set group if it's not specified in the request. if (!setAttr3.getUpdateFields().contains(SetAttrField.GID)) { setAttr3.getUpdateFields().add(SetAttrField.GID); setAttr3.setGid(securityHandler.getGid()); } setattrInternal(dfsClient, fileIdPath, setAttr3, false); } postOpObjAttr = Nfs3Utils.getFileAttr(dfsClient, fileIdPath, iug); dirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(preOpDirAttr), dfsClient, dirFileIdPath, iug); // Add open stream OpenFileCtx openFileCtx = new OpenFileCtx(fos, postOpObjAttr, writeDumpDir + "/" + postOpObjAttr.getFileId(), dfsClient, iug); fileHandle = new FileHandle(postOpObjAttr.getFileId()); if (!writeManager.addOpenFileStream(fileHandle, openFileCtx)) { LOG.warn("Can't add more stream, close it." + " Future write will become append"); fos.close(); fos = null; } else { if (LOG.isDebugEnabled()) { LOG.debug("Opened stream for file:" + fileName + ", fileId:" + fileHandle.getFileId()); } } } catch (IOException e) { LOG.error("Exception", e); if (fos != null) { try { fos.close(); } catch (IOException e1) { LOG.error( "Can't close stream for dirFileId:" + dirHandle.getFileId() + " filename: " + fileName, e1); } } if (dirWcc == null) { try { dirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(preOpDirAttr), dfsClient, dirFileIdPath, iug); } catch (IOException e1) { LOG.error("Can't get postOpDirAttr for dirFileId:" + dirHandle.getFileId(), e1); } } if (e instanceof AccessControlException) { return new CREATE3Response(Nfs3Status.NFS3ERR_ACCES, fileHandle, postOpObjAttr, dirWcc); } else { return new CREATE3Response(Nfs3Status.NFS3ERR_IO, fileHandle, postOpObjAttr, dirWcc); } } return new CREATE3Response(Nfs3Status.NFS3_OK, fileHandle, postOpObjAttr, dirWcc); } @Override public MKDIR3Response mkdir(XDR xdr, SecurityHandler securityHandler, InetAddress client) { MKDIR3Response response = new MKDIR3Response(Nfs3Status.NFS3_OK); DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } MKDIR3Request request = null; try { request = new MKDIR3Request(xdr); } catch (IOException e) { LOG.error("Invalid MKDIR request"); return new MKDIR3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle dirHandle = request.getHandle(); String fileName = request.getName(); if (request.getObjAttr().getUpdateFields().contains(SetAttrField.SIZE)) { LOG.error("Setting file size is not supported when mkdir: " + fileName + " in dirHandle" + dirHandle); return new MKDIR3Response(Nfs3Status.NFS3ERR_INVAL); } String dirFileIdPath = Nfs3Utils.getFileIdPath(dirHandle); Nfs3FileAttributes preOpDirAttr = null; Nfs3FileAttributes postOpDirAttr = null; Nfs3FileAttributes postOpObjAttr = null; FileHandle objFileHandle = null; try { preOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); if (preOpDirAttr == null) { LOG.info("Can't get path for dir fileId:" + dirHandle.getFileId()); return new MKDIR3Response(Nfs3Status.NFS3ERR_STALE); } if (!checkAccessPrivilege(client, AccessPrivilege.READ_WRITE)) { return new MKDIR3Response(Nfs3Status.NFS3ERR_ACCES, null, preOpDirAttr, new WccData(Nfs3Utils.getWccAttr(preOpDirAttr), preOpDirAttr)); } final String fileIdPath = dirFileIdPath + "/" + fileName; SetAttr3 setAttr3 = request.getObjAttr(); FsPermission permission = setAttr3.getUpdateFields().contains(SetAttrField.MODE) ? new FsPermission((short) setAttr3.getMode()) : FsPermission.getDefault().applyUMask(umask); if (!dfsClient.mkdirs(fileIdPath, permission, false)) { WccData dirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(preOpDirAttr), dfsClient, dirFileIdPath, iug); return new MKDIR3Response(Nfs3Status.NFS3ERR_IO, null, null, dirWcc); } // Set group if it's not specified in the request. if (!setAttr3.getUpdateFields().contains(SetAttrField.GID)) { setAttr3.getUpdateFields().add(SetAttrField.GID); setAttr3.setGid(securityHandler.getGid()); } setattrInternal(dfsClient, fileIdPath, setAttr3, false); postOpObjAttr = Nfs3Utils.getFileAttr(dfsClient, fileIdPath, iug); objFileHandle = new FileHandle(postOpObjAttr.getFileId()); WccData dirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(preOpDirAttr), dfsClient, dirFileIdPath, iug); return new MKDIR3Response(Nfs3Status.NFS3_OK, new FileHandle(postOpObjAttr.getFileId()), postOpObjAttr, dirWcc); } catch (IOException e) { LOG.warn("Exception ", e); // Try to return correct WccData if (postOpDirAttr == null) { try { postOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); } catch (IOException e1) { LOG.info("Can't get postOpDirAttr for " + dirFileIdPath, e); } } WccData dirWcc = new WccData(Nfs3Utils.getWccAttr(preOpDirAttr), postOpDirAttr); if (e instanceof AccessControlException) { return new MKDIR3Response(Nfs3Status.NFS3ERR_PERM, objFileHandle, postOpObjAttr, dirWcc); } else { return new MKDIR3Response(Nfs3Status.NFS3ERR_IO, objFileHandle, postOpObjAttr, dirWcc); } } } public READDIR3Response mknod(XDR xdr, SecurityHandler securityHandler, InetAddress client) { return new READDIR3Response(Nfs3Status.NFS3ERR_NOTSUPP); } @Override public REMOVE3Response remove(XDR xdr, SecurityHandler securityHandler, InetAddress client) { REMOVE3Response response = new REMOVE3Response(Nfs3Status.NFS3_OK); DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } REMOVE3Request request = null; try { request = new REMOVE3Request(xdr); } catch (IOException e) { LOG.error("Invalid REMOVE request"); return new REMOVE3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle dirHandle = request.getHandle(); String fileName = request.getName(); if (LOG.isDebugEnabled()) { LOG.debug("NFS REMOVE dir fileId: " + dirHandle.getFileId() + " fileName: " + fileName); } String dirFileIdPath = Nfs3Utils.getFileIdPath(dirHandle); Nfs3FileAttributes preOpDirAttr = null; Nfs3FileAttributes postOpDirAttr = null; try { preOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); if (preOpDirAttr == null) { LOG.info("Can't get path for dir fileId:" + dirHandle.getFileId()); return new REMOVE3Response(Nfs3Status.NFS3ERR_STALE); } String fileIdPath = dirFileIdPath + "/" + fileName; HdfsFileStatus fstat = Nfs3Utils.getFileStatus(dfsClient, fileIdPath); if (fstat == null) { WccData dirWcc = new WccData(Nfs3Utils.getWccAttr(preOpDirAttr), preOpDirAttr); return new REMOVE3Response(Nfs3Status.NFS3ERR_NOENT, dirWcc); } if (fstat.isDir()) { WccData dirWcc = new WccData(Nfs3Utils.getWccAttr(preOpDirAttr), preOpDirAttr); return new REMOVE3Response(Nfs3Status.NFS3ERR_ISDIR, dirWcc); } boolean result = dfsClient.delete(fileIdPath, false); WccData dirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(preOpDirAttr), dfsClient, dirFileIdPath, iug); if (!result) { return new REMOVE3Response(Nfs3Status.NFS3ERR_ACCES, dirWcc); } return new REMOVE3Response(Nfs3Status.NFS3_OK, dirWcc); } catch (IOException e) { LOG.warn("Exception ", e); // Try to return correct WccData if (postOpDirAttr == null) { try { postOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); } catch (IOException e1) { LOG.info("Can't get postOpDirAttr for " + dirFileIdPath, e1); } } WccData dirWcc = new WccData(Nfs3Utils.getWccAttr(preOpDirAttr), postOpDirAttr); if (e instanceof AccessControlException) { return new REMOVE3Response(Nfs3Status.NFS3ERR_PERM, dirWcc); } else { return new REMOVE3Response(Nfs3Status.NFS3ERR_IO, dirWcc); } } } @Override public RMDIR3Response rmdir(XDR xdr, SecurityHandler securityHandler, InetAddress client) { RMDIR3Response response = new RMDIR3Response(Nfs3Status.NFS3_OK); DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } RMDIR3Request request = null; try { request = new RMDIR3Request(xdr); } catch (IOException e) { LOG.error("Invalid RMDIR request"); return new RMDIR3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle dirHandle = request.getHandle(); String fileName = request.getName(); if (LOG.isDebugEnabled()) { LOG.debug("NFS RMDIR dir fileId: " + dirHandle.getFileId() + " fileName: " + fileName); } String dirFileIdPath = Nfs3Utils.getFileIdPath(dirHandle); Nfs3FileAttributes preOpDirAttr = null; Nfs3FileAttributes postOpDirAttr = null; try { preOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); if (preOpDirAttr == null) { LOG.info("Can't get path for dir fileId:" + dirHandle.getFileId()); return new RMDIR3Response(Nfs3Status.NFS3ERR_STALE); } WccData errWcc = new WccData(Nfs3Utils.getWccAttr(preOpDirAttr), preOpDirAttr); if (!checkAccessPrivilege(client, AccessPrivilege.READ_WRITE)) { return new RMDIR3Response(Nfs3Status.NFS3ERR_ACCES, errWcc); } String fileIdPath = dirFileIdPath + "/" + fileName; HdfsFileStatus fstat = Nfs3Utils.getFileStatus(dfsClient, fileIdPath); if (fstat == null) { return new RMDIR3Response(Nfs3Status.NFS3ERR_NOENT, errWcc); } if (!fstat.isDir()) { return new RMDIR3Response(Nfs3Status.NFS3ERR_NOTDIR, errWcc); } if (fstat.getChildrenNum() > 0) { return new RMDIR3Response(Nfs3Status.NFS3ERR_NOTEMPTY, errWcc); } boolean result = dfsClient.delete(fileIdPath, false); WccData dirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(preOpDirAttr), dfsClient, dirFileIdPath, iug); if (!result) { return new RMDIR3Response(Nfs3Status.NFS3ERR_ACCES, dirWcc); } return new RMDIR3Response(Nfs3Status.NFS3_OK, dirWcc); } catch (IOException e) { LOG.warn("Exception ", e); // Try to return correct WccData if (postOpDirAttr == null) { try { postOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); } catch (IOException e1) { LOG.info("Can't get postOpDirAttr for " + dirFileIdPath, e1); } } WccData dirWcc = new WccData(Nfs3Utils.getWccAttr(preOpDirAttr), postOpDirAttr); if (e instanceof AccessControlException) { return new RMDIR3Response(Nfs3Status.NFS3ERR_PERM, dirWcc); } else { return new RMDIR3Response(Nfs3Status.NFS3ERR_IO, dirWcc); } } } @Override public RENAME3Response rename(XDR xdr, SecurityHandler securityHandler, InetAddress client) { RENAME3Response response = new RENAME3Response(Nfs3Status.NFS3_OK); DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } RENAME3Request request = null; try { request = new RENAME3Request(xdr); } catch (IOException e) { LOG.error("Invalid RENAME request"); return new RENAME3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle fromHandle = request.getFromDirHandle(); String fromName = request.getFromName(); FileHandle toHandle = request.getToDirHandle(); String toName = request.getToName(); if (LOG.isDebugEnabled()) { LOG.debug("NFS RENAME from: " + fromHandle.getFileId() + "/" + fromName + " to: " + toHandle.getFileId() + "/" + toName); } String fromDirFileIdPath = Nfs3Utils.getFileIdPath(fromHandle); String toDirFileIdPath = Nfs3Utils.getFileIdPath(toHandle); Nfs3FileAttributes fromPreOpAttr = null; Nfs3FileAttributes toPreOpAttr = null; WccData fromDirWcc = null; WccData toDirWcc = null; try { fromPreOpAttr = Nfs3Utils.getFileAttr(dfsClient, fromDirFileIdPath, iug); if (fromPreOpAttr == null) { LOG.info("Can't get path for fromHandle fileId:" + fromHandle.getFileId()); return new RENAME3Response(Nfs3Status.NFS3ERR_STALE); } toPreOpAttr = Nfs3Utils.getFileAttr(dfsClient, toDirFileIdPath, iug); if (toPreOpAttr == null) { LOG.info("Can't get path for toHandle fileId:" + toHandle.getFileId()); return new RENAME3Response(Nfs3Status.NFS3ERR_STALE); } if (!checkAccessPrivilege(client, AccessPrivilege.READ_WRITE)) { WccData fromWcc = new WccData(Nfs3Utils.getWccAttr(fromPreOpAttr), fromPreOpAttr); WccData toWcc = new WccData(Nfs3Utils.getWccAttr(toPreOpAttr), toPreOpAttr); return new RENAME3Response(Nfs3Status.NFS3ERR_ACCES, fromWcc, toWcc); } String src = fromDirFileIdPath + "/" + fromName; String dst = toDirFileIdPath + "/" + toName; dfsClient.rename(src, dst, Options.Rename.NONE); // Assemble the reply fromDirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(fromPreOpAttr), dfsClient, fromDirFileIdPath, iug); toDirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(toPreOpAttr), dfsClient, toDirFileIdPath, iug); return new RENAME3Response(Nfs3Status.NFS3_OK, fromDirWcc, toDirWcc); } catch (IOException e) { LOG.warn("Exception ", e); // Try to return correct WccData try { fromDirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(fromPreOpAttr), dfsClient, fromDirFileIdPath, iug); toDirWcc = Nfs3Utils.createWccData(Nfs3Utils.getWccAttr(toPreOpAttr), dfsClient, toDirFileIdPath, iug); } catch (IOException e1) { LOG.info("Can't get postOpDirAttr for " + fromDirFileIdPath + " or" + toDirFileIdPath, e1); } if (e instanceof AccessControlException) { return new RENAME3Response(Nfs3Status.NFS3ERR_PERM, fromDirWcc, toDirWcc); } else { return new RENAME3Response(Nfs3Status.NFS3ERR_IO, fromDirWcc, toDirWcc); } } } @Override public SYMLINK3Response symlink(XDR xdr, SecurityHandler securityHandler, InetAddress client) { SYMLINK3Response response = new SYMLINK3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_WRITE)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } SYMLINK3Request request = null; try { request = new SYMLINK3Request(xdr); } catch (IOException e) { LOG.error("Invalid SYMLINK request"); response.setStatus(Nfs3Status.NFS3ERR_INVAL); return response; } FileHandle dirHandle = request.getHandle(); String name = request.getName(); String symData = request.getSymData(); String linkDirIdPath = Nfs3Utils.getFileIdPath(dirHandle); // Don't do any name check to source path, just leave it to HDFS String linkIdPath = linkDirIdPath + "/" + name; if (LOG.isDebugEnabled()) { LOG.debug("NFS SYMLINK, target: " + symData + " link: " + linkIdPath); } try { WccData dirWcc = response.getDirWcc(); WccAttr preOpAttr = Nfs3Utils.getWccAttr(dfsClient, linkDirIdPath); dirWcc.setPreOpAttr(preOpAttr); dfsClient.createSymlink(symData, linkIdPath, false); // Set symlink attr is considered as to change the attr of the target // file. So no need to set symlink attr here after it's created. HdfsFileStatus linkstat = dfsClient.getFileLinkInfo(linkIdPath); Nfs3FileAttributes objAttr = Nfs3Utils.getNfs3FileAttrFromFileStatus(linkstat, iug); dirWcc.setPostOpAttr(Nfs3Utils.getFileAttr(dfsClient, linkDirIdPath, iug)); return new SYMLINK3Response(Nfs3Status.NFS3_OK, new FileHandle(objAttr.getFileId()), objAttr, dirWcc); } catch (IOException e) { LOG.warn("Exception:" + e); response.setStatus(Nfs3Status.NFS3ERR_IO); return response; } } public READDIR3Response link(XDR xdr, SecurityHandler securityHandler, InetAddress client) { return new READDIR3Response(Nfs3Status.NFS3ERR_NOTSUPP); } /** * Used by readdir and readdirplus to get dirents. It retries the listing if * the startAfter can't be found anymore. */ private DirectoryListing listPaths(DFSClient dfsClient, String dirFileIdPath, byte[] startAfter) throws IOException { DirectoryListing dlisting = null; try { dlisting = dfsClient.listPaths(dirFileIdPath, startAfter); } catch (RemoteException e) { IOException io = e.unwrapRemoteException(); if (!(io instanceof DirectoryListingStartAfterNotFoundException)) { throw io; } // This happens when startAfter was just deleted LOG.info("Cookie cound't be found: " + new String(startAfter) + ", do listing from beginning"); dlisting = dfsClient.listPaths(dirFileIdPath, HdfsFileStatus.EMPTY_NAME); } return dlisting; } @Override public READDIR3Response readdir(XDR xdr, SecurityHandler securityHandler, InetAddress client) { READDIR3Response response = new READDIR3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } READDIR3Request request = null; try { request = new READDIR3Request(xdr); } catch (IOException e) { LOG.error("Invalid READDIR request"); return new READDIR3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle handle = request.getHandle(); long cookie = request.getCookie(); if (cookie < 0) { LOG.error("Invalid READDIR request, with negitve cookie:" + cookie); return new READDIR3Response(Nfs3Status.NFS3ERR_INVAL); } long count = request.getCount(); if (count <= 0) { LOG.info("Nonpositive count in invalid READDIR request:" + count); return new READDIR3Response(Nfs3Status.NFS3_OK); } if (LOG.isDebugEnabled()) { LOG.debug("NFS READDIR fileId: " + handle.getFileId() + " cookie: " + cookie + " count: " + count); } HdfsFileStatus dirStatus = null; DirectoryListing dlisting = null; Nfs3FileAttributes postOpAttr = null; long dotdotFileId = 0; try { String dirFileIdPath = Nfs3Utils.getFileIdPath(handle); dirStatus = dfsClient.getFileInfo(dirFileIdPath); if (dirStatus == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); return new READDIR3Response(Nfs3Status.NFS3ERR_STALE); } if (!dirStatus.isDir()) { LOG.error("Can't readdir for regular file, fileId:" + handle.getFileId()); return new READDIR3Response(Nfs3Status.NFS3ERR_NOTDIR); } long cookieVerf = request.getCookieVerf(); if ((cookieVerf != 0) && (cookieVerf != dirStatus.getModificationTime())) { LOG.error("CookierVerf mismatch. request cookierVerf:" + cookieVerf + " dir cookieVerf:" + dirStatus.getModificationTime()); return new READDIR3Response(Nfs3Status.NFS3ERR_BAD_COOKIE); } if (cookie == 0) { // Get dotdot fileId String dotdotFileIdPath = dirFileIdPath + "/.."; HdfsFileStatus dotdotStatus = dfsClient.getFileInfo(dotdotFileIdPath); if (dotdotStatus == null) { // This should not happen throw new IOException("Can't get path for handle path:" + dotdotFileIdPath); } dotdotFileId = dotdotStatus.getFileId(); } // Get the list from the resume point byte[] startAfter; if (cookie == 0) { startAfter = HdfsFileStatus.EMPTY_NAME; } else { String inodeIdPath = Nfs3Utils.getFileIdPath(cookie); startAfter = inodeIdPath.getBytes(); } dlisting = listPaths(dfsClient, dirFileIdPath, startAfter); postOpAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); if (postOpAttr == null) { LOG.error("Can't get path for fileId:" + handle.getFileId()); return new READDIR3Response(Nfs3Status.NFS3ERR_STALE); } } catch (IOException e) { LOG.warn("Exception ", e); return new READDIR3Response(Nfs3Status.NFS3ERR_IO); } /** * Set up the dirents in the response. fileId is used as the cookie with one * exception. Linux client can either be stuck with "ls" command (on REHL) * or report "Too many levels of symbolic links" (Ubuntu). * * The problem is that, only two items returned, "." and ".." when the * namespace is empty. Both of them are "/" with the same cookie(root * fileId). Linux client doesn't think such a directory is a real directory. * Even though NFS protocol specifies cookie is an opaque data, Linux client * somehow doesn't like an empty dir returns same cookie for both "." and * "..". * * The workaround is to use 0 as the cookie for "." and always return "." as * the first entry in readdir/readdirplus response. */ HdfsFileStatus[] fstatus = dlisting.getPartialListing(); int n = (int) Math.min(fstatus.length, count - 2); boolean eof = (n < fstatus.length) ? false : (dlisting.getRemainingEntries() == 0); Entry3[] entries; if (cookie == 0) { entries = new Entry3[n + 2]; entries[0] = new READDIR3Response.Entry3(postOpAttr.getFileId(), ".", 0); entries[1] = new READDIR3Response.Entry3(dotdotFileId, "..", dotdotFileId); for (int i = 2; i < n + 2; i++) { entries[i] = new READDIR3Response.Entry3(fstatus[i - 2].getFileId(), fstatus[i - 2].getLocalName(), fstatus[i - 2].getFileId()); } } else { // Resume from last readdirplus. If the cookie is "..", the result // list is up the directory content since HDFS uses name as resume point. entries = new Entry3[n]; for (int i = 0; i < n; i++) { entries[i] = new READDIR3Response.Entry3(fstatus[i].getFileId(), fstatus[i].getLocalName(), fstatus[i].getFileId()); } } DirList3 dirList = new READDIR3Response.DirList3(entries, eof); return new READDIR3Response(Nfs3Status.NFS3_OK, postOpAttr, dirStatus.getModificationTime(), dirList); } public READDIRPLUS3Response readdirplus(XDR xdr, SecurityHandler securityHandler, InetAddress client) { if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_ACCES); } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_SERVERFAULT); } READDIRPLUS3Request request = null; try { request = new READDIRPLUS3Request(xdr); } catch (IOException e) { LOG.error("Invalid READDIRPLUS request"); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle handle = request.getHandle(); long cookie = request.getCookie(); if (cookie < 0) { LOG.error("Invalid READDIRPLUS request, with negitve cookie:" + cookie); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_INVAL); } long dirCount = request.getDirCount(); if (dirCount <= 0) { LOG.info("Nonpositive dircount in invalid READDIRPLUS request:" + dirCount); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_INVAL); } int maxCount = request.getMaxCount(); if (maxCount <= 0) { LOG.info("Nonpositive maxcount in invalid READDIRPLUS request:" + maxCount); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_INVAL); } if (LOG.isDebugEnabled()) { LOG.debug("NFS READDIRPLUS fileId: " + handle.getFileId() + " cookie: " + cookie + " dirCount: " + dirCount + " maxCount: " + maxCount); } HdfsFileStatus dirStatus; DirectoryListing dlisting = null; Nfs3FileAttributes postOpDirAttr = null; long dotdotFileId = 0; try { String dirFileIdPath = Nfs3Utils.getFileIdPath(handle); dirStatus = dfsClient.getFileInfo(dirFileIdPath); if (dirStatus == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_STALE); } if (!dirStatus.isDir()) { LOG.error("Can't readdirplus for regular file, fileId:" + handle.getFileId()); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_NOTDIR); } long cookieVerf = request.getCookieVerf(); if ((cookieVerf != 0) && (cookieVerf != dirStatus.getModificationTime())) { LOG.error("CookierVerf mismatch. request cookierVerf:" + cookieVerf + " dir cookieVerf:" + dirStatus.getModificationTime()); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_BAD_COOKIE); } if (cookie == 0) { // Get dotdot fileId String dotdotFileIdPath = dirFileIdPath + "/.."; HdfsFileStatus dotdotStatus = dfsClient.getFileInfo(dotdotFileIdPath); if (dotdotStatus == null) { // This should not happen throw new IOException("Can't get path for handle path:" + dotdotFileIdPath); } dotdotFileId = dotdotStatus.getFileId(); } // Get the list from the resume point byte[] startAfter; if (cookie == 0) { startAfter = HdfsFileStatus.EMPTY_NAME; } else { String inodeIdPath = Nfs3Utils.getFileIdPath(cookie); startAfter = inodeIdPath.getBytes(); } dlisting = listPaths(dfsClient, dirFileIdPath, startAfter); postOpDirAttr = Nfs3Utils.getFileAttr(dfsClient, dirFileIdPath, iug); if (postOpDirAttr == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_STALE); } } catch (IOException e) { LOG.warn("Exception ", e); return new READDIRPLUS3Response(Nfs3Status.NFS3ERR_IO); } // Set up the dirents in the response HdfsFileStatus[] fstatus = dlisting.getPartialListing(); int n = (int) Math.min(fstatus.length, dirCount - 2); boolean eof = (n < fstatus.length) ? false : (dlisting.getRemainingEntries() == 0); READDIRPLUS3Response.EntryPlus3[] entries; if (cookie == 0) { entries = new READDIRPLUS3Response.EntryPlus3[n + 2]; entries[0] = new READDIRPLUS3Response.EntryPlus3(postOpDirAttr.getFileId(), ".", 0, postOpDirAttr, new FileHandle(postOpDirAttr.getFileId())); entries[1] = new READDIRPLUS3Response.EntryPlus3(dotdotFileId, "..", dotdotFileId, postOpDirAttr, new FileHandle(dotdotFileId)); for (int i = 2; i < n + 2; i++) { long fileId = fstatus[i - 2].getFileId(); FileHandle childHandle = new FileHandle(fileId); Nfs3FileAttributes attr; try { attr = writeManager.getFileAttr(dfsClient, childHandle, iug); } catch (IOException e) { LOG.error("Can't get file attributes for fileId:" + fileId, e); continue; } entries[i] = new READDIRPLUS3Response.EntryPlus3(fileId, fstatus[i - 2].getLocalName(), fileId, attr, childHandle); } } else { // Resume from last readdirplus. If the cookie is "..", the result // list is up the directory content since HDFS uses name as resume point. entries = new READDIRPLUS3Response.EntryPlus3[n]; for (int i = 0; i < n; i++) { long fileId = fstatus[i].getFileId(); FileHandle childHandle = new FileHandle(fileId); Nfs3FileAttributes attr; try { attr = writeManager.getFileAttr(dfsClient, childHandle, iug); } catch (IOException e) { LOG.error("Can't get file attributes for fileId:" + fileId, e); continue; } entries[i] = new READDIRPLUS3Response.EntryPlus3(fileId, fstatus[i].getLocalName(), fileId, attr, childHandle); } } DirListPlus3 dirListPlus = new READDIRPLUS3Response.DirListPlus3(entries, eof); return new READDIRPLUS3Response(Nfs3Status.NFS3_OK, postOpDirAttr, dirStatus.getModificationTime(), dirListPlus); } @Override public FSSTAT3Response fsstat(XDR xdr, SecurityHandler securityHandler, InetAddress client) { FSSTAT3Response response = new FSSTAT3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } FSSTAT3Request request = null; try { request = new FSSTAT3Request(xdr); } catch (IOException e) { LOG.error("Invalid FSSTAT request"); return new FSSTAT3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle handle = request.getHandle(); if (LOG.isDebugEnabled()) { LOG.debug("NFS FSSTAT fileId: " + handle.getFileId()); } try { // Use superUserClient to get file system status FsStatus fsStatus = superUserClient.getDiskStatus(); long totalBytes = fsStatus.getCapacity(); long freeBytes = fsStatus.getRemaining(); Nfs3FileAttributes attrs = writeManager.getFileAttr(dfsClient, handle, iug); if (attrs == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); return new FSSTAT3Response(Nfs3Status.NFS3ERR_STALE); } long maxFsObjects = config.getLong("dfs.max.objects", 0); if (maxFsObjects == 0) { // A value of zero in HDFS indicates no limit to the number // of objects that dfs supports. Using Integer.MAX_VALUE instead of // Long.MAX_VALUE so 32bit client won't complain. maxFsObjects = Integer.MAX_VALUE; } return new FSSTAT3Response(Nfs3Status.NFS3_OK, attrs, totalBytes, freeBytes, freeBytes, maxFsObjects, maxFsObjects, maxFsObjects, 0); } catch (IOException e) { LOG.warn("Exception ", e); return new FSSTAT3Response(Nfs3Status.NFS3ERR_IO); } } @Override public FSINFO3Response fsinfo(XDR xdr, SecurityHandler securityHandler, InetAddress client) { FSINFO3Response response = new FSINFO3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } FSINFO3Request request = null; try { request = new FSINFO3Request(xdr); } catch (IOException e) { LOG.error("Invalid FSINFO request"); return new FSINFO3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle handle = request.getHandle(); if (LOG.isDebugEnabled()) { LOG.debug("NFS FSINFO fileId: " + handle.getFileId()); } try { int rtmax = config.getInt(Nfs3Constant.MAX_READ_TRANSFER_SIZE_KEY, Nfs3Constant.MAX_READ_TRANSFER_SIZE_DEFAULT); int wtmax = config.getInt(Nfs3Constant.MAX_WRITE_TRANSFER_SIZE_KEY, Nfs3Constant.MAX_WRITE_TRANSFER_SIZE_DEFAULT); int dtperf = config.getInt(Nfs3Constant.MAX_READDIR_TRANSFER_SIZE_KEY, Nfs3Constant.MAX_READDIR_TRANSFER_SIZE_DEFAULT); Nfs3FileAttributes attrs = Nfs3Utils.getFileAttr(dfsClient, Nfs3Utils.getFileIdPath(handle), iug); if (attrs == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); return new FSINFO3Response(Nfs3Status.NFS3ERR_STALE); } int fsProperty = Nfs3Constant.FSF3_CANSETTIME | Nfs3Constant.FSF3_HOMOGENEOUS; return new FSINFO3Response(Nfs3Status.NFS3_OK, attrs, rtmax, rtmax, 1, wtmax, wtmax, 1, dtperf, Long.MAX_VALUE, new NfsTime(1), fsProperty); } catch (IOException e) { LOG.warn("Exception ", e); return new FSINFO3Response(Nfs3Status.NFS3ERR_IO); } } @Override public PATHCONF3Response pathconf(XDR xdr, SecurityHandler securityHandler, InetAddress client) { PATHCONF3Response response = new PATHCONF3Response(Nfs3Status.NFS3_OK); if (!checkAccessPrivilege(client, AccessPrivilege.READ_ONLY)) { response.setStatus(Nfs3Status.NFS3ERR_ACCES); return response; } DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } PATHCONF3Request request = null; try { request = new PATHCONF3Request(xdr); } catch (IOException e) { LOG.error("Invalid PATHCONF request"); return new PATHCONF3Response(Nfs3Status.NFS3ERR_INVAL); } FileHandle handle = request.getHandle(); Nfs3FileAttributes attrs; if (LOG.isDebugEnabled()) { LOG.debug("NFS PATHCONF fileId: " + handle.getFileId()); } try { attrs = Nfs3Utils.getFileAttr(dfsClient, Nfs3Utils.getFileIdPath(handle), iug); if (attrs == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); return new PATHCONF3Response(Nfs3Status.NFS3ERR_STALE); } return new PATHCONF3Response(Nfs3Status.NFS3_OK, attrs, 0, HdfsConstants.MAX_PATH_LENGTH, true, false, false, true); } catch (IOException e) { LOG.warn("Exception ", e); return new PATHCONF3Response(Nfs3Status.NFS3ERR_IO); } } @Override public COMMIT3Response commit(XDR xdr, Channel channel, int xid, SecurityHandler securityHandler, InetAddress client) { COMMIT3Response response = new COMMIT3Response(Nfs3Status.NFS3_OK); DFSClient dfsClient = clientCache.getDfsClient(securityHandler.getUser()); if (dfsClient == null) { response.setStatus(Nfs3Status.NFS3ERR_SERVERFAULT); return response; } COMMIT3Request request = null; try { request = new COMMIT3Request(xdr); } catch (IOException e) { LOG.error("Invalid COMMIT request"); response.setStatus(Nfs3Status.NFS3ERR_INVAL); return response; } FileHandle handle = request.getHandle(); if (LOG.isDebugEnabled()) { LOG.debug("NFS COMMIT fileId: " + handle.getFileId() + " offset=" + request.getOffset() + " count=" + request.getCount()); } String fileIdPath = Nfs3Utils.getFileIdPath(handle); Nfs3FileAttributes preOpAttr = null; try { preOpAttr = Nfs3Utils.getFileAttr(dfsClient, fileIdPath, iug); if (preOpAttr == null) { LOG.info("Can't get path for fileId:" + handle.getFileId()); return new COMMIT3Response(Nfs3Status.NFS3ERR_STALE); } if (!checkAccessPrivilege(client, AccessPrivilege.READ_WRITE)) { return new COMMIT3Response(Nfs3Status.NFS3ERR_ACCES, new WccData(Nfs3Utils.getWccAttr(preOpAttr), preOpAttr), Nfs3Constant.WRITE_COMMIT_VERF); } long commitOffset = (request.getCount() == 0) ? 0 : (request.getOffset() + request.getCount()); // Insert commit as an async request writeManager.handleCommit(dfsClient, handle, commitOffset, channel, xid, preOpAttr); return null; } catch (IOException e) { LOG.warn("Exception ", e); Nfs3FileAttributes postOpAttr = null; try { postOpAttr = writeManager.getFileAttr(dfsClient, handle, iug); } catch (IOException e1) { LOG.info("Can't get postOpAttr for fileId: " + handle.getFileId(), e1); } WccData fileWcc = new WccData(Nfs3Utils.getWccAttr(preOpAttr), postOpAttr); return new COMMIT3Response(Nfs3Status.NFS3ERR_IO, fileWcc, Nfs3Constant.WRITE_COMMIT_VERF); } } private SecurityHandler getSecurityHandler(Credentials credentials, Verifier verifier) { if (credentials instanceof CredentialsSys) { return new SysSecurityHandler((CredentialsSys) credentials, iug); } else { // TODO: support GSS and handle other cases return null; } } @Override public void handleInternal(ChannelHandlerContext ctx, RpcInfo info) { RpcCall rpcCall = (RpcCall) info.header(); final NFSPROC3 nfsproc3 = NFSPROC3.fromValue(rpcCall.getProcedure()); int xid = rpcCall.getXid(); byte[] data = new byte[info.data().readableBytes()]; info.data().readBytes(data); XDR xdr = new XDR(data); XDR out = new XDR(); InetAddress client = ((InetSocketAddress) info.remoteAddress()).getAddress(); Channel channel = info.channel(); Credentials credentials = rpcCall.getCredential(); // Ignore auth only for NFSPROC3_NULL, especially for Linux clients. if (nfsproc3 != NFSPROC3.NULL) { if (credentials.getFlavor() != AuthFlavor.AUTH_SYS && credentials.getFlavor() != AuthFlavor.RPCSEC_GSS) { LOG.info("Wrong RPC AUTH flavor, " + credentials.getFlavor() + " is not AUTH_SYS or RPCSEC_GSS."); XDR reply = new XDR(); RpcDeniedReply rdr = new RpcDeniedReply(xid, RpcReply.ReplyState.MSG_ACCEPTED, RpcDeniedReply.RejectState.AUTH_ERROR, new VerifierNone()); rdr.write(reply); ChannelBuffer buf = ChannelBuffers.wrappedBuffer(reply.asReadOnlyWrap().buffer()); RpcResponse rsp = new RpcResponse(buf, info.remoteAddress()); RpcUtil.sendRpcResponse(ctx, rsp); return; } } if (!isIdempotent(rpcCall)) { RpcCallCache.CacheEntry entry = rpcCallCache.checkOrAddToCache(client, xid); if (entry != null) { // in cache if (entry.isCompleted()) { LOG.info("Sending the cached reply to retransmitted request " + xid); RpcUtil.sendRpcResponse(ctx, entry.getResponse()); return; } else { // else request is in progress LOG.info("Retransmitted request, transaction still in progress " + xid); // Ignore the request and do nothing return; } } } SecurityHandler securityHandler = getSecurityHandler(credentials, rpcCall.getVerifier()); NFS3Response response = null; if (nfsproc3 == NFSPROC3.NULL) { response = nullProcedure(); } else if (nfsproc3 == NFSPROC3.GETATTR) { response = getattr(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.SETATTR) { response = setattr(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.LOOKUP) { response = lookup(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.ACCESS) { response = access(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.READLINK) { response = readlink(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.READ) { if (LOG.isDebugEnabled()) { LOG.debug(Nfs3Utils.READ_RPC_START + xid); } response = read(xdr, securityHandler, client); if (LOG.isDebugEnabled() && (nfsproc3 == NFSPROC3.READ)) { LOG.debug(Nfs3Utils.READ_RPC_END + xid); } } else if (nfsproc3 == NFSPROC3.WRITE) { if (LOG.isDebugEnabled()) { LOG.debug(Nfs3Utils.WRITE_RPC_START + xid); } response = write(xdr, channel, xid, securityHandler, client); // Write end debug trace is in Nfs3Utils.writeChannel } else if (nfsproc3 == NFSPROC3.CREATE) { response = create(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.MKDIR) { response = mkdir(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.SYMLINK) { response = symlink(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.MKNOD) { response = mknod(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.REMOVE) { response = remove(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.RMDIR) { response = rmdir(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.RENAME) { response = rename(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.LINK) { response = link(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.READDIR) { response = readdir(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.READDIRPLUS) { response = readdirplus(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.FSSTAT) { response = fsstat(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.FSINFO) { response = fsinfo(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.PATHCONF) { response = pathconf(xdr, securityHandler, client); } else if (nfsproc3 == NFSPROC3.COMMIT) { response = commit(xdr, channel, xid, securityHandler, client); } else { // Invalid procedure RpcAcceptedReply.getInstance(xid, RpcAcceptedReply.AcceptState.PROC_UNAVAIL, new VerifierNone()) .write(out); } if (response == null) { if (LOG.isDebugEnabled()) { LOG.debug("No sync response, expect an async response for request XID=" + rpcCall.getXid()); } return; } // TODO: currently we just return VerifierNone out = response.writeHeaderAndResponse(out, xid, new VerifierNone()); ChannelBuffer buf = ChannelBuffers.wrappedBuffer(out.asReadOnlyWrap().buffer()); RpcResponse rsp = new RpcResponse(buf, info.remoteAddress()); if (!isIdempotent(rpcCall)) { rpcCallCache.callCompleted(client, xid, rsp); } RpcUtil.sendRpcResponse(ctx, rsp); } @Override protected boolean isIdempotent(RpcCall call) { final NFSPROC3 nfsproc3 = NFSPROC3.fromValue(call.getProcedure()); return nfsproc3 == null || nfsproc3.isIdempotent(); } private boolean checkAccessPrivilege(final InetAddress client, final AccessPrivilege expected) { AccessPrivilege access = exports.getAccessPrivilege(client); if (access == AccessPrivilege.NONE) { return false; } if (access == AccessPrivilege.READ_ONLY && expected == AccessPrivilege.READ_WRITE) { return false; } return true; } @VisibleForTesting WriteManager getWriteManager() { return this.writeManager; } }