saltcorn-samba 0.4.6 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/index.js +61 -51
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,88 @@ All notable changes to `saltcorn-samba` are documented here.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.4.9] – 2026-07-05
|
|
8
|
+
|
|
9
|
+
### Fixed – **`0xC0000033` beim Auflisten des Basispfads (smb3-client-vs-Samba-4.23-Bug jetzt auch für Basispfade abgefangen)**
|
|
10
|
+
|
|
11
|
+
Auch in 0.4.8 lief der Verbindungstest mit gesetztem Basispfad in
|
|
12
|
+
`QUERY_DIRECTORY failed: 0xC0000033` (`OBJECT_NAME_INVALID`). Ohne
|
|
13
|
+
Basispfad war die Verbindung korrekt und zeigte den gelben Hinweis.
|
|
14
|
+
|
|
15
|
+
**Ursache:** `smb3-client` sendet `QUERY_DIRECTORY` mit hart kodiertem
|
|
16
|
+
`FileInformationClass = 37` (`FileIdBothDirectoryInformation`). Samba
|
|
17
|
+
4.23 lehnt das für bestimmte Verzeichnisse ab — auch dann, wenn der
|
|
18
|
+
Ordner existiert, geöffnet und `stat`-bar ist. Genau derselbe Bug, der
|
|
19
|
+
bisher nur beim Auflisten des Share-Roots als ‚Root nicht auflistbar'
|
|
20
|
+
aufgefangen wurde, kann auch den konfigurierten Basispfad selbst
|
|
21
|
+
treffen.
|
|
22
|
+
|
|
23
|
+
**Fix:** Der Fallback greift jetzt auch für Basispfade. Wenn
|
|
24
|
+
`client.readdir("")` mit `0xC0000033` scheitert, versucht die Test-Route
|
|
25
|
+
zusätzlich `client.stat("")` auf denselben Pfad:
|
|
26
|
+
|
|
27
|
+
- **stat OK** → Verbindung + Basispfad bestätigt, gelber Hinweis mit
|
|
28
|
+
Erklärung des bekannten Bugs und Workaround-Vorschlägen.
|
|
29
|
+
- **stat schlägt auch fehl** → normaler Fehlerpfad (Basispfad-
|
|
30
|
+
Diagnose mit Schreibvarianten-Probes) läuft weiter wie bisher.
|
|
31
|
+
|
|
32
|
+
Damit lassen sich Basispfade eintragen, auf die `readdir` per
|
|
33
|
+
`FileIdBothDirectoryInformation` scheitert — der Download und die
|
|
34
|
+
Unterordner-Ansicht funktionieren über die normalen SMB-Calls
|
|
35
|
+
trotzdem.
|
|
36
|
+
|
|
37
|
+
## [0.4.8] – 2026-07-05
|
|
38
|
+
|
|
39
|
+
### Fixed – **Basispfad wurde beim Verbindungstest doppelt vorangestellt (die eigentliche Ursache)**
|
|
40
|
+
|
|
41
|
+
In allen Vorgängerversionen (0.4.4 – 0.4.7) hat der Verbindungstest den
|
|
42
|
+
konfigurierten Basispfad **zweimal** auf den Server geschickt und deshalb
|
|
43
|
+
systematisch die Meldung *„Der Basispfad ist auf dem Server nicht auffindbar"*
|
|
44
|
+
produziert — auch dann, wenn der Pfad in Wirklichkeit existierte und mit
|
|
45
|
+
`smbclient` oder dem Windows-Explorer problemlos erreichbar war.
|
|
46
|
+
|
|
47
|
+
**Ursache:** `buildClient(config)` speichert `config.base_path` intern als
|
|
48
|
+
`client.basePath`. Der Wrapper-`resolvePath(rel)` baut daraus
|
|
49
|
+
`<share>/<basePath>/<rel>`. Die Test-Route rief aber
|
|
50
|
+
`client.readdir(testCfg.base_path)` auf und übergab den Basispfad **noch
|
|
51
|
+
einmal** als relatives Argument. Ergebnis: aus `base_path = "static"` wurde
|
|
52
|
+
auf der Leitung `buero/static/static` — den es natürlich nicht gibt. Die
|
|
53
|
+
Diagnose-Ausgabe (`tested_path: static/static`) zeigte den Bug bereits
|
|
54
|
+
klar an, wurde aber bisher als Symptom statt Ursache gelesen.
|
|
55
|
+
|
|
56
|
+
**Fix:** Die Test-Route ruft jetzt `client.readdir("")` auf. Der Wrapper
|
|
57
|
+
resolvet das intern korrekt zu `<share>/<basePath>` — also genau dem Pfad,
|
|
58
|
+
den der Benutzer im Formular eingetragen hat. Für Anzeige-Zwecke (Meldungen,
|
|
59
|
+
Diagnose-Kacheln, Fallback-Probes) wird der Basispfad separat in
|
|
60
|
+
`baseForDisplay` gehalten.
|
|
61
|
+
|
|
62
|
+
### Added – **Standalone-Diagnose-Skript `tools/diag-basepath.js`**
|
|
63
|
+
|
|
64
|
+
Wer den Verbindungsproblemen auf den Grund gehen will, kann jetzt außerhalb
|
|
65
|
+
von Saltcorn probieren, was smb3-client tatsächlich sieht:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
node tools/diag-basepath.js \
|
|
69
|
+
--host 192.168.110.10 --share buero --path static \
|
|
70
|
+
--user 01_vassen --domain buero.ib-vassen.de --password ...
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Das Skript führt sechs Probes durch (Share-Root, Share-Root mit Slash,
|
|
74
|
+
`stat` auf Ziel, `readdir` auf Ziel, Groß-/Kleinschreibvariante) und
|
|
75
|
+
druckt für jede den NT-Status-Code — hilfreich beim Aufspüren von
|
|
76
|
+
Schreibvarianten oder ACL-Problemen.
|
|
77
|
+
|
|
78
|
+
## [0.4.7] – 2026-07-05
|
|
79
|
+
|
|
80
|
+
### Fixed – **Irreführende Windows-UNC-Anzeige in der Fehlermeldung**
|
|
81
|
+
|
|
82
|
+
Die v0.4.6-Fehlermeldung zeigte den getesteten Pfad als Windows-UNC
|
|
83
|
+
(`\\\\192.168.110.10\\buero\\static`). Das war irreführend, weil das
|
|
84
|
+
Plugin an smb3-client tatsächlich Forward-Slash-Pfade schickt
|
|
85
|
+
(`buero/static`) — die Backslash-Anzeige suggerierte einen Bug, der
|
|
86
|
+
keiner ist. Jetzt wird der Pfad in genau der Form gezeigt, die auch
|
|
87
|
+
tatsächlich über die Leitung geht: `<share>/<basispfad>`.
|
|
88
|
+
|
|
7
89
|
## [0.4.6] – 2026-07-05
|
|
8
90
|
|
|
9
91
|
### Fixed – **Schreibvarianten-Test verdoppelte den Basispfad**
|
package/index.js
CHANGED
|
@@ -853,72 +853,74 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
853
853
|
// to stat("") — that already proves the whole handshake
|
|
854
854
|
// (TCP + Negotiate + Session + TREE_CONNECT + Auth) works, which
|
|
855
855
|
// is all the connection test actually promises.
|
|
856
|
-
|
|
857
|
-
//
|
|
858
|
-
//
|
|
859
|
-
//
|
|
860
|
-
//
|
|
861
|
-
//
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
// for directories with strict ACLs. readdir() sends
|
|
865
|
-
// createOptions=1 (DIRECTORY_FILE) and is the right primitive
|
|
866
|
-
// for a base_path check anyway — we want to know the folder
|
|
867
|
-
// can actually be listed, not just opened.
|
|
856
|
+
//
|
|
857
|
+
// IMPORTANT: buildClient already stores `base_path` as the
|
|
858
|
+
// wrapper's `basePath`, and wrapper.readdir(x) resolves to
|
|
859
|
+
// `share/basePath/x`. So we call readdir("") to list the
|
|
860
|
+
// configured basePath itself — not readdir(base_path), which
|
|
861
|
+
// would append it twice (bug fixed in 0.4.8).
|
|
862
|
+
const rel = "";
|
|
863
|
+
const baseForDisplay = testCfg.base_path ? sanitizeRelativePath(testCfg.base_path) : "";
|
|
868
864
|
try {
|
|
869
865
|
return await client.readdir(rel);
|
|
870
866
|
} catch (err) {
|
|
871
867
|
// Look at both the wrapper error and its underlying cause.
|
|
872
868
|
const causeMsg = String((err && err.cause && err.cause.message) || "");
|
|
873
869
|
const msg = String((err && err.message) || err || "") + " " + causeMsg;
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
870
|
+
// The path we effectively asked the server to enumerate is
|
|
871
|
+
// `share/basePath` (rel is always "" here — basePath is the
|
|
872
|
+
// subdirectory to test).
|
|
873
|
+
const effectivePath = testCfg.share + (baseForDisplay ? "/" + baseForDisplay : "");
|
|
874
|
+
const isRootProbe = !baseForDisplay;
|
|
875
|
+
// smb3-client hardcodes FileInformationClass=37
|
|
876
|
+
// (FileIdBothDirectoryInformation) in QUERY_DIRECTORY. Some
|
|
877
|
+
// Samba 4.23+ configurations reject that with 0xC0000033
|
|
878
|
+
// (OBJECT_NAME_INVALID) for specific directories — even though
|
|
879
|
+
// the same directory can be opened, stat'd and traversed. In
|
|
880
|
+
// that case we fall back to a plain stat: if the folder exists
|
|
881
|
+
// (stat succeeds) we can prove the connection works, but flag
|
|
882
|
+
// that enumeration is broken so the caller shows the yellow
|
|
883
|
+
// hint instead of a red error.
|
|
884
|
+
const isEnumBug = /0xC0000033|OBJECT_NAME_INVALID|QUERY_DIRECTORY failed/i.test(msg);
|
|
885
|
+
if (isEnumBug) {
|
|
880
886
|
try {
|
|
881
887
|
await client.stat("");
|
|
882
|
-
// Signal to the caller that the connection works, but
|
|
883
|
-
// the share root cannot be enumerated on this server.
|
|
884
888
|
const marker = [];
|
|
885
|
-
|
|
889
|
+
if (isRootProbe) {
|
|
890
|
+
marker._rootNotEnumerable = true;
|
|
891
|
+
} else {
|
|
892
|
+
marker._basePathNotEnumerable = true;
|
|
893
|
+
marker._basePathForDisplay = baseForDisplay;
|
|
894
|
+
}
|
|
886
895
|
return marker;
|
|
887
896
|
} catch (statErr) {
|
|
888
|
-
// stat also failed —
|
|
889
|
-
|
|
897
|
+
// stat also failed — fall through to normal error handling
|
|
898
|
+
// (missing-basepath diagnostics or generic rethrow).
|
|
890
899
|
}
|
|
891
900
|
}
|
|
892
|
-
//
|
|
893
|
-
//
|
|
894
|
-
//
|
|
895
|
-
// (if not the share root) and probe common alternative
|
|
896
|
-
// spellings of the missing segment so we can distinguish
|
|
897
|
-
// typo / case-mismatch / permission problems.
|
|
901
|
+
// NAME_NOT_FOUND / PATH_NOT_FOUND on a configured base_path.
|
|
902
|
+
// Gather diagnostics: raw probes against alternate spellings
|
|
903
|
+
// and (if possible) a listing of the parent directory.
|
|
898
904
|
const isMissing = /OBJECT_NAME_NOT_FOUND|OBJECT_PATH_NOT_FOUND|ENOENT|STATUS_NO_SUCH_FILE|existiert.*nicht/i.test(msg);
|
|
899
|
-
if (
|
|
900
|
-
const parts =
|
|
905
|
+
if (baseForDisplay && isMissing) {
|
|
906
|
+
const parts = baseForDisplay.split("/").filter(Boolean);
|
|
901
907
|
const missing = parts[parts.length - 1];
|
|
902
908
|
const parent = parts.slice(0, -1).join("/");
|
|
903
909
|
const parentAbs = parent || "(Share-Root)";
|
|
904
|
-
//
|
|
905
|
-
//
|
|
906
|
-
//
|
|
907
|
-
// (e.g. readdir("static") → "buero/static/static").
|
|
910
|
+
// Probe via the raw smb3-client so we bypass the wrapper's
|
|
911
|
+
// basePath prefix and can test siblings/alternate spellings
|
|
912
|
+
// directly from the share root.
|
|
908
913
|
const raw = client._raw;
|
|
909
914
|
const rawShare = client.shareName;
|
|
910
915
|
async function rawReaddir(relFromShareRoot) {
|
|
911
|
-
const full =
|
|
916
|
+
const full = relFromShareRoot ? rawShare + "/" + relFromShareRoot : rawShare;
|
|
912
917
|
return await raw.readdir(full, { withFileTypes: true });
|
|
913
918
|
}
|
|
914
919
|
let siblings = null;
|
|
915
920
|
let parent_error = null;
|
|
916
921
|
if (parent) {
|
|
917
922
|
try {
|
|
918
|
-
|
|
919
|
-
// absolute-from-share-root paths in the raw call.
|
|
920
|
-
const parentFromShare = [client.basePath, parent].filter(Boolean).join("/");
|
|
921
|
-
const listing = await rawReaddir(parentFromShare);
|
|
923
|
+
const listing = await rawReaddir(parent);
|
|
922
924
|
siblings = Array.isArray(listing)
|
|
923
925
|
? listing.map((d) => ({
|
|
924
926
|
name: d && d.name,
|
|
@@ -929,9 +931,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
929
931
|
parent_error = String((parentErr && parentErr.message) || parentErr || "");
|
|
930
932
|
}
|
|
931
933
|
}
|
|
932
|
-
// Probe alternate spellings
|
|
933
|
-
// This lets us report "Ordner existiert unter anderem Namen" if
|
|
934
|
-
// Samba is running with case sensitive = yes / case-preserved.
|
|
934
|
+
// Probe alternate spellings of the missing segment.
|
|
935
935
|
const probes = [];
|
|
936
936
|
const seen = new Set();
|
|
937
937
|
const addProbe = (name) => {
|
|
@@ -945,9 +945,9 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
945
945
|
addProbe(missing.charAt(0).toUpperCase() + missing.slice(1).toLowerCase());
|
|
946
946
|
const probe_results = [];
|
|
947
947
|
for (const p of probes) {
|
|
948
|
-
//
|
|
949
|
-
//
|
|
950
|
-
const relFromShare = [
|
|
948
|
+
// From-share-root path: `parent/candidate`. NEVER include
|
|
949
|
+
// client.basePath here — the raw client is share-relative.
|
|
950
|
+
const relFromShare = [parent, p].filter(Boolean).join("/");
|
|
951
951
|
let ok = false;
|
|
952
952
|
let perr = null;
|
|
953
953
|
try {
|
|
@@ -961,7 +961,6 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
961
961
|
const workingAlt = probe_results.find(
|
|
962
962
|
(r) => r.ok && r.candidate !== missing
|
|
963
963
|
);
|
|
964
|
-
const testedPath = "\\\\" + testCfg.server + "\\" + testCfg.share + "\\" + rel.replace(/\//g, "\\");
|
|
965
964
|
let hintText;
|
|
966
965
|
if (workingAlt) {
|
|
967
966
|
hintText =
|
|
@@ -972,11 +971,11 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
972
971
|
"case-sensitive (\u201ecase sensitive = yes\u201c in smb.conf).";
|
|
973
972
|
} else {
|
|
974
973
|
hintText =
|
|
975
|
-
"Der Basispfad \u201e" +
|
|
974
|
+
"Der Basispfad \u201e" + baseForDisplay + "\u201c wurde auf der " +
|
|
976
975
|
"Freigabe \u201e" + testCfg.share + "\u201c nicht " +
|
|
977
|
-
"gefunden.
|
|
978
|
-
"M\u00f6gliche Ursachen: (a) Ordner existiert unter
|
|
979
|
-
"diesem Namen nicht in dieser Freigabe (Basispfad ist " +
|
|
976
|
+
"gefunden. Intern getestet wurde: \u201e" + effectivePath +
|
|
977
|
+
"\u201c. M\u00f6gliche Ursachen: (a) Ordner existiert unter " +
|
|
978
|
+
"genau diesem Namen nicht in dieser Freigabe (Basispfad ist " +
|
|
980
979
|
"relativ zur Freigabe \u2014 also z.\u202fB. \u201eunterordner\u201c, " +
|
|
981
980
|
"nicht \u201e" + testCfg.share + "/unterordner\u201c); " +
|
|
982
981
|
"(b) der angemeldete Benutzer \u201e" +
|
|
@@ -1003,6 +1002,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
1003
1002
|
});
|
|
1004
1003
|
const took = Date.now() - started;
|
|
1005
1004
|
const rootNotEnum = Array.isArray(listing) && listing._rootNotEnumerable === true;
|
|
1005
|
+
const baseNotEnum = Array.isArray(listing) && listing._basePathNotEnumerable === true;
|
|
1006
1006
|
return res.json({
|
|
1007
1007
|
ok: true,
|
|
1008
1008
|
server: testCfg.server,
|
|
@@ -1024,6 +1024,16 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
1024
1024
|
"Verhalten mit smb3-client). Bitte setzen Sie einen Basispfad " +
|
|
1025
1025
|
"in der Plugin-Config (z.\u202fB. einen Unterordner der " +
|
|
1026
1026
|
"Freigabe) — dann funktioniert der File-Manager vollständig."
|
|
1027
|
+
: baseNotEnum
|
|
1028
|
+
? "Verbindung + Anmeldung erfolgreich, Basispfad \u201e" +
|
|
1029
|
+
(listing._basePathForDisplay || testCfg.base_path || "") +
|
|
1030
|
+
"\u201c wurde vom Server best\u00e4tigt (stat OK). Das direkte " +
|
|
1031
|
+
"Auflisten dieses Ordners lehnt Samba 4.23 mit smb3-client " +
|
|
1032
|
+
"aktuell ab (bekannter Bug: FileInformationClass=37 hart " +
|
|
1033
|
+
"kodiert, siehe README). Ordner-Unterb\u00e4ume, Datei-Downloads " +
|
|
1034
|
+
"und PDF-Ansicht funktionieren \u2014 nur die Wurzel des " +
|
|
1035
|
+
"Basispfads kann nicht direkt aufgelistet werden. Workaround: " +
|
|
1036
|
+
"tieferen Basispfad setzen oder Unterordner ansprechen."
|
|
1027
1037
|
: undefined,
|
|
1028
1038
|
});
|
|
1029
1039
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saltcorn-samba",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"description": "Saltcorn plugin: browse, upload, rename and delete files on a Samba/CIFS share via SMB 3.1.1 (AES-CMAC signing, optional encryption). File-manager view, directory tree, inline PDF viewer, external-app open (smb://).",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|