saltcorn-samba 0.4.0 → 0.4.1

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 CHANGED
@@ -4,6 +4,50 @@ 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.1] – 2026-07-05
8
+
9
+ ### Fixed – **`QUERY_DIRECTORY failed: 0xC0000033` auf Share-Root (Samba 4.20+/4.23+)**
10
+
11
+ Symptom nach dem 0.4.0-Update: der Verbindungstest schlägt sofort fehl mit
12
+
13
+ ```
14
+ Fehler: QUERY_DIRECTORY failed: 0xC0000033
15
+ ```
16
+
17
+ (`STATUS_OBJECT_NAME_INVALID`). Betroffen sind Setups **ohne Basispfad** —
18
+ sobald ein Basispfad gesetzt ist, greift der Fehler nicht.
19
+
20
+ **Ursache:** `smb3-client` öffnet den Share-Root im SMB2-CREATE mit einem
21
+ leeren Filename (`""`). Moderne Samba-Versionen (bestehen jedenfalls
22
+ **4.20+**, bestätigt auf **4.23.9**) akzeptieren das für CREATE, weisen
23
+ aber das anschließende `QUERY_DIRECTORY` mit `FileIdBothDirectoryInformation`
24
+ auf dem leeren Namen als `OBJECT_NAME_INVALID` zurück. Vergleichbare
25
+ Probleme sind aus anderen Java-/Go-SMB-Client-Bibliotheken bekannt (z. B.
26
+ [smbj#80](https://github.com/hierynomus/smbj/issues/80)).
27
+
28
+ **Lösung:**
29
+
30
+ 1. `smb-client.js#readdir("")` bekommt einen Fallback: wenn der
31
+ Server auf dem leeren Root mit `OBJECT_NAME_INVALID` antwortet, wird
32
+ die Auflistung noch einmal mit `share/.` (aktuelles Verzeichnis) und,
33
+ falls das ebenfalls scheitert, mit `share/*` (Wildcard) probiert.
34
+ Erst wenn auch das nicht klappt, wird eine deutsche Fehlermeldung mit
35
+ Handlungsanweisung ausgelöst.
36
+ 2. Die `/sambatest`-Route greift auf `client.stat("")` zurück, wenn das
37
+ Root-`readdir` mit `OBJECT_NAME_INVALID` scheitert. `stat("")`
38
+ verwendet CREATE ohne `DIRECTORY_FILE`-Flag und ohne QUERY_DIRECTORY —
39
+ das beweist Netzwerk + Negotiate + Session-Setup + TREE_CONNECT + Auth
40
+ ohne die problematische Query. Der Test liefert dann `entry_count: 0`
41
+ und den Hinweis, dass ein Basispfad gesetzt werden sollte, sofern der
42
+ Server das Root-Listing nicht anders bereitstellt.
43
+
44
+ ### Empfehlung
45
+
46
+ Wenn Ihr Samba-Server das Share-Root-`QUERY_DIRECTORY` weiterhin ablehnt,
47
+ setzen Sie in der Plugin-Config einen **Basispfad** (z. B. `daten` oder
48
+ `projekte`) — dann sind alle Directory-Listings innerhalb dieses
49
+ Unterverzeichnisses, was durchgängig funktioniert.
50
+
7
51
  ## [0.4.0] – 2026-07-05
8
52
 
9
53
  ### ⚠️ BREAKING CHANGES
package/index.js CHANGED
@@ -795,8 +795,25 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
795
795
  try {
796
796
  const listing = await withClient(testCfg, async (client) => {
797
797
  // If base_path is set, list it — that also verifies traversal.
798
+ // For the share-root case we first try readdir; some Samba builds
799
+ // reject QUERY_DIRECTORY on the empty root name, so we fall back
800
+ // to stat("") — that already proves the whole handshake
801
+ // (TCP + Negotiate + Session + TREE_CONNECT + Auth) works, which
802
+ // is all the connection test actually promises.
798
803
  const rel = testCfg.base_path ? sanitizeRelativePath(testCfg.base_path) : "";
799
- return await client.readdir(rel);
804
+ try {
805
+ return await client.readdir(rel);
806
+ } catch (err) {
807
+ const msg = String((err && err.message) || err || "");
808
+ const isRootProbe = !rel;
809
+ const isNameInvalid = /0xC0000033|OBJECT_NAME_INVALID/i.test(msg);
810
+ if (isRootProbe && isNameInvalid) {
811
+ // Fall back: proof-of-life via stat on share root.
812
+ await client.stat("");
813
+ return [];
814
+ }
815
+ throw err;
816
+ }
800
817
  });
801
818
  const took = Date.now() - started;
802
819
  return res.json({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saltcorn-samba",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
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": {
package/smb-client.js CHANGED
@@ -273,7 +273,43 @@ async function buildClient(config) {
273
273
  */
274
274
  async readdir(rel) {
275
275
  const full = resolvePath(rel);
276
- const dirents = await client.readdir(full, { withFileTypes: true });
276
+ let dirents;
277
+ try {
278
+ dirents = await client.readdir(full, { withFileTypes: true });
279
+ } catch (err) {
280
+ // Some Samba builds (observed on 4.20+/4.23+) reject
281
+ // SMB2 QUERY_DIRECTORY on the *share root* with
282
+ // STATUS_OBJECT_NAME_INVALID (0xC0000033) because the CREATE
283
+ // used an empty filename. The fallback below re-issues the
284
+ // request with an explicit path (".") which most Samba VFS
285
+ // modules accept for the current directory.
286
+ const status = (err && (err.status || err.code)) || "";
287
+ const msg = String((err && err.message) || err || "");
288
+ const isRoot = !rel && !basePath;
289
+ const isNameInvalid =
290
+ /0xC0000033|STATUS_OBJECT_NAME_INVALID|OBJECT_NAME_INVALID/i.test(msg) ||
291
+ status === 0xc0000033;
292
+ if (isRoot && isNameInvalid) {
293
+ try {
294
+ dirents = await client.readdir(shareName + "/.", { withFileTypes: true });
295
+ } catch (_) {
296
+ // Second fallback: an explicit wildcard segment.
297
+ try {
298
+ dirents = await client.readdir(shareName + "/*", { withFileTypes: true });
299
+ } catch (_) {
300
+ const e = new Error(
301
+ "QUERY_DIRECTORY auf dem Share-Root schlug fehl (" +
302
+ (msg || "OBJECT_NAME_INVALID") + "). " +
303
+ "Setzen Sie einen Basispfad im Plugin-Konfig oder prüfen Sie die Share-Definition auf dem Server."
304
+ );
305
+ e.cause = err;
306
+ throw e;
307
+ }
308
+ }
309
+ } else {
310
+ throw err;
311
+ }
312
+ }
277
313
  // Parallel enrichment. Bounded to a reasonable concurrency to avoid
278
314
  // saturating the SMB session on huge directories.
279
315
  const CHUNK = 16;