2 * Unix SMB/CIFS implementation.
3 * Utility functions for reparse points.
5 * Copyright (C) Jeremy Allison 2018
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "util_reparse.h"
23 #include "libcli/smb/reparse.h"
24 #include "source3/smbd/proto.h"
26 static NTSTATUS
fsctl_get_reparse_point_reg(struct files_struct
*fsp
,
37 * 64k+8 bytes is the maximum reparse point length
41 val
= talloc_array(ctx
, uint8_t, MIN(max_out_len
, 65536 + 8));
43 return NT_STATUS_NO_MEMORY
;
46 sizeret
= SMB_VFS_FGETXATTR(fsp
,
47 SAMBA_XATTR_REPARSE_ATTRIB
,
49 talloc_get_size(val
));
51 if ((sizeret
== -1) && (errno
== ERANGE
)) {
52 status
= NT_STATUS_BUFFER_TOO_SMALL
;
56 if ((sizeret
== -1) && (errno
== ENOATTR
)) {
57 DBG_DEBUG(SAMBA_XATTR_REPARSE_ATTRIB
" does not exist\n");
58 status
= NT_STATUS_NOT_A_REPARSE_POINT
;
63 status
= map_nt_error_from_unix(errno
);
64 DBG_DEBUG("SMB_VFS_FGETXATTR failed: %s\n", strerror(errno
));
76 NTSTATUS
fsctl_get_reparse_point(struct files_struct
*fsp
,
78 uint32_t *_reparse_tag
,
84 uint8_t *out_data
= NULL
;
86 uint32_t reparse_tag
= 0;
87 const uint8_t *reparse_data
= NULL
;
88 size_t reparse_data_length
;
89 NTSTATUS status
= NT_STATUS_NOT_A_REPARSE_POINT
;
91 dos_mode
= fdos_mode(fsp
);
92 if ((dos_mode
& FILE_ATTRIBUTE_REPARSE_POINT
) == 0) {
93 return NT_STATUS_NOT_A_REPARSE_POINT
;
96 if (S_ISREG(fsp
->fsp_name
->st
.st_ex_mode
)) {
97 DBG_DEBUG("%s is a regular file\n", fsp_str_dbg(fsp
));
98 status
= fsctl_get_reparse_point_reg(
99 fsp
, mem_ctx
, &out_data
, max_out_len
, &out_len
);
102 if (!NT_STATUS_IS_OK(status
)) {
106 status
= reparse_buffer_check(out_data
,
110 &reparse_data_length
);
111 if (!NT_STATUS_IS_OK(status
)) {
112 DBG_DEBUG("Invalid reparse data: %s\n", nt_errstr(status
));
113 TALLOC_FREE(out_data
);
117 *_reparse_tag
= reparse_tag
;
118 *_out_data
= out_data
;
124 NTSTATUS
fsctl_set_reparse_point(struct files_struct
*fsp
,
126 const uint8_t *in_data
,
129 uint32_t reparse_tag
;
130 const uint8_t *reparse_data
= NULL
;
131 size_t reparse_data_length
;
132 uint32_t existing_tag
;
133 uint8_t *existing_data
= NULL
;
134 uint32_t existing_len
;
139 DBG_DEBUG("Called on %s\n", fsp_str_dbg(fsp
));
141 if (!S_ISREG(fsp
->fsp_name
->st
.st_ex_mode
)) {
142 DBG_DEBUG("Can only set reparse point for regular files\n");
143 return NT_STATUS_ACCESS_DENIED
;
146 status
= reparse_buffer_check(in_data
,
150 &reparse_data_length
);
151 if (!NT_STATUS_IS_OK(status
)) {
152 DBG_DEBUG("check_reparse_data_buffer failed: %s\n",
157 DBG_DEBUG("reparse tag=%" PRIX32
", length=%zu\n",
159 reparse_data_length
);
161 status
= fsctl_get_reparse_point(fsp
,
167 if (NT_STATUS_IS_OK(status
)) {
169 TALLOC_FREE(existing_data
);
171 if (existing_tag
!= reparse_tag
) {
172 DBG_DEBUG("Can't overwrite tag %" PRIX32
173 " with tag %" PRIX32
"\n",
176 return NT_STATUS_IO_REPARSE_TAG_MISMATCH
;
181 ret
= SMB_VFS_FSETXATTR(
182 fsp
, SAMBA_XATTR_REPARSE_ATTRIB
, in_data
, in_len
, 0);
184 status
= map_nt_error_from_unix(errno
);
185 DBG_DEBUG("setxattr fail on %s - %s\n",
192 * Files with reparse points don't have the ATTR_NORMAL bit
195 dos_mode
= fdos_mode(fsp
);
196 dos_mode
&= ~FILE_ATTRIBUTE_NORMAL
;
197 dos_mode
|= FILE_ATTRIBUTE_REPARSE_POINT
;
199 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(fsp
->conn
, fsp
, dos_mode
);
201 if (!NT_STATUS_IS_OK(status
)) {
202 DBG_ERR("set reparse attr fail on %s - %s\n",
211 NTSTATUS
fsctl_del_reparse_point(struct files_struct
*fsp
,
213 const uint8_t *in_data
,
216 uint32_t existing_tag
;
217 uint8_t *existing_data
= NULL
;
218 uint32_t existing_len
;
219 uint32_t reparse_tag
;
220 const uint8_t *reparse_data
= NULL
;
221 size_t reparse_data_length
;
226 status
= fsctl_get_reparse_point(fsp
,
232 if (!NT_STATUS_IS_OK(status
)) {
235 TALLOC_FREE(existing_data
);
237 status
= reparse_buffer_check(in_data
,
241 &reparse_data_length
);
242 if (!NT_STATUS_IS_OK(status
)) {
245 if (reparse_data_length
!= 0) {
246 return NT_STATUS_IO_REPARSE_DATA_INVALID
;
249 if (existing_tag
!= reparse_tag
) {
250 DBG_DEBUG("Expect correct tag %" PRIX32
", got tag %" PRIX32
254 return NT_STATUS_IO_REPARSE_TAG_MISMATCH
;
257 ret
= SMB_VFS_FREMOVEXATTR(fsp
, SAMBA_XATTR_REPARSE_ATTRIB
);
259 status
= map_nt_error_from_unix(errno
);
260 DBG_DEBUG("removexattr fail on %s - %s\n",
267 * Files with reparse points don't have the ATTR_NORMAL bit
270 dos_mode
= fdos_mode(fsp
);
271 dos_mode
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
273 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(fsp
->conn
, fsp
, dos_mode
);
275 if (!NT_STATUS_IS_OK(status
)) {
276 DBG_ERR("set reparse attr fail on %s - %s\n",