org.dcache.nfs.v3.MountServer.java Source code

Java tutorial

Introduction

Here is the source code for org.dcache.nfs.v3.MountServer.java

Source

/*
 * Copyright (c) 2009 - 2015 Deutsches Elektronen-Synchroton,
 * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program (see the file COPYING.LIB for more
 * details); if not, write to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.dcache.nfs.v3;

import com.google.common.base.Splitter;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.io.IOException;
import org.dcache.nfs.v3.xdr.exportnode;
import org.dcache.nfs.v3.xdr.mountbody;
import org.dcache.nfs.v3.xdr.fhandle3;
import org.dcache.nfs.v3.xdr.mountres3;
import org.dcache.nfs.v3.xdr.name;
import org.dcache.nfs.v3.xdr.exports;
import org.dcache.nfs.v3.xdr.mount_protServerStub;
import org.dcache.nfs.v3.xdr.fhstatus;
import org.dcache.nfs.v3.xdr.groups;
import org.dcache.nfs.v3.xdr.dirpath;
import org.dcache.nfs.v3.xdr.mountlist;
import org.dcache.nfs.v3.xdr.groupnode;
import org.dcache.nfs.v3.xdr.mountres3_ok;
import org.dcache.nfs.v3.xdr.mountstat3;
import java.net.InetAddress;
import java.util.*;

import org.dcache.nfs.ChimeraNFSException;
import org.dcache.nfs.ExportFile;
import org.dcache.nfs.FsExport;
import org.dcache.nfs.status.*;
import org.dcache.nfs.vfs.Inode;
import org.dcache.nfs.vfs.PseudoFs;
import org.dcache.nfs.vfs.Stat;
import org.dcache.nfs.vfs.VirtualFileSystem;
import org.dcache.xdr.RpcAuthType;
import org.dcache.xdr.RpcCall;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MountServer extends mount_protServerStub {

    private static final Logger _log = LoggerFactory.getLogger(MountServer.class);
    private final ExportFile _exportFile;
    private final Multimap<String, InetAddress> _mounts = HashMultimap.create();
    private final VirtualFileSystem _vfs;

    /*
     * pseudo flavors as defined in RFC2623
     */
    public final static int RPC_AUTH_GSS_KRB5 = 390003;
    public final static int RPC_AUTH_GSS_KRB5I = 390004;
    public final static int RPC_AUTH_GSS_KRB5P = 390005;

    public MountServer(ExportFile exportFile, VirtualFileSystem fs) {
        super();
        _exportFile = exportFile;
        _vfs = fs;
    }

    @Override
    public void MOUNTPROC3_NULL_3(RpcCall call$) {
        // NOP
    }

    @Override
    public mountres3 MOUNTPROC3_MNT_3(RpcCall call$, dirpath arg1) {

        mountres3 m = new mountres3();

        java.io.File f = new java.io.File(arg1.value);
        String mountPoint = f.getAbsolutePath();
        InetAddress remoteAddress = call$.getTransport().getRemoteSocketAddress().getAddress();
        _log.debug("Mount request for: {}", mountPoint);

        FsExport export = _exportFile.getExport(mountPoint, remoteAddress);
        if (export == null) {
            m.fhs_status = mountstat3.MNT3ERR_ACCES;
            _log.info("Mount deny for: {}:{}", remoteAddress, mountPoint);
            return m;
        }

        m.mountinfo = new mountres3_ok();

        try {

            Inode rootInode = path2Inode(_vfs, mountPoint);
            Stat stat = _vfs.getattr(rootInode);

            if (stat.type() == Stat.Type.SYMLINK) {
                /*
                 * we resolve symlink only once
                 */
                String path = _vfs.readlink(rootInode);
                rootInode = path2Inode(_vfs, path);
                stat = _vfs.getattr(rootInode);
            }

            if (stat.type() != Stat.Type.DIRECTORY) {
                throw new NotDirException("Path is not a directory");
            }

            byte[] b = PseudoFs.pseudoIdToReal(rootInode, export.getIndex()).toNfsHandle();

            m.fhs_status = mountstat3.MNT3_OK;
            m.mountinfo.fhandle = new fhandle3(b);
            m.mountinfo.auth_flavors = exportSecFlavors(export);

            _mounts.put(mountPoint, remoteAddress);

        } catch (ChimeraNFSException e) {
            _log.warn("mount request failed: ", e.getMessage());
            m.fhs_status = e.getStatus();
        } catch (IOException e) {
            m.fhs_status = mountstat3.MNT3ERR_SERVERFAULT;
        }

        return m;

    }

    @Override
    public mountlist MOUNTPROC3_DUMP_3(RpcCall call$) {

        mountlist mFullList = new mountlist();
        mountlist mList = mFullList;
        mList.value = null;

        for (Map.Entry<String, InetAddress> mountEntry : _mounts.entries()) {
            String path = mountEntry.getKey();
            InetAddress remoteAddress = mountEntry.getValue();

            mList.value = new mountbody();
            mList.value.ml_directory = new dirpath(path);
            mList.value.ml_hostname = new name(remoteAddress.getHostName());
            mList.value.ml_next = new mountlist();
            mList.value.ml_next.value = null;
            mList = mList.value.ml_next;
        }

        return mFullList;
    }

    @Override
    public void MOUNTPROC3_UMNT_3(RpcCall call$, dirpath arg1) {
        _mounts.remove(arg1.value, call$.getTransport().getRemoteSocketAddress().getHostName());
    }

    @Override
    public void MOUNTPROC3_UMNTALL_3(RpcCall call$) {
    }

    @Override
    public exports MOUNTPROC3_EXPORT_3(RpcCall call$) {

        exports eFullList = new exports();
        exports eList = eFullList;

        eList.value = null;

        Multimap<String, String> exports = groupBy(_exportFile.getExports());
        for (String path : exports.keySet()) {

            eList.value = new exportnode();
            eList.value.ex_dir = new dirpath(path);
            eList.value.ex_groups = new groups();
            eList.value.ex_groups.value = null;
            groups g = eList.value.ex_groups;

            for (String client : exports.get(path)) {

                g.value = new groupnode();
                g.value.gr_name = new name(client);
                g.value.gr_next = new groups();
                g.value.gr_next.value = null;

                g = g.value.gr_next;
            }

            eList.value.ex_next = new exports();
            eList.value.ex_next.value = null;
            eList = eList.value.ex_next;

        }
        return eFullList;
    }

    /*
     * MOUNT version 1 support for exports, umount and so on
     */
    @Override
    public void MOUNTPROC_NULL_1(RpcCall call$) {
        // ping-pong
    }

    @Override
    public fhstatus MOUNTPROC_MNT_1(RpcCall call$, dirpath arg1) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public mountlist MOUNTPROC_DUMP_1(RpcCall call$) {
        // Same as V3
        return this.MOUNTPROC3_DUMP_3(call$);
    }

    @Override
    public void MOUNTPROC_UMNT_1(RpcCall call$, dirpath arg1) {
        // same as v3
        this.MOUNTPROC3_UMNT_3(call$, arg1);
    }

    @Override
    public void MOUNTPROC_UMNTALL_1(RpcCall call$) {
        // TODO Auto-generated method stub
    }

    @Override
    public exports MOUNTPROC_EXPORT_1(RpcCall call$) {
        // Same as V3
        return this.MOUNTPROC3_EXPORT_3(call$);
    }

    @Override
    public exports MOUNTPROC_EXPORTALL_1(RpcCall call$) {
        // TODO Auto-generated method stub
        return null;
    }

    private static Inode path2Inode(VirtualFileSystem fs, String path) throws ChimeraNFSException, IOException {

        Splitter splitter = Splitter.on('/').omitEmptyStrings();
        Inode inode = fs.getRootInode();

        for (String pathElement : splitter.split(path)) {
            inode = fs.lookup(inode, pathElement);
        }
        return inode;
    }

    private Multimap<String, String> groupBy(Iterable<FsExport> exports) {
        Multimap<String, String> asMultiMap = HashMultimap.create();
        for (FsExport export : exports) {
            asMultiMap.put(export.getPath(), export.client());
        }

        return asMultiMap;
    }

    private int[] exportSecFlavors(FsExport export) throws ChimeraNFSException {
        FsExport.Sec sec = export.getSec();
        int[] supportedFlavors;
        switch (sec) {
        case KRB5:
            supportedFlavors = new int[] { RPC_AUTH_GSS_KRB5, RPC_AUTH_GSS_KRB5I, RPC_AUTH_GSS_KRB5P };
            break;
        case KRB5I:
            supportedFlavors = new int[] { RPC_AUTH_GSS_KRB5I, RPC_AUTH_GSS_KRB5P };
            break;
        case KRB5P:
            supportedFlavors = new int[] { RPC_AUTH_GSS_KRB5P };
            break;
        case SYS:
            supportedFlavors = new int[] { RPC_AUTH_GSS_KRB5, RPC_AUTH_GSS_KRB5I, RPC_AUTH_GSS_KRB5P,
                    RpcAuthType.UNIX };
            break;
        case NONE:
            supportedFlavors = new int[] { RPC_AUTH_GSS_KRB5, RPC_AUTH_GSS_KRB5I, RPC_AUTH_GSS_KRB5P,
                    RpcAuthType.UNIX, RpcAuthType.NONE };
            break;
        default:
            // shuold never happen
            throw new PermException("Unsupported secutiry flavor");
        }
        return supportedFlavors;
    }
}