org.apache.hadoop.fs.nfs.NFSv3FileSystem.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.fs.nfs.NFSv3FileSystem.java

Source

/**
 * Copyright 2014 NetApp Inc. All Rights Reserved.
 *
 * Licensed 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.fs.nfs;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.BufferedFSInputStream;
import org.apache.hadoop.fs.CreateFlag;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileAlreadyExistsException;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.ParentNotDirectoryException;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.nfs.NfsFileType;
import org.apache.hadoop.nfs.nfs3.FileHandle;
import org.apache.hadoop.nfs.nfs3.Nfs3Constant;
import org.apache.hadoop.nfs.nfs3.Nfs3DirList;
import org.apache.hadoop.nfs.nfs3.Nfs3DirList.Nfs3DirEntry;
import org.apache.hadoop.nfs.nfs3.Nfs3FileAttributes;
import org.apache.hadoop.nfs.nfs3.Nfs3SetAttr;
import org.apache.hadoop.nfs.nfs3.Nfs3Status;
import org.apache.hadoop.nfs.nfs3.request.SetAttr3;
import org.apache.hadoop.nfs.nfs3.request.SetAttr3.SetAttrField;
import org.apache.hadoop.nfs.nfs3.response.CREATE3Response;
import org.apache.hadoop.nfs.nfs3.response.GETATTR3Response;
import org.apache.hadoop.nfs.nfs3.response.MKDIR3Response;
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.security.UserGroupInformation;
import org.apache.hadoop.util.Progressable;
import org.apache.hadoop.fs.nfs.stream.NFSBufferedInputStream;
import org.apache.hadoop.fs.nfs.stream.NFSBufferedOutputStream;
import org.apache.hadoop.fs.nfs.topology.Endpoint;
import org.apache.hadoop.fs.nfs.topology.Mapping;
import org.apache.hadoop.fs.nfs.topology.Namespace;
import org.apache.hadoop.fs.nfs.topology.NamespaceOptions;
import org.apache.hadoop.fs.nfs.topology.SimpleTopologyRouter;
import org.apache.hadoop.fs.nfs.topology.TopologyRouter;
import org.apache.hadoop.oncrpc.security.Credentials;
import org.apache.hadoop.oncrpc.security.CredentialsNone;
import org.apache.hadoop.oncrpc.security.CredentialsSys;

/**
 * <p>
 * A {@link FileSystem} backed by NFS.
 * </p>
 */
@InterfaceAudience.Public
@InterfaceStability.Stable
public class NFSv3FileSystem extends FileSystem {

    private Configuration configuration;
    private Path workingDir;
    private URI uri;
    private Namespace space;

    private HandleCache handleCache;

    private TopologyRouter router;

    // Actual UID, GID, user name and group name to use when creating a new file/directory. Values
    // from the configuration file will overwrite default values defined above.
    private int NFS_UID;
    private int NFS_GID;

    public static final String NFS_URI_SCHEME = "nfs";
    public static final int FILE_HANDLE_CACHE_SIZE = 1048576;
    public static final int DEFAULT_NFS_PORT = 2049;
    public static final int DEFAULT_READ_BLOCK_SIZE_BITS = 20;
    public static final int DEFAULT_WRITE_BLOCK_SIZE_BITS = 20;

    // Default file split size
    public static final int DEFAULT_NFS_SPLIT_SIZE_BITS = 28;

    // Default UID and GID when creating a new file/directory
    public static final int NFS_UID_DEFAULT = 0;
    public static final int NFS_GID_DEFAULT = 0;
    public static final String NFS_USER_NAME_DEFAULT = "root";
    public static final String NFS_GROUP_NAME_DEFAULT = "root";

    public static final String UNIX_DOT_DIR = ".";
    public static final String UNIX_DOT_DOT_DIR = "..";

    public final static Log LOG = LogFactory.getLog(NFSv3FileSystem.class);

    public NFSv3FileSystem() {
        // set store in initialize()
    }

    public NFSv3FileSystem(URI uri, Configuration conf) throws IOException {
        initialize(uri, conf);
    }

