The branch, master has been updated
       via  5edd1e7c3ee smbd: Implement FSCTL_DELETE_REPARSE_POINT
       via  97c79d47dd7 tests: Test FSCTL_DELETE_REPARSE_POINT
       via  d80a884f54c tests: Run reparse tests
       via  4fa6cffcfae tests: Expected failures in reparse point tests should 
not be errors
       via  8ace45e0da9 smbd: Implement fsctl_set_reparse_point
       via  6eba4b794f0 smbd: Implement fsctl_get_reparse_point
       via  7dc07710fbe tests: Clean up behind ourselves in test_create_reparse
       via  4536cfb1ec1 tests: Codify IO_REPARSE_TAG_MISMATCH behaviour
       via  fb74b3b5073 tests: Clarify a reparse point test
       via  6526f20e42b s3: smbd: smb2-posix: Add SAMBA_XATTR_REPARSE_ATTRIB 
"user.SmbReparse" name.
       via  deed7fab03d selftest: Default to "tmp" share in reparsepoints.py
       via  1fa7668ee82 smbd: Use reparse_buffer_check() in 
fsctl_set_reparse_point()
       via  4b98f993a25 smbd: Prepare to return the reparse tag from 
fsctl_get_reparse_point
       via  db5c23e4be7 smbd: Change the output of fsctl_get_reparse_point to 
uint8
       via  fafe29a2640 reparse: Tighten reparse point length check
       via  2defbc994de smbd: Return FILE_ATTRIBUTE_REPARSE_POINT from 
"user.DOSATTRIB"
      from  c5a1c8d45b9 s4:dsdb: Fix stack use after scope in 
gkdi_create_root_key()

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 5edd1e7c3ee8aa0e873b98285ddf2344cf96932c
Author: Volker Lendecke <v...@samba.org>
Date:   Mon May 6 16:59:44 2024 +0200

    smbd: Implement FSCTL_DELETE_REPARSE_POINT
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>
    
    Autobuild-User(master): Jeremy Allison <j...@samba.org>
    Autobuild-Date(master): Mon May  6 21:55:03 UTC 2024 on atb-devel-224

commit 97c79d47dd7294f4f78d9dbc799153afaf5fa03f
Author: Volker Lendecke <v...@samba.org>
Date:   Mon May 6 16:35:25 2024 +0200

    tests: Test FSCTL_DELETE_REPARSE_POINT
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit d80a884f54c50e8c47c038cb8a7a120f3f142699
Author: Volker Lendecke <v...@samba.org>
Date:   Fri May 3 15:03:30 2024 +0200

    tests: Run reparse tests
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 4fa6cffcfae68d816ddecb58003775b58b38cfd7
Author: Volker Lendecke <v...@samba.org>
Date:   Sun May 5 12:16:39 2024 +0200

    tests: Expected failures in reparse point tests should not be errors
    
    We need to put them into knownfail.d individually
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 8ace45e0da9dcfc660e83e9486646723b8eaa015
Author: Volker Lendecke <v...@samba.org>
Date:   Fri Dec 2 11:56:08 2022 +0100

    smbd: Implement fsctl_set_reparse_point
    
    Store the data in the "user.SmbReparse" xattr. Only allow this on
    regular files. Windows does it for directories too, but we can not
    allow this: Setting a symlink reparse point in a xattr on a directory
    would go unnoticed by our openat2-optimization. If someone really
    needs this, we could have a VFS module disallowing openat2 and doing
    the appropriate checks on every openat-call.
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 6eba4b794f0bfa9b85dc7ca754a12169d7b89bd5
Author: Volker Lendecke <v...@samba.org>
Date:   Sun May 5 11:14:45 2024 +0200

    smbd: Implement fsctl_get_reparse_point
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 7dc07710fbede128d0b7064f78d86dfabd47cf5d
Author: Volker Lendecke <v...@samba.org>
Date:   Fri May 3 14:52:42 2024 +0200

    tests: Clean up behind ourselves in test_create_reparse
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 4536cfb1ec1fdf7dcaf0989d04770512c208e241
Author: Volker Lendecke <v...@samba.org>
Date:   Fri May 3 14:49:46 2024 +0200

    tests: Codify IO_REPARSE_TAG_MISMATCH behaviour
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit fb74b3b50734838c77a314bb37ef5d7b8dec7fb3
Author: Volker Lendecke <v...@samba.org>
Date:   Fri May 3 14:49:24 2024 +0200

    tests: Clarify a reparse point test
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 6526f20e42bcc533ad5c8a4c402622dc0a5c0fd8
Author: Jeremy Allison <j...@samba.org>
Date:   Tue Sep 18 09:03:22 2018 -0700

    s3: smbd: smb2-posix: Add SAMBA_XATTR_REPARSE_ATTRIB "user.SmbReparse" name.
    
    Ensure it's prohibited for normal user access.
    
    Signed-off-by: Jeremy Allison <j...@samba.org>
    Reviewed-by: Volker Lendecke <v...@samba.org>

