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 static NTSTATUS
fsctl_get_reparse_point_int(
77 struct files_struct
*fsp
,
78 const struct reparse_data_buffer
*reparse_data
,
84 uint8_t *out_data
= NULL
;
87 out_len
= reparse_data_buffer_marshall(reparse_data
, NULL
, 0);
89 return NT_STATUS_INSUFFICIENT_RESOURCES
;
91 if (max_out_len
< out_len
) {
92 return NT_STATUS_BUFFER_TOO_SMALL
;
95 out_data
= talloc_array(ctx
, uint8_t, out_len
);
96 if (out_data
== NULL
) {
97 return NT_STATUS_NO_MEMORY
;
100 reparse_data_buffer_marshall(reparse_data
, out_data
, out_len
);
102 *_out_data
= out_data
;
108 static NTSTATUS
fsctl_get_reparse_point_fifo(struct files_struct
*fsp
,
111 uint32_t max_out_len
,
114 struct reparse_data_buffer reparse_data
= {
115 .tag
= IO_REPARSE_TAG_NFS
,
116 .parsed
.nfs
.type
= NFS_SPECFILE_FIFO
,
119 return fsctl_get_reparse_point_int(
120 fsp
, &reparse_data
, ctx
, _out_data
, max_out_len
, _out_len
);
123 static NTSTATUS
fsctl_get_reparse_point_sock(struct files_struct
*fsp
,
126 uint32_t max_out_len
,
129 struct reparse_data_buffer reparse_data
= {
130 .tag
= IO_REPARSE_TAG_NFS
,
131 .parsed
.nfs
.type
= NFS_SPECFILE_SOCK
,
134 return fsctl_get_reparse_point_int(
135 fsp
, &reparse_data
, ctx
, _out_data
, max_out_len
, _out_len
);
138 static NTSTATUS
fsctl_get_reparse_point_dev(struct files_struct
*fsp
,
143 uint32_t max_out_len
,
146 struct reparse_data_buffer reparse_data
= {
147 .tag
= IO_REPARSE_TAG_NFS
,
148 .parsed
.nfs
.type
= nfs_type
,
149 .parsed
.nfs
.data
.dev
.major
= unix_dev_major(rdev
),
150 .parsed
.nfs
.data
.dev
.minor
= unix_dev_minor(rdev
),
153 return fsctl_get_reparse_point_int(
154 fsp
, &reparse_data
, ctx
, _out_data
, max_out_len
, _out_len
);
157 NTSTATUS
fsctl_get_reparse_point(struct files_struct
*fsp
,
159 uint32_t *_reparse_tag
,
161 uint32_t max_out_len
,
165 uint8_t *out_data
= NULL
;
166 uint32_t out_len
= 0;
167 uint32_t reparse_tag
= 0;
168 const uint8_t *reparse_data
= NULL
;
169 size_t reparse_data_length
;
170 NTSTATUS status
= NT_STATUS_NOT_A_REPARSE_POINT
;
172 dos_mode
= fdos_mode(fsp
);
173 if ((dos_mode
& FILE_ATTRIBUTE_REPARSE_POINT
) == 0) {
174 return NT_STATUS_NOT_A_REPARSE_POINT
;
177 switch (fsp
->fsp_name
->st
.st_ex_mode
& S_IFMT
) {
179 DBG_DEBUG("%s is a regular file\n", fsp_str_dbg(fsp
));
180 status
= fsctl_get_reparse_point_reg(
181 fsp
, mem_ctx
, &out_data
, max_out_len
, &out_len
);
184 DBG_DEBUG("%s is a fifo\n", fsp_str_dbg(fsp
));
185 status
= fsctl_get_reparse_point_fifo(
186 fsp
, mem_ctx
, &out_data
, max_out_len
, &out_len
);
189 DBG_DEBUG("%s is a socket\n", fsp_str_dbg(fsp
));
190 status
= fsctl_get_reparse_point_sock(
191 fsp
, mem_ctx
, &out_data
, max_out_len
, &out_len
);
194 DBG_DEBUG("%s is a block device\n", fsp_str_dbg(fsp
));
195 status
= fsctl_get_reparse_point_dev(
198 fsp
->fsp_name
->st
.st_ex_rdev
,
205 DBG_DEBUG("%s is a character device\n", fsp_str_dbg(fsp
));
206 status
= fsctl_get_reparse_point_dev(
209 fsp
->fsp_name
->st
.st_ex_rdev
,
219 if (!NT_STATUS_IS_OK(status
)) {
220 DBG_DEBUG("failed: %s\n", nt_errstr(status
));
224 status
= reparse_buffer_check(out_data
,
228 &reparse_data_length
);
229 if (!NT_STATUS_IS_OK(status
)) {
230 DBG_DEBUG("Invalid reparse data: %s\n", nt_errstr(status
));
231 TALLOC_FREE(out_data
);
235 *_reparse_tag
= reparse_tag
;
236 *_out_data
= out_data
;
242 NTSTATUS
fsctl_get_reparse_tag(struct files_struct
*fsp
,
243 uint32_t *_reparse_tag
)
245 uint8_t *out_data
= NULL
;
249 status
= fsctl_get_reparse_point(fsp
,
255 TALLOC_FREE(out_data
);
259 NTSTATUS
fsctl_set_reparse_point(struct files_struct
*fsp
,
261 const uint8_t *in_data
,
264 uint32_t reparse_tag
;
265 const uint8_t *reparse_data
= NULL
;
266 size_t reparse_data_length
;
267 uint32_t existing_tag
;
272 DBG_DEBUG("Called on %s\n", fsp_str_dbg(fsp
));
274 if (!S_ISREG(fsp
->fsp_name
->st
.st_ex_mode
)) {
275 DBG_DEBUG("Can only set reparse point for regular files\n");
276 return NT_STATUS_ACCESS_DENIED
;
279 status
= reparse_buffer_check(in_data
,
283 &reparse_data_length
);
284 if (!NT_STATUS_IS_OK(status
)) {
285 DBG_DEBUG("check_reparse_data_buffer failed: %s\n",
290 DBG_DEBUG("reparse tag=%" PRIX32
", length=%zu\n",
292 reparse_data_length
);
294 status
= fsctl_get_reparse_tag(fsp
, &existing_tag
);
295 if (NT_STATUS_IS_OK(status
) && (existing_tag
!= reparse_tag
)) {
296 DBG_DEBUG("Can't overwrite tag %" PRIX32
" with tag %" PRIX32
300 return NT_STATUS_IO_REPARSE_TAG_MISMATCH
;
304 ret
= SMB_VFS_FSETXATTR(
305 fsp
, SAMBA_XATTR_REPARSE_ATTRIB
, in_data
, in_len
, 0);
307 status
= map_nt_error_from_unix(errno
);
308 DBG_DEBUG("setxattr fail on %s - %s\n",
315 * Files with reparse points don't have the ATTR_NORMAL bit
318 dos_mode
= fdos_mode(fsp
);
319 dos_mode
&= ~FILE_ATTRIBUTE_NORMAL
;
320 dos_mode
|= FILE_ATTRIBUTE_REPARSE_POINT
;
322 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(fsp
->conn
, fsp
, dos_mode
);
324 if (!NT_STATUS_IS_OK(status
)) {
325 DBG_ERR("set reparse attr fail on %s - %s\n",
331 fsp
->fsp_name
->st
.cached_dos_attributes
= dos_mode
;
336 NTSTATUS
fsctl_del_reparse_point(struct files_struct
*fsp
,
338 const uint8_t *in_data
,
341 uint32_t existing_tag
;
342 uint32_t reparse_tag
;
343 const uint8_t *reparse_data
= NULL
;
344 size_t reparse_data_length
;
349 status
= fsctl_get_reparse_tag(fsp
, &existing_tag
);
350 if (!NT_STATUS_IS_OK(status
)) {
354 status
= reparse_buffer_check(in_data
,
358 &reparse_data_length
);
359 if (!NT_STATUS_IS_OK(status
)) {
362 if (reparse_data_length
!= 0) {
363 return NT_STATUS_IO_REPARSE_DATA_INVALID
;
366 if (existing_tag
!= reparse_tag
) {
367 DBG_DEBUG("Expect correct tag %" PRIX32
", got tag %" PRIX32
371 return NT_STATUS_IO_REPARSE_TAG_MISMATCH
;
374 ret
= SMB_VFS_FREMOVEXATTR(fsp
, SAMBA_XATTR_REPARSE_ATTRIB
);
376 status
= map_nt_error_from_unix(errno
);
377 DBG_DEBUG("removexattr fail on %s - %s\n",
384 * Files with reparse points don't have the ATTR_NORMAL bit
387 dos_mode
= fdos_mode(fsp
);
388 dos_mode
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
390 status
= SMB_VFS_FSET_DOS_ATTRIBUTES(fsp
->conn
, fsp
, dos_mode
);
392 if (!NT_STATUS_IS_OK(status
)) {
393 DBG_ERR("set reparse attr fail on %s - %s\n",
399 fsp
->fsp_name
->st
.cached_dos_attributes
= dos_mode
;