From ed315114a0551c25ad6aa392599816cf79384756 Mon Sep 17 00:00:00 2001 From: "Timur I. Bakeyev" Date: Fri, 29 Jun 2018 22:40:19 +0200 Subject: [PATCH] Remove the 64K limitation for the stream by replacing get_ea_value() with the locale equivalent function get_xattr_value(). That also fixes the odd behaviour where extra '\0' have been added at the end of the stream's data. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11606 Signed-off-by: Timur I. Bakeyev --- source3/modules/vfs_streams_xattr.c | 265 ++++++++++++++++++++++-------------- 1 file changed, 163 insertions(+), 102 deletions(-) diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index 8714007cb8d..ee0a32f6ebc 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -1,10 +1,10 @@ /* * Store streams in xattrs * - * Copyright (C) Volker Lendecke, 2008 + * Copyright (C) Volker Lendecke, 2008 + * Copyright (C) Timur I. Bakeyev, 2017 * * Partly based on James Peach's Darwin module, which is - * * Copyright (C) James Peach 2006-2007 * * This program is free software; you can redistribute it and/or modify @@ -79,25 +79,78 @@ static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname) } static ssize_t get_xattr_size(connection_struct *conn, - const struct smb_filename *smb_fname, - const char *xattr_name) + const struct smb_filename *smb_fname, + const char *xattr_name) { - NTSTATUS status; - struct ea_struct ea; ssize_t result; - status = get_ea_value(talloc_tos(), conn, NULL, smb_fname, - xattr_name, &ea); + result = SMB_VFS_GETXATTR(conn, smb_fname, xattr_name, NULL, 0); - if (!NT_STATUS_IS_OK(status)) { - return -1; + return result; +} + +static NTSTATUS get_xattr_value(TALLOC_CTX *mem_ctx, + connection_struct *conn, + const struct smb_filename *smb_fname, + const char *ea_name, + struct ea_struct *pea) +{ + ssize_t attr_size; + + attr_size = get_xattr_size(conn, smb_fname, ea_name); + + if (attr_size == -1) { + return map_nt_error_from_unix(errno); } - result = ea.value.length-1; - TALLOC_FREE(ea.value.data); - return result; + pea->value = data_blob_talloc(mem_ctx, NULL, attr_size); + if(pea->value.data == NULL && attr_size) { + DEBUG(5, + ("get_xattr_value: for EA '%s' failed to allocate %lu bytes\n", + ea_name, (unsigned long)attr_size) + ); + return NT_STATUS_NO_MEMORY; + } + + attr_size = SMB_VFS_GETXATTR(conn, smb_fname, ea_name, pea->value.data, pea->value.length); + + if (attr_size == -1) { + return map_nt_error_from_unix(errno); + } + + if(pea->value.length != attr_size) { + DEBUG(5, + ("get_xattr_value: for EA '%s' requested %lu, read %lu bytes\n", + ea_name, (unsigned long)pea->value.length, (unsigned long)attr_size) + ); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(10,("get_xattr_value: EA '%s' is of length %lu\n", ea_name, (unsigned long)attr_size)); + /* + * This can dump huge amount of data multiple times. For example + * for 1Mb ADS and chunk size 64Kb the same 1Mb dump will be + * logged 16 times! + */ + dump_data(50, (uint8_t *)pea->value.data, pea->value.length); + + pea->flags = 0; + + if (strnequal(ea_name, "user.", 5)) { + pea->name = talloc_strdup(mem_ctx, &ea_name[5]); + } else { + pea->name = talloc_strdup(mem_ctx, ea_name); + } + + if (pea->name == NULL) { + data_blob_free(&pea->value); + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; } + /** * Given a stream name, populate xattr_name with the xattr name to use for * accessing the stream. @@ -114,6 +167,7 @@ static NTSTATUS streams_xattr_get_name(vfs_handle_struct *handle, SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config, return NT_STATUS_UNSUCCESSFUL); + /* stream_name is passed as ':stream', so skip leading ':' */ sname = talloc_strdup(ctx, stream_name + 1); if (sname == NULL) { return NT_STATUS_NO_MEMORY; @@ -125,7 +179,7 @@ static NTSTATUS streams_xattr_get_name(vfs_handle_struct *handle, * characters from their on-the-wire Unicode Private Range * encoding to their native ASCII representation. * - * As as result the name of xattrs storing the streams (via + * As a result the name of xattrs storing the streams (via * vfs_streams_xattr) may contain a colon, so we have to use * strrchr_m() instead of strchr_m() for matching the stream * type suffix. @@ -157,7 +211,7 @@ static NTSTATUS streams_xattr_get_name(vfs_handle_struct *handle, return NT_STATUS_NO_MEMORY; } - DEBUG(10, ("xattr_name: %s, stream_name: %s\n", *xattr_name, + DEBUG(10, ("xattr_name: '%s', stream_name: '%s'\n", *xattr_name, stream_name)); talloc_free(sname); @@ -265,8 +319,8 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp, return -1; } - sbuf->st_ex_size = get_xattr_size(handle->conn, - smb_fname_base, io->xattr_name); + sbuf->st_ex_size = get_xattr_size(handle->conn, smb_fname_base, + io->xattr_name); if (sbuf->st_ex_size == -1) { TALLOC_FREE(smb_fname_base); SET_STAT_INVALID(*sbuf); @@ -453,10 +507,10 @@ static int streams_xattr_open(vfs_handle_struct *handle, pipe_fds[1] = -1; fakefd = pipe_fds[0]; - status = get_ea_value(talloc_tos(), handle->conn, NULL, - smb_fname, xattr_name, &ea); + status = get_xattr_value(talloc_tos(), handle->conn, + smb_fname, xattr_name, &ea); - DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status))); + DEBUG(10, ("get_xattr_value returned %s\n", nt_errstr(status))); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { @@ -476,19 +530,13 @@ static int streams_xattr_open(vfs_handle_struct *handle, /* * The attribute does not exist or needs to be truncated */ - - /* - * Darn, xattrs need at least 1 byte - */ - char null = '\0'; - DEBUG(10, ("creating or truncating attribute %s on file %s\n", xattr_name, smb_fname->base_name)); ret = SMB_VFS_SETXATTR(fsp->conn, smb_fname, xattr_name, - &null, sizeof(null), + NULL, 0, flags & O_EXCL ? XATTR_CREATE : 0); if (ret != 0) { goto fail; @@ -631,8 +679,8 @@ static int streams_xattr_rename(vfs_handle_struct *handle, } /* read the old stream */ - status = get_ea_value(talloc_tos(), handle->conn, NULL, - smb_fname_src, src_xattr_name, &ea); + status = get_xattr_value(talloc_tos(), handle->conn, + smb_fname_src, src_xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { errno = ENOENT; goto fail; @@ -719,14 +767,13 @@ static NTSTATUS walk_xattr_streams(vfs_handle_struct *handle, continue; } - status = get_ea_value(names, + status = get_xattr_value(names, handle->conn, - NULL, smb_fname, names[i], &ea); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("Could not get ea %s for file %s: %s\n", + DEBUG(10, ("Could not get EA %s for file %s: %s\n", names[i], smb_fname->base_name, nt_errstr(status))); @@ -788,16 +835,16 @@ struct streaminfo_state { NTSTATUS status; }; -static bool collect_one_stream(struct ea_struct *ea, void *private_data) +static bool collect_one_stream(struct ea_struct *pea, void *private_data) { struct streaminfo_state *state = (struct streaminfo_state *)private_data; if (!add_one_stream(state->mem_ctx, &state->num_streams, &state->streams, - ea->name, ea->value.length-1, + pea->name, pea->value.length, smb_roundup(state->handle->conn, - ea->value.length-1))) { + pea->value.length))) { state->status = NT_STATUS_NO_MEMORY; return false; } @@ -917,14 +964,17 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle, files_struct *fsp, const void *data, size_t n, off_t offset) { - struct stream_io *sio = + struct stream_io *sio = (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp); + struct smb_filename *smb_fname_base = NULL; + TALLOC_CTX *frame = NULL; + struct ea_struct ea; NTSTATUS status; - struct smb_filename *smb_fname_base = NULL; int ret; - DEBUG(10, ("streams_xattr_pwrite called for %d bytes\n", (int)n)); + DEBUG(10, ("streams_xattr_pwrite: offset=%lu, size=%lu\n", + (unsigned long)offset, (unsigned long)n)); if (sio == NULL) { return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); @@ -934,6 +984,8 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle, return -1; } + frame = talloc_stackframe(); + /* Create an smb_filename with stream_name == NULL. */ smb_fname_base = synthetic_smb_fname(talloc_tos(), sio->base, @@ -945,35 +997,28 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle, return -1; } - status = get_ea_value(talloc_tos(), handle->conn, NULL, - smb_fname_base, sio->xattr_name, &ea); + status = get_xattr_value(talloc_tos(), handle->conn, + smb_fname_base, sio->xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return -1; } - if ((offset + n) > ea.value.length-1) { - uint8_t *tmp; - - tmp = talloc_realloc(talloc_tos(), ea.value.data, uint8_t, - offset + n + 1); - - if (tmp == NULL) { - TALLOC_FREE(ea.value.data); - errno = ENOMEM; - return -1; - } - ea.value.data = tmp; - ea.value.length = offset + n + 1; - ea.value.data[offset+n] = 0; - } - - memcpy(ea.value.data + offset, data, n); + if ((offset + n) > ea.value.length) { + if(!data_blob_realloc(talloc_tos(), &ea.value, offset + n)) { + TALLOC_FREE(frame); + errno = ENOMEM; + return -1; + } + } + memcpy(ea.value.data + offset, data, n); ret = SMB_VFS_SETXATTR(fsp->conn, fsp->fsp_name, sio->xattr_name, ea.value.data, ea.value.length, 0); - TALLOC_FREE(ea.value.data); + + TALLOC_FREE(frame); if (ret == -1) { return -1; @@ -986,15 +1031,17 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle, files_struct *fsp, void *data, size_t n, off_t offset) { - struct stream_io *sio = + struct stream_io *sio = (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp); + struct smb_filename *smb_fname_base = NULL; + TALLOC_CTX *frame = NULL; + struct ea_struct ea; NTSTATUS status; - size_t length, overlap; - struct smb_filename *smb_fname_base = NULL; + size_t overlap; - DEBUG(10, ("streams_xattr_pread: offset=%d, size=%d\n", - (int)offset, (int)n)); + DEBUG(10, ("streams_xattr_pread: offset=%lu, size=%lu\n", + (unsigned long)offset, (unsigned long)n)); if (sio == NULL) { return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset); @@ -1004,6 +1051,8 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle, return -1; } + frame = talloc_stackframe(); + /* Create an smb_filename with stream_name == NULL. */ smb_fname_base = synthetic_smb_fname(talloc_tos(), sio->base, @@ -1011,31 +1060,33 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle, NULL, fsp->fsp_name->flags); if (smb_fname_base == NULL) { + TALLOC_FREE(frame); errno = ENOMEM; return -1; } - status = get_ea_value(talloc_tos(), handle->conn, NULL, - smb_fname_base, sio->xattr_name, &ea); + status = get_xattr_value(talloc_tos(), handle->conn, + smb_fname_base, sio->xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return -1; } - length = ea.value.length-1; + DEBUG(10, ("streams_xattr_pread: get_xattr_value() returned %lu bytes\n", + (unsigned long)ea.value.length)); - DEBUG(10, ("streams_xattr_pread: get_ea_value returned %d bytes\n", - (int)length)); + /* Attempt to read past EOF. */ + if (ea.value.length <= offset) { + TALLOC_FREE(frame); + return 0; + } - /* Attempt to read past EOF. */ - if (length <= offset) { - return 0; - } + overlap = (offset + n) > ea.value.length ? (ea.value.length - offset) : n; + memcpy(data, ea.value.data + offset, overlap); - overlap = (offset + n) > length ? (length - offset) : n; - memcpy(data, ea.value.data + offset, overlap); + TALLOC_FREE(frame); - TALLOC_FREE(ea.value.data); - return overlap; + return overlap; } struct streams_xattr_pread_state { @@ -1202,16 +1253,18 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle, struct files_struct *fsp, off_t offset) { - int ret; - uint8_t *tmp; - struct ea_struct ea; - NTSTATUS status; - struct stream_io *sio = + struct stream_io *sio = (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp); struct smb_filename *smb_fname_base = NULL; + TALLOC_CTX *frame = NULL; - DEBUG(10, ("streams_xattr_ftruncate called for file %s offset %.0f\n", - fsp_str_dbg(fsp), (double)offset)); + struct ea_struct ea; + NTSTATUS status; + size_t orig_length; + int ret; + + DEBUG(10, ("streams_xattr_ftruncate: called for file '%s' with offset %lu\n", + fsp_str_dbg(fsp), (unsigned long)offset)); if (sio == NULL) { return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset); @@ -1221,6 +1274,8 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle, return -1; } + frame = talloc_stackframe(); + /* Create an smb_filename with stream_name == NULL. */ smb_fname_base = synthetic_smb_fname(talloc_tos(), sio->base, @@ -1228,40 +1283,46 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle, NULL, fsp->fsp_name->flags); if (smb_fname_base == NULL) { + TALLOC_FREE(frame); errno = ENOMEM; return -1; } - status = get_ea_value(talloc_tos(), handle->conn, NULL, - smb_fname_base, sio->xattr_name, &ea); + status = get_xattr_value(talloc_tos(), handle->conn, + smb_fname_base, sio->xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return -1; } + orig_length = ea.value.length; - tmp = talloc_realloc(talloc_tos(), ea.value.data, uint8_t, - offset + 1); + /* Requested size matches the original size */ + if(orig_length == offset) { + TALLOC_FREE(frame); + return 0; + } - if (tmp == NULL) { - TALLOC_FREE(ea.value.data); + /* That can both shrink and expand */ + /* XXX: If offset == 0 the result of talloc_realloc is NULL, but still valid */ + if(offset && !data_blob_realloc(talloc_tos(), &ea.value, offset)) { + TALLOC_FREE(frame); errno = ENOMEM; return -1; } - /* Did we expand ? */ - if (ea.value.length < offset + 1) { - memset(&tmp[ea.value.length], '\0', - offset + 1 - ea.value.length); + /* If we expanded, fill up extra space with zeros */ + if (orig_length < offset) { + memset(ea.value.data + orig_length, 0, + offset - orig_length); } - ea.value.data = tmp; - ea.value.length = offset + 1; - ea.value.data[offset] = 0; - + /* XXX: We should use ea.value.length here, but when offset == 0 + it's not reset to 0 in data_blob_realloc() */ ret = SMB_VFS_SETXATTR(fsp->conn, fsp->fsp_name, sio->xattr_name, - ea.value.data, ea.value.length, 0); - TALLOC_FREE(ea.value.data); + ea.value.data, offset, 0); + TALLOC_FREE(frame); if (ret == -1) { return -1; @@ -1279,9 +1340,9 @@ static int streams_xattr_fallocate(struct vfs_handle_struct *handle, struct stream_io *sio = (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp); - DEBUG(10, ("streams_xattr_fallocate called for file %s offset %.0f" - "len = %.0f\n", - fsp_str_dbg(fsp), (double)offset, (double)len)); + DEBUG(10, ("streams_xattr_fallocate: called for file '%s' with offset %lu" + "len = %lu\n", + fsp_str_dbg(fsp), (unsigned long)offset, (unsigned long)len)); if (sio == NULL) { return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len); -- 2.16.3