Commit b40d763d authored by Tigran Mkrtchyan's avatar Tigran Mkrtchyan
Browse files

src: remove dependency on chimera

remove basic server code, which is efficiently
chimera + config files.

Acked-by: Gerd Behrmann
Target: master
parent 751c5ab7
......@@ -4,22 +4,11 @@ NFS4J
The pure java implementation of NFS server version 3, 4.0 and 4.1 including pNFS extension.
Building from sources
---------------------
To build nfs4j from source code Java8 and Maven3 are required.
Starting a basic server
-------
The nfs4j comes with a basic stand-alone NFS server which is uses Spring-XML configuration file.
```$ java -jar basic-server/target/nfs4j-basic-server-<version>-jar-with-dependencies.jar oncrpcsvc.xml```
You can customize the configuration file to your needs. The **oncrpcsvc** bean is the one which will be started.
Implementing own NFS server
---------------------------
......
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dcache</groupId>
<artifactId>nfs4j</artifactId>
<version>0.11.0-SNAPSHOT</version>
</parent>
<groupId>org.dcache</groupId>
<artifactId>nfs4j-basic-server</artifactId>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<executions>
<execution>
<goals>
<goal>attached</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>org.dcache.xdr.SpringRunner</mainClass>
<packageName>org.dcache.nfs</packageName>
<addExtensions />
</manifest>
<manifestEntries>
<mode>development</mode>
<Implementation-Build>${buildNumber}</Implementation-Build>
<url>${project.url}</url>
<Build-Time>${maven.build.timestamp}</Build-Time>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--
EXTERNAL DEPENDENCIES
-->
<dependencies>
<dependency>
<groupId>org.dcache</groupId>
<artifactId>nfs4j-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.dcache</groupId>
<artifactId>oncrpc4j-spring</artifactId>
</dependency>
<dependency>
<groupId>org.dcache.chimera</groupId>
<artifactId>chimera-core</artifactId>
</dependency>
</dependencies>
</project>
/*
* 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;
import com.google.common.base.Splitter;
import java.io.IOException;
import org.dcache.auth.Subjects;
import org.dcache.nfs.status.NoEntException;
import org.dcache.nfs.vfs.Inode;
import org.dcache.nfs.vfs.Stat;
import org.dcache.nfs.vfs.VirtualFileSystem;
/**
* Class to scan export file and create missing directories
*/
public class ExportPathCreator {
private ExportFile exportFile;
private VirtualFileSystem vfs;
public void setVfs(VirtualFileSystem vfs) {
this.vfs = vfs;
}
public void setExportFile(ExportFile exportFile) {
this.exportFile = exportFile;
}
private static Inode tryToCreateIfMissing(VirtualFileSystem vfs, Inode inode, String name) throws IOException {
try {
return vfs.lookup(inode, name);
} catch (NoEntException e) {
return vfs.create(inode, Stat.Type.DIRECTORY, name, Subjects.ROOT, 0777);
}
}
public void init() throws IOException {
Inode root = vfs.getRootInode();
exportFile.getExports()
.map(FsExport::getPath)
.forEach(path -> {
Splitter splitter = Splitter.on('/').omitEmptyStrings();
Inode inode = root;
for (String s : splitter.split(path)) {
try {
inode = tryToCreateIfMissing(vfs, inode, s);
}catch(IOException e) {
return;
}
}
});
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<jmxConfigurator />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
#
# JDBC properties for Chimera
#
chimera.db.changeset=classpath:org/dcache/chimera/changelog/changelog-master.xml
chimera.db.driver=org.h2.Driver
chimera.db.url=jdbc:h2:./chimera-db
chimera.db.user=sa
chimera.db.password=
chimera.db.dialect=H2
chimera.db.pool.min=1
chimera.db.pool.max=8
......@@ -85,14 +85,6 @@
<groupId>org.dcache</groupId>
<artifactId>oncrpc4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.dcache.chimera</groupId>
<artifactId>chimera-core</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.gmbal</groupId>
<artifactId>gmbal</artifactId>
</dependency>
</dependencies>
</project>
/*
* 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.vfs;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.Subject;
import org.dcache.acl.ACE;
import org.dcache.acl.enums.AceFlags;
import org.dcache.acl.enums.AceType;
import org.dcache.acl.enums.Who;
import org.dcache.auth.Subjects;
import org.dcache.chimera.ChimeraFsException;
import org.dcache.chimera.DirectoryStreamHelper;
import org.dcache.chimera.DirNotEmptyHimeraFsException;
import org.dcache.chimera.FileExistsChimeraFsException;
import org.dcache.chimera.FileNotFoundHimeraFsException;
import org.dcache.chimera.FsInode;
import org.dcache.chimera.FsInodeType;
import org.dcache.chimera.HimeraDirectoryEntry;
import org.dcache.chimera.InvalidArgumentChimeraException;
import org.dcache.chimera.IsDirChimeraException;
import org.dcache.chimera.JdbcFs;
import org.dcache.chimera.NotDirChimeraException;
import org.dcache.chimera.StorageGenericLocation;
import org.dcache.nfs.status.*;
import org.dcache.nfs.v4.NfsIdMapping;
import org.dcache.nfs.v4.acl.Acls;
import org.dcache.nfs.v4.xdr.aceflag4;
import org.dcache.nfs.v4.xdr.acemask4;
import org.dcache.nfs.v4.xdr.acetype4;
import org.dcache.nfs.v4.xdr.nfsace4;
import org.dcache.nfs.v4.xdr.uint32_t;
import org.dcache.nfs.v4.xdr.utf8str_mixed;
import static org.dcache.nfs.v4.xdr.nfs4_prot.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Interface to a virtual file system.
*/
public class ChimeraVfs implements VirtualFileSystem, AclCheckable {
private final static Logger _log = LoggerFactory.getLogger(ChimeraVfs.class);
private final JdbcFs _fs;
private final NfsIdMapping _idMapping;
public ChimeraVfs(JdbcFs fs, NfsIdMapping idMapping) {
_fs = fs;
_idMapping = idMapping;
}
@Override
public Inode getRootInode() throws IOException {
return toInode(FsInode.getRoot(_fs));
}
@Override
public Inode lookup(Inode parent, String path) throws IOException {
try {
FsInode parentFsInode = toFsInode(parent);
FsInode fsInode = parentFsInode.inodeOf(path);
return toInode(fsInode);
}catch (FileNotFoundHimeraFsException e) {
throw new NoEntException("Path Do not exist.");
}
}
@Override
public Inode create(Inode parent, Stat.Type type, String path, Subject subject, int mode) throws IOException {
int uid = (int)Subjects.getUid(subject);
int gid = (int)Subjects.getPrimaryGid(subject);
try {
FsInode parentFsInode = toFsInode(parent);
FsInode fsInode = _fs.createFile(parentFsInode, path, uid, gid, mode | typeToChimera(type), typeToChimera(type));
return toInode(fsInode);
} catch (FileExistsChimeraFsException e) {
throw new ExistException("path already exists");
}
}
@Override
public Inode mkdir(Inode parent, String path, Subject subject, int mode) throws IOException {
int uid = (int) Subjects.getUid(subject);
int gid = (int) Subjects.getPrimaryGid(subject);
try {
FsInode parentFsInode = toFsInode(parent);
FsInode fsInode = parentFsInode.mkdir(path, uid, gid, mode);
return toInode(fsInode);
} catch (FileExistsChimeraFsException e) {
throw new ExistException("path already exists");
}
}
@Override
public Inode link(Inode parent, Inode link, String path, Subject subject) throws IOException {
FsInode parentFsInode = toFsInode(parent);
FsInode linkInode = toFsInode(link);
try {
FsInode fsInode = _fs.createHLink(parentFsInode, linkInode, path);
return toInode(fsInode);
}catch (NotDirChimeraException e) {
throw new NotDirException("parent not a directory");
} catch (FileExistsChimeraFsException e) {
throw new ExistException("path already exists");
}
}
@Override
public Inode symlink(Inode parent, String path, String link, Subject subject, int mode) throws IOException {
int uid = (int) Subjects.getUid(subject);
int gid = (int) Subjects.getPrimaryGid(subject);
try {
FsInode parentFsInode = toFsInode(parent);
FsInode fsInode = _fs.createLink(parentFsInode, path, uid, gid, mode, link.getBytes(StandardCharsets.UTF_8));
return toInode(fsInode);
} catch (FileExistsChimeraFsException e) {
throw new ExistException("path already exists");
}
}
@Override
public int read(Inode inode, byte[] data, long offset, int count) throws IOException {
FsInode fsInode = toFsInode(inode);
return fsInode.read(offset, data, 0, count);
}
@Override
public boolean move(Inode src, String oldName, Inode dest, String newName) throws IOException {
FsInode from = toFsInode(src);
FsInode to = toFsInode(dest);
try {
return _fs.move(from, oldName, to, newName);
} catch (NotDirChimeraException e) {
throw new NotDirException("not a directory");
} catch (FileExistsChimeraFsException e) {
throw new ExistException("destination exists");
} catch (DirNotEmptyHimeraFsException e) {
throw new NotEmptyException("directory exist and not empty");
} catch (FileNotFoundHimeraFsException e) {
throw new NoEntException("file not found");
}
}
@Override
public String readlink(Inode inode) throws IOException {
FsInode fsInode = toFsInode(inode);
int count = (int) fsInode.statCache().getSize();
byte[] data = new byte[count];
int n = _fs.read(fsInode, 0, data, 0, count);
if (n < 0) {
throw new NfsIoException("Can't read symlink");
}
return new String(data, 0, n, StandardCharsets.UTF_8);
}
@Override
public void remove(Inode parent, String path) throws IOException {
FsInode parentFsInode = toFsInode(parent);
try {
_fs.remove(parentFsInode, path);
} catch (FileNotFoundHimeraFsException e) {
throw new NoEntException("path not found");
} catch (DirNotEmptyHimeraFsException e) {
throw new NotEmptyException("directory not empty");
}
}
@Override
public WriteResult write(Inode inode, byte[] data, long offset, int count, StabilityLevel stabilityLevel) throws IOException {
FsInode fsInode = toFsInode(inode);
int bytesWritten = fsInode.write(offset, data, 0, count);
return new WriteResult(StabilityLevel.FILE_SYNC, bytesWritten);
}
@Override
public void commit(Inode inode, long offset, int count) throws IOException {
//nop (all IO is FILE_SYNC so no commits expected)
}
@Override
public List<DirectoryEntry> list(Inode inode) throws IOException {
FsInode parentFsInode = toFsInode(inode);
List<HimeraDirectoryEntry> list = DirectoryStreamHelper.listOf(parentFsInode);
return Lists.transform(list, new ChimeraDirectoryEntryToVfs());
}
@Override
public Inode parentOf(Inode inode) throws IOException {
try {
FsInode parent = toFsInode(inode).inodeOf("..");
return toInode(parent);
} catch (FileNotFoundHimeraFsException e) {
throw new NoEntException("no parent");
}
}
@Override
public FsStat getFsStat() throws IOException {
org.dcache.chimera.FsStat fsStat = _fs.getFsStat();
return new FsStat(fsStat.getTotalSpace(),
fsStat.getTotalFiles(),
fsStat.getUsedSpace(),
fsStat.getUsedFiles());
}
private FsInode toFsInode(Inode inode) throws IOException {
return _fs.inodeFromBytes(inode.getFileId());
}
private Inode toInode(final FsInode inode) {
try {
return Inode.forFile(_fs.inodeToBytes(inode));
} catch (ChimeraFsException e) {
throw new RuntimeException("bug found", e);
}
}
@Override
public Stat getattr(Inode inode) throws IOException {
FsInode fsInode = toFsInode(inode);
try {
return fromChimeraStat(fsInode.stat(), fsInode.id());
} catch (FileNotFoundHimeraFsException e) {
throw new NoEntException("Path Do not exist.");
}
}
@Override
public void setattr(Inode inode, Stat stat) throws IOException {
FsInode fsInode = toFsInode(inode);
try {
fsInode.setStat(toChimeraStat(stat));
} catch (InvalidArgumentChimeraException e) {
throw new InvalException(e.getMessage());
} catch (IsDirChimeraException e) {
throw new IsDirException(e.getMessage());
} catch (FileNotFoundHimeraFsException e) {
throw new StaleException(e.getMessage());
}
}
@Override
public nfsace4[] getAcl(Inode inode) throws IOException {
FsInode fsInode = toFsInode(inode);
try {
nfsace4[] aces;
List<ACE> dacl = _fs.getACL(fsInode);
org.dcache.chimera.posix.Stat stat = fsInode.statCache();
nfsace4[] unixAcl = Acls.of(stat.getMode(), fsInode.isDirectory());
aces = new nfsace4[dacl.size() + unixAcl.length];
int i = 0;
for (ACE ace : dacl) {
aces[i] = valueOf(ace, _idMapping);
i++;
}
System.arraycopy(unixAcl, 0, aces, i, unixAcl.length);
return Acls.compact(aces);
} catch (FileNotFoundHimeraFsException e) {
throw new StaleException(e.getMessage());
}
}
@Override
public void setAcl(Inode inode, nfsace4[] acl) throws IOException {
FsInode fsInode = toFsInode(inode);
List<ACE> dacl = new ArrayList<>();
for (nfsace4 ace : acl) {
dacl.add(valueOf(ace, _idMapping));
}
try {
_fs.setACL(fsInode, dacl);
} catch (FileNotFoundHimeraFsException e) {
throw new StaleException(e.getMessage());
}
}
private static Stat fromChimeraStat(org.dcache.chimera.posix.Stat pStat, long fileid) {
Stat stat = new Stat();
stat.setATime(pStat.getATime());
stat.setCTime(pStat.getCTime());
stat.setMTime(pStat.getMTime());
stat.setGid(pStat.getGid());
stat.setUid(pStat.getUid());
stat.setDev(pStat.getDev());
stat.setIno(pStat.getIno());
stat.setMode(pStat.getMode());
stat.setNlink(pStat.getNlink());
stat.setRdev(pStat.getRdev());
stat.setSize(pStat.getSize());
stat.setFileid(fileid);
stat.setGeneration(pStat.getGeneration());
return stat;
}
private static org.dcache.chimera.posix.Stat toChimeraStat(Stat stat) {
org.dcache.chimera.posix.Stat pStat = new org.dcache.chimera.posix.Stat();
if (stat.isDefined(Stat.StatAttribute.ATIME)) {
pStat.setATime(stat.getATime());
}
if (stat.isDefined(Stat.StatAttribute.CTIME)) {
pStat.setCTime(stat.getCTime());
}
if (stat.isDefined(Stat.StatAttribute.MTIME)) {
pStat.setMTime(stat.getMTime());
}
if (stat.isDefined(Stat.StatAttribute.GROUP)) {
pStat.setGid(stat.getGid());
}
if (stat.isDefined(Stat.StatAttribute.OWNER)) {
pStat.setUid(stat.getUid());
}
if (stat.isDefined(Stat.StatAttribute.DEV)) {
pStat.setDev(stat.getDev());
}
if (stat.isDefined(Stat.StatAttribute.INO)) {
pStat.setIno(stat.getIno());
}
if (stat.isDefined(Stat.StatAttribute.MODE)) {
pStat.setMode(stat.getMode());
}
if (stat.isDefined(Stat.StatAttribute.NLINK)) {
pStat.setNlink(stat.getNlink());
}
if (stat.isDefined(Stat.StatAttribute.RDEV)) {
pStat.setRdev(stat.getRdev());
}
if (stat.isDefined(Stat.StatAttribute.SIZE)) {
pStat.setSize(stat.getSize());
}
if (stat.isDefined(Stat.StatAttribute.GENERATION)) {
pStat.setGeneration(stat.getGeneration());
}
return pStat;
}
@Override
public int access(Inode inode, int mode) throws IOException {
int accessmask = mode;
if ((mode & (ACCESS4_MODIFY | ACCESS4_EXTEND))