utils: Fix up 14a533680245
[samba4-gss.git] / source3 / modules / util_reparse.c
blob2694410be4fa3a7ff2bd202b41ee0c6280c58631
1 /*
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/>.
21 #include "includes.h"
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,
27 TALLOC_CTX *ctx,
28 uint8_t **_out_data,
29 uint32_t max_out_len,
30 uint32_t *_out_len)
32 uint8_t *val = NULL;
33 ssize_t sizeret;
34 NTSTATUS status;
37 * 64k+8 bytes is the maximum reparse point length
38 * possible
41 val = talloc_array(ctx, uint8_t, MIN(max_out_len, 65536 + 8));
42 if (val == NULL) {
43 return NT_STATUS_NO_MEMORY;
46 sizeret = SMB_VFS_FGETXATTR(fsp,
47 SAMBA_XATTR_REPARSE_ATTRIB,
48 val,
49 talloc_get_size(val));
51 if ((sizeret == -1) && (errno == ERANGE)) {
52 status = NT_STATUS_BUFFER_TOO_SMALL;
53 goto fail;
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;
59 goto fail;
62 if (sizeret == -1) {
63 status = map_nt_error_from_unix(errno);
64 DBG_DEBUG("SMB_VFS_FGETXATTR failed: %s\n", strerror(errno));
65 goto fail;
68 *_out_data = val;
69 *_out_len = sizeret;
70 return NT_STATUS_OK;
71 fail:
72 TALLOC_FREE(val);
73 return status;
76 static NTSTATUS fsctl_get_reparse_point_int(
77 struct files_struct *fsp,
78 const struct reparse_data_buffer *reparse_data,
79 TALLOC_CTX *ctx,
80 uint8_t **_out_data,
81 uint32_t max_out_len,
82 uint32_t *_out_len)
84 uint8_t *out_data = NULL;
85 ssize_t out_len;
87 out_len = reparse_data_buffer_marshall(reparse_data, NULL, 0);
88 if (out_len == -1) {
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;
103 *_out_len = out_len;
105 return NT_STATUS_OK;
108 static NTSTATUS fsctl_get_reparse_point_fifo(struct files_struct *fsp,
109 TALLOC_CTX *ctx,
110 uint8_t **_out_data,
111 uint32_t max_out_len,
112 uint32_t *_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,
124 TALLOC_CTX *ctx,
125 uint8_t **_out_data,
126 uint32_t max_out_len,
127 uint32_t *_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,
139 uint64_t nfs_type,
140 dev_t rdev,
141 TALLOC_CTX *ctx,
142 uint8_t **_out_data,
143 uint32_t max_out_len,
144 uint32_t *_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,
158 TALLOC_CTX *mem_ctx,
159 uint32_t *_reparse_tag,
160 uint8_t **_out_data,
161 uint32_t max_out_len,
162 uint32_t *_out_len)
164 uint32_t dos_mode;
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) {
178 case S_IFREG:
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);
182 break;
183 case S_IFIFO:
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);
187 break;
188 case S_IFSOCK:
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);
192 break;
193 case S_IFBLK:
194 DBG_DEBUG("%s is a block device\n", fsp_str_dbg(fsp));
195 status = fsctl_get_reparse_point_dev(
196 fsp,
197 NFS_SPECFILE_BLK,
198 fsp->fsp_name->st.st_ex_rdev,
199 mem_ctx,
200 &out_data,
201 max_out_len,
202 &out_len);
203 break;
204 case S_IFCHR:
205 DBG_DEBUG("%s is a character device\n", fsp_str_dbg(fsp));
206 status = fsctl_get_reparse_point_dev(
207 fsp,
208 NFS_SPECFILE_CHR,
209 fsp->fsp_name->st.st_ex_rdev,
210 mem_ctx,
211 &out_data,
212 max_out_len,
213 &out_len);
214 break;
215 default:
216 break;
219 if (!NT_STATUS_IS_OK(status)) {
220 DBG_DEBUG("failed: %s\n", nt_errstr(status));
221 return status;
224 status = reparse_buffer_check(out_data,
225 out_len,
226 &reparse_tag,
227 &reparse_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);
232 return status;
235 *_reparse_tag = reparse_tag;
236 *_out_data = out_data;
237 *_out_len = out_len;
239 return NT_STATUS_OK;
242 NTSTATUS fsctl_get_reparse_tag(struct files_struct *fsp,
243 uint32_t *_reparse_tag)
245 uint8_t *out_data = NULL;
246 uint32_t out_len;
247 NTSTATUS status;
249 status = fsctl_get_reparse_point(fsp,
250 talloc_tos(),
251 _reparse_tag,
252 &out_data,
253 UINT32_MAX,
254 &out_len);
255 TALLOC_FREE(out_data);
256 return status;
259 NTSTATUS fsctl_set_reparse_point(struct files_struct *fsp,
260 TALLOC_CTX *mem_ctx,
261 const uint8_t *in_data,
262 uint32_t in_len)
264 uint32_t reparse_tag;
265 const uint8_t *reparse_data = NULL;
266 size_t reparse_data_length;
267 uint32_t existing_tag;
268 NTSTATUS status;
269 uint32_t dos_mode;
270 int ret;
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,
280 in_len,
281 &reparse_tag,
282 &reparse_data,
283 &reparse_data_length);
284 if (!NT_STATUS_IS_OK(status)) {
285 DBG_DEBUG("check_reparse_data_buffer failed: %s\n",
286 nt_errstr(status));
287 return status;
290 DBG_DEBUG("reparse tag=%" PRIX32 ", length=%zu\n",
291 reparse_tag,
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
297 "\n",
298 existing_tag,
299 reparse_tag);
300 return NT_STATUS_IO_REPARSE_TAG_MISMATCH;
303 /* Store the data */
304 ret = SMB_VFS_FSETXATTR(
305 fsp, SAMBA_XATTR_REPARSE_ATTRIB, in_data, in_len, 0);
306 if (ret == -1) {
307 status = map_nt_error_from_unix(errno);
308 DBG_DEBUG("setxattr fail on %s - %s\n",
309 fsp_str_dbg(fsp),
310 strerror(errno));
311 return status;
315 * Files with reparse points don't have the ATTR_NORMAL bit
316 * set
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",
326 fsp_str_dbg(fsp),
327 nt_errstr(status));
328 return status;
331 fsp->fsp_name->st.cached_dos_attributes = dos_mode;
333 return NT_STATUS_OK;
336 NTSTATUS fsctl_del_reparse_point(struct files_struct *fsp,
337 TALLOC_CTX *mem_ctx,
338 const uint8_t *in_data,
339 uint32_t in_len)
341 uint32_t existing_tag;
342 uint32_t reparse_tag;
343 const uint8_t *reparse_data = NULL;
344 size_t reparse_data_length;
345 NTSTATUS status;
346 uint32_t dos_mode;
347 int ret;
349 status = fsctl_get_reparse_tag(fsp, &existing_tag);
350 if (!NT_STATUS_IS_OK(status)) {
351 return status;
354 status = reparse_buffer_check(in_data,
355 in_len,
356 &reparse_tag,
357 &reparse_data,
358 &reparse_data_length);
359 if (!NT_STATUS_IS_OK(status)) {
360 return 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
368 "\n",
369 existing_tag,
370 reparse_tag);
371 return NT_STATUS_IO_REPARSE_TAG_MISMATCH;
374 ret = SMB_VFS_FREMOVEXATTR(fsp, SAMBA_XATTR_REPARSE_ATTRIB);
375 if (ret == -1) {
376 status = map_nt_error_from_unix(errno);
377 DBG_DEBUG("removexattr fail on %s - %s\n",
378 fsp_str_dbg(fsp),
379 strerror(errno));
380 return status;
384 * Files with reparse points don't have the ATTR_NORMAL bit
385 * set
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",
394 fsp_str_dbg(fsp),
395 nt_errstr(status));
396 return status;
399 fsp->fsp_name->st.cached_dos_attributes = dos_mode;
401 return NT_STATUS_OK;