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/>.
21 #include "smbd/smbd.h"
22 #include "system/filesys.h"
25 #define DBGC_CLASS DBGC_VFS
28 * Excerpt from a mail from tridge:
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
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
39 static uint32_t hash_fn(DATA_BLOB key
)
41 uint32_t value
; /* Used to compute the hash value. */
42 uint32_t i
; /* Used to cycle through random values. */
44 /* Set the initial value from the key size. */
45 for (value
= 0x238F13AF * key
.length
, i
=0; i
< key
.length
; i
++)
46 value
= (value
+ (key
.data
[i
] << (i
*5 % 24)));
48 return (1103515243 * value
+ 12345);
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.
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
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.
70 static bool file_is_valid(vfs_handle_struct
*handle
,
71 const struct smb_filename
*smb_fname
)
75 DEBUG(10, ("file_is_valid (%s) called\n", smb_fname
->base_name
));
77 if (SMB_VFS_GETXATTR(handle
->conn
, smb_fname
, SAMBA_XATTR_MARKER
,
78 &buf
, sizeof(buf
)) != sizeof(buf
)) {
79 DEBUG(10, ("GETXATTR failed: %s\n", strerror(errno
)));
84 DEBUG(10, ("got wrong buffer content: '%c'\n", buf
));
91 static bool mark_file_valid(vfs_handle_struct
*handle
,
92 const struct smb_filename
*smb_fname
)
97 DEBUG(10, ("marking file %s as valid\n", smb_fname
->base_name
));
99 ret
= SMB_VFS_SETXATTR(handle
->conn
, smb_fname
, SAMBA_XATTR_MARKER
,
100 &buf
, sizeof(buf
), 0);
103 DEBUG(10, ("SETXATTR failed: %s\n", strerror(errno
)));
111 * Given an smb_filename, determine the stream directory using the file's
114 static char *stream_dir(vfs_handle_struct
*handle
,
115 const struct smb_filename
*smb_fname
,
116 const SMB_STRUCT_STAT
*base_sbuf
, bool create_it
)
118 const struct loadparm_substitution
*lp_sub
=
119 loadparm_s3_global_substitution();
121 struct smb_filename
*smb_fname_hash
= NULL
;
123 SMB_STRUCT_STAT base_sbuf_tmp
;
124 uint8_t first
, second
;
130 char *rootdir
= NULL
;
131 struct smb_filename
*rootdir_fname
= NULL
;
132 struct smb_filename
*tmp_fname
= NULL
;
135 check_valid
= lp_parm_bool(SNUM(handle
->conn
),
136 "streams_depot", "check_valid", true);
138 tmp
= talloc_asprintf(talloc_tos(), "%s/.streams",
139 handle
->conn
->connectpath
);
146 rootdir
= lp_parm_substituted_string(talloc_tos(), lp_sub
,
147 SNUM(handle
->conn
), "streams_depot", "directory",
149 if (rootdir
== NULL
) {
154 rootdir_fname
= synthetic_smb_fname(talloc_tos(),
159 if (rootdir_fname
== NULL
) {
164 /* Stat the base file if it hasn't already been done. */
165 if (base_sbuf
== NULL
) {
166 struct smb_filename
*smb_fname_base
;
168 smb_fname_base
= synthetic_smb_fname(
170 smb_fname
->base_name
,
174 if (smb_fname_base
== NULL
) {
178 if (SMB_VFS_NEXT_STAT(handle
, smb_fname_base
) == -1) {
179 TALLOC_FREE(smb_fname_base
);
182 base_sbuf_tmp
= smb_fname_base
->st
;
183 TALLOC_FREE(smb_fname_base
);
185 base_sbuf_tmp
= *base_sbuf
;
188 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &base_sbuf_tmp
);
190 push_file_id_16((char *)id_buf
, &id
);
192 hash
= hash_fn(data_blob_const(id_buf
, sizeof(id_buf
)));
195 second
= (hash
>> 8) & 0xff;
197 id_hex
= hex_encode_talloc(talloc_tos(), id_buf
, sizeof(id_buf
));
199 if (id_hex
== NULL
) {
204 result
= talloc_asprintf(talloc_tos(), "%s/%2.2X/%2.2X/%s", rootdir
,
205 first
, second
, id_hex
);
209 if (result
== NULL
) {
214 smb_fname_hash
= synthetic_smb_fname(talloc_tos(),
219 if (smb_fname_hash
== NULL
) {
224 if (SMB_VFS_NEXT_STAT(handle
, smb_fname_hash
) == 0) {
225 struct smb_filename
*smb_fname_new
= NULL
;
229 if (!S_ISDIR(smb_fname_hash
->st
.st_ex_mode
)) {
235 file_is_valid(handle
, smb_fname
)) {
240 * Someone has recreated a file under an existing inode
241 * without deleting the streams directory.
242 * Move it away or remove if streams_depot:delete_lost is set.
246 delete_lost
= lp_parm_bool(SNUM(handle
->conn
), "streams_depot",
247 "delete_lost", false);
250 DEBUG(3, ("Someone has recreated a file under an "
251 "existing inode. Removing: %s\n",
252 smb_fname_hash
->base_name
));
253 recursive_rmdir(talloc_tos(), handle
->conn
,
255 SMB_VFS_NEXT_UNLINKAT(handle
,
256 handle
->conn
->cwd_fsp
,
260 newname
= talloc_asprintf(talloc_tos(), "lost-%lu",
262 DEBUG(3, ("Someone has recreated a file under an "
263 "existing inode. Renaming: %s to: %s\n",
264 smb_fname_hash
->base_name
,
266 if (newname
== NULL
) {
271 smb_fname_new
= synthetic_smb_fname(
277 TALLOC_FREE(newname
);
278 if (smb_fname_new
== NULL
) {
283 if (SMB_VFS_NEXT_RENAMEAT(handle
,
284 handle
->conn
->cwd_fsp
,
286 handle
->conn
->cwd_fsp
,
287 smb_fname_new
) == -1) {
288 TALLOC_FREE(smb_fname_new
);
289 if ((errno
== EEXIST
) || (errno
== ENOTEMPTY
)) {
295 TALLOC_FREE(smb_fname_new
);
304 ret
= SMB_VFS_NEXT_MKDIRAT(handle
,
305 handle
->conn
->cwd_fsp
,
308 if ((ret
!= 0) && (errno
!= EEXIST
)) {
312 tmp
= talloc_asprintf(result
, "%s/%2.2X", rootdir
, first
);
318 tmp_fname
= synthetic_smb_fname(talloc_tos(),
323 if (tmp_fname
== NULL
) {
328 ret
= SMB_VFS_NEXT_MKDIRAT(handle
,
329 handle
->conn
->cwd_fsp
,
332 if ((ret
!= 0) && (errno
!= EEXIST
)) {
337 TALLOC_FREE(tmp_fname
);
339 tmp
= talloc_asprintf(result
, "%s/%2.2X/%2.2X", rootdir
, first
,
346 tmp_fname
= synthetic_smb_fname(talloc_tos(),
351 if (tmp_fname
== NULL
) {
356 ret
= SMB_VFS_NEXT_MKDIRAT(handle
,
357 handle
->conn
->cwd_fsp
,
360 if ((ret
!= 0) && (errno
!= EEXIST
)) {
365 TALLOC_FREE(tmp_fname
);
367 /* smb_fname_hash is the struct smb_filename version of 'result' */
368 ret
= SMB_VFS_NEXT_MKDIRAT(handle
,
369 handle
->conn
->cwd_fsp
,
372 if ((ret
!= 0) && (errno
!= EEXIST
)) {
376 if (check_valid
&& !mark_file_valid(handle
, smb_fname
)) {
380 TALLOC_FREE(rootdir_fname
);
381 TALLOC_FREE(rootdir
);
382 TALLOC_FREE(tmp_fname
);
383 TALLOC_FREE(smb_fname_hash
);
387 TALLOC_FREE(rootdir_fname
);
388 TALLOC_FREE(rootdir
);
389 TALLOC_FREE(tmp_fname
);
390 TALLOC_FREE(smb_fname_hash
);
395 * Given a stream name, populate smb_fname_out with the actual location of the
398 static NTSTATUS
stream_smb_fname(vfs_handle_struct
*handle
,
399 const struct smb_filename
*smb_fname
,
400 struct smb_filename
**smb_fname_out
,
403 char *dirname
, *stream_fname
;
407 *smb_fname_out
= NULL
;
409 stype
= strchr_m(smb_fname
->stream_name
+ 1, ':');
412 if (strcasecmp_m(stype
, ":$DATA") != 0) {
413 return NT_STATUS_INVALID_PARAMETER
;
417 dirname
= stream_dir(handle
, smb_fname
, NULL
, create_dir
);
419 if (dirname
== NULL
) {
420 status
= map_nt_error_from_unix(errno
);
424 stream_fname
= talloc_asprintf(talloc_tos(), "%s/%s", dirname
,
425 smb_fname
->stream_name
);
427 if (stream_fname
== NULL
) {
428 status
= NT_STATUS_NO_MEMORY
;
433 /* Append an explicit stream type if one wasn't specified. */
434 stream_fname
= talloc_asprintf(talloc_tos(), "%s:$DATA",
436 if (stream_fname
== NULL
) {
437 status
= NT_STATUS_NO_MEMORY
;
441 /* Normalize the stream type to upercase. */
442 if (!strupper_m(strrchr_m(stream_fname
, ':') + 1)) {
443 status
= NT_STATUS_INVALID_PARAMETER
;
448 DEBUG(10, ("stream filename = %s\n", stream_fname
));
450 /* Create an smb_filename with stream_name == NULL. */
451 *smb_fname_out
= synthetic_smb_fname(talloc_tos(),
456 if (*smb_fname_out
== NULL
) {
457 return NT_STATUS_NO_MEMORY
;
463 DEBUG(5, ("stream_name failed: %s\n", strerror(errno
)));
464 TALLOC_FREE(*smb_fname_out
);
468 static NTSTATUS
walk_streams(vfs_handle_struct
*handle
,
469 struct smb_filename
*smb_fname_base
,
471 bool (*fn
)(const char *dirname
,
477 struct smb_filename
*dir_smb_fname
= NULL
;
478 DIR *dirhandle
= NULL
;
479 const char *dirent
= NULL
;
480 char *talloced
= NULL
;
482 dirname
= stream_dir(handle
, smb_fname_base
, &smb_fname_base
->st
,
485 if (dirname
== NULL
) {
486 if (errno
== ENOENT
) {
492 return map_nt_error_from_unix(errno
);
495 DEBUG(10, ("walk_streams: dirname=%s\n", dirname
));
497 dir_smb_fname
= synthetic_smb_fname(talloc_tos(),
501 smb_fname_base
->flags
);
502 if (dir_smb_fname
== NULL
) {
503 TALLOC_FREE(dirname
);
504 return NT_STATUS_NO_MEMORY
;
507 dirhandle
= SMB_VFS_NEXT_OPENDIR(handle
, dir_smb_fname
, NULL
, 0);
509 TALLOC_FREE(dir_smb_fname
);
511 if (dirhandle
== NULL
) {
512 TALLOC_FREE(dirname
);
513 return map_nt_error_from_unix(errno
);
516 while ((dirent
= vfs_readdirname(handle
->conn
, dirhandle
, NULL
,
517 &talloced
)) != NULL
) {
519 if (ISDOT(dirent
) || ISDOTDOT(dirent
)) {
520 TALLOC_FREE(talloced
);
524 DEBUG(10, ("walk_streams: dirent=%s\n", dirent
));
526 if (!fn(dirname
, dirent
, private_data
)) {
527 TALLOC_FREE(talloced
);
530 TALLOC_FREE(talloced
);
533 SMB_VFS_NEXT_CLOSEDIR(handle
, dirhandle
);
535 if (pdirname
!= NULL
) {
539 TALLOC_FREE(dirname
);
545 static int streams_depot_stat(vfs_handle_struct
*handle
,
546 struct smb_filename
*smb_fname
)
548 struct smb_filename
*smb_fname_stream
= NULL
;
552 DEBUG(10, ("streams_depot_stat called for [%s]\n",
553 smb_fname_str_dbg(smb_fname
)));
555 if (!is_named_stream(smb_fname
)) {
556 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
559 /* Stat the actual stream now. */
560 status
= stream_smb_fname(handle
, smb_fname
, &smb_fname_stream
,
562 if (!NT_STATUS_IS_OK(status
)) {
564 errno
= map_errno_from_nt_status(status
);
568 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname_stream
);
570 /* Update the original smb_fname with the stat info. */
571 smb_fname
->st
= smb_fname_stream
->st
;
573 TALLOC_FREE(smb_fname_stream
);
579 static int streams_depot_lstat(vfs_handle_struct
*handle
,
580 struct smb_filename
*smb_fname
)
582 struct smb_filename
*smb_fname_stream
= NULL
;
586 DEBUG(10, ("streams_depot_lstat called for [%s]\n",
587 smb_fname_str_dbg(smb_fname
)));
589 if (!is_named_stream(smb_fname
)) {
590 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
593 /* Stat the actual stream now. */
594 status
= stream_smb_fname(handle
, smb_fname
, &smb_fname_stream
,
596 if (!NT_STATUS_IS_OK(status
)) {
598 errno
= map_errno_from_nt_status(status
);
602 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname_stream
);
605 TALLOC_FREE(smb_fname_stream
);
609 static int streams_depot_open(vfs_handle_struct
*handle
,
610 struct smb_filename
*smb_fname
,
611 files_struct
*fsp
, int flags
, mode_t mode
)
613 struct smb_filename
*smb_fname_stream
= NULL
;
614 struct smb_filename
*smb_fname_base
= NULL
;
618 if (!is_named_stream(smb_fname
)) {
619 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
622 /* Ensure the base file still exists. */
623 smb_fname_base
= synthetic_smb_fname(talloc_tos(),
624 smb_fname
->base_name
,
628 if (smb_fname_base
== NULL
) {
634 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname_base
);
639 /* Determine the stream name, and then open it. */
640 status
= stream_smb_fname(handle
, smb_fname
, &smb_fname_stream
, true);
641 if (!NT_STATUS_IS_OK(status
)) {
643 errno
= map_errno_from_nt_status(status
);
647 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname_stream
, fsp
, flags
, mode
);
650 TALLOC_FREE(smb_fname_stream
);
651 TALLOC_FREE(smb_fname_base
);
655 static int streams_depot_unlink_internal(vfs_handle_struct
*handle
,
656 struct files_struct
*dirfsp
,
657 const struct smb_filename
*smb_fname
,
660 struct smb_filename
*smb_fname_base
= NULL
;
663 DEBUG(10, ("streams_depot_unlink called for %s\n",
664 smb_fname_str_dbg(smb_fname
)));
666 /* If there is a valid stream, just unlink the stream and return. */
667 if (is_named_stream(smb_fname
)) {
668 struct smb_filename
*smb_fname_stream
= NULL
;
671 status
= stream_smb_fname(handle
, smb_fname
, &smb_fname_stream
,
673 if (!NT_STATUS_IS_OK(status
)) {
674 errno
= map_errno_from_nt_status(status
);
678 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
683 TALLOC_FREE(smb_fname_stream
);
688 * We potentially need to delete the per-inode streams directory
691 smb_fname_base
= synthetic_smb_fname(talloc_tos(),
692 smb_fname
->base_name
,
696 if (smb_fname_base
== NULL
) {
701 if (smb_fname_base
->flags
& SMB_FILENAME_POSIX_PATH
) {
702 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname_base
);
704 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname_base
);
708 TALLOC_FREE(smb_fname_base
);
713 * We know the unlink should succeed as the ACL
714 * check is already done in the caller. Remove the
715 * file *after* the streams.
718 char *dirname
= stream_dir(handle
, smb_fname_base
,
719 &smb_fname_base
->st
, false);
721 if (dirname
!= NULL
) {
722 struct smb_filename
*smb_fname_dir
=
723 synthetic_smb_fname(talloc_tos(),
728 if (smb_fname_dir
== NULL
) {
729 TALLOC_FREE(smb_fname_base
);
730 TALLOC_FREE(dirname
);
734 SMB_VFS_NEXT_UNLINKAT(handle
,
738 TALLOC_FREE(smb_fname_dir
);
740 TALLOC_FREE(dirname
);
743 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
747 TALLOC_FREE(smb_fname_base
);
751 static int streams_depot_rmdir_internal(vfs_handle_struct
*handle
,
752 struct files_struct
*dirfsp
,
753 const struct smb_filename
*smb_fname
)
755 struct smb_filename
*smb_fname_base
= NULL
;
758 DBG_DEBUG("called for %s\n", smb_fname
->base_name
);
761 * We potentially need to delete the per-inode streams directory
764 smb_fname_base
= synthetic_smb_fname(talloc_tos(),
765 smb_fname
->base_name
,
769 if (smb_fname_base
== NULL
) {
774 if (smb_fname_base
->flags
& SMB_FILENAME_POSIX_PATH
) {
775 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname_base
);
777 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname_base
);
781 TALLOC_FREE(smb_fname_base
);
786 * We know the rmdir should succeed as the ACL
787 * check is already done in the caller. Remove the
788 * directory *after* the streams.
791 char *dirname
= stream_dir(handle
, smb_fname_base
,
792 &smb_fname_base
->st
, false);
794 if (dirname
!= NULL
) {
795 struct smb_filename
*smb_fname_dir
=
796 synthetic_smb_fname(talloc_tos(),
801 if (smb_fname_dir
== NULL
) {
802 TALLOC_FREE(smb_fname_base
);
803 TALLOC_FREE(dirname
);
807 SMB_VFS_NEXT_UNLINKAT(handle
,
811 TALLOC_FREE(smb_fname_dir
);
813 TALLOC_FREE(dirname
);
816 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
820 TALLOC_FREE(smb_fname_base
);
824 static int streams_depot_unlinkat(vfs_handle_struct
*handle
,
825 struct files_struct
*dirfsp
,
826 const struct smb_filename
*smb_fname
,
830 SMB_ASSERT(dirfsp
== dirfsp
->conn
->cwd_fsp
);
831 if (flags
& AT_REMOVEDIR
) {
832 ret
= streams_depot_rmdir_internal(handle
,
836 ret
= streams_depot_unlink_internal(handle
,
844 static int streams_depot_renameat(vfs_handle_struct
*handle
,
845 files_struct
*srcfsp
,
846 const struct smb_filename
*smb_fname_src
,
847 files_struct
*dstfsp
,
848 const struct smb_filename
*smb_fname_dst
)
850 struct smb_filename
*smb_fname_src_stream
= NULL
;
851 struct smb_filename
*smb_fname_dst_stream
= NULL
;
852 bool src_is_stream
, dst_is_stream
;
856 DEBUG(10, ("streams_depot_renameat called for %s => %s\n",
857 smb_fname_str_dbg(smb_fname_src
),
858 smb_fname_str_dbg(smb_fname_dst
)));
860 src_is_stream
= is_ntfs_stream_smb_fname(smb_fname_src
);
861 dst_is_stream
= is_ntfs_stream_smb_fname(smb_fname_dst
);
863 if (!src_is_stream
&& !dst_is_stream
) {
864 return SMB_VFS_NEXT_RENAMEAT(handle
,
871 /* for now don't allow renames from or to the default stream */
872 if (is_ntfs_default_stream_smb_fname(smb_fname_src
) ||
873 is_ntfs_default_stream_smb_fname(smb_fname_dst
)) {
878 status
= stream_smb_fname(handle
, smb_fname_src
, &smb_fname_src_stream
,
880 if (!NT_STATUS_IS_OK(status
)) {
881 errno
= map_errno_from_nt_status(status
);
885 status
= stream_smb_fname(handle
, smb_fname_dst
,
886 &smb_fname_dst_stream
, false);
887 if (!NT_STATUS_IS_OK(status
)) {
888 errno
= map_errno_from_nt_status(status
);
892 ret
= SMB_VFS_NEXT_RENAMEAT(handle
,
894 smb_fname_src_stream
,
896 smb_fname_dst_stream
);
899 TALLOC_FREE(smb_fname_src_stream
);
900 TALLOC_FREE(smb_fname_dst_stream
);
904 static bool add_one_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
905 struct stream_struct
**streams
,
906 const char *name
, off_t size
,
909 struct stream_struct
*tmp
;
911 tmp
= talloc_realloc(mem_ctx
, *streams
, struct stream_struct
,
917 tmp
[*num_streams
].name
= talloc_strdup(tmp
, name
);
918 if (tmp
[*num_streams
].name
== NULL
) {
922 tmp
[*num_streams
].size
= size
;
923 tmp
[*num_streams
].alloc_size
= alloc_size
;
930 struct streaminfo_state
{
932 vfs_handle_struct
*handle
;
933 unsigned int num_streams
;
934 struct stream_struct
*streams
;
938 static bool collect_one_stream(const char *dirname
,
942 struct streaminfo_state
*state
=
943 (struct streaminfo_state
*)private_data
;
944 struct smb_filename
*smb_fname
= NULL
;
948 sname
= talloc_asprintf(talloc_tos(), "%s/%s", dirname
, dirent
);
950 state
->status
= NT_STATUS_NO_MEMORY
;
955 smb_fname
= synthetic_smb_fname(talloc_tos(), sname
, NULL
, NULL
, 0);
956 if (smb_fname
== NULL
) {
957 state
->status
= NT_STATUS_NO_MEMORY
;
962 if (SMB_VFS_NEXT_STAT(state
->handle
, smb_fname
) == -1) {
963 DEBUG(10, ("Could not stat %s: %s\n", sname
,
969 if (!add_one_stream(state
->mem_ctx
,
970 &state
->num_streams
, &state
->streams
,
971 dirent
, smb_fname
->st
.st_ex_size
,
972 SMB_VFS_GET_ALLOC_SIZE(state
->handle
->conn
, NULL
,
974 state
->status
= NT_STATUS_NO_MEMORY
;
982 TALLOC_FREE(smb_fname
);
986 static NTSTATUS
streams_depot_streaminfo(vfs_handle_struct
*handle
,
987 struct files_struct
*fsp
,
988 const struct smb_filename
*smb_fname
,
990 unsigned int *pnum_streams
,
991 struct stream_struct
**pstreams
)
993 struct smb_filename
*smb_fname_base
= NULL
;
996 struct streaminfo_state state
;
998 smb_fname_base
= synthetic_smb_fname(talloc_tos(),
999 smb_fname
->base_name
,
1003 if (smb_fname_base
== NULL
) {
1004 return NT_STATUS_NO_MEMORY
;
1007 if ((fsp
!= NULL
) && (fsp
->fh
->fd
!= -1)) {
1008 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, &smb_fname_base
->st
);
1011 if (smb_fname_base
->flags
& SMB_FILENAME_POSIX_PATH
) {
1012 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname_base
);
1014 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname_base
);
1019 status
= map_nt_error_from_unix(errno
);
1023 state
.streams
= *pstreams
;
1024 state
.num_streams
= *pnum_streams
;
1025 state
.mem_ctx
= mem_ctx
;
1026 state
.handle
= handle
;
1027 state
.status
= NT_STATUS_OK
;
1029 if (S_ISLNK(smb_fname_base
->st
.st_ex_mode
)) {
1031 * Currently we do't have SMB_VFS_LLISTXATTR
1032 * inside the VFS which means there's no way
1033 * to cope with a symlink when lp_posix_pathnames().
1034 * returns true. For now ignore links.
1035 * FIXME - by adding SMB_VFS_LLISTXATTR. JRA.
1037 status
= NT_STATUS_OK
;
1039 status
= walk_streams(handle
, smb_fname_base
, NULL
, collect_one_stream
,
1043 if (!NT_STATUS_IS_OK(status
)) {
1044 TALLOC_FREE(state
.streams
);
1048 if (!NT_STATUS_IS_OK(state
.status
)) {
1049 TALLOC_FREE(state
.streams
);
1050 status
= state
.status
;
1054 *pnum_streams
= state
.num_streams
;
1055 *pstreams
= state
.streams
;
1056 status
= SMB_VFS_NEXT_STREAMINFO(handle
,
1064 TALLOC_FREE(smb_fname_base
);
1068 static uint32_t streams_depot_fs_capabilities(struct vfs_handle_struct
*handle
,
1069 enum timestamp_set_resolution
*p_ts_res
)
1071 return SMB_VFS_NEXT_FS_CAPABILITIES(handle
, p_ts_res
) | FILE_NAMED_STREAMS
;
1074 static struct vfs_fn_pointers vfs_streams_depot_fns
= {
1075 .fs_capabilities_fn
= streams_depot_fs_capabilities
,
1076 .open_fn
= streams_depot_open
,
1077 .stat_fn
= streams_depot_stat
,
1078 .lstat_fn
= streams_depot_lstat
,
1079 .unlinkat_fn
= streams_depot_unlinkat
,
1080 .renameat_fn
= streams_depot_renameat
,
1081 .streaminfo_fn
= streams_depot_streaminfo
,
1085 NTSTATUS
vfs_streams_depot_init(TALLOC_CTX
*ctx
)
1087 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "streams_depot",
1088 &vfs_streams_depot_fns
);