s3:smbd: fix NULL dereference in case of readlink failure
[Samba.git] / source3 / modules / vfs_streams_depot.c
blob566d34996c1fa830edfb1eb1affdb9feb7893a06
1 /*
2 * Store streams in a separate subdirectory
4 * Copyright (C) Volker Lendecke, 2007
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.
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.
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/>.
20 #include "includes.h"
21 #include "smbd/smbd.h"
22 #include "system/filesys.h"
23 #include "source3/smbd/dir.h"
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_VFS
29 * Excerpt from a mail from tridge:
31 * Volker, what I'm thinking of is this:
32 * /mount-point/.streams/XX/YY/aaaa.bbbb/namedstream1
33 * /mount-point/.streams/XX/YY/aaaa.bbbb/namedstream2
35 * where XX/YY is a 2 level hash based on the fsid/inode. "aaaa.bbbb"
36 * is the fsid/inode. "namedstreamX" is a file named after the stream
37 * name.
40 static uint32_t hash_fn(DATA_BLOB key)
42 uint32_t value; /* Used to compute the hash value. */
43 uint32_t i; /* Used to cycle through random values. */
45 /* Set the initial value from the key size. */
46 for (value = 0x238F13AF * key.length, i=0; i < key.length; i++)
47 value = (value + (key.data[i] << (i*5 % 24)));
49 return (1103515243 * value + 12345);
53 * With the hashing scheme based on the inode we need to protect against
54 * streams showing up on files with re-used inodes. This can happen if we
55 * create a stream directory from within Samba, and a local process or NFS
56 * client deletes the file without deleting the streams directory. When the
57 * inode is re-used and the stream directory is still around, the streams in
58 * there would be show up as belonging to the new file.
60 * There are several workarounds for this, probably the easiest one is on
61 * systems which have a true birthtime stat element: When the file has a later
62 * birthtime than the streams directory, then we have to recreate the
63 * directory.
65 * The other workaround is to somehow mark the file as generated by Samba with
66 * something that a NFS client would not do. The closest one is a special
67 * xattr value being set. On systems which do not support xattrs, it might be
68 * an option to put in a special ACL entry for a non-existing group.
71 static bool file_is_valid(vfs_handle_struct *handle,
72 const struct smb_filename *smb_fname)
74 char buf;
75 NTSTATUS status;
76 struct smb_filename *pathref = NULL;
77 int ret;
79 DEBUG(10, ("file_is_valid (%s) called\n", smb_fname->base_name));
81 status = synthetic_pathref(talloc_tos(),
82 handle->conn->cwd_fsp,
83 smb_fname->base_name,
84 NULL,
85 NULL,
86 smb_fname->twrp,
87 smb_fname->flags,
88 &pathref);
89 if (!NT_STATUS_IS_OK(status)) {
90 return false;
92 ret = SMB_VFS_FGETXATTR(pathref->fsp,
93 SAMBA_XATTR_MARKER,
94 &buf,
95 sizeof(buf));
96 if (ret != sizeof(buf)) {
97 int saved_errno = errno;
98 DBG_DEBUG("FGETXATTR failed: %s\n", strerror(saved_errno));
99 TALLOC_FREE(pathref);
100 errno = saved_errno;
101 return false;
104 TALLOC_FREE(pathref);
106 if (buf != '1') {
107 DEBUG(10, ("got wrong buffer content: '%c'\n", buf));
108 return false;
111 return true;
115 * Return the root of the stream directory. Can be
116 * external to the share definition but by default
117 * is "handle->conn->connectpath/.streams".
119 * Note that this is an *absolute* path, starting
120 * with '/', so the dirfsp being used in the
121 * calls below isn't looked at.
124 static char *stream_rootdir(vfs_handle_struct *handle,
125 TALLOC_CTX *ctx)
127 const struct loadparm_substitution *lp_sub =
128 loadparm_s3_global_substitution();
129 char *tmp;
131 tmp = talloc_asprintf(ctx,
132 "%s/.streams",
133 handle->conn->connectpath);
134 if (tmp == NULL) {
135 errno = ENOMEM;
136 return NULL;
139 return lp_parm_substituted_string(ctx,
140 lp_sub,
141 SNUM(handle->conn),
142 "streams_depot",
143 "directory",
144 tmp);
148 * Given an smb_filename, determine the stream directory using the file's
149 * base_name.
151 static char *stream_dir(vfs_handle_struct *handle,
152 const struct smb_filename *smb_fname,
153 const SMB_STRUCT_STAT *base_sbuf, bool create_it)
155 uint32_t hash;
156 struct smb_filename *smb_fname_hash = NULL;
157 char *result = NULL;
158 SMB_STRUCT_STAT base_sbuf_tmp;
159 char *tmp = NULL;
160 uint8_t first, second;
161 char *id_hex;
162 struct file_id id;
163 uint8_t id_buf[16];
164 bool check_valid;
165 char *rootdir = NULL;
166 struct smb_filename *rootdir_fname = NULL;
167 struct smb_filename *tmp_fname = NULL;
168 struct vfs_rename_how rhow = { .flags = 0, };
169 int ret;
171 check_valid = lp_parm_bool(SNUM(handle->conn),
172 "streams_depot", "check_valid", true);
174 rootdir = stream_rootdir(handle,
175 talloc_tos());
176 if (rootdir == NULL) {
177 errno = ENOMEM;
178 goto fail;
181 rootdir_fname = synthetic_smb_fname(talloc_tos(),
182 rootdir,
183 NULL,
184 NULL,
185 smb_fname->twrp,
186 smb_fname->flags);
187 if (rootdir_fname == NULL) {
188 errno = ENOMEM;
189 goto fail;
192 /* Stat the base file if it hasn't already been done. */
193 if (base_sbuf == NULL) {
194 struct smb_filename *smb_fname_base;
196 smb_fname_base = synthetic_smb_fname(
197 talloc_tos(),
198 smb_fname->base_name,
199 NULL,
200 NULL,
201 smb_fname->twrp,
202 smb_fname->flags);
203 if (smb_fname_base == NULL) {
204 errno = ENOMEM;
205 goto fail;
207 if (SMB_VFS_NEXT_STAT(handle, smb_fname_base) == -1) {
208 TALLOC_FREE(smb_fname_base);
209 goto fail;
211 base_sbuf_tmp = smb_fname_base->st;
212 TALLOC_FREE(smb_fname_base);
213 } else {
214 base_sbuf_tmp = *base_sbuf;
217 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &base_sbuf_tmp);
219 push_file_id_16(id_buf, &id);
221 hash = hash_fn(data_blob_const(id_buf, sizeof(id_buf)));
223 first = hash & 0xff;
224 second = (hash >> 8) & 0xff;
226 id_hex = hex_encode_talloc(talloc_tos(), id_buf, sizeof(id_buf));
228 if (id_hex == NULL) {
229 errno = ENOMEM;
230 goto fail;
233 result = talloc_asprintf(talloc_tos(), "%s/%2.2X/%2.2X/%s", rootdir,
234 first, second, id_hex);
236 TALLOC_FREE(id_hex);
238 if (result == NULL) {
239 errno = ENOMEM;
240 return NULL;
243 smb_fname_hash = synthetic_smb_fname(talloc_tos(),
244 result,
245 NULL,
246 NULL,
247 smb_fname->twrp,
248 smb_fname->flags);
249 if (smb_fname_hash == NULL) {
250 errno = ENOMEM;
251 goto fail;
254 if (SMB_VFS_NEXT_STAT(handle, smb_fname_hash) == 0) {
255 struct smb_filename *smb_fname_new = NULL;
256 char *newname;
257 bool delete_lost;
259 if (!S_ISDIR(smb_fname_hash->st.st_ex_mode)) {
260 errno = EINVAL;
261 goto fail;
264 if (!check_valid ||
265 file_is_valid(handle, smb_fname)) {
266 return result;
270 * Someone has recreated a file under an existing inode
271 * without deleting the streams directory.
272 * Move it away or remove if streams_depot:delete_lost is set.
275 again:
276 delete_lost = lp_parm_bool(SNUM(handle->conn), "streams_depot",
277 "delete_lost", false);
279 if (delete_lost) {
280 DEBUG(3, ("Someone has recreated a file under an "
281 "existing inode. Removing: %s\n",
282 smb_fname_hash->base_name));
283 recursive_rmdir(talloc_tos(), handle->conn,
284 smb_fname_hash);
285 SMB_VFS_NEXT_UNLINKAT(handle,
286 handle->conn->cwd_fsp,
287 smb_fname_hash,
288 AT_REMOVEDIR);
289 } else {
290 newname = talloc_asprintf(talloc_tos(), "lost-%lu",
291 random());
292 DEBUG(3, ("Someone has recreated a file under an "
293 "existing inode. Renaming: %s to: %s\n",
294 smb_fname_hash->base_name,
295 newname));
296 if (newname == NULL) {
297 errno = ENOMEM;
298 goto fail;
301 smb_fname_new = synthetic_smb_fname(
302 talloc_tos(),
303 newname,
304 NULL,
305 NULL,
306 smb_fname->twrp,
307 smb_fname->flags);
308 TALLOC_FREE(newname);
309 if (smb_fname_new == NULL) {
310 errno = ENOMEM;
311 goto fail;
314 ret = SMB_VFS_NEXT_RENAMEAT(handle,
315 handle->conn->cwd_fsp,
316 smb_fname_hash,
317 handle->conn->cwd_fsp,
318 smb_fname_new,
319 &rhow);
320 if (ret == -1) {
321 TALLOC_FREE(smb_fname_new);
322 if ((errno == EEXIST) || (errno == ENOTEMPTY)) {
323 goto again;
325 goto fail;
328 TALLOC_FREE(smb_fname_new);
332 if (!create_it) {
333 errno = ENOENT;
334 goto fail;
337 ret = SMB_VFS_NEXT_MKDIRAT(handle,
338 handle->conn->cwd_fsp,
339 rootdir_fname,
340 0755);
341 if ((ret != 0) && (errno != EEXIST)) {
342 goto fail;
345 tmp = talloc_asprintf(result, "%s/%2.2X", rootdir, first);
346 if (tmp == NULL) {
347 errno = ENOMEM;
348 goto fail;
351 tmp_fname = synthetic_smb_fname(talloc_tos(),
352 tmp,
353 NULL,
354 NULL,
355 smb_fname->twrp,
356 smb_fname->flags);
357 if (tmp_fname == NULL) {
358 errno = ENOMEM;
359 goto fail;
362 ret = SMB_VFS_NEXT_MKDIRAT(handle,
363 handle->conn->cwd_fsp,
364 tmp_fname,
365 0755);
366 if ((ret != 0) && (errno != EEXIST)) {
367 goto fail;
370 TALLOC_FREE(tmp);
371 TALLOC_FREE(tmp_fname);
373 tmp = talloc_asprintf(result, "%s/%2.2X/%2.2X", rootdir, first,
374 second);
375 if (tmp == NULL) {
376 errno = ENOMEM;
377 goto fail;
380 tmp_fname = synthetic_smb_fname(talloc_tos(),
381 tmp,
382 NULL,
383 NULL,
384 smb_fname->twrp,
385 smb_fname->flags);
386 if (tmp_fname == NULL) {
387 errno = ENOMEM;
388 goto fail;
391 ret = SMB_VFS_NEXT_MKDIRAT(handle,
392 handle->conn->cwd_fsp,
393 tmp_fname,
394 0755);
395 if ((ret != 0) && (errno != EEXIST)) {
396 goto fail;
399 TALLOC_FREE(tmp);
400 TALLOC_FREE(tmp_fname);
402 /* smb_fname_hash is the struct smb_filename version of 'result' */
403 ret = SMB_VFS_NEXT_MKDIRAT(handle,
404 handle->conn->cwd_fsp,
405 smb_fname_hash,
406 0755);
407 if ((ret != 0) && (errno != EEXIST)) {
408 goto fail;
411 TALLOC_FREE(rootdir_fname);
412 TALLOC_FREE(rootdir);
413 TALLOC_FREE(tmp_fname);
414 TALLOC_FREE(smb_fname_hash);
415 return result;
417 fail:
418 TALLOC_FREE(rootdir_fname);
419 TALLOC_FREE(rootdir);
420 TALLOC_FREE(tmp_fname);
421 TALLOC_FREE(smb_fname_hash);
422 TALLOC_FREE(result);
423 return NULL;
426 * Given a stream name, populate smb_fname_out with the actual location of the
427 * stream.
429 static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
430 const struct stat_ex *base_sbuf,
431 const struct smb_filename *smb_fname,
432 struct smb_filename **smb_fname_out,
433 bool create_dir)
435 char *dirname, *stream_fname;
436 const char *stype;
437 NTSTATUS status;
439 *smb_fname_out = NULL;
441 stype = strchr_m(smb_fname->stream_name + 1, ':');
443 if (stype) {
444 if (strcasecmp_m(stype, ":$DATA") != 0) {
445 return NT_STATUS_INVALID_PARAMETER;
449 dirname = stream_dir(handle, smb_fname, base_sbuf, create_dir);
451 if (dirname == NULL) {
452 status = map_nt_error_from_unix(errno);
453 goto fail;
456 stream_fname = talloc_asprintf(talloc_tos(), "%s/%s", dirname,
457 smb_fname->stream_name);
459 if (stream_fname == NULL) {
460 status = NT_STATUS_NO_MEMORY;
461 goto fail;
464 if (stype == NULL) {
465 /* Append an explicit stream type if one wasn't specified. */
466 stream_fname = talloc_asprintf(talloc_tos(), "%s:$DATA",
467 stream_fname);
468 if (stream_fname == NULL) {
469 status = NT_STATUS_NO_MEMORY;
470 goto fail;
472 } else {
473 /* Normalize the stream type to uppercase. */
474 if (!strupper_m(strrchr_m(stream_fname, ':') + 1)) {
475 status = NT_STATUS_INVALID_PARAMETER;
476 goto fail;
480 DEBUG(10, ("stream filename = %s\n", stream_fname));
482 /* Create an smb_filename with stream_name == NULL. */
483 *smb_fname_out = synthetic_smb_fname(talloc_tos(),
484 stream_fname,
485 NULL,
486 NULL,
487 smb_fname->twrp,
488 smb_fname->flags);
489 if (*smb_fname_out == NULL) {
490 return NT_STATUS_NO_MEMORY;
493 return NT_STATUS_OK;
495 fail:
496 DEBUG(5, ("stream_name failed: %s\n", strerror(errno)));
497 TALLOC_FREE(*smb_fname_out);
498 return status;
501 static NTSTATUS walk_streams(vfs_handle_struct *handle,
502 struct smb_filename *smb_fname_base,
503 char **pdirname,
504 bool (*fn)(const struct smb_filename *dirname,
505 const char *dirent,
506 void *private_data),
507 void *private_data)
509 char *dirname;
510 char *rootdir = NULL;
511 char *orig_connectpath = NULL;
512 struct smb_filename *dir_smb_fname = NULL;
513 struct smb_Dir *dir_hnd = NULL;
514 const char *dname = NULL;
515 char *talloced = NULL;
516 NTSTATUS status;
518 dirname = stream_dir(handle, smb_fname_base, &smb_fname_base->st,
519 false);
521 if (dirname == NULL) {
522 if (errno == ENOENT) {
524 * no stream around
526 return NT_STATUS_OK;
528 return map_nt_error_from_unix(errno);
531 DEBUG(10, ("walk_streams: dirname=%s\n", dirname));
533 dir_smb_fname = synthetic_smb_fname(talloc_tos(),
534 dirname,
535 NULL,
536 NULL,
537 smb_fname_base->twrp,
538 smb_fname_base->flags);
539 if (dir_smb_fname == NULL) {
540 TALLOC_FREE(dirname);
541 return NT_STATUS_NO_MEMORY;
545 * For OpenDir to succeed if the stream rootdir is outside
546 * the share path, we must temporarily swap out the connect
547 * path for this share. We're dealing with absolute paths
548 * here so we don't care about chdir calls.
550 rootdir = stream_rootdir(handle, talloc_tos());
551 if (rootdir == NULL) {
552 TALLOC_FREE(dir_smb_fname);
553 TALLOC_FREE(dirname);
554 return NT_STATUS_NO_MEMORY;
557 orig_connectpath = handle->conn->connectpath;
558 handle->conn->connectpath = rootdir;
560 status = OpenDir(
561 talloc_tos(), handle->conn, dir_smb_fname, NULL, 0, &dir_hnd);
562 if (!NT_STATUS_IS_OK(status)) {
563 handle->conn->connectpath = orig_connectpath;
564 TALLOC_FREE(rootdir);
565 TALLOC_FREE(dir_smb_fname);
566 TALLOC_FREE(dirname);
567 return status;
570 while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
571 if (ISDOT(dname) || ISDOTDOT(dname)) {
572 TALLOC_FREE(talloced);
573 continue;
576 DBG_DEBUG("dirent=%s\n", dname);
578 if (!fn(dir_smb_fname, dname, private_data)) {
579 TALLOC_FREE(talloced);
580 break;
582 TALLOC_FREE(talloced);
585 /* Restore the original connectpath. */
586 handle->conn->connectpath = orig_connectpath;
587 TALLOC_FREE(rootdir);
588 TALLOC_FREE(dir_smb_fname);
589 TALLOC_FREE(dir_hnd);
591 if (pdirname != NULL) {
592 *pdirname = dirname;
594 else {
595 TALLOC_FREE(dirname);
598 return NT_STATUS_OK;
601 static int streams_depot_stat(vfs_handle_struct *handle,
602 struct smb_filename *smb_fname)
604 struct smb_filename *smb_fname_stream = NULL;
605 NTSTATUS status;
606 int ret = -1;
608 DEBUG(10, ("streams_depot_stat called for [%s]\n",
609 smb_fname_str_dbg(smb_fname)));
611 if (!is_named_stream(smb_fname)) {
612 return SMB_VFS_NEXT_STAT(handle, smb_fname);
615 /* Stat the actual stream now. */
616 status = stream_smb_fname(
617 handle, NULL, smb_fname, &smb_fname_stream, false);
618 if (!NT_STATUS_IS_OK(status)) {
619 ret = -1;
620 errno = map_errno_from_nt_status(status);
621 goto done;
624 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_stream);
626 /* Update the original smb_fname with the stat info. */
627 smb_fname->st = smb_fname_stream->st;
628 done:
629 TALLOC_FREE(smb_fname_stream);
630 return ret;
635 static int streams_depot_lstat(vfs_handle_struct *handle,
636 struct smb_filename *smb_fname)
638 struct smb_filename *smb_fname_stream = NULL;
639 NTSTATUS status;
640 int ret = -1;
642 DEBUG(10, ("streams_depot_lstat called for [%s]\n",
643 smb_fname_str_dbg(smb_fname)));
645 if (!is_named_stream(smb_fname)) {
646 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
649 /* Stat the actual stream now. */
650 status = stream_smb_fname(
651 handle, NULL, smb_fname, &smb_fname_stream, false);
652 if (!NT_STATUS_IS_OK(status)) {
653 ret = -1;
654 errno = map_errno_from_nt_status(status);
655 goto done;
658 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname_stream);
660 done:
661 TALLOC_FREE(smb_fname_stream);
662 return ret;
665 static int streams_depot_openat(struct vfs_handle_struct *handle,
666 const struct files_struct *dirfsp,
667 const struct smb_filename *smb_fname,
668 struct files_struct *fsp,
669 const struct vfs_open_how *how)
671 struct smb_filename *smb_fname_stream = NULL;
672 struct files_struct *fspcwd = NULL;
673 NTSTATUS status;
674 bool create_it;
675 int ret = -1;
677 if (!is_named_stream(smb_fname)) {
678 return SMB_VFS_NEXT_OPENAT(handle,
679 dirfsp,
680 smb_fname,
681 fsp,
682 how);
685 if (how->resolve != 0) {
686 errno = ENOSYS;
687 return -1;
690 SMB_ASSERT(fsp_is_alternate_stream(fsp));
691 SMB_ASSERT(dirfsp == NULL);
692 SMB_ASSERT(VALID_STAT(fsp->base_fsp->fsp_name->st));
694 create_it = (how->flags & O_CREAT);
696 /* Determine the stream name, and then open it. */
697 status = stream_smb_fname(
698 handle,
699 &fsp->base_fsp->fsp_name->st,
700 fsp->fsp_name,
701 &smb_fname_stream,
702 create_it);
703 if (!NT_STATUS_IS_OK(status)) {
704 ret = -1;
705 errno = map_errno_from_nt_status(status);
706 goto done;
709 if (create_it) {
710 bool check_valid = lp_parm_bool(
711 SNUM(handle->conn),
712 "streams_depot",
713 "check_valid",
714 true);
716 if (check_valid) {
717 char buf = '1';
719 DBG_DEBUG("marking file %s as valid\n",
720 fsp->base_fsp->fsp_name->base_name);
722 ret = SMB_VFS_FSETXATTR(
723 fsp->base_fsp,
724 SAMBA_XATTR_MARKER,
725 &buf,
726 sizeof(buf),
729 if (ret == -1) {
730 DBG_DEBUG("FSETXATTR failed: %s\n",
731 strerror(errno));
732 goto done;
737 status = vfs_at_fspcwd(talloc_tos(), handle->conn, &fspcwd);
738 if (!NT_STATUS_IS_OK(status)) {
739 ret = -1;
740 errno = map_errno_from_nt_status(status);
741 goto done;
744 ret = SMB_VFS_NEXT_OPENAT(handle,
745 fspcwd,
746 smb_fname_stream,
747 fsp,
748 how);
750 done:
751 TALLOC_FREE(smb_fname_stream);
752 TALLOC_FREE(fspcwd);
753 return ret;
756 static int streams_depot_unlink_internal(vfs_handle_struct *handle,
757 struct files_struct *dirfsp,
758 const struct smb_filename *smb_fname,
759 int flags)
761 struct smb_filename *full_fname = NULL;
762 char *dirname = NULL;
763 int ret = -1;
765 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
766 dirfsp,
767 smb_fname);
768 if (full_fname == NULL) {
769 return -1;
772 DEBUG(10, ("streams_depot_unlink called for %s\n",
773 smb_fname_str_dbg(full_fname)));
775 /* If there is a valid stream, just unlink the stream and return. */
776 if (is_named_stream(full_fname)) {
777 struct smb_filename *smb_fname_stream = NULL;
778 NTSTATUS status;
780 status = stream_smb_fname(
781 handle, NULL, full_fname, &smb_fname_stream, false);
782 TALLOC_FREE(full_fname);
783 if (!NT_STATUS_IS_OK(status)) {
784 errno = map_errno_from_nt_status(status);
785 return -1;
788 ret = SMB_VFS_NEXT_UNLINKAT(handle,
789 dirfsp->conn->cwd_fsp,
790 smb_fname_stream,
793 TALLOC_FREE(smb_fname_stream);
794 return ret;
798 * We potentially need to delete the per-inode streams directory
801 if (full_fname->flags & SMB_FILENAME_POSIX_PATH) {
802 ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
803 } else {
804 ret = SMB_VFS_NEXT_STAT(handle, full_fname);
805 if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
806 if (VALID_STAT(smb_fname->st) &&
807 S_ISLNK(smb_fname->st.st_ex_mode)) {
809 * Original name was a link - Could be
810 * trying to remove a dangling symlink.
812 ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
816 if (ret == -1) {
817 TALLOC_FREE(full_fname);
818 return -1;
822 * We know the unlink should succeed as the ACL
823 * check is already done in the caller. Remove the
824 * file *after* the streams.
826 dirname = stream_dir(handle,
827 full_fname,
828 &full_fname->st,
829 false);
830 TALLOC_FREE(full_fname);
831 if (dirname != NULL) {
832 struct smb_filename *smb_fname_dir = NULL;
834 smb_fname_dir = synthetic_smb_fname(talloc_tos(),
835 dirname,
836 NULL,
837 NULL,
838 smb_fname->twrp,
839 smb_fname->flags);
840 if (smb_fname_dir == NULL) {
841 TALLOC_FREE(dirname);
842 errno = ENOMEM;
843 return -1;
846 SMB_VFS_NEXT_UNLINKAT(handle,
847 dirfsp->conn->cwd_fsp,
848 smb_fname_dir,
849 AT_REMOVEDIR);
850 TALLOC_FREE(smb_fname_dir);
851 TALLOC_FREE(dirname);
854 ret = SMB_VFS_NEXT_UNLINKAT(handle,
855 dirfsp,
856 smb_fname,
857 flags);
858 return ret;
861 static int streams_depot_rmdir_internal(vfs_handle_struct *handle,
862 struct files_struct *dirfsp,
863 const struct smb_filename *smb_fname)
865 struct smb_filename *full_fname = NULL;
866 struct smb_filename *smb_fname_base = NULL;
867 int ret = -1;
869 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
870 dirfsp,
871 smb_fname);
872 if (full_fname == NULL) {
873 return -1;
876 DBG_DEBUG("called for %s\n", full_fname->base_name);
879 * We potentially need to delete the per-inode streams directory
882 smb_fname_base = synthetic_smb_fname(talloc_tos(),
883 full_fname->base_name,
884 NULL,
885 NULL,
886 full_fname->twrp,
887 full_fname->flags);
888 TALLOC_FREE(full_fname);
889 if (smb_fname_base == NULL) {
890 errno = ENOMEM;
891 return -1;
894 if (smb_fname_base->flags & SMB_FILENAME_POSIX_PATH) {
895 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname_base);
896 } else {
897 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
900 if (ret == -1) {
901 TALLOC_FREE(smb_fname_base);
902 return -1;
906 * We know the rmdir should succeed as the ACL
907 * check is already done in the caller. Remove the
908 * directory *after* the streams.
911 char *dirname = stream_dir(handle, smb_fname_base,
912 &smb_fname_base->st, false);
914 if (dirname != NULL) {
915 struct smb_filename *smb_fname_dir =
916 synthetic_smb_fname(talloc_tos(),
917 dirname,
918 NULL,
919 NULL,
920 smb_fname->twrp,
921 smb_fname->flags);
922 if (smb_fname_dir == NULL) {
923 TALLOC_FREE(smb_fname_base);
924 TALLOC_FREE(dirname);
925 errno = ENOMEM;
926 return -1;
928 SMB_VFS_NEXT_UNLINKAT(handle,
929 dirfsp->conn->cwd_fsp,
930 smb_fname_dir,
931 AT_REMOVEDIR);
932 TALLOC_FREE(smb_fname_dir);
934 TALLOC_FREE(dirname);
937 ret = SMB_VFS_NEXT_UNLINKAT(handle,
938 dirfsp,
939 smb_fname,
940 AT_REMOVEDIR);
941 TALLOC_FREE(smb_fname_base);
942 return ret;
945 static int streams_depot_unlinkat(vfs_handle_struct *handle,
946 struct files_struct *dirfsp,
947 const struct smb_filename *smb_fname,
948 int flags)
950 int ret;
951 if (flags & AT_REMOVEDIR) {
952 ret = streams_depot_rmdir_internal(handle,
953 dirfsp,
954 smb_fname);
955 } else {
956 ret = streams_depot_unlink_internal(handle,
957 dirfsp,
958 smb_fname,
959 flags);
961 return ret;
964 static int streams_depot_renameat(vfs_handle_struct *handle,
965 files_struct *srcfsp,
966 const struct smb_filename *smb_fname_src,
967 files_struct *dstfsp,
968 const struct smb_filename *smb_fname_dst,
969 const struct vfs_rename_how *how)
971 struct smb_filename *smb_fname_src_stream = NULL;
972 struct smb_filename *smb_fname_dst_stream = NULL;
973 struct smb_filename *full_src = NULL;
974 struct smb_filename *full_dst = NULL;
975 bool src_is_stream, dst_is_stream;
976 NTSTATUS status;
977 int ret = -1;
979 DEBUG(10, ("streams_depot_renameat called for %s => %s\n",
980 smb_fname_str_dbg(smb_fname_src),
981 smb_fname_str_dbg(smb_fname_dst)));
983 src_is_stream = is_ntfs_stream_smb_fname(smb_fname_src);
984 dst_is_stream = is_ntfs_stream_smb_fname(smb_fname_dst);
986 if (!src_is_stream && !dst_is_stream) {
987 return SMB_VFS_NEXT_RENAMEAT(handle,
988 srcfsp,
989 smb_fname_src,
990 dstfsp,
991 smb_fname_dst,
992 how);
995 if (how->flags != 0) {
996 errno = EINVAL;
997 goto done;
1000 /* for now don't allow renames from or to the default stream */
1001 if (is_ntfs_default_stream_smb_fname(smb_fname_src) ||
1002 is_ntfs_default_stream_smb_fname(smb_fname_dst)) {
1003 errno = ENOSYS;
1004 goto done;
1007 full_src = full_path_from_dirfsp_atname(talloc_tos(),
1008 srcfsp,
1009 smb_fname_src);
1010 if (full_src == NULL) {
1011 errno = ENOMEM;
1012 goto done;
1015 full_dst = full_path_from_dirfsp_atname(talloc_tos(),
1016 dstfsp,
1017 smb_fname_dst);
1018 if (full_dst == NULL) {
1019 errno = ENOMEM;
1020 goto done;
1023 status = stream_smb_fname(
1024 handle, NULL, full_src, &smb_fname_src_stream, false);
1025 if (!NT_STATUS_IS_OK(status)) {
1026 errno = map_errno_from_nt_status(status);
1027 goto done;
1030 status = stream_smb_fname(
1031 handle, NULL, full_dst, &smb_fname_dst_stream, false);
1032 if (!NT_STATUS_IS_OK(status)) {
1033 errno = map_errno_from_nt_status(status);
1034 goto done;
1038 * We must use handle->conn->cwd_fsp as
1039 * srcfsp and dstfsp directory handles here
1040 * as we used the full pathname from the cwd dir
1041 * to calculate the streams directory and filename
1042 * within.
1044 ret = SMB_VFS_NEXT_RENAMEAT(handle,
1045 handle->conn->cwd_fsp,
1046 smb_fname_src_stream,
1047 handle->conn->cwd_fsp,
1048 smb_fname_dst_stream,
1049 how);
1051 done:
1052 TALLOC_FREE(smb_fname_src_stream);
1053 TALLOC_FREE(smb_fname_dst_stream);
1054 return ret;
1057 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
1058 struct stream_struct **streams,
1059 const char *name, off_t size,
1060 off_t alloc_size)
1062 struct stream_struct *tmp;
1064 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
1065 (*num_streams)+1);
1066 if (tmp == NULL) {
1067 return false;
1070 tmp[*num_streams].name = talloc_strdup(tmp, name);
1071 if (tmp[*num_streams].name == NULL) {
1072 return false;
1075 tmp[*num_streams].size = size;
1076 tmp[*num_streams].alloc_size = alloc_size;
1078 *streams = tmp;
1079 *num_streams += 1;
1080 return true;
1083 struct streaminfo_state {
1084 TALLOC_CTX *mem_ctx;
1085 vfs_handle_struct *handle;
1086 unsigned int num_streams;
1087 struct stream_struct *streams;
1088 NTSTATUS status;
1091 static bool collect_one_stream(const struct smb_filename *dirfname,
1092 const char *dirent,
1093 void *private_data)
1095 const char *dirname = dirfname->base_name;
1096 struct streaminfo_state *state =
1097 (struct streaminfo_state *)private_data;
1098 struct smb_filename *smb_fname = NULL;
1099 char *sname = NULL;
1100 bool ret;
1102 sname = talloc_asprintf(talloc_tos(), "%s/%s", dirname, dirent);
1103 if (sname == NULL) {
1104 state->status = NT_STATUS_NO_MEMORY;
1105 ret = false;
1106 goto out;
1109 smb_fname = synthetic_smb_fname(talloc_tos(),
1110 sname,
1111 NULL,
1112 NULL,
1113 dirfname->twrp,
1115 if (smb_fname == NULL) {
1116 state->status = NT_STATUS_NO_MEMORY;
1117 ret = false;
1118 goto out;
1121 if (SMB_VFS_NEXT_STAT(state->handle, smb_fname) == -1) {
1122 DEBUG(10, ("Could not stat %s: %s\n", sname,
1123 strerror(errno)));
1124 ret = true;
1125 goto out;
1128 if (!add_one_stream(state->mem_ctx,
1129 &state->num_streams, &state->streams,
1130 dirent, smb_fname->st.st_ex_size,
1131 SMB_VFS_GET_ALLOC_SIZE(state->handle->conn, NULL,
1132 &smb_fname->st))) {
1133 state->status = NT_STATUS_NO_MEMORY;
1134 ret = false;
1135 goto out;
1138 ret = true;
1139 out:
1140 TALLOC_FREE(sname);
1141 TALLOC_FREE(smb_fname);
1142 return ret;
1145 static NTSTATUS streams_depot_fstreaminfo(vfs_handle_struct *handle,
1146 struct files_struct *fsp,
1147 TALLOC_CTX *mem_ctx,
1148 unsigned int *pnum_streams,
1149 struct stream_struct **pstreams)
1151 struct smb_filename *smb_fname_base = NULL;
1152 int ret;
1153 NTSTATUS status;
1154 struct streaminfo_state state;
1156 smb_fname_base = synthetic_smb_fname(talloc_tos(),
1157 fsp->fsp_name->base_name,
1158 NULL,
1159 NULL,
1160 fsp->fsp_name->twrp,
1161 fsp->fsp_name->flags);
1162 if (smb_fname_base == NULL) {
1163 return NT_STATUS_NO_MEMORY;
1166 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &smb_fname_base->st);
1167 if (ret == -1) {
1168 status = map_nt_error_from_unix(errno);
1169 goto out;
1172 state.streams = *pstreams;
1173 state.num_streams = *pnum_streams;
1174 state.mem_ctx = mem_ctx;
1175 state.handle = handle;
1176 state.status = NT_STATUS_OK;
1178 status = walk_streams(handle,
1179 smb_fname_base,
1180 NULL,
1181 collect_one_stream,
1182 &state);
1184 if (!NT_STATUS_IS_OK(status)) {
1185 TALLOC_FREE(state.streams);
1186 goto out;
1189 if (!NT_STATUS_IS_OK(state.status)) {
1190 TALLOC_FREE(state.streams);
1191 status = state.status;
1192 goto out;
1195 *pnum_streams = state.num_streams;
1196 *pstreams = state.streams;
1197 status = SMB_VFS_NEXT_FSTREAMINFO(handle,
1198 fsp->base_fsp ? fsp->base_fsp : fsp,
1199 mem_ctx,
1200 pnum_streams,
1201 pstreams);
1203 out:
1204 TALLOC_FREE(smb_fname_base);
1205 return status;
1208 static uint32_t streams_depot_fs_capabilities(struct vfs_handle_struct *handle,
1209 enum timestamp_set_resolution *p_ts_res)
1211 return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_NAMED_STREAMS;
1214 static struct vfs_fn_pointers vfs_streams_depot_fns = {
1215 .fs_capabilities_fn = streams_depot_fs_capabilities,
1216 .openat_fn = streams_depot_openat,
1217 .stat_fn = streams_depot_stat,
1218 .lstat_fn = streams_depot_lstat,
1219 .unlinkat_fn = streams_depot_unlinkat,
1220 .renameat_fn = streams_depot_renameat,
1221 .fstreaminfo_fn = streams_depot_fstreaminfo,
1224 static_decl_vfs;
1225 NTSTATUS vfs_streams_depot_init(TALLOC_CTX *ctx)
1227 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "streams_depot",
1228 &vfs_streams_depot_fns);