Line data Source code
1 : /*
2 : * Store streams in a separate subdirectory
3 : *
4 : * Copyright (C) Volker Lendecke, 2007
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "smbd/smbd.h"
22 : #include "system/filesys.h"
23 :
24 : #undef DBGC_CLASS
25 : #define DBGC_CLASS DBGC_VFS
26 :
27 : /*
28 : * Excerpt from a mail from tridge:
29 : *
30 : * Volker, what I'm thinking of is this:
31 : * /mount-point/.streams/XX/YY/aaaa.bbbb/namedstream1
32 : * /mount-point/.streams/XX/YY/aaaa.bbbb/namedstream2
33 : *
34 : * where XX/YY is a 2 level hash based on the fsid/inode. "aaaa.bbbb"
35 : * is the fsid/inode. "namedstreamX" is a file named after the stream
36 : * name.
37 : */
38 :
39 4252 : static uint32_t hash_fn(DATA_BLOB key)
40 : {
41 : uint32_t value; /* Used to compute the hash value. */
42 : uint32_t i; /* Used to cycle through random values. */
43 :
44 : /* Set the initial value from the key size. */
45 72284 : for (value = 0x238F13AF * key.length, i=0; i < key.length; i++)
46 68032 : value = (value + (key.data[i] << (i*5 % 24)));
47 :
48 4252 : return (1103515243 * value + 12345);
49 : }
50 :
51 : /*
52 : * With the hashing scheme based on the inode we need to protect against
53 : * streams showing up on files with re-used inodes. This can happen if we
54 : * create a stream directory from within Samba, and a local process or NFS
55 : * client deletes the file without deleting the streams directory. When the
56 : * inode is re-used and the stream directory is still around, the streams in
57 : * there would be show up as belonging to the new file.
58 : *
59 : * There are several workarounds for this, probably the easiest one is on
60 : * systems which have a true birthtime stat element: When the file has a later
61 : * birthtime than the streams directory, then we have to recreate the
62 : * directory.
63 : *
64 : * The other workaround is to somehow mark the file as generated by Samba with
65 : * something that a NFS client would not do. The closest one is a special
66 : * xattr value being set. On systems which do not support xattrs, it might be
67 : * an option to put in a special ACL entry for a non-existing group.
68 : */
69 :
70 110 : static bool file_is_valid(vfs_handle_struct *handle,
71 : const struct smb_filename *smb_fname)
72 : {
73 : char buf;
74 : NTSTATUS status;
75 110 : struct smb_filename *pathref = NULL;
76 : int ret;
77 :
78 110 : DEBUG(10, ("file_is_valid (%s) called\n", smb_fname->base_name));
79 :
80 165 : status = synthetic_pathref(talloc_tos(),
81 110 : handle->conn->cwd_fsp,
82 110 : smb_fname->base_name,
83 : NULL,
84 : NULL,
85 55 : smb_fname->twrp,
86 55 : smb_fname->flags,
87 : &pathref);
88 110 : if (!NT_STATUS_IS_OK(status)) {
89 0 : return false;
90 : }
91 110 : ret = SMB_VFS_FGETXATTR(pathref->fsp,
92 : SAMBA_XATTR_MARKER,
93 : &buf,
94 : sizeof(buf));
95 110 : if (ret != sizeof(buf)) {
96 0 : int saved_errno = errno;
97 0 : DBG_DEBUG("FGETXATTR failed: %s\n", strerror(saved_errno));
98 0 : TALLOC_FREE(pathref);
99 0 : errno = saved_errno;
100 0 : return false;
101 : }
102 :
103 110 : TALLOC_FREE(pathref);
104 :
105 110 : if (buf != '1') {
106 0 : DEBUG(10, ("got wrong buffer content: '%c'\n", buf));
107 0 : return false;
108 : }
109 :
110 110 : return true;
111 : }
112 :
113 : /*
114 : * Return the root of the stream directory. Can be
115 : * external to the share definition but by default
116 : * is "handle->conn->connectpath/.streams".
117 : *
118 : * Note that this is an *absolute* path, starting
119 : * with '/', so the dirfsp being used in the
120 : * calls below isn't looked at.
121 : */
122 :
123 4284 : static char *stream_rootdir(vfs_handle_struct *handle,
124 : TALLOC_CTX *ctx)
125 : {
126 3820 : const struct loadparm_substitution *lp_sub =
127 464 : loadparm_s3_global_substitution();
128 : char *tmp;
129 :
130 4284 : tmp = talloc_asprintf(ctx,
131 : "%s/.streams",
132 4284 : handle->conn->connectpath);
133 4284 : if (tmp == NULL) {
134 0 : errno = ENOMEM;
135 0 : return NULL;
136 : }
137 :
138 8104 : return lp_parm_substituted_string(ctx,
139 : lp_sub,
140 8104 : SNUM(handle->conn),
141 : "streams_depot",
142 : "directory",
143 : tmp);
144 : }
145 :
146 : /**
147 : * Given an smb_filename, determine the stream directory using the file's
148 : * base_name.
149 : */
150 4252 : static char *stream_dir(vfs_handle_struct *handle,
151 : const struct smb_filename *smb_fname,
152 : const SMB_STRUCT_STAT *base_sbuf, bool create_it)
153 : {
154 : uint32_t hash;
155 4252 : struct smb_filename *smb_fname_hash = NULL;
156 4252 : char *result = NULL;
157 : SMB_STRUCT_STAT base_sbuf_tmp;
158 4252 : char *tmp = NULL;
159 : uint8_t first, second;
160 : char *id_hex;
161 : struct file_id id;
162 : uint8_t id_buf[16];
163 : bool check_valid;
164 4252 : char *rootdir = NULL;
165 4252 : struct smb_filename *rootdir_fname = NULL;
166 4252 : struct smb_filename *tmp_fname = NULL;
167 : int ret;
168 :
169 4252 : check_valid = lp_parm_bool(SNUM(handle->conn),
170 : "streams_depot", "check_valid", true);
171 :
172 4252 : rootdir = stream_rootdir(handle,
173 : talloc_tos());
174 4252 : if (rootdir == NULL) {
175 0 : errno = ENOMEM;
176 0 : goto fail;
177 : }
178 :
179 4252 : rootdir_fname = synthetic_smb_fname(talloc_tos(),
180 : rootdir,
181 : NULL,
182 : NULL,
183 448 : smb_fname->twrp,
184 448 : smb_fname->flags);
185 4252 : if (rootdir_fname == NULL) {
186 0 : errno = ENOMEM;
187 0 : goto fail;
188 : }
189 :
190 : /* Stat the base file if it hasn't already been done. */
191 4252 : if (base_sbuf == NULL) {
192 : struct smb_filename *smb_fname_base;
193 :
194 36 : smb_fname_base = synthetic_smb_fname(
195 : talloc_tos(),
196 24 : smb_fname->base_name,
197 : NULL,
198 : NULL,
199 12 : smb_fname->twrp,
200 12 : smb_fname->flags);
201 24 : if (smb_fname_base == NULL) {
202 0 : errno = ENOMEM;
203 0 : goto fail;
204 : }
205 24 : if (SMB_VFS_NEXT_STAT(handle, smb_fname_base) == -1) {
206 0 : TALLOC_FREE(smb_fname_base);
207 0 : goto fail;
208 : }
209 24 : base_sbuf_tmp = smb_fname_base->st;
210 24 : TALLOC_FREE(smb_fname_base);
211 : } else {
212 4228 : base_sbuf_tmp = *base_sbuf;
213 : }
214 :
215 4252 : id = SMB_VFS_FILE_ID_CREATE(handle->conn, &base_sbuf_tmp);
216 :
217 4252 : push_file_id_16((char *)id_buf, &id);
218 :
219 4252 : hash = hash_fn(data_blob_const(id_buf, sizeof(id_buf)));
220 :
221 4252 : first = hash & 0xff;
222 4252 : second = (hash >> 8) & 0xff;
223 :
224 4252 : id_hex = hex_encode_talloc(talloc_tos(), id_buf, sizeof(id_buf));
225 :
226 4252 : if (id_hex == NULL) {
227 0 : errno = ENOMEM;
228 0 : goto fail;
229 : }
230 :
231 4252 : result = talloc_asprintf(talloc_tos(), "%s/%2.2X/%2.2X/%s", rootdir,
232 : first, second, id_hex);
233 :
234 4252 : TALLOC_FREE(id_hex);
235 :
236 4252 : if (result == NULL) {
237 0 : errno = ENOMEM;
238 0 : return NULL;
239 : }
240 :
241 4252 : smb_fname_hash = synthetic_smb_fname(talloc_tos(),
242 : result,
243 : NULL,
244 : NULL,
245 448 : smb_fname->twrp,
246 448 : smb_fname->flags);
247 4252 : if (smb_fname_hash == NULL) {
248 0 : errno = ENOMEM;
249 0 : goto fail;
250 : }
251 :
252 4252 : if (SMB_VFS_NEXT_STAT(handle, smb_fname_hash) == 0) {
253 110 : struct smb_filename *smb_fname_new = NULL;
254 : char *newname;
255 : bool delete_lost;
256 :
257 110 : if (!S_ISDIR(smb_fname_hash->st.st_ex_mode)) {
258 0 : errno = EINVAL;
259 0 : goto fail;
260 : }
261 :
262 220 : if (!check_valid ||
263 110 : file_is_valid(handle, smb_fname)) {
264 110 : return result;
265 : }
266 :
267 : /*
268 : * Someone has recreated a file under an existing inode
269 : * without deleting the streams directory.
270 : * Move it away or remove if streams_depot:delete_lost is set.
271 : */
272 :
273 0 : again:
274 0 : delete_lost = lp_parm_bool(SNUM(handle->conn), "streams_depot",
275 : "delete_lost", false);
276 :
277 0 : if (delete_lost) {
278 0 : DEBUG(3, ("Someone has recreated a file under an "
279 : "existing inode. Removing: %s\n",
280 : smb_fname_hash->base_name));
281 0 : recursive_rmdir(talloc_tos(), handle->conn,
282 : smb_fname_hash);
283 0 : SMB_VFS_NEXT_UNLINKAT(handle,
284 : handle->conn->cwd_fsp,
285 : smb_fname_hash,
286 : AT_REMOVEDIR);
287 : } else {
288 0 : newname = talloc_asprintf(talloc_tos(), "lost-%lu",
289 : random());
290 0 : DEBUG(3, ("Someone has recreated a file under an "
291 : "existing inode. Renaming: %s to: %s\n",
292 : smb_fname_hash->base_name,
293 : newname));
294 0 : if (newname == NULL) {
295 0 : errno = ENOMEM;
296 0 : goto fail;
297 : }
298 :
299 0 : smb_fname_new = synthetic_smb_fname(
300 : talloc_tos(),
301 : newname,
302 : NULL,
303 : NULL,
304 0 : smb_fname->twrp,
305 0 : smb_fname->flags);
306 0 : TALLOC_FREE(newname);
307 0 : if (smb_fname_new == NULL) {
308 0 : errno = ENOMEM;
309 0 : goto fail;
310 : }
311 :
312 0 : if (SMB_VFS_NEXT_RENAMEAT(handle,
313 : handle->conn->cwd_fsp,
314 : smb_fname_hash,
315 : handle->conn->cwd_fsp,
316 : smb_fname_new) == -1) {
317 0 : TALLOC_FREE(smb_fname_new);
318 0 : if ((errno == EEXIST) || (errno == ENOTEMPTY)) {
319 0 : goto again;
320 : }
321 0 : goto fail;
322 : }
323 :
324 0 : TALLOC_FREE(smb_fname_new);
325 : }
326 : }
327 :
328 4142 : if (!create_it) {
329 4128 : errno = ENOENT;
330 4128 : goto fail;
331 : }
332 :
333 14 : ret = SMB_VFS_NEXT_MKDIRAT(handle,
334 : handle->conn->cwd_fsp,
335 : rootdir_fname,
336 : 0755);
337 14 : if ((ret != 0) && (errno != EEXIST)) {
338 0 : goto fail;
339 : }
340 :
341 14 : tmp = talloc_asprintf(result, "%s/%2.2X", rootdir, first);
342 14 : if (tmp == NULL) {
343 0 : errno = ENOMEM;
344 0 : goto fail;
345 : }
346 :
347 14 : tmp_fname = synthetic_smb_fname(talloc_tos(),
348 : tmp,
349 : NULL,
350 : NULL,
351 7 : smb_fname->twrp,
352 7 : smb_fname->flags);
353 14 : if (tmp_fname == NULL) {
354 0 : errno = ENOMEM;
355 0 : goto fail;
356 : }
357 :
358 14 : ret = SMB_VFS_NEXT_MKDIRAT(handle,
359 : handle->conn->cwd_fsp,
360 : tmp_fname,
361 : 0755);
362 14 : if ((ret != 0) && (errno != EEXIST)) {
363 0 : goto fail;
364 : }
365 :
366 14 : TALLOC_FREE(tmp);
367 14 : TALLOC_FREE(tmp_fname);
368 :
369 14 : tmp = talloc_asprintf(result, "%s/%2.2X/%2.2X", rootdir, first,
370 : second);
371 14 : if (tmp == NULL) {
372 0 : errno = ENOMEM;
373 0 : goto fail;
374 : }
375 :
376 14 : tmp_fname = synthetic_smb_fname(talloc_tos(),
377 : tmp,
378 : NULL,
379 : NULL,
380 7 : smb_fname->twrp,
381 7 : smb_fname->flags);
382 14 : if (tmp_fname == NULL) {
383 0 : errno = ENOMEM;
384 0 : goto fail;
385 : }
386 :
387 14 : ret = SMB_VFS_NEXT_MKDIRAT(handle,
388 : handle->conn->cwd_fsp,
389 : tmp_fname,
390 : 0755);
391 14 : if ((ret != 0) && (errno != EEXIST)) {
392 0 : goto fail;
393 : }
394 :
395 14 : TALLOC_FREE(tmp);
396 14 : TALLOC_FREE(tmp_fname);
397 :
398 : /* smb_fname_hash is the struct smb_filename version of 'result' */
399 14 : ret = SMB_VFS_NEXT_MKDIRAT(handle,
400 : handle->conn->cwd_fsp,
401 : smb_fname_hash,
402 : 0755);
403 14 : if ((ret != 0) && (errno != EEXIST)) {
404 0 : goto fail;
405 : }
406 :
407 14 : TALLOC_FREE(rootdir_fname);
408 14 : TALLOC_FREE(rootdir);
409 14 : TALLOC_FREE(tmp_fname);
410 14 : TALLOC_FREE(smb_fname_hash);
411 14 : return result;
412 :
413 4128 : fail:
414 4128 : TALLOC_FREE(rootdir_fname);
415 4128 : TALLOC_FREE(rootdir);
416 4128 : TALLOC_FREE(tmp_fname);
417 4128 : TALLOC_FREE(smb_fname_hash);
418 4128 : TALLOC_FREE(result);
419 4128 : return NULL;
420 : }
421 : /**
422 : * Given a stream name, populate smb_fname_out with the actual location of the
423 : * stream.
424 : */
425 86 : static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
426 : const struct stat_ex *base_sbuf,
427 : const struct smb_filename *smb_fname,
428 : struct smb_filename **smb_fname_out,
429 : bool create_dir)
430 : {
431 : char *dirname, *stream_fname;
432 : const char *stype;
433 : NTSTATUS status;
434 :
435 86 : *smb_fname_out = NULL;
436 :
437 86 : stype = strchr_m(smb_fname->stream_name + 1, ':');
438 :
439 86 : if (stype) {
440 48 : if (strcasecmp_m(stype, ":$DATA") != 0) {
441 0 : return NT_STATUS_INVALID_PARAMETER;
442 : }
443 : }
444 :
445 86 : dirname = stream_dir(handle, smb_fname, base_sbuf, create_dir);
446 :
447 86 : if (dirname == NULL) {
448 6 : status = map_nt_error_from_unix(errno);
449 6 : goto fail;
450 : }
451 :
452 80 : stream_fname = talloc_asprintf(talloc_tos(), "%s/%s", dirname,
453 40 : smb_fname->stream_name);
454 :
455 80 : if (stream_fname == NULL) {
456 0 : status = NT_STATUS_NO_MEMORY;
457 0 : goto fail;
458 : }
459 :
460 80 : if (stype == NULL) {
461 : /* Append an explicit stream type if one wasn't specified. */
462 32 : stream_fname = talloc_asprintf(talloc_tos(), "%s:$DATA",
463 : stream_fname);
464 32 : if (stream_fname == NULL) {
465 0 : status = NT_STATUS_NO_MEMORY;
466 0 : goto fail;
467 : }
468 : } else {
469 : /* Normalize the stream type to upercase. */
470 48 : if (!strupper_m(strrchr_m(stream_fname, ':') + 1)) {
471 0 : status = NT_STATUS_INVALID_PARAMETER;
472 0 : goto fail;
473 : }
474 : }
475 :
476 80 : DEBUG(10, ("stream filename = %s\n", stream_fname));
477 :
478 : /* Create an smb_filename with stream_name == NULL. */
479 80 : *smb_fname_out = synthetic_smb_fname(talloc_tos(),
480 : stream_fname,
481 : NULL,
482 : NULL,
483 40 : smb_fname->twrp,
484 40 : smb_fname->flags);
485 80 : if (*smb_fname_out == NULL) {
486 0 : return NT_STATUS_NO_MEMORY;
487 : }
488 :
489 80 : return NT_STATUS_OK;
490 :
491 6 : fail:
492 6 : DEBUG(5, ("stream_name failed: %s\n", strerror(errno)));
493 6 : TALLOC_FREE(*smb_fname_out);
494 6 : return status;
495 : }
496 :
497 2801 : static NTSTATUS walk_streams(vfs_handle_struct *handle,
498 : struct smb_filename *smb_fname_base,
499 : char **pdirname,
500 : bool (*fn)(const struct smb_filename *dirname,
501 : const char *dirent,
502 : void *private_data),
503 : void *private_data)
504 : {
505 : char *dirname;
506 2801 : char *rootdir = NULL;
507 2801 : char *orig_connectpath = NULL;
508 2801 : struct smb_filename *dir_smb_fname = NULL;
509 2801 : struct smb_Dir *dir_hnd = NULL;
510 2801 : const char *dname = NULL;
511 2801 : long offset = 0;
512 2801 : char *talloced = NULL;
513 : NTSTATUS status;
514 :
515 2801 : dirname = stream_dir(handle, smb_fname_base, &smb_fname_base->st,
516 : false);
517 :
518 2801 : if (dirname == NULL) {
519 2769 : if (errno == ENOENT) {
520 : /*
521 : * no stream around
522 : */
523 2769 : return NT_STATUS_OK;
524 : }
525 0 : return map_nt_error_from_unix(errno);
526 : }
527 :
528 32 : DEBUG(10, ("walk_streams: dirname=%s\n", dirname));
529 :
530 32 : dir_smb_fname = synthetic_smb_fname(talloc_tos(),
531 : dirname,
532 : NULL,
533 : NULL,
534 : smb_fname_base->twrp,
535 : smb_fname_base->flags);
536 32 : if (dir_smb_fname == NULL) {
537 0 : TALLOC_FREE(dirname);
538 0 : return NT_STATUS_NO_MEMORY;
539 : }
540 :
541 : /*
542 : * For OpenDir to succeed if the stream rootdir is outside
543 : * the share path, we must temporarily swap out the connect
544 : * path for this share. We're dealing with absolute paths
545 : * here so we don't care about chdir calls.
546 : */
547 32 : rootdir = stream_rootdir(handle, talloc_tos());
548 32 : if (rootdir == NULL) {
549 0 : TALLOC_FREE(dir_smb_fname);
550 0 : TALLOC_FREE(dirname);
551 0 : return NT_STATUS_NO_MEMORY;
552 : }
553 :
554 32 : orig_connectpath = handle->conn->connectpath;
555 32 : handle->conn->connectpath = rootdir;
556 :
557 32 : status = OpenDir(
558 32 : talloc_tos(), handle->conn, dir_smb_fname, NULL, 0, &dir_hnd);
559 32 : if (!NT_STATUS_IS_OK(status)) {
560 0 : handle->conn->connectpath = orig_connectpath;
561 0 : TALLOC_FREE(rootdir);
562 0 : TALLOC_FREE(dir_smb_fname);
563 0 : TALLOC_FREE(dirname);
564 0 : return status;
565 : }
566 :
567 144 : while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
568 64 : != NULL)
569 : {
570 96 : if (ISDOT(dname) || ISDOTDOT(dname)) {
571 64 : TALLOC_FREE(talloced);
572 64 : continue;
573 : }
574 :
575 32 : DBG_DEBUG("dirent=%s\n", dname);
576 :
577 32 : if (!fn(dir_smb_fname, dname, private_data)) {
578 0 : TALLOC_FREE(talloced);
579 0 : break;
580 : }
581 32 : TALLOC_FREE(talloced);
582 : }
583 :
584 : /* Restore the original connectpath. */
585 32 : handle->conn->connectpath = orig_connectpath;
586 32 : TALLOC_FREE(rootdir);
587 32 : TALLOC_FREE(dir_smb_fname);
588 32 : TALLOC_FREE(dir_hnd);
589 :
590 32 : if (pdirname != NULL) {
591 0 : *pdirname = dirname;
592 : }
593 : else {
594 32 : TALLOC_FREE(dirname);
595 : }
596 :
597 32 : return NT_STATUS_OK;
598 : }
599 :
600 143013 : static int streams_depot_stat(vfs_handle_struct *handle,
601 : struct smb_filename *smb_fname)
602 : {
603 143013 : struct smb_filename *smb_fname_stream = NULL;
604 : NTSTATUS status;
605 143013 : int ret = -1;
606 :
607 143013 : DEBUG(10, ("streams_depot_stat called for [%s]\n",
608 : smb_fname_str_dbg(smb_fname)));
609 :
610 143013 : if (!is_named_stream(smb_fname)) {
611 143001 : return SMB_VFS_NEXT_STAT(handle, smb_fname);
612 : }
613 :
614 : /* Stat the actual stream now. */
615 12 : status = stream_smb_fname(
616 : handle, NULL, smb_fname, &smb_fname_stream, false);
617 12 : if (!NT_STATUS_IS_OK(status)) {
618 0 : ret = -1;
619 0 : errno = map_errno_from_nt_status(status);
620 0 : goto done;
621 : }
622 :
623 12 : ret = SMB_VFS_NEXT_STAT(handle, smb_fname_stream);
624 :
625 : /* Update the original smb_fname with the stat info. */
626 12 : smb_fname->st = smb_fname_stream->st;
627 12 : done:
628 12 : TALLOC_FREE(smb_fname_stream);
629 12 : return ret;
630 : }
631 :
632 :
633 :
634 1896 : static int streams_depot_lstat(vfs_handle_struct *handle,
635 : struct smb_filename *smb_fname)
636 : {
637 1896 : struct smb_filename *smb_fname_stream = NULL;
638 : NTSTATUS status;
639 1896 : int ret = -1;
640 :
641 1896 : DEBUG(10, ("streams_depot_lstat called for [%s]\n",
642 : smb_fname_str_dbg(smb_fname)));
643 :
644 1896 : if (!is_named_stream(smb_fname)) {
645 1896 : return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
646 : }
647 :
648 : /* Stat the actual stream now. */
649 0 : status = stream_smb_fname(
650 : handle, NULL, smb_fname, &smb_fname_stream, false);
651 0 : if (!NT_STATUS_IS_OK(status)) {
652 0 : ret = -1;
653 0 : errno = map_errno_from_nt_status(status);
654 0 : goto done;
655 : }
656 :
657 0 : ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname_stream);
658 :
659 0 : done:
660 0 : TALLOC_FREE(smb_fname_stream);
661 0 : return ret;
662 : }
663 :
664 153981 : static int streams_depot_openat(struct vfs_handle_struct *handle,
665 : const struct files_struct *dirfsp,
666 : const struct smb_filename *smb_fname,
667 : struct files_struct *fsp,
668 : const struct vfs_open_how *how)
669 : {
670 153981 : struct smb_filename *smb_fname_stream = NULL;
671 153981 : struct files_struct *fspcwd = NULL;
672 : NTSTATUS status;
673 : bool create_it;
674 153981 : int ret = -1;
675 :
676 153981 : if (!is_named_stream(smb_fname)) {
677 153919 : return SMB_VFS_NEXT_OPENAT(handle,
678 : dirfsp,
679 : smb_fname,
680 : fsp,
681 : how);
682 : }
683 :
684 62 : if (how->resolve != 0) {
685 0 : errno = ENOSYS;
686 0 : return -1;
687 : }
688 :
689 62 : SMB_ASSERT(fsp_is_alternate_stream(fsp));
690 62 : SMB_ASSERT(dirfsp == NULL);
691 62 : SMB_ASSERT(VALID_STAT(fsp->base_fsp->fsp_name->st));
692 :
693 62 : create_it = (how->flags & O_CREAT);
694 :
695 : /* Determine the stream name, and then open it. */
696 93 : status = stream_smb_fname(
697 : handle,
698 62 : &fsp->base_fsp->fsp_name->st,
699 62 : fsp->fsp_name,
700 : &smb_fname_stream,
701 : create_it);
702 62 : if (!NT_STATUS_IS_OK(status)) {
703 6 : ret = -1;
704 6 : errno = map_errno_from_nt_status(status);
705 6 : goto done;
706 : }
707 :
708 56 : if (create_it) {
709 21 : bool check_valid = lp_parm_bool(
710 21 : SNUM(handle->conn),
711 : "streams_depot",
712 : "check_valid",
713 : true);
714 :
715 14 : if (check_valid) {
716 14 : char buf = '1';
717 :
718 14 : DBG_DEBUG("marking file %s as valid\n",
719 : fsp->base_fsp->fsp_name->base_name);
720 :
721 14 : ret = SMB_VFS_FSETXATTR(
722 : fsp->base_fsp,
723 : SAMBA_XATTR_MARKER,
724 : &buf,
725 : sizeof(buf),
726 : 0);
727 :
728 14 : if (ret == -1) {
729 0 : DBG_DEBUG("FSETXATTR failed: %s\n",
730 : strerror(errno));
731 0 : return -1;
732 : }
733 : }
734 : }
735 :
736 56 : status = vfs_at_fspcwd(talloc_tos(), handle->conn, &fspcwd);
737 56 : if (!NT_STATUS_IS_OK(status)) {
738 0 : ret = -1;
739 0 : errno = map_errno_from_nt_status(status);
740 0 : goto done;
741 : }
742 :
743 56 : ret = SMB_VFS_NEXT_OPENAT(handle,
744 : fspcwd,
745 : smb_fname_stream,
746 : fsp,
747 : how);
748 :
749 62 : done:
750 62 : TALLOC_FREE(smb_fname_stream);
751 62 : TALLOC_FREE(fspcwd);
752 62 : return ret;
753 : }
754 :
755 573 : static int streams_depot_unlink_internal(vfs_handle_struct *handle,
756 : struct files_struct *dirfsp,
757 : const struct smb_filename *smb_fname,
758 : int flags)
759 : {
760 573 : struct smb_filename *full_fname = NULL;
761 573 : char *dirname = NULL;
762 573 : int ret = -1;
763 :
764 573 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
765 : dirfsp,
766 : smb_fname);
767 573 : if (full_fname == NULL) {
768 0 : return -1;
769 : }
770 :
771 573 : DEBUG(10, ("streams_depot_unlink called for %s\n",
772 : smb_fname_str_dbg(full_fname)));
773 :
774 : /* If there is a valid stream, just unlink the stream and return. */
775 573 : if (is_named_stream(full_fname)) {
776 12 : struct smb_filename *smb_fname_stream = NULL;
777 : NTSTATUS status;
778 :
779 12 : status = stream_smb_fname(
780 : handle, NULL, full_fname, &smb_fname_stream, false);
781 12 : TALLOC_FREE(full_fname);
782 12 : if (!NT_STATUS_IS_OK(status)) {
783 0 : errno = map_errno_from_nt_status(status);
784 0 : return -1;
785 : }
786 :
787 12 : ret = SMB_VFS_NEXT_UNLINKAT(handle,
788 : dirfsp->conn->cwd_fsp,
789 : smb_fname_stream,
790 : 0);
791 :
792 12 : TALLOC_FREE(smb_fname_stream);
793 12 : return ret;
794 : }
795 :
796 : /*
797 : * We potentially need to delete the per-inode streams directory
798 : */
799 :
800 561 : if (full_fname->flags & SMB_FILENAME_POSIX_PATH) {
801 0 : ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
802 : } else {
803 561 : ret = SMB_VFS_NEXT_STAT(handle, full_fname);
804 561 : if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
805 0 : if (VALID_STAT(smb_fname->st) &&
806 0 : S_ISLNK(smb_fname->st.st_ex_mode)) {
807 : /*
808 : * Original name was a link - Could be
809 : * trying to remove a dangling symlink.
810 : */
811 0 : ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
812 : }
813 : }
814 : }
815 561 : if (ret == -1) {
816 0 : TALLOC_FREE(full_fname);
817 0 : return -1;
818 : }
819 :
820 : /*
821 : * We know the unlink should succeed as the ACL
822 : * check is already done in the caller. Remove the
823 : * file *after* the streams.
824 : */
825 561 : dirname = stream_dir(handle,
826 : full_fname,
827 561 : &full_fname->st,
828 : false);
829 561 : TALLOC_FREE(full_fname);
830 561 : if (dirname != NULL) {
831 8 : struct smb_filename *smb_fname_dir = NULL;
832 :
833 8 : smb_fname_dir = synthetic_smb_fname(talloc_tos(),
834 : dirname,
835 : NULL,
836 : NULL,
837 4 : smb_fname->twrp,
838 4 : smb_fname->flags);
839 8 : if (smb_fname_dir == NULL) {
840 0 : TALLOC_FREE(dirname);
841 0 : errno = ENOMEM;
842 0 : return -1;
843 : }
844 :
845 8 : SMB_VFS_NEXT_UNLINKAT(handle,
846 : dirfsp->conn->cwd_fsp,
847 : smb_fname_dir,
848 : AT_REMOVEDIR);
849 8 : TALLOC_FREE(smb_fname_dir);
850 8 : TALLOC_FREE(dirname);
851 : }
852 :
853 561 : ret = SMB_VFS_NEXT_UNLINKAT(handle,
854 : dirfsp,
855 : smb_fname,
856 : flags);
857 561 : return ret;
858 : }
859 :
860 804 : static int streams_depot_rmdir_internal(vfs_handle_struct *handle,
861 : struct files_struct *dirfsp,
862 : const struct smb_filename *smb_fname)
863 : {
864 804 : struct smb_filename *full_fname = NULL;
865 804 : struct smb_filename *smb_fname_base = NULL;
866 804 : int ret = -1;
867 :
868 804 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
869 : dirfsp,
870 : smb_fname);
871 804 : if (full_fname == NULL) {
872 0 : return -1;
873 : }
874 :
875 804 : DBG_DEBUG("called for %s\n", full_fname->base_name);
876 :
877 : /*
878 : * We potentially need to delete the per-inode streams directory
879 : */
880 :
881 1565 : smb_fname_base = synthetic_smb_fname(talloc_tos(),
882 804 : full_fname->base_name,
883 : NULL,
884 : NULL,
885 : full_fname->twrp,
886 : full_fname->flags);
887 804 : TALLOC_FREE(full_fname);
888 804 : if (smb_fname_base == NULL) {
889 0 : errno = ENOMEM;
890 0 : return -1;
891 : }
892 :
893 804 : if (smb_fname_base->flags & SMB_FILENAME_POSIX_PATH) {
894 0 : ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname_base);
895 : } else {
896 804 : ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
897 : }
898 :
899 804 : if (ret == -1) {
900 0 : TALLOC_FREE(smb_fname_base);
901 0 : return -1;
902 : }
903 :
904 : /*
905 : * We know the rmdir should succeed as the ACL
906 : * check is already done in the caller. Remove the
907 : * directory *after* the streams.
908 : */
909 : {
910 804 : char *dirname = stream_dir(handle, smb_fname_base,
911 804 : &smb_fname_base->st, false);
912 :
913 804 : if (dirname != NULL) {
914 2 : struct smb_filename *smb_fname_dir =
915 4 : synthetic_smb_fname(talloc_tos(),
916 : dirname,
917 : NULL,
918 : NULL,
919 2 : smb_fname->twrp,
920 2 : smb_fname->flags);
921 4 : if (smb_fname_dir == NULL) {
922 0 : TALLOC_FREE(smb_fname_base);
923 0 : TALLOC_FREE(dirname);
924 0 : errno = ENOMEM;
925 0 : return -1;
926 : }
927 4 : SMB_VFS_NEXT_UNLINKAT(handle,
928 : dirfsp->conn->cwd_fsp,
929 : smb_fname_dir,
930 : AT_REMOVEDIR);
931 4 : TALLOC_FREE(smb_fname_dir);
932 : }
933 804 : TALLOC_FREE(dirname);
934 : }
935 :
936 804 : ret = SMB_VFS_NEXT_UNLINKAT(handle,
937 : dirfsp,
938 : smb_fname,
939 : AT_REMOVEDIR);
940 804 : TALLOC_FREE(smb_fname_base);
941 804 : return ret;
942 : }
943 :
944 1377 : static int streams_depot_unlinkat(vfs_handle_struct *handle,
945 : struct files_struct *dirfsp,
946 : const struct smb_filename *smb_fname,
947 : int flags)
948 : {
949 : int ret;
950 1377 : if (flags & AT_REMOVEDIR) {
951 804 : ret = streams_depot_rmdir_internal(handle,
952 : dirfsp,
953 : smb_fname);
954 : } else {
955 573 : ret = streams_depot_unlink_internal(handle,
956 : dirfsp,
957 : smb_fname,
958 : flags);
959 : }
960 1377 : return ret;
961 : }
962 :
963 20 : static int streams_depot_renameat(vfs_handle_struct *handle,
964 : files_struct *srcfsp,
965 : const struct smb_filename *smb_fname_src,
966 : files_struct *dstfsp,
967 : const struct smb_filename *smb_fname_dst)
968 : {
969 20 : struct smb_filename *smb_fname_src_stream = NULL;
970 20 : struct smb_filename *smb_fname_dst_stream = NULL;
971 20 : struct smb_filename *full_src = NULL;
972 20 : struct smb_filename *full_dst = NULL;
973 : bool src_is_stream, dst_is_stream;
974 : NTSTATUS status;
975 20 : int ret = -1;
976 :
977 20 : DEBUG(10, ("streams_depot_renameat called for %s => %s\n",
978 : smb_fname_str_dbg(smb_fname_src),
979 : smb_fname_str_dbg(smb_fname_dst)));
980 :
981 20 : src_is_stream = is_ntfs_stream_smb_fname(smb_fname_src);
982 20 : dst_is_stream = is_ntfs_stream_smb_fname(smb_fname_dst);
983 :
984 20 : if (!src_is_stream && !dst_is_stream) {
985 20 : return SMB_VFS_NEXT_RENAMEAT(handle,
986 : srcfsp,
987 : smb_fname_src,
988 : dstfsp,
989 : smb_fname_dst);
990 : }
991 :
992 : /* for now don't allow renames from or to the default stream */
993 0 : if (is_ntfs_default_stream_smb_fname(smb_fname_src) ||
994 0 : is_ntfs_default_stream_smb_fname(smb_fname_dst)) {
995 0 : errno = ENOSYS;
996 0 : goto done;
997 : }
998 :
999 0 : full_src = full_path_from_dirfsp_atname(talloc_tos(),
1000 : srcfsp,
1001 : smb_fname_src);
1002 0 : if (full_src == NULL) {
1003 0 : errno = ENOMEM;
1004 0 : goto done;
1005 : }
1006 :
1007 0 : full_dst = full_path_from_dirfsp_atname(talloc_tos(),
1008 : dstfsp,
1009 : smb_fname_dst);
1010 0 : if (full_dst == NULL) {
1011 0 : errno = ENOMEM;
1012 0 : goto done;
1013 : }
1014 :
1015 0 : status = stream_smb_fname(
1016 : handle, NULL, full_src, &smb_fname_src_stream, false);
1017 0 : if (!NT_STATUS_IS_OK(status)) {
1018 0 : errno = map_errno_from_nt_status(status);
1019 0 : goto done;
1020 : }
1021 :
1022 0 : status = stream_smb_fname(
1023 : handle, NULL, full_dst, &smb_fname_dst_stream, false);
1024 0 : if (!NT_STATUS_IS_OK(status)) {
1025 0 : errno = map_errno_from_nt_status(status);
1026 0 : goto done;
1027 : }
1028 :
1029 : /*
1030 : * We must use handle->conn->cwd_fsp as
1031 : * srcfsp and dstfsp directory handles here
1032 : * as we used the full pathname from the cwd dir
1033 : * to calculate the streams directory and filename
1034 : * within.
1035 : */
1036 0 : ret = SMB_VFS_NEXT_RENAMEAT(handle,
1037 : handle->conn->cwd_fsp,
1038 : smb_fname_src_stream,
1039 : handle->conn->cwd_fsp,
1040 : smb_fname_dst_stream);
1041 :
1042 0 : done:
1043 0 : TALLOC_FREE(smb_fname_src_stream);
1044 0 : TALLOC_FREE(smb_fname_dst_stream);
1045 0 : return ret;
1046 : }
1047 :
1048 32 : static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
1049 : struct stream_struct **streams,
1050 : const char *name, off_t size,
1051 : off_t alloc_size)
1052 : {
1053 : struct stream_struct *tmp;
1054 :
1055 32 : tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
1056 : (*num_streams)+1);
1057 32 : if (tmp == NULL) {
1058 0 : return false;
1059 : }
1060 :
1061 32 : tmp[*num_streams].name = talloc_strdup(tmp, name);
1062 32 : if (tmp[*num_streams].name == NULL) {
1063 0 : return false;
1064 : }
1065 :
1066 32 : tmp[*num_streams].size = size;
1067 32 : tmp[*num_streams].alloc_size = alloc_size;
1068 :
1069 32 : *streams = tmp;
1070 32 : *num_streams += 1;
1071 32 : return true;
1072 : }
1073 :
1074 : struct streaminfo_state {
1075 : TALLOC_CTX *mem_ctx;
1076 : vfs_handle_struct *handle;
1077 : unsigned int num_streams;
1078 : struct stream_struct *streams;
1079 : NTSTATUS status;
1080 : };
1081 :
1082 32 : static bool collect_one_stream(const struct smb_filename *dirfname,
1083 : const char *dirent,
1084 : void *private_data)
1085 : {
1086 32 : const char *dirname = dirfname->base_name;
1087 32 : struct streaminfo_state *state =
1088 : (struct streaminfo_state *)private_data;
1089 32 : struct smb_filename *smb_fname = NULL;
1090 32 : char *sname = NULL;
1091 : bool ret;
1092 :
1093 32 : sname = talloc_asprintf(talloc_tos(), "%s/%s", dirname, dirent);
1094 32 : if (sname == NULL) {
1095 0 : state->status = NT_STATUS_NO_MEMORY;
1096 0 : ret = false;
1097 0 : goto out;
1098 : }
1099 :
1100 32 : smb_fname = synthetic_smb_fname(talloc_tos(),
1101 : sname,
1102 : NULL,
1103 : NULL,
1104 16 : dirfname->twrp,
1105 : 0);
1106 32 : if (smb_fname == NULL) {
1107 0 : state->status = NT_STATUS_NO_MEMORY;
1108 0 : ret = false;
1109 0 : goto out;
1110 : }
1111 :
1112 32 : if (SMB_VFS_NEXT_STAT(state->handle, smb_fname) == -1) {
1113 0 : DEBUG(10, ("Could not stat %s: %s\n", sname,
1114 : strerror(errno)));
1115 0 : ret = true;
1116 0 : goto out;
1117 : }
1118 :
1119 32 : if (!add_one_stream(state->mem_ctx,
1120 : &state->num_streams, &state->streams,
1121 : dirent, smb_fname->st.st_ex_size,
1122 32 : SMB_VFS_GET_ALLOC_SIZE(state->handle->conn, NULL,
1123 : &smb_fname->st))) {
1124 0 : state->status = NT_STATUS_NO_MEMORY;
1125 0 : ret = false;
1126 0 : goto out;
1127 : }
1128 :
1129 32 : ret = true;
1130 32 : out:
1131 32 : TALLOC_FREE(sname);
1132 32 : TALLOC_FREE(smb_fname);
1133 32 : return ret;
1134 : }
1135 :
1136 2801 : static NTSTATUS streams_depot_fstreaminfo(vfs_handle_struct *handle,
1137 : struct files_struct *fsp,
1138 : TALLOC_CTX *mem_ctx,
1139 : unsigned int *pnum_streams,
1140 : struct stream_struct **pstreams)
1141 : {
1142 2801 : struct smb_filename *smb_fname_base = NULL;
1143 : int ret;
1144 : NTSTATUS status;
1145 : struct streaminfo_state state;
1146 :
1147 7867 : smb_fname_base = synthetic_smb_fname(talloc_tos(),
1148 2801 : fsp->fsp_name->base_name,
1149 : NULL,
1150 : NULL,
1151 2801 : fsp->fsp_name->twrp,
1152 2801 : fsp->fsp_name->flags);
1153 2801 : if (smb_fname_base == NULL) {
1154 0 : return NT_STATUS_NO_MEMORY;
1155 : }
1156 :
1157 2801 : ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &smb_fname_base->st);
1158 2801 : if (ret == -1) {
1159 0 : status = map_nt_error_from_unix(errno);
1160 0 : goto out;
1161 : }
1162 :
1163 2801 : state.streams = *pstreams;
1164 2801 : state.num_streams = *pnum_streams;
1165 2801 : state.mem_ctx = mem_ctx;
1166 2801 : state.handle = handle;
1167 2801 : state.status = NT_STATUS_OK;
1168 :
1169 2801 : status = walk_streams(handle,
1170 : smb_fname_base,
1171 : NULL,
1172 : collect_one_stream,
1173 : &state);
1174 :
1175 2801 : if (!NT_STATUS_IS_OK(status)) {
1176 0 : TALLOC_FREE(state.streams);
1177 0 : goto out;
1178 : }
1179 :
1180 2801 : if (!NT_STATUS_IS_OK(state.status)) {
1181 0 : TALLOC_FREE(state.streams);
1182 0 : status = state.status;
1183 0 : goto out;
1184 : }
1185 :
1186 2801 : *pnum_streams = state.num_streams;
1187 2801 : *pstreams = state.streams;
1188 2801 : status = SMB_VFS_NEXT_FSTREAMINFO(handle,
1189 : fsp->base_fsp ? fsp->base_fsp : fsp,
1190 : mem_ctx,
1191 : pnum_streams,
1192 : pstreams);
1193 :
1194 2801 : out:
1195 2801 : TALLOC_FREE(smb_fname_base);
1196 2801 : return status;
1197 : }
1198 :
1199 3893 : static uint32_t streams_depot_fs_capabilities(struct vfs_handle_struct *handle,
1200 : enum timestamp_set_resolution *p_ts_res)
1201 : {
1202 3893 : return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_NAMED_STREAMS;
1203 : }
1204 :
1205 : static struct vfs_fn_pointers vfs_streams_depot_fns = {
1206 : .fs_capabilities_fn = streams_depot_fs_capabilities,
1207 : .openat_fn = streams_depot_openat,
1208 : .stat_fn = streams_depot_stat,
1209 : .lstat_fn = streams_depot_lstat,
1210 : .unlinkat_fn = streams_depot_unlinkat,
1211 : .renameat_fn = streams_depot_renameat,
1212 : .fstreaminfo_fn = streams_depot_fstreaminfo,
1213 : };
1214 :
1215 : static_decl_vfs;
1216 4875 : NTSTATUS vfs_streams_depot_init(TALLOC_CTX *ctx)
1217 : {
1218 4875 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "streams_depot",
1219 : &vfs_streams_depot_fns);
1220 : }
|