fuse-wiiu/src/main/java/de/mas/wiiu/jnus/fuse_wiiu/implementation/GroupFuseContainer.java

195 lines
6.0 KiB
Java

package de.mas.wiiu.jnus.fuse_wiiu.implementation;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Map.Entry;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import de.mas.wiiu.jnus.fuse_wiiu.interfaces.FuseContainer;
import de.mas.wiiu.jnus.fuse_wiiu.interfaces.FuseDirectory;
import jnr.ffi.Pointer;
import jnr.ffi.types.off_t;
import jnr.ffi.types.size_t;
import ru.serce.jnrfuse.ErrorCodes;
import ru.serce.jnrfuse.FuseFillDir;
import ru.serce.jnrfuse.struct.FileStat;
import ru.serce.jnrfuse.struct.FuseFileInfo;
/**
* Implementation of an FuseContainer which can hold serveral FuseContainers emulated as directories.
*
* @author Maschell
*
*/
public abstract class GroupFuseContainer implements FuseContainer {
private final Map<String, FuseContainer> containerMap = new HashMap<>();
private final Map<String, Long> lastAccess = new HashMap<>();
private final Optional<FuseDirectory> parent;
public GroupFuseContainer(Optional<FuseDirectory> parent) {
this.parent = parent;
}
/**
* Removing old container from the list that haven't been updated in a given time frame.
*
* @param duration
* @return Number of elements that have been removed.
*/
protected int removeUnused(long duration) {
int count = 0;
for (Entry<String, Long> cur : lastAccess.entrySet().stream().filter(e -> System.currentTimeMillis() - e.getValue() > duration)
.collect(Collectors.toList())) {
lastAccess.remove(cur.getKey());
containerMap.remove(cur.getKey()).deinit();
System.out.println("Unmounting " + cur.getKey());
count++;
}
if (count > 0) {
synchronized (initDone) {
initDone = false;
}
}
return count;
}
/**
*
* @param path
* @param func
* @param defaultValue
* @return
*/
private int doForContainer(String path, BiFunction<String, FuseContainer, Integer> func, int defaultValue) {
path.replace("\\", "/");
path = path.substring(1);
String[] parts = path.split("/");
FuseContainer container = containerMap.get(parts[0]);
if (container != null) {
lastAccess.put(parts[0], System.currentTimeMillis());
container.init();
String newPath = path.substring(parts[0].length());
if (newPath.length() == 0) {
newPath = "/";
}
return func.apply(newPath, container);
}
return defaultValue;
}
@Override
public int getattr(String path, FileStat stat) {
path.replace("\\", "/");
if (path.equals("/")) {
stat.st_mode.set(FileStat.S_IFDIR | FileStat.ALL_READ);
stat.st_nlink.set(2);
return 0;
}
if (path.split("/").length == 2) {
for (String container : containerMap.keySet()) {
if (container.equals(path.split("/")[1])) {
stat.st_mode.set(FileStat.S_IFDIR | FileStat.ALL_READ);
stat.st_nlink.set(2);
return 0;
}
}
}
return doForContainer(path, (newPath, container) -> container.getattr(newPath, stat), -ErrorCodes.ENOENT());
}
@Override
public int readdir(String path, Pointer buf, FuseFillDir filter, @off_t long offset, FuseFileInfo fi) {
path.replace("\\", "/");
if (path.equals("/")) {
filter.apply(buf, ".", null, 0);
if (getParent().isPresent()) {
filter.apply(buf, "..", null, 0);
}
for (String container : containerMap.keySet()) {
filter.apply(buf, container, null, 0);
}
return 0;
}
return doForContainer(path, (newPath, container) -> container.readdir(newPath, buf, filter, offset, fi), 0);
}
@Override
public int read(String path, Pointer buf, @size_t long size, @off_t long offset, FuseFileInfo fi) {
path.replace("\\", "/");
if (path.length() <= 1) {
return -ErrorCodes.EISDIR();
}
return doForContainer(path, (newPath, container) -> container.read(newPath, buf, size, offset, fi), 0);
}
@Override
public int open(String path, FuseFileInfo fi) {
path.replace("\\", "/");
if (path.length() <= 1) {
return -ErrorCodes.EISDIR();
}
return doForContainer(path, (newPath, container) -> container.open(newPath, fi), 0);
}
@Override
public Optional<FuseDirectory> getParent() {
return parent;
}
public FuseContainer addFuseContainer(String name, FuseContainer container) {
return containerMap.put(name, container);
}
public void clearFuseContainer() {
containerMap.clear();
}
public FuseContainer getFuseContainer(String name) {
return containerMap.get(name);
}
public boolean hasFuseContainer(String name) {
return containerMap.containsKey(name);
}
public FuseContainer removeFuseContainer(String name) {
return containerMap.remove(name);
}
private Boolean initDone = false;
@Override
public void init() {
synchronized (initDone) {
if (!initDone) {
doInit();
initDone = true;
}
}
}
/**
* This function is used to add FuseContainers to this GroupFuseContainer and can be called because of two reason. 1. The GroupFuseContainer is access for
* the first time and the list FuseContainers in this group need to be added to the map using "addFuseContainer". 2. Some of the children have been removed
* (due to inactivity). In this case the functions should add them again. So it needs to either add the missing one (check by hasFuseContainer), or
* completely wipe and start over.
*/
abstract protected void doInit();
@Override
public void deinit() {
//
}
}