From 699994b6b7ab0264748035a34c0f99f7e406ebec Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 8 Aug 2015 20:21:39 +0200 Subject: [PATCH 1/5] 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 --- source3/modules/vfs_fruit.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 8ac4ba1..545eb14 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -3085,7 +3085,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, @@ -3246,6 +3246,7 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, { NTSTATUS status; struct fruit_config_data *config = NULL; + files_struct *fsp; status = check_aapl(handle, req, in_context_blobs, out_context_blobs); if (!NT_STATUS_IS_OK(status)) { @@ -3267,6 +3268,7 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, if (!NT_STATUS_IS_OK(status)) { return status; } + fsp = *result; if (config->copyfile_enabled) { /* @@ -3275,11 +3277,35 @@ 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) + { + int rc; + + rc = SMB_VFS_STAT(handle->conn, fsp->fsp_name); + if (rc != 0) { + DBG_ERR("stat %s failed\n", fsp_str_dbg(fsp)); + goto fail; + } + + 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; } @@ -3296,11 +3322,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; -- 2.1.0 From 9f0b821a060b1fe873d8e0d4bf1bc36ec602b7d3 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 12 Aug 2015 07:34:53 +0200 Subject: [PATCH 2/5] vfs_fruit: split and simplify fruit_ftruncate Bug: https://bugzilla.samba.org/show_bug.cgi?id=11467 Signed-off-by: Ralph Boehme --- source3/modules/vfs_fruit.c | 90 ++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 545eb14..3a00703 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -3163,6 +3163,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) { + DBG_WARNING("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) @@ -3170,7 +3226,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)); @@ -3183,40 +3238,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; } -- 2.1.0 From 773c40b6e30e47a2b3d289dc759f0eb282b0b674 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 25 Aug 2015 17:06:52 +0200 Subject: [PATCH 3/5] 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 --- source3/modules/vfs_fruit.c | 57 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 3a00703..cae9267 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -2391,13 +2391,41 @@ static int fruit_unlink(vfs_handle_struct *handle, 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)) { + 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); @@ -2406,7 +2434,11 @@ 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) { rc = adouble_path(talloc_tos(), smb_fname->base_name, &adp); @@ -2418,17 +2450,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, -- 2.1.0 From 677122d7c95ce1bd9108b30382849c4d2392e42d Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 6 Aug 2015 11:32:29 +0200 Subject: [PATCH 4/5] 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 --- source4/torture/vfs/fruit.c | 118 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index a74dd7d..43d2902 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -1421,6 +1421,123 @@ 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_adouble_conversion(struct torture_context *tctx, struct smb2_tree *tree1, struct smb2_tree *tree2) @@ -2324,6 +2441,7 @@ struct torture_suite *torture_vfs_fruit(void) torture_suite_add_2ns_smb2_test(suite, "OS X AppleDouble file conversion", test_adouble_conversion); torture_suite_add_2ns_smb2_test(suite, "SMB2/CREATE context AAPL", test_aapl); torture_suite_add_2ns_smb2_test(suite, "stream names", test_stream_names); + torture_suite_add_2ns_smb2_test(suite, "truncate resource fork to 0 bytes", test_rfork_truncate); return suite; } -- 2.1.0 From f60bcee6b9f5a4a0810b886ee596031379f95a1d Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 7 Aug 2015 15:48:33 +0200 Subject: [PATCH 5/5] 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 --- source4/torture/vfs/fruit.c | 129 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 43d2902..1c1d4ad 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); @@ -1538,6 +1545,127 @@ done: 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__); + + 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"); + + ZERO_STRUCT(create); + create.in.fname = fname; + 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; + status = smb2_create(tree1, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + + ret = check_stream_list(tree1, tctx, fname, 1, streams, + create.out.file.handle); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list"); + smb2_util_close(tree1, create.out.file.handle); + + torture_comment(tctx, "(%s) close empty created rfork, open should return ENOENT\n", + __location__); + smb2_util_close(tree1, fh1); + 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"); + +done: + smb2_util_unlink(tree1, fname); + smb2_deltree(tree1, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + static bool test_adouble_conversion(struct torture_context *tctx, struct smb2_tree *tree1, struct smb2_tree *tree2) @@ -2442,6 +2570,7 @@ struct torture_suite *torture_vfs_fruit(void) torture_suite_add_2ns_smb2_test(suite, "SMB2/CREATE context AAPL", test_aapl); torture_suite_add_2ns_smb2_test(suite, "stream names", test_stream_names); torture_suite_add_2ns_smb2_test(suite, "truncate resource fork to 0 bytes", test_rfork_truncate); + torture_suite_add_2ns_smb2_test(suite, "opening and creating resource fork", test_rfork_create); return suite; } -- 2.1.0