[Pkg-samba-maint] Bug#1107355: unblock: samba/2:4.22.2+dfsg-1
Michael Tokarev
mjt at tls.msk.ru
Fri Jun 6 10:32:26 BST 2025
Package: release.debian.org
Severity: normal
User: release.debian.org at packages.debian.org
Usertags: unblock
X-Debbugs-Cc: samba at packages.debian.org, pkg-samba-maint at lists.alioth.debian.org
Control: affects -1 + src:samba
Please unblock package samba
[ Reason ]
There's a new upstream stable/bugfix point release. It contains,
among multiple other fixes, a fix for CVE-2025-0620 (#1107248).
This release also fixes an omission required for trixie, when
samba in trixie does not support bind9 in trixie (#1106959) -
a trivial change to add support for bind9 version 9.20.
It also contains a minor fix which is not necessary for trixie
but it doesn't hurt either, - it adds libcrypt-dev build
dependency, which is already pulled in anyway due to libc-dev
which depends on it.
[ Tests ]
All basic functionality is working fine in this release, including
domain operations - it definitely is not worse than the previous
release already in testing/trixie (4.22.1+dfsg-1). It also passes
all debci tests so far.
[ Risks ]
There's always some risk of breaking something. Samba upstream
minor releases are usually quite good thoug, and this one does
not look like an exception. All the fixes are rather contained
and fixes actual issues, even if complex at times.
[ Checklist ]
[X] all changes are documented in the d/changelog
[X] I reviewed all changes and I approve them
[X] attach debdiff against the package in testing
unblock samba/2:4.22.2+dfsg-1
[ Additional Info ]
https://salsa.debian.org/samba-team/samba/-/commits/samba-4.22.2
is the series of upstream commits (up to samba-4.22.1 tag) with
explanations and a reference to samba bugzilla for each change.
Thanks,
/mjt
diff -Nru samba-4.22.1+dfsg/debian/changelog samba-4.22.2+dfsg/debian/changelog
--- samba-4.22.1+dfsg/debian/changelog 2025-04-18 13:02:55.000000000 +0300
+++ samba-4.22.2+dfsg/debian/changelog 2025-06-05 19:12:34.000000000 +0300
@@ -1,3 +1,27 @@
+samba (2:4.22.2+dfsg-1) unstable; urgency=medium
+
+ * new upstream stable/bugfix release:
+ - https://bugzilla.samba.org/show_bug.cgi?id=15707:
+ CVE-2025-0620: smbd doesn't pick up group membership changes when
+ re-authenticating an expired SMB session
+ Closes: #1107248
+ - https://bugzilla.samba.org/show_bug.cgi?id=15727:
+ net ad join fails with
+ "Failed to join domain: failed to create kerberos keytab"
+ - https://bugzilla.samba.org/show_bug.cgi?id=15819:
+ vfs_ceph_snapshots fails to list snapshots for entries at any level
+ beyond share root
+ - https://bugzilla.samba.org/show_bug.cgi?id=15851:
+ dcerpcd not able to bind to listening port
+ - https://bugzilla.samba.org/show_bug.cgi?id=15858:
+ CTDB does not put nodes running NFS into grace on graceful shutdown
+ - https://bugzilla.samba.org/show_bug.cgi?id=15861:
+ Profile sync fails due to Directory Leases
+ * d/control: add libcrypt-dev build-dependency (Closes: #1106959)
+ * add-support-for-bind-9.20.patch (Closes: #1107139)
+
+ -- Michael Tokarev <mjt at tls.msk.ru> Thu, 05 Jun 2025 19:12:34 +0300
+
samba (2:4.22.1+dfsg-1) unstable; urgency=medium
* new upstream stable/bugfix release:
diff -Nru samba-4.22.1+dfsg/debian/control samba-4.22.2+dfsg/debian/control
--- samba-4.22.1+dfsg/debian/control 2025-04-17 14:27:34.000000000 +0300
+++ samba-4.22.2+dfsg/debian/control 2025-06-05 18:53:51.000000000 +0300
@@ -37,6 +37,7 @@
libcephfs-dev [amd64 arm64 mips64el ppc64el riscv64 s390x],
librados-dev [amd64 arm64 mips64el ppc64el riscv64 s390x],
libcmocka-dev,
+ libcrypt-dev,
libcups2-dev,
libdbus-1-dev,
libglusterfs-dev [amd64 arm64 loong64 mips64el ppc64 ppc64el riscv64 s390x sparc64],
diff -Nru samba-4.22.1+dfsg/debian/patches/add-support-for-bind-9.20.patch samba-4.22.2+dfsg/debian/patches/add-support-for-bind-9.20.patch
--- samba-4.22.1+dfsg/debian/patches/add-support-for-bind-9.20.patch 1970-01-01 03:00:00.000000000 +0300
+++ samba-4.22.2+dfsg/debian/patches/add-support-for-bind-9.20.patch 2025-06-05 18:53:51.000000000 +0300
@@ -0,0 +1,55 @@
+From: Michael Tokarev <mjt at tls.msk.ru>
+Date: Tue, 3 Jun 2025 09:41:57 +0300
+Subject: s4/dlz: add support for bind 9.20
+Forwarded: yes, https://gitlab.com/samba-team/samba/-/merge_requests/4067
+Bug-Debian: https://bugs.debian.org/1107139
+
+bind dlz interface does not change much, yet we build
+dlz_bind9_NN for every bind9 version NN we support -
+despite many of them differ only in soversion, with
+the code being identical.
+
+For bind9_20, use dlz_bind9_18.so which we already have.
+
+It'd be nice to extract actual bind9 version string in
+sambadns.py and use it in more direct way.
+
+Bug: https://bugzilla.samba.org/show_bug.cgi?id=15790
+Signed-off-by: Michael Tokarev <mjt at tls.msk.ru>
+---
+ python/samba/provision/sambadns.py | 3 ++-
+ source4/setup/named.conf.dlz | 4 ++--
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/python/samba/provision/sambadns.py b/python/samba/provision/sambadns.py
+index 952e875c862..a3515bbe37b 100644
+--- a/python/samba/provision/sambadns.py
++++ b/python/samba/provision/sambadns.py
+@@ -1030,7 +1030,8 @@ def create_named_conf(paths, realm, dnsdomain, dns_backend, logger):
+ bind9_14 = ''
+ elif bind_info.upper().find('BIND 9.16') != -1:
+ bind9_16 = ''
+- elif bind_info.upper().find('BIND 9.18') != -1:
++ elif bind_info.upper().find('BIND 9.18') != -1 \
++ or bind_info.upper().find('BIND 9.20') != -1:
+ bind9_18 = ''
+ elif bind_info.upper().find('BIND 9.7') != -1:
+ raise ProvisioningError("DLZ option incompatible with BIND 9.7.")
+diff --git a/source4/setup/named.conf.dlz b/source4/setup/named.conf.dlz
+index cbe7d805f58..9753cdc503b 100644
+--- a/source4/setup/named.conf.dlz
++++ b/source4/setup/named.conf.dlz
+@@ -30,8 +30,8 @@ dlz "AD DNS Zone" {
+
+ # For BIND 9.16.x
+ ${BIND9_16} database "dlopen ${MODULESDIR}/bind9/dlz_bind9_16.so";
+- #
+- # For BIND 9.18.x
++
++ # For BIND 9.18.x and 9.20.x
+ ${BIND9_18} database "dlopen ${MODULESDIR}/bind9/dlz_bind9_18.so";
+ };
+
+--
+2.39.5
+
diff -Nru samba-4.22.1+dfsg/debian/patches/series samba-4.22.2+dfsg/debian/patches/series
--- samba-4.22.1+dfsg/debian/patches/series 2025-04-18 12:52:27.000000000 +0300
+++ samba-4.22.2+dfsg/debian/patches/series 2025-06-05 18:53:51.000000000 +0300
@@ -23,3 +23,4 @@
ctdb-use-run-instead-of-var-run.patch
revert-ldb-use-hexchars_upper-from-replace.h.patch
replace-xpg-strerror.patch
+add-support-for-bind-9.20.patch
diff -Nru samba-4.22.1+dfsg/VERSION samba-4.22.2+dfsg/VERSION
--- samba-4.22.1+dfsg/VERSION 2025-04-17 20:12:25.638450100 +0300
+++ samba-4.22.2+dfsg/VERSION 2025-06-05 18:38:33.686580400 +0300
@@ -27,7 +27,7 @@
########################################################
SAMBA_VERSION_MAJOR=4
SAMBA_VERSION_MINOR=22
-SAMBA_VERSION_RELEASE=1
+SAMBA_VERSION_RELEASE=2
########################################################
# If a official release has a serious bug #
diff -Nru samba-4.22.1+dfsg/WHATSNEW.txt samba-4.22.2+dfsg/WHATSNEW.txt
--- samba-4.22.1+dfsg/WHATSNEW.txt 2025-04-17 20:12:25.638450100 +0300
+++ samba-4.22.2+dfsg/WHATSNEW.txt 2025-06-05 18:38:33.686580400 +0300
@@ -1,4 +1,88 @@
==============================
+ Release Notes for Samba 4.22.2
+ June 05, 2025
+ ==============================
+
+
+This is the latest stable release of the Samba 4.22 release series.
+It contains the security-relevant bugfix CVE-2025-0620:
+
+ smbd doesn't pick up group membership changes
+ when re-authenticating an expired SMB session
+ https://www.samba.org/samba/security/CVE-2025-0620.html
+
+
+Description of CVE-2025-0620
+-----------------------------
+
+ With Kerberos authentication SMB sessions typically have an
+ associated lifetime, requiring re-authentication by the
+ client when the session expires. As part of the
+ re-authentication, Samba receives the current group
+ membership information and is expected to reflect this
+ change in further SMB request processing.
+
+ For historic reasons, Samba maintains a cache of
+ associations between a user's impersonation information and
+ connected shares. A recent change in this cache caused Samba
+ to not reflect group membership changes from session
+ re-authentication when processing further SMB requests.
+
+ As a result, when an administrator removes a user from a
+ particular group in Active Directory, this change will not
+ become effective unless the user disconnects from the server
+ and establishes a new connection.
+
+
+Changes since 4.22.1
+--------------------
+
+o Ralph Boehme <slow at samba.org>
+ * BUG 15707: (CVE-2025-0620) [SECURITY] CVE-2025-0620: smbd doesn't pick up
+ group membership changes when re-authenticating an expired SMB
+ session.
+ * BUG 15861: Profile sync fails due to Directory Leases.
+
+o Pavel Filipenský <pfilipensky at samba.org>
+ * BUG 15727: net ad join fails with "Failed to join domain: failed to create
+ kerberos keytab".
+
+o Stefan Metzmacher <metze at samba.org>
+ * BUG 15851: dcerpcd not able to bind to listening port.
+
+o Anoop C S <anoopcs at samba.org>
+ * BUG 15819: vfs_ceph_snapshots fails to list snapshots for entries at any
+ level beyond share root.
+
+o Martin Schwenke <mschwenke at ddn.com>
+ * BUG 15858: CTDB does not put nodes running NFS into grace on graceful
+ shutdown.
+
+
+#######################################
+Reporting bugs & Development Discussion
+#######################################
+
+Please discuss this release on the samba-technical mailing list or by
+joining the #samba-technical:matrix.org matrix room, or
+#samba-technical IRC channel on irc.libera.chat.
+
+If you do report problems then please try to send high quality
+feedback. If you don't provide vital information to help us track down
+the problem then you will probably be ignored. All bug reports should
+be filed under the Samba 4.1 and newer product in the project's Bugzilla
+database (https://bugzilla.samba.org/).
+
+
+======================================================================
+== Our Code, Our Bugs, Our Responsibility.
+== The Samba Team
+======================================================================
+
+
+Release notes for older releases follow:
+----------------------------------------
+ ==============================
Release Notes for Samba 4.22.1
April 17, 2025
==============================
@@ -74,8 +158,7 @@
======================================================================
-Release notes for older releases follow:
-----------------------------------------
+----------------------------------------------------------------------
==============================
Release Notes for Samba 4.22.0
March 06, 2025
diff -Nru samba-4.22.1+dfsg/ctdb/conf/ctdb_config.c samba-4.22.2+dfsg/ctdb/conf/ctdb_config.c
--- samba-4.22.1+dfsg/ctdb/conf/ctdb_config.c 2025-02-06 13:31:53.708143700 +0300
+++ samba-4.22.2+dfsg/ctdb/conf/ctdb_config.c 2025-06-05 18:38:33.734580800 +0300
@@ -110,6 +110,14 @@
FAILOVER_CONF_SECTION,
FAILOVER_CONF_DISABLED,
&ctdb_config.failover_disabled);
+ conf_assign_integer_pointer(conf,
+ FAILOVER_CONF_SECTION,
+ FAILOVER_CONF_SHUTDOWN_EXTRA_TIMEOUT,
+ &ctdb_config.shutdown_extra_timeout);
+ conf_assign_integer_pointer(conf,
+ FAILOVER_CONF_SECTION,
+ FAILOVER_CONF_SHUTDOWN_FAILOVER_TIMEOUT,
+ &ctdb_config.shutdown_failover_timeout);
/*
* Legacy
diff -Nru samba-4.22.1+dfsg/ctdb/conf/ctdb_config.h samba-4.22.2+dfsg/ctdb/conf/ctdb_config.h
--- samba-4.22.1+dfsg/ctdb/conf/ctdb_config.h 2025-02-06 13:31:53.712144000 +0300
+++ samba-4.22.2+dfsg/ctdb/conf/ctdb_config.h 2025-06-05 18:38:33.734580800 +0300
@@ -44,6 +44,8 @@
/* Failover */
bool failover_disabled;
+ int shutdown_extra_timeout;
+ int shutdown_failover_timeout;
/* Legacy */
bool realtime_scheduling;
diff -Nru samba-4.22.1+dfsg/ctdb/conf/failover_conf.c samba-4.22.2+dfsg/ctdb/conf/failover_conf.c
--- samba-4.22.1+dfsg/ctdb/conf/failover_conf.c 2025-02-06 13:31:53.712144000 +0300
+++ samba-4.22.2+dfsg/ctdb/conf/failover_conf.c 2025-06-05 18:38:33.734580800 +0300
@@ -50,4 +50,16 @@
FAILOVER_CONF_DISABLED,
false,
check_static_boolean_change);
+
+ conf_define_integer(conf,
+ FAILOVER_CONF_SECTION,
+ FAILOVER_CONF_SHUTDOWN_EXTRA_TIMEOUT,
+ 0,
+ NULL);
+
+ conf_define_integer(conf,
+ FAILOVER_CONF_SECTION,
+ FAILOVER_CONF_SHUTDOWN_FAILOVER_TIMEOUT,
+ 10,
+ NULL);
}
diff -Nru samba-4.22.1+dfsg/ctdb/conf/failover_conf.h samba-4.22.2+dfsg/ctdb/conf/failover_conf.h
--- samba-4.22.1+dfsg/ctdb/conf/failover_conf.h 2025-02-06 13:31:53.712144000 +0300
+++ samba-4.22.2+dfsg/ctdb/conf/failover_conf.h 2025-06-05 18:38:33.734580800 +0300
@@ -25,6 +25,9 @@
#define FAILOVER_CONF_SECTION "failover"
#define FAILOVER_CONF_DISABLED "disabled"
+#define FAILOVER_CONF_SHUTDOWN_EXTRA_TIMEOUT "shutdown extra timeout"
+#define FAILOVER_CONF_SHUTDOWN_FAILOVER_TIMEOUT "shutdown failover timeout"
+
void failover_conf_init(struct conf_context *conf);
diff -Nru samba-4.22.1+dfsg/ctdb/doc/ctdb.conf.5.xml samba-4.22.2+dfsg/ctdb/doc/ctdb.conf.5.xml
--- samba-4.22.1+dfsg/ctdb/doc/ctdb.conf.5.xml 2025-02-06 13:31:53.716143800 +0300
+++ samba-4.22.2+dfsg/ctdb/doc/ctdb.conf.5.xml 2025-06-05 18:38:33.738581000 +0300
@@ -496,6 +496,56 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>shutdown extra timeout = <parameter>TIMEOUT</parameter></term>
+ <listitem>
+ <para>
+ CTDB will wait for TIMEOUT seconds after failover
+ completes during shutdown. This can provide extra time
+ for SMB durable handles to be reclaimed. If set to 0 then
+ no extra timeout occurs.
+ </para>
+ <para>
+ This timeout only occurs if both of the following
+ conditions are true:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ shutdown failover timeout (below) is not 0
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Failover during shutdown completes and does not time out
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ Default: <literal>0</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>shutdown failover timeout = <parameter>TIMEOUT</parameter></term>
+ <listitem>
+ <para>
+ CTDB will wait for TIMEOUT seconds for failover to
+ complete during shutdown. This allows NFS servers on
+ other nodes to go into grace during graceful shutdown of a
+ node. Failover during shutdown also helps with SMB
+ durable handle reclaim.
+ </para>
+ <para>
+ Set this to 0 to disable explicit failover on shutdown.
+ </para>
+ <para>
+ Default: <literal>10</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff -Nru samba-4.22.1+dfsg/ctdb/protocol/protocol.h samba-4.22.2+dfsg/ctdb/protocol/protocol.h
--- samba-4.22.1+dfsg/ctdb/protocol/protocol.h 2025-02-06 13:31:53.724144000 +0300
+++ samba-4.22.2+dfsg/ctdb/protocol/protocol.h 2025-06-05 18:38:33.738581000 +0300
@@ -234,6 +234,13 @@
#define CTDB_SRVID_TEST_RANGE 0xAE00000000000000LL
+/* Range of ports reserved for CTDB server (top 8 bits)
+ * All ports matching the 8 top bits are reserved for exclusive use by
+ * the CTDB server
+ */
+#define CTDB_SRVID_SERVER_RANGE 0x9E00000000000000LL
+
+
enum ctdb_controls {CTDB_CONTROL_PROCESS_EXISTS = 0,
CTDB_CONTROL_STATISTICS = 1,
/* #2 removed */
diff -Nru samba-4.22.1+dfsg/ctdb/server/ctdb_daemon.c samba-4.22.2+dfsg/ctdb/server/ctdb_daemon.c
--- samba-4.22.1+dfsg/ctdb/server/ctdb_daemon.c 2025-02-06 13:31:53.728144000 +0300
+++ samba-4.22.2+dfsg/ctdb/server/ctdb_daemon.c 2025-06-05 18:38:33.742581000 +0300
@@ -23,6 +23,7 @@
#include "system/wait.h"
#include "system/time.h"
+#include <errno.h>
#include <talloc.h>
/* Allow use of deprecated function tevent_loop_allow_nesting() */
#define TEVENT_DEPRECATED
@@ -41,6 +42,7 @@
#include "ctdb_client.h"
#include "protocol/protocol.h"
+#include "protocol/protocol_basic.h"
#include "protocol/protocol_api.h"
#include "common/rb_tree.h"
@@ -50,7 +52,9 @@
#include "common/logging.h"
#include "common/pidfile.h"
#include "common/sock_io.h"
+#include "common/srvid.h"
+#include "conf/ctdb_config.h"
#include "conf/node.h"
struct ctdb_client_pid_list {
@@ -2219,15 +2223,234 @@
return ret;
}
+/*
+ * Construct a SRVID for accepting replies to this ctdbd. The bottom
+ * 24 bits of the PNN are used in the top half. extra_mask is used in
+ * the bottom half.
+ */
+
+static uint64_t ctdb_srvid_id(struct ctdb_context *ctdb, uint32_t extra_mask)
+{
+ uint64_t pnn_mask = (uint64_t)(ctdb->pnn & 0xFFFFFF) << 32;
+
+ return CTDB_SRVID_SERVER_RANGE | pnn_mask | extra_mask;
+}
+
+/*
+ * Do a takeover run on shutdown
+ *
+ * This allows for a graceful transition of resources to another node.
+ * This ensures all nodes go into grace for NFS and, with an extra
+ * timeout, allows data transfer for SMB durable handles.
+ *
+ * Nodes need to be in CTDB_RUNSTATE_RUNNING to host public IP
+ * addresses. So, this node will release all IPs. The good news is
+ * that a node can remain leader when in CTDB_RUNSTATE_SHUTDOWN, so
+ * shutting down the cluster will not be adversely delayed by this.
+ * The only issue to guard against is delaying shutdown of this node
+ * if it is the only node and doesn't have CTDB_CAP_RECMASTER, in
+ * which case there is no node to do the takeover run. Hence, the
+ * timeout.
+ */
+
+struct shutdown_takeover_state {
+ bool takeover_done;
+ bool timed_out;
+ struct tevent_timer *te;
+ unsigned int leader_broadcast_count;
+};
+
+static void shutdown_takeover_handler(uint64_t srvid,
+ TDB_DATA data,
+ void *private_data)
+{
+ struct shutdown_takeover_state *state = private_data;
+ int32_t result = 0;
+ size_t count = 0;
+ int ret = 0;
+
+ ret = ctdb_int32_pull(data.dptr, data.dsize, &result, &count);
+ if (ret == EMSGSIZE) {
+ /*
+ * Can't happen unless there's bug somewhere else, so
+ * just ignore - ctdb_shutdown_takeover() will
+ * probably time out...
+ */
+ DBG_WARNING("Wrong size for result\n");
+ return;
+ }
+
+ if (result == -1) {
+ /*
+ * No early return - can't afford endless retries
+ * during shutdown...
+ */
+ DBG_WARNING("Takeover run failed\n");
+ } else {
+ DBG_NOTICE("Takeover run successful by node=%"PRIi32"\n",
+ result);
+ }
+
+ state->takeover_done = true;
+}
+
+static void shutdown_timeout_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval yt,
+ void *private_data)
+{
+ struct shutdown_takeover_state *state = private_data;
+
+ TALLOC_FREE(state->te);
+ state->timed_out = true;
+}
+
+static void shutdown_leader_handler(uint64_t srvid,
+ TDB_DATA data,
+ void *private_data)
+{
+ struct shutdown_takeover_state *state = private_data;
+ uint32_t pnn = 0;
+ size_t count = 0;
+ int ret = 0;
+
+ ret = ctdb_uint32_pull(data.dptr, data.dsize, &pnn, &count);
+ if (ret == EMSGSIZE) {
+ /*
+ * Can't happen unless there's bug somewhere else, so
+ * just ignore
+ */
+ DBG_WARNING("Wrong size for result\n");
+ return;
+ }
+
+ DBG_DEBUG("Leader broadcast received from node=%"PRIu32"\n", pnn);
+ state->leader_broadcast_count++;
+}
+
+static void ctdb_shutdown_takeover(struct ctdb_context *ctdb)
+{
+ struct shutdown_takeover_state state = {
+ .takeover_done = false,
+ .timed_out = false,
+ .te = NULL,
+ .leader_broadcast_count = 0,
+ };
+ /*
+ * This one is memcpy()ed onto the wire, so initialise below
+ * after ZERO_STRUCT(), to keep things valgrind clean
+ */
+ struct ctdb_srvid_message rd;
+ struct TDB_DATA rddata = {
+ .dptr = (uint8_t *)&rd,
+ .dsize = sizeof(rd),
+ };
+ int ret = 0;
+
+ if (ctdb_config.shutdown_failover_timeout <= 0) {
+ return;
+ }
+
+ ZERO_STRUCT(rd);
+ rd = (struct ctdb_srvid_message) {
+ .pnn = ctdb->pnn,
+ .srvid = ctdb_srvid_id(ctdb, 0),
+ };
+
+ ret = srvid_register(ctdb->srv,
+ ctdb->srv,
+ rd.srvid,
+ shutdown_takeover_handler,
+ &state);
+ if (ret != 0) {
+ DBG_WARNING("Failed to register takeover run handler\n");
+ return;
+ }
+
+ state.te = tevent_add_timer(
+ ctdb->ev,
+ ctdb->srv,
+ timeval_current_ofs(ctdb_config.shutdown_failover_timeout, 0),
+ shutdown_timeout_handler,
+ &state);
+ if (state.te == NULL) {
+ DBG_WARNING("Failed to set shutdown timeout\n");
+ goto done;
+ }
+
+ ret = srvid_register(ctdb->srv,
+ ctdb->srv,
+ CTDB_SRVID_LEADER,
+ shutdown_leader_handler,
+ &state);
+ if (ret != 0) {
+ /* Leader broadcasts provide extra information, so no
+ * problem if they can't be monitored...
+ */
+ DBG_WARNING("Failed to register leader handler\n");
+ }
+
+ ret = ctdb_daemon_send_message(ctdb,
+ CTDB_BROADCAST_CONNECTED,
+ CTDB_SRVID_TAKEOVER_RUN,
+ rddata);
+ if (ret != 0) {
+ DBG_WARNING("Failed to send IP takeover run request\n");
+ goto done;
+ }
+
+ while (!state.takeover_done && !state.timed_out) {
+ tevent_loop_once(ctdb->ev);
+ }
+
+ if (state.takeover_done) {
+ goto done;
+ }
+
+ if (state.timed_out) {
+ DBG_WARNING("Timed out waiting for takeover run "
+ "(%u leader broadcasts received)\n",
+ state.leader_broadcast_count);
+ }
+done:
+ srvid_deregister(ctdb->srv, CTDB_SRVID_TAKEOVER_RUN, &state);
+ srvid_deregister(ctdb->srv, CTDB_SRVID_LEADER, &state);
+ TALLOC_FREE(state.te);
+
+ if (!state.takeover_done || ctdb_config.shutdown_extra_timeout <= 0) {
+ return;
+ }
+
+ state.timed_out = false;
+ state.te = tevent_add_timer(
+ ctdb->ev,
+ ctdb->srv,
+ timeval_current_ofs(ctdb_config.shutdown_extra_timeout, 0),
+ shutdown_timeout_handler,
+ &state);
+ if (state.te == NULL) {
+ DBG_WARNING("Failed to set extra timeout\n");
+ return;
+ }
+
+ DBG_NOTICE("Waiting %ds for shutdown extra timeout\n",
+ ctdb_config.shutdown_extra_timeout);
+ while (!state.timed_out) {
+ tevent_loop_once(ctdb->ev);
+ }
+ DBG_INFO("shutdown extra timeout complete\n");
+}
+
void ctdb_shutdown_sequence(struct ctdb_context *ctdb, int exit_code)
{
if (ctdb->runstate == CTDB_RUNSTATE_SHUTDOWN) {
- DEBUG(DEBUG_NOTICE,("Already shutting down so will not proceed.\n"));
+ D_NOTICE("Already shutting down so will not proceed.\n");
return;
}
- DEBUG(DEBUG_ERR,("Shutdown sequence commencing.\n"));
+ D_ERR("Shutdown sequence commencing.\n");
ctdb_set_runstate(ctdb, CTDB_RUNSTATE_SHUTDOWN);
+ ctdb_shutdown_takeover(ctdb);
ctdb_stop_recoverd(ctdb);
ctdb_stop_keepalive(ctdb);
ctdb_stop_monitoring(ctdb);
@@ -2237,7 +2460,7 @@
ctdb->methods->shutdown(ctdb);
}
- DEBUG(DEBUG_ERR,("Shutdown sequence complete, exiting.\n"));
+ D_ERR("Shutdown sequence complete, exiting.\n");
exit(exit_code);
}
diff -Nru samba-4.22.1+dfsg/ctdb/server/ctdb_monitor.c samba-4.22.2+dfsg/ctdb/server/ctdb_monitor.c
--- samba-4.22.1+dfsg/ctdb/server/ctdb_monitor.c 2025-02-06 13:31:53.732143900 +0300
+++ samba-4.22.2+dfsg/ctdb/server/ctdb_monitor.c 2025-06-05 18:38:33.742581000 +0300
@@ -217,6 +217,11 @@
*/
static void ctdb_startup_callback(struct ctdb_context *ctdb, int status, void *p)
{
+ if (ctdb->runstate == CTDB_RUNSTATE_SHUTDOWN) {
+ DBG_WARNING("Detected early shutdown, not starting monitoring\n");
+ return;
+ }
+
if (status != 0) {
DEBUG(DEBUG_ERR,("startup event failed\n"));
tevent_add_timer(ctdb->ev, ctdb->monitor->monitor_context,
@@ -249,6 +254,12 @@
struct ctdb_context);
int ret;
+ if (ctdb->runstate == CTDB_RUNSTATE_SHUTDOWN) {
+ DBG_WARNING(
+ "Detected early shutdown, not running startup event\n");
+ return;
+ }
+
/* This is necessary to avoid the "startup" event colliding
* with the "ipreallocated" event from the takeover run
* following the first recovery. We might as well serialise
@@ -432,6 +443,13 @@
*/
void ctdb_wait_for_first_recovery(struct ctdb_context *ctdb)
{
+ if (ctdb->runstate == CTDB_RUNSTATE_SHUTDOWN) {
+ DBG_WARNING(
+ "Detected early shutdown, "
+ "not waiting for first recovery\n");
+ return;
+ }
+
ctdb_set_runstate(ctdb, CTDB_RUNSTATE_FIRST_RECOVERY);
ctdb->monitor = talloc(ctdb, struct ctdb_monitor_state);
diff -Nru samba-4.22.1+dfsg/ctdb/server/ctdb_takeover.c samba-4.22.2+dfsg/ctdb/server/ctdb_takeover.c
--- samba-4.22.1+dfsg/ctdb/server/ctdb_takeover.c 2025-02-06 13:31:53.732143900 +0300
+++ samba-4.22.2+dfsg/ctdb/server/ctdb_takeover.c 2025-06-05 18:38:33.746580800 +0300
@@ -2519,8 +2519,9 @@
struct start_ipreallocate_callback_state *state;
/* Nodes that are not RUNNING can not host IPs */
- if (ctdb->runstate != CTDB_RUNSTATE_RUNNING) {
- DBG_INFO("Skipping \"startipreallocate\" event, not RUNNING\n");
+ if (ctdb->runstate < CTDB_RUNSTATE_RUNNING) {
+ DBG_INFO("Skipping \"startipreallocate\" event, "
+ "not RUNNING/SHUTDOWN\n");
return 0;
}
diff -Nru samba-4.22.1+dfsg/ctdb/tests/UNIT/cunit/config_test_001.sh samba-4.22.2+dfsg/ctdb/tests/UNIT/cunit/config_test_001.sh
--- samba-4.22.1+dfsg/ctdb/tests/UNIT/cunit/config_test_001.sh 2025-02-06 13:31:53.744144000 +0300
+++ samba-4.22.2+dfsg/ctdb/tests/UNIT/cunit/config_test_001.sh 2025-06-05 18:38:33.746580800 +0300
@@ -49,6 +49,8 @@
# debug script =
[failover]
# disabled = false
+ # shutdown extra timeout = 0
+ # shutdown failover timeout = 10
[legacy]
# realtime scheduling = true
# lmaster capability = true
diff -Nru samba-4.22.1+dfsg/lib/krb5_wrap/krb5_samba.c samba-4.22.2+dfsg/lib/krb5_wrap/krb5_samba.c
--- samba-4.22.1+dfsg/lib/krb5_wrap/krb5_samba.c 2025-04-17 20:12:25.646450300 +0300
+++ samba-4.22.2+dfsg/lib/krb5_wrap/krb5_samba.c 2025-06-05 18:38:33.750581000 +0300
@@ -869,16 +869,17 @@
TALLOC_FREE(frame);
return ENOMEM;
}
- TALLOC_FREE(frame);
ret = krb5_parse_name_flags(context, utf8_name, flags, principal);
if (ret != KRB5_PARSE_MALFORMED) {
+ TALLOC_FREE(frame);
return ret;
}
flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
ret = krb5_parse_name_flags(context, utf8_name, flags, principal);
+ TALLOC_FREE(frame);
return ret;
}
diff -Nru samba-4.22.1+dfsg/selftest/knownfail samba-4.22.2+dfsg/selftest/knownfail
--- samba-4.22.1+dfsg/selftest/knownfail 2025-02-06 13:31:54.380147700 +0300
+++ samba-4.22.2+dfsg/selftest/knownfail 2025-06-05 18:38:33.750581000 +0300
@@ -204,11 +204,8 @@
^samba4.smb2.ioctl-on-stream.ioctl-on-stream\(ad_dc_ntvfs\)
^samba3.smb2.dir.one
^samba3.smb2.dir.modify
-^samba3.smb2.oplock.batch20
^samba3.smb2.oplock.stream1
-^samba3.smb2.streams.rename
^samba3.smb2.streams.rename2
-^samba3.smb2.streams streams_xattr.rename\(nt4_dc\)
^samba3.smb2.streams streams_xattr.rename2\(nt4_dc\)
^samba3.smb2.getinfo.complex
^samba3.smb2.getinfo.fsinfo # quotas don't work yet
diff -Nru samba-4.22.1+dfsg/source3/modules/vfs_ceph_snapshots.c samba-4.22.2+dfsg/source3/modules/vfs_ceph_snapshots.c
--- samba-4.22.1+dfsg/source3/modules/vfs_ceph_snapshots.c 2025-02-06 13:31:54.480148300 +0300
+++ samba-4.22.2+dfsg/source3/modules/vfs_ceph_snapshots.c 2025-06-05 18:38:33.750581000 +0300
@@ -531,10 +531,13 @@
* Temporally use the caller's return buffer for this.
*/
if (strlen(name) == 0) {
- ret = strlcpy(_converted_buf, snapdir, buflen);
+ ret = snprintf(_converted_buf, buflen, "%s/%s",
+ handle->conn->connectpath, snapdir);
} else {
- ret = snprintf(_converted_buf, buflen, "%s/%s", name, snapdir);
+ ret = snprintf(_converted_buf, buflen, "%s/%s/%s",
+ handle->conn->connectpath, name, snapdir);
}
+
if (ret >= buflen) {
ret = -EINVAL;
goto err_out;
@@ -940,21 +943,11 @@
{
time_t timestamp = 0;
struct smb_filename *smb_fname = NULL;
- char stripped[PATH_MAX + 1];
char conv[PATH_MAX + 1];
int ret;
int saved_errno = 0;
- ret = ceph_snap_gmt_strip_snapshot(handle,
- smb_fname_in,
- ×tamp,
- stripped,
- sizeof(stripped));
- if (ret < 0) {
- errno = -ret;
- return -1;
- }
- if (timestamp == 0) {
+ if (smb_fname_in->twrp == 0) {
return SMB_VFS_NEXT_OPENAT(handle,
dirfsp,
smb_fname_in,
@@ -962,8 +955,18 @@
how);
}
+ timestamp = nt_time_to_unix(smb_fname_in->twrp);
+
+ smb_fname = full_path_from_dirfsp_atname(talloc_tos(),
+ dirfsp,
+ smb_fname_in);
+ if (smb_fname == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
ret = ceph_snap_gmt_convert(handle,
- stripped,
+ smb_fname->base_name,
timestamp,
conv,
sizeof(conv));
@@ -971,10 +974,7 @@
errno = -ret;
return -1;
}
- smb_fname = cp_smb_filename(talloc_tos(), smb_fname_in);
- if (smb_fname == NULL) {
- return -1;
- }
+
smb_fname->base_name = conv;
ret = SMB_VFS_NEXT_OPENAT(handle,
diff -Nru samba-4.22.1+dfsg/source3/rpc_server/rpc_sock_helper.c samba-4.22.2+dfsg/source3/rpc_server/rpc_sock_helper.c
--- samba-4.22.1+dfsg/source3/rpc_server/rpc_sock_helper.c 2025-02-06 13:31:54.528148700 +0300
+++ samba-4.22.2+dfsg/source3/rpc_server/rpc_sock_helper.c 2025-06-05 18:38:33.750581000 +0300
@@ -110,32 +110,16 @@
********************************************************************/
static NTSTATUS dcesrv_create_ncacn_ip_tcp_socket(
- const struct sockaddr_storage *ifss, uint16_t *port, int *out_fd)
+ const struct sockaddr_storage *ifss,
+ uint16_t port,
+ bool rebind,
+ int *out_fd)
{
int fd = -1;
- if (*port == 0) {
- static uint16_t low = 0;
- uint16_t i;
-
- if (low == 0) {
- low = lp_rpc_low_port();
- }
-
- for (i = low; i <= lp_rpc_high_port(); i++) {
- fd = open_socket_in(SOCK_STREAM, ifss, i, false);
- if (fd >= 0) {
- *port = i;
- low = i+1;
- break;
- }
- }
- } else {
- fd = open_socket_in(SOCK_STREAM, ifss, *port, true);
- }
-
+ fd = open_socket_in(SOCK_STREAM, ifss, port, rebind);
if (fd < 0) {
- DBG_ERR("Failed to create socket on port %u!\n", *port);
+ DBG_ERR("Failed to create socket on port %u!\n", port);
return map_nt_error_from_unix(-fd);
}
@@ -143,7 +127,7 @@
set_socket_options(fd, "SO_KEEPALIVE");
set_socket_options(fd, lp_socket_options());
- DBG_DEBUG("Opened ncacn_ip_tcp socket fd %d for port %u\n", fd, *port);
+ DBG_DEBUG("Opened ncacn_ip_tcp socket fd %d for port %u\n", fd, port);
*out_fd = fd;
@@ -156,15 +140,24 @@
size_t *pnum_fds,
int **pfds)
{
+ static uint16_t next_low_port;
+ static uint16_t conf_high_port;
uint16_t port = 0;
+ uint16_t highest_port = 0;
char port_str[11];
const char *endpoint = NULL;
size_t i = 0, num_fds;
int *fds = NULL;
struct samba_sockaddr *addrs = NULL;
NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+ bool rebind = false;
bool ok;
+ if (next_low_port == 0) {
+ next_low_port = lp_rpc_low_port();
+ conf_high_port = lp_rpc_high_port();
+ }
+
endpoint = dcerpc_binding_get_string_option(b, "endpoint");
if (endpoint != NULL) {
port = atoi(endpoint);
@@ -181,15 +174,21 @@
addrs = talloc_array(mem_ctx, struct samba_sockaddr, num_fds);
if (addrs == NULL) {
+ num_fds = 0; /* nothing to close */
status = NT_STATUS_NO_MEMORY;
goto fail;
}
fds = talloc_array(mem_ctx, int, num_fds);
if (fds == NULL) {
+ num_fds = 0; /* nothing to close */
status = NT_STATUS_NO_MEMORY;
goto fail;
}
+ for (i=0; i<num_fds; i++) {
+ fds[i] = -1;
+ }
+
/*
* Fill "addrs"
*/
@@ -202,7 +201,6 @@
ok = sockaddr_storage_to_samba_sockaddr(
&addrs[i], ifss);
if (!ok) {
- i = 0; /* nothing to close */
goto fail;
}
}
@@ -234,13 +232,60 @@
}
}
- for (i=0; i<num_fds; i++) {
- status = dcesrv_create_ncacn_ip_tcp_socket(
- &addrs[i].u.ss, &port, &fds[i]);
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
+ if (port != 0) {
+ rebind = true;
+ highest_port = port;
+ } else {
+ rebind = false;
+ port = next_low_port;
+ highest_port = conf_high_port;
+ }
+
+ for (; port <= highest_port; port += 1) {
+ for (i=0; i<num_fds; i++) {
+ status = dcesrv_create_ncacn_ip_tcp_socket(
+ &addrs[i].u.ss,
+ port,
+ rebind,
+ &fds[i]);
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_ADDRESS_ALREADY_ASSOCIATED))
+ {
+ break;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ samba_sockaddr_set_port(&addrs[i], port);
+ }
+
+ if (port == next_low_port) {
+ next_low_port += 1;
+ }
+
+ if (i == num_fds) {
+ /*
+ * We were able to bind to the same port on all
+ * addresses
+ */
+ break;
+ }
+
+ /*
+ * The port was not available on at least one address, so close
+ * them all and try the next port or return the error.
+ */
+ for (i=0; i<num_fds; i++) {
+ if (fds[i] == -1) {
+ continue;
+ }
+ close(fds[i]);
+ fds[i] = -1;
}
- samba_sockaddr_set_port(&addrs[i], port);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
}
/* Set the port in the endpoint */
@@ -261,9 +306,12 @@
return NT_STATUS_OK;
fail:
- while (i > 0) {
- close(fds[i-1]);
- i -= 1;
+ for (i=0; i<num_fds; i++) {
+ if (fds[i] == -1) {
+ continue;
+ }
+ close(fds[i]);
+ fds[i] = -1;
}
TALLOC_FREE(fds);
TALLOC_FREE(addrs);
diff -Nru samba-4.22.1+dfsg/source3/selftest/tests.py samba-4.22.2+dfsg/source3/selftest/tests.py
--- samba-4.22.1+dfsg/source3/selftest/tests.py 2025-02-06 13:31:54.556148800 +0300
+++ samba-4.22.2+dfsg/source3/selftest/tests.py 2025-06-05 18:38:33.758581000 +0300
@@ -1363,7 +1363,6 @@
plansmbtorture4testsuite(t, "ad_dc_smb1", '//$SERVER/tmp -U$USERNAME%$PASSWORD --option=torture:wksname=samba3rpctest')
elif t == "smb2.streams":
plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
- plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/streams_xattr -U$USERNAME%$PASSWORD', 'streams_xattr')
elif t == "smb2.stream-inherit-perms":
plansmbtorture4testsuite(t, "fileserver", '//$SERVER/inherit_perms -U$USERNAME%$PASSWORD')
diff -Nru samba-4.22.1+dfsg/source3/smbd/close.c samba-4.22.2+dfsg/source3/smbd/close.c
--- samba-4.22.1+dfsg/source3/smbd/close.c 2025-04-17 20:12:25.670450400 +0300
+++ samba-4.22.2+dfsg/source3/smbd/close.c 2025-06-05 18:38:33.762581000 +0300
@@ -278,6 +278,47 @@
return state.found_another;
}
+struct has_delete_access_opens_state {
+ bool delete_access;
+ bool delete_pending;
+};
+
+static bool has_delete_access_opens_fn(
+ struct share_mode_entry *e,
+ bool *modified,
+ void *private_data)
+{
+ struct has_delete_access_opens_state *state = private_data;
+
+ if (share_entry_stale_pid(e)) {
+ return false;
+ }
+
+ if (e->access_mask & SEC_STD_DELETE) {
+ state->delete_access = true;
+ }
+ return false;
+}
+
+bool has_delete_opens(struct files_struct *fsp,
+ struct share_mode_lock *lck,
+ bool *delete_access,
+ bool *delete_pending)
+{
+ struct has_delete_access_opens_state state = {};
+ bool ok;
+
+ ok = share_mode_forall_entries(
+ lck, has_delete_access_opens_fn, &state);
+ if (!ok) {
+ return false;
+ }
+
+ *delete_access = state.delete_access;
+ *delete_pending = is_delete_on_close_set(lck, fsp->name_hash);
+ return true;
+}
+
bool has_nonposix_opens(struct share_mode_lock *lck)
{
struct has_other_nonposix_opens_state state = {};
diff -Nru samba-4.22.1+dfsg/source3/smbd/conn.c samba-4.22.2+dfsg/source3/smbd/conn.c
--- samba-4.22.1+dfsg/source3/smbd/conn.c 2025-02-06 13:31:54.556148800 +0300
+++ samba-4.22.2+dfsg/source3/smbd/conn.c 2025-06-05 18:38:33.762581000 +0300
@@ -173,8 +173,8 @@
for (i=0; i<VUID_CACHE_SIZE; i++) {
ent = &conn->vuid_cache->array[i];
- if (ent->vuid != vuid) {
- continue;
+ if (ent->vuid == vuid) {
+ break;
}
}
if (i == VUID_CACHE_SIZE) {
diff -Nru samba-4.22.1+dfsg/source3/smbd/open.c samba-4.22.2+dfsg/source3/smbd/open.c
--- samba-4.22.1+dfsg/source3/smbd/open.c 2025-04-17 20:12:25.674450400 +0300
+++ samba-4.22.2+dfsg/source3/smbd/open.c 2025-06-05 18:38:33.766581000 +0300
@@ -346,7 +346,6 @@
NTSTATUS status;
struct security_descriptor *parent_sd = NULL;
uint32_t access_granted = 0;
- uint32_t name_hash;
bool delete_on_close_set;
TALLOC_CTX *frame = talloc_stackframe();
@@ -406,15 +405,7 @@
goto out;
}
- /* Check if the directory has delete-on-close set */
- status = file_name_hash(fsp->conn,
- fsp->fsp_name->base_name,
- &name_hash);
- if (!NT_STATUS_IS_OK(status)) {
- goto out;
- }
-
- get_file_infos(fsp->file_id, name_hash, &delete_on_close_set, NULL);
+ get_file_infos(fsp->file_id, fsp->name_hash, &delete_on_close_set, NULL);
if (delete_on_close_set) {
status = NT_STATUS_DELETE_PENDING;
goto out;
diff -Nru samba-4.22.1+dfsg/source3/smbd/proto.h samba-4.22.2+dfsg/source3/smbd/proto.h
--- samba-4.22.1+dfsg/source3/smbd/proto.h 2025-04-17 20:12:25.674450400 +0300
+++ samba-4.22.2+dfsg/source3/smbd/proto.h 2025-06-05 18:38:33.770581000 +0300
@@ -145,6 +145,10 @@
bool has_other_nonposix_opens(struct share_mode_lock *lck,
struct files_struct *fsp);
bool has_nonposix_opens(struct share_mode_lock *lck);
+bool has_delete_opens(struct files_struct *fsp,
+ struct share_mode_lock *lck,
+ bool *delete_access,
+ bool *delete_pending);
/* The following definitions come from smbd/conn.c */
@@ -1243,6 +1247,7 @@
struct tevent_context *ev,
struct timeval timeout,
struct files_struct *fsp,
+ uint32_t access_mask,
bool recursive,
struct share_mode_lock **lck);
diff -Nru samba-4.22.1+dfsg/source3/smbd/smb2_close.c samba-4.22.2+dfsg/source3/smbd/smb2_close.c
--- samba-4.22.1+dfsg/source3/smbd/smb2_close.c 2025-02-06 13:31:54.576149000 +0300
+++ samba-4.22.2+dfsg/source3/smbd/smb2_close.c 2025-06-05 18:38:33.770581000 +0300
@@ -369,6 +369,7 @@
ev,
timeout,
in_fsp,
+ SEC_RIGHTS_DIR_ALL,
false,
&lck);
if (tevent_req_nomem(subreq, req)) {
diff -Nru samba-4.22.1+dfsg/source3/smbd/smb2_oplock.c samba-4.22.2+dfsg/source3/smbd/smb2_oplock.c
--- samba-4.22.1+dfsg/source3/smbd/smb2_oplock.c 2025-02-06 13:31:54.580149000 +0300
+++ samba-4.22.2+dfsg/source3/smbd/smb2_oplock.c 2025-06-05 18:38:33.770581000 +0300
@@ -1336,8 +1336,13 @@
ret = SMB_VFS_STAT(conn, parent_fname);
if (ret != 0) {
- DBG_ERR("Failed to stat [%s]: %s\n",
- smb_fname_str_dbg(parent_fname), strerror(errno));
+ DBG_ERR("Trigger [conn: %s] [smb_fname: %s] cwd [%s], "
+ "failed to stat parent [%s]: %s\n",
+ conn->connectpath,
+ smb_fname_str_dbg(smb_fname),
+ get_current_dir_name(),
+ smb_fname_str_dbg(parent_fname),
+ strerror(errno));
TALLOC_FREE(parent_fname);
return;
}
@@ -1511,6 +1516,7 @@
TALLOC_CTX *mem_ctx;
struct tevent_context *ev;
struct timeval timeout;
+ uint32_t access_mask;
bool recursive;
bool recursive_h_leases_break;
struct files_struct *fsp;
@@ -1541,6 +1547,7 @@
struct tevent_context *ev,
struct timeval timeout,
struct files_struct *fsp,
+ uint32_t access_mask,
bool recursive,
struct share_mode_lock **lck)
{
@@ -1559,6 +1566,7 @@
.mem_ctx = mem_ctx,
.ev = ev,
.timeout = timeout,
+ .access_mask = access_mask,
.recursive = recursive,
.recursive_h_leases_break = recursive,
.fsp = fsp,
@@ -1599,6 +1607,10 @@
}
}
+ if ((state->access_mask & e->access_mask) == 0) {
+ return false;
+ }
+
lease_type = get_lease_type(e, fsp->file_id);
if ((lease_type & SMB2_LEASE_HANDLE) == 0) {
return false;
@@ -1634,6 +1646,12 @@
DBG_DEBUG("fsp [%s]\n", fsp_str_dbg(state->fsp));
+ if (state->lck == NULL) {
+ DBG_DEBUG("fsp [%s] all opens are gone\n",
+ fsp_str_dbg(state->fsp));
+ return;
+ }
+
ok = share_mode_forall_leases(state->lck,
delay_for_handle_lease_break_fn,
state);
@@ -1691,11 +1709,6 @@
}
state->lck = get_existing_share_mode_lock(state, state->fsp->file_id);
- if (state->lck == NULL) {
- tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
- return;
- }
-
/*
* This could potentially end up looping for some if a client
* aggressively reaquires H-leases on the file, but we have a
@@ -1959,11 +1972,6 @@
state->recursive_h_leases_break = false;
state->lck = get_existing_share_mode_lock(state, state->fsp->file_id);
- if (state->lck == NULL) {
- tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
- return;
- }
-
delay_for_handle_lease_break_check(req);
}
diff -Nru samba-4.22.1+dfsg/source3/smbd/smb2_reply.c samba-4.22.2+dfsg/source3/smbd/smb2_reply.c
--- samba-4.22.1+dfsg/source3/smbd/smb2_reply.c 2025-04-17 20:12:25.674450400 +0300
+++ samba-4.22.2+dfsg/source3/smbd/smb2_reply.c 2025-06-05 18:38:33.774581200 +0300
@@ -1344,49 +1344,6 @@
}
/****************************************************************************
- Returns an error if the parent directory for a filename is open in an
- incompatible way.
-****************************************************************************/
-
-static NTSTATUS parent_dirname_compatible_open(connection_struct *conn,
- const struct smb_filename *smb_fname_dst_in)
-{
- struct smb_filename *smb_fname_parent = NULL;
- struct file_id id;
- files_struct *fsp = NULL;
- int ret;
- NTSTATUS status;
-
- status = SMB_VFS_PARENT_PATHNAME(conn,
- talloc_tos(),
- smb_fname_dst_in,
- &smb_fname_parent,
- NULL);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- ret = vfs_stat(conn, smb_fname_parent);
- if (ret == -1) {
- return map_nt_error_from_unix(errno);
- }
-
- /*
- * We're only checking on this smbd here, mostly good
- * enough.. and will pass tests.
- */
-
- id = vfs_file_id_from_sbuf(conn, &smb_fname_parent->st);
- for (fsp = file_find_di_first(conn->sconn, id, true); fsp;
- fsp = file_find_di_next(fsp, true)) {
- if (fsp->access_mask & DELETE_ACCESS) {
- return NT_STATUS_SHARING_VIOLATION;
- }
- }
- return NT_STATUS_OK;
-}
-
-/****************************************************************************
Rename an open file - given an fsp.
****************************************************************************/
@@ -1414,11 +1371,6 @@
true : conn->case_preserve;
struct vfs_rename_how rhow = { .flags = 0, };
- status = parent_dirname_compatible_open(conn, smb_fname_dst_in);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
if (file_has_open_streams(fsp)) {
return NT_STATUS_ACCESS_DENIED;
}
diff -Nru samba-4.22.1+dfsg/source3/smbd/smb2_setinfo.c samba-4.22.2+dfsg/source3/smbd/smb2_setinfo.c
--- samba-4.22.1+dfsg/source3/smbd/smb2_setinfo.c 2025-02-06 13:31:54.580149000 +0300
+++ samba-4.22.2+dfsg/source3/smbd/smb2_setinfo.c 2025-06-05 18:38:33.774581200 +0300
@@ -175,10 +175,12 @@
struct files_struct *fsp;
struct share_mode_lock *lck;
struct files_struct *dstfsp;
+ struct files_struct *dst_parent_dirfsp;
uint16_t file_info_level;
DATA_BLOB data;
bool delay;
bool rename_dst_check_done;
+ bool rename_dst_parent_check_done;
};
static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req);
@@ -394,6 +396,9 @@
static void smbd_smb2_setinfo_lease_break_fsp_done(struct tevent_req *subreq);
static void smbd_smb2_setinfo_rename_dst_check(struct tevent_req *req);
static void smbd_smb2_setinfo_rename_dst_delay_done(struct tevent_req *subreq);
+static void smbd_smb2_setinfo_rename_dst_parent_check(struct tevent_req *req);
+static void smbd_smb2_setinfo_rename_dst_parent_delay_done(
+ struct tevent_req *subreq);
static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req)
{
@@ -413,6 +418,16 @@
return;
}
+ smbd_smb2_setinfo_rename_dst_parent_check(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return;
+ }
+ if (state->delay) {
+ DBG_DEBUG("Waiting for h-lease breaks on rename destination "
+ "parent directory\n");
+ return;
+ }
+
smbd_smb2_setinfo_lease_break_fsp_check(req);
if (!tevent_req_is_in_progress(req)) {
return;
@@ -489,6 +504,7 @@
state->ev,
timeout,
fsp,
+ SEC_RIGHTS_DIR_ALL,
rename,
&state->lck);
if (tevent_req_nomem(subreq, req)) {
@@ -656,6 +672,7 @@
state->ev,
timeout,
state->dstfsp,
+ SEC_RIGHTS_DIR_ALL,
false,
&state->lck);
if (subreq == NULL) {
@@ -765,6 +782,169 @@
smbd_smb2_setinfo_lease_break_check(req);
}
+static void smbd_smb2_setinfo_rename_dst_parent_check(struct tevent_req *req)
+{
+ struct smbd_smb2_setinfo_state *state = tevent_req_data(
+ req, struct smbd_smb2_setinfo_state);
+ struct tevent_req *subreq = NULL;
+ struct timeval timeout;
+ bool overwrite = false;
+ struct files_struct *fsp = state->fsp;
+ struct files_struct *dst_parent_dirfsp = NULL;
+ struct smb_filename *smb_fname_dst = NULL;
+ char *dst_original_lcomp = NULL;
+ NTSTATUS status;
+
+ if (state->rename_dst_parent_check_done) {
+ return;
+ }
+ state->rename_dst_parent_check_done = true;
+
+ if (state->file_info_level != SMB2_FILE_RENAME_INFORMATION_INTERNAL) {
+ return;
+ }
+ if (is_named_stream(fsp->fsp_name)) {
+ return;
+ }
+ status = smb2_parse_file_rename_information(state,
+ fsp->conn,
+ state->smb2req->smb1req,
+ (char *)state->data.data,
+ state->data.length,
+ fsp,
+ fsp->fsp_name,
+ &overwrite,
+ &dst_parent_dirfsp,
+ &smb_fname_dst,
+ &dst_original_lcomp);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ SMB_ASSERT(dst_parent_dirfsp != NULL);
+ state->dst_parent_dirfsp = dst_parent_dirfsp;
+
+ state->lck = get_existing_share_mode_lock(state,
+ dst_parent_dirfsp->file_id);
+ if (state->lck == NULL) {
+ /* No opens around */
+ return;
+ }
+
+ timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT, 0);
+ timeout = timeval_sum(&state->smb2req->request_time, &timeout);
+
+ subreq = delay_for_handle_lease_break_send(state,
+ state->ev,
+ timeout,
+ dst_parent_dirfsp,
+ SEC_STD_DELETE,
+ false,
+ &state->lck);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ if (tevent_req_is_in_progress(subreq)) {
+ state->delay = true;
+ tevent_req_set_callback(
+ subreq,
+ smbd_smb2_setinfo_rename_dst_parent_delay_done,
+ req);
+ return;
+ }
+
+ status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->lck != NULL) {
+ bool delete_open;
+ bool detete_pending;
+ bool ok;
+
+ ok = has_delete_opens(dst_parent_dirfsp,
+ state->lck,
+ &delete_open,
+ &detete_pending);
+ TALLOC_FREE(state->lck);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (detete_pending) {
+ tevent_req_nterror(req, NT_STATUS_DELETE_PENDING);
+ return;
+ }
+ if (delete_open) {
+ tevent_req_nterror(req, NT_STATUS_SHARING_VIOLATION);
+ return;
+ }
+ }
+
+ return;
+}
+
+static void smbd_smb2_setinfo_rename_dst_parent_delay_done(
+ struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbd_smb2_setinfo_state *state = tevent_req_data(
+ req, struct smbd_smb2_setinfo_state);
+ struct smbXsrv_session *session = state->smb2req->session;
+ NTSTATUS status;
+ bool ok;
+
+ status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /*
+ * Make sure we run as the user again
+ */
+ ok = change_to_user_and_service(state->fsp->conn,
+ session->global->session_wire_id);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
+ if (state->lck != NULL) {
+ bool delete_open;
+ bool detete_pending;
+
+ ok = has_delete_opens(state->dst_parent_dirfsp,
+ state->lck,
+ &delete_open,
+ &detete_pending);
+ TALLOC_FREE(state->lck);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (detete_pending) {
+ tevent_req_nterror(req, NT_STATUS_DELETE_PENDING);
+ return;
+ }
+ if (delete_open) {
+ tevent_req_nterror(req, NT_STATUS_SHARING_VIOLATION);
+ return;
+ }
+ }
+
+ /*
+ * We've finished breaking H-lease on the rename destination, now
+ * trigger the fsp check.
+ */
+ state->rename_dst_parent_check_done = true;
+ smbd_smb2_setinfo_lease_break_check(req);
+}
+
static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req)
{
NTSTATUS status;
diff -Nru samba-4.22.1+dfsg/source3/smbd/smbXsrv_session.c samba-4.22.2+dfsg/source3/smbd/smbXsrv_session.c
--- samba-4.22.1+dfsg/source3/smbd/smbXsrv_session.c 2025-02-06 13:31:54.584149000 +0300
+++ samba-4.22.2+dfsg/source3/smbd/smbXsrv_session.c 2025-06-05 18:38:33.774581200 +0300
@@ -2618,6 +2618,7 @@
state->ev,
timeout,
fsp,
+ SEC_RIGHTS_DIR_ALL,
false,
&lck);
if (tevent_req_nomem(subreq, state->req)) {
diff -Nru samba-4.22.1+dfsg/source4/selftest/tests.py samba-4.22.2+dfsg/source4/selftest/tests.py
--- samba-4.22.1+dfsg/source4/selftest/tests.py 2025-02-06 13:31:55.500154300 +0300
+++ samba-4.22.2+dfsg/source4/selftest/tests.py 2025-06-05 18:38:33.778581100 +0300
@@ -443,6 +443,7 @@
"smb2.twrp",
"smb2.ea",
"smb2.create_no_streams",
+ "smb2.streams",
]
smb2 = [x for x in smbtorture4_testsuites("smb2.") if x not in smb2_s3only]
diff -Nru samba-4.22.1+dfsg/source4/torture/smb2/lease.c samba-4.22.2+dfsg/source4/torture/smb2/lease.c
--- samba-4.22.1+dfsg/source4/torture/smb2/lease.c 2025-04-17 20:12:25.678450600 +0300
+++ samba-4.22.2+dfsg/source4/torture/smb2/lease.c 2025-06-05 18:38:33.782581000 +0300
@@ -6685,6 +6685,115 @@
return ret;
}
+static bool test_rename_dst_parent(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ const char *srcdir = "test_rename_dst_parent_src";
+ const char *srcfile = "test_rename_dst_parent_src\\file";
+ const char *dstdir = "test_rename_dst_parent_dst";
+ const char *dstfile = "test_rename_dst_parent_dst\\file";
+ struct smb2_create create_dp;
+ struct smb2_lease lease_dp;
+ struct smb2_handle h1 = {};
+ struct smb2_handle handle_sf = {};
+ struct smb2_handle handle_dp = {};
+ union smb_setfileinfo sfinfo = {};
+ NTSTATUS status;
+ bool ret = true;
+
+ tree->session->transport->lease.handler = torture_lease_handler;
+ tree->session->transport->lease.private_data = tree;
+ torture_reset_lease_break_info(tctx, &lease_break_info);
+
+ status = torture_smb2_testdir(tree, srcdir, &h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+
+ status = torture_smb2_testdir(tree, dstdir, &h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+
+ status = smb2_create_simple_file(tctx, tree, srcfile, &handle_sf);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create_simple_file\n");
+
+ /* Get a RH lease on the destination parent */
+
+ smb2_lease_v2_create_share(&create_dp, &lease_dp, true, dstdir,
+ smb2_util_share_access("RWD"),
+ 0x01, NULL,
+ smb2_util_lease_state("RH"), 0);
+ create_dp.in.desired_access = DELETE_ACCESS;
+ status = smb2_create(tree, mem_ctx, &create_dp);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create failed\n");
+ handle_dp = create_dp.out.file.handle;
+ CHECK_LEASE_V2(&create_dp, "RH", true, 0x01, 0, 0, 1);
+
+ /*
+ * Rename, expect break on dst parent. As we'll be keeping our
+ * conflicting open, the rename should fail.
+ */
+
+ sfinfo.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfinfo.generic.in.file.handle = handle_sf;
+ sfinfo.rename_information.in.new_name = dstfile;
+
+ status = smb2_setinfo_file(tree, &sfinfo);
+ torture_assert_ntstatus_equal_goto(
+ tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done,
+ "smb2_setinfo_file\n");
+
+ CHECK_BREAK_INFO_V2(tree->session->transport,
+ "RH", "R", 0x01, 2);
+
+ /* Upgrade lease to "RH" */
+
+ smb2_lease_v2_create_share(&create_dp, &lease_dp, true, dstdir,
+ smb2_util_share_access("RWD"),
+ 0x01, NULL,
+ smb2_util_lease_state("RH"), 0);
+ create_dp.in.desired_access = DELETE_ACCESS;
+ status = smb2_create(tree, mem_ctx, &create_dp);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create failed\n");
+ h1 = create_dp.out.file.handle;
+ smb2_util_close(tree, h1);
+ CHECK_LEASE_V2(&create_dp, "RH", true, 0x01, 0, 0, 3);
+
+ /*
+ * Rename, expect break on dst parent. Let the break
+ * handler close the handle so the rename should pass.
+ */
+
+ torture_reset_lease_break_info(tctx, &lease_break_info);
+ lease_break_info.lease_handle = handle_dp;
+
+ sfinfo.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfinfo.generic.in.file.handle = handle_sf;
+ sfinfo.rename_information.in.new_name = dstfile;
+
+ status = smb2_setinfo_file(tree, &sfinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_setinfo_file failed\n");
+
+ CHECK_LEASE_BREAK(&lease_break_info.lease_break, "RH", "R", 0x01);
+
+done:
+ if (!smb2_util_handle_empty(handle_sf)) {
+ smb2_util_close(tree, handle_sf);
+ }
+ if (!smb2_util_handle_empty(handle_dp)) {
+ smb2_util_close(tree, handle_dp);
+ }
+ smb2_deltree(tree, srcdir);
+ smb2_deltree(tree, dstdir);
+ return ret;
+}
+
static bool test_overwrite(struct torture_context *tctx,
struct smb2_tree *tree,
struct smb2_tree *tree2)
@@ -7624,6 +7733,7 @@
torture_suite_add_2smb2_test(suite, "setctime", test_dirlease_setctime);
torture_suite_add_2smb2_test(suite, "setatime", test_dirlease_setatime);
torture_suite_add_1smb2_test(suite, "rename", test_rename);
+ torture_suite_add_1smb2_test(suite, "rename_dst_parent", test_rename_dst_parent);
torture_suite_add_2smb2_test(suite, "overwrite", test_overwrite);
torture_suite_add_1smb2_test(suite, "hardlink", test_hardlink);
torture_suite_add_1smb2_test(suite, "unlink_same_set_and_close", test_unlink_same_set_and_close);
diff -Nru samba-4.22.1+dfsg/source4/torture/smb2/lease_break_handler.c samba-4.22.2+dfsg/source4/torture/smb2/lease_break_handler.c
--- samba-4.22.1+dfsg/source4/torture/smb2/lease_break_handler.c 2025-02-06 13:31:55.632155200 +0300
+++ samba-4.22.2+dfsg/source4/torture/smb2/lease_break_handler.c 2025-06-05 18:38:33.782581000 +0300
@@ -31,7 +31,7 @@
struct lease_break_info lease_break_info;
-void torture_lease_break_callback(struct smb2_request *req)
+static void torture_lease_break_callback(struct smb2_request *req)
{
NTSTATUS status;
@@ -42,6 +42,17 @@
return;
}
+static void torture_lease_break_close_callback(struct smb2_request *req)
+{
+ NTSTATUS status;
+
+ status = smb2_close_recv(req, &lease_break_info.close);
+ if (!NT_STATUS_IS_OK(status)) {
+ lease_break_info.failures++;
+ }
+ return;
+}
+
/* a lease break request handler */
bool torture_lease_handler(struct smb2_transport *transport,
const struct smb2_lease_break *lb,
@@ -64,6 +75,25 @@
lease_break_info.lease_break = *lb;
lease_break_info.count++;
+ if (!smb2_util_handle_empty(lease_break_info.lease_handle) &&
+ (lb->current_lease.lease_state & SMB2_LEASE_HANDLE) &&
+ !(lb->new_lease_state & SMB2_LEASE_HANDLE))
+ {
+ torture_comment(lease_break_info.tctx,
+ "transport[%p] closing handle\n",
+ transport);
+
+ ZERO_STRUCT(lease_break_info.close);
+ lease_break_info.close.in.file.handle =
+ lease_break_info.lease_handle;
+ ZERO_STRUCT(lease_break_info.lease_handle);
+
+ req = smb2_close_send(tree, &lease_break_info.close);
+ req->async.fn = torture_lease_break_close_callback;
+ req->async.private_data = NULL;
+ return true;
+ }
+
if (lease_break_info.lease_skip_ack) {
torture_comment(lease_break_info.tctx,
"transport[%p] Skip %s to %s in lease handler\n",
@@ -88,18 +118,6 @@
return true;
}
-/*
- * A lease break handler which ignores incoming lease break requests
- * To be used in cases where the client is expected to ignore incoming
- * lease break requests
- */
-bool torture_lease_ignore_handler(struct smb2_transport *transport,
- const struct smb2_lease_break *lb,
- void *private_data)
-{
- return true;
-}
-
/*
Timer handler function notifies the registering function that time is up
*/
diff -Nru samba-4.22.1+dfsg/source4/torture/smb2/lease_break_handler.h samba-4.22.2+dfsg/source4/torture/smb2/lease_break_handler.h
--- samba-4.22.1+dfsg/source4/torture/smb2/lease_break_handler.h 2025-02-06 13:31:55.632155200 +0300
+++ samba-4.22.2+dfsg/source4/torture/smb2/lease_break_handler.h 2025-06-05 18:38:33.782581000 +0300
@@ -28,6 +28,10 @@
struct smb2_transport *lease_transport;
bool lease_skip_ack;
struct smb2_lease_break_ack lease_break_ack;
+
+ struct smb2_handle lease_handle;
+ struct smb2_close close;
+
int count;
int failures;
@@ -147,9 +151,6 @@
bool torture_lease_handler(struct smb2_transport *transport,
const struct smb2_lease_break *lb,
void *private_data);
-bool torture_lease_ignore_handler(struct smb2_transport *transport,
- const struct smb2_lease_break *lb,
- void *private_data);
void torture_wait_for_lease_break(struct torture_context *tctx);
static inline void torture_reset_lease_break_info(struct torture_context *tctx,
diff -Nru samba-4.22.1+dfsg/source4/torture/smb2/notify.c samba-4.22.2+dfsg/source4/torture/smb2/notify.c
--- samba-4.22.1+dfsg/source4/torture/smb2/notify.c 2025-02-06 13:31:55.632155200 +0300
+++ samba-4.22.2+dfsg/source4/torture/smb2/notify.c 2025-06-05 18:38:33.782581000 +0300
@@ -2532,7 +2532,11 @@
torture_comment(torture, "Testing change notify of a rename with inotify\n");
- status = torture_smb2_testdir(tree1, BASEDIR_INR, &h1);
+ status = torture_smb2_testdir_access(
+ tree1,
+ BASEDIR_INR,
+ &h1,
+ SEC_RIGHTS_DIR_ALL & ~SEC_STD_DELETE);
torture_assert_ntstatus_ok_goto(torture, status, ok, done, "torture_smb2_testdir failed");
ZERO_STRUCT(create);
diff -Nru samba-4.22.1+dfsg/source4/torture/smb2/streams.c samba-4.22.2+dfsg/source4/torture/smb2/streams.c
--- samba-4.22.1+dfsg/source4/torture/smb2/streams.c 2025-02-06 13:31:55.636155100 +0300
+++ samba-4.22.2+dfsg/source4/torture/smb2/streams.c 2025-06-05 18:38:33.782581000 +0300
@@ -1522,17 +1522,15 @@
status = smb2_setinfo_file(tree, &sinfo);
CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
- if (!torture_setting_bool(tctx, "samba4", false)) {
- /*
- * Check SMB2 rename to the default stream using :<stream>.
- */
- torture_comment(tctx, "(%s) Checking SMB2 rename to default stream "
- "using :<stream>\n", __location__);
- sinfo.rename_information.in.file.handle = h1;
- sinfo.rename_information.in.new_name = stream_name_default;
- status = smb2_setinfo_file(tree, &sinfo);
- CHECK_STATUS(status, NT_STATUS_OK);
- }
+ /*
+ * Check SMB2 rename to the default stream using :<stream>.
+ */
+ torture_comment(tctx, "(%s) Checking SMB2 rename to default stream "
+ "using :<stream>\n", __location__);
+ sinfo.rename_information.in.file.handle = h1;
+ sinfo.rename_information.in.new_name = stream_name_default;
+ status = smb2_setinfo_file(tree, &sinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
smb2_util_close(tree, h1);
More information about the Pkg-samba-maint
mailing list