/*
* $Id$
*
* Copyright (C) 2003-2014 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.protocol.nfs.nfs2;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.jnode.net.nfs.Protocol;
import org.jnode.net.nfs.nfs2.CreateFileResult;
import org.jnode.net.nfs.nfs2.FileAttribute;
import org.jnode.net.nfs.nfs2.LookupResult;
import org.jnode.net.nfs.nfs2.NFS2Client;
import org.jnode.net.nfs.nfs2.NFS2Exception;
import org.jnode.net.nfs.nfs2.Time;
import org.jnode.net.nfs.nfs2.mount.ExportEntry;
import org.jnode.net.nfs.nfs2.mount.Mount1Client;
import org.jnode.net.nfs.nfs2.mount.MountException;
import org.jnode.net.nfs.nfs2.mount.MountResult;
public class NFS2OutputStream extends OutputStream {
private static final boolean DEFAULT_PERMISSION[] =
new boolean[] {true, true, false, true, false, false, true, false, false};
private static final int DEFAULT_BUFFER_SIZE = NFS2Client.MAX_DATA;
private Mount1Client mountClient;
private NFS2Client nfsClient;
private String mountDirectory;
private byte[] fileHandle;
@SuppressWarnings("unused")
private FileAttribute fileAttribute;
private long fileOffset;
private byte[] buffer;
private int count;
public NFS2OutputStream(URL url) throws IOException {
int uid;
int gid;
String userinfo = url.getUserInfo();
if (userinfo != null) {
final int pos = userinfo.indexOf(':');
if (pos != -1) {
uid = Integer.parseInt(userinfo.substring(0, pos));
gid = Integer.parseInt(userinfo.substring(pos + 1));
} else {
throw new IOException("The url doesn't contains the uid and guid.");
}
} else {
throw new IOException("The url doesn't contains the uid and guid.");
}
mountClient = new Mount1Client(InetAddress.getByName(url.getHost()), Protocol.TCP, uid, gid);
nfsClient = new NFS2Client(InetAddress.getByName(url.getHost()), Protocol.TCP, uid, gid);
String path = url.getPath();
List<ExportEntry> exportList;
try {
exportList = mountClient.export();
} catch (MountException e1) {
mountClient.close();
throw new IOException(e1.getMessage());
}
ExportEntry exportEntry = null;
for (ExportEntry e : exportList) {
if (path.startsWith(e.getDirectory())) {
if (exportEntry == null) {
exportEntry = e;
} else {
if (exportEntry.getDirectory().length() < e.getDirectory().length()) {
exportEntry = e;
}
}
}
}
if (exportEntry == null) {
throw new IOException("The path " + path + " it is not exported");
}
mountDirectory = exportEntry.getDirectory();
MountResult mountResult;
try {
mountResult = mountClient.mount(mountDirectory);
} catch (MountException e) {
mountClient.close();
throw new IOException(e.getMessage());
}
byte[] tempFileHandle = mountResult.getFileHandle();
try {
String filePath = path.substring(exportEntry.getDirectory().length());
StringTokenizer tokenizer = new StringTokenizer(filePath, "/");
List<String> tokenList = new ArrayList<String>();
while (tokenizer.hasMoreElements()) {
String t = tokenizer.nextToken();
tokenList.add(t);
}
for (int i = 0; i < tokenList.size() - 1; i++) {
String t = tokenList.get(i);
LookupResult lookup = nfsClient.lookup(tempFileHandle, t);
if (lookup.getFileAttribute().getType() == FileAttribute.FILE) {
throw new IOException("The path contains a file : " + t + '.');
} else if (lookup.getFileAttribute().getType() == FileAttribute.DIRECTORY) {
tempFileHandle = lookup.getFileHandle();
} else {
throw new IOException("The path contains an unknow resource: " + t +
". It is not directory or file");
}
}
CreateFileResult result =
nfsClient.createFile(tempFileHandle, tokenList.get(tokenList.size() - 1),
DEFAULT_PERMISSION, uid, gid, 0, new Time(-1, -1), new Time(-1, -1));
fileHandle = result.getFileHandle();
fileAttribute = result.getFileAttribute();
} catch (NFS2Exception e) {
try {
mountClient.unmount(mountDirectory);
} catch (MountException e1) {
// ignore
}
mountClient.close();
nfsClient.close();
throw new IOException(e.getMessage());
}
if (fileHandle == null) {
throw new IOException("The target of the " + url.toString() + " it is not a file.");
}
buffer = new byte[DEFAULT_BUFFER_SIZE];
}
private void flushBuffer() throws IOException {
if (count == 0) {
return;
}
try {
fileAttribute = nfsClient.writeFile(fileHandle, (int) fileOffset, buffer, 0, count);
} catch (NFS2Exception e) {
throw new IOException(e);
}
fileOffset += count;
count = 0;
}
@Override
public synchronized void write(int b) throws IOException {
if (count >= buffer.length) {
flushBuffer();
}
buffer[count] = (byte) b;
count++;
}
@Override
public synchronized void flush() throws IOException {
flushBuffer();
}
@Override
public synchronized void write(byte[] b, int off, int len) throws IOException {
int writeBytes = 0;
while (writeBytes != len) {
if (count >= buffer.length) {
flushBuffer();
}
int c = Math.min(buffer.length - count, len - writeBytes);
System.arraycopy(b, off + writeBytes, buffer, count, c);
writeBytes += c;
count += c;
}
}
// TODO Remove the synch in the future
@Override
public synchronized void close() throws IOException {
if (mountClient != null) {
try {
mountClient.unmount(mountDirectory);
} catch (MountException e) {
// ignore
}
try {
mountClient.close();
} catch (IOException e) {
// ignore
}
}
if (nfsClient != null) {
try {
nfsClient.close();
} catch (IOException e) {
// ignore
}
}
}
}