commit deed7fab03d2f2c7865cbdc9ef6c55353714d5a6
Author: Volker Lendecke <v...@samba.org>
Date:   Mon Jan 2 13:56:12 2023 +0100

    selftest: Default to "tmp" share in reparsepoints.py
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 1fa7668ee827e0ccac4e15c257d73bf5de2ed701
Author: Volker Lendecke <v...@samba.org>
Date:   Thu May 2 16:34:43 2024 +0200

    smbd: Use reparse_buffer_check() in fsctl_set_reparse_point()
    
    check_reparse_data_buffer() was duplicated code
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 4b98f993a257ca66df0cb42f920377dbe4b2bc91
Author: Volker Lendecke <v...@samba.org>
Date:   Sat May 4 11:19:14 2024 +0200

    smbd: Prepare to return the reparse tag from fsctl_get_reparse_point
    
    We'll need this in many places, for example when listing directories
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit db5c23e4be79ab501b06ee31bb2a562287eb9dd7
Author: Volker Lendecke <v...@samba.org>
Date:   Sat May 4 19:32:03 2024 +0200

    smbd: Change the output of fsctl_get_reparse_point to uint8
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit fafe29a26407237d9742cf135a7db759c88656b6
Author: Volker Lendecke <v...@samba.org>
Date:   Sat May 4 10:54:27 2024 +0200

    reparse: Tighten reparse point length check
    
    test_create_reparse shows that the length checks need to be precise,
    not just checking for overflow.
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 2defbc994de3c0ce763c5272bdf23b6054e5af95
Author: Volker Lendecke <v...@samba.org>
Date:   Fri Dec 2 15:02:18 2022 +0100

    smbd: Return FILE_ATTRIBUTE_REPARSE_POINT from "user.DOSATTRIB"
    
    Signed-off-by: Volker Lendecke <v...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

-----------------------------------------------------------------------

Summary of changes:
 libcli/smb/reparse.c                |  12 +-
 libcli/smb/reparse.h                |   5 +
 python/samba/tests/reparsepoints.py | 104 +++++++++++++--
 selftest/knownfail.d/reparse        |   2 +
 selftest/skip                       |   1 -
 source3/include/smb.h               |   2 +
 source3/modules/util_reparse.c      | 251 ++++++++++++++++++++++++++++++++----
 source3/modules/util_reparse.h      |   5 +-
 source3/modules/vfs_default.c       |   3 +-
 source3/smbd/dosmode.c              |  12 +-
 source3/smbd/smb2_trans2.c          |   1 +
 11 files changed, 352 insertions(+), 46 deletions(-)
 create mode 100644 selftest/knownfail.d/reparse


Changeset truncated at 500 lines:

diff --git a/libcli/smb/reparse.c b/libcli/smb/reparse.c
index 49ecc77725d..08071ca85d7 100644
--- a/libcli/smb/reparse.c
+++ b/libcli/smb/reparse.c
@@ -26,11 +26,11 @@
 #include "lib/util/charset/charset.h"
 #include "smb_util.h"
 
