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