The branch, v4-2-test has been updated via e806635 s4:torture:vfs_fruit: created empty resourceforks via 1f796a5 s4:torture:vfs_fruit: add a resource fork truncation test via 33bc5d2 vfs_fruit: delete ._ file when deleting the basefile via aeda612 vfs_fruit: split and simplify fruit_ftruncate via 09653dc vfs_fruit: handling of empty resource fork from 9bc968a samr4: Use <SID=%s> in GetGroupsForUser
https://git.samba.org/?p=samba.git;a=shortlog;h=v4-2-test - Log ----------------------------------------------------------------- commit e806635294d5211f5cfe7ea833384d3e6503f0c4 Author: Ralph Boehme <s...@samba.org> Date: Fri Aug 7 15:48:33 2015 +0200 s4:torture:vfs_fruit: created empty resourceforks Check for opens and creates, created empty resourceforks result in ENOENT in subsequent opens. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11467 Signed-off-by: Ralph Boehme <s...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> Autobuild-User(master): Jeremy Allison <j...@samba.org> Autobuild-Date(master): Wed Sep 2 06:50:16 CEST 2015 on sn-devel-104 Autobuild-User(v4-2-test): Karolin Seeger <ksee...@samba.org> Autobuild-Date(v4-2-test): Fri Sep 4 15:24:26 CEST 2015 on sn-devel-104 commit 1f796a53bd5dd767a921eee8ea794bf3169a993f Author: Ralph Boehme <s...@samba.org> Date: Thu Aug 6 11:32:29 2015 +0200 s4:torture:vfs_fruit: add a resource fork truncation test Truncating a resource fork to 0 bytes should make it inaccessible for subsequent creates and return NT_STATUS_OBJECT_NAME_NOT_FOUND. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11467 Signed-off-by: Ralph Boehme <s...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 33bc5d28b4480bdfff266554301ade9af335ff2e Author: Ralph Boehme <s...@samba.org> Date: Tue Aug 25 17:06:52 2015 +0200 vfs_fruit: delete ._ file when deleting the basefile 0 byte resource fork streams are not listed by vfs_streaminfo, as a result stream cleanup/deletion of file deletion doesn't remove the resourcefork stream. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11467 Signed-off-by: Ralph Boehme <s...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit aeda61241868077a9c771bfa656b079af5e8681e Author: Ralph Boehme <s...@samba.org> Date: Wed Aug 12 07:34:53 2015 +0200 vfs_fruit: split and simplify fruit_ftruncate Bug: https://bugzilla.samba.org/show_bug.cgi?id=11467 Signed-off-by: Ralph Boehme <s...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 09653dc7110f239b42e2a7c0ba32081cda86fbab Author: Ralph Boehme <s...@samba.org> Date: Sat Aug 8 20:21:39 2015 +0200 vfs_fruit: handling of empty resource fork Opening the resource fork stream with O_CREAT mustn't create a visible node in the filesystem, only create a file handle. As long as the creator didn't write into the stream, other openers withour O_CREAT MUST get an ENOENT error. This is way OS X SMB server implements it. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11467 Signed-off-by: Ralph Boehme <s...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> ----------------------------------------------------------------------- Summary of changes: source3/modules/vfs_fruit.c | 186 ++++++++++++++++++++++++--------- source4/torture/vfs/fruit.c | 247 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 383 insertions(+), 50 deletions(-) Changeset truncated at 500 lines: diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index fb9350b..f60ec5e 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -2396,15 +2396,44 @@ static int fruit_unlink(vfs_handle_struct *handle, { int rc = -1; struct fruit_config_data *config = NULL; - char *adp = NULL; - - if (!is_ntfs_stream_smb_fname(smb_fname)) { - return SMB_VFS_NEXT_UNLINK(handle, smb_fname); - } SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data, return -1); + if (!is_ntfs_stream_smb_fname(smb_fname)) { + char *adp = NULL; + + rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); + if (rc != 0) { + return -1; + } + + if (config->rsrc != FRUIT_RSRC_ADFILE) { + return 0; + } + + /* + * 0 byte resource fork streams are not listed by + * vfs_streaminfo, as a result stream cleanup/deletion of file + * deletion doesn't remove the resourcefork stream. + */ + rc = adouble_path(talloc_tos(), + smb_fname->base_name, &adp); + if (rc != 0) { + return -1; + } + + /* FIXME: direct unlink(), missing smb_fname */ + DEBUG(1,("fruit_unlink: %s\n", adp)); + rc = unlink(adp); + if ((rc == -1) && (errno == ENOENT)) { + rc = 0; + } + + TALLOC_FREE(adp); + return 0; + } + if (is_afpinfo_stream(smb_fname)) { if (config->meta == FRUIT_META_STREAM) { rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); @@ -2413,8 +2442,14 @@ static int fruit_unlink(vfs_handle_struct *handle, smb_fname->base_name, AFPINFO_EA_NETATALK); } - } else if (is_afpresource_stream(smb_fname)) { + + return rc; + } + + if (is_afpresource_stream(smb_fname)) { if (config->rsrc == FRUIT_RSRC_ADFILE) { + char *adp = NULL; + rc = adouble_path(talloc_tos(), smb_fname->base_name, &adp); if (rc != 0) { @@ -2425,17 +2460,20 @@ static int fruit_unlink(vfs_handle_struct *handle, if ((rc == -1) && (errno == ENOENT)) { rc = 0; } + TALLOC_FREE(adp); } else { rc = SMB_VFS_REMOVEXATTR(handle->conn, - smb_fname->base_name, - AFPRESOURCE_EA_NETATALK); + smb_fname->base_name, + AFPRESOURCE_EA_NETATALK); } - } else { - rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); + + return rc; } - TALLOC_FREE(adp); - return rc; + return SMB_VFS_NEXT_UNLINK(handle, smb_fname); + + + return 0; } static int fruit_chmod(vfs_handle_struct *handle, @@ -3092,7 +3130,7 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle, if (config->rsrc != FRUIT_RSRC_STREAM) { ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_RSRC); - if (ad) { + if (ad && (ad_getentrylen(ad, ADEID_RFORK) > 0)) { if (!add_fruit_stream( mem_ctx, pnum_streams, pstreams, AFPRESOURCE_STREAM_NAME, @@ -3170,6 +3208,62 @@ static int fruit_fallocate(struct vfs_handle_struct *handle, return -1; } +static int fruit_ftruncate_meta(struct vfs_handle_struct *handle, + struct files_struct *fsp, + off_t offset, + struct adouble *ad) +{ + /* + * As this request hasn't been seen in the wild, + * the only sensible use I can imagine is the client + * truncating the stream to 0 bytes size. + * We simply remove the metadata on such a request. + */ + if (offset != 0) { + DEBUG(1,("ftruncate %s to %jd", + fsp_str_dbg(fsp), (intmax_t)offset)); + return -1; + } + + return SMB_VFS_FREMOVEXATTR(fsp, AFPRESOURCE_EA_NETATALK); +} + +static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle, + struct files_struct *fsp, + off_t offset, + struct adouble *ad) +{ + int rc; + struct fruit_config_data *config; + + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct fruit_config_data, return -1); + + if (config->rsrc == FRUIT_RSRC_XATTR && offset == 0) { + return SMB_VFS_FREMOVEXATTR(fsp, + AFPRESOURCE_EA_NETATALK); + } + + rc = SMB_VFS_NEXT_FTRUNCATE( + handle, fsp, + offset + ad_getentryoff(ad, ADEID_RFORK)); + if (rc != 0) { + return -1; + } + + if (config->rsrc == FRUIT_RSRC_ADFILE) { + ad_setentrylen(ad, ADEID_RFORK, offset); + rc = ad_write(ad, NULL); + if (rc != 0) { + return -1; + } + DEBUG(10, ("fruit_ftruncate_rsrc file %s offset %jd\n", + fsp_str_dbg(fsp), (intmax_t)offset)); + } + + return 0; +} + static int fruit_ftruncate(struct vfs_handle_struct *handle, struct files_struct *fsp, off_t offset) @@ -3177,7 +3271,6 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle, int rc = 0; struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp); - struct fruit_config_data *config; DEBUG(10, ("streams_xattr_ftruncate called for file %s offset %.0f\n", fsp_str_dbg(fsp), (double)offset)); @@ -3190,40 +3283,15 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle, return -1; } - SMB_VFS_HANDLE_GET_DATA(handle, config, - struct fruit_config_data, return -1); - switch (ad->ad_type) { case ADOUBLE_META: - /* - * As this request hasn't been seen in the wild, - * the only sensible use I can imagine is the client - * truncating the stream to 0 bytes size. - * We simply remove the metadata on such a request. - */ - if (offset == 0) { - rc = SMB_VFS_FREMOVEXATTR(fsp, - AFPRESOURCE_EA_NETATALK); - } + rc = fruit_ftruncate_meta(handle, fsp, offset, ad); break; + case ADOUBLE_RSRC: - if (config->rsrc == FRUIT_RSRC_XATTR && offset == 0) { - rc = SMB_VFS_FREMOVEXATTR(fsp, - AFPRESOURCE_EA_NETATALK); - } else { - rc = SMB_VFS_NEXT_FTRUNCATE( - handle, fsp, - offset + ad_getentryoff(ad, ADEID_RFORK)); - if (rc != 0) { - return -1; - } - ad_setentrylen(ad, ADEID_RFORK, offset); - rc = ad_write(ad, NULL); - if (rc != 0) { - return -1; - } - } + rc = fruit_ftruncate_rsrc(handle, fsp, offset, ad); break; + default: return -1; } @@ -3253,6 +3321,7 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, { NTSTATUS status; struct fruit_config_data *config = NULL; + files_struct *fsp = NULL; status = check_aapl(handle, req, in_context_blobs, out_context_blobs); if (!NT_STATUS_IS_OK(status)) { @@ -3274,6 +3343,7 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, if (!NT_STATUS_IS_OK(status)) { return status; } + fsp = *result; if (config->copyfile_enabled) { /* @@ -3282,11 +3352,27 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, * for copychunk should be allowed in a copychunk * request with a count of 0. */ - (*result)->aapl_copyfile_supported = true; + fsp->aapl_copyfile_supported = true; + } + + /* + * If this is a plain open for existing files, opening an 0 + * byte size resource fork MUST fail with + * NT_STATUS_OBJECT_NAME_NOT_FOUND. + * + * Cf the vfs_fruit torture tests in test_rfork_create(). + */ + if (is_afpresource_stream(fsp->fsp_name) && + create_disposition == FILE_OPEN) + { + if (fsp->fsp_name->st.st_ex_size == 0) { + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } } + if (is_ntfs_stream_smb_fname(smb_fname) - || (*result == NULL) - || ((*result)->is_directory)) { + || fsp->is_directory) { return status; } @@ -3303,11 +3389,11 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, return status; fail: - DEBUG(1, ("fruit_create_file: %s\n", nt_errstr(status))); + DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status))); - if (*result) { - close_file(req, *result, ERROR_CLOSE); - *result = NULL; + if (fsp) { + close_file(req, fsp, ERROR_CLOSE); + *result = fsp = NULL; } return status; diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 34b5342..5ca55e7 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -59,6 +59,13 @@ goto done; \ }} while (0) +static bool check_stream_list(struct smb2_tree *tree, + struct torture_context *tctx, + const char *fname, + int num_exp, + const char **exp, + struct smb2_handle h); + static int qsort_string(char * const *s1, char * const *s2) { return strcmp(*s1, *s2); @@ -1421,6 +1428,244 @@ done: return ret; } +static bool test_rfork_truncate(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_rfork_truncate"; + const char *rfork = BASEDIR "\\torture_rfork_truncate" AFPRESOURCE_STREAM; + const char *rfork_content = "1234567890"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create create; + struct smb2_handle fh1, fh2, fh3; + union smb_setfileinfo sinfo; + + smb2_util_unlink(tree1, fname); + + status = torture_smb2_testdir(tree1, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree1, testdirh); + + ret = torture_setup_file(mem_ctx, tree1, fname, false); + if (ret == false) { + goto done; + } + + ret &= write_stream(tree1, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM, + 10, 10, rfork_content); + + /* Truncate back to size 0, further access MUST return ENOENT */ + + torture_comment(tctx, "(%s) truncate resource fork to size 0\n", + __location__); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = fname; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree1, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh1 = create.out.file.handle; + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree1, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh2 = create.out.file.handle; + + ZERO_STRUCT(sinfo); + sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sinfo.end_of_file_info.in.file.handle = fh2; + sinfo.end_of_file_info.in.size = 0; + status = smb2_setinfo_file(tree1, &sinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_setinfo_file"); + + /* + * Now check size, we should get OBJECT_NAME_NOT_FOUND (!) + */ + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree1, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, "smb2_create"); + + /* + * Do another open on the rfork and write to the new handle. A + * naive server might unlink the AppleDouble resource fork + * file when its truncated to 0 bytes above, so in case both + * open handles share the same underlying fd, the unlink would + * cause the below write to be lost. + */ + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree1, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh3 = create.out.file.handle; + + status = smb2_util_write(tree1, fh3, "foo", 0, 3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_write"); + + smb2_util_close(tree1, fh3); + smb2_util_close(tree1, fh2); + smb2_util_close(tree1, fh1); + + ret = check_stream(tree1, __location__, tctx, mem_ctx, fname, AFPRESOURCE_STREAM, + 0, 3, 0, 3, "foo"); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream"); + +done: + smb2_util_unlink(tree1, fname); + smb2_deltree(tree1, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +static bool test_rfork_create(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_rfork_create"; + const char *rfork = BASEDIR "\\torture_rfork_create" AFPRESOURCE_STREAM; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create create; + struct smb2_handle fh1; + const char *streams[] = { + "::$DATA" + }; + union smb_fileinfo finfo; + + smb2_util_unlink(tree1, fname); + + status = torture_smb2_testdir(tree1, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir"); + smb2_util_close(tree1, testdirh); + + ret = torture_setup_file(mem_ctx, tree1, fname, false); + if (ret == false) { + goto done; + } + + torture_comment(tctx, "(%s) open rfork, should return ENOENT\n", + __location__); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree1, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, "smb2_create"); + + torture_comment(tctx, "(%s) create resource fork\n", __location__); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree1, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh1 = create.out.file.handle; + + torture_comment(tctx, "(%s) getinfo on create handle\n", + __location__); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION; + finfo.generic.in.file.handle = fh1; + status = smb2_getinfo_file(tree1, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file"); + if (finfo.all_info.out.size != 0) { + torture_result(tctx, TORTURE_FAIL, + "(%s) Incorrect resource fork size\n", + __location__); + ret = false; + smb2_util_close(tree1, fh1); + goto done; + } + + torture_comment(tctx, "(%s) open rfork, should still return ENOENT\n", + __location__); + -- Samba Shared Repository