-static NTSTATUS reparse_buffer_check(const uint8_t *in_data,
-                                    size_t in_len,
-                                    uint32_t *reparse_tag,
-                                    const uint8_t **_reparse_data,
-                                    size_t *_reparse_data_length)
+NTSTATUS reparse_buffer_check(const uint8_t *in_data,
+                             size_t in_len,
+                             uint32_t *reparse_tag,
+                             const uint8_t **_reparse_data,
+                             size_t *_reparse_data_length)
 {
        uint16_t reparse_data_length;
 
@@ -45,7 +45,7 @@ static NTSTATUS reparse_buffer_check(const uint8_t *in_data,
 
        reparse_data_length = PULL_LE_U16(in_data, 4);
 
-       if (reparse_data_length > (in_len - 8)) {
+       if (reparse_data_length != (in_len - 8)) {
                DBG_DEBUG("in_len=%zu, reparse_data_length=%" PRIu16 "\n",
                          in_len,
                          reparse_data_length);
diff --git a/libcli/smb/reparse.h b/libcli/smb/reparse.h
index 23274bf3852..e4410d974e4 100644
--- a/libcli/smb/reparse.h
+++ b/libcli/smb/reparse.h
@@ -63,6 +63,11 @@ struct reparse_data_buffer {
        } parsed;
 };
 
+NTSTATUS reparse_buffer_check(const uint8_t *in_data,
+                             size_t in_len,
+                             uint32_t *reparse_tag,
+                             const uint8_t **_reparse_data,
+                             size_t *_reparse_data_length);
 NTSTATUS reparse_data_buffer_parse(TALLOC_CTX *mem_ctx,
                                   struct reparse_data_buffer *dst,
                                   const uint8_t *buf,
diff --git a/python/samba/tests/reparsepoints.py 
b/python/samba/tests/reparsepoints.py
index cb7421df518..96ca6eefdd5 100644
--- a/python/samba/tests/reparsepoints.py
+++ b/python/samba/tests/reparsepoints.py
@@ -24,7 +24,9 @@ import samba.tests.libsmb
 class ReparsePoints(samba.tests.libsmb.LibsmbTests):
 
     def connection(self):
-        share = samba.tests.env_get_var_value("SHARENAME")
+        share = samba.tests.env_get_var_value("SHARENAME", allow_missing=True)
+        if not share:
+            share = "tmp"
         smb1 = samba.tests.env_get_var_value("SMB1", allow_missing=True)
         conn = libsmb.Conn(
             self.server_ip,
@@ -72,9 +74,11 @@ class ReparsePoints(samba.tests.libsmb.LibsmbTests):
 
         fd = conn.create(
             filename,
-            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE | sec.SEC_STD_DELETE,
             CreateDisposition=libsmb.FILE_CREATE)
 
+        conn.delete_on_close(fd, 1)
+
         with self.assertRaises(NTSTATUSError) as e:
             conn.fsctl(fd, libsmb.FSCTL_SET_REPARSE_POINT, b'', 0)
 
@@ -103,9 +107,16 @@ class ReparsePoints(samba.tests.libsmb.LibsmbTests):
         self.assertEqual(e.exception.args[0],
                          ntstatus.NT_STATUS_IO_REPARSE_DATA_INVALID)
 
+        # Exact length works
         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)
+
+        b = reparse_symlink.put(0x80000026, 0, b'asdf')
+
+        # We can't overwrite an existing reparse point with a different tag
+        with self.assertRaises(NTSTATUSError) as e:
+            conn.fsctl(fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+        self.assertEqual(e.exception.args[0],
+                         ntstatus.NT_STATUS_IO_REPARSE_TAG_MISMATCH)
 
     # Show that we can write to a reparse point when opened properly
     def test_write_reparse(self):
@@ -151,8 +162,20 @@ class ReparsePoints(samba.tests.libsmb.LibsmbTests):
             sec.SEC_STD_DELETE,
             CreateDisposition=libsmb.FILE_CREATE,
             CreateOptions=libsmb.FILE_DIRECTORY_FILE)
+
         b = reparse_symlink.put(0x80000025, 0, b'asdfasdfasdfasdfasdfasdf')
-        conn.fsctl(dir_fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+
+        try:
+            conn.fsctl(dir_fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+        except NTSTATUSError as e:
+            err = e.args[0]
+            if (err != ntstatus.NT_STATUS_ACCESS_DENIED):
+                raise
+
+        if (err == ntstatus.NT_STATUS_ACCESS_DENIED):
+            self.fail("Could not set reparse point on directory")
+            conn.delete_on_close(fd, 1)
+            return
 
         with self.assertRaises(NTSTATUSError) as e:
             fd = conn.create(
@@ -188,20 +211,22 @@ class ReparsePoints(samba.tests.libsmb.LibsmbTests):
             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)
 
+        ok = (err == ntstatus.NT_STATUS_DIRECTORY_NOT_EMPTY)
+        if not ok:
+            self.fail(f'set_reparse on nonempty directory returned {err}')
+
     # Show that reparse point opens respect share modes
 
     def test_reparse_share_modes(self):
@@ -236,6 +261,69 @@ class ReparsePoints(samba.tests.libsmb.LibsmbTests):
         conn.delete_on_close(fd1, 1)
         conn.close(fd1)
 
+    def test_delete_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)
+        b = reparse_symlink.put(0x80000025, 0, b'asdfasdfasdfasdfasdfasdf')
+        conn.fsctl(fd, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
+        conn.close(fd)
+
+        (fd,cr,_) = conn.create_ex(
+            filename,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE|sec.SEC_STD_DELETE,
+            CreateOptions=libsmb.FILE_OPEN_REPARSE_POINT,
+            CreateDisposition=libsmb.FILE_OPEN)
+
+        self.assertEqual(cr['file_attributes'] &
+                         libsmb.FILE_ATTRIBUTE_REPARSE_POINT,
+                         libsmb.FILE_ATTRIBUTE_REPARSE_POINT)
+
+        b = reparse_symlink.put(0x80000026, 0, b'')
+        with self.assertRaises(NTSTATUSError) as e:
+            conn.fsctl(fd, libsmb.FSCTL_DELETE_REPARSE_POINT, b, 0)
+        self.assertEqual(e.exception.args[0],
+                         ntstatus.NT_STATUS_IO_REPARSE_TAG_MISMATCH)
+
+        b = reparse_symlink.put(0x80000026, 0, b' ')
+        with self.assertRaises(NTSTATUSError) as e:
+            conn.fsctl(fd, libsmb.FSCTL_DELETE_REPARSE_POINT, b, 0)
+        self.assertEqual(e.exception.args[0],
+                         ntstatus.NT_STATUS_IO_REPARSE_DATA_INVALID)
+
+        b = reparse_symlink.put(0x80000025, 0, b' ')
+        with self.assertRaises(NTSTATUSError) as e:
+            conn.fsctl(fd, libsmb.FSCTL_DELETE_REPARSE_POINT, b, 0)
+        self.assertEqual(e.exception.args[0],
+                         ntstatus.NT_STATUS_IO_REPARSE_DATA_INVALID)
+
+        b = reparse_symlink.put(0x80000025, 0, b'')
+        conn.fsctl(fd, libsmb.FSCTL_DELETE_REPARSE_POINT, b, 0)
+
+        with self.assertRaises(NTSTATUSError) as e:
+            conn.fsctl(fd, libsmb.FSCTL_DELETE_REPARSE_POINT, b, 0)
+        self.assertEqual(e.exception.args[0],
+                         ntstatus.NT_STATUS_NOT_A_REPARSE_POINT)
+
+        conn.close(fd)
+
+        (fd,cr,_) = conn.create_ex(
+            filename,
+            DesiredAccess=sec.SEC_FILE_WRITE_ATTRIBUTE|sec.SEC_STD_DELETE,
+            CreateDisposition=libsmb.FILE_OPEN)
+
+        self.assertEqual(cr['file_attributes'] &
+                         libsmb.FILE_ATTRIBUTE_REPARSE_POINT,
+                         0)
+
+        conn.delete_on_close(fd, 1)
+        conn.close(fd)
+
 if __name__ == '__main__':
     import unittest
     unittest.main()
diff --git a/selftest/knownfail.d/reparse b/selftest/knownfail.d/reparse
new file mode 100644
index 00000000000..11d094aa5c6
--- /dev/null
+++ b/selftest/knownfail.d/reparse
@@ -0,0 +1,2 @@
+^samba.tests.reparsepoints.samba.tests.reparsepoints.ReparsePoints.test_create_reparse_directory
+^samba.tests.reparsepoints.samba.tests.reparsepoints.ReparsePoints.test_create_reparse_nonempty_directory
diff --git a/selftest/skip b/selftest/skip
index cc2fe8979e8..b5266bb16d8 100644
--- a/selftest/skip
+++ b/selftest/skip
@@ -146,5 +146,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.*
 ^samba3.blackbox.open-eintr.*
diff --git a/source3/include/smb.h b/source3/include/smb.h
index e47ccd66133..7f3482fe442 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -614,6 +614,8 @@ struct ea_list {
 #define SAMBA_XATTR_DOSSTREAM_PREFIX "user.DosStream."
 /* Prefix for xattrs storing streams. */
 #define SAMBA_XATTR_MARKER "user.SAMBA_STREAMS"
+/* EA to use to store reparse points. */
+#define SAMBA_XATTR_REPARSE_ATTRIB "user.SmbReparse"
 
 /* usershare error codes. */
 enum usershare_err {
diff --git a/source3/modules/util_reparse.c b/source3/modules/util_reparse.c
index 45cacbdbe22..6f47367a4b8 100644
--- a/source3/modules/util_reparse.c
+++ b/source3/modules/util_reparse.c
@@ -20,40 +20,104 @@
 
 #include "includes.h"
 #include "util_reparse.h"
+#include "libcli/smb/reparse.h"
+#include "source3/smbd/proto.h"
 
-NTSTATUS fsctl_get_reparse_point(struct files_struct *fsp,
-                                TALLOC_CTX *mem_ctx,
-                                char **out_data,
-                                uint32_t max_out_len,
-                                uint32_t *out_len)
+static NTSTATUS fsctl_get_reparse_point_reg(struct files_struct *fsp,
+                                           TALLOC_CTX *ctx,
+                                           uint8_t **_out_data,
+                                           uint32_t max_out_len,
+                                           uint32_t *_out_len)
 {
-       DBG_DEBUG("Called on %s\n", fsp_str_dbg(fsp));
-       return NT_STATUS_NOT_A_REPARSE_POINT;
+       uint8_t *val = NULL;
+       ssize_t sizeret;
+       NTSTATUS status;
+
+       /*
+        * 64k+8 bytes is the maximum reparse point length
+        * possible
+        */
+
+       val = talloc_array(ctx, uint8_t, MIN(max_out_len, 65536 + 8));
+       if (val == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       sizeret = SMB_VFS_FGETXATTR(fsp,
+                                   SAMBA_XATTR_REPARSE_ATTRIB,
+                                   val,
+                                   talloc_get_size(val));
+
+       if ((sizeret == -1) && (errno == ERANGE)) {
+               status = NT_STATUS_BUFFER_TOO_SMALL;
+               goto fail;
+       }
+
+       if ((sizeret == -1) && (errno == ENOATTR)) {
+               DBG_DEBUG(SAMBA_XATTR_REPARSE_ATTRIB " does not exist\n");
+               status = NT_STATUS_NOT_A_REPARSE_POINT;
+               goto fail;
+       }
+
+       if (sizeret == -1) {
+               status = map_nt_error_from_unix(errno);
+               DBG_DEBUG("SMB_VFS_FGETXATTR failed: %s\n", strerror(errno));
+               goto fail;
+       }
+
+       *_out_data = val;
+       *_out_len = sizeret;
+       return NT_STATUS_OK;
+fail:
+       TALLOC_FREE(val);
+       return status;
 }
 
-static NTSTATUS check_reparse_data_buffer(
-       const uint8_t *in_data, size_t in_len)
+NTSTATUS fsctl_get_reparse_point(struct files_struct *fsp,
+                                TALLOC_CTX *mem_ctx,
+                                uint32_t *_reparse_tag,
+                                uint8_t **_out_data,
+                                uint32_t max_out_len,
+                                uint32_t *_out_len)
 {
-       uint16_t reparse_data_length;
+       uint32_t dos_mode;
+       uint8_t *out_data = NULL;
+       uint32_t out_len = 0;
+       uint32_t reparse_tag = 0;
+       const uint8_t *reparse_data = NULL;
+       size_t reparse_data_length;
+       NTSTATUS status = NT_STATUS_NOT_A_REPARSE_POINT;
 
-       if (in_len == 0) {
-               DBG_DEBUG("in_len=0\n");
-               return NT_STATUS_INVALID_BUFFER_SIZE;
+       dos_mode = fdos_mode(fsp);
+       if ((dos_mode & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
+               return NT_STATUS_NOT_A_REPARSE_POINT;
        }
-       if (in_len < 8) {
-               DBG_DEBUG("in_len=%zu\n", in_len);
-               return NT_STATUS_IO_REPARSE_DATA_INVALID;
+
+       if (S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
+               DBG_DEBUG("%s is a regular file\n", fsp_str_dbg(fsp));
+               status = fsctl_get_reparse_point_reg(
+                       fsp, mem_ctx, &out_data, max_out_len, &out_len);
        }
 
-       reparse_data_length = PULL_LE_U16(in_data, 4);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       if (reparse_data_length != (in_len - 8)) {
-               DBG_DEBUG("in_len=%zu, reparse_data_length=%"PRIu16"\n",
-                         in_len,
-                         reparse_data_length);
-               return NT_STATUS_IO_REPARSE_DATA_INVALID;
+       status = reparse_buffer_check(out_data,
+                                     out_len,
+                                     &reparse_tag,
+                                     &reparse_data,
+                                     &reparse_data_length);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("Invalid reparse data: %s\n", nt_errstr(status));
+               TALLOC_FREE(out_data);
+               return status;
        }
 
+       *_reparse_tag = reparse_tag;
+       *_out_data = out_data;
+       *_out_len = out_len;
+
        return NT_STATUS_OK;
 }
 
@@ -62,16 +126,86 @@ NTSTATUS fsctl_set_reparse_point(struct files_struct *fsp,
                                 const uint8_t *in_data,
                                 uint32_t in_len)
 {
+       uint32_t reparse_tag;
+       const uint8_t *reparse_data = NULL;
+       size_t reparse_data_length;
+       uint32_t existing_tag;
+       uint8_t *existing_data = NULL;
+       uint32_t existing_len;
        NTSTATUS status;
+       uint32_t dos_mode;
+       int ret;
 
        DBG_DEBUG("Called on %s\n", fsp_str_dbg(fsp));
 
-       status = check_reparse_data_buffer(in_data, in_len);
+       if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
+               DBG_DEBUG("Can only set reparse point for regular files\n");
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       status = reparse_buffer_check(in_data,
+                                     in_len,
+                                     &reparse_tag,
+                                     &reparse_data,
+                                     &reparse_data_length);
        if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("check_reparse_data_buffer failed: %s\n",
+                         nt_errstr(status));
                return status;
        }
 
-       return NT_STATUS_NOT_A_REPARSE_POINT;
+       DBG_DEBUG("reparse tag=%" PRIX32 ", length=%zu\n",
+                 reparse_tag,
+                 reparse_data_length);
+
+       status = fsctl_get_reparse_point(fsp,
+                                        talloc_tos(),
+                                        &existing_tag,
+                                        &existing_data,
+                                        UINT32_MAX,
+                                        &existing_len);
+       if (NT_STATUS_IS_OK(status)) {
+
+               TALLOC_FREE(existing_data);
+
+               if (existing_tag != reparse_tag) {
+                       DBG_DEBUG("Can't overwrite tag %" PRIX32
+                                 " with tag %" PRIX32 "\n",
+                                 existing_tag,
+                                 reparse_tag);
+                       return NT_STATUS_IO_REPARSE_TAG_MISMATCH;
+               }
+       }
+
+       /* Store the data */
+       ret = SMB_VFS_FSETXATTR(
+               fsp, SAMBA_XATTR_REPARSE_ATTRIB, in_data, in_len, 0);
+       if (ret == -1) {
+               status = map_nt_error_from_unix(errno);
+               DBG_DEBUG("setxattr fail on %s - %s\n",
+                         fsp_str_dbg(fsp),
+                         strerror(errno));
+               return status;
+       }
+
+       /*
+        * Files with reparse points don't have the ATTR_NORMAL bit
+        * set
+        */
+       dos_mode = fdos_mode(fsp);
+       dos_mode &= ~FILE_ATTRIBUTE_NORMAL;
+       dos_mode |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+       status = SMB_VFS_FSET_DOS_ATTRIBUTES(fsp->conn, fsp, dos_mode);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("set reparse attr fail on %s - %s\n",
+                       fsp_str_dbg(fsp),
+                       nt_errstr(status));
+               return status;
+       }
+
+       return NT_STATUS_OK;
 }
 
 NTSTATUS fsctl_del_reparse_point(struct files_struct *fsp,
@@ -79,6 +213,71 @@ NTSTATUS fsctl_del_reparse_point(struct files_struct *fsp,
                                 const uint8_t *in_data,
                                 uint32_t in_len)
 {
-       DBG_DEBUG("Called on %s\n", fsp_str_dbg(fsp));
-       return NT_STATUS_NOT_A_REPARSE_POINT;
+       uint32_t existing_tag;
+       uint8_t *existing_data = NULL;
+       uint32_t existing_len;
+       uint32_t reparse_tag;
+       const uint8_t *reparse_data = NULL;
+       size_t reparse_data_length;
+       NTSTATUS status;
+       uint32_t dos_mode;
+       int ret;
+
+       status = fsctl_get_reparse_point(fsp,
+                                        talloc_tos(),
+                                        &existing_tag,
+                                        &existing_data,
+                                        UINT32_MAX,
+                                        &existing_len);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       TALLOC_FREE(existing_data);
+
+       status = reparse_buffer_check(in_data,
+                                     in_len,
+                                     &reparse_tag,
+                                     &reparse_data,
+                                     &reparse_data_length);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       if (reparse_data_length != 0) {
+               return NT_STATUS_IO_REPARSE_DATA_INVALID;
+       }


-- 
Samba Shared Repository

Reply via email to