Changeset View
Changeset View
Standalone View
Standalone View
files/patch-source3_modules_vfs__streams__xattr.c
Property | Old Value | New Value |
---|---|---|
fbsd:nokeywords | null | yes \ No newline at end of property |
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
--- source3/modules/vfs_streams_xattr.c.orig 2019-01-15 10:07:00 UTC | |||||
+++ 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,79 @@ static SMB_INO_T stream_inode(const SMB_ | |||||
} | |||||
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); | |||||
+ // ? -1 | |||||
+ return result; | |||||
+} | |||||
- if (!NT_STATUS_IS_OK(status)) { | |||||
- return -1; | |||||
+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); | |||||
+ /* We may have xattr of a 0 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; | |||||
+ // ? user. | |||||
+ 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 +168,7 @@ static NTSTATUS streams_xattr_get_name(v | |||||
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 +180,7 @@ static NTSTATUS streams_xattr_get_name(v | |||||
* 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 +212,7 @@ static NTSTATUS streams_xattr_get_name(v | |||||
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); | |||||
@@ -270,8 +325,8 @@ static int streams_xattr_fstat(vfs_handl | |||||
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); | |||||
@@ -446,10 +501,10 @@ static int streams_xattr_open(vfs_handle | |||||
goto fail; | |||||
} | |||||
- 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)) { | |||||
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { | |||||
@@ -480,19 +535,13 @@ static int streams_xattr_open(vfs_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; | |||||
@@ -678,8 +727,8 @@ static int streams_xattr_rename(vfs_hand | |||||
} | |||||
/* 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; | |||||
@@ -766,14 +815,13 @@ static NTSTATUS walk_xattr_streams(vfs_h | |||||
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))); | |||||
@@ -835,16 +883,17 @@ 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; | |||||
+ // ? -1 | |||||
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; | |||||
} | |||||
@@ -964,14 +1013,17 @@ static ssize_t streams_xattr_pwrite(vfs_ | |||||
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); | |||||
@@ -981,6 +1033,8 @@ static ssize_t streams_xattr_pwrite(vfs_ | |||||
return -1; | |||||
} | |||||
+ frame = talloc_stackframe(); | |||||
+ | |||||
/* Create an smb_filename with stream_name == NULL. */ | |||||
smb_fname_base = synthetic_smb_fname(talloc_tos(), | |||||
sio->base, | |||||
@@ -988,39 +1042,55 @@ static ssize_t streams_xattr_pwrite(vfs_ | |||||
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); | |||||
- if (!NT_STATUS_IS_OK(status)) { | |||||
- return -1; | |||||
- } | |||||
- | |||||
- if ((offset + n) > ea.value.length-1) { | |||||
- uint8_t *tmp; | |||||
+ status = get_xattr_value(talloc_tos(), handle->conn, | |||||
+ smb_fname_base, sio->xattr_name, &ea); | |||||
- tmp = talloc_realloc(talloc_tos(), ea.value.data, uint8_t, | |||||
- offset + n + 1); | |||||
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { | |||||
+ /* | |||||
+ * This can happen if we sit behind vfs_fruit: | |||||
+ * fruit_ftruncate calls UNLINK on an attribute | |||||
+ * truncating the "file" to zero length. A later | |||||
+ * pwrite faces a non-existing attribute, we need to | |||||
+ * cope with that here. | |||||
+ * | |||||
+ * This might be not the last word on this. | |||||
+ */ | |||||
- 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; | |||||
- } | |||||
+ ea = (struct ea_struct) {0}; | |||||
+ ea.name = talloc_strdup(talloc_tos(), sio->xattr_name); | |||||
+ if (ea.name == NULL) { | |||||
+ TALLOC_FREE(frame); | |||||
+ errno = ENOMEM; | |||||
+ return -1; | |||||
+ } | |||||
+ status = NT_STATUS_OK; | |||||
+ } | |||||
- memcpy(ea.value.data + offset, data, n); | |||||
+ if (!NT_STATUS_IS_OK(status)) { | |||||
+ TALLOC_FREE(frame); | |||||
+ return -1; | |||||
+ } | |||||
+ // ? -1 | |||||
+ 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; | |||||
@@ -1033,15 +1103,17 @@ static ssize_t streams_xattr_pread(vfs_h | |||||
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); | |||||
@@ -1051,6 +1123,8 @@ static ssize_t streams_xattr_pread(vfs_h | |||||
return -1; | |||||
} | |||||
+ frame = talloc_stackframe(); | |||||
+ | |||||
/* Create an smb_filename with stream_name == NULL. */ | |||||
smb_fname_base = synthetic_smb_fname(talloc_tos(), | |||||
sio->base, | |||||
@@ -1058,31 +1132,35 @@ static ssize_t streams_xattr_pread(vfs_h | |||||
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; | |||||
} | |||||
+ // ? -1 | |||||
+ //length = ea.value.length-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 { | |||||
@@ -1249,16 +1327,18 @@ static int streams_xattr_ftruncate(struc | |||||
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); | |||||
@@ -1268,6 +1348,8 @@ static int streams_xattr_ftruncate(struc | |||||
return -1; | |||||
} | |||||
+ frame = talloc_stackframe(); | |||||
+ | |||||
/* Create an smb_filename with stream_name == NULL. */ | |||||
smb_fname_base = synthetic_smb_fname(talloc_tos(), | |||||
sio->base, | |||||
@@ -1275,40 +1357,46 @@ static int streams_xattr_ftruncate(struc | |||||
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; | |||||
@@ -1326,9 +1414,9 @@ static int streams_xattr_fallocate(struc | |||||
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); |