4 Commits

Author SHA1 Message Date
8d3244fe64 Webui: use node-forge for hashing
apparently crypto works only on ssl websites
2025-04-26 14:09:05 +02:00
0a8985c93f Short readme 2025-04-26 11:19:06 +02:00
a8cf483eee Simplify ObjectPersistentStore 2025-04-26 11:11:51 +02:00
f7338f4e80 Dhfs-app: check that kill tests ls/cat return success 2025-04-26 10:44:33 +02:00
11 changed files with 99 additions and 132 deletions

View File

@@ -14,6 +14,9 @@ Syncthing and allowing you to stream your files like Google Drive File Stream
This is a simple wrapper around the jar/web ui distribution that allows you to run/stop
the DHFS server in the background, and update itself (hopefully!)
## How to use it and how it works?
## How to use it?
TODO 😁
Unpack the run-wrapper and run the `run` script. The filesystem should be mounted to the `fuse` folder in the run-wrapper root directory.
Then, a web interface will be available at `losthost:8080`, that can be used to connect with other peers.

View File

@@ -115,7 +115,7 @@ public class KillIT {
Log.info(ls2);
Log.info(cat2);
return ls1.equals(ls2) && cat1.equals(cat2);
return ls1.equals(ls2) && cat1.equals(cat2) && ls1.getExitCode() == 0 && ls2.getExitCode() == 0 && cat1.getExitCode() == 0 && cat2.getExitCode() == 0;
});
}

View File

@@ -133,7 +133,7 @@ public class LazyFsIT {
+ ls2 + "\n"
+ cat2 + "\n");
return ls1.equals(ls2) && cat1.equals(cat2);
return ls1.equals(ls2) && cat1.equals(cat2) && ls1.getExitCode() == 0 && ls2.getExitCode() == 0 && cat1.getExitCode() == 0 && cat2.getExitCode() == 0;
});
}

View File

