mirror of
https://github.com/usatiuk/dhfs.git
synced 2025-10-29 04:57:48 +01:00
simple symlinks
Some checks failed
Server / build-dhfs (push) Failing after 6m43s
Server / build-webui (push) Successful in 2m25s
Server / publish-docker (push) Has been skipped
Server / publish-run-wrapper (push) Has been skipped
Some checks failed
Server / build-dhfs (push) Failing after 6m43s
Server / build-webui (push) Successful in 2m25s
Server / publish-docker (push) Has been skipped
Server / publish-run-wrapper (push) Has been skipped
This commit is contained in:
@@ -92,6 +92,7 @@ public class FileConflictResolver implements ConflictResolver {
|
||||
|
||||
boolean wasChanged = oursFile.getMtime() != first.getMtime()
|
||||
|| oursFile.getCtime() != first.getCtime()
|
||||
|| first.isSymlink() != second.isSymlink()
|
||||
|| chunksDiff;
|
||||
|
||||
if (m.getBestVersion() > newChangelog.values().stream().reduce(0L, Long::sum))
|
||||
@@ -127,7 +128,7 @@ public class FileConflictResolver implements ConflictResolver {
|
||||
oursFile.setMtime(first.getMtime());
|
||||
oursFile.setCtime(first.getCtime());
|
||||
|
||||
var newFile = new File(UUID.randomUUID(), second.getMode(), oursDir.getUuid());
|
||||
var newFile = new File(UUID.randomUUID(), second.getMode(), oursDir.getUuid(), second.isSymlink());
|
||||
|
||||
newFile.setMtime(second.getMtime());
|
||||
newFile.setCtime(second.getCtime());
|
||||
|
||||
@@ -18,10 +18,13 @@ public class File extends FsNode {
|
||||
private final NavigableMap<Long, String> _chunks = new TreeMap<>();
|
||||
@Getter
|
||||
private final UUID _parent;
|
||||
@Getter
|
||||
private final boolean _symlink;
|
||||
|
||||
public File(UUID uuid, long mode, UUID parent) {
|
||||
public File(UUID uuid, long mode, UUID parent, boolean symlink) {
|
||||
super(uuid, mode);
|
||||
_parent = parent;
|
||||
_symlink = symlink;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,4 +33,9 @@ public interface DhfsFileService {
|
||||
Long write(String fileUuid, long offset, byte[] data);
|
||||
|
||||
Boolean truncate(String fileUuid, long length);
|
||||
|
||||
String readlink(String uuid);
|
||||
ByteString readlinkBS(String uuid);
|
||||
|
||||
String symlink(String oldpath, String newpath);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import jakarta.inject.Inject;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
@@ -140,7 +141,7 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
||||
|
||||
var fuuid = UUID.randomUUID();
|
||||
Log.trace("Creating file " + fuuid);
|
||||
File f = new File(fuuid, mode, UUID.fromString(parent.getName()));
|
||||
File f = new File(fuuid, mode, UUID.fromString(parent.getName()), false);
|
||||
|
||||
if (!parent.runWriteLocked(JObject.ResolutionStrategy.REMOTE, (m, d, bump, invalidate) -> {
|
||||
if (!(d instanceof Directory dir))
|
||||
@@ -151,10 +152,13 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
||||
|
||||
bump.apply();
|
||||
|
||||
boolean created = dir.putKid(fname, fuuid);
|
||||
if (!created) return false;
|
||||
|
||||
jObjectManager.put(f, Optional.of(dir.getName()));
|
||||
|
||||
dir.setMtime(System.currentTimeMillis());
|
||||
return dir.putKid(fname, fuuid);
|
||||
return true;
|
||||
}))
|
||||
return Optional.empty();
|
||||
|
||||
@@ -270,7 +274,7 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
||||
if (theFile.getData() instanceof Directory d) {
|
||||
newDent = d;
|
||||
} else if (theFile.getData() instanceof File f) {
|
||||
var newFile = new File(UUID.randomUUID(), f.getMode(), UUID.fromString(dentTo.getName()));
|
||||
var newFile = new File(UUID.randomUUID(), f.getMode(), UUID.fromString(dentTo.getName()), f.isSymlink());
|
||||
newFile.setMtime(f.getMtime());
|
||||
newFile.setCtime(f.getCtime());
|
||||
newFile.getChunks().putAll(f.getChunks());
|
||||
@@ -752,6 +756,65 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readlink(String uuid) {
|
||||
return readlinkBS(uuid).toStringUtf8();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString readlinkBS(String uuid) {
|
||||
var fileOpt = jObjectManager.get(uuid).orElseThrow(() -> new StatusRuntimeException(Status.NOT_FOUND.withDescription("File not found when trying to readlink: " + uuid)));
|
||||
|
||||
return fileOpt.runReadLocked(JObject.ResolutionStrategy.REMOTE, (md, fileData) -> {
|
||||
if (!(fileData instanceof File)) {
|
||||
throw new StatusRuntimeException(Status.INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (!((File) fileData).isSymlink())
|
||||
throw new StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Not a symlink: " + uuid));
|
||||
|
||||
return read(uuid, 0, Math.toIntExact(size(uuid))).get();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String symlink(String oldpath, String newpath) {
|
||||
var parent = traverse(getRoot(), Path.of(newpath).getParent());
|
||||
|
||||
String fname = Path.of(newpath).getFileName().toString();
|
||||
|
||||
var fuuid = UUID.randomUUID();
|
||||
Log.trace("Creating file " + fuuid);
|
||||
File f = new File(fuuid, 0, UUID.fromString(parent.getName()), true);
|
||||
|
||||
ChunkData newChunkData = createChunk(UnsafeByteOperations.unsafeWrap(oldpath.getBytes(StandardCharsets.UTF_8)));
|
||||
ChunkInfo newChunkInfo = new ChunkInfo(newChunkData.getHash(), newChunkData.getBytes().size());
|
||||
|
||||
f.getChunks().put(0L, newChunkInfo.getHash());
|
||||
|
||||
if (!parent.runWriteLocked(JObject.ResolutionStrategy.REMOTE, (m, d, bump, invalidate) -> {
|
||||
if (!(d instanceof Directory dir))
|
||||
return false;
|
||||
|
||||
if (dir.getKid(fname).isPresent())
|
||||
return false;
|
||||
|
||||
bump.apply();
|
||||
|
||||
boolean created = dir.putKid(fname, fuuid);
|
||||
if (!created) return false;
|
||||
|
||||
jObjectManager.put(newChunkData, Optional.of(newChunkInfo.getName()));
|
||||
jObjectManager.put(newChunkInfo, Optional.of(f.getName()));
|
||||
jObjectManager.put(f, Optional.of(dir.getName()));
|
||||
|
||||
dir.setMtime(System.currentTimeMillis());
|
||||
return true;
|
||||
})) return null;
|
||||
|
||||
return f.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean setTimes(String fileUuid, long atimeMs, long mtimeMs) {
|
||||
var file = jObjectManager.get(fileUuid).orElseThrow(
|
||||
|
||||
@@ -28,8 +28,7 @@ import ru.serce.jnrfuse.struct.Timespec;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
import static jnr.posix.FileStat.S_IFDIR;
|
||||
import static jnr.posix.FileStat.S_IFREG;
|
||||
import static jnr.posix.FileStat.*;
|
||||
|
||||
@ApplicationScoped
|
||||
public class DhfsFuse extends FuseStubFS {
|
||||
@@ -92,7 +91,10 @@ public class DhfsFuse extends FuseStubFS {
|
||||
return -ErrorCodes.ENOENT();
|
||||
}
|
||||
if (found.get() instanceof File f) {
|
||||
stat.st_mode.set(S_IFREG | f.getMode());
|
||||
if (f.isSymlink())
|
||||
stat.st_mode.set(S_IFLNK | 0777); // FIXME?
|
||||
else
|
||||
stat.st_mode.set(S_IFREG | f.getMode());
|
||||
stat.st_nlink.set(1);
|
||||
stat.st_size.set(fileService.size(uuid));
|
||||
} else if (found.get() instanceof Directory d) {
|
||||
@@ -295,4 +297,34 @@ public class DhfsFuse extends FuseStubFS {
|
||||
return -ErrorCodes.EIO();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readlink(String path, Pointer buf, long size) {
|
||||
if (size < 0) return -ErrorCodes.EINVAL();
|
||||
try {
|
||||
var fileOpt = fileService.open(path);
|
||||
if (fileOpt.isEmpty()) return -ErrorCodes.ENOENT();
|
||||
var file = fileOpt.get();
|
||||
var read = fileService.readlinkBS(fileOpt.get());
|
||||
if (read.isEmpty()) return 0;
|
||||
UnsafeByteOperations.unsafeWriteTo(read, new JnrPtrByteOutput(buf, size));
|
||||
buf.putByte(Math.min(size - 1, read.size()), (byte) 0);
|
||||
return 0;
|
||||
} catch (Exception e) {
|
||||
Log.error("When reading " + path, e);
|
||||
return -ErrorCodes.EIO();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int symlink(String oldpath, String newpath) {
|
||||
try {
|
||||
var ret = fileService.symlink(oldpath, newpath);
|
||||
if (ret == null) return -ErrorCodes.EEXIST();
|
||||
else return 0;
|
||||
} catch (Exception e) {
|
||||
Log.error("When creating " + newpath, e);
|
||||
return -ErrorCodes.EIO();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class DhfsFileServiceSimpleTestImpl {
|
||||
ChunkInfo c2i = new ChunkInfo(c2.getHash(), c2.getBytes().size());
|
||||
ChunkData c3 = new ChunkData(ByteString.copyFrom("91011".getBytes()));
|
||||
ChunkInfo c3i = new ChunkInfo(c3.getHash(), c3.getBytes().size());
|
||||
File f = new File(fuuid, 777, null);
|
||||
File f = new File(fuuid, 777, null, false);
|
||||
f.getChunks().put(0L, c1.getHash());
|
||||
f.getChunks().put((long) c1.getBytes().size(), c2.getHash());
|
||||
f.getChunks().put((long) c1.getBytes().size() + c2.getBytes().size(), c3.getHash());
|
||||
|
||||
@@ -32,4 +32,22 @@ public class DhfsFuseTest {
|
||||
Assertions.assertDoesNotThrow(() -> Files.readAllBytes(testPath));
|
||||
Assertions.assertArrayEquals(Files.readAllBytes(testPath), testString);
|
||||
}
|
||||
|
||||
@Test
|
||||
void symlinkTest() throws IOException, InterruptedException {
|
||||
byte[] testString = "symlinkedfile".getBytes();
|
||||
Path testPath = Path.of(root).resolve("symlinktarget");
|
||||
Path testSymlink = Path.of(root).resolve("symlinktest");
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> Files.createFile(testPath));
|
||||
Assertions.assertDoesNotThrow(() -> Files.write(testPath, testString));
|
||||
Assertions.assertDoesNotThrow(() -> Files.readAllBytes(testPath));
|
||||
Assertions.assertArrayEquals(Files.readAllBytes(testPath), testString);
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> Files.createSymbolicLink(testSymlink, testPath));
|
||||
Assertions.assertTrue(() -> Files.isSymbolicLink(testSymlink));
|
||||
Assertions.assertEquals(testPath, Files.readSymbolicLink(testSymlink));
|
||||
Assertions.assertDoesNotThrow(() -> Files.readAllBytes(testSymlink));
|
||||
Assertions.assertArrayEquals(Files.readAllBytes(testSymlink), testString);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user