From: Volker Lendecke Date: Mon, 22 Sep 2025 13:55:57 +0000 (+0200) Subject: vfs: Add SMB_VFS_RENAME_STREAM X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1a8d7c3dd60040a9aed646f73a353712a3153589;p=thirdparty%2Fsamba.git vfs: Add SMB_VFS_RENAME_STREAM [MS-FSA] 2.1.5.15.12.1 Algorithm for Performing Stream Rename is simpler and diffent enough from renaming a file or directory that I believe a separate VFS operation is justified instead of tunneling it through the renameat call. For example it's only possible to rename streams within a file, so only one stream open fsp and a newname which is guaranteed to be a stream name is necessary. Add stub implementations to our streams modules, to be filled later. Signed-off-by: Volker Lendecke vfs_streams_xattr: rename_streams Reviewed-by: Ralph Boehme --- diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c index a3521982f16..abb369c480a 100644 --- a/examples/VFS/skel_opaque.c +++ b/examples/VFS/skel_opaque.c @@ -305,6 +305,15 @@ static int skel_renameat(vfs_handle_struct *handle, return -1; } +static int skel_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + errno = ENOSYS; + return -1; +} + static struct tevent_req *skel_fsync_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -1002,6 +1011,7 @@ static struct vfs_fn_pointers skel_opaque_fns = { .sendfile_fn = skel_sendfile, .recvfile_fn = skel_recvfile, .renameat_fn = skel_renameat, + .rename_stream_fn = skel_rename_stream, .fsync_send_fn = skel_fsync_send, .fsync_recv_fn = skel_fsync_recv, .stat_fn = skel_stat, diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c index 4d1312414a0..9a354619265 100644 --- a/examples/VFS/skel_transparent.c +++ b/examples/VFS/skel_transparent.c @@ -395,6 +395,17 @@ static int skel_renameat(vfs_handle_struct *handle, how); } +static int skel_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + return SMB_VFS_NEXT_RENAME_STREAM(handle, + src_fsp, + dst_name, + replace_if_exists); +} + struct skel_fsync_state { int ret; struct vfs_aio_state vfs_aio_state; @@ -1313,6 +1324,7 @@ static struct vfs_fn_pointers skel_transparent_fns = { .sendfile_fn = skel_sendfile, .recvfile_fn = skel_recvfile, .renameat_fn = skel_renameat, + .rename_stream_fn = skel_rename_stream, .fsync_send_fn = skel_fsync_send, .fsync_recv_fn = skel_fsync_recv, .stat_fn = skel_stat, diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index c51e0b22672..f41f6c5c11c 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -75,6 +75,7 @@ struct tevent_context; SMBPROFILE_STATS_BYTES(syscall_sendfile) \ SMBPROFILE_STATS_BYTES(syscall_recvfile) \ SMBPROFILE_STATS_BASIC(syscall_renameat) \ + SMBPROFILE_STATS_BASIC(syscall_rename_stream) \ SMBPROFILE_STATS_BYTES(syscall_asys_fsync) \ SMBPROFILE_STATS_BASIC(syscall_stat) \ SMBPROFILE_STATS_BASIC(syscall_fstat) \ diff --git a/source3/include/vfs.h b/source3/include/vfs.h index 34bd97fa3de..cf6590a56cd 100644 --- a/source3/include/vfs.h +++ b/source3/include/vfs.h @@ -392,6 +392,8 @@ * Version 50 - Add struct files_struct.fsp_flags.posix_append * Change to Version 51 - will ship with 4.23 * Version 51 - Add ntcreatex_deny_[dos|fcb] and ntcreatex_stream_baseopen + * Change to Version 52 - will ship with 4.24 + * Version 52 - Add rename_stream */ #define SMB_VFS_INTERFACE_VERSION 51 @@ -1076,6 +1078,10 @@ struct vfs_fn_pointers { struct files_struct *dstdir_fsp, const struct smb_filename *smb_fname_dst, const struct vfs_rename_how *how); + int (*rename_stream_fn)(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists); struct tevent_req *(*fsync_send_fn)(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -1585,6 +1591,10 @@ int smb_vfs_call_renameat(struct vfs_handle_struct *handle, struct files_struct *dstfsp, const struct smb_filename *smb_fname_dst, const struct vfs_rename_how *how); +int smb_vfs_call_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists); struct tevent_req *smb_vfs_call_fsync_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, @@ -2024,6 +2034,10 @@ int vfs_not_implemented_renameat(vfs_handle_struct *handle, files_struct *dstfsp, const struct smb_filename *smb_fname_dst, const struct vfs_rename_how *how); +int vfs_not_implemented_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists); struct tevent_req *vfs_not_implemented_fsync_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h index f2d8174369a..7f2e1defb46 100644 --- a/source3/include/vfs_macros.h +++ b/source3/include/vfs_macros.h @@ -209,6 +209,20 @@ #define SMB_VFS_NEXT_RENAMEAT(handle, oldfsp, old, newfsp, newname, how) \ smb_vfs_call_renameat((handle)->next, (oldfsp), (old), (newfsp), (newname), (how)) +#define SMB_VFS_RENAME_STREAM(conn, src_fsp, dst_name, replace_if_exists) \ + smb_vfs_call_rename_stream((conn)->vfs_handles, \ + (src_fsp), \ + (dst_name), \ + (replace_if_exists)) +#define SMB_VFS_NEXT_RENAME_STREAM(handle, \ + src_fsp, \ + dst_name, \ + replace_if_exists) \ + smb_vfs_call_rename_stream((handle)->next, \ + (src_fsp), \ + (dst_name), \ + (replace_if_exists)) + #define SMB_VFS_FSYNC_SEND(mem_ctx, ev, fsp) \ smb_vfs_call_fsync_send((fsp)->conn->vfs_handles, (mem_ctx), (ev), \ (fsp)) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 90c67adcc3b..5501723da9d 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -1304,6 +1304,18 @@ static int vfswrap_renameat(vfs_handle_struct *handle, return result; } +static int vfswrap_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + int result = -1; + START_PROFILE_X(SNUM(handle->conn), syscall_rename_stream); + errno = ENOSYS; + END_PROFILE_X(syscall_rename_stream); + return result; +} + static int vfswrap_stat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { @@ -4061,6 +4073,7 @@ static struct vfs_fn_pointers vfs_default_fns = { .sendfile_fn = vfswrap_sendfile, .recvfile_fn = vfswrap_recvfile, .renameat_fn = vfswrap_renameat, + .rename_stream_fn = vfswrap_rename_stream, .fsync_send_fn = vfswrap_fsync_send, .fsync_recv_fn = vfswrap_fsync_recv, .stat_fn = vfswrap_stat, diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c index fd7b810d59c..febca8fd97a 100644 --- a/source3/modules/vfs_full_audit.c +++ b/source3/modules/vfs_full_audit.c @@ -132,6 +132,7 @@ typedef enum _vfs_op_type { SMB_VFS_OP_SENDFILE, SMB_VFS_OP_RECVFILE, SMB_VFS_OP_RENAMEAT, + SMB_VFS_OP_RENAME_STREAM, SMB_VFS_OP_FSYNC_SEND, SMB_VFS_OP_FSYNC_RECV, SMB_VFS_OP_STAT, @@ -211,7 +212,7 @@ typedef enum _vfs_op_type { SMB_VFS_OP_FSETXATTR, /* aio operations */ - SMB_VFS_OP_AIO_FORCE, + SMB_VFS_OP_AIO_FORCE, /* offline operations */ SMB_VFS_OP_IS_OFFLINE, @@ -267,6 +268,7 @@ static struct { { SMB_VFS_OP_SENDFILE, "sendfile" }, { SMB_VFS_OP_RECVFILE, "recvfile" }, { SMB_VFS_OP_RENAMEAT, "renameat" }, + { SMB_VFS_OP_RENAME_STREAM, "rename_stream" }, { SMB_VFS_OP_FSYNC_SEND, "fsync_send" }, { SMB_VFS_OP_FSYNC_RECV, "fsync_recv" }, { SMB_VFS_OP_STAT, "stat" }, @@ -1449,6 +1451,33 @@ static int smb_full_audit_renameat(vfs_handle_struct *handle, return result; } +static int smb_full_audit_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + int result; + int saved_errno; + + result = SMB_VFS_NEXT_RENAME_STREAM(handle, + src_fsp, + dst_name, + replace_if_exists); + saved_errno = errno; + + do_log(SMB_VFS_OP_RENAME_STREAM, + (result >= 0), + handle, + "%s|%s", + fsp_str_do_log(src_fsp), + dst_name); + + if (result == -1) { + errno = saved_errno; + } + return result; +} + struct smb_full_audit_fsync_state { vfs_handle_struct *handle; files_struct *fsp; @@ -2939,6 +2968,7 @@ static struct vfs_fn_pointers vfs_full_audit_fns = { .sendfile_fn = smb_full_audit_sendfile, .recvfile_fn = smb_full_audit_recvfile, .renameat_fn = smb_full_audit_renameat, + .rename_stream_fn = smb_full_audit_rename_stream, .fsync_send_fn = smb_full_audit_fsync_send, .fsync_recv_fn = smb_full_audit_fsync_recv, .stat_fn = smb_full_audit_stat, diff --git a/source3/modules/vfs_not_implemented.c b/source3/modules/vfs_not_implemented.c index 4a28cf486ce..0e210496b43 100644 --- a/source3/modules/vfs_not_implemented.c +++ b/source3/modules/vfs_not_implemented.c @@ -328,6 +328,16 @@ int vfs_not_implemented_renameat(vfs_handle_struct *handle, return -1; } +_PUBLIC_ +int vfs_not_implemented_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + errno = ENOSYS; + return -1; +} + _PUBLIC_ struct tevent_req *vfs_not_implemented_fsync_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, @@ -1098,6 +1108,7 @@ static struct vfs_fn_pointers vfs_not_implemented_fns = { .sendfile_fn = vfs_not_implemented_sendfile, .recvfile_fn = vfs_not_implemented_recvfile, .renameat_fn = vfs_not_implemented_renameat, + .rename_stream_fn = vfs_not_implemented_rename_stream, .fsync_send_fn = vfs_not_implemented_fsync_send, .fsync_recv_fn = vfs_not_implemented_fsync_recv, .stat_fn = vfs_not_implemented_stat, diff --git a/source3/modules/vfs_streams_depot.c b/source3/modules/vfs_streams_depot.c index 81a11668864..fd44eef76a5 100644 --- a/source3/modules/vfs_streams_depot.c +++ b/source3/modules/vfs_streams_depot.c @@ -1065,6 +1065,15 @@ done: return ret; } +static int streams_depot_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + errno = ENOSYS; + return -1; +} + static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams, struct stream_struct **streams, const char *name, off_t size, @@ -1237,6 +1246,7 @@ static struct vfs_fn_pointers vfs_streams_depot_fns = { .fstatat_fn = streams_depot_fstatat, .unlinkat_fn = streams_depot_unlinkat, .renameat_fn = streams_depot_renameat, + .rename_stream_fn = streams_depot_rename_stream, .fstreaminfo_fn = streams_depot_fstreaminfo, }; diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index e8ce0e419e1..3b870f579be 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -1303,6 +1303,15 @@ static int streams_xattr_renameat(vfs_handle_struct *handle, return ret; } +static int streams_xattr_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + errno = ENOSYS; + return -1; +} + static NTSTATUS walk_xattr_streams(vfs_handle_struct *handle, files_struct *fsp, const struct smb_filename *smb_fname, @@ -2310,6 +2319,7 @@ static struct vfs_fn_pointers vfs_streams_xattr_fns = { .pwrite_recv_fn = streams_xattr_pwrite_recv, .unlinkat_fn = streams_xattr_unlinkat, .renameat_fn = streams_xattr_renameat, + .rename_stream_fn = streams_xattr_rename_stream, .ftruncate_fn = streams_xattr_ftruncate, .fallocate_fn = streams_xattr_fallocate, .fstreaminfo_fn = streams_xattr_fstreaminfo, diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c index 8a9263604c9..83f87b6e790 100644 --- a/source3/modules/vfs_time_audit.c +++ b/source3/modules/vfs_time_audit.c @@ -949,6 +949,38 @@ static int smb_time_audit_renameat(vfs_handle_struct *handle, return result; } +static int smb_time_audit_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + int result; + struct timespec ts1, ts2; + double timediff; + char *msg = NULL; + + clock_gettime_mono(&ts1); + result = SMB_VFS_NEXT_RENAME_STREAM(handle, + src_fsp, + dst_name, + replace_if_exists); + clock_gettime_mono(&ts2); + timediff = nsec_time_diff(&ts2, &ts1) * 1.0e-9; + + if (timediff > audit_timeout) { + msg = talloc_asprintf(talloc_tos(), + "%s -> %s", + fsp_str_dbg(src_fsp), + dst_name); + if (msg != NULL) { + smb_time_audit_log_msg("rename_stream", timediff, msg); + TALLOC_FREE(msg); + } + } + + return result; +} + struct smb_time_audit_fsync_state { struct files_struct *fsp; int ret; @@ -2726,6 +2758,7 @@ static struct vfs_fn_pointers vfs_time_audit_fns = { .sendfile_fn = smb_time_audit_sendfile, .recvfile_fn = smb_time_audit_recvfile, .renameat_fn = smb_time_audit_renameat, + .rename_stream_fn = smb_time_audit_rename_stream, .fsync_send_fn = smb_time_audit_fsync_send, .fsync_recv_fn = smb_time_audit_fsync_recv, .stat_fn = smb_time_audit_stat, @@ -2797,7 +2830,6 @@ static struct vfs_fn_pointers vfs_time_audit_fns = { .freaddir_attr_fn = smb_time_audit_freaddir_attr, }; - static_decl_vfs; NTSTATUS vfs_time_audit_init(TALLOC_CTX *ctx) { diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 82fe586506f..af54fc8f2f5 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -1821,6 +1821,18 @@ int smb_vfs_call_renameat(struct vfs_handle_struct *handle, how); } +int smb_vfs_call_rename_stream(struct vfs_handle_struct *handle, + struct files_struct *src_fsp, + const char *dst_name, + bool replace_if_exists) +{ + VFS_FIND(rename_stream); + return handle->fns->rename_stream_fn(handle, + src_fsp, + dst_name, + replace_if_exists); +} + struct smb_vfs_call_fsync_state { int (*recv_fn)(struct tevent_req *req, struct vfs_aio_state *vfs_aio_state); int retval;