    @Override
    public void initialize(URI uri, Configuration conf) throws IOException {

        super.initialize(uri, conf);

        // Save configuration
        this.configuration = conf;
        this.uri = uri;

        // Here, we get either a config option pointing
        // to additional info or a simple URI (in which case we just use defaults)
        if (configuration.get("fs." + NFS_URI_SCHEME + ".configuration") != null) {
            space = Mapping.loadFromFile(configuration.get("fs." + NFS_URI_SCHEME + ".configuration"))
                    .getNamespace(uri);
        } // Create a namespace with defaults
        else {
            LOG.info("The URI " + uri + " has no additional config defined, resorting to defaults");
            space = new Mapping().buildNamespace(uri);
        }

        //Only when two config files are set and both exist, user/group information will be taken
        if (space.getConfiguration().getNFSUserConfigFile() != null
                && space.getConfiguration().getNFSGroupConfigFile() != null) {
            LOG.info("User config file: " + space.getConfiguration().getNFSUserConfigFile());
            LOG.info("Group config file: " + space.getConfiguration().getNFSGroupConfigFile());
            File userConf = new File(space.getConfiguration().getNFSUserConfigFile());
            File groupConf = new File(space.getConfiguration().getNFSGroupConfigFile());

            if (userConf.exists() && groupConf.exists()) {
                UserGroupInformation currentUser = UserGroupInformation.getCurrentUser();
                String currentUserName = currentUser.getShortUserName();
                String currentGroupName = currentUser.getPrimaryGroupName();

                NamespaceOptions option = space.getConfiguration();

                if (option.getUserIdFromUserName(currentUserName) == null
                        || option.getGroupIdFromGroupName(currentGroupName) == null) {
                    throw new IOException("the userid or groupid mapping has not been set for current user/group");
                }
                NFS_UID = Integer.parseInt(option.getUserIdFromUserName(currentUserName));
                NFS_GID = Integer.parseInt(option.getGroupIdFromGroupName(currentGroupName));
            }
        } else {
            // Get configuration from namespace
            NFS_UID = space.getConfiguration().getNfsUid();
            NFS_GID = space.getConfiguration().getNfsGid();
        }
        // Initialize router
        // TODO: Make the router class configurable
        router = new SimpleTopologyRouter();
        router.initialize(this, space, configuration);

        setConf(conf);
        this.handleCache = new HandleCache(FILE_HANDLE_CACHE_SIZE);
        workingDir = getHomeDirectory();
    }

    public long getSplitSize() {
        return (1L << space.getConfiguration().getNfsSplitSizeBits());
    }

    @Override
    public String getScheme() {
        return NFS_URI_SCHEME;
    }

    @Override
    public URI getUri() {
        return uri;
    }

    @Override
    public void close() throws IOException {
        //close() is not implemented
    }

    @Override
    public Path getWorkingDirectory() {
        return workingDir;
    }

    public BlockLocation[] getFileBlockLocations(Path p, long start, long len) throws IOException {
        FileStatus file = getFileStatus(p);
        BlockLocation[] locations = getFileBlockLocations(file, start, len);
        return locations;
    }

    @Override
    public BlockLocation[] getFileBlockLocations(FileStatus file, long start, long len) throws IOException {
        if (file == null) {
            return null;
        }
        if (start < 0 || len < 0) {
            throw new IllegalArgumentException("Invalid start or len parameter");
        }
        if (file.getLen() <= start) {
            LOG.info("END getFileBlockLocations(): length is <= start so no locations");
            return new BlockLocation[0];
        }

        //TODO: See if we can provide the actual endpoints for topology awareness scheduling
        String hostname = space.getUri().getHost();
        int port = space.getUri().getPort();

        String[] name = { hostname + ":" + port };
        String[] host = { hostname };
        BlockLocation locations[] = new BlockLocation[] { new BlockLocation(name, host, 0, file.getLen()) };
        return locations;
    }

    @Override
    public void setWorkingDirectory(Path new_dir) {
        workingDir = makeAbsolute(new_dir);
    }

    private Path makeAbsolute(Path path) {
        if (path == null) {
            return null;
        }
        if (path.isAbsolute() && !path.isAbsoluteAndSchemeAuthorityNull()) {
            return path;
        }
        Path newPath = new Path(workingDir, path);
        return newPath;
    }

