/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.net.nfs.nfs2.mount; import java.io.IOException; import java.net.InetAddress; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.acplt.oncrpc.OncRpcClient; import org.acplt.oncrpc.OncRpcClientAuthUnix; import org.acplt.oncrpc.OncRpcException; import org.acplt.oncrpc.OncRpcProtocols; import org.acplt.oncrpc.XdrAble; import org.acplt.oncrpc.XdrDecodingStream; import org.acplt.oncrpc.XdrEncodingStream; import org.acplt.oncrpc.XdrVoid; import org.apache.log4j.Logger; import org.jnode.net.nfs.Protocol; /** * * @author Andrei Dore */ public class Mount1Client { private static final Logger LOGGER = Logger.getLogger(Mount1Client.class); private static final int MOUNT_CODE = 100005; private static final int MOUNT_VERSION = 1; private static final int PROCEDURE_TEST = 0; private static final int PROCEDURE_MOUNT = 1; private static final int PROCEDURE_DUMP = 2; private static final int PROCEDURE_UNMOUNT = 3; private static final int PROCEDURE_EXPORT = 5; public static final int FILE_HANDLE_SIZE = 32; public static final int MAX_PATH_LENGHT = 1024; public static final int MAX_NAME_LENGHT = 255; public static final int MOUNT_OK = 0; private InetAddress host; private Protocol protocol; private int uid; private int gid; private List<OncRpcClient> rpcClientPool; private boolean closed; /** * Constructs a <code>Mount1Client</code> client stub proxy object from * which the MOUNTPROG remote program can be accessed. * * @param host * Internet address of host where to contact the remote * program. * @param protocol * {@link org.acplt.oncrpc.OncRpcProtocols Protocol} to be * used for ONC/RPC calls. */ public Mount1Client(InetAddress host, Protocol protocol, int uid, int gid) { this.host = host; this.protocol = protocol; this.uid = uid; this.gid = gid; rpcClientPool = new LinkedList<OncRpcClient>(); } private OncRpcClient createRpcClient() throws OncRpcException, IOException { OncRpcClient client = OncRpcClient.newOncRpcClient(host, MOUNT_CODE, MOUNT_VERSION, protocol == Protocol.UDP ? OncRpcProtocols.ONCRPC_UDP : OncRpcProtocols.ONCRPC_TCP); client.setTimeout(10000); if (uid != -1 && gid != -1) { client.setAuth(new OncRpcClientAuthUnix("test", uid, gid)); } return client; } private synchronized OncRpcClient getRpcClient() throws OncRpcException, IOException { if (closed) { throw new IOException("The mount client it is closed"); } if (rpcClientPool.size() == 0) { return createRpcClient(); } else { return rpcClientPool.remove(0); } } private synchronized void releaseRpcClient(OncRpcClient client) throws IOException { if (closed) { throw new IOException("The mount client it is closed"); } if (client != null) { rpcClientPool.add(client); } } private void call(final int functionId, final XdrAble parameter, final XdrAble result) throws MountException, IOException { int countCall = 0; OncRpcClient client = null; while (true) { try { countCall++; client = getRpcClient(); client.call(functionId, parameter, result); break; } catch (Exception e) { // if we receive an exception we will close the client and next // time we will use another rpc client if (client != null) { try { client.close(); } catch (OncRpcException e1) { // Ignore this } client = null; } if (e instanceof RuntimeException) { throw (RuntimeException) e; } if (e instanceof OncRpcException) { if (countCall > 5) { throw new MountException(e.getMessage(), e); } else { LOGGER .warn("An error occurs when nfs file system try to call the rpc method. Reason : " + e.getMessage() + ". It will try again"); continue; } } else { throw new MountException(e.getMessage(), e); } } finally { if (client != null) { try { releaseRpcClient(client); } catch (IOException e) { // ignore } } } } } /** * Call remote procedure test. * * @throws OncRpcException * if an ONC/RPC error occurs. * @throws IOException * if an I/O error occurs. * @throws MountException */ public void test() throws IOException, MountException { call(PROCEDURE_TEST, XdrVoid.XDR_VOID, XdrVoid.XDR_VOID); } /** * Call remote procedure mount. * * @param path parameter (of type DirPath) to the remote procedure call. * @return Result from remote procedure call (of type MountResult). * @throws OncRpcException * if an ONC/RPC error occurs. * @throws IOException * if an I/O error occurs. * @throws MountException */ public MountResult mount(final String path) throws IOException, MountException { XdrAble mountParameter = new Parameter() { public void xdrEncode(XdrEncodingStream xdrEncodingStream) throws OncRpcException, IOException { xdrEncodingStream.xdrEncodeString(path); } }; final MountResult result = new MountResult(); XdrAble mountResult = new Result() { public void xdrDecode(XdrDecodingStream xdrDecodingStream) throws OncRpcException, IOException { result.setFileHandle(readFileHandle(xdrDecodingStream)); } }; call(PROCEDURE_MOUNT, mountParameter, new ResultWithCode(mountResult)); return result; } public List<RemoteMountFileSystem> dump() throws IOException, MountException { final List<RemoteMountFileSystem> remoteMountFileSystemList = new ArrayList<RemoteMountFileSystem>(); XdrAble dumpResult = new Result() { public void xdrDecode(XdrDecodingStream xdrDecodingStream) throws OncRpcException, IOException { while (xdrDecodingStream.xdrDecodeBoolean()) { String host = xdrDecodingStream.xdrDecodeString(); String remoteDirectory = xdrDecodingStream.xdrDecodeString(); RemoteMountFileSystem remoteMountFileSystem = new RemoteMountFileSystem(host, remoteDirectory); remoteMountFileSystemList.add(remoteMountFileSystem); } } }; call(PROCEDURE_DUMP, XdrVoid.XDR_VOID, dumpResult); return remoteMountFileSystemList; } public List<ExportEntry> export() throws IOException, MountException { final List<ExportEntry> exportEntryList = new ArrayList<ExportEntry>(); XdrAble dumpResult = new Result() { public void xdrDecode(XdrDecodingStream xdrDecodingStream) throws OncRpcException, IOException { while (xdrDecodingStream.xdrDecodeBoolean()) { String path = readPath(xdrDecodingStream); List<String> groupList = readGroup(xdrDecodingStream); ExportEntry exportEntry = new ExportEntry(path, groupList); exportEntryList.add(exportEntry); } } private List<String> readGroup(XdrDecodingStream xdrDecodingStream) throws OncRpcException, IOException { List<String> groupList = new ArrayList<String>(); while (xdrDecodingStream.xdrDecodeBoolean()) { String group = readName(xdrDecodingStream); groupList.add(group); } return groupList; } }; call(PROCEDURE_EXPORT, XdrVoid.XDR_VOID, dumpResult); return exportEntryList; } public void unmount(final String dirPath) throws IOException, MountException { XdrAble mountParameter = new Parameter() { public void xdrEncode(XdrEncodingStream xdrEncodingStream) throws OncRpcException, IOException { xdrEncodingStream.xdrEncodeString(dirPath); } }; call(PROCEDURE_UNMOUNT, mountParameter, XdrVoid.XDR_VOID); } private String readPath(XdrDecodingStream xdrDecodingStream) throws OncRpcException, IOException { return xdrDecodingStream.xdrDecodeString(); } private String readName(XdrDecodingStream xdrDecodingStream) throws OncRpcException, IOException { return xdrDecodingStream.xdrDecodeString(); } private byte[] readFileHandle(XdrDecodingStream xdrDecodingStream) throws OncRpcException, IOException { return xdrDecodingStream.xdrDecodeOpaque(Mount1Client.FILE_HANDLE_SIZE); } private abstract class Parameter implements XdrAble { public void xdrDecode(XdrDecodingStream arg0) throws OncRpcException, IOException { } } private abstract class Result implements XdrAble { public void xdrEncode(XdrEncodingStream arg0) throws OncRpcException, IOException { } } private class ResultWithCode implements XdrAble { private int resultCode; private XdrAble xdrAble; public ResultWithCode(XdrAble xdrAble) { this.xdrAble = xdrAble; } public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException { } public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException { resultCode = xdr.xdrDecodeInt(); if (resultCode == 0) { xdrAble.xdrDecode(xdr); } else { throw new OncRpcException("An error occur when system try to mount. Error code: " + resultCode); } } public int getResultCode() { return resultCode; } } // TODO Remove the synch in the future public synchronized void close() throws IOException { closed = true; List<OncRpcException> exceptionList = new ArrayList<OncRpcException>(); for (OncRpcClient client : rpcClientPool) { try { client.close(); } catch (OncRpcException e) { exceptionList.add(e); } } if (exceptionList.size() != 0) { StringBuilder builder = new StringBuilder(); builder.append("An error occurs when the mount client close connections. Reason:"); for (OncRpcException anExceptionList : exceptionList) { builder.append(anExceptionList.getMessage()); builder.append('.'); } throw new IOException(builder.toString()); } } }