mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
ec3c37ee53
BUG: https://bugzilla.samba.org/show_bug.cgi?id=13584 Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org> Autobuild-User(master): Jeremy Allison <jra@samba.org> Autobuild-Date(master): Tue Aug 21 02:33:05 CEST 2018 on sn-devel-144
5112 lines
168 KiB
C
5112 lines
168 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
|
|
vfs_fruit tests
|
|
|
|
Copyright (C) Ralph Boehme 2014
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "system/filesys.h"
|
|
#include "libcli/libcli.h"
|
|
#include "libcli/smb2/smb2.h"
|
|
#include "libcli/smb2/smb2_calls.h"
|
|
#include "libcli/smb/smb2_create_ctx.h"
|
|
#include "lib/cmdline/popt_common.h"
|
|
#include "param/param.h"
|
|
#include "libcli/resolve/resolve.h"
|
|
#include "MacExtensions.h"
|
|
#include "lib/util/tsort.h"
|
|
|
|
#include "torture/torture.h"
|
|
#include "torture/util.h"
|
|
#include "torture/smb2/proto.h"
|
|
#include "torture/vfs/proto.h"
|
|
#include "librpc/gen_ndr/ndr_ioctl.h"
|
|
#include "libcli/security/dom_sid.h"
|
|
#include "../librpc/gen_ndr/ndr_security.h"
|
|
#include "libcli/security/secace.h"
|
|
#include "libcli/security/security_descriptor.h"
|
|
|
|
#define BASEDIR "vfs_fruit_dir"
|
|
#define FNAME_CC_SRC "testfsctl.dat"
|
|
#define FNAME_CC_DST "testfsctl2.dat"
|
|
|
|
#define CHECK_STATUS(status, correct) do { \
|
|
if (!NT_STATUS_EQUAL(status, correct)) { \
|
|
torture_result(tctx, TORTURE_FAIL, \
|
|
"(%s) Incorrect status %s - should be %s\n", \
|
|
__location__, nt_errstr(status), nt_errstr(correct)); \
|
|
ret = false; \
|
|
goto done; \
|
|
}} while (0)
|
|
|
|
#define CHECK_VALUE(v, correct) do { \
|
|
if ((v) != (correct)) { \
|
|
torture_result(tctx, TORTURE_FAIL, \
|
|
"(%s) Incorrect value %s=%u - should be %u\n", \
|
|
__location__, #v, (unsigned)v, (unsigned)correct); \
|
|
ret = false; \
|
|
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,
|
|
bool is_dir);
|
|
|
|
static int qsort_string(char * const *s1, char * const *s2)
|
|
{
|
|
return strcmp(*s1, *s2);
|
|
}
|
|
|
|
static int qsort_stream(const struct stream_struct * s1, const struct stream_struct *s2)
|
|
{
|
|
return strcmp(s1->stream_name.s, s2->stream_name.s);
|
|
}
|
|
|
|
/*
|
|
* REVIEW:
|
|
* This is hokey, but what else can we do?
|
|
*/
|
|
#if defined(HAVE_ATTROPEN) || defined(FREEBSD)
|
|
#define AFPINFO_EA_NETATALK "org.netatalk.Metadata"
|
|
#define AFPRESOURCE_EA_NETATALK "org.netatalk.ResourceFork"
|
|
#else
|
|
#define AFPINFO_EA_NETATALK "user.org.netatalk.Metadata"
|
|
#define AFPRESOURCE_EA_NETATALK "user.org.netatalk.ResourceFork"
|
|
#endif
|
|
|
|
/*
|
|
The metadata xattr char buf below contains the following attributes:
|
|
|
|
-------------------------------------------------------------------------------
|
|
Entry ID : 00000008 : File Dates Info
|
|
Offset : 00000162 : 354
|
|
Length : 00000010 : 16
|
|
|
|
-DATE------: : (GMT) : (Local)
|
|
create : 1B442169 : Mon Jun 30 13:23:53 2014 : Mon Jun 30 15:23:53 2014
|
|
modify : 1B442169 : Mon Jun 30 13:23:53 2014 : Mon Jun 30 15:23:53 2014
|
|
backup : 80000000 : Unknown or Initial
|
|
access : 1B442169 : Mon Jun 30 13:23:53 2014 : Mon Jun 30 15:23:53 2014
|
|
|
|
-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 1B 44 21 69 1B 44 21 69 80 00 00 00 1B 44 21 69 : .D!i.D!i.....D!i
|
|
|
|
-------------------------------------------------------------------------------
|
|
Entry ID : 00000009 : Finder Info
|
|
Offset : 0000007A : 122
|
|
Length : 00000020 : 32
|
|
|
|
-FInfo-----:
|
|
Type : 42415252 : BARR
|
|
Creator : 464F4F4F : FOOO
|
|
isAlias : 0
|
|
Invisible : 1
|
|
hasBundle : 0
|
|
nameLocked : 0
|
|
Stationery : 0
|
|
CustomIcon : 0
|
|
Reserved : 0
|
|
Inited : 0
|
|
NoINITS : 0
|
|
Shared : 0
|
|
SwitchLaunc: 0
|
|
Hidden Ext : 0
|
|
color : 000 : none
|
|
isOnDesk : 0
|
|
Location v : 0000 : 0
|
|
Location h : 0000 : 0
|
|
Fldr : 0000 : ..
|
|
|
|
-FXInfo----:
|
|
Rsvd|IconID: 0000 : 0
|
|
Rsvd : 0000 : ..
|
|
Rsvd : 0000 : ..
|
|
Rsvd : 0000 : ..
|
|
AreInvalid : 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
CustomBadge: 0
|
|
ObjctIsBusy: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
RoutingInfo: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
Rsvd|commnt: 0000 : 0
|
|
PutAway : 00000000 : 0
|
|
|
|
-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 42 41 52 52 46 4F 4F 4F 40 00 00 00 00 00 00 00 : BARRFOOO@.......
|
|
00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
|
|
-------------------------------------------------------------------------------
|
|
Entry ID : 0000000E : AFP File Info
|
|
Offset : 00000172 : 370
|
|
Length : 00000004 : 4
|
|
|
|
-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 00 00 01 A1 : ....
|
|
*/
|
|
|
|
char metadata_xattr[] = {
|
|
0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
|
|
0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x08, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00,
|
|
0x00, 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
|
|
0x00, 0x7a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
|
|
0x00, 0x0e, 0x00, 0x00, 0x01, 0x72, 0x00, 0x00,
|
|
0x00, 0x04, 0x80, 0x44, 0x45, 0x56, 0x00, 0x00,
|
|
0x01, 0x76, 0x00, 0x00, 0x00, 0x08, 0x80, 0x49,
|
|
0x4e, 0x4f, 0x00, 0x00, 0x01, 0x7e, 0x00, 0x00,
|
|
0x00, 0x08, 0x80, 0x53, 0x59, 0x4e, 0x00, 0x00,
|
|
0x01, 0x86, 0x00, 0x00, 0x00, 0x08, 0x80, 0x53,
|
|
0x56, 0x7e, 0x00, 0x00, 0x01, 0x8e, 0x00, 0x00,
|
|
0x00, 0x04, 0x42, 0x41, 0x52, 0x52, 0x46, 0x4f,
|
|
0x4f, 0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x1b, 0x44, 0x21, 0x69, 0x1b, 0x44,
|
|
0x21, 0x69, 0x80, 0x00, 0x00, 0x00, 0x1b, 0x44,
|
|
0x21, 0x69, 0x00, 0x00, 0x01, 0xa1, 0x00, 0xfd,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x20,
|
|
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0xe3,
|
|
0x86, 0x53, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x01,
|
|
0x00, 0x00
|
|
};
|
|
|
|
/*
|
|
The buf below contains the following AppleDouble encoded data:
|
|
|
|
-------------------------------------------------------------------------------
|
|
MagicNumber: 00051607 : AppleDouble
|
|
Version : 00020000 : Version 2
|
|
Filler : 4D 61 63 20 4F 53 20 58 20 20 20 20 20 20 20 20 : Mac OS X
|
|
Num. of ent: 0002 : 2
|
|
|
|
-------------------------------------------------------------------------------
|
|
Entry ID : 00000009 : Finder Info
|
|
Offset : 00000032 : 50
|
|
Length : 00000EB0 : 3760
|
|
|
|
-FInfo-----:
|
|
Type : 54455354 : TEST
|
|
Creator : 534C4F57 : SLOW
|
|
isAlias : 0
|
|
Invisible : 0
|
|
hasBundle : 0
|
|
nameLocked : 0
|
|
Stationery : 0
|
|
CustomIcon : 0
|
|
Reserved : 0
|
|
Inited : 0
|
|
NoINITS : 0
|
|
Shared : 0
|
|
SwitchLaunc: 0
|
|
Hidden Ext : 0
|
|
color : 100 : blue
|
|
isOnDesk : 0
|
|
Location v : 0000 : 0
|
|
Location h : 0000 : 0
|
|
Fldr : 0000 : ..
|
|
|
|
-FXInfo----:
|
|
Rsvd|IconID: 0000 : 0
|
|
Rsvd : 0000 : ..
|
|
Rsvd : 0000 : ..
|
|
Rsvd : 0000 : ..
|
|
AreInvalid : 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
CustomBadge: 0
|
|
ObjctIsBusy: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
RoutingInfo: 0
|
|
unknown bit: 0
|
|
unknown bit: 0
|
|
Rsvd|commnt: 0000 : 0
|
|
PutAway : 00000000 : 0
|
|
|
|
-EA--------:
|
|
pad : 0000 : ..
|
|
magic : 41545452 : ATTR
|
|
debug_tag : 53D4580C : 1406425100
|
|
total_size : 00000EE2 : 3810
|
|
data_start : 000000BC : 188
|
|
data_length: 0000005E : 94
|
|
reserved[0]: 00000000 : ....
|
|
reserved[1]: 00000000 : ....
|
|
reserved[2]: 00000000 : ....
|
|
flags : 0000 : ..
|
|
num_attrs : 0002 : 2
|
|
-EA ENTRY--:
|
|
offset : 000000BC : 188
|
|
length : 0000005B : 91
|
|
flags : 0000 : ..
|
|
namelen : 24 : 36
|
|
-EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 63 6F 6D 2E 61 70 70 6C 65 2E 6D 65 74 61 64 61 : com.apple.metada
|
|
00000010 : 74 61 3A 5F 6B 4D 44 49 74 65 6D 55 73 65 72 54 : ta:_kMDItemUserT
|
|
00000020 : 61 67 73 00 : ags.
|
|
-EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 62 70 6C 69 73 74 30 30 A5 01 02 03 04 05 54 74 : bplist00......Tt
|
|
00000010 : 65 73 74 66 00 47 00 72 00 FC 00 6E 00 0A 00 32 : estf.G.r...n...2
|
|
00000020 : 56 4C 69 6C 61 0A 33 56 47 65 6C 62 0A 35 56 42 : VLila.3VGelb.5VB
|
|
00000030 : 6C 61 75 0A 34 08 0E 13 20 27 2E 00 00 00 00 00 : lau.4... '......
|
|
00000040 : 00 01 01 00 00 00 00 00 00 00 06 00 00 00 00 00 : ................
|
|
00000050 : 00 00 00 00 00 00 00 00 00 00 35 : ..........5
|
|
-EA ENTRY--:
|
|
offset : 00000117 : 279
|
|
length : 00000003 : 3
|
|
flags : 0000 : ..
|
|
namelen : 08 : 8
|
|
-EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 66 6F 6F 3A 62 61 72 00 : foo:bar.
|
|
-EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 62 61 7A : baz
|
|
|
|
-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 54 45 53 54 53 4C 4F 57 00 08 00 00 00 00 00 00 : TESTSLOW........
|
|
00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
00000020 : 00 00 41 54 54 52 53 D4 58 0C 00 00 0E E2 00 00 : ..ATTRS.X.......
|
|
00000030 : 00 BC 00 00 00 5E 00 00 00 00 00 00 00 00 00 00 : .....^..........
|
|
00000040 : 00 00 00 00 00 02 00 00 00 BC 00 00 00 5B 00 00 : .............[..
|
|
00000050 : 24 63 6F 6D 2E 61 70 70 6C 65 2E 6D 65 74 61 64 : $com.apple.metad
|
|
00000060 : 61 74 61 3A 5F 6B 4D 44 49 74 65 6D 55 73 65 72 : ata:_kMDItemUser
|
|
00000070 : 54 61 67 73 00 00 00 00 01 17 00 00 00 03 00 00 : Tags............
|
|
00000080 : 08 66 6F 6F 3A 62 61 72 00 66 62 70 6C 69 73 74 : .foo:bar.fbplist
|
|
00000090 : 30 30 A5 01 02 03 04 05 54 74 65 73 74 66 00 47 : 00......Ttestf.G
|
|
000000A0 : 00 72 00 FC 00 6E 00 0A 00 32 56 4C 69 6C 61 0A : .r...n...2VLila.
|
|
000000B0 : 33 56 47 65 6C 62 0A 35 56 42 6C 61 75 0A 34 08 : 3VGelb.5VBlau.4.
|
|
000000C0 : 0E 13 20 27 2E 00 00 00 00 00 00 01 01 00 00 00 : .. '............
|
|
000000D0 : 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
000000E0 : 00 00 00 00 35 62 61 7A 00 00 00 00 00 00 00 00 : ....5baz........
|
|
000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
... all zeroes ...
|
|
00000EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
|
|
-------------------------------------------------------------------------------
|
|
Entry ID : 00000002 : Resource Fork
|
|
Offset : 00000EE2 : 3810
|
|
Length : 0000011E : 286
|
|
|
|
-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)
|
|
00000000 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................
|
|
00000010 : 54 68 69 73 20 72 65 73 6F 75 72 63 65 20 66 6F : This resource fo
|
|
00000020 : 72 6B 20 69 6E 74 65 6E 74 69 6F 6E 61 6C 6C 79 : rk intentionally
|
|
00000030 : 20 6C 65 66 74 20 62 6C 61 6E 6B 20 20 20 00 00 : left blank ..
|
|
00000040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
00000050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
00000060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
00000070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
00000080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
00000090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
000000A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
000000B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
000000C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
000000D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
000000E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
|
|
00000100 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................
|
|
00000110 : 00 00 00 00 00 00 00 00 00 1C 00 1E FF FF : ..............
|
|
|
|
It was created with:
|
|
$ hexdump -ve '"\t" 7/1 "0x%02x, " 1/1 " 0x%02x," "\n"'
|
|
*/
|
|
static char osx_adouble_w_xattr[] = {
|
|
0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00,
|
|
0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58,
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
|
0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
|
|
0x00, 0x32, 0x00, 0x00, 0x0e, 0xb0, 0x00, 0x00,
|
|
0x00, 0x02, 0x00, 0x00, 0x0e, 0xe2, 0x00, 0x00,
|
|
0x01, 0x1e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x4c,
|
|
0x4f, 0x57, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x41, 0x54, 0x54, 0x52,
|
|
0x53, 0xd4, 0x58, 0x0c, 0x00, 0x00, 0x0e, 0xe2,
|
|
0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x5e,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
|
0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x5b,
|
|
0x00, 0x00, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x61,
|
|
0x70, 0x70, 0x6c, 0x65, 0x2e, 0x6d, 0x65, 0x74,
|
|
0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x5f, 0x6b,
|
|
0x4d, 0x44, 0x49, 0x74, 0x65, 0x6d, 0x55, 0x73,
|
|
0x65, 0x72, 0x54, 0x61, 0x67, 0x73, 0x00, 0x00,
|
|
0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x00, 0x03,
|
|
0x00, 0x00, 0x08, 0x66, 0x6f, 0x6f, 0x3a, 0x62,
|
|
0x61, 0x72, 0x00, 0x66, 0x62, 0x70, 0x6c, 0x69,
|
|
0x73, 0x74, 0x30, 0x30, 0xa5, 0x01, 0x02, 0x03,
|
|
0x04, 0x05, 0x54, 0x74, 0x65, 0x73, 0x74, 0x66,
|
|
0x00, 0x47, 0x00, 0x72, 0x00, 0xfc, 0x00, 0x6e,
|
|
0x00, 0x0a, 0x00, 0x32, 0x56, 0x4c, 0x69, 0x6c,
|
|
0x61, 0x0a, 0x33, 0x56, 0x47, 0x65, 0x6c, 0x62,
|
|
0x0a, 0x35, 0x56, 0x42, 0x6c, 0x61, 0x75, 0x0a,
|
|
0x34, 0x08, 0x0e, 0x13, 0x20, 0x27, 0x2e, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x62,
|
|
0x61, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x1e, 0x54, 0x68, 0x69, 0x73, 0x20, 0x72,
|
|
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20,
|
|
0x66, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x74,
|
|
0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
|
|
0x6c, 0x79, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20,
|
|
0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x20, 0x20, 0x20,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x1c, 0x00, 0x1e, 0xff, 0xff
|
|
};
|
|
|
|
/**
|
|
* talloc and intialize an AfpInfo
|
|
**/
|
|
static AfpInfo *torture_afpinfo_new(TALLOC_CTX *mem_ctx)
|
|
{
|
|
AfpInfo *info;
|
|
|
|
info = talloc_zero(mem_ctx, AfpInfo);
|
|
if (info == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
info->afpi_Signature = AFP_Signature;
|
|
info->afpi_Version = AFP_Version;
|
|
info->afpi_BackupTime = AFP_BackupTime;
|
|
|
|
return info;
|
|
}
|
|
|
|
/**
|
|
* Pack AfpInfo into a talloced buffer
|
|
**/
|
|
static char *torture_afpinfo_pack(TALLOC_CTX *mem_ctx,
|
|
AfpInfo *info)
|
|
{
|
|
char *buf;
|
|
|
|
buf = talloc_zero_array(mem_ctx, char, AFP_INFO_SIZE);
|
|
if (buf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
RSIVAL(buf, 0, info->afpi_Signature);
|
|
RSIVAL(buf, 4, info->afpi_Version);
|
|
RSIVAL(buf, 12, info->afpi_BackupTime);
|
|
memcpy(buf + 16, info->afpi_FinderInfo, sizeof(info->afpi_FinderInfo));
|
|
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Unpack AfpInfo
|
|
**/
|
|
#if 0
|
|
static void torture_afpinfo_unpack(AfpInfo *info, char *data)
|
|
{
|
|
info->afpi_Signature = RIVAL(data, 0);
|
|
info->afpi_Version = RIVAL(data, 4);
|
|
info->afpi_BackupTime = RIVAL(data, 12);
|
|
memcpy(info->afpi_FinderInfo, (const char *)data + 16,
|
|
sizeof(info->afpi_FinderInfo));
|
|
}
|
|
#endif
|
|
|
|
static bool torture_write_afpinfo(struct smb2_tree *tree,
|
|
struct torture_context *tctx,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *fname,
|
|
AfpInfo *info)
|
|
{
|
|
struct smb2_handle handle;
|
|
struct smb2_create io;
|
|
NTSTATUS status;
|
|
const char *full_name;
|
|
char *infobuf;
|
|
bool ret = true;
|
|
|
|
full_name = talloc_asprintf(mem_ctx, "%s%s", fname, AFPINFO_STREAM_NAME);
|
|
if (full_name == NULL) {
|
|
torture_comment(tctx, "talloc_asprintf error\n");
|
|
return false;
|
|
}
|
|
ZERO_STRUCT(io);
|
|
io.in.desired_access = SEC_FILE_WRITE_DATA;
|
|
io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
|
|
io.in.create_options = 0;
|
|
io.in.fname = full_name;
|
|
|
|
status = smb2_create(tree, mem_ctx, &io);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
handle = io.out.file.handle;
|
|
|
|
infobuf = torture_afpinfo_pack(mem_ctx, info);
|
|
if (infobuf == NULL) {
|
|
return false;
|
|
}
|
|
|
|
status = smb2_util_write(tree, handle, infobuf, 0, AFP_INFO_SIZE);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
smb2_util_close(tree, handle);
|
|
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Read 'count' bytes at 'offset' from stream 'fname:sname' and
|
|
* compare against buffer 'value'
|
|
**/
|
|
static bool check_stream(struct smb2_tree *tree,
|
|
const char *location,
|
|
struct torture_context *tctx,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *fname,
|
|
const char *sname,
|
|
off_t read_offset,
|
|
size_t read_count,
|
|
off_t comp_offset,
|
|
size_t comp_count,
|
|
const char *value)
|
|
{
|
|
struct smb2_handle handle;
|
|
struct smb2_create create;
|
|
struct smb2_read r;
|
|
NTSTATUS status;
|
|
char *full_name;
|
|
bool ret = true;
|
|
|
|
full_name = talloc_asprintf(mem_ctx, "%s%s", fname, sname);
|
|
if (full_name == NULL) {
|
|
torture_comment(tctx, "talloc_asprintf error\n");
|
|
return false;
|
|
}
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_READ_DATA;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.fname = full_name;
|
|
|
|
torture_comment(tctx, "Open stream %s\n", full_name);
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
TALLOC_FREE(full_name);
|
|
if (value == NULL) {
|
|
return true;
|
|
}
|
|
torture_comment(tctx, "Unable to open stream %s\n", full_name);
|
|
return false;
|
|
}
|
|
|
|
handle = create.out.file.handle;
|
|
if (value == NULL) {
|
|
TALLOC_FREE(full_name);
|
|
smb2_util_close(tree, handle);
|
|
return true;
|
|
}
|
|
|
|
ZERO_STRUCT(r);
|
|
r.in.file.handle = handle;
|
|
r.in.length = read_count;
|
|
r.in.offset = read_offset;
|
|
|
|
status = smb2_read(tree, tree, &r);
|
|
|
|
torture_assert_ntstatus_ok_goto(
|
|
tctx, status, ret, done,
|
|
talloc_asprintf(tctx, "(%s) Failed to read %lu bytes from stream '%s'\n",
|
|
location, (long)strlen(value), full_name));
|
|
|
|
torture_assert_goto(tctx, r.out.data.length == read_count, ret, done,
|
|
talloc_asprintf(tctx, "smb2_read returned %jd bytes, expected %jd\n",
|
|
(intmax_t)r.out.data.length, (intmax_t)read_count));
|
|
|
|
torture_assert_goto(
|
|
tctx, memcmp(r.out.data.data + comp_offset, value, comp_count) == 0,
|
|
ret, done,
|
|
talloc_asprintf(tctx, "(%s) Bad data in stream\n", location));
|
|
|
|
done:
|
|
TALLOC_FREE(full_name);
|
|
smb2_util_close(tree, handle);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Read 'count' bytes at 'offset' from stream 'fname:sname' and
|
|
* compare against buffer 'value'
|
|
**/
|
|
static ssize_t read_stream(struct smb2_tree *tree,
|
|
const char *location,
|
|
struct torture_context *tctx,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *fname,
|
|
const char *sname,
|
|
off_t read_offset,
|
|
size_t read_count)
|
|
{
|
|
struct smb2_handle handle;
|
|
struct smb2_create create;
|
|
struct smb2_read r;
|
|
NTSTATUS status;
|
|
const char *full_name;
|
|
bool ret = true;
|
|
|
|
full_name = talloc_asprintf(mem_ctx, "%s%s", fname, sname);
|
|
if (full_name == NULL) {
|
|
torture_comment(tctx, "talloc_asprintf error\n");
|
|
return -1;
|
|
}
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_READ_DATA;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.fname = full_name;
|
|
|
|
torture_comment(tctx, "Open stream %s\n", full_name);
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
torture_comment(tctx, "Unable to open stream %s\n",
|
|
full_name);
|
|
return -1;
|
|
}
|
|
|
|
handle = create.out.file.handle;
|
|
|
|
ZERO_STRUCT(r);
|
|
r.in.file.handle = handle;
|
|
r.in.length = read_count;
|
|
r.in.offset = read_offset;
|
|
|
|
status = smb2_read(tree, tree, &r);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
CHECK_STATUS(status, NT_STATUS_END_OF_FILE);
|
|
}
|
|
|
|
smb2_util_close(tree, handle);
|
|
|
|
done:
|
|
if (ret == false) {
|
|
return -1;
|
|
}
|
|
return r.out.data.length;
|
|
}
|
|
|
|
/**
|
|
* Read 'count' bytes at 'offset' from stream 'fname:sname' and
|
|
* compare against buffer 'value'
|
|
**/
|
|
static bool write_stream(struct smb2_tree *tree,
|
|
const char *location,
|
|
struct torture_context *tctx,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *fname,
|
|
const char *sname,
|
|
off_t offset,
|
|
size_t size,
|
|
const char *value)
|
|
{
|
|
struct smb2_handle handle;
|
|
struct smb2_create create;
|
|
NTSTATUS status;
|
|
const char *full_name;
|
|
|
|
full_name = talloc_asprintf(mem_ctx, "%s%s", fname, sname ? sname : "");
|
|
if (full_name == NULL) {
|
|
torture_comment(tctx, "talloc_asprintf error\n");
|
|
return false;
|
|
}
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_WRITE_DATA;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
|
|
create.in.fname = full_name;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
if (value == NULL) {
|
|
return true;
|
|
} else {
|
|
torture_comment(tctx, "Unable to open stream %s\n",
|
|
full_name);
|
|
sleep(10000000);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
handle = create.out.file.handle;
|
|
if (value == NULL) {
|
|
return true;
|
|
}
|
|
|
|
status = smb2_util_write(tree, handle, value, offset, size);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
torture_comment(tctx, "(%s) Failed to write %lu bytes to "
|
|
"stream '%s'\n", location, (long)size, full_name);
|
|
return false;
|
|
}
|
|
|
|
smb2_util_close(tree, handle);
|
|
return true;
|
|
}
|
|
|
|
static bool torture_setup_local_xattr(struct torture_context *tctx,
|
|
const char *path_option,
|
|
const char *name,
|
|
const char *xattr,
|
|
const char *metadata,
|
|
size_t size)
|
|
{
|
|
int ret = true;
|
|
int result;
|
|
const char *spath;
|
|
char *path;
|
|
|
|
spath = torture_setting_string(tctx, path_option, NULL);
|
|
if (spath == NULL) {
|
|
printf("No sharepath for option %s\n", path_option);
|
|
return false;
|
|
}
|
|
|
|
path = talloc_asprintf(tctx, "%s/%s", spath, name);
|
|
|
|
result = setxattr(path, xattr, metadata, size, 0);
|
|
if (result != 0) {
|
|
ret = false;
|
|
}
|
|
|
|
TALLOC_FREE(path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Create a file or directory
|
|
**/
|
|
static bool torture_setup_file(TALLOC_CTX *mem_ctx, struct smb2_tree *tree,
|
|
const char *name, bool dir)
|
|
{
|
|
struct smb2_create io;
|
|
NTSTATUS status;
|
|
|
|
smb2_util_unlink(tree, name);
|
|
ZERO_STRUCT(io);
|
|
io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
|
|
io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
|
|
io.in.share_access =
|
|
NTCREATEX_SHARE_ACCESS_DELETE|
|
|
NTCREATEX_SHARE_ACCESS_READ|
|
|
NTCREATEX_SHARE_ACCESS_WRITE;
|
|
io.in.create_options = 0;
|
|
io.in.fname = name;
|
|
if (dir) {
|
|
io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
|
|
io.in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE;
|
|
io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
io.in.create_disposition = NTCREATEX_DISP_CREATE;
|
|
}
|
|
|
|
status = smb2_create(tree, mem_ctx, &io);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return false;
|
|
}
|
|
|
|
status = smb2_util_close(tree, io.out.file.handle);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool enable_aapl(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
NTSTATUS status;
|
|
bool ret = true;
|
|
struct smb2_create io;
|
|
DATA_BLOB data;
|
|
struct smb2_create_blob *aapl = NULL;
|
|
uint32_t aapl_server_caps;
|
|
uint32_t expected_scaps = (SMB2_CRTCTX_AAPL_UNIX_BASED |
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR |
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE |
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE);
|
|
bool is_osx_server = torture_setting_bool(tctx, "osx", false);
|
|
|
|
ZERO_STRUCT(io);
|
|
io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
|
|
io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
io.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
io.in.share_access = (NTCREATEX_SHARE_ACCESS_DELETE |
|
|
NTCREATEX_SHARE_ACCESS_READ |
|
|
NTCREATEX_SHARE_ACCESS_WRITE);
|
|
io.in.fname = "";
|
|
|
|
/*
|
|
* Issuing an SMB2/CREATE with a suitably formed AAPL context,
|
|
* controls behaviour of Apple's SMB2 extensions for the whole
|
|
* session!
|
|
*/
|
|
|
|
data = data_blob_talloc(mem_ctx, NULL, 3 * sizeof(uint64_t));
|
|
SBVAL(data.data, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
|
|
SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS |
|
|
SMB2_CRTCTX_AAPL_VOLUME_CAPS |
|
|
SMB2_CRTCTX_AAPL_MODEL_INFO));
|
|
SBVAL(data.data, 16, (SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR |
|
|
SMB2_CRTCTX_AAPL_UNIX_BASED |
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE));
|
|
|
|
status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_blob_add");
|
|
|
|
status = smb2_create(tree, tctx, &io);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create");
|
|
|
|
status = smb2_util_close(tree, io.out.file.handle);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close");
|
|
|
|
/*
|
|
* Now check returned AAPL context
|
|
*/
|
|
torture_comment(tctx, "Comparing returned AAPL capabilities\n");
|
|
|
|
aapl = smb2_create_blob_find(&io.out.blobs,
|
|
SMB2_CREATE_TAG_AAPL);
|
|
torture_assert_goto(tctx, aapl != NULL, ret, done, "missing AAPL context");
|
|
|
|
if (!is_osx_server) {
|
|
size_t expected_aapl_ctx_size;
|
|
|
|
expected_aapl_ctx_size = strlen("MacSamba") * 2 + 40;
|
|
|
|
torture_assert_goto(
|
|
tctx, aapl->data.length == expected_aapl_ctx_size,
|
|
ret, done, "bad AAPL size");
|
|
}
|
|
|
|
aapl_server_caps = BVAL(aapl->data.data, 16);
|
|
torture_assert_goto(tctx, aapl_server_caps == expected_scaps,
|
|
ret, done, "bad AAPL caps");
|
|
|
|
done:
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_read_netatalk_metadata(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\torture_read_metadata";
|
|
NTSTATUS status;
|
|
struct smb2_handle testdirh;
|
|
bool ret = true;
|
|
ssize_t len;
|
|
const char *localdir = NULL;
|
|
|
|
torture_comment(tctx, "Checking metadata access\n");
|
|
|
|
localdir = torture_setting_string(tctx, "localdir", NULL);
|
|
if (localdir == NULL) {
|
|
torture_skip(tctx, "Need localdir for test");
|
|
}
|
|
|
|
smb2_util_unlink(tree, fname);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
if (ret == false) {
|
|
goto done;
|
|
}
|
|
|
|
ret = torture_setup_local_xattr(tctx, "localdir",
|
|
BASEDIR "/torture_read_metadata",
|
|
AFPINFO_EA_NETATALK,
|
|
metadata_xattr, sizeof(metadata_xattr));
|
|
if (ret == false) {
|
|
goto done;
|
|
}
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 0, 4, "AFP");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 16, 8, "BARRFOOO");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
16, 8, 0, 3, "AFP");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
/* Check reading offset and read size > sizeof(AFPINFO_STREAM) */
|
|
|
|
len = read_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
AFPINFO_STREAM, 0, 61);
|
|
CHECK_VALUE(len, 60);
|
|
|
|
len = read_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
AFPINFO_STREAM, 59, 2);
|
|
CHECK_VALUE(len, 2);
|
|
|
|
len = read_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
AFPINFO_STREAM, 60, 1);
|
|
CHECK_VALUE(len, 1);
|
|
|
|
len = read_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
AFPINFO_STREAM, 61, 1);
|
|
CHECK_VALUE(len, 0);
|
|
|
|
done:
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_read_afpinfo(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\torture_read_metadata";
|
|
NTSTATUS status;
|
|
struct smb2_handle testdirh;
|
|
bool ret = true;
|
|
ssize_t len;
|
|
AfpInfo *info;
|
|
const char *type_creator = "SMB,OLE!";
|
|
|
|
torture_comment(tctx, "Checking metadata access\n");
|
|
|
|
smb2_util_unlink(tree, fname);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir failed");
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed");
|
|
|
|
info = torture_afpinfo_new(mem_ctx);
|
|
torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed");
|
|
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 0, 4, "AFP");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 16, 8, type_creator);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
/*
|
|
* OS X ignores offset <= 60 and treats the as
|
|
* offset=0. Reading from offsets > 60 returns EOF=0.
|
|
*/
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
16, 8, 0, 8, "AFP\0\0\0\001\0");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
len = read_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
AFPINFO_STREAM, 0, 61);
|
|
torture_assert_goto(tctx, len == 60, ret, done, "read_stream failed");
|
|
|
|
len = read_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
AFPINFO_STREAM, 59, 2);
|
|
torture_assert_goto(tctx, len == 2, ret, done, "read_stream failed");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
59, 2, 0, 2, "AF");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
len = read_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
AFPINFO_STREAM, 60, 1);
|
|
torture_assert_goto(tctx, len == 1, ret, done, "read_stream failed");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
60, 1, 0, 1, "A");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
len = read_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
AFPINFO_STREAM, 61, 1);
|
|
torture_assert_goto(tctx, len == 0, ret, done, "read_stream failed");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_write_atalk_metadata(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\torture_write_metadata";
|
|
const char *type_creator = "SMB,OLE!";
|
|
NTSTATUS status;
|
|
struct smb2_handle testdirh;
|
|
bool ret = true;
|
|
AfpInfo *info;
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
if (ret == false) {
|
|
goto done;
|
|
}
|
|
|
|
info = torture_afpinfo_new(mem_ctx);
|
|
if (info == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
|
|
ret &= check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 16, 8, type_creator);
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_write_atalk_rfork_io(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\torture_write_rfork_io";
|
|
const char *rfork = BASEDIR "\\torture_write_rfork_io" AFPRESOURCE_STREAM_NAME;
|
|
const char *rfork_content = "1234567890";
|
|
NTSTATUS status;
|
|
struct smb2_handle testdirh;
|
|
bool ret = true;
|
|
|
|
union smb_open io;
|
|
struct smb2_handle filehandle;
|
|
union smb_fileinfo finfo;
|
|
union smb_setfileinfo sinfo;
|
|
|
|
smb2_util_unlink(tree, fname);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
if (ret == false) {
|
|
goto done;
|
|
}
|
|
|
|
torture_comment(tctx, "(%s) writing to resource fork\n",
|
|
__location__);
|
|
|
|
ret &= write_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
10, 10, rfork_content);
|
|
|
|
ret &= check_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
0, 20, 10, 10, rfork_content);
|
|
|
|
/* Check size after write */
|
|
|
|
ZERO_STRUCT(io);
|
|
io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
|
|
SEC_FILE_WRITE_ATTRIBUTE;
|
|
io.smb2.in.fname = rfork;
|
|
status = smb2_create(tree, mem_ctx, &(io.smb2));
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
filehandle = io.smb2.out.file.handle;
|
|
|
|
torture_comment(tctx, "(%s) check resource fork size after write\n",
|
|
__location__);
|
|
|
|
ZERO_STRUCT(finfo);
|
|
finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
|
|
finfo.generic.in.file.handle = filehandle;
|
|
status = smb2_getinfo_file(tree, mem_ctx, &finfo);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
if (finfo.all_info.out.size != 20) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) Incorrect resource fork size\n",
|
|
__location__);
|
|
ret = false;
|
|
smb2_util_close(tree, filehandle);
|
|
goto done;
|
|
}
|
|
smb2_util_close(tree, filehandle);
|
|
|
|
/* Write at large offset */
|
|
|
|
torture_comment(tctx, "(%s) writing to resource fork at large offset\n",
|
|
__location__);
|
|
|
|
ret &= write_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
(off_t)64*1024*1024, 10, rfork_content);
|
|
|
|
ret &= check_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
(off_t)64*1024*1024, 10, 0, 10, rfork_content);
|
|
|
|
/* Truncate back to size of 1 byte */
|
|
|
|
torture_comment(tctx, "(%s) truncate resource fork and check size\n",
|
|
__location__);
|
|
|
|
ZERO_STRUCT(io);
|
|
io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
io.smb2.in.desired_access = SEC_FILE_ALL;
|
|
io.smb2.in.fname = rfork;
|
|
status = smb2_create(tree, mem_ctx, &(io.smb2));
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
filehandle = io.smb2.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 = filehandle;
|
|
sinfo.end_of_file_info.in.size = 1;
|
|
status = smb2_setinfo_file(tree, &sinfo);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
smb2_util_close(tree, filehandle);
|
|
|
|
/* Now check size */
|
|
ZERO_STRUCT(io);
|
|
io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
|
|
SEC_FILE_WRITE_ATTRIBUTE;
|
|
io.smb2.in.fname = rfork;
|
|
status = smb2_create(tree, mem_ctx, &(io.smb2));
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
filehandle = io.smb2.out.file.handle;
|
|
|
|
ZERO_STRUCT(finfo);
|
|
finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
|
|
finfo.generic.in.file.handle = filehandle;
|
|
status = smb2_getinfo_file(tree, mem_ctx, &finfo);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
if (finfo.all_info.out.size != 1) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) Incorrect resource fork size\n",
|
|
__location__);
|
|
ret = false;
|
|
smb2_util_close(tree, filehandle);
|
|
goto done;
|
|
}
|
|
smb2_util_close(tree, filehandle);
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_rfork_truncate(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
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(tree, fname);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
if (ret == false) {
|
|
goto done;
|
|
}
|
|
|
|
ret &= write_stream(tree, __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(tree, 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(tree, 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(tree, &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(tree, 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(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create");
|
|
fh3 = create.out.file.handle;
|
|
|
|
status = smb2_util_write(tree, fh3, "foo", 0, 3);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_write");
|
|
|
|
smb2_util_close(tree, fh3);
|
|
smb2_util_close(tree, fh2);
|
|
smb2_util_close(tree, fh1);
|
|
|
|
ret = check_stream(tree, __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(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_rfork_create(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
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(tree, fname);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, 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(tree, 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(tree, 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(tree, 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(tree, 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(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, "smb2_create");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 1, streams, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list");
|
|
|
|
torture_comment(tctx, "(%s) close empty created rfork, open 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(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, "smb2_create");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_rfork_create_ro(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
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;
|
|
|
|
smb2_util_unlink(tree, fname);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testdir\n");
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
if (ret == false) {
|
|
goto done;
|
|
}
|
|
|
|
torture_comment(tctx, "(%s) Try opening read-only with "
|
|
"open_if create disposition, should return ENOENT\n",
|
|
__location__);
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.fname = rfork;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
|
|
create.in.desired_access = SEC_FILE_READ_DATA | SEC_STD_READ_CONTROL;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.share_access = FILE_SHARE_READ | FILE_SHARE_DELETE;
|
|
status = smb2_create(tree, mem_ctx, &(create));
|
|
torture_assert_ntstatus_equal_goto(tctx, status,
|
|
NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "smb2_create failed\n");
|
|
|
|
torture_comment(tctx, "(%s) Now write something to the "
|
|
"rsrc stream, then the same open should succeed\n",
|
|
__location__);
|
|
|
|
ret = write_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
0, 3, "foo");
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"write_stream failed\n");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM,
|
|
0, 3, 0, 3, "foo");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.fname = rfork;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
|
|
create.in.desired_access = SEC_FILE_READ_DATA | SEC_STD_READ_CONTROL;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.share_access = FILE_SHARE_READ | FILE_SHARE_DELETE;
|
|
status = smb2_create(tree, mem_ctx, &(create));
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"smb2_create failed\n");
|
|
|
|
smb2_util_close(tree, create.out.file.handle);
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_adouble_conversion(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\test_adouble_conversion";
|
|
const char *adname = BASEDIR "/._test_adouble_conversion";
|
|
NTSTATUS status;
|
|
struct smb2_handle testdirh;
|
|
bool ret = true;
|
|
const char *data = "This resource fork intentionally left blank";
|
|
size_t datalen = strlen(data);
|
|
const char *streams[] = {
|
|
"::$DATA",
|
|
AFPINFO_STREAM,
|
|
AFPRESOURCE_STREAM,
|
|
":com.apple.metadata" "\xef\x80\xa2" "_kMDItemUserTags:$DATA",
|
|
":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */
|
|
};
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ret = torture_setup_file(tctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"torture_setup_file failed\n");
|
|
|
|
ret = torture_setup_file(tctx, tree, adname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"torture_setup_file failed\n");
|
|
|
|
ret = write_stream(tree, __location__, tctx, mem_ctx,
|
|
adname, NULL,
|
|
0, sizeof(osx_adouble_w_xattr), osx_adouble_w_xattr);
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"write_stream failed\n");
|
|
|
|
torture_comment(tctx, "(%s) test OS X AppleDouble conversion\n",
|
|
__location__);
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM,
|
|
16, datalen, 0, datalen, data);
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"check AFPRESOURCE_STREAM failed\n");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPINFO_STREAM,
|
|
0, 60, 16, 8, "TESTSLOW");
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"check AFPINFO_STREAM failed\n");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname,
|
|
":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */
|
|
0, 3, 0, 3, "baz");
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"check foo:bar stream failed\n");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 5, streams, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list");
|
|
|
|
done:
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_aapl(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\test_aapl";
|
|
NTSTATUS status;
|
|
struct smb2_handle testdirh;
|
|
bool ret = true;
|
|
struct smb2_create io;
|
|
DATA_BLOB data;
|
|
struct smb2_create_blob *aapl = NULL;
|
|
AfpInfo *info;
|
|
const char *type_creator = "SMB,OLE!";
|
|
char type_creator_buf[9];
|
|
uint32_t aapl_cmd;
|
|
uint32_t aapl_reply_bitmap;
|
|
uint32_t aapl_server_caps;
|
|
uint32_t aapl_vol_caps;
|
|
char *model;
|
|
struct smb2_find f;
|
|
unsigned int count;
|
|
union smb_search_data *d;
|
|
uint64_t rfork_len;
|
|
bool is_osx_server = torture_setting_bool(tctx, "osx", false);
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
ZERO_STRUCT(io);
|
|
io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
|
|
io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
|
|
io.in.share_access = (NTCREATEX_SHARE_ACCESS_DELETE |
|
|
NTCREATEX_SHARE_ACCESS_READ |
|
|
NTCREATEX_SHARE_ACCESS_WRITE);
|
|
io.in.fname = fname;
|
|
|
|
/*
|
|
* Issuing an SMB2/CREATE with a suitably formed AAPL context,
|
|
* controls behaviour of Apple's SMB2 extensions for the whole
|
|
* session!
|
|
*/
|
|
|
|
data = data_blob_talloc(mem_ctx, NULL, 3 * sizeof(uint64_t));
|
|
SBVAL(data.data, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
|
|
SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS |
|
|
SMB2_CRTCTX_AAPL_VOLUME_CAPS |
|
|
SMB2_CRTCTX_AAPL_MODEL_INFO));
|
|
SBVAL(data.data, 16, (SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR |
|
|
SMB2_CRTCTX_AAPL_UNIX_BASED |
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE));
|
|
|
|
torture_comment(tctx, "Testing SMB2 create context AAPL\n");
|
|
status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
status = smb2_create(tree, tctx, &io);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
status = smb2_util_close(tree, io.out.file.handle);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
/*
|
|
* Now check returned AAPL context
|
|
*/
|
|
torture_comment(tctx, "Comparing returned AAPL capabilities\n");
|
|
|
|
aapl = smb2_create_blob_find(&io.out.blobs,
|
|
SMB2_CREATE_TAG_AAPL);
|
|
|
|
if (aapl == NULL) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) unexpectedly no AAPL capabilities were returned.",
|
|
__location__);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
if (!is_osx_server) {
|
|
size_t expected_aapl_ctx_size;
|
|
bool size_ok;
|
|
|
|
/*
|
|
* uint32_t CommandCode = kAAPL_SERVER_QUERY
|
|
* uint32_t Reserved = 0;
|
|
* uint64_t ReplyBitmap = kAAPL_SERVER_CAPS |
|
|
* kAAPL_VOLUME_CAPS |
|
|
* kAAPL_MODEL_INFO;
|
|
* uint64_t ServerCaps = kAAPL_SUPPORTS_READDIR_ATTR |
|
|
* kAAPL_SUPPORTS_OSX_COPYFILE;
|
|
* uint64_t VolumeCaps = kAAPL_SUPPORT_RESOLVE_ID |
|
|
* kAAPL_CASE_SENSITIVE;
|
|
* uint32_t Pad2 = 0;
|
|
* uint32_t ModelStringLen = 10;
|
|
* ucs2_t ModelString[5] = "MacSamba";
|
|
*/
|
|
expected_aapl_ctx_size = strlen("MacSamba") * 2 + 40;
|
|
|
|
size_ok = aapl->data.length == expected_aapl_ctx_size;
|
|
torture_assert_goto(tctx, size_ok, ret, done, "bad AAPL size");
|
|
}
|
|
|
|
aapl_cmd = IVAL(aapl->data.data, 0);
|
|
if (aapl_cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) unexpected cmd: %d",
|
|
__location__, (int)aapl_cmd);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
aapl_reply_bitmap = BVAL(aapl->data.data, 8);
|
|
if (aapl_reply_bitmap != (SMB2_CRTCTX_AAPL_SERVER_CAPS |
|
|
SMB2_CRTCTX_AAPL_VOLUME_CAPS |
|
|
SMB2_CRTCTX_AAPL_MODEL_INFO)) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) unexpected reply_bitmap: %d",
|
|
__location__, (int)aapl_reply_bitmap);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
aapl_server_caps = BVAL(aapl->data.data, 16);
|
|
if (aapl_server_caps != (SMB2_CRTCTX_AAPL_UNIX_BASED |
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR |
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE |
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE)) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) unexpected server_caps: %d",
|
|
__location__, (int)aapl_server_caps);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
aapl_vol_caps = BVAL(aapl->data.data, 24);
|
|
if (aapl_vol_caps != 0) {
|
|
/* this will fail on a case insensitive fs ... */
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) unexpected vol_caps: %d",
|
|
__location__, (int)aapl_vol_caps);
|
|
}
|
|
|
|
ret = convert_string_talloc(mem_ctx,
|
|
CH_UTF16LE, CH_UNIX,
|
|
aapl->data.data + 40, 10,
|
|
&model, NULL);
|
|
if (ret == false) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) convert_string_talloc() failed",
|
|
__location__);
|
|
goto done;
|
|
}
|
|
torture_comment(tctx, "Got server model: \"%s\"\n", model);
|
|
|
|
/*
|
|
* Now that Requested AAPL extensions are enabled, setup some
|
|
* Mac files with metadata and resource fork
|
|
*/
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
if (ret == false) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) torture_setup_file() failed",
|
|
__location__);
|
|
goto done;
|
|
}
|
|
|
|
info = torture_afpinfo_new(mem_ctx);
|
|
if (info == NULL) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) torture_afpinfo_new() failed",
|
|
__location__);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
|
|
if (ret == false) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) torture_write_afpinfo() failed",
|
|
__location__);
|
|
goto done;
|
|
}
|
|
|
|
ret = write_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
0, 3, "foo");
|
|
if (ret == false) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) write_stream() failed",
|
|
__location__);
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Ok, file is prepared, now call smb2/find
|
|
*/
|
|
|
|
ZERO_STRUCT(io);
|
|
io.in.desired_access = SEC_RIGHTS_DIR_READ;
|
|
io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
|
|
io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
io.in.share_access = (NTCREATEX_SHARE_ACCESS_READ |
|
|
NTCREATEX_SHARE_ACCESS_WRITE |
|
|
NTCREATEX_SHARE_ACCESS_DELETE);
|
|
io.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
io.in.fname = BASEDIR;
|
|
status = smb2_create(tree, tctx, &io);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
ZERO_STRUCT(f);
|
|
f.in.file.handle = io.out.file.handle;
|
|
f.in.pattern = "test_aapl";
|
|
f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
|
|
f.in.max_response_size = 0x1000;
|
|
f.in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
|
|
|
|
status = smb2_find_level(tree, tree, &f, &count, &d);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
status = smb2_util_close(tree, io.out.file.handle);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
if (strcmp(d[0].id_both_directory_info.name.s, "test_aapl") != 0) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) write_stream() failed",
|
|
__location__);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
if (d[0].id_both_directory_info.short_name.private_length != 24) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) bad short_name length %" PRIu32 ", expected 24",
|
|
__location__, d[0].id_both_directory_info.short_name.private_length);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
torture_comment(tctx, "short_name buffer:\n");
|
|
dump_data(0, d[0].id_both_directory_info.short_name_buf, 24);
|
|
|
|
/*
|
|
* Extract data as specified by the AAPL extension:
|
|
* - ea_size contains max_access
|
|
* - short_name contains resource fork length + FinderInfo
|
|
* - reserved2 contains the unix mode
|
|
*/
|
|
torture_comment(tctx, "mac_access: %" PRIx32 "\n",
|
|
d[0].id_both_directory_info.ea_size);
|
|
|
|
rfork_len = BVAL(d[0].id_both_directory_info.short_name_buf, 0);
|
|
if (rfork_len != 3) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) expected resource fork length 3, got: %" PRIu64,
|
|
__location__, rfork_len);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(type_creator_buf, d[0].id_both_directory_info.short_name_buf + 8, 8);
|
|
type_creator_buf[8] = 0;
|
|
if (strcmp(type_creator, type_creator_buf) != 0) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) expected type/creator \"%s\" , got: %s",
|
|
__location__, type_creator, type_creator_buf);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static uint64_t patt_hash(uint64_t off)
|
|
{
|
|
return off;
|
|
}
|
|
|
|
static bool write_pattern(struct torture_context *torture,
|
|
struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
|
struct smb2_handle h, uint64_t off, uint64_t len,
|
|
uint64_t patt_off)
|
|
{
|
|
NTSTATUS status;
|
|
uint64_t i;
|
|
uint8_t *buf;
|
|
uint64_t io_sz = MIN(1024 * 64, len);
|
|
|
|
if (len == 0) {
|
|
return true;
|
|
}
|
|
|
|
torture_assert(torture, (len % 8) == 0, "invalid write len");
|
|
|
|
buf = talloc_zero_size(mem_ctx, io_sz);
|
|
torture_assert(torture, (buf != NULL), "no memory for file data buf");
|
|
|
|
while (len > 0) {
|
|
for (i = 0; i <= io_sz - 8; i += 8) {
|
|
SBVAL(buf, i, patt_hash(patt_off));
|
|
patt_off += 8;
|
|
}
|
|
|
|
status = smb2_util_write(tree, h,
|
|
buf, off, io_sz);
|
|
torture_assert_ntstatus_ok(torture, status, "file write");
|
|
|
|
len -= io_sz;
|
|
off += io_sz;
|
|
}
|
|
|
|
talloc_free(buf);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool check_pattern(struct torture_context *torture,
|
|
struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
|
struct smb2_handle h, uint64_t off, uint64_t len,
|
|
uint64_t patt_off)
|
|
{
|
|
if (len == 0) {
|
|
return true;
|
|
}
|
|
|
|
torture_assert(torture, (len % 8) == 0, "invalid read len");
|
|
|
|
while (len > 0) {
|
|
uint64_t i;
|
|
struct smb2_read r;
|
|
NTSTATUS status;
|
|
uint64_t io_sz = MIN(1024 * 64, len);
|
|
|
|
ZERO_STRUCT(r);
|
|
r.in.file.handle = h;
|
|
r.in.length = io_sz;
|
|
r.in.offset = off;
|
|
status = smb2_read(tree, mem_ctx, &r);
|
|
torture_assert_ntstatus_ok(torture, status, "read");
|
|
|
|
torture_assert_u64_equal(torture, r.out.data.length, io_sz,
|
|
"read data len mismatch");
|
|
|
|
for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) {
|
|
uint64_t data = BVAL(r.out.data.data, i);
|
|
torture_assert_u64_equal(torture, data, patt_hash(patt_off),
|
|
talloc_asprintf(torture, "read data "
|
|
"pattern bad at %llu\n",
|
|
(unsigned long long)off + i));
|
|
}
|
|
talloc_free(r.out.data.data);
|
|
len -= io_sz;
|
|
off += io_sz;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool test_setup_open(struct torture_context *torture,
|
|
struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
|
const char *fname,
|
|
struct smb2_handle *fh,
|
|
uint32_t desired_access,
|
|
uint32_t file_attributes)
|
|
{
|
|
struct smb2_create io;
|
|
NTSTATUS status;
|
|
|
|
ZERO_STRUCT(io);
|
|
io.in.desired_access = desired_access;
|
|
io.in.file_attributes = file_attributes;
|
|
io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
|
|
io.in.share_access =
|
|
NTCREATEX_SHARE_ACCESS_DELETE|
|
|
NTCREATEX_SHARE_ACCESS_READ|
|
|
NTCREATEX_SHARE_ACCESS_WRITE;
|
|
if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
|
|
}
|
|
io.in.fname = fname;
|
|
|
|
status = smb2_create(tree, mem_ctx, &io);
|
|
torture_assert_ntstatus_ok(torture, status, "file create");
|
|
|
|
*fh = io.out.file.handle;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool test_setup_create_fill(struct torture_context *torture,
|
|
struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
|
const char *fname,
|
|
struct smb2_handle *fh,
|
|
uint64_t size,
|
|
uint32_t desired_access,
|
|
uint32_t file_attributes)
|
|
{
|
|
bool ok;
|
|
|
|
ok = test_setup_open(torture, tree, mem_ctx,
|
|
fname,
|
|
fh,
|
|
desired_access,
|
|
file_attributes);
|
|
torture_assert(torture, ok, "file open");
|
|
|
|
if (size > 0) {
|
|
ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
|
|
torture_assert(torture, ok, "write pattern");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool test_setup_copy_chunk(struct torture_context *torture,
|
|
struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
|
uint32_t nchunks,
|
|
const char *src_name,
|
|
struct smb2_handle *src_h,
|
|
uint64_t src_size,
|
|
uint32_t src_desired_access,
|
|
const char *dst_name,
|
|
struct smb2_handle *dest_h,
|
|
uint64_t dest_size,
|
|
uint32_t dest_desired_access,
|
|
struct srv_copychunk_copy *cc_copy,
|
|
union smb_ioctl *io)
|
|
{
|
|
struct req_resume_key_rsp res_key;
|
|
bool ok;
|
|
NTSTATUS status;
|
|
enum ndr_err_code ndr_ret;
|
|
|
|
ok = test_setup_create_fill(torture, tree, mem_ctx, src_name,
|
|
src_h, src_size, src_desired_access,
|
|
FILE_ATTRIBUTE_NORMAL);
|
|
torture_assert(torture, ok, "src file create fill");
|
|
|
|
ok = test_setup_create_fill(torture, tree, mem_ctx, dst_name,
|
|
dest_h, dest_size, dest_desired_access,
|
|
FILE_ATTRIBUTE_NORMAL);
|
|
torture_assert(torture, ok, "dest file create fill");
|
|
|
|
ZERO_STRUCTPN(io);
|
|
io->smb2.level = RAW_IOCTL_SMB2;
|
|
io->smb2.in.file.handle = *src_h;
|
|
io->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
|
|
/* Allow for Key + ContextLength + Context */
|
|
io->smb2.in.max_response_size = 32;
|
|
io->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
|
|
|
|
status = smb2_ioctl(tree, mem_ctx, &io->smb2);
|
|
torture_assert_ntstatus_ok(torture, status,
|
|
"FSCTL_SRV_REQUEST_RESUME_KEY");
|
|
|
|
ndr_ret = ndr_pull_struct_blob(&io->smb2.out.out, mem_ctx, &res_key,
|
|
(ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
|
|
|
|
torture_assert_ndr_success(torture, ndr_ret,
|
|
"ndr_pull_req_resume_key_rsp");
|
|
|
|
ZERO_STRUCTPN(io);
|
|
io->smb2.level = RAW_IOCTL_SMB2;
|
|
io->smb2.in.file.handle = *dest_h;
|
|
io->smb2.in.function = FSCTL_SRV_COPYCHUNK;
|
|
io->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
|
|
io->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
|
|
|
|
ZERO_STRUCTPN(cc_copy);
|
|
memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
|
|
cc_copy->chunk_count = nchunks;
|
|
cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
|
|
torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool check_copy_chunk_rsp(struct torture_context *torture,
|
|
struct srv_copychunk_rsp *cc_rsp,
|
|
uint32_t ex_chunks_written,
|
|
uint32_t ex_chunk_bytes_written,
|
|
uint32_t ex_total_bytes_written)
|
|
{
|
|
torture_assert_int_equal(torture, cc_rsp->chunks_written,
|
|
ex_chunks_written, "num chunks");
|
|
torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
|
|
ex_chunk_bytes_written, "chunk bytes written");
|
|
torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
|
|
ex_total_bytes_written, "chunk total bytes");
|
|
return true;
|
|
}
|
|
|
|
static bool neg_aapl_copyfile(struct torture_context *tctx,
|
|
struct smb2_tree *tree,
|
|
uint64_t flags)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = "aapl";
|
|
NTSTATUS status;
|
|
struct smb2_create io;
|
|
DATA_BLOB data;
|
|
struct smb2_create_blob *aapl = NULL;
|
|
uint32_t aapl_cmd;
|
|
uint32_t aapl_reply_bitmap;
|
|
uint32_t aapl_server_caps;
|
|
bool ret = true;
|
|
|
|
ZERO_STRUCT(io);
|
|
io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
|
|
io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
|
|
io.in.share_access = (NTCREATEX_SHARE_ACCESS_DELETE |
|
|
NTCREATEX_SHARE_ACCESS_READ |
|
|
NTCREATEX_SHARE_ACCESS_WRITE);
|
|
io.in.fname = fname;
|
|
|
|
data = data_blob_talloc(mem_ctx, NULL, 3 * sizeof(uint64_t));
|
|
SBVAL(data.data, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
|
|
SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS));
|
|
SBVAL(data.data, 16, flags);
|
|
|
|
status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
status = smb2_create(tree, tctx, &io);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
aapl = smb2_create_blob_find(&io.out.blobs,
|
|
SMB2_CREATE_TAG_AAPL);
|
|
if (aapl == NULL) {
|
|
ret = false;
|
|
goto done;
|
|
|
|
}
|
|
if (aapl->data.length < 24) {
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
aapl_cmd = IVAL(aapl->data.data, 0);
|
|
if (aapl_cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) unexpected cmd: %d",
|
|
__location__, (int)aapl_cmd);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
aapl_reply_bitmap = BVAL(aapl->data.data, 8);
|
|
if (!(aapl_reply_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS)) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) unexpected reply_bitmap: %d",
|
|
__location__, (int)aapl_reply_bitmap);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
aapl_server_caps = BVAL(aapl->data.data, 16);
|
|
if (!(aapl_server_caps & flags)) {
|
|
torture_result(tctx, TORTURE_FAIL,
|
|
"(%s) unexpected server_caps: %d",
|
|
__location__, (int)aapl_server_caps);
|
|
ret = false;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
status = smb2_util_close(tree, io.out.file.handle);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
smb2_util_unlink(tree, "aapl");
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_copyfile(struct torture_context *torture,
|
|
struct smb2_tree *tree)
|
|
{
|
|
struct smb2_handle src_h;
|
|
struct smb2_handle dest_h;
|
|
NTSTATUS status;
|
|
union smb_ioctl io;
|
|
TALLOC_CTX *tmp_ctx = talloc_new(tree);
|
|
struct srv_copychunk_copy cc_copy;
|
|
struct srv_copychunk_rsp cc_rsp;
|
|
enum ndr_err_code ndr_ret;
|
|
bool ok;
|
|
const char *sname = ":foo" "\xef\x80\xa2" "bar:$DATA";
|
|
|
|
/*
|
|
* First test a copy_chunk with a 0 chunk count without having
|
|
* enabled this via AAPL. The request must not fail and the
|
|
* copied length in the response must be 0. This is verified
|
|
* against Windows 2008r2.
|
|
*/
|
|
|
|
ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
|
|
0, /* 0 chunks, copyfile semantics */
|
|
FNAME_CC_SRC,
|
|
&src_h, 4096, /* fill 4096 byte src file */
|
|
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
|
|
FNAME_CC_DST,
|
|
&dest_h, 0, /* 0 byte dest file */
|
|
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
|
|
&cc_copy,
|
|
&io);
|
|
if (!ok) {
|
|
torture_fail_goto(torture, done, "setup copy chunk error");
|
|
}
|
|
|
|
ndr_ret = ndr_push_struct_blob(&io.smb2.in.out, tmp_ctx,
|
|
&cc_copy,
|
|
(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
|
|
torture_assert_ndr_success(torture, ndr_ret,
|
|
"ndr_push_srv_copychunk_copy");
|
|
|
|
status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
|
|
torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK");
|
|
|
|
ndr_ret = ndr_pull_struct_blob(&io.smb2.out.out, tmp_ctx,
|
|
&cc_rsp,
|
|
(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
|
|
torture_assert_ndr_success(torture, ndr_ret,
|
|
"ndr_pull_srv_copychunk_rsp");
|
|
|
|
ok = check_copy_chunk_rsp(torture, &cc_rsp,
|
|
0, /* chunks written */
|
|
0, /* chunk bytes unsuccessfully written */
|
|
0); /* total bytes written */
|
|
if (!ok) {
|
|
torture_fail_goto(torture, done, "bad copy chunk response data");
|
|
}
|
|
|
|
/*
|
|
* Now enable AAPL copyfile and test again, the file and the
|
|
* stream must be copied by the server.
|
|
*/
|
|
ok = neg_aapl_copyfile(torture, tree,
|
|
SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE);
|
|
if (!ok) {
|
|
torture_skip_goto(torture, done, "missing AAPL copyfile");
|
|
goto done;
|
|
}
|
|
|
|
smb2_util_close(tree, src_h);
|
|
smb2_util_close(tree, dest_h);
|
|
smb2_util_unlink(tree, FNAME_CC_SRC);
|
|
smb2_util_unlink(tree, FNAME_CC_DST);
|
|
|
|
ok = torture_setup_file(tmp_ctx, tree, FNAME_CC_SRC, false);
|
|
if (!ok) {
|
|
torture_fail(torture, "setup file error");
|
|
}
|
|
ok = write_stream(tree, __location__, torture, tmp_ctx,
|
|
FNAME_CC_SRC, AFPRESOURCE_STREAM,
|
|
10, 10, "1234567890");
|
|
if (!ok) {
|
|
torture_fail(torture, "setup stream error");
|
|
}
|
|
|
|
ok = write_stream(tree, __location__, torture, tmp_ctx,
|
|
FNAME_CC_SRC, sname,
|
|
10, 10, "abcdefghij");
|
|
torture_assert_goto(torture, ok == true, ok, done, "write_stream failed\n");
|
|
|
|
ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
|
|
0, /* 0 chunks, copyfile semantics */
|
|
FNAME_CC_SRC,
|
|
&src_h, 4096, /* fill 4096 byte src file */
|
|
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
|
|
FNAME_CC_DST,
|
|
&dest_h, 0, /* 0 byte dest file */
|
|
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
|
|
&cc_copy,
|
|
&io);
|
|
if (!ok) {
|
|
torture_fail_goto(torture, done, "setup copy chunk error");
|
|
}
|
|
|
|
ndr_ret = ndr_push_struct_blob(&io.smb2.in.out, tmp_ctx,
|
|
&cc_copy,
|
|
(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
|
|
torture_assert_ndr_success(torture, ndr_ret,
|
|
"ndr_push_srv_copychunk_copy");
|
|
|
|
status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
|
|
torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK");
|
|
|
|
ndr_ret = ndr_pull_struct_blob(&io.smb2.out.out, tmp_ctx,
|
|
&cc_rsp,
|
|
(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
|
|
torture_assert_ndr_success(torture, ndr_ret,
|
|
"ndr_pull_srv_copychunk_rsp");
|
|
|
|
ok = check_copy_chunk_rsp(torture, &cc_rsp,
|
|
0, /* chunks written */
|
|
0, /* chunk bytes unsuccessfully written */
|
|
4096); /* total bytes written */
|
|
if (!ok) {
|
|
torture_fail_goto(torture, done, "bad copy chunk response data");
|
|
}
|
|
|
|
ok = test_setup_open(torture, tree, tmp_ctx, FNAME_CC_DST, &dest_h,
|
|
SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL);
|
|
if (!ok) {
|
|
torture_fail_goto(torture, done,"open failed");
|
|
}
|
|
ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
|
|
if (!ok) {
|
|
torture_fail_goto(torture, done, "inconsistent file data");
|
|
}
|
|
|
|
ok = check_stream(tree, __location__, torture, tmp_ctx,
|
|
FNAME_CC_DST, AFPRESOURCE_STREAM,
|
|
0, 20, 10, 10, "1234567890");
|
|
if (!ok) {
|
|
torture_fail_goto(torture, done, "inconsistent stream data");
|
|
}
|
|
|
|
ok = check_stream(tree, __location__, torture, tmp_ctx,
|
|
FNAME_CC_DST, sname,
|
|
0, 20, 10, 10, "abcdefghij");
|
|
torture_assert_goto(torture, ok == true, ok, done, "check_stream failed\n");
|
|
|
|
done:
|
|
smb2_util_close(tree, src_h);
|
|
smb2_util_close(tree, dest_h);
|
|
smb2_util_unlink(tree, FNAME_CC_SRC);
|
|
smb2_util_unlink(tree, FNAME_CC_DST);
|
|
talloc_free(tmp_ctx);
|
|
return true;
|
|
}
|
|
|
|
static bool check_stream_list(struct smb2_tree *tree,
|
|
struct torture_context *tctx,
|
|
const char *fname,
|
|
int num_exp,
|
|
const char **exp,
|
|
bool is_dir)
|
|
{
|
|
bool ret = true;
|
|
union smb_fileinfo finfo;
|
|
NTSTATUS status;
|
|
int i;
|
|
TALLOC_CTX *tmp_ctx = talloc_new(tctx);
|
|
char **exp_sort;
|
|
struct stream_struct *stream_sort;
|
|
struct smb2_create create;
|
|
struct smb2_handle h;
|
|
|
|
ZERO_STRUCT(h);
|
|
torture_assert_goto(tctx, tmp_ctx != NULL, ret, done, "talloc_new failed");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.fname = fname;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.create_options = is_dir ? NTCREATEX_OPTIONS_DIRECTORY : 0;
|
|
create.in.file_attributes = is_dir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
|
|
status = smb2_create(tree, tmp_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create");
|
|
h = create.out.file.handle;
|
|
|
|
finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
|
|
finfo.generic.in.file.handle = h;
|
|
|
|
status = smb2_getinfo_file(tree, tctx, &finfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "get stream info");
|
|
|
|
smb2_util_close(tree, h);
|
|
|
|
torture_assert_int_equal_goto(tctx, finfo.stream_info.out.num_streams, num_exp,
|
|
ret, done, "stream count");
|
|
|
|
if (num_exp == 0) {
|
|
TALLOC_FREE(tmp_ctx);
|
|
goto done;
|
|
}
|
|
|
|
exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
|
|
torture_assert_goto(tctx, exp_sort != NULL, ret, done, __location__);
|
|
|
|
TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
|
|
|
|
stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
|
|
finfo.stream_info.out.num_streams *
|
|
sizeof(*stream_sort));
|
|
torture_assert_goto(tctx, stream_sort != NULL, ret, done, __location__);
|
|
|
|
TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream);
|
|
|
|
for (i=0; i<num_exp; i++) {
|
|
torture_comment(tctx, "i[%d] exp[%s] got[%s]\n",
|
|
i, exp_sort[i], stream_sort[i].stream_name.s);
|
|
torture_assert_str_equal_goto(tctx, stream_sort[i].stream_name.s, exp_sort[i],
|
|
ret, done, "stream name");
|
|
}
|
|
|
|
done:
|
|
TALLOC_FREE(tmp_ctx);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
test stream names
|
|
*/
|
|
static bool test_stream_names(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
struct smb2_handle h;
|
|
const char *fname = BASEDIR "\\stream_names.txt";
|
|
const char *sname1;
|
|
bool ret;
|
|
/* UTF8 private use are starts at 0xef 0x80 0x80 (0xf000) */
|
|
const char *streams[] = {
|
|
":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */
|
|
"::$DATA"
|
|
};
|
|
|
|
sname1 = talloc_asprintf(mem_ctx, "%s%s", fname, streams[0]);
|
|
|
|
/* clean slate ...*/
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, h);
|
|
|
|
torture_comment(tctx, "(%s) testing stream names\n", __location__);
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_WRITE_DATA;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.share_access =
|
|
NTCREATEX_SHARE_ACCESS_DELETE|
|
|
NTCREATEX_SHARE_ACCESS_READ|
|
|
NTCREATEX_SHARE_ACCESS_WRITE;
|
|
create.in.create_disposition = NTCREATEX_DISP_CREATE;
|
|
create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
|
|
create.in.fname = sname1;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, create.out.file.handle);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 2, streams, false);
|
|
CHECK_VALUE(ret, true);
|
|
|
|
done:
|
|
status = smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Renaming a directory with open file, should work for OS X AAPL clients */
|
|
static bool test_rename_dir_openfile(struct torture_context *torture,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
union smb_open io;
|
|
union smb_close cl;
|
|
union smb_setfileinfo sinfo;
|
|
struct smb2_handle d1, h1;
|
|
const char *renamedir = BASEDIR "-new";
|
|
bool server_is_osx = torture_setting_bool(torture, "osx", false);
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
smb2_deltree(tree, renamedir);
|
|
|
|
ZERO_STRUCT(io.smb2);
|
|
io.generic.level = RAW_OPEN_SMB2;
|
|
io.smb2.in.create_flags = 0;
|
|
io.smb2.in.desired_access = 0x0017019f;
|
|
io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
|
|
io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
io.smb2.in.share_access = 0;
|
|
io.smb2.in.alloc_size = 0;
|
|
io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
|
|
io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
|
|
io.smb2.in.security_flags = 0;
|
|
io.smb2.in.fname = BASEDIR;
|
|
|
|
status = smb2_create(tree, torture, &(io.smb2));
|
|
torture_assert_ntstatus_ok(torture, status, "smb2_create dir");
|
|
d1 = io.smb2.out.file.handle;
|
|
|
|
ZERO_STRUCT(io.smb2);
|
|
io.generic.level = RAW_OPEN_SMB2;
|
|
io.smb2.in.create_flags = 0;
|
|
io.smb2.in.desired_access = 0x0017019f;
|
|
io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
|
|
io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
io.smb2.in.share_access = 0;
|
|
io.smb2.in.alloc_size = 0;
|
|
io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
|
|
io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
|
|
io.smb2.in.security_flags = 0;
|
|
io.smb2.in.fname = BASEDIR "\\file.txt";
|
|
|
|
status = smb2_create(tree, torture, &(io.smb2));
|
|
torture_assert_ntstatus_ok(torture, status, "smb2_create file");
|
|
h1 = io.smb2.out.file.handle;
|
|
|
|
if (!server_is_osx) {
|
|
torture_comment(torture, "Renaming directory without AAPL, must fail\n");
|
|
|
|
ZERO_STRUCT(sinfo);
|
|
sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
|
|
sinfo.rename_information.in.file.handle = d1;
|
|
sinfo.rename_information.in.overwrite = 0;
|
|
sinfo.rename_information.in.root_fid = 0;
|
|
sinfo.rename_information.in.new_name = renamedir;
|
|
status = smb2_setinfo_file(tree, &sinfo);
|
|
|
|
torture_assert_ntstatus_equal(torture, status,
|
|
NT_STATUS_ACCESS_DENIED,
|
|
"smb2_setinfo_file");
|
|
|
|
ZERO_STRUCT(cl.smb2);
|
|
cl.smb2.level = RAW_CLOSE_SMB2;
|
|
cl.smb2.in.file.handle = d1;
|
|
status = smb2_close(tree, &(cl.smb2));
|
|
torture_assert_ntstatus_ok(torture, status, "smb2_close");
|
|
ZERO_STRUCT(d1);
|
|
}
|
|
|
|
torture_comment(torture, "Enabling AAPL\n");
|
|
|
|
ret = enable_aapl(torture, tree);
|
|
torture_assert(torture, ret == true, "enable_aapl failed");
|
|
|
|
torture_comment(torture, "Renaming directory with AAPL\n");
|
|
|
|
ZERO_STRUCT(io.smb2);
|
|
io.generic.level = RAW_OPEN_SMB2;
|
|
io.smb2.in.desired_access = 0x0017019f;
|
|
io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
io.smb2.in.share_access = 0;
|
|
io.smb2.in.alloc_size = 0;
|
|
io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
|
|
io.smb2.in.security_flags = 0;
|
|
io.smb2.in.fname = BASEDIR;
|
|
|
|
status = smb2_create(tree, torture, &(io.smb2));
|
|
torture_assert_ntstatus_ok(torture, status, "smb2_create dir");
|
|
d1 = io.smb2.out.file.handle;
|
|
|
|
ZERO_STRUCT(sinfo);
|
|
sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
|
|
sinfo.rename_information.in.file.handle = d1;
|
|
sinfo.rename_information.in.overwrite = 0;
|
|
sinfo.rename_information.in.root_fid = 0;
|
|
sinfo.rename_information.in.new_name = renamedir;
|
|
|
|
status = smb2_setinfo_file(tree, &sinfo);
|
|
torture_assert_ntstatus_ok(torture, status, "smb2_setinfo_file");
|
|
|
|
ZERO_STRUCT(cl.smb2);
|
|
cl.smb2.level = RAW_CLOSE_SMB2;
|
|
cl.smb2.in.file.handle = d1;
|
|
status = smb2_close(tree, &(cl.smb2));
|
|
torture_assert_ntstatus_ok(torture, status, "smb2_close");
|
|
ZERO_STRUCT(d1);
|
|
|
|
cl.smb2.in.file.handle = h1;
|
|
status = smb2_close(tree, &(cl.smb2));
|
|
torture_assert_ntstatus_ok(torture, status, "smb2_close");
|
|
ZERO_STRUCT(h1);
|
|
|
|
torture_comment(torture, "Cleaning up\n");
|
|
|
|
if (h1.data[0] || h1.data[1]) {
|
|
ZERO_STRUCT(cl.smb2);
|
|
cl.smb2.level = RAW_CLOSE_SMB2;
|
|
cl.smb2.in.file.handle = h1;
|
|
status = smb2_close(tree, &(cl.smb2));
|
|
}
|
|
|
|
smb2_util_unlink(tree, BASEDIR "\\file.txt");
|
|
smb2_util_unlink(tree, BASEDIR "-new\\file.txt");
|
|
smb2_deltree(tree, renamedir);
|
|
smb2_deltree(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_afpinfo_enoent(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
struct smb2_handle h1;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file" AFPINFO_STREAM_NAME;
|
|
|
|
torture_comment(tctx, "Opening file without AFP_AfpInfo\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, h1);
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
torture_comment(tctx, "Opening not existing AFP_AfpInfo\n");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; /* stat open */
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "Got unexpected AFP_AfpInfo stream");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_create_delete_on_close(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
struct smb2_handle h1;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file" AFPINFO_STREAM_NAME;
|
|
const char *type_creator = "SMB,OLE!";
|
|
AfpInfo *info = NULL;
|
|
const char *streams_basic[] = {
|
|
"::$DATA"
|
|
};
|
|
const char *streams_afpinfo[] = {
|
|
"::$DATA",
|
|
AFPINFO_STREAM
|
|
};
|
|
|
|
torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
|
|
|
|
torture_comment(tctx, "Checking whether create with delete-on-close work with AFP_AfpInfo\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, h1);
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
torture_comment(tctx, "Opening not existing AFP_AfpInfo\n");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; /* stat open */
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "Got unexpected AFP_AfpInfo stream");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "Got unexpected AFP_AfpInfo stream");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
torture_comment(tctx, "Deleting AFP_AfpInfo via create with delete-on-close\n");
|
|
|
|
info = torture_afpinfo_new(mem_ctx);
|
|
torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed");
|
|
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 16, 8, type_creator);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad type/creator in AFP_AfpInfo");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE | SEC_STD_SYNCHRONIZE | SEC_STD_DELETE;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
smb2_util_close(tree, h1);
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "Got unexpected AFP_AfpInfo stream");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_setinfo_delete_on_close(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
union smb_setfileinfo sfinfo;
|
|
struct smb2_handle h1;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file" AFPINFO_STREAM_NAME;
|
|
const char *type_creator = "SMB,OLE!";
|
|
AfpInfo *info = NULL;
|
|
const char *streams_basic[] = {
|
|
"::$DATA"
|
|
};
|
|
|
|
torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
|
|
|
|
torture_comment(tctx, "Deleting AFP_AfpInfo via setinfo with delete-on-close\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, h1);
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
info = torture_afpinfo_new(mem_ctx);
|
|
torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed");
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE | SEC_STD_SYNCHRONIZE | SEC_STD_DELETE;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
|
|
/* Delete stream via setinfo delete-on-close */
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.disposition_info.in.delete_on_close = 1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
|
|
sfinfo.generic.in.file.handle = h1;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set delete-on-close failed");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "Got unexpected AFP_AfpInfo stream");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_setinfo_eof(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
union smb_setfileinfo sfinfo;
|
|
struct smb2_handle h1;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file" AFPINFO_STREAM_NAME;
|
|
const char *type_creator = "SMB,OLE!";
|
|
AfpInfo *info = NULL;
|
|
const char *streams_afpinfo[] = {
|
|
"::$DATA",
|
|
AFPINFO_STREAM
|
|
};
|
|
|
|
torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
|
|
|
|
torture_comment(tctx, "Set AFP_AfpInfo EOF to 61, 1 and 0\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, h1);
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
info = torture_afpinfo_new(mem_ctx);
|
|
torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed");
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
|
|
torture_comment(tctx, "Set AFP_AfpInfo EOF to 61\n");
|
|
|
|
/* Test setinfo end-of-file info */
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 61;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ALLOTTED_SPACE_EXCEEDED,
|
|
ret, done, "set eof 61 failed");
|
|
|
|
torture_comment(tctx, "Set AFP_AfpInfo EOF to 1\n");
|
|
|
|
/* Truncation returns success, but has no effect */
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 1;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status,
|
|
ret, done, "set eof 1 failed");
|
|
smb2_util_close(tree, h1);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 16, 8, type_creator);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "FinderInfo changed");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
|
|
/*
|
|
* Delete stream via setinfo end-of-file info to 0, should
|
|
* return success but stream MUST NOT deleted
|
|
*/
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 0;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set eof 0 failed");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 16, 8, type_creator);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "FinderInfo changed");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_afpinfo_all0(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
struct smb2_handle h1 = {{0}};
|
|
struct smb2_handle baseh = {{0}};
|
|
union smb_setfileinfo setfinfo;
|
|
union smb_fileinfo getfinfo;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file" AFPINFO_STREAM;
|
|
const char *type_creator = "SMB,OLE!";
|
|
AfpInfo *info = NULL;
|
|
char *infobuf = NULL;
|
|
const char *streams_basic[] = {
|
|
"::$DATA"
|
|
};
|
|
const char *streams_afpinfo[] = {
|
|
"::$DATA",
|
|
AFPINFO_STREAM
|
|
};
|
|
|
|
torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
|
|
|
|
torture_comment(tctx, "Write all 0 to AFP_AfpInfo and see what happens\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, h1);
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
info = torture_afpinfo_new(mem_ctx);
|
|
torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed");
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
/* Write all 0 to AFP_AfpInfo */
|
|
memset(info->afpi_FinderInfo, 0, AFP_FinderSize);
|
|
infobuf = torture_afpinfo_pack(mem_ctx, info);
|
|
torture_assert_not_null_goto(tctx, infobuf, ret, done,
|
|
"torture_afpinfo_pack failed\n");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
|
|
create.in.fname = fname;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"smb2_create failed\n");
|
|
baseh = create.out.file.handle;
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_goto(tctx, ret == true, ret, done,
|
|
"smb2_create failed\n");
|
|
h1 = create.out.file.handle;
|
|
|
|
status = smb2_util_write(tree, h1, infobuf, 0, AFP_INFO_SIZE);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"smb2_util_write failed\n");
|
|
|
|
/*
|
|
* Get stream information on open handle, must return only default
|
|
* stream, the AFP_AfpInfo stream must not be returned.
|
|
*/
|
|
|
|
ZERO_STRUCT(getfinfo);
|
|
getfinfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
|
|
getfinfo.generic.in.file.handle = baseh;
|
|
|
|
status = smb2_getinfo_file(tree, tctx, &getfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"get stream info\n");
|
|
|
|
torture_assert_int_equal_goto(tctx, getfinfo.stream_info.out.num_streams,
|
|
1, ret, done, "stream count");
|
|
|
|
smb2_util_close(tree, baseh);
|
|
ZERO_STRUCT(baseh);
|
|
|
|
/*
|
|
* Try to set some file-basic-info (time) on the stream. This catches
|
|
* naive implementation mistakes that simply deleted the backing store
|
|
* from the filesystem in the zero-out step.
|
|
*/
|
|
|
|
ZERO_STRUCT(setfinfo);
|
|
unix_to_nt_time(&setfinfo.basic_info.in.write_time, time(NULL));
|
|
setfinfo.basic_info.in.attrib = 0x20;
|
|
setfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
|
|
setfinfo.generic.in.file.handle = h1;
|
|
|
|
status = smb2_setinfo_file(tree, &setfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"smb2_getinfo_file failed\n");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list");
|
|
|
|
smb2_util_close(tree, h1);
|
|
ZERO_STRUCT(h1);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
done:
|
|
if (!smb2_util_handle_empty(h1)) {
|
|
smb2_util_close(tree, h1);
|
|
}
|
|
if (!smb2_util_handle_empty(baseh)) {
|
|
smb2_util_close(tree, baseh);
|
|
}
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_create_delete_on_close_resource(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
struct smb2_handle h1;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file" AFPRESOURCE_STREAM_NAME;
|
|
const char *streams_basic[] = {
|
|
"::$DATA"
|
|
};
|
|
const char *streams_afpresource[] = {
|
|
"::$DATA",
|
|
AFPRESOURCE_STREAM
|
|
};
|
|
|
|
torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
|
|
|
|
torture_comment(tctx, "Checking whether create with delete-on-close is ignored for AFP_AfpResource\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok(tctx, status, "torture_smb2_testdir");
|
|
smb2_util_close(tree, h1);
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
torture_comment(tctx, "Opening not existing AFP_AfpResource\n");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; /* stat open */
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "Got unexpected AFP_AfpResource stream");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "Got unexpected AFP_AfpResource stream");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
torture_comment(tctx, "Trying to delete AFP_AfpResource via create with delete-on-close\n");
|
|
|
|
ret = write_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
0, 10, "1234567890");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Writing to AFP_AfpResource failed");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPRESOURCE_STREAM_NAME,
|
|
0, 10, 0, 10, "1234567890");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad content from AFP_AfpResource");
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE | SEC_STD_SYNCHRONIZE | SEC_STD_DELETE;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
smb2_util_close(tree, h1);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPRESOURCE_STREAM_NAME,
|
|
0, 10, 0, 10, "1234567890");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad content from AFP_AfpResource");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_setinfo_delete_on_close_resource(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
union smb_setfileinfo sfinfo;
|
|
struct smb2_handle h1;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file" AFPRESOURCE_STREAM_NAME;
|
|
const char *streams_afpresource[] = {
|
|
"::$DATA",
|
|
AFPRESOURCE_STREAM
|
|
};
|
|
|
|
torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
|
|
|
|
torture_comment(tctx, "Trying to delete AFP_AfpResource via setinfo with delete-on-close\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, h1);
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
ret = write_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
10, 10, "1234567890");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Writing to AFP_AfpResource failed");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE | SEC_STD_SYNCHRONIZE | SEC_STD_DELETE;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
|
|
/* Try to delete stream via setinfo delete-on-close */
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.disposition_info.in.delete_on_close = 1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
|
|
sfinfo.generic.in.file.handle = h1;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set delete-on-close failed");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"Got unexpected AFP_AfpResource stream");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_setinfo_eof_resource(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
union smb_setfileinfo sfinfo;
|
|
union smb_fileinfo finfo;
|
|
struct smb2_handle h1;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file" AFPRESOURCE_STREAM_NAME;
|
|
const char *streams_basic[] = {
|
|
"::$DATA"
|
|
};
|
|
|
|
torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
|
|
|
|
torture_comment(tctx, "Set AFP_AfpResource EOF to 1 and 0\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
|
|
smb2_util_close(tree, h1);
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
ret = write_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
10, 10, "1234567890");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Writing to AFP_AfpResource failed");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
|
|
torture_comment(tctx, "Set AFP_AfpResource EOF to 1\n");
|
|
|
|
/* Test setinfo end-of-file info */
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 1;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status,
|
|
ret, done, "set eof 1 failed");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
/* Check size == 1 */
|
|
ZERO_STRUCT(create);
|
|
create.in.fname = sname;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
|
|
ZERO_STRUCT(finfo);
|
|
finfo.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
|
|
finfo.generic.in.file.handle = h1;
|
|
status = smb2_getinfo_file(tree, mem_ctx, &finfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file failed");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
torture_assert_goto(tctx, finfo.all_info.out.size == 1, ret, done, "size != 1");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
|
|
/*
|
|
* Delete stream via setinfo end-of-file info to 0, this
|
|
* should delete the stream.
|
|
*/
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 0;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set eof 0 failed");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.fname = sname;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "smb2_create failed");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* This tests that right after creating the AFP_AfpInfo stream,
|
|
* reading from the stream returns an empty, default metadata blob of
|
|
* 60 bytes.
|
|
*
|
|
* NOTE: against OS X SMB server this only works if the read request
|
|
* is compounded with the create that created the stream, is fails
|
|
* otherwise. We don't care...
|
|
*/
|
|
static bool test_null_afpinfo(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = "test_null_afpinfo";
|
|
const char *sname = "test_null_afpinfo" AFPINFO_STREAM_NAME;
|
|
NTSTATUS status;
|
|
bool ret = true;
|
|
struct smb2_request *req[3];
|
|
struct smb2_handle handle;
|
|
struct smb2_create create;
|
|
struct smb2_read read;
|
|
AfpInfo *afpinfo = NULL;
|
|
char *afpinfo_buf = NULL;
|
|
const char *type_creator = "SMB,OLE!";
|
|
|
|
torture_comment(tctx, "Checking create of AfpInfo stream\n");
|
|
|
|
smb2_util_unlink(tree, fname);
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
|
|
create.in.share_access = FILE_SHARE_READ | FILE_SHARE_DELETE;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
|
|
create.in.fname = sname;
|
|
|
|
smb2_transport_compound_start(tree->session->transport, 2);
|
|
|
|
req[0] = smb2_create_send(tree, &create);
|
|
|
|
handle.data[0] = UINT64_MAX;
|
|
handle.data[1] = UINT64_MAX;
|
|
|
|
smb2_transport_compound_set_related(tree->session->transport, true);
|
|
|
|
ZERO_STRUCT(read);
|
|
read.in.file.handle = handle;
|
|
read.in.length = AFP_INFO_SIZE;
|
|
req[1] = smb2_read_send(tree, &read);
|
|
|
|
status = smb2_create_recv(req[0], tree, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_recv failed");
|
|
|
|
handle = create.out.file.handle;
|
|
|
|
status = smb2_read_recv(req[1], tree, &read);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_read_recv failed");
|
|
|
|
afpinfo = torture_afpinfo_new(mem_ctx);
|
|
torture_assert_goto(tctx, afpinfo != NULL, ret, done, "torture_afpinfo_new failed");
|
|
|
|
memcpy(afpinfo->afpi_FinderInfo, type_creator, 8);
|
|
|
|
afpinfo_buf = torture_afpinfo_pack(tctx, afpinfo);
|
|
torture_assert_goto(tctx, afpinfo_buf != NULL, ret, done, "torture_afpinfo_new failed");
|
|
|
|
status = smb2_util_write(tree, handle, afpinfo_buf, 0, AFP_INFO_SIZE);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_write failed");
|
|
|
|
smb2_util_close(tree, handle);
|
|
|
|
ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
|
|
0, 60, 16, 8, type_creator);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_delete_file_with_rfork(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
const char *fname = "torture_write_rfork_io";
|
|
const char *rfork_content = "1234567890";
|
|
NTSTATUS status;
|
|
bool ret = true;
|
|
|
|
smb2_util_unlink(tree, fname);
|
|
|
|
torture_comment(tctx, "Test deleting file with resource fork\n");
|
|
|
|
ret = torture_setup_file(tctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed\n");
|
|
|
|
ret = write_stream(tree, __location__, tctx, tctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
10, 10, rfork_content);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed\n");
|
|
|
|
ret = check_stream(tree, __location__, tctx, tctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
0, 20, 10, 10, rfork_content);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed\n");
|
|
|
|
status = smb2_util_unlink(tree, fname);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "check_stream failed\n");
|
|
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static bool test_rename_and_read_rsrc(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create, create2;
|
|
struct smb2_handle h1, h2;
|
|
const char *fname = "test_rename_openfile";
|
|
const char *sname = "test_rename_openfile" AFPRESOURCE_STREAM_NAME;
|
|
const char *fname_renamed = "test_rename_openfile_renamed";
|
|
const char *data = "1234567890";
|
|
union smb_setfileinfo sinfo;
|
|
|
|
ret = enable_aapl(tctx, tree);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
|
|
|
|
torture_comment(tctx, "Create file with resource fork\n");
|
|
|
|
ret = torture_setup_file(tctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
ret = write_stream(tree, __location__, tctx, tctx,
|
|
fname, AFPRESOURCE_STREAM_NAME, 0, 10, data);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed");
|
|
|
|
torture_comment(tctx, "Open resource fork\n");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree, tctx, &create);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h1 = create.out.file.handle;
|
|
|
|
torture_comment(tctx, "Rename base file\n");
|
|
|
|
ZERO_STRUCT(create2);
|
|
create2.in.desired_access = SEC_FILE_ALL;
|
|
create2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
|
|
create2.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
|
|
create2.in.fname = fname;
|
|
|
|
status = smb2_create(tree, tctx, &create2);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
h2 = create2.out.file.handle;
|
|
|
|
ZERO_STRUCT(sinfo);
|
|
sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
|
|
sinfo.rename_information.in.file.handle = h2;
|
|
sinfo.rename_information.in.overwrite = 0;
|
|
sinfo.rename_information.in.root_fid = 0;
|
|
sinfo.rename_information.in.new_name = fname_renamed;
|
|
|
|
status = smb2_setinfo_file(tree, &sinfo);
|
|
torture_assert_ntstatus_equal_goto(
|
|
tctx, status, NT_STATUS_ACCESS_DENIED, ret, done,
|
|
"smb2_setinfo_file failed");
|
|
|
|
smb2_util_close(tree, h1);
|
|
smb2_util_close(tree, h2);
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_unlink(tree, fname_renamed);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_readdir_attr_illegal_ntfs(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *name = "test" "\xef\x80\xa2" "aapl"; /* "test:aapl" */
|
|
const char *fname = BASEDIR "\\test" "\xef\x80\xa2" "aapl"; /* "test:aapl" */
|
|
NTSTATUS status;
|
|
struct smb2_handle testdirh;
|
|
bool ret = true;
|
|
struct smb2_create io;
|
|
AfpInfo *info;
|
|
const char *type_creator = "SMB,OLE!";
|
|
struct smb2_find f;
|
|
unsigned int count;
|
|
union smb_search_data *d;
|
|
uint64_t rfork_len;
|
|
int i;
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir failed");
|
|
smb2_util_close(tree, testdirh);
|
|
|
|
torture_comment(tctx, "Enabling AAPL\n");
|
|
|
|
ret = enable_aapl(tctx, tree);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
|
|
|
|
/*
|
|
* Now that Requested AAPL extensions are enabled, setup some
|
|
* Mac files with metadata and resource fork
|
|
*/
|
|
|
|
torture_comment(tctx, "Preparing file\n");
|
|
|
|
ret = torture_setup_file(mem_ctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed");
|
|
|
|
info = torture_afpinfo_new(mem_ctx);
|
|
torture_assert_not_null_goto(tctx, info, ret, done, "torture_afpinfo_new failed");
|
|
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed");
|
|
|
|
ret = write_stream(tree, __location__, tctx, mem_ctx,
|
|
fname, AFPRESOURCE_STREAM_NAME,
|
|
0, 3, "foo");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed");
|
|
|
|
/*
|
|
* Ok, file is prepared, now call smb2/find
|
|
*/
|
|
|
|
torture_comment(tctx, "Issue find\n");
|
|
|
|
ZERO_STRUCT(io);
|
|
io.in.desired_access = SEC_RIGHTS_DIR_READ;
|
|
io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
|
|
io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
io.in.share_access = (NTCREATEX_SHARE_ACCESS_READ |
|
|
NTCREATEX_SHARE_ACCESS_WRITE |
|
|
NTCREATEX_SHARE_ACCESS_DELETE);
|
|
io.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
io.in.fname = BASEDIR;
|
|
status = smb2_create(tree, tctx, &io);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
|
|
|
|
ZERO_STRUCT(f);
|
|
f.in.file.handle = io.out.file.handle;
|
|
f.in.pattern = "*";
|
|
f.in.max_response_size = 0x1000;
|
|
f.in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
|
|
|
|
status = smb2_find_level(tree, tree, &f, &count, &d);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_level failed");
|
|
|
|
status = smb2_util_close(tree, io.out.file.handle);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed");
|
|
|
|
torture_comment(tctx, "Checking find response with enriched macOS metadata\n");
|
|
|
|
for (i = 0; i < count; i++) {
|
|
const char *found = d[i].id_both_directory_info.name.s;
|
|
|
|
if (!strcmp(found, ".") || !strcmp(found, ".."))
|
|
continue;
|
|
if (strncmp(found, "._", 2) == 0) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
torture_assert_str_equal_goto(tctx,
|
|
d[i].id_both_directory_info.name.s, name,
|
|
ret, done, "bad name");
|
|
|
|
rfork_len = BVAL(d[i].id_both_directory_info.short_name_buf, 0);
|
|
torture_assert_int_equal_goto(tctx, rfork_len, 3, ret, done, "bad resource fork length");
|
|
|
|
torture_assert_mem_equal_goto(tctx, type_creator,
|
|
d[i].id_both_directory_info.short_name_buf + 8,
|
|
8, ret, done, "Bad FinderInfo");
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_invalid_afpinfo(struct torture_context *tctx,
|
|
struct smb2_tree *tree1,
|
|
struct smb2_tree *tree2)
|
|
{
|
|
const char *fname = "filtest_invalid_afpinfo";
|
|
const char *sname = "filtest_invalid_afpinfo" AFPINFO_STREAM_NAME;
|
|
struct smb2_create create;
|
|
const char *streams_basic[] = {
|
|
"::$DATA"
|
|
};
|
|
const char *streams_afpinfo[] = {
|
|
"::$DATA",
|
|
AFPINFO_STREAM
|
|
};
|
|
NTSTATUS status;
|
|
bool ret = true;
|
|
|
|
if (tree2 == NULL) {
|
|
torture_skip_goto(tctx, done, "need second share without fruit\n");
|
|
}
|
|
|
|
torture_comment(tctx, "Testing invalid AFP_AfpInfo stream\n");
|
|
|
|
ret = torture_setup_file(tctx, tree2, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
ret = write_stream(tree2, __location__, tctx, tctx,
|
|
fname, AFPINFO_STREAM_NAME,
|
|
0, 3, "foo");
|
|
torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed");
|
|
|
|
ret = check_stream_list(tree2, tctx, fname, 2, streams_afpinfo, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
torture_comment(tctx, "Listing streams, bad AFPINFO stream must not be present\n");
|
|
|
|
ret = check_stream_list(tree1, tctx, fname, 1, streams_basic, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
|
|
|
|
torture_comment(tctx, "Try to open AFPINFO stream, must fail\n");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_ALL;
|
|
create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree1, tctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
|
|
ret, done, "Stream still around?");
|
|
|
|
done:
|
|
smb2_util_unlink(tree1, fname);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_zero_file_id(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
const char *fname = "filtest_file_id";
|
|
struct smb2_create create = {0};
|
|
NTSTATUS status;
|
|
bool ret = true;
|
|
uint8_t zero_file_id[8] = {0};
|
|
|
|
torture_comment(tctx, "Testing zero file id\n");
|
|
|
|
ret = torture_setup_file(tctx, tree, fname, false);
|
|
torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.fname = fname;
|
|
create.in.query_on_disk_id = true;
|
|
|
|
status = smb2_create(tree, tctx, &create);
|
|
torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret,
|
|
done,
|
|
"test file could not be opened");
|
|
torture_assert_mem_not_equal_goto(tctx, create.out.on_disk_id,
|
|
zero_file_id, 8, ret, done,
|
|
"unexpected zero file id");
|
|
|
|
smb2_util_close(tree, create.out.file.handle);
|
|
|
|
ret = enable_aapl(tctx, tree);
|
|
torture_assert(tctx, ret == true, "enable_aapl failed");
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.fname = fname;
|
|
create.in.query_on_disk_id = true;
|
|
|
|
status = smb2_create(tree, tctx, &create);
|
|
torture_assert_ntstatus_equal_goto(
|
|
tctx, status, NT_STATUS_OK, ret, done,
|
|
"test file could not be opened with AAPL");
|
|
torture_assert_mem_equal_goto(tctx, create.out.on_disk_id, zero_file_id,
|
|
8, ret, done, "non-zero file id");
|
|
|
|
smb2_util_close(tree, create.out.file.handle);
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
return ret;
|
|
}
|
|
|
|
static bool copy_one_stream(struct torture_context *torture,
|
|
struct smb2_tree *tree,
|
|
TALLOC_CTX *tmp_ctx,
|
|
const char *src_sname,
|
|
const char *dst_sname)
|
|
{
|
|
struct smb2_handle src_h = {{0}};
|
|
struct smb2_handle dest_h = {{0}};
|
|
NTSTATUS status;
|
|
union smb_ioctl io;
|
|
struct srv_copychunk_copy cc_copy;
|
|
struct srv_copychunk_rsp cc_rsp;
|
|
enum ndr_err_code ndr_ret;
|
|
bool ok = false;
|
|
|
|
ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
|
|
1, /* 1 chunk */
|
|
src_sname,
|
|
&src_h, 256, /* fill 256 byte src file */
|
|
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
|
|
dst_sname,
|
|
&dest_h, 0, /* 0 byte dest file */
|
|
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
|
|
&cc_copy,
|
|
&io);
|
|
torture_assert_goto(torture, ok == true, ok, done,
|
|
"setup copy chunk error\n");
|
|
|
|
/* copy all src file data (via a single chunk desc) */
|
|
cc_copy.chunks[0].source_off = 0;
|
|
cc_copy.chunks[0].target_off = 0;
|
|
cc_copy.chunks[0].length = 256;
|
|
|
|
ndr_ret = ndr_push_struct_blob(
|
|
&io.smb2.in.out, tmp_ctx, &cc_copy,
|
|
(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
|
|
|
|
torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
|
|
"ndr_push_srv_copychunk_copy\n");
|
|
|
|
status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
|
|
torture_assert_ntstatus_ok_goto(torture, status, ok, done,
|
|
"FSCTL_SRV_COPYCHUNK\n");
|
|
|
|
ndr_ret = ndr_pull_struct_blob(
|
|
&io.smb2.out.out, tmp_ctx, &cc_rsp,
|
|
(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
|
|
|
|
torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
|
|
"ndr_pull_srv_copychunk_rsp\n");
|
|
|
|
ok = check_copy_chunk_rsp(torture, &cc_rsp,
|
|
1, /* chunks written */
|
|
0, /* chunk bytes unsuccessfully written */
|
|
256); /* total bytes written */
|
|
torture_assert_goto(torture, ok == true, ok, done,
|
|
"bad copy chunk response data\n");
|
|
|
|
ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
|
|
if (!ok) {
|
|
torture_fail(torture, "inconsistent file data\n");
|
|
}
|
|
|
|
done:
|
|
if (!smb2_util_handle_empty(src_h)) {
|
|
smb2_util_close(tree, src_h);
|
|
}
|
|
if (!smb2_util_handle_empty(dest_h)) {
|
|
smb2_util_close(tree, dest_h);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static bool copy_finderinfo_stream(struct torture_context *torture,
|
|
struct smb2_tree *tree,
|
|
TALLOC_CTX *tmp_ctx,
|
|
const char *src_name,
|
|
const char *dst_name)
|
|
{
|
|
struct smb2_handle src_h = {{0}};
|
|
struct smb2_handle dest_h = {{0}};
|
|
NTSTATUS status;
|
|
union smb_ioctl io;
|
|
struct srv_copychunk_copy cc_copy;
|
|
struct srv_copychunk_rsp cc_rsp;
|
|
enum ndr_err_code ndr_ret;
|
|
const char *type_creator = "SMB,OLE!";
|
|
AfpInfo *info = NULL;
|
|
const char *src_name_afpinfo = NULL;
|
|
const char *dst_name_afpinfo = NULL;
|
|
bool ok = false;
|
|
|
|
src_name_afpinfo = talloc_asprintf(tmp_ctx, "%s%s", src_name,
|
|
AFPINFO_STREAM);
|
|
torture_assert_not_null_goto(torture, src_name_afpinfo, ok, done,
|
|
"talloc_asprintf failed\n");
|
|
|
|
dst_name_afpinfo = talloc_asprintf(tmp_ctx, "%s%s", dst_name,
|
|
AFPINFO_STREAM);
|
|
torture_assert_not_null_goto(torture, dst_name_afpinfo, ok, done,
|
|
"talloc_asprintf failed\n");
|
|
|
|
info = torture_afpinfo_new(tmp_ctx);
|
|
torture_assert_not_null_goto(torture, info, ok, done,
|
|
"torture_afpinfo_new failed\n");
|
|
|
|
memcpy(info->afpi_FinderInfo, type_creator, 8);
|
|
ok = torture_write_afpinfo(tree, torture, tmp_ctx, src_name, info);
|
|
torture_assert_goto(torture, ok == true, ok, done,
|
|
"torture_write_afpinfo failed\n");
|
|
|
|
ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
|
|
1, /* 1 chunk */
|
|
src_name_afpinfo,
|
|
&src_h, 0,
|
|
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
|
|
dst_name_afpinfo,
|
|
&dest_h, 0,
|
|
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
|
|
&cc_copy,
|
|
&io);
|
|
torture_assert_goto(torture, ok == true, ok, done,
|
|
"setup copy chunk error\n");
|
|
|
|
/* copy all src file data (via a single chunk desc) */
|
|
cc_copy.chunks[0].source_off = 0;
|
|
cc_copy.chunks[0].target_off = 0;
|
|
cc_copy.chunks[0].length = 60;
|
|
|
|
ndr_ret = ndr_push_struct_blob(
|
|
&io.smb2.in.out, tmp_ctx, &cc_copy,
|
|
(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
|
|
|
|
torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
|
|
"ndr_push_srv_copychunk_copy\n");
|
|
|
|
status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
|
|
torture_assert_ntstatus_ok_goto(torture, status, ok, done,
|
|
"FSCTL_SRV_COPYCHUNK\n");
|
|
|
|
ndr_ret = ndr_pull_struct_blob(
|
|
&io.smb2.out.out, tmp_ctx, &cc_rsp,
|
|
(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
|
|
|
|
torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
|
|
"ndr_pull_srv_copychunk_rsp\n");
|
|
|
|
smb2_util_close(tree, src_h);
|
|
ZERO_STRUCT(src_h);
|
|
smb2_util_close(tree, dest_h);
|
|
ZERO_STRUCT(dest_h);
|
|
|
|
ok = check_copy_chunk_rsp(torture, &cc_rsp,
|
|
1, /* chunks written */
|
|
0, /* chunk bytes unsuccessfully written */
|
|
60); /* total bytes written */
|
|
torture_assert_goto(torture, ok == true, ok, done,
|
|
"bad copy chunk response data\n");
|
|
|
|
ok = check_stream(tree, __location__, torture, tmp_ctx,
|
|
dst_name, AFPINFO_STREAM,
|
|
0, 60, 16, 8, type_creator);
|
|
torture_assert_goto(torture, ok == true, ok, done, "check_stream failed\n");
|
|
|
|
done:
|
|
if (!smb2_util_handle_empty(src_h)) {
|
|
smb2_util_close(tree, src_h);
|
|
}
|
|
if (!smb2_util_handle_empty(dest_h)) {
|
|
smb2_util_close(tree, dest_h);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static bool test_copy_chunk_streams(struct torture_context *torture,
|
|
struct smb2_tree *tree)
|
|
{
|
|
const char *src_name = "src";
|
|
const char *dst_name = "dst";
|
|
struct names {
|
|
const char *src_sname;
|
|
const char *dst_sname;
|
|
} names[] = {
|
|
{ "src:foo", "dst:foo" },
|
|
{ "src" AFPRESOURCE_STREAM, "dst" AFPRESOURCE_STREAM }
|
|
};
|
|
int i;
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
bool ok = false;
|
|
|
|
tmp_ctx = talloc_new(tree);
|
|
torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
|
|
"torture_setup_file\n");
|
|
|
|
smb2_util_unlink(tree, src_name);
|
|
smb2_util_unlink(tree, dst_name);
|
|
|
|
ok = torture_setup_file(torture, tree, src_name, false);
|
|
torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
|
|
ok = torture_setup_file(torture, tree, dst_name, false);
|
|
torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
|
|
|
|
for (i = 0; i < ARRAY_SIZE(names); i++) {
|
|
ok = copy_one_stream(torture, tree, tmp_ctx,
|
|
names[i].src_sname,
|
|
names[i].dst_sname);
|
|
torture_assert_goto(torture, ok == true, ok, done,
|
|
"copy_one_stream failed\n");
|
|
}
|
|
|
|
ok = copy_finderinfo_stream(torture, tree, tmp_ctx,
|
|
src_name, dst_name);
|
|
torture_assert_goto(torture, ok == true, ok, done,
|
|
"copy_finderinfo_stream failed\n");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, src_name);
|
|
smb2_util_unlink(tree, dst_name);
|
|
talloc_free(tmp_ctx);
|
|
return ok;
|
|
}
|
|
|
|
/*
|
|
* Ensure this security descriptor has exactly one mode, uid
|
|
* and gid.
|
|
*/
|
|
|
|
static NTSTATUS check_nfs_sd(const struct security_descriptor *psd)
|
|
{
|
|
uint32_t i;
|
|
bool got_one_mode = false;
|
|
bool got_one_uid = false;
|
|
bool got_one_gid = false;
|
|
|
|
if (psd->dacl == NULL) {
|
|
return NT_STATUS_INVALID_SECURITY_DESCR;
|
|
}
|
|
|
|
for (i = 0; i < psd->dacl->num_aces; i++) {
|
|
if (dom_sid_compare_domain(&global_sid_Unix_NFS_Mode,
|
|
&psd->dacl->aces[i].trustee) == 0) {
|
|
if (got_one_mode == true) {
|
|
/* Can't have more than one. */
|
|
return NT_STATUS_INVALID_SECURITY_DESCR;
|
|
}
|
|
got_one_mode = true;
|
|
}
|
|
}
|
|
for (i = 0; i < psd->dacl->num_aces; i++) {
|
|
if (dom_sid_compare_domain(&global_sid_Unix_NFS_Users,
|
|
&psd->dacl->aces[i].trustee) == 0) {
|
|
if (got_one_uid == true) {
|
|
/* Can't have more than one. */
|
|
return NT_STATUS_INVALID_SECURITY_DESCR;
|
|
}
|
|
got_one_uid = true;
|
|
}
|
|
}
|
|
for (i = 0; i < psd->dacl->num_aces; i++) {
|
|
if (dom_sid_compare_domain(&global_sid_Unix_NFS_Groups,
|
|
&psd->dacl->aces[i].trustee) == 0) {
|
|
if (got_one_gid == true) {
|
|
/* Can't have more than one. */
|
|
return NT_STATUS_INVALID_SECURITY_DESCR;
|
|
}
|
|
got_one_gid = true;
|
|
}
|
|
}
|
|
/* Must have at least one of each. */
|
|
if (got_one_mode == false ||
|
|
got_one_uid == false ||
|
|
got_one_gid == false) {
|
|
return NT_STATUS_INVALID_SECURITY_DESCR;
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static bool test_nfs_aces(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
struct security_ace ace;
|
|
struct dom_sid sid;
|
|
const char *fname = BASEDIR "\\nfs_aces.txt";
|
|
struct smb2_handle h = {{0}};
|
|
union smb_fileinfo finfo2;
|
|
union smb_setfileinfo set;
|
|
struct security_descriptor *psd = NULL;
|
|
NTSTATUS status;
|
|
bool ret = true;
|
|
|
|
ret = enable_aapl(tctx, tree);
|
|
torture_assert(tctx, ret == true, "enable_aapl failed");
|
|
|
|
/* clean slate ...*/
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, h);
|
|
|
|
/* Create a test file. */
|
|
status = torture_smb2_testfile_access(tree,
|
|
fname,
|
|
&h,
|
|
SEC_STD_READ_CONTROL |
|
|
SEC_STD_WRITE_DAC |
|
|
SEC_RIGHTS_FILE_ALL);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
/* Get the ACL. */
|
|
finfo2.query_secdesc.in.secinfo_flags =
|
|
SECINFO_OWNER |
|
|
SECINFO_GROUP |
|
|
SECINFO_DACL;
|
|
finfo2.generic.level = RAW_FILEINFO_SEC_DESC;
|
|
finfo2.generic.in.file.handle = h;
|
|
status = smb2_getinfo_file(tree, tctx, &finfo2);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
psd = finfo2.query_secdesc.out.sd;
|
|
|
|
/* Ensure we have only single mode/uid/gid NFS entries. */
|
|
status = check_nfs_sd(psd);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
NDR_PRINT_DEBUG(
|
|
security_descriptor,
|
|
discard_const_p(struct security_descriptor, psd));
|
|
}
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
/* Add a couple of extra NFS uids and gids. */
|
|
sid_compose(&sid, &global_sid_Unix_NFS_Users, 27);
|
|
init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
|
|
status = security_descriptor_dacl_add(psd, &ace);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
status = security_descriptor_dacl_add(psd, &ace);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
sid_compose(&sid, &global_sid_Unix_NFS_Groups, 300);
|
|
init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
|
|
status = security_descriptor_dacl_add(psd, &ace);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
status = security_descriptor_dacl_add(psd, &ace);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
/* Now set on the file handle. */
|
|
set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
|
|
set.set_secdesc.in.file.handle = h;
|
|
set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
|
|
set.set_secdesc.in.sd = psd;
|
|
status = smb2_setinfo_file(tree, &set);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
/* Get the ACL again. */
|
|
finfo2.query_secdesc.in.secinfo_flags =
|
|
SECINFO_OWNER |
|
|
SECINFO_GROUP |
|
|
SECINFO_DACL;
|
|
finfo2.generic.level = RAW_FILEINFO_SEC_DESC;
|
|
finfo2.generic.in.file.handle = h;
|
|
status = smb2_getinfo_file(tree, tctx, &finfo2);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
psd = finfo2.query_secdesc.out.sd;
|
|
|
|
/* Ensure we have only single mode/uid/gid NFS entries. */
|
|
status = check_nfs_sd(psd);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
NDR_PRINT_DEBUG(
|
|
security_descriptor,
|
|
discard_const_p(struct security_descriptor, psd));
|
|
}
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
done:
|
|
if (!smb2_util_handle_empty(h)) {
|
|
smb2_util_close(tree, h);
|
|
}
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static bool test_setinfo_stream_eof(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
bool ret = true;
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
union smb_setfileinfo sfinfo;
|
|
union smb_fileinfo finfo;
|
|
struct smb2_handle h1;
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
const char *fname = BASEDIR "\\file";
|
|
const char *sname = BASEDIR "\\file:foo";
|
|
|
|
torture_assert_goto(tctx, mem_ctx != NULL, ret, done,
|
|
"talloc_new failed\n");
|
|
|
|
torture_comment(tctx, "Test setting EOF on a stream\n");
|
|
|
|
smb2_deltree(tree, BASEDIR);
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testdir\n");
|
|
smb2_util_close(tree, h1);
|
|
|
|
status = torture_smb2_testfile(tree, fname, &h1);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
smb2_util_close(tree, h1);
|
|
|
|
status = torture_smb2_testfile_access(tree, sname, &h1,
|
|
SEC_FILE_WRITE_DATA);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
|
|
status = smb2_util_write(tree, h1, "1234567890", 0, 10);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"smb2_util_write failed\n");
|
|
smb2_util_close(tree, h1);
|
|
|
|
/*
|
|
* Test setting EOF to 21
|
|
*/
|
|
|
|
torture_comment(tctx, "Setting stream EOF to 21\n");
|
|
|
|
status = torture_smb2_testfile_access(tree, sname, &h1,
|
|
SEC_FILE_WRITE_DATA);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 21;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status,
|
|
ret, done, "set EOF 21 failed\n");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
status = torture_smb2_testfile_access(tree, sname, &h1,
|
|
SEC_FILE_WRITE_DATA);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
|
|
ZERO_STRUCT(finfo);
|
|
finfo.generic.level = RAW_FILEINFO_STANDARD_INFORMATION;
|
|
finfo.generic.in.file.handle = h1;
|
|
status = smb2_getinfo_file(tree, mem_ctx, &finfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"smb2_getinfo_file failed");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
torture_assert_goto(tctx, finfo.standard_info.out.size == 21,
|
|
ret, done, "size != 21\n");
|
|
|
|
/*
|
|
* Test setting EOF to 0
|
|
*/
|
|
|
|
torture_comment(tctx, "Setting stream EOF to 0\n");
|
|
|
|
status = torture_smb2_testfile_access(tree, sname, &h1,
|
|
SEC_FILE_WRITE_DATA);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 0;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"set eof 0 failed\n");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
status = torture_smb2_testfile_access(tree, sname, &h1,
|
|
SEC_FILE_WRITE_DATA);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
|
|
ZERO_STRUCT(finfo);
|
|
finfo.generic.level = RAW_FILEINFO_STANDARD_INFORMATION;
|
|
finfo.generic.in.file.handle = h1;
|
|
status = smb2_getinfo_file(tree, mem_ctx, &finfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"smb2_getinfo_file failed\n");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
torture_assert_goto(tctx, finfo.standard_info.out.size == 0,
|
|
ret, done, "size != 0\n");
|
|
|
|
/*
|
|
* Test setinfo end-of-file info to 1
|
|
*/
|
|
|
|
torture_comment(tctx, "Setting stream EOF to 1\n");
|
|
|
|
status = torture_smb2_testfile_access(tree, sname, &h1,
|
|
SEC_FILE_WRITE_DATA);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 1;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"set EOF 1 failed\n");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
status = torture_smb2_testfile_access(tree, sname, &h1,
|
|
SEC_FILE_WRITE_DATA);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
|
|
ZERO_STRUCT(finfo);
|
|
finfo.generic.level = RAW_FILEINFO_STANDARD_INFORMATION;
|
|
finfo.generic.in.file.handle = h1;
|
|
status = smb2_getinfo_file(tree, mem_ctx, &finfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"smb2_getinfo_file failed\n");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
torture_assert_goto(tctx, finfo.standard_info.out.size == 1,
|
|
ret, done, "size != 1\n");
|
|
|
|
/*
|
|
* Test setting EOF to 0 with AAPL enabled, should delete stream
|
|
*/
|
|
|
|
torture_comment(tctx, "Enabling AAPL extensions\n");
|
|
|
|
ret = enable_aapl(tctx, tree);
|
|
torture_assert(tctx, ret == true, "enable_aapl failed\n");
|
|
|
|
torture_comment(tctx, "Setting stream EOF to 0\n");
|
|
status = torture_smb2_testfile_access(tree, sname, &h1,
|
|
SEC_FILE_WRITE_DATA);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"torture_smb2_testfile failed\n");
|
|
|
|
ZERO_STRUCT(sfinfo);
|
|
sfinfo.generic.in.file.handle = h1;
|
|
sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
|
|
sfinfo.position_information.in.position = 0;
|
|
status = smb2_setinfo_file(tree, &sfinfo);
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
"set eof 0 failed\n");
|
|
|
|
smb2_util_close(tree, h1);
|
|
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.create_disposition = NTCREATEX_DISP_OPEN;
|
|
create.in.fname = sname;
|
|
|
|
status = smb2_create(tree, tctx, &create);
|
|
torture_assert_ntstatus_equal_goto(
|
|
tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done,
|
|
"Unexpected status\n");
|
|
|
|
done:
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_util_rmdir(tree, BASEDIR);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Note: This test depends on "vfs objects = catia fruit streams_xattr". For
|
|
* some tests torture must be run on the host it tests and takes an additional
|
|
* argument with the local path to the share:
|
|
* "--option=torture:localdir=<SHAREPATH>".
|
|
*
|
|
* When running against an OS X SMB server add "--option=torture:osx=true"
|
|
*/
|
|
struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx)
|
|
{
|
|
struct torture_suite *suite = torture_suite_create(
|
|
ctx, "fruit");
|
|
|
|
suite->description = talloc_strdup(suite, "vfs_fruit tests");
|
|
|
|
torture_suite_add_1smb2_test(suite, "copyfile", test_copyfile);
|
|
torture_suite_add_1smb2_test(suite, "read metadata", test_read_afpinfo);
|
|
torture_suite_add_1smb2_test(suite, "write metadata", test_write_atalk_metadata);
|
|
torture_suite_add_1smb2_test(suite, "resource fork IO", test_write_atalk_rfork_io);
|
|
torture_suite_add_1smb2_test(suite, "SMB2/CREATE context AAPL", test_aapl);
|
|
torture_suite_add_1smb2_test(suite, "stream names", test_stream_names);
|
|
torture_suite_add_1smb2_test(suite, "truncate resource fork to 0 bytes", test_rfork_truncate);
|
|
torture_suite_add_1smb2_test(suite, "opening and creating resource fork", test_rfork_create);
|
|
torture_suite_add_1smb2_test(suite, "rename_dir_openfile", test_rename_dir_openfile);
|
|
torture_suite_add_1smb2_test(suite, "File without AFP_AfpInfo", test_afpinfo_enoent);
|
|
torture_suite_add_1smb2_test(suite, "create delete-on-close AFP_AfpInfo", test_create_delete_on_close);
|
|
torture_suite_add_1smb2_test(suite, "setinfo delete-on-close AFP_AfpInfo", test_setinfo_delete_on_close);
|
|
torture_suite_add_1smb2_test(suite, "setinfo eof AFP_AfpInfo", test_setinfo_eof);
|
|
torture_suite_add_1smb2_test(suite, "delete AFP_AfpInfo by writing all 0", test_afpinfo_all0);
|
|
torture_suite_add_1smb2_test(suite, "create delete-on-close AFP_AfpResource", test_create_delete_on_close_resource);
|
|
torture_suite_add_1smb2_test(suite, "setinfo delete-on-close AFP_AfpResource", test_setinfo_delete_on_close_resource);
|
|
torture_suite_add_1smb2_test(suite, "setinfo eof AFP_AfpResource", test_setinfo_eof_resource);
|
|
torture_suite_add_1smb2_test(suite, "setinfo eof stream", test_setinfo_stream_eof);
|
|
torture_suite_add_1smb2_test(suite, "null afpinfo", test_null_afpinfo);
|
|
torture_suite_add_1smb2_test(suite, "delete", test_delete_file_with_rfork);
|
|
torture_suite_add_1smb2_test(suite, "read open rsrc after rename", test_rename_and_read_rsrc);
|
|
torture_suite_add_1smb2_test(suite, "readdir_attr with names with illegal ntfs characters", test_readdir_attr_illegal_ntfs);
|
|
torture_suite_add_2ns_smb2_test(suite, "invalid AFP_AfpInfo", test_invalid_afpinfo);
|
|
torture_suite_add_1smb2_test(suite, "creating rsrc with read-only access", test_rfork_create_ro);
|
|
torture_suite_add_1smb2_test(suite, "copy-chunk streams", test_copy_chunk_streams);
|
|
torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion", test_adouble_conversion);
|
|
torture_suite_add_1smb2_test(suite, "NFS ACE entries", test_nfs_aces);
|
|
|
|
return suite;
|
|
}
|
|
|
|
static bool test_stream_names_local(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
NTSTATUS status;
|
|
struct smb2_create create;
|
|
struct smb2_handle h;
|
|
const char *fname = BASEDIR "\\stream_names.txt";
|
|
const char *sname1;
|
|
bool ret;
|
|
/* UTF8 private use are starts at 0xef 0x80 0x80 (0xf000) */
|
|
const char *streams[] = {
|
|
":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */
|
|
":bar" "\xef\x80\xa2" "baz:$DATA", /* "bar:baz:$DATA" */
|
|
"::$DATA"
|
|
};
|
|
const char *localdir = NULL;
|
|
|
|
localdir = torture_setting_string(tctx, "localdir", NULL);
|
|
if (localdir == NULL) {
|
|
torture_skip(tctx, "Need localdir for test");
|
|
}
|
|
|
|
sname1 = talloc_asprintf(mem_ctx, "%s%s", fname, streams[0]);
|
|
|
|
/* clean slate ...*/
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, h);
|
|
|
|
torture_comment(tctx, "(%s) testing stream names\n", __location__);
|
|
ZERO_STRUCT(create);
|
|
create.in.desired_access = SEC_FILE_WRITE_DATA;
|
|
create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
|
|
create.in.share_access =
|
|
NTCREATEX_SHARE_ACCESS_DELETE|
|
|
NTCREATEX_SHARE_ACCESS_READ|
|
|
NTCREATEX_SHARE_ACCESS_WRITE;
|
|
create.in.create_disposition = NTCREATEX_DISP_CREATE;
|
|
create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
|
|
create.in.fname = sname1;
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, create.out.file.handle);
|
|
|
|
ret = torture_setup_local_xattr(tctx, "localdir", BASEDIR "/stream_names.txt",
|
|
"user.DosStream.bar:baz:$DATA",
|
|
"data", strlen("data"));
|
|
CHECK_VALUE(ret, true);
|
|
|
|
ret = check_stream_list(tree, tctx, fname, 3, streams, false);
|
|
CHECK_VALUE(ret, true);
|
|
|
|
done:
|
|
status = smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
talloc_free(mem_ctx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool test_fruit_locking_conflict(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx;
|
|
struct smb2_create create;
|
|
struct smb2_handle h;
|
|
struct smb2_lock lck;
|
|
struct smb2_lock_element el;
|
|
const char *fname = BASEDIR "\\locking_conflict.txt";
|
|
NTSTATUS status;
|
|
bool ret = false;
|
|
|
|
mem_ctx = talloc_new(tctx);
|
|
torture_assert_not_null(tctx, mem_ctx, "talloc_new failed");
|
|
|
|
/* clean slate ...*/
|
|
smb2_util_unlink(tree, fname);
|
|
smb2_deltree(tree, fname);
|
|
smb2_deltree(tree, BASEDIR);
|
|
|
|
status = torture_smb2_testdir(tree, BASEDIR, &h);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
smb2_util_close(tree, h);
|
|
|
|
create = (struct smb2_create) {
|
|
.in.desired_access = SEC_RIGHTS_FILE_READ,
|
|
.in.file_attributes = FILE_ATTRIBUTE_NORMAL,
|
|
.in.share_access =
|
|
NTCREATEX_SHARE_ACCESS_READ|
|
|
NTCREATEX_SHARE_ACCESS_WRITE,
|
|
.in.create_disposition = NTCREATEX_DISP_CREATE,
|
|
.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
|
|
.in.fname = fname,
|
|
};
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
h = create.out.file.handle;
|
|
|
|
el = (struct smb2_lock_element) {
|
|
.offset = 0xfffffffffffffffc,
|
|
.length = 1,
|
|
.flags = SMB2_LOCK_FLAG_EXCLUSIVE,
|
|
};
|
|
lck = (struct smb2_lock) {
|
|
.in.lock_count = 1,
|
|
.in.file.handle = h,
|
|
.in.locks = &el,
|
|
};
|
|
|
|
status = smb2_lock(tree, &lck);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
el = (struct smb2_lock_element) {
|
|
.offset = 0,
|
|
.length = 0x7fffffffffffffff,
|
|
.flags = SMB2_LOCK_FLAG_EXCLUSIVE,
|
|
};
|
|
status = smb2_lock(tree, &lck);
|
|
CHECK_STATUS(status, NT_STATUS_OK);
|
|
|
|
create = (struct smb2_create) {
|
|
.in.desired_access =
|
|
SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE,
|
|
.in.file_attributes = FILE_ATTRIBUTE_NORMAL,
|
|
.in.share_access = NTCREATEX_SHARE_ACCESS_READ,
|
|
.in.create_disposition = NTCREATEX_DISP_OPEN,
|
|
.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
|
|
.in.fname = fname,
|
|
};
|
|
|
|
status = smb2_create(tree, mem_ctx, &create);
|
|
CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
|
|
|
|
{
|
|
struct smb2_close cl = {
|
|
.level = RAW_CLOSE_SMB2,
|
|
.in.file.handle = h,
|
|
};
|
|
smb2_close(tree, &cl);
|
|
}
|
|
|
|
ret = true;
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
struct torture_suite *torture_vfs_fruit_netatalk(TALLOC_CTX *ctx)
|
|
{
|
|
struct torture_suite *suite = torture_suite_create(
|
|
ctx, "fruit_netatalk");
|
|
|
|
suite->description = talloc_strdup(suite, "vfs_fruit tests for Netatalk interop that require fruit:metadata=netatalk");
|
|
|
|
torture_suite_add_1smb2_test(suite, "read netatalk metadata", test_read_netatalk_metadata);
|
|
torture_suite_add_1smb2_test(suite, "stream names with locally created xattr", test_stream_names_local);
|
|
torture_suite_add_1smb2_test(
|
|
suite, "locking conflict", test_fruit_locking_conflict);
|
|
|
|
return suite;
|
|
}
|
|
|
|
struct torture_suite *torture_vfs_fruit_file_id(TALLOC_CTX *ctx)
|
|
{
|
|
struct torture_suite *suite =
|
|
torture_suite_create(ctx, "fruit_file_id");
|
|
|
|
suite->description =
|
|
talloc_strdup(suite, "vfs_fruit tests for on-disk file ID that "
|
|
"require fruit:zero_file_id=yes");
|
|
|
|
torture_suite_add_1smb2_test(suite, "zero file id if AAPL negotiated",
|
|
test_zero_file_id);
|
|
|
|
return suite;
|
|
}
|
|
|
|
static bool test_timemachine_volsize(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
{
|
|
TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
struct smb2_handle h = {{0}};
|
|
union smb_fsinfo fsinfo;
|
|
NTSTATUS status;
|
|
bool ok = true;
|
|
const char *info_plist =
|
|
"<dict>\n"
|
|
" <key>band-size</key>\n"
|
|
" <integer>8192</integer>\n"
|
|
"</dict>\n";
|
|
|
|
smb2_deltree(tree, "test.sparsebundle");
|
|
|
|
ok = enable_aapl(tctx, tree);
|
|
torture_assert_goto(tctx, ok, ok, done, "enable_aapl failed");
|
|
|
|
status = smb2_util_mkdir(tree, "test.sparsebundle");
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
|
|
"smb2_util_mkdir\n");
|
|
|
|
ok = write_stream(tree, __location__, tctx, mem_ctx,
|
|
"test.sparsebundle/Info.plist", NULL,
|
|
0, strlen(info_plist), info_plist);
|
|
torture_assert_goto(tctx, ok, ok, done, "write_stream failed\n");
|
|
|
|
status = smb2_util_mkdir(tree, "test.sparsebundle/bands");
|
|
torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
|
|
"smb2_util_mkdir\n");
|
|
|
|
ok = torture_setup_file(tctx, tree, "test.sparsebundle/bands/1", false);
|
|
torture_assert_goto(tctx, ok, ok, done, "torture_setup_file failed\n");
|
|
|
|
ok = torture_setup_file(tctx, tree, "test.sparsebundle/bands/2", false);
|
|
torture_assert_goto(tctx, ok, ok, done, "torture_setup_file failed\n");
|
|
|
|
status = smb2_util_roothandle(tree, &h);
|
|
torture_assert_ntstatus_ok(tctx, status, "Unable to create root handle");
|
|
|
|
ZERO_STRUCT(fsinfo);
|
|
fsinfo.generic.level = RAW_QFS_SIZE_INFORMATION;
|
|
fsinfo.generic.handle = h;
|
|
|
|
status = smb2_getinfo_fs(tree, tree, &fsinfo);
|
|
torture_assert_ntstatus_ok(tctx, status, "smb2_getinfo_fs failed");
|
|
|
|
torture_comment(tctx, "sectors_per_unit: %" PRIu32"\n"
|
|
"bytes_per_sector: %" PRIu32"\n"
|
|
"total_alloc_units: %" PRIu64"\n"
|
|
"avail_alloc_units: %" PRIu64"\n",
|
|
fsinfo.size_info.out.sectors_per_unit,
|
|
fsinfo.size_info.out.bytes_per_sector,
|
|
fsinfo.size_info.out.total_alloc_units,
|
|
fsinfo.size_info.out.avail_alloc_units);
|
|
|
|
/*
|
|
* Let me explain the numbers:
|
|
*
|
|
* - the share is set to "fruit:time machine max size = 32K"
|
|
* - we've faked a bandsize of 8 K in the Info.plist file
|
|
* - we've created two bands files
|
|
* - one allocation unit is made of two sectors with 512 B each
|
|
* => we've consumed 16 allocation units, there should be 16 free
|
|
*/
|
|
|
|
torture_assert_goto(tctx, fsinfo.size_info.out.sectors_per_unit == 2,
|
|
ok, done, "Bad sectors_per_unit");
|
|
|
|
torture_assert_goto(tctx, fsinfo.size_info.out.bytes_per_sector == 512,
|
|
ok, done, "Bad bytes_per_sector");
|
|
|
|
torture_assert_goto(tctx, fsinfo.size_info.out.total_alloc_units == 32,
|
|
ok, done, "Bad total_alloc_units");
|
|
|
|
torture_assert_goto(tctx, fsinfo.size_info.out.avail_alloc_units == 16,
|
|
ok, done, "Bad avail_alloc_units");
|
|
|
|
done:
|
|
if (!smb2_util_handle_empty(h)) {
|
|
smb2_util_close(tree, h);
|
|
}
|
|
smb2_deltree(tree, "test.sparsebundle");
|
|
talloc_free(mem_ctx);
|
|
return ok;
|
|
}
|
|
|
|
struct torture_suite *torture_vfs_fruit_timemachine(TALLOC_CTX *ctx)
|
|
{
|
|
struct torture_suite *suite = torture_suite_create(
|
|
ctx, "fruit_timemachine");
|
|
|
|
suite->description = talloc_strdup(
|
|
suite, "vfs_fruit tests for TimeMachine");
|
|
|
|
torture_suite_add_1smb2_test(suite, "Timemachine-volsize",
|
|
test_timemachine_volsize);
|
|
|
|
return suite;
|
|
}
|