    @Override
    public Path getHomeDirectory() {
        Path homeDir = makeQualified(new Path("/user/" + System.getProperty("user.name")));
        return homeDir;
    }

    @Override
    public Path makeQualified(Path path) {
        checkPath(path);
        Path p = path.makeQualified(this.getUri(), this.getWorkingDirectory());
        return p;
    }

    @Override
    public FSDataInputStream open(Path f, int bufferSize) throws IOException {
        f = makeAbsolute(f);
        NFSv3FileSystemStore store = router.getStore(f);

        // Directories cannot be opened for reading
        FileStatus status = getFileStatus(f);
        if (status != null && status.isDirectory()) {
            throw new FileNotFoundException("open(): cannot open a directory " + f + " for reading");
        }

        FileHandle handle = getAndVerifyFileHandle(f);
        if (handle == null) {
            LOG.error("open(): file handle is undefined for file" + f);
            return null;
        }

        return new FSDataInputStream(new BufferedFSInputStream(new NFSBufferedInputStream(store, handle, f,
                this.getConf(), this.getSplitSize(), getCredentials(), statistics), bufferSize));

    }

    @Override
    public FSDataOutputStream append(Path f, int bufferSize, Progressable progress) throws IOException {
        f = makeAbsolute(f);
        NFSv3FileSystemStore store = router.getStore(f);

        FileHandle handle = getAndVerifyFileHandle(f);
        if (handle == null) {
            throw new FileNotFoundException("append(): file " + f + " does not exist");
        }

        return new FSDataOutputStream(
                new NFSBufferedOutputStream(configuration, handle, f, store, getCredentials(), true), statistics);
    }

