tests: Start testing reparsepoints
authorVolker Lendecke <vl@samba.org>
Thu, 10 Nov 2022 17:31:11 +0000 (18:31 +0100)
committerJeremy Allison <jra@samba.org>
Tue, 22 Nov 2022 18:27:33 +0000 (18:27 +0000)
This still all fails, but if you run them against Windows they work.

How to run:

PYTHONPATH=bin/python \
LOCAL_PATH=/tmp \
SMB1_SHARE=share \
SMB2_SHARE=share \
SHARENAME=share \
SERVER_IP=<server-ip> \
DOMAIN=<your-domain> \
USERNAME=Administrator \
PASSWORD=<your-password> \
SMB_CONF_PATH=/usr/local/samba/etc/smb.conf \
SERVERCONFFILE="$SMB_CONF_PATH" \
python3 -m samba.subunit.run samba.tests.reparsepoints

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
python/samba/tests/reparsepoints.py [new file with mode: 0644]
selftest/skip
source3/selftest/tests.py

diff --git a/python/samba/tests/reparsepoints.py b/python/samba/tests/reparsepoints.py
new file mode 100644 (file)
index 0000000..07abdb0
--- /dev/null
@@ -0,0 +1,167 @@
+# Unix SMB/CIFS implementation.
+# Copyright Volker Lendecke <vl@samba.org> 2022
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from samba.samba3 import libsmb_samba_internal as libsmb
+from samba import (ntstatus,NTSTATUSError)
+from samba.dcerpc import security as sec
+from samba import reparse_symlink
+import samba.tests.libsmb
+
+class ReparsePoints(samba.tests.libsmb.LibsmbTests):
+
+    def connection(self):
+        share = samba.tests.env_get_var_value("SHARENAME")
+        smb1 = samba.tests.env_get_var_value("SMB1", allow_missing=True)
+        conn = libsmb.Conn(
+            self.server_ip,
+            share,
+            self.lp,
+            self.creds,
+            force_smb1=smb1)
+        return conn
+
+    def clean_file(self, conn, filename):
+        try:
+            conn.unlink(filename)
+        except NTSTATUSError as e:
+            err = e.args[0]
+            ok = (err == ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND)
+            ok |= (err == ntstatus.NT_STATUS_OBJECT_PATH_NOT_FOUND)
+            if not ok:
+                raise
+
+    def test_error_not_a_reparse_point(self):
+        conn = self.connection()
+        filename = 'reparse'
+        self.clean_file(conn, filename)
+
+        fd = conn.create(
+            filename,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE,
+            CreateDisposition=libsmb.FILE_CREATE)
+
+        with self.assertRaises(NTSTATUSError) as e:
+            conn.fsctl(fd, libsmb.FSCTL_GET_REPARSE_POINT, b'', 1024)
+            self.assertEqual(e.args[0], ntstatus.NT_STATUS_NOT_A_REPARSE_POINT)
+
+        conn.close(fd)
+
+        self.clean_file(conn, filename)
+
+    def test_create_reparse(self):
+        conn = self.connection()
+        filename = 'reparse'
+        self.clean_file(conn, filename)
+
+        fd = conn.create(
+            filename,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE,
+            CreateDisposition=libsmb.FILE_CREATE)
+        b = reparse_symlink.put(0x80000025, 0, b'asdfasdfasdfasdfasdfasdf')
+        conn.fsctl(fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+        b = reparse_symlink.put(0x80000026, 0, b'asdfasdfasdfasdfasdfasdf')
+        conn.fsctl(fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+
+    # Show that directories can carry reparse points
+
+    def test_create_reparse_directory(self):
+        conn = self.connection()
+        dirname = "reparse_dir"
+
+        self.clean_file(conn, dirname)
+
+        fd = conn.create(
+            dirname,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE|
+            sec.SEC_STD_DELETE,
+            CreateDisposition=libsmb.FILE_CREATE,
+            CreateOptions=libsmb.FILE_DIRECTORY_FILE)
+        b = reparse_symlink.put(0x80000025, 0, b'asdfasdfasdfasdfasdfasdf')
+        conn.fsctl(fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+        conn.delete_on_close(fd, 1)
+
+    # Only empty directories can carry reparse points
+
+    def test_create_reparse_nonempty_directory(self):
+        conn = self.connection()
+        dirname = "reparse_dir"
+        filename = f'{dirname}\\file.txt'
+
+        self.clean_file(conn, filename)
+        self.clean_file(conn, dirname)
+
+        dir_fd = conn.create(
+            dirname,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE|
+            sec.SEC_STD_DELETE,
+            CreateDisposition=libsmb.FILE_CREATE,
+            CreateOptions=libsmb.FILE_DIRECTORY_FILE)
+        fd = conn.create(
+            filename,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE|
+            sec.SEC_STD_DELETE,
+            CreateDisposition=libsmb.FILE_CREATE)
+
+        b = reparse_symlink.put(0x80000025, 0, b'asdf')
+        try:
+            conn.fsctl(dir_fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+        except NTSTATUSError as e:
+            err = e.args[0]
+            ok = (err == ntstatus.NT_STATUS_DIRECTORY_NOT_EMPTY)
+            if not ok:
+                raise
+
+        conn.delete_on_close(fd, 1)
+        conn.close(fd)
+        conn.delete_on_close(dir_fd, 1)
+        conn.close(dir_fd)
+
+    # Show that reparse point opens respect share modes
+
+    def test_reparse_share_modes(self):
+        conn = self.connection()
+        filename = 'reparse'
+        self.clean_file(conn, filename)
+
+        fd = conn.create(
+            filename,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE,
+            CreateDisposition=libsmb.FILE_CREATE)
+        b = reparse_symlink.put(0x80000025, 0, b'asdfasdfasdfasdfasdfasdf')
+        conn.fsctl(fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+        conn.close(fd);
+
+        fd1 = conn.create(
+            filename,
+            DesiredAccess=sec.SEC_FILE_READ_DATA|sec.SEC_STD_DELETE,
+            CreateDisposition=libsmb.FILE_OPEN,
+            CreateOptions=libsmb.FILE_OPEN_REPARSE_POINT)
+
+        with self.assertRaises(NTSTATUSError) as e:
+            fd2 = conn.create(
+                filename,
+                DesiredAccess=sec.SEC_FILE_READ_DATA,
+                CreateDisposition=libsmb.FILE_OPEN,
+                CreateOptions=libsmb.FILE_OPEN_REPARSE_POINT)
+            self.assertEqual(e.args[0], ntstatus.NT_STATUS_SHARING_VIOLATION)
+
+        conn.delete_on_close(fd1, 1);
+        conn.close(fd1)
+
+if __name__ == '__main__':
+    import unittest
+    unittest.main()
index d5cc786f8a55349b3863c95664f433bf53d0dc69..321a09e988b69fea4cd4105b15334a54ab0022e4 100644 (file)
@@ -148,3 +148,4 @@ bench # don't run benchmarks in our selftest
 ^samba4.smb2.mangle.*\(ad_dc_ntvfs\)$ # Ignore ad_dc_ntvfs since this is a new test
 ^samba4.smb2.tcon.*\(ad_dc_ntvfs\)$ # Ignore ad_dc_ntvfs since this is a new test
 ^samba4.smb2.mkdir.*\(ad_dc_ntvfs\)$ # Ignore ad_dc_ntvfs since this is a new test
+^samba.tests.reparsepoints.*
index f6cc6e0c639fc96e0b5372772de820d452f31315..dceed0e00727a7ce9a04697a806a1882f724f9e9 100755 (executable)
@@ -1687,3 +1687,4 @@ for t in CLUSTERED_LOCAL_TESTS:
          "-N 1000 -o 2000"])
 
 planpythontestsuite("fileserver", "samba.tests.smb3unix")
+planpythontestsuite("fileserver", "samba.tests.reparsepoints")