@@ -10,7 +10,6 @@ import com.usatiuk.objects.iterators.IteratorStart;
import com.usatiuk.objects.iterators.KeyPredicateKvIterator;
import com.usatiuk.objects.iterators.ReversibleKvIterator;
import com.usatiuk.objects.snapshot.Snapshot;
import com.usatiuk.utils.RefcountedCloseable;
import io.quarkus.arc.properties.IfBuildProperty;
import io.quarkus.logging.Log;
import io.quarkus.runtime.ShutdownEvent;
@@ -104,53 +103,48 @@ public class LmdbObjectPersistentStore implements ObjectPersistentStore {
if (!_ready) throw new IllegalStateException("Wrong service order!");
}
@Nonnull
@Override
public Optional<ByteString> readObject(JObjectKey name) {
verifyReady();
try (Txn<ByteBuffer> txn = _env.txnRead()) {
var value = _db.get(txn, name.toByteBuffer());
return Optional.ofNullable(value).map(ByteString::copyFrom);
}
}
@Override
public Snapshot<JObjectKey, ByteString> getSnapshot() {
var txn = new RefcountedCloseable<>(_env.txnRead());
long commitId = readTxId(txn.get()).orElseThrow();
return new Snapshot<JObjectKey, ByteString>() {
private final RefcountedCloseable<Txn<ByteBuffer>> _txn = txn;
private final long _id = commitId;
private boolean _closed = false;
var txn = _env.txnRead();
try {
long commitId = readTxId(txn).orElseThrow();
return new Snapshot<JObjectKey, ByteString>() {
private final Txn<ByteBuffer> _txn = txn;
private final long _id = commitId;
private boolean _closed = false;
@Override
public CloseableKvIterator<JObjectKey, ByteString> getIterator(IteratorStart start, JObjectKey key) {
assert !_closed;
return new KeyPredicateKvIterator<>(new LmdbKvIterator(_txn.ref(), start, key), start, key, (k) -> !k.value().equals(DB_VER_OBJ_NAME_STR));
}
@Override
public CloseableKvIterator<JObjectKey, ByteString> getIterator(IteratorStart start, JObjectKey key) {
assert !_closed;
return new KeyPredicateKvIterator<>(new LmdbKvIterator(_txn, start, key), start, key, (k) -> !k.value().equals(DB_VER_OBJ_NAME_STR));
}
@Nonnull
@Override
public Optional<ByteString> readObject(JObjectKey name) {
assert !_closed;
var got = _db.get(_txn.get(), name.toByteBuffer());
var ret = Optional.ofNullable(got).map(UnsafeByteOperations::unsafeWrap);
return ret;
}
@Nonnull
@Override
public Optional<ByteString> readObject(JObjectKey name) {
assert !_closed;
var got = _db.get(_txn, name.toByteBuffer());
var ret = Optional.ofNullable(got).map(UnsafeByteOperations::unsafeWrap);
return ret;
}
@Override
public long id() {
assert !_closed;
return _id;
}
@Override
public long id() {
assert !_closed;
return _id;
}
@Override
public void close() {
assert !_closed;
_closed = true;
_txn.unref();
}
};
@Override
public void close() {
assert !_closed;
_closed = true;
_txn.close();
}
};
} catch (Exception e) {
txn.close();
throw e;
}
}
@Override
@@ -205,28 +199,28 @@ public class LmdbObjectPersistentStore implements ObjectPersistentStore {
private class LmdbKvIterator extends ReversibleKvIterator<JObjectKey, ByteString> {
private static final Cleaner CLEANER = Cleaner.create();
private final RefcountedCloseable<Txn<ByteBuffer>> _txn;
private final Txn<ByteBuffer> _txn; // Managed by the snapshot
private final Cursor<ByteBuffer> _cursor;
private final MutableObject<Boolean> _closed = new MutableObject<>(false);
// private final Exception _allocationStacktrace = new Exception();
private final Exception _allocationStacktrace = null;
// private final Exception _allocationStacktrace = null;
private boolean _hasNext = false;
private JObjectKey _peekedNextKey = null;
LmdbKvIterator(RefcountedCloseable<Txn<ByteBuffer>> txn, IteratorStart start, JObjectKey key) {
LmdbKvIterator(Txn<ByteBuffer> txn, IteratorStart start, JObjectKey key) {
_txn = txn;
_goingForward = true;
_cursor = _db.openCursor(_txn.get());
_cursor = _db.openCursor(_txn);
var closedRef = _closed;
var bt = _allocationStacktrace;
CLEANER.register(this, () -> {
if (!closedRef.getValue()) {
Log.error("Iterator was not closed before GC, allocated at: {0}", bt);
System.exit(-1);
}
});
// var bt = _allocationStacktrace;
// CLEANER.register(this, () -> {
// if (!closedRef.getValue()) {
// Log.error("Iterator was not closed before GC, allocated at: {0}", bt);
// System.exit(-1);
// }
// });
verifyReady();
@@ -308,7 +302,6 @@ public class LmdbObjectPersistentStore implements ObjectPersistentStore {
}
_closed.setValue(true);
_cursor.close();
_txn.unref();
}
@Override

View File

@@ -21,14 +21,6 @@ public class MemoryObjectPersistentStore implements ObjectPersistentStore {
private TreePMap<JObjectKey, ByteString> _objects = TreePMap.empty();
private long _lastCommitId = 0;
@Nonnull
@Override
public Optional<ByteString> readObject(JObjectKey name) {
synchronized (this) {
return Optional.ofNullable(_objects.get(name));
}
}
@Override
public Snapshot<JObjectKey, ByteString> getSnapshot() {
synchronized (this) {

View File

@@ -10,9 +10,6 @@ import java.util.Optional;
// Persistent storage of objects
// All changes are written as sequential transactions
public interface ObjectPersistentStore {
@Nonnull
Optional<ByteString> readObject(JObjectKey name);
Snapshot<JObjectKey, ByteString> getSnapshot();
Runnable prepareTx(TxManifestRaw names, long txId);

View File

@@ -23,11 +23,6 @@ public class SerializingObjectPersistentStore {
@Inject
ObjectPersistentStore delegateStore;
@Nonnull
Optional<JDataVersionedWrapper> readObject(JObjectKey name) {
return delegateStore.readObject(name).map(serializer::deserialize);
}
public Snapshot<JObjectKey, JDataVersionedWrapper> getSnapshot() {
return new Snapshot<JObjectKey, JDataVersionedWrapper>() {
private final Snapshot<JObjectKey, ByteString> _backing = delegateStore.getSnapshot();

View File

@@ -1,53 +0,0 @@
package com.usatiuk.utils;
import io.quarkus.logging.Log;
import org.apache.commons.lang3.mutable.MutableObject;
import java.lang.ref.Cleaner;
public class RefcountedCloseable<T extends AutoCloseable> {
private static final Cleaner CLEANER = Cleaner.create();
private final T _closeable;
private final MutableObject<Boolean> _closed = new MutableObject<>(false);
private int _refCount = 1;
public RefcountedCloseable(T closeable) {
_closeable = closeable;
var closedRef = _closed;
CLEANER.register(this, () -> {
if (!closedRef.getValue()) {
Log.error("RefcountedCloseable was not closed before GC");
System.exit(-1);
}
});
}
public RefcountedCloseable<T> ref() {
synchronized (this) {
if (_closed.getValue()) {
return null;
}
_refCount++;
return this;
}
}
public void unref() {
synchronized (this) {
_refCount--;
if (_refCount == 0) {
try {
assert !_closed.getValue();
_closed.setValue(true);
_closeable.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
public T get() {
return _closeable;
}
}

View File

@@ -9,6 +9,7 @@
"version": "0.0.1",
"dependencies": {
"jwt-decode": "^4.0.0",
"node-forge": "^1.3.1",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router": "^7.5.2",
@@ -21,6 +22,7 @@
"@parcel/validator-typescript": "^2.14.4",
"@types/eslint": "^9.6.1",
"@types/eslint-config-prettier": "^6.11.3",
"@types/node-forge": "^1.3.11",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"@typescript-eslint/eslint-plugin": "^8.28.0",
@@ -2436,6 +2438,26 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.15.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.2.tgz",
"integrity": "sha512-uKXqKN9beGoMdBfcaTY1ecwz6ctxuJAcUlwE55938g0ZJ8lRxwAZqRz2AJ4pzpt5dHdTPMB863UZ0ESiFUcP7A==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@types/node-forge": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz",
"integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/react": {
"version": "19.0.12",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz",
@@ -5465,6 +5487,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
"node": ">= 6.13.0"
}
},
"node_modules/node-gyp-build-optional-packages": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz",
@@ -6871,6 +6902,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",

View File

@@ -10,6 +10,7 @@
"browserslist": "> 0.5%, last 2 versions, not dead",
"dependencies": {
"jwt-decode": "^4.0.0",
"node-forge": "^1.3.1",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router": "^7.5.2",
@@ -25,6 +26,7 @@
"@parcel/validator-typescript": "^2.14.4",
"@types/eslint": "^9.6.1",
"@types/eslint-config-prettier": "^6.11.3",
"@types/node-forge": "^1.3.11",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"@typescript-eslint/eslint-plugin": "^8.28.0",

View File

@@ -1,7 +1,7 @@
import * as forge from "node-forge";
export async function hashCert(cert: string) {
const hash = await crypto.subtle.digest(
"SHA-1",
new TextEncoder().encode(cert),
);
return btoa(String.fromCharCode(...new Uint8Array(hash)));
const md = forge.md.sha1.create();
md.update(cert);
return md.digest().toHex();
}