    @Override
    public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize,
            short replication, long blockSize, Progressable progress) throws IOException {
        f = makeAbsolute(f);
        NFSv3FileSystemStore store = router.getStore(f);

        FileHandle handle = getAndVerifyFileHandle(f);
        if (handle == null) {
            Path parent = f.getParent();

            NFSv3FileSystemStore parentStore = router.getStore(parent);
            if (!parentStore.equals(store)) {
                throw new IOException("Cannot create across two junctions");
            }

            FileHandle parentHandle = null;
            if (parent.isRoot() || isRoot(parent)) {
                parentHandle = store.getRootFileHandle();
            } else {
                if (mkdirs(parent)) {
                    parentHandle = getAndVerifyFileHandle(parent);
                } else {
                    throw new IOException("create(): failed to create parent dir " + parent);
                }
            }

            // At this point, we should have a valid parent handle.
            if (parentHandle == null) {
                throw new IOException("create(): parent handle is null for creating " + f);
            }

            handle = create(store, parentHandle, f.getName(), permission);
        } else {
            FileStatus status = getFileStatus(f);
            if (status != null) {
                if (status.isDirectory()) {
                    throw new FileAlreadyExistsException("create(): path " + f + " is already a directory");
                } else {
                    if (overwrite != true) {
                        throw new FileAlreadyExistsException("create(): file already exists " + f);
                    }
                    truncate(store, handle, 0);
                }
            } else {
                throw new IOException("create(): could not get status of file " + f);
            }
        }
        return new FSDataOutputStream(
                new NFSBufferedOutputStream(configuration, handle, f, store, getCredentials(), false), statistics);
    }

    public FSDataOutputStream createNonRecursive(Path f, FsPermission permission, EnumSet<CreateFlag> flags,
            int bufferSize, short replication, long blockSize, Progressable progress) throws IOException {
        if (flags.contains(CreateFlag.APPEND)) {
            return append(f, bufferSize, progress);
        } else if (flags.contains(CreateFlag.OVERWRITE)) {
            return this.create(f, permission, true, bufferSize, replication, blockSize, progress);
        } else {
            return this.create(f, permission, false, bufferSize, replication, blockSize, progress);
        }
    }

    @Override
    public boolean rename(Path src, Path dst) throws IOException {
        src = makeAbsolute(src);
        dst = makeAbsolute(dst);

        NFSv3FileSystemStore srcStore = router.getStore(src);
        NFSv3FileSystemStore dstStore = router.getStore(dst);

        FileStatus srcStatus;
        FileStatus dstStatus;

        // Cannot rename across filesystems
        if (!srcStore.equals(dstStore)) {
            throw new IOException("rename(): cannot rename src=" + src + " dst=" + dst
                    + " because they use two different junctions");
        }

        // Check status of src and dst paths
        try {
            srcStatus = getFileStatus(src);
        } catch (FileNotFoundException exception) {
            srcStatus = null;
        }

        try {
            dstStatus = getFileStatus(dst);
        } catch (FileNotFoundException exception) {
            dstStatus = null;
        }

        // Source path must exist
        if (srcStatus == null) {
            LOG.warn("rename(): source path " + src + " does not exist");
            return false;
        }

        if (src.getParent() == null) {
            LOG.warn("rename(): root directory " + src + " cannot be renamed");
            return false;
        }

        if (dst.getParent() == null) {
            LOG.warn("rename(): cannot rename directory to root " + dst);
            return false;
        }

        // All the paths must be in the same filesystem
        NFSv3FileSystemStore srcParentStore = router.getStore(src.getParent());
        NFSv3FileSystemStore dstParentStore = router.getStore(dst.getParent());
        if (!srcStore.equals(srcParentStore) && !srcStore.equals(dstParentStore)) {
            throw new IOException("Cannot rename across different junctions");
        }

        FileHandle srcParentHandle = getAndVerifyFileHandle(src.getParent());
        FileHandle dstParentHandle = getAndVerifyFileHandle(dst.getParent());

        if (srcParentHandle == null) {
            LOG.warn("rename(): parent of source " + src + " does not exist");
            return false;
        }

        if (dstParentHandle == null) {
            LOG.warn("rename(): parent of destination " + dst + " does not exist");
            return false;
        }

        int status = Nfs3Status.NFS3ERR_IO;

        // Destination is a file
        if (dstStatus != null && dstStatus.isFile()) {
            if (srcStatus.isDirectory()) {
                LOG.warn("rename(): trying to rename existing directory " + src + " into a file " + dst);
                return false;
            } else if (srcStatus.isFile()) {
                return src.equals(dst);
            } else {
                throw new IOException("rename(): source " + src + " is neither a file nor a directory");
            }
        } // Destination is a directory, so move source into destination
        else if (dstStatus != null && dstStatus.isDirectory()) {
            FileHandle dstHandle = getAndVerifyFileHandle(dst);
            if (dstHandle == null) {
                throw new IOException(
                        "rename(): destination " + dst + " is a directory but its handle cannot be found");
            }
            RENAME3Response rename3Response = srcStore.rename(srcParentHandle, src.getName(), dstHandle,
                    src.getName(), getCredentials());
            status = rename3Response.getStatus();
        } // Destination does not exist
        else if (dstStatus == null) {
            RENAME3Response rename3Response = srcStore.rename(srcParentHandle, src.getName(), dstParentHandle,
                    dst.getName(), getCredentials());
            status = rename3Response.getStatus();
        }

        if (status != Nfs3Status.NFS3_OK) {
            if (status == Nfs3Status.NFS3ERR_INVAL) {
                return false;
            }
            throw new IOException(
                    "rename(): rename of src " + src + " to dst " + dst + "  returned status " + status);
        }

        // Remove old handles
        handleCache.removeAll(Path.getPathWithoutSchemeAndAuthority(src).toString());

        return true;
    }

    @Override
    public boolean delete(Path f, boolean recursive) throws IOException {
        f = makeAbsolute(f);
        NFSv3FileSystemStore store = router.getStore(f);

        FileHandle handle = getAndVerifyFileHandle(f);
        if (handle == null) {
            LOG.warn("delete(): file " + f + " does not exist");
            return false;
        }

        Nfs3FileAttributes attributes = store.getFileAttributes(handle, getCredentials());
        if (attributes == null) {
            throw new IOException("delete(): could not get file attributes for " + f);
        }

        int fileType = attributes.getType();
        if (fileType == NfsFileType.NFSREG.toValue()) {
            return remove(f);
        } else if (fileType == NfsFileType.NFSDIR.toValue()) {
            Set<Path> subPaths = listSubPaths(f);
            if (recursive == false && (subPaths != null && subPaths.isEmpty() == false)) {
                throw new IOException("delete(): directory " + f + " is not empty so it cannot be deleted");
            }

            FileStatus[] files = listStatus(f);
            for (FileStatus fileStatus : files) {
                if (delete(fileStatus.getPath(), recursive) == false) {
                    LOG.warn("delete(): recursive delete failed for " + fileStatus.getPath());
                    return false;
                }
            }
            return rmdir(f);
        } else {
            throw new IOException("delete(): file " + f + " is neither a file nor directory");
        }
    }

    @Override
    public FileStatus[] listStatus(Path f) throws FileNotFoundException, IOException {
        f = makeAbsolute(f);

        FileStatus fileStatus = getFileStatus(f);
        if (!fileStatus.isDirectory()) {
            return new FileStatus[] { fileStatus };
        }

        // Path f is a dir, return a list of all files/dir in this dir
        ArrayList<FileStatus> fileStatusList = new ArrayList<>();
        for (Path path : listSubPaths(f)) {
            FileStatus s1 = getFileStatus(path);
            fileStatusList.add(s1);
        }
        FileStatus[] array = fileStatusList.toArray(new FileStatus[0]);
        return array;
    }

    @Override
    public boolean mkdirs(Path f, FsPermission permission) throws IOException {
        f = makeAbsolute(f);
        NFSv3FileSystemStore store;

        // Capture root paths of all endpoints
        Set<String> rootPaths = new HashSet<>();
        for (Endpoint ep : space.getEndpoints()) {
            if (ep.getPath() != null) {
                rootPaths.add(ep.getPath());
            }
        }

        // Will check up till the root of one of exported paths
        List<String> dirs = new LinkedList<>();
        Path path = Path.getPathWithoutSchemeAndAuthority(f);
        do {
            dirs.add(0, path.getName());
            path = path.getParent();
        } while (!path.isRoot() && !rootPaths.contains(path.toString()));

        store = router.getStore(path);
        FileHandle parentDir = store.getRootFileHandle();

        path = null;
        for (String dir : dirs) {
            if (path == null) {
                path = new Path(Path.SEPARATOR + dir);
            } else {
                path = new Path(path.toString() + Path.SEPARATOR + dir);
            }

            NFSv3FileSystemStore s = router.getStore(path);
            if (!s.equals(store)) {
                throw new IOException("Trying to create directories across junctions");
            }

            FileHandle dirHandle = store.getFileHandle(parentDir, dir, getCredentials());
            if (dirHandle != null) {
                Nfs3FileAttributes attr = store.getFileAttributes(dirHandle, getCredentials());
                if (attr.getType() != NfsFileType.NFSDIR.toValue()) {
                    throw new FileAlreadyExistsException("Trying to make subdirectory inside a file");
                }
                parentDir = dirHandle;
            } else {
                mkdir(store, parentDir, dir, permission);
                parentDir = store.getFileHandle(parentDir, dir, getCredentials());
            }
        }
        return true;
    }

    private void checkNFSStatus(FileHandle handle, Path path, int status, String NFSCall) throws IOException {
        /* If not OK or stale handle, then we are in trouble. */
        if (status != Nfs3Status.NFS3_OK && status != Nfs3Status.NFS3ERR_STALE) {
            String errorMsg = NFSCall + " error: " + status;
            if (path != null) {
                errorMsg += " for path " + path.toUri().getPath();
            }
            throw new IOException(errorMsg);
        }

        /* If handle is stale, remove it from handleCache. */
        if (status == Nfs3Status.NFS3ERR_STALE) {
            handleCache.removeByValue(handle);
            LOG.warn("NFS_GETATTR failed with status=" + status);
        }
    }

    private Boolean remove(Path f) throws IOException {
        NFSv3FileSystemStore store = router.getStore(f);

        // Parent and child must be on the same filesystem
        NFSv3FileSystemStore parentStore = router.getStore(f.getParent());
        if (!parentStore.equals(store)) {
            LOG.error("rmdir(): Parent " + f.getParent() + " and child " + f + " are on different junctions");
            throw new IOException(
                    "rmdir(): Parent " + f.getParent() + " and child " + f + " are on different junctions");
        }

        FileHandle dirHandle = getAndVerifyFileHandle(f.getParent());
        if (dirHandle == null) {
            throw new IOException("remove(): parent of path " + f + " does not exist");
        }

        String pathString = f.toUri().getPath();
        String name = f.getName();
        int status;
        REMOVE3Response remove3Response = store.remove(dirHandle, name, getCredentials());
        status = remove3Response.getStatus();
        if (status != Nfs3Status.NFS3_OK) {
            throw new IOException("remove(): failed for " + f + " with error status " + status);
        }

        // Remove the stale handle from the handle cache
        if (handleCache.get(pathString) != null) {
            handleCache.remove(pathString);
        }
        return true;
    }

    private Boolean rmdir(Path f) throws IOException {
        int status;
        NFSv3FileSystemStore store = router.getStore(f);

        if (f.isRoot() || isRoot(f)) {
            LOG.warn("rmdir(): cannot delete root directory");
            return true;
        }

        // Parent and child must be on the same filesystem
        NFSv3FileSystemStore parentStore = router.getStore(f.getParent());
        if (!parentStore.equals(store)) {
            LOG.error("rmdir(): Parent " + f.getParent() + " and child " + f + " are on different junctions");
            throw new IOException(
                    "rmdir(): Parent " + f.getParent() + " and child " + f + " are on different junctions");
        }

        String pathString = f.toUri().getPath();
        FileHandle parentDirHandle = getAndVerifyFileHandle(f.getParent());
        String name = f.getName();

        if (parentDirHandle == null) {
            throw new IOException("rmdir(): parent dir " + f.getParent() + " does not exist");
        }

        RMDIR3Response rmdir3Response = store.rmdir(parentDirHandle, name, getCredentials());
        status = rmdir3Response.getStatus();
        if (status != Nfs3Status.NFS3_OK) {
            throw new IOException("rmdir(): failed for " + f + " with error status " + status);
        }

        // Remove the stale handle from the handle cache
        if (handleCache.get(pathString) != null) {
            handleCache.remove(pathString);
        }

        return true;
    }

    private Set<Path> listSubPaths(Path f) throws IOException {
        f = makeAbsolute(f);
        NFSv3FileSystemStore store = router.getStore(f);
        Path fsPath = Path.getPathWithoutSchemeAndAuthority(f);
        FileStatus fileStatus = getFileStatus(f);

        // Return null if it is a file
        if (!fileStatus.isDirectory()) {
            return null;
        }

        FileHandle handle = getAndVerifyFileHandle(f);
        if (handle == null) {
            LOG.info("Directory to list does not exist: " + f);
            return null;
        }

        // Read in all entries in this directory
        Set<Path> paths = new TreeSet<>();
        long cookie = 0;
        long cookieVerf = 0;

        // Keep fetching directory entries until the list stops
        while (true) {
            Nfs3DirList dirList = store.getDirectoryList(handle, cookie, cookieVerf, store.getDirListSize(),
                    getCredentials());
            if (dirList != null) {
                List<Nfs3DirEntry> entryList = dirList.getEntries();
                for (Nfs3DirEntry entry : entryList) {
                    cookie = entry.getCookie();
                    // Ignore dot and dot-dot entries
                    if (entry.getName().equals(UNIX_DOT_DIR) || entry.getName().equals(UNIX_DOT_DOT_DIR)
                            || entry.getName().equals(".vsadmin")) {
                        continue;
                    }

                    Path newPath;
                    if (fsPath.isRoot()) {
                        newPath = new Path(Path.SEPARATOR + entry.getName());
                    } else {
                        newPath = new Path(fsPath.toString() + Path.SEPARATOR + entry.getName());
                    }
                    paths.add(newPath);

                }
            }

            // Check for more entries
            if (dirList == null || dirList.isEof()) {
                break;
            } else {
                cookieVerf = dirList.getCookieVerf();
            }
        }

        return paths;
    }

    private boolean mkdir(NFSv3FileSystemStore store, FileHandle dir, String name, FsPermission permission)
            throws IOException {
        int status;
        EnumSet<SetAttrField> updateFields = EnumSet.noneOf(SetAttrField.class);
        /*
         * Note we do not set a specific size for a directory. NFS server should be able to figure it
         * out when creating it. We also not set the mtime and ctime. Use the timestamp at the server
         * machine.
         */
        updateFields.add(SetAttr3.SetAttrField.UID);
        updateFields.add(SetAttr3.SetAttrField.GID);
        updateFields.add(SetAttr3.SetAttrField.MODE);

        Nfs3SetAttr objAttr = new Nfs3SetAttr(permission.toShort(), NFS_UID, NFS_GID, 0, null, null, updateFields);
        MKDIR3Response mkdir3Response = store.mkdir(dir, name, objAttr, getCredentials());
        status = mkdir3Response.getStatus();
        if (status != Nfs3Status.NFS3_OK) {
            if (status == Nfs3Status.NFS3ERR_EXIST) {
                LOG.error("mkdir(): Could not create directory with name " + name);
                throw new FileAlreadyExistsException();
            } else if (status == Nfs3Status.NFS3ERR_NOTDIR) {
                throw new ParentNotDirectoryException();
            } else {
                throw new IOException("mkdir(): returned error status " + status);
            }
        }
        return true;
    }

    private FileHandle create(NFSv3FileSystemStore store, FileHandle dir, String name, FsPermission permission)
            throws IOException {
        EnumSet<SetAttrField> updateFields = EnumSet.noneOf(SetAttrField.class);
        updateFields.add(SetAttr3.SetAttrField.UID);
        updateFields.add(SetAttr3.SetAttrField.GID);
        updateFields.add(SetAttr3.SetAttrField.MODE);
        Nfs3SetAttr objAttr = new Nfs3SetAttr(permission.toShort(), NFS_UID, NFS_GID, 0, null, null, updateFields);

        CREATE3Response create3Response = store.create(dir, name, Nfs3Constant.CREATE_UNCHECKED, objAttr, 0,
                getCredentials());
        int status = create3Response.getStatus();
        if (status != Nfs3Status.NFS3_OK) {
            throw new IOException("create(): returned error status " + status);
        }

        FileHandle handle = create3Response.getObjHandle();
        return handle;
    }

    private void truncate(NFSv3FileSystemStore store, FileHandle handle, long newSize) throws IOException {
        int status;
        EnumSet<SetAttrField> updateFields = EnumSet.noneOf(SetAttrField.class);
        updateFields.add(SetAttr3.SetAttrField.SIZE);
        Nfs3SetAttr objAttr = new Nfs3SetAttr();
        objAttr.setUpdateFields(updateFields);
        objAttr.setSize(newSize);

        SETATTR3Response setAttr3Response = store.setattr(handle, objAttr, false, null, getCredentials());
        status = setAttr3Response.getStatus();
        checkNFSStatus(handle, null, status, "NFS_SETATTR");
    }

    private FileHandle getAndVerifyFileHandle(Path path) throws IOException {
        int status;
        if (path == null) {
            return null;
        }

        path = makeAbsolute(path);
        NFSv3FileSystemStore store = router.getStore(path);
        Path fsPath = Path.getPathWithoutSchemeAndAuthority(path);
        FileHandle handle;

        // Root paths (top root or junctioned-root)
        if (fsPath.isRoot() || isRoot(path)) {
            handle = store.getRootFileHandle();
            GETATTR3Response getAttr3Response = store.getattr(handle, getCredentials());
            status = getAttr3Response.getStatus();
            if (status != Nfs3Status.NFS3_OK) {
                throw new IOException("getAndVerifyHandle(): Could not get attributes for path " + path);
            }
            return handle;
        }

        // Make sure parent and child are in the same junctioned filesystem
        NFSv3FileSystemStore parentStore = router.getStore(path.getParent());
        if (!parentStore.equals(store)) {
            throw new IOException("getAndVerifyHandle(): Parent " + path.getParent() + " and child " + path
                    + " are not on the same filesystem!");
        }

        // If the handle is in the cache and valid, return it 
        handle = handleCache.get(fsPath.toString());
        if (handle != null) {
            GETATTR3Response getAttr3Response = store.getattr(handle, getCredentials());
            status = getAttr3Response.getStatus();
            if (status == Nfs3Status.NFS3_OK) {
                return handle;
            } else {
                // we have a stale handle in the handle cache, remove it
                assert (status == Nfs3Status.NFS3ERR_STALE);
                handleCache.remove(fsPath.toString());
            }
        }

        // else, get the valid parent handle and then lookup for the handle
        FileHandle parentHandle = getAndVerifyFileHandle(path.getParent());
        if (parentHandle == null) {
            LOG.info("getAndVerifyHandle(): Parent path " + path.getParent() + " could not be found");
            return null;
        }

        handle = store.getFileHandle(parentHandle, fsPath.getName(), getCredentials());
        if (handle != null) {
            handleCache.put(fsPath.toString(), handle);
        }
        return handle;
    }

    @Override
    public FileStatus getFileStatus(Path f) throws IOException {
        f = makeAbsolute(f);
        NFSv3FileSystemStore store = router.getStore(f);

        FileHandle handle = getAndVerifyFileHandle(f);
        if (handle == null) {
            throw new FileNotFoundException("getFileStatus(): file " + f + " does not exist");
        }

        Nfs3FileAttributes fileAttr = store.getFileAttributes(handle, getCredentials());
        if (fileAttr == null) {
            throw new IOException("getFileStatus(): could not get attributes of file " + f);
        }

        Boolean isDir = false;
        if (fileAttr.getType() == NfsFileType.NFSDIR.toValue()) {
            isDir = true;
        }

        NamespaceOptions option = space.getConfiguration();

        String fileOwner, fileOwnerGroup;
        Credentials cred = getCredentials();
        if (cred instanceof CredentialsSys) {
            /*The userid to username mapping in the config file*/
            try {
                if (option.getUserNameFromUserId(String.valueOf(fileAttr.getUid())) != null) {
                    fileOwner = option.getUserNameFromUserId(String.valueOf(fileAttr.getUid()));
                } else {
                    fileOwner = String.valueOf(fileAttr.getUid());
                }

                /*The groupid to groupname mapping in the config file*/
                if (option.getGroupNameFromGroupId(String.valueOf(fileAttr.getGid())) != null) {
                    fileOwnerGroup = option.getGroupNameFromGroupId(String.valueOf(fileAttr.getGid()));
                } else {
                    fileOwnerGroup = String.valueOf(fileAttr.getGid());
                }
            } catch (NullPointerException ex) {
                fileOwner = String.valueOf(fileAttr.getUid());
                fileOwnerGroup = String.valueOf(fileAttr.getGid());
            }
        } else if (cred instanceof CredentialsNone) {
            fileOwner = String.valueOf(fileAttr.getUid());
            fileOwnerGroup = String.valueOf(fileAttr.getGid());
        } else {
            throw new IOException("The credential type is not supported!");
        }
        FileStatus fileStatus = new FileStatus(fileAttr.getSize(), isDir, 1, getSplitSize(),
                fileAttr.getMtime().getMilliSeconds(), fileAttr.getAtime().getMilliSeconds(),
                new FsPermission((short) fileAttr.getMode()), fileOwner, fileOwnerGroup,
                f.makeQualified(uri, workingDir));
        return fileStatus;
    }

    protected boolean isRoot(Path path) throws IOException {
        if (space == null) {
            throw new IOException("isRoot(); Namespace is null");
        }
        if (path == null) {
            throw new IOException("isRoot(): Path is null");
        }

        NFSv3FileSystemStore store = router.getStore(path);
        Path fsPath = Path.getPathWithoutSchemeAndAuthority(path);
        return store.getEndpoint().getPath().equals(fsPath.toString());
    }

    protected Credentials getCredentials() throws IOException {
        if (space == null) {
            throw new IOException("No namespace defined!");
        }

        NamespaceOptions options = space.getConfiguration();
        String authScheme = (options.getNfsAuthScheme() == null)
                ? NamespaceOptions.getDefaultOptions().getNfsAuthScheme()
                : options.getNfsAuthScheme();
        if (authScheme.equalsIgnoreCase("AUTH_SYS") || authScheme.equalsIgnoreCase("AUTH_UNIX")) {
            CredentialsSys sys = new CredentialsSys();
            sys.setUID(NFS_UID);
            sys.setGID(NFS_GID);
            sys.setStamp(new Long(System.currentTimeMillis()).intValue());
            return sys;
        } else {
            return new CredentialsNone();
        }
    }

} // End class