libndr: Avoid assigning duplicate versions to symbols
[amitay/samba.git] / source4 / torture / vfs / fruit.c
index a9ae891a23f4af52013cf5fac702b4baa7033dde..a094ce0ab6515eba6fda46d5810626f793d5b30b 100644 (file)
@@ -886,517 +886,1955 @@ static char osx_adouble_w_xattr[] = {
        0x00, 0x00, 0x00, 0x1c, 0x00, 0x1e, 0xff, 0xff
 };
 
+/*
+ * 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   : 00000002 : Resource Fork
+ * Offset     : 00000052 : 82
+ * 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   : F0 F1 F2 F3 F5 F5 F6 F7 F8 F9 FA FB FC FD FE FF : ................
+ * 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       : ..............
+ *
+ * Entry ID   : 00000009 : Finder Info
+ * Offset     : 00000032 : 50
+ * Length     : 00000020 : 32
+ *
+ * -NOTE------: cannot detect whether FInfo or DInfo. assume FInfo.
+ *
+ * -FInfo-----:
+ * Type       : 57415645 : WAVE
+ * Creator    : 5054756C : PTul
+ * 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      : 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   : 57 41 56 45 50 54 75 6C 00 00 00 00 00 00 00 00 : WAVEPTul........
+ * 00000010   : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
+ *  *
+ * It was created with:
+ * $ hexdump -ve '"\t" 7/1 "0x%02x, " 1/1 " 0x%02x," "\n"'
+ */
+static char osx_adouble_without_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, 0x02, 0x00, 0x00,
+       0x00, 0x52, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00,
+       0x00, 0x09, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
+       0x00, 0x20, 0x57, 0x41, 0x56, 0x45, 0x50, 0x54,
+       0x75, 0x6c, 0x00, 0x00, 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, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
+       0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd,
+       0xfe, 0xff, 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
+};
+
+/*
+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   : F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF : 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_non_empty_rfork_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, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
+       0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd,
+       0xfe, 0xff, 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)) {
+               if (value == NULL) {
+                       TALLOC_FREE(full_name);
+                       return true;
+               }
+               torture_comment(tctx, "Unable to open stream %s\n", full_name);
+               TALLOC_FREE(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;
+}
+
 /**
- * talloc and intialize an AfpInfo
+ * Create a file or directory
  **/
-static AfpInfo *torture_afpinfo_new(TALLOC_CTX *mem_ctx)
+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!";
 
-       info = talloc_zero(mem_ctx, AfpInfo);
-       if (info == NULL) {
-               return NULL;
-       }
+       torture_comment(tctx, "Checking metadata access\n");
 
-       info->afpi_Signature = AFP_Signature;
-       info->afpi_Version = AFP_Version;
-       info->afpi_BackupTime = AFP_BackupTime;
+       smb2_util_unlink(tree, fname);
 
-       return info;
-}
+       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);
 
-/**
- * Pack AfpInfo into a talloced buffer
- **/
-static char *torture_afpinfo_pack(TALLOC_CTX *mem_ctx,
-                                 AfpInfo *info)
-{
-       char *buf;
+       ret = torture_setup_file(mem_ctx, tree, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed");
 
-       buf = talloc_zero_array(mem_ctx, char, AFP_INFO_SIZE);
-       if (buf == NULL) {
-               return NULL;
-       }
+       info = torture_afpinfo_new(mem_ctx);
+       torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed");
 
-       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));
+       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");
 
-       return buf;
-}
+       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");
 
-/**
- * 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));
+       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;
 }
-#endif
 
-static bool torture_write_afpinfo(struct smb2_tree *tree,
-                                 struct torture_context *tctx,
-                                 TALLOC_CTX *mem_ctx,
-                                 const char *fname,
-                                 AfpInfo *info)
+static bool test_write_atalk_metadata(struct torture_context *tctx,
+                                     struct smb2_tree *tree)
 {
-       struct smb2_handle handle;
-       struct smb2_create io;
+       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       const char *fname = BASEDIR "\\torture_write_metadata";
+       const char *type_creator = "SMB,OLE!";
        NTSTATUS status;
-       const char *full_name;
-       char *infobuf;
+       struct smb2_handle testdirh;
        bool ret = true;
+       AfpInfo *info;
 
-       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);
+       smb2_deltree(tree, BASEDIR);
+       status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
        CHECK_STATUS(status, NT_STATUS_OK);
+       smb2_util_close(tree, testdirh);
 
-       handle = io.out.file.handle;
-
-       infobuf = torture_afpinfo_pack(mem_ctx, info);
-       if (infobuf == NULL) {
-               return false;
+       ret = torture_setup_file(mem_ctx, tree, fname, false);
+       if (ret == false) {
+               goto done;
        }
 
-       status = smb2_util_write(tree, handle, infobuf, 0, AFP_INFO_SIZE);
-       CHECK_STATUS(status, NT_STATUS_OK);
+       info = torture_afpinfo_new(mem_ctx);
+       if (info == NULL) {
+               goto done;
+       }
 
-       smb2_util_close(tree, handle);
+       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;
 }
 
-/**
- * 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)
+static bool test_write_atalk_rfork_io(struct torture_context *tctx,
+                                     struct smb2_tree *tree)
 {
-       struct smb2_handle handle;
-       struct smb2_create create;
-       struct smb2_read r;
+       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;
-       char *full_name;
+       struct smb2_handle testdirh;
        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;
+       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);
+
+       /* 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 != 64*1024*1024 + 10) {
+               torture_result(tctx, TORTURE_FAIL,
+                              "(%s) Incorrect resource fork size\n",
+                              __location__);
+               ret = false;
+               smb2_util_close(tree, filehandle);
+               goto done;
        }
-       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;
+       smb2_util_close(tree, filehandle);
 
-       torture_comment(tctx, "Open stream %s\n", full_name);
+       ret &= check_stream(tree, __location__, tctx, mem_ctx,
+                           fname, AFPRESOURCE_STREAM_NAME,
+                           (off_t)64*1024*1024, 10, 0, 10, rfork_content);
 
-       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;
-       }
+       /* Truncate back to size of 1 byte */
 
-       handle = create.out.file.handle;
-       if (value == NULL) {
-               TALLOC_FREE(full_name);
-               smb2_util_close(tree, handle);
-               return true;
-       }
+       torture_comment(tctx, "(%s) truncate resource fork and check size\n",
+                       __location__);
 
-       ZERO_STRUCT(r);
-       r.in.file.handle = handle;
-       r.in.length      = read_count;
-       r.in.offset      = read_offset;
+       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;
 
-       status = smb2_read(tree, tree, &r);
+       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);
 
-       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));
+       smb2_util_close(tree, filehandle);
 
-       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));
+       /* 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;
 
-       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));
+       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:
-       TALLOC_FREE(full_name);
-       smb2_util_close(tree, handle);
+       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, BASEDIR);
+       talloc_free(mem_ctx);
        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)
+static bool test_rfork_truncate(struct torture_context *tctx,
+                               struct smb2_tree *tree)
 {
-       struct smb2_handle handle;
-       struct smb2_create create;
-       struct smb2_read r;
+       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;
-       const char *full_name;
+       struct smb2_handle testdirh;
        bool ret = true;
+       struct smb2_create create;
+       struct smb2_handle fh1, fh2, fh3;
+       union smb_setfileinfo sinfo;
 
-       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;
+       ret = enable_aapl(tctx, tree);
+       torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
 
-       torture_comment(tctx, "Open stream %s\n", full_name);
+       smb2_util_unlink(tree, fname);
 
-       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;
+       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;
        }
 
-       handle = create.out.file.handle;
+       ret &= write_stream(tree, __location__, tctx, mem_ctx,
+                           fname, AFPRESOURCE_STREAM,
+                           10, 10, rfork_content);
 
-       ZERO_STRUCT(r);
-       r.in.file.handle = handle;
-       r.in.length      = read_count;
-       r.in.offset      = read_offset;
+       /* Truncate back to size 0, further access MUST return ENOENT */
 
-       status = smb2_read(tree, tree, &r);
-       if (!NT_STATUS_IS_OK(status)) {
-               CHECK_STATUS(status, NT_STATUS_END_OF_FILE);
-       }
+       torture_comment(tctx, "(%s) truncate resource fork to size 0\n",
+                       __location__);
 
-       smb2_util_close(tree, handle);
+       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;
 
-done:
-       if (ret == false) {
-               return -1;
-       }
-       return r.out.data.length;
-}
+       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;
 
-/**
- * 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;
+       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");
 
-       full_name = talloc_asprintf(mem_ctx, "%s%s", fname, sname ? sname : "");
-       if (full_name == NULL) {
-           torture_comment(tctx, "talloc_asprintf error\n");
-           return false;
-       }
+       /*
+        * Now check size, we should get OBJECT_NAME_NOT_FOUND (!)
+        */
        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;
+       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);
-       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;
-               }
-       }
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create");
+       fh3 = create.out.file.handle;
 
-       handle = create.out.file.handle;
-       if (value == NULL) {
-               return true;
-       }
+       status = smb2_util_write(tree, fh3, "foo", 0, 3);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_write");
 
-       status = smb2_util_write(tree, handle, value, offset, size);
+       smb2_util_close(tree, fh3);
+       smb2_util_close(tree, fh2);
+       smb2_util_close(tree, fh1);
 
-       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;
-       }
+       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");
 
-       smb2_util_close(tree, handle);
-       return true;
+done:
+       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, BASEDIR);
+       talloc_free(mem_ctx);
+       return ret;
 }
 
-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)
+static bool test_rfork_create(struct torture_context *tctx,
+                             struct smb2_tree *tree)
 {
-       int ret = true;
-       int result;
-       const char *spath;
-       char *path;
+       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;
 
-       spath = torture_setting_string(tctx, path_option, NULL);
-       if (spath == NULL) {
-               printf("No sharepath for option %s\n", path_option);
-               return false;
-       }
+       ret = enable_aapl(tctx, tree);
+       torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
 
-       path = talloc_asprintf(tctx, "%s/%s", spath, name);
+       smb2_util_unlink(tree, fname);
 
-       result = setxattr(path, xattr, metadata, size, 0);
-       if (result != 0) {
-               ret = false;
+       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;
        }
 
-       TALLOC_FREE(path);
+       torture_comment(tctx, "(%s) open rfork, should return ENOENT\n",
+                       __location__);
 
-       return ret;
-}
+       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");
 
-/**
- * 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;
+       torture_comment(tctx, "(%s) create resource fork\n", __location__);
 
-       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|
+       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;
-       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, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create");
+       fh1 = create.out.file.handle;
 
-       status = smb2_create(tree, mem_ctx, &io);
-       if (!NT_STATUS_IS_OK(status)) {
-               return false;
-       }
+       torture_comment(tctx, "(%s) getinfo on create handle\n",
+                       __location__);
 
-       status = smb2_util_close(tree, io.out.file.handle);
-       if (!NT_STATUS_IS_OK(status)) {
-               return false;
+       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;
        }
 
-       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));
+       torture_comment(tctx, "(%s) open rfork, should still return ENOENT\n",
+                       __location__);
 
-       status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_blob_add");
+       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");
 
-       status = smb2_create(tree, tctx, &io);
-       torture_assert_ntstatus_ok_goto(tctx, status, 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");
 
-       status = smb2_util_close(tree, io.out.file.handle);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close");
+       torture_comment(tctx, "(%s) close empty created rfork, open should return ENOENT\n",
+                       __location__);
 
-       /*
-        * Now check returned AAPL context
-        */
-       torture_comment(tctx, "Comparing returned AAPL capabilities\n");
+       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");
 
-       aapl = smb2_create_blob_find(&io.out.blobs,
-                                    SMB2_CREATE_TAG_AAPL);
-       torture_assert_goto(tctx, aapl != NULL, ret, done, "missing AAPL context");
+done:
+       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, BASEDIR);
+       talloc_free(mem_ctx);
+       return ret;
+}
 
-       if (!is_osx_server) {
-               size_t expected_aapl_ctx_size;
+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;
 
-               expected_aapl_ctx_size = strlen("MacSamba") * 2 + 40;
+       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);
 
-               torture_assert_goto(
-                       tctx, aapl->data.length == expected_aapl_ctx_size,
-                       ret, done, "bad AAPL size");
+       ret = torture_setup_file(mem_ctx, tree, fname, false);
+       if (ret == false) {
+               goto done;
        }
 
-       aapl_server_caps = BVAL(aapl->data.data, 16);
-       torture_assert_goto(tctx, aapl_server_caps == expected_scaps,
-                           ret, done, "bad AAPL caps");
+       torture_comment(tctx, "(%s) Try opening read-only with "
+                       "open_if create disposition, should work\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_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_read_netatalk_metadata(struct torture_context *tctx,
-                                       struct smb2_tree *tree)
+static bool test_adouble_conversion(struct torture_context *tctx,
+                                   struct smb2_tree *tree)
 {
        TALLOC_CTX *mem_ctx = talloc_new(tctx);
-       const char *fname = BASEDIR "\\torture_read_metadata";
+       const char *fname = BASEDIR "\\test_adouble_conversion";
+       const char *adname = BASEDIR "/._test_adouble_conversion";
        NTSTATUS status;
        struct smb2_handle testdirh;
        bool ret = true;
-       ssize_t len;
-       const char *localdir = NULL;
-
-       torture_comment(tctx, "Checking metadata access\n");
+       const char data[] = {
+               0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+               0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+       };
+       size_t datalen = sizeof(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" */
+       };
+       bool is_osx = torture_setting_bool(tctx, "osx", false);
 
-       localdir = torture_setting_string(tctx, "localdir", NULL);
-       if (localdir == NULL) {
-               torture_skip(tctx, "Need localdir for test");
+       if (is_osx) {
+               torture_skip(tctx, "Test only works with Samba\n");
        }
 
-       smb2_util_unlink(tree, fname);
+       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;
-       }
-
-       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 = torture_setup_file(tctx, tree, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_setup_file failed\n");
 
-       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 = torture_setup_file(tctx, tree, adname, false);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_setup_file failed\n");
 
-       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");
+       ret = write_stream(tree, __location__, tctx, mem_ctx,
+                          adname, NULL,
+                          0,
+                          sizeof(osx_adouble_non_empty_rfork_w_xattr),
+                          osx_adouble_non_empty_rfork_w_xattr);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "write_stream failed\n");
 
-       /* Check reading offset and read size > sizeof(AFPINFO_STREAM) */
+       torture_comment(tctx, "(%s) test OS X AppleDouble conversion\n",
+           __location__);
 
-       len = read_stream(tree, __location__, tctx, mem_ctx, fname,
-                         AFPINFO_STREAM, 0, 61);
-       CHECK_VALUE(len, 60);
+       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");
 
-       len = read_stream(tree, __location__, tctx, mem_ctx, fname,
-                         AFPINFO_STREAM, 59, 2);
-       CHECK_VALUE(len, 2);
+       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");
 
-       len = read_stream(tree, __location__, tctx, mem_ctx, fname,
-                         AFPINFO_STREAM, 60, 1);
-       CHECK_VALUE(len, 1);
+       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");
 
-       len = read_stream(tree, __location__, tctx, mem_ctx, fname,
-                         AFPINFO_STREAM, 61, 1);
-       CHECK_VALUE(len, 0);
+       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);
@@ -1404,365 +2842,405 @@ done:
        return ret;
 }
 
-static bool test_read_afpinfo(struct torture_context *tctx,
-                             struct smb2_tree *tree)
+/*
+ * Test conversion of AppleDouble file without embedded xattr data
+ */
+static bool test_adouble_conversion_wo_xattr(struct torture_context *tctx,
+                                            struct smb2_tree *tree)
 {
        TALLOC_CTX *mem_ctx = talloc_new(tctx);
-       const char *fname = BASEDIR "\\torture_read_metadata";
+       const char *fname = BASEDIR "\\test_adouble_conversion";
+       const char *adname = BASEDIR "/._test_adouble_conversion";
        NTSTATUS status;
        struct smb2_handle testdirh;
        bool ret = true;
-       ssize_t len;
-       AfpInfo *info;
-       const char *type_creator = "SMB,OLE!";
+       const char *streams[] = {
+               "::$DATA",
+               AFPINFO_STREAM,
+               AFPRESOURCE_STREAM
+       };
+       struct smb2_create create;
+       struct smb2_find find;
+       unsigned int count;
+       union smb_search_data *d;
+       const char data[] = {
+               0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+               0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+       };
+       size_t datalen = sizeof(data);
+       bool is_osx = torture_setting_bool(tctx, "osx", false);
 
-       torture_comment(tctx, "Checking metadata access\n");
+       if (is_osx) {
+               torture_skip(tctx, "Test only works with Samba\n");
+       }
 
-       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, BASEDIR);
 
        status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir failed");
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testdir failed\n");
        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");
+       ret = torture_setup_file(tctx, tree, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_setup_file failed\n");
 
-       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 = torture_setup_file(tctx, tree, adname, false);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_setup_file failed\n");
 
-       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 = write_stream(tree, __location__, tctx, mem_ctx,
+                          adname, NULL, 0,
+                          sizeof(osx_adouble_without_xattr),
+                          osx_adouble_without_xattr);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "write_stream failed\n");
 
-       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");
+       ret = enable_aapl(tctx, tree);
+       torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
 
        /*
-        * OS X ignores offset <= 60 and treats the as
-        * offset=0. Reading from offsets > 60 returns EOF=0.
+        * Issue a smb2_find(), this triggers the server-side conversion
         */
 
-       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");
+       create = (struct smb2_create) {
+               .in.desired_access = SEC_RIGHTS_DIR_READ,
+               .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+               .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+               .in.share_access = NTCREATEX_SHARE_ACCESS_READ,
+               .in.create_disposition = NTCREATEX_DISP_OPEN,
+               .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
+               .in.fname = BASEDIR,
+       };
 
-       len = read_stream(tree, __location__, tctx, mem_ctx, fname,
-                         AFPINFO_STREAM, 59, 2);
-       torture_assert_goto(tctx, len == 2, ret, done, "read_stream failed");
+       status = smb2_create(tree, tctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
 
-       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");
+       find = (struct smb2_find) {
+               .in.file.handle = create.out.file.handle,
+               .in.pattern = "*",
+               .in.max_response_size = 0x1000,
+               .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+       };
 
-       len = read_stream(tree, __location__, tctx, mem_ctx, fname,
-                         AFPINFO_STREAM, 60, 1);
-       torture_assert_goto(tctx, len == 1, ret, done, "read_stream failed");
+       status = smb2_find_level(tree, tree, &find, &count, &d);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_find_level failed\n");
 
-       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");
+       status = smb2_util_close(tree, create.out.file.handle);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_close 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");
+       /*
+        * Check number of streams
+        */
 
-done:
-       smb2_util_unlink(tree, fname);
-       smb2_deltree(tree, BASEDIR);
-       talloc_free(mem_ctx);
-       return ret;
-}
+       ret = check_stream_list(tree, tctx, fname, 3, streams, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list");
 
-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);
+       /*
+        * Check Resourcefork data can be read.
+        */
 
-       ret = torture_setup_file(mem_ctx, tree, fname, false);
-       if (ret == false) {
-               goto done;
-       }
+       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");
 
-       info = torture_afpinfo_new(mem_ctx);
-       if (info == NULL) {
-               goto done;
-       }
+       /*
+        * Check FinderInfo data has been migrated to stream.
+        */
 
-       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);
+       ret = check_stream(tree, __location__, tctx, mem_ctx,
+                          fname, AFPINFO_STREAM,
+                          0, 60, 16, 8, "WAVEPTul");
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "check AFPINFO_STREAM failed\n");
 
 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)
+static bool test_aapl(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";
+       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;
+       uint32_t expected_vol_caps = 0;
+       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);
 
-       union smb_open io;
-       struct smb2_handle filehandle;
-       union smb_fileinfo finfo;
-       union smb_setfileinfo sinfo;
-
-       smb2_util_unlink(tree, fname);
+       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;
-       }
-
-       torture_comment(tctx, "(%s) writing to resource fork\n",
-           __location__);
+       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;
 
-       ret &= write_stream(tree, __location__, tctx, mem_ctx,
-                           fname, AFPRESOURCE_STREAM_NAME,
-                           10, 10, rfork_content);
+       /*
+        * Issuing an SMB2/CREATE with a suitably formed AAPL context,
+        * controls behaviour of Apple's SMB2 extensions for the whole
+        * session!
+        */
 
-       ret &= check_stream(tree, __location__, tctx, mem_ctx,
-                           fname, AFPRESOURCE_STREAM_NAME,
-                           0, 20, 10, 10, rfork_content);
+       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));
 
-       /* Check size after write */
+       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);
 
-       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));
+       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);
-       filehandle = io.smb2.out.file.handle;
 
-       torture_comment(tctx, "(%s) check resource fork size after write\n",
-           __location__);
+       /*
+        * Now check returned AAPL context
+        */
+       torture_comment(tctx, "Comparing returned AAPL capabilities\n");
 
-       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) {
+       aapl = smb2_create_blob_find(&io.out.blobs,
+                                    SMB2_CREATE_TAG_AAPL);
+
+       if (aapl == NULL) {
                torture_result(tctx, TORTURE_FAIL,
-                              "(%s) Incorrect resource fork size\n",
+                              "(%s) unexpectedly no AAPL capabilities were returned.",
                               __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__);
+       if (!is_osx_server) {
+               size_t expected_aapl_ctx_size;
+               bool size_ok;
 
-       ret &= write_stream(tree, __location__, tctx, mem_ctx,
-                           fname, AFPRESOURCE_STREAM_NAME,
-                           (off_t)64*1024*1024, 10, rfork_content);
+               /*
+                * 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;
 
-       ret &= check_stream(tree, __location__, tctx, mem_ctx,
-                           fname, AFPRESOURCE_STREAM_NAME,
-                           (off_t)64*1024*1024, 10, 0, 10, rfork_content);
+               size_ok = aapl->data.length == expected_aapl_ctx_size;
+               torture_assert_goto(tctx, size_ok, ret, done, "bad AAPL size");
+       }
 
-       /* Truncate back to size of 1 byte */
+       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;
+       }
 
-       torture_comment(tctx, "(%s) truncate resource fork and check size\n",
-                       __location__);
+       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;
+       }
 
-       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;
+       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;
+       }
 
-       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);
+       if (is_osx_server) {
+               expected_vol_caps = 5;
+       }
+       aapl_vol_caps = BVAL(aapl->data.data, 24);
+       if (aapl_vol_caps != expected_vol_caps) {
+               /* this will fail on a case insensitive fs ... */
+               torture_result(tctx, TORTURE_FAIL,
+                               "(%s) unexpected vol_caps: %d",
+                               __location__, (int)aapl_vol_caps);
+       }
 
-       smb2_util_close(tree, filehandle);
+       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 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;
+       /*
+        * 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;
+       }
 
-       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) {
+       info = torture_afpinfo_new(mem_ctx);
+       if (info == NULL) {
                torture_result(tctx, TORTURE_FAIL,
-                              "(%s) Incorrect resource fork size\n",
+                              "(%s) torture_afpinfo_new() failed",
                               __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);
+       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 = torture_setup_file(mem_ctx, tree, fname, false);
+       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;
        }
 
-       ret &= write_stream(tree, __location__, tctx, mem_ctx,
-                           fname, AFPRESOURCE_STREAM,
-                           10, 10, rfork_content);
+       /*
+        * Ok, file is prepared, now call smb2/find
+        */
 
-       /* Truncate back to size 0, further access MUST return ENOENT */
+       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);
 
-       torture_comment(tctx, "(%s) truncate resource fork to size 0\n",
-                       __location__);
+       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;
 
-       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;
+       status = smb2_find_level(tree, tree, &f, &count, &d);
+       CHECK_STATUS(status, NT_STATUS_OK);
 
-       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;
+       status = smb2_util_close(tree, io.out.file.handle);
+       CHECK_STATUS(status, NT_STATUS_OK);
 
-       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");
+       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;
+       }
 
-       /*
-        * 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");
+       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);
 
        /*
-        * 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.
+        * 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
         */
-       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");
+       torture_comment(tctx, "mac_access: %" PRIx32 "\n",
+                       d[0].id_both_directory_info.ea_size);
 
-       smb2_util_close(tree, fh3);
-       smb2_util_close(tree, fh2);
-       smb2_util_close(tree, fh1);
+       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;
+       }
 
-       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");
+       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);
@@ -1771,286 +3249,239 @@ done:
        return ret;
 }
 
-static bool test_rfork_create(struct torture_context *tctx,
-                             struct smb2_tree *tree)
+static uint64_t patt_hash(uint64_t off)
 {
-       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);
+       return off;
+}
 
-       status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir");
-       smb2_util_close(tree, testdirh);
+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);
 
-       ret = torture_setup_file(mem_ctx, tree, fname, false);
-       if (ret == false) {
-               goto done;
+       if (len == 0) {
+               return true;
        }
 
-       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_assert(torture, (len % 8) == 0, "invalid write len");
 
-       torture_comment(tctx, "(%s) create resource fork\n", __location__);
+       buf = talloc_zero_size(mem_ctx, io_sz);
+       torture_assert(torture, (buf != NULL), "no memory for file data buf");
 
-       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;
+       while (len > 0) {
+               for (i = 0; i <= io_sz - 8; i += 8) {
+                       SBVAL(buf, i, patt_hash(patt_off));
+                       patt_off += 8;
+               }
 
-       torture_comment(tctx, "(%s) getinfo on create handle\n",
-                       __location__);
+               status = smb2_util_write(tree, h,
+                                        buf, off, io_sz);
+               torture_assert_ntstatus_ok(torture, status, "file write");
 
-       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;
+               len -= io_sz;
+               off += io_sz;
        }
 
-       torture_comment(tctx, "(%s) open rfork, should still return ENOENT\n",
-                       __location__);
+       talloc_free(buf);
 
-       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");
+       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;
+       }
 
-       ret = check_stream_list(tree, tctx, fname, 1, streams, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list");
+       torture_assert(torture, (len % 8) == 0, "invalid read len");
 
-       torture_comment(tctx, "(%s) close empty created rfork, open should return ENOENT\n",
-                       __location__);
+       while (len > 0) {
+               uint64_t i;
+               struct smb2_read r;
+               NTSTATUS status;
+               uint64_t io_sz = MIN(1024 * 64, len);
 
-       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");
+               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");
 
-done:
-       smb2_util_unlink(tree, fname);
-       smb2_deltree(tree, BASEDIR);
-       talloc_free(mem_ctx);
-       return ret;
+               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_rfork_create_ro(struct torture_context *tctx,
-                                struct smb2_tree *tree)
+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)
 {
-       TALLOC_CTX *mem_ctx = talloc_new(tctx);
-       const char *fname = BASEDIR "\\torture_rfork_create";
-       const char *rfork = BASEDIR "\\torture_rfork_create" AFPRESOURCE_STREAM;
+       struct smb2_create io;
        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;
+       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;
 
-       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__);
+       status = smb2_create(tree, mem_ctx, &io);
+       torture_assert_ntstatus_ok(torture, status, "file create");
 
-       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");
+       *fh = io.out.file.handle;
 
-       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");
+       return true;
+}
 
-       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");
+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;
 
-       smb2_util_close(tree, create.out.file.handle);
+       ok = test_setup_open(torture, tree, mem_ctx,
+                            fname,
+                            fh,
+                            desired_access,
+                            file_attributes);
+       torture_assert(torture, ok, "file open");
 
-done:
-       smb2_util_unlink(tree, fname);
-       smb2_deltree(tree, BASEDIR);
-       talloc_free(mem_ctx);
-       return ret;
+       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_adouble_conversion(struct torture_context *tctx,
-                                   struct smb2_tree *tree)
+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)
 {
-       TALLOC_CTX *mem_ctx = talloc_new(tctx);
-       const char *fname = BASEDIR "\\test_adouble_conversion";
-       const char *adname = BASEDIR "/._test_adouble_conversion";
+       struct req_resume_key_rsp res_key;
+       bool ok;
        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" */
-       };
+       enum ndr_err_code ndr_ret;
 
-       smb2_deltree(tree, BASEDIR);
+       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");
 
-       status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
-       CHECK_STATUS(status, NT_STATUS_OK);
-       smb2_util_close(tree, testdirh);
+       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");
 
-       ret = torture_setup_file(tctx, tree, fname, false);
-       torture_assert_goto(tctx, ret == true, ret, done,
-                           "torture_setup_file failed\n");
+       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_output_response = 32;
+       io->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
 
-       ret = torture_setup_file(tctx, tree, adname, false);
-       torture_assert_goto(tctx, ret == true, ret, done,
-                           "torture_setup_file failed\n");
+       status = smb2_ioctl(tree, mem_ctx, &io->smb2);
+       torture_assert_ntstatus_ok(torture, status,
+                                  "FSCTL_SRV_REQUEST_RESUME_KEY");
 
-       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");
+       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_comment(tctx, "(%s) test OS X AppleDouble conversion\n",
-           __location__);
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_pull_req_resume_key_rsp");
 
-       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");
+       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_output_response = sizeof(struct srv_copychunk_rsp);
+       io->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
 
-       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");
+       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");
 
-       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");
+       return true;
+}
 
-       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 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 test_aapl(struct torture_context *tctx,
-                     struct smb2_tree *tree)
+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 = BASEDIR "\\test_aapl";
+       const char *fname = "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);
+       bool ret = true;
 
        ZERO_STRUCT(io);
        io.in.desired_access     = SEC_FLAG_MAXIMUM_ALLOWED;
@@ -2061,68 +3492,27 @@ static bool test_aapl(struct torture_context *tctx,
                              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));
+       SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS));
+       SBVAL(data.data, 16, flags);
 
-       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");
+       }
+       if (aapl->data.length < 24) {
+               ret = false;
+               goto done;
        }
 
        aapl_cmd = IVAL(aapl->data.data, 0);
@@ -2135,9 +3525,7 @@ static bool test_aapl(struct torture_context *tctx,
        }
 
        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)) {
+       if (!(aapl_reply_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS)) {
                torture_result(tctx, TORTURE_FAIL,
                               "(%s) unexpected reply_bitmap: %d",
                               __location__, (int)aapl_reply_bitmap);
@@ -2146,10 +3534,7 @@ static bool test_aapl(struct torture_context *tctx,
        }
 
        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)) {
+       if (!(aapl_server_caps & flags)) {
                torture_result(tctx, TORTURE_FAIL,
                               "(%s) unexpected server_caps: %d",
                               __location__, (int)aapl_server_caps);
@@ -2157,882 +3542,873 @@ static bool test_aapl(struct torture_context *tctx,
                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;
-       }
+done:
+       status = smb2_util_close(tree, io.out.file.handle);
+       CHECK_STATUS(status, NT_STATUS_OK);
 
-       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;
-       }
+       smb2_util_unlink(tree, "aapl");
+       talloc_free(mem_ctx);
+       return ret;
+}
 
-       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;
-       }
+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";
 
        /*
-        * Ok, file is prepared, now call smb2/find
+        * 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.
         */
 
-       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;
+       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");
        }
 
-       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;
-       }
+       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");
 
-       torture_comment(tctx, "short_name buffer:\n");
-       dump_data(0, d[0].id_both_directory_info.short_name_buf, 24);
+       status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+       torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK");
 
-       /*
-        * 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);
+       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");
 
-       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;
+       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");
        }
 
-       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;
+       /*
+        * 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;
        }
 
-done:
-       smb2_util_unlink(tree, fname);
-       smb2_deltree(tree, BASEDIR);
-       talloc_free(mem_ctx);
-       return ret;
-}
+       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);
 
-static uint64_t patt_hash(uint64_t off)
-{
-       return off;
-}
+       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");
+       }
 
-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);
+       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");
 
-       if (len == 0) {
-               return true;
+       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");
        }
 
-       torture_assert(torture, (len % 8) == 0, "invalid write len");
+       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");
 
-       buf = talloc_zero_size(mem_ctx, io_sz);
-       torture_assert(torture, (buf != NULL), "no memory for file data buf");
+       status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+       torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK");
 
-       while (len > 0) {
-               for (i = 0; i <= io_sz - 8; i += 8) {
-                       SBVAL(buf, i, patt_hash(patt_off));
-                       patt_off += 8;
-               }
+       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");
 
-               status = smb2_util_write(tree, h,
-                                        buf, off, io_sz);
-               torture_assert_ntstatus_ok(torture, status, "file write");
+       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");
+       }
 
-               len -= io_sz;
-               off += io_sz;
+       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");
        }
 
-       talloc_free(buf);
+       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_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)
+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)
 {
-       if (len == 0) {
-               return true;
-       }
-
-       torture_assert(torture, (len % 8) == 0, "invalid read len");
+       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;
 
-       while (len > 0) {
-               uint64_t i;
-               struct smb2_read r;
-               NTSTATUS status;
-               uint64_t io_sz = MIN(1024 * 64, len);
+       ZERO_STRUCT(h);
+       torture_assert_goto(tctx, tmp_ctx != NULL, ret, done, "talloc_new failed");
 
-               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");
+       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;
+       create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+       status = smb2_create(tree, tmp_ctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create");
+       h = create.out.file.handle;
 
-               torture_assert_u64_equal(torture, r.out.data.length, io_sz,
-                                        "read data len mismatch");
+       finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
+       finfo.generic.in.file.handle = h;
 
-               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;
-       }
+       status = smb2_getinfo_file(tree, tctx, &finfo);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "get stream info");
 
-       return true;
-}
+       smb2_util_close(tree, h);
 
-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;
+       torture_assert_int_equal_goto(tctx, finfo.stream_info.out.num_streams, num_exp,
+                                     ret, done, "stream count");
 
-       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;
+       if (num_exp == 0) {
+               TALLOC_FREE(tmp_ctx);
+               goto done;
        }
-       io.in.fname = fname;
 
-       status = smb2_create(tree, mem_ctx, &io);
-       torture_assert_ntstatus_ok(torture, status, "file create");
-
-       *fh = io.out.file.handle;
+       exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
+       torture_assert_goto(tctx, exp_sort != NULL, ret, done, __location__);
 
-       return true;
-}
+       TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
 
-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;
+       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__);
 
-       ok = test_setup_open(torture, tree, mem_ctx,
-                            fname,
-                            fh,
-                            desired_access,
-                            file_attributes);
-       torture_assert(torture, ok, "file open");
+       TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream);
 
-       if (size > 0) {
-               ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
-               torture_assert(torture, ok, "write pattern");
+       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");
        }
-       return true;
+
+done:
+       TALLOC_FREE(tmp_ctx);
+       return ret;
 }
 
-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)
+static bool check_stream_list_handle(struct smb2_tree *tree,
+                                    struct torture_context *tctx,
+                                    struct smb2_handle h,
+                                    int num_exp,
+                                    const char **exp,
+                                    bool is_dir)
 {
-       struct req_resume_key_rsp res_key;
-       bool ok;
+       bool ret = true;
+       union smb_fileinfo finfo;
        NTSTATUS status;
-       enum ndr_err_code ndr_ret;
+       int i;
+       TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+       char **exp_sort;
+       struct stream_struct *stream_sort;
 
-       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");
+       torture_assert_goto(tctx, tmp_ctx != NULL, ret, done,
+                           "talloc_new failed\n");
 
-       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");
+       finfo = (union smb_fileinfo) {
+               .stream_info.level = RAW_FILEINFO_STREAM_INFORMATION,
+               .stream_info.in.file.handle = h,
+       };
 
-       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_getinfo_file(tree, tctx, &finfo);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "get stream info\n");
 
-       status = smb2_ioctl(tree, mem_ctx, &io->smb2);
-       torture_assert_ntstatus_ok(torture, status,
-                                  "FSCTL_SRV_REQUEST_RESUME_KEY");
+       torture_assert_int_equal_goto(tctx, finfo.stream_info.out.num_streams,
+                                     num_exp, ret, done, "stream count\n");
 
-       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);
+       if (num_exp == 0) {
+               TALLOC_FREE(tmp_ctx);
+               goto done;
+       }
 
-       torture_assert_ndr_success(torture, ndr_ret,
-                                  "ndr_pull_req_resume_key_rsp");
+       exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
+       torture_assert_goto(tctx, exp_sort != NULL, ret, done, __location__);
 
-       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;
+       TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
 
-       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");
+       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__);
 
-       return true;
-}
+       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\n");
+       }
 
-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;
+done:
+       TALLOC_FREE(tmp_ctx);
+       return ret;
 }
 
-static bool neg_aapl_copyfile(struct torture_context *tctx,
-                             struct smb2_tree *tree,
-                             uint64_t flags)
+/*
+  test stream names
+*/
+static bool test_stream_names(struct torture_context *tctx,
+                             struct smb2_tree *tree)
 {
        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;
+       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"
+       };
 
-       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;
+       sname1 = talloc_asprintf(mem_ctx, "%s%s", fname, streams[0]);
 
-       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);
+       /* clean slate ...*/
+       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, fname);
+       smb2_deltree(tree, BASEDIR);
 
-       status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data);
+       status = torture_smb2_testdir(tree, BASEDIR, &h);
        CHECK_STATUS(status, NT_STATUS_OK);
+       smb2_util_close(tree, h);
 
-       status = smb2_create(tree, tctx, &io);
-       CHECK_STATUS(status, NT_STATUS_OK);
+       ret = torture_setup_file(mem_ctx, tree, fname, false);
+       torture_assert_goto(tctx, ret, ret, done, "torture_setup_file");
 
-       aapl = smb2_create_blob_find(&io.out.blobs,
-                                    SMB2_CREATE_TAG_AAPL);
-       if (aapl == NULL) {
-               ret = false;
-               goto done;
+       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;
 
-       }
-       if (aapl->data.length < 24) {
-               ret = false;
-               goto done;
-       }
+       status = smb2_create(tree, mem_ctx, &create);
+       CHECK_STATUS(status, NT_STATUS_OK);
 
-       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;
-       }
+       status = smb2_util_write(tree, create.out.file.handle, "foo", 0, 3);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_write failed\n");
 
-       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;
-       }
+       smb2_util_close(tree, create.out.file.handle);
 
-       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;
-       }
+       ret = check_stream_list(tree, tctx, fname, 2, streams, false);
+       CHECK_VALUE(ret, true);
 
 done:
-       status = smb2_util_close(tree, io.out.file.handle);
-       CHECK_STATUS(status, NT_STATUS_OK);
-
-       smb2_util_unlink(tree, "aapl");
+       status = smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, BASEDIR);
        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";
+/* 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");
 
-       /*
-        * 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.
-        */
+               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);
 
-       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");
+               torture_assert_ntstatus_equal(torture, status,
+                                             NT_STATUS_ACCESS_DENIED,
+                                             "smb2_setinfo_file");
        }
 
-       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_util_close(tree, d1);
+       torture_assert_ntstatus_ok(torture, status, "smb2_util_close\n");
+       ZERO_STRUCT(d1);
 
-       status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
-       torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK");
+       torture_comment(torture, "Enabling AAPL\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(torture, ndr_ret,
-                                  "ndr_pull_srv_copychunk_rsp");
+       ret = enable_aapl(torture, tree);
+       torture_assert(torture, ret == true, "enable_aapl failed");
 
-       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");
-       }
+       torture_comment(torture, "Renaming directory with AAPL\n");
 
-       /*
-        * 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;
-       }
+       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;
 
-       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);
+       status = smb2_create(tree, torture, &(io.smb2));
+       torture_assert_ntstatus_ok(torture, status, "smb2_create dir");
+       d1 = io.smb2.out.file.handle;
 
-       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");
-       }
+       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;
 
-       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");
+       status = smb2_setinfo_file(tree, &sinfo);
+       torture_assert_ntstatus_ok(torture, status, "smb2_setinfo_file");
 
-       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");
+       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));
        }
 
-       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");
+       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;
+}
 
-       status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
-       torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK");
+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;
 
-       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");
+       torture_comment(tctx, "Opening file without AFP_AfpInfo\n");
 
-       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");
-       }
+       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");
 
-       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");
-       }
+       torture_comment(tctx, "Opening not existing AFP_AfpInfo\n");
 
-       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");
-       }
+       ZERO_STRUCT(create);
+       create.in.create_disposition = NTCREATEX_DISP_OPEN;
+       create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; /* stat open */
+       create.in.fname = sname;
 
-       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");
+       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_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;
+       smb2_util_unlink(tree, fname);
+       smb2_util_rmdir(tree, BASEDIR);
+       return ret;
 }
 
-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 bool test_create_delete_on_close(struct torture_context *tctx,
+                                       struct smb2_tree *tree)
 {
        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;
+       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");
 
-       ZERO_STRUCT(h);
-       torture_assert_goto(tctx, tmp_ctx != NULL, ret, done, "talloc_new failed");
+       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.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;
+       create.in.fname = sname;
 
-       finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
-       finfo.generic.in.file.handle = h;
+       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");
 
-       status = smb2_getinfo_file(tree, tctx, &finfo);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "get stream info");
+       ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
-       smb2_util_close(tree, h);
+       torture_comment(tctx, "Deleting AFP_AfpInfo via create with delete-on-close\n");
 
-       torture_assert_int_equal_goto(tctx, finfo.stream_info.out.num_streams, num_exp,
-                                     ret, done, "stream count");
+       info = torture_afpinfo_new(mem_ctx);
+       torture_assert_goto(tctx, info != NULL, ret, done, "torture_afpinfo_new failed");
 
-       if (num_exp == 0) {
-               TALLOC_FREE(tmp_ctx);
-               goto done;
-       }
+       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");
 
-       exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
-       torture_assert_goto(tctx, exp_sort != NULL, ret, done, __location__);
+       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");
 
-       TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
+       ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
-       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__);
+       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;
 
-       TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream);
+       status = smb2_create(tree, mem_ctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
 
-       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");
-       }
+       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:
-       TALLOC_FREE(tmp_ctx);
+       smb2_util_unlink(tree, fname);
+       smb2_util_rmdir(tree, BASEDIR);
        return ret;
 }
 
-/*
-  test stream names
-*/
-static bool test_stream_names(struct torture_context *tctx,
-                             struct smb2_tree *tree)
+static bool test_setinfo_delete_on_close(struct torture_context *tctx,
+                                        struct smb2_tree *tree)
 {
-       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       bool ret = true;
        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) */
+       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[] = {
-               ":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */
+               AFPINFO_STREAM,
+               "::$DATA"
+       };
+       const char *streams_basic[] = {
                "::$DATA"
        };
 
-       sname1 = talloc_asprintf(mem_ctx, "%s%s", fname, streams[0]);
+       torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
+
+       torture_comment(tctx, "Deleting AFP_AfpInfo via setinfo with delete-on-close\n");
 
-       /* clean slate ...*/
-       smb2_util_unlink(tree, fname);
-       smb2_deltree(tree, fname);
        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");
 
-       status = torture_smb2_testdir(tree, BASEDIR, &h);
-       CHECK_STATUS(status, NT_STATUS_OK);
-       smb2_util_close(tree, h);
+       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");
 
-       torture_comment(tctx, "(%s) testing stream names\n", __location__);
        ZERO_STRUCT(create);
-       create.in.desired_access = SEC_FILE_WRITE_DATA;
+       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.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;
+       create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
 
        status = smb2_create(tree, mem_ctx, &create);
-       CHECK_STATUS(status, NT_STATUS_OK);
-       smb2_util_close(tree, create.out.file.handle);
+       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");
 
        ret = check_stream_list(tree, tctx, fname, 2, streams, false);
-       CHECK_VALUE(ret, true);
+       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
-done:
-       status = smb2_util_unlink(tree, fname);
-       smb2_deltree(tree, BASEDIR);
-       talloc_free(mem_ctx);
+       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_DELETE_PENDING,
+                                          ret, done, "Got unexpected AFP_AfpInfo stream");
+
+       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;
 }
 
-/* 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)
+static bool test_setinfo_eof(struct torture_context *tctx,
+                            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);
+       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
+       };
 
-       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;
+       torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
 
-       status = smb2_create(tree, torture, &(io.smb2));
-       torture_assert_ntstatus_ok(torture, status, "smb2_create dir");
-       d1 = io.smb2.out.file.handle;
+       torture_comment(tctx, "Set AFP_AfpInfo EOF to 61, 1 and 0\n");
 
-       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";
+       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");
 
-       status = smb2_create(tree, torture, &(io.smb2));
-       torture_assert_ntstatus_ok(torture, status, "smb2_create file");
-       h1 = io.smb2.out.file.handle;
+       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");
 
-       if (!server_is_osx) {
-               torture_comment(torture, "Renaming directory without AAPL, must fail\n");
+       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;
 
-               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);
+       status = smb2_create(tree, mem_ctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
 
-               torture_assert_ntstatus_equal(torture, status,
-                                             NT_STATUS_ACCESS_DENIED,
-                                             "smb2_setinfo_file");
+       h1 = create.out.file.handle;
 
-               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(tctx, "Set AFP_AfpInfo EOF to 61\n");
 
-       torture_comment(torture, "Enabling AAPL\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");
 
-       ret = enable_aapl(torture, tree);
-       torture_assert(torture, ret == true, "enable_aapl failed");
+       torture_comment(tctx, "Set AFP_AfpInfo EOF to 1\n");
 
-       torture_comment(torture, "Renaming directory with AAPL\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);
 
-       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;
+       ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
-       status = smb2_create(tree, torture, &(io.smb2));
-       torture_assert_ntstatus_ok(torture, status, "smb2_create dir");
-       d1 = io.smb2.out.file.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, "FinderInfo changed");
 
-       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;
+       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_setinfo_file(tree, &sinfo);
-       torture_assert_ntstatus_ok(torture, status, "smb2_setinfo_file");
+       status = smb2_create(tree, mem_ctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
 
-       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);
+       h1 = create.out.file.handle;
 
-       cl.smb2.in.file.handle = h1;
-       status = smb2_close(tree, &(cl.smb2));
-       torture_assert_ntstatus_ok(torture, status, "smb2_close");
-       ZERO_STRUCT(h1);
+       /*
+        * 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");
 
-       torture_comment(torture, "Cleaning up\n");
+       smb2_util_close(tree, h1);
 
-       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));
-       }
+       ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
-       smb2_util_unlink(tree, BASEDIR "\\file.txt");
-       smb2_util_unlink(tree, BASEDIR "-new\\file.txt");
-       smb2_deltree(tree, renamedir);
-       smb2_deltree(tree, BASEDIR);
+       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_enoent(struct torture_context *tctx,
-                               struct smb2_tree *tree)
+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;
+       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_NAME;
+       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_comment(tctx, "Opening file without AFP_AfpInfo\n");
+       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);
@@ -3041,55 +4417,134 @@ static bool test_afpinfo_enoent(struct torture_context *tctx,
        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");
+       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.desired_access = SEC_FILE_READ_ATTRIBUTE; /* stat open */
+       create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+       create.in.fname = fname;
+
+       status = smb2_create(tree, mem_ctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, 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_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
-                                          ret, done, "Got unexpected AFP_AfpInfo stream");
+       torture_assert_ntstatus_ok_goto(tctx, status, 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(struct torture_context *tctx,
-                                       struct smb2_tree *tree)
+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" AFPINFO_STREAM_NAME;
-       const char *type_creator = "SMB,OLE!";
-       AfpInfo *info = NULL;
+       const char *fname = BASEDIR "\\file";
+       const char *sname = BASEDIR "\\file" AFPRESOURCE_STREAM_NAME;
        const char *streams_basic[] = {
                "::$DATA"
        };
-       const char *streams_afpinfo[] = {
+       const char *streams_afpresource[] = {
                "::$DATA",
-               AFPINFO_STREAM
+               AFPRESOURCE_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");
+       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_goto(tctx, status, ret, done, "torture_smb2_testdir");
+       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_AfpInfo\n");
+       torture_comment(tctx, "Opening not existing AFP_AfpResource\n");
 
        ZERO_STRUCT(create);
        create.in.create_disposition = NTCREATEX_DISP_OPEN;
@@ -3098,7 +4553,7 @@ static bool test_create_delete_on_close(struct torture_context *tctx,
 
        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, done, "Got unexpected AFP_AfpResource stream");
 
        ZERO_STRUCT(create);
        create.in.create_disposition = NTCREATEX_DISP_OPEN;
@@ -3107,25 +4562,23 @@ static bool test_create_delete_on_close(struct torture_context *tctx,
 
        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, 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, "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");
+       torture_comment(tctx, "Trying to delete AFP_AfpResource via create with delete-on-close\n");
 
-       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, 10, "1234567890");
+       torture_assert_goto(tctx, ret == true, ret, done, "Writing to AFP_AfpResource 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(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_afpinfo, false);
+       ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false);
        torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
        ZERO_STRUCT(create);
@@ -3142,26 +4595,21 @@ static bool test_create_delete_on_close(struct torture_context *tctx,
        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);
+       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(struct torture_context *tctx,
-                                        struct smb2_tree *tree)
+static bool test_setinfo_delete_on_close_resource(struct torture_context *tctx,
+                                                 struct smb2_tree *tree)
 {
        bool ret = true;
        NTSTATUS status;
@@ -3170,16 +4618,15 @@ static bool test_setinfo_delete_on_close(struct torture_context *tctx,
        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 *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, "Deleting AFP_AfpInfo via setinfo with delete-on-close\n");
+       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);
@@ -3188,11 +4635,10 @@ static bool test_setinfo_delete_on_close(struct torture_context *tctx,
        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 = 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;
@@ -3206,7 +4652,7 @@ static bool test_setinfo_delete_on_close(struct torture_context *tctx,
 
        h1 = create.out.file.handle;
 
-       /* Delete stream via setinfo delete-on-close */
+       /* 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;
@@ -3216,7 +4662,7 @@ static bool test_setinfo_delete_on_close(struct torture_context *tctx,
 
        smb2_util_close(tree, h1);
 
-       ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
+       ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false);
        torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
        ZERO_STRUCT(create);
@@ -3226,8 +4672,8 @@ static bool test_setinfo_delete_on_close(struct torture_context *tctx,
        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");
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "Got unexpected AFP_AfpResource stream");
 
 done:
        smb2_util_unlink(tree, fname);
@@ -3235,27 +4681,28 @@ done:
        return ret;
 }
 
-static bool test_setinfo_eof(struct torture_context *tctx,
-                            struct smb2_tree *tree)
+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" AFPINFO_STREAM_NAME;
-       const char *type_creator = "SMB,OLE!";
-       AfpInfo *info = NULL;
-       const char *streams_afpinfo[] = {
-               "::$DATA",
-               AFPINFO_STREAM
+       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_AfpInfo EOF to 61, 1 and 0\n");
+       ret = enable_aapl(tctx, tree);
+       torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
+
+       torture_comment(tctx, "Set AFP_AfpResource EOF to 1 and 0\n");
 
        smb2_deltree(tree, BASEDIR);
        status = torture_smb2_testdir(tree, BASEDIR, &h1);
@@ -3264,11 +4711,57 @@ static bool test_setinfo_eof(struct torture_context *tctx,
        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 = 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;
@@ -3280,1547 +4773,2168 @@ static bool test_setinfo_eof(struct torture_context *tctx,
        status = smb2_create(tree, mem_ctx, &create);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
 
-       h1 = create.out.file.handle;
+       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!";
+       struct smb2_handle handle2;
+       struct smb2_read r;
+
+       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");
+
+       status = torture_smb2_testfile_access(tree, sname, &handle2,
+                                             SEC_FILE_READ_DATA);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testfile failed\n");
+       r = (struct smb2_read) {
+               .in.file.handle = handle2,
+               .in.length      = AFP_INFO_SIZE,
+       };
 
-       torture_comment(tctx, "Set AFP_AfpInfo EOF to 61\n");
+       status = smb2_read(tree, tree, &r);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testfile failed\n");
+       smb2_util_close(tree, handle2);
 
-       /* 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");
+       afpinfo = torture_afpinfo_new(mem_ctx);
+       torture_assert_goto(tctx, afpinfo != NULL, ret, done, "torture_afpinfo_new failed");
 
-       torture_comment(tctx, "Set AFP_AfpInfo EOF to 1\n");
+       memcpy(afpinfo->afpi_FinderInfo, type_creator, 8);
 
-       /* 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);
+       afpinfo_buf = torture_afpinfo_pack(tctx, afpinfo);
+       torture_assert_goto(tctx, afpinfo_buf != NULL, ret, done, "torture_afpinfo_new failed");
 
-       ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+       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, "FinderInfo changed");
+       torture_assert_goto(tctx, ret == true, ret, done, "check_stream 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;
+done:
+       smb2_util_unlink(tree, fname);
+       talloc_free(mem_ctx);
+       return ret;
+}
 
-       status = smb2_create(tree, mem_ctx, &create);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
+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;
 
-       h1 = create.out.file.handle;
+       smb2_util_unlink(tree, fname);
 
-       /*
-        * 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");
+       torture_comment(tctx, "Test deleting file with resource fork\n");
 
-       smb2_util_close(tree, h1);
+       ret = torture_setup_file(tctx, tree, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed\n");
 
-       ret = check_stream_list(tree, tctx, fname, 2, streams_afpinfo, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+       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, mem_ctx, fname, AFPINFO_STREAM,
-                          0, 60, 16, 8, type_creator);
-       torture_assert_goto(tctx, ret == true, ret, done, "FinderInfo changed");
+       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:
-       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)
+static bool test_rename_and_read_rsrc(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
-       };
+       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;
+       bool server_is_macos = torture_setting_bool(tctx, "osx", false);
+       NTSTATUS expected_status;
 
-       torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
+       ret = enable_aapl(tctx, tree);
+       torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
 
-       torture_comment(tctx, "Write all 0 to AFP_AfpInfo and see what happens\n");
+       torture_comment(tctx, "Create file with resource fork\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);
+       ret = torture_setup_file(tctx, 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");
+       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");
 
-       /* 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");
+       torture_comment(tctx, "Open resource fork\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.create_disposition = NTCREATEX_DISP_OPEN;
+       create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
        create.in.fname = sname;
 
-       status = smb2_create(tree, mem_ctx, &create);
-       torture_assert_goto(tctx, ret == true, ret, done,
-                           "smb2_create failed\n");
+       status = smb2_create(tree, tctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
+
        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");
+       torture_comment(tctx, "Rename base file\n");
 
-       /*
-        * Get stream information on open handle, must return only default
-        * stream, the AFP_AfpInfo stream must not be returned.
-        */
+       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;
 
-       ZERO_STRUCT(getfinfo);
-       getfinfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
-       getfinfo.generic.in.file.handle = baseh;
+       status = smb2_create(tree, tctx, &create2);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
 
-       status = smb2_getinfo_file(tree, tctx, &getfinfo);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "get stream info\n");
+       h2 = create2.out.file.handle;
 
-       torture_assert_int_equal_goto(tctx, getfinfo.stream_info.out.num_streams,
-                                     1, ret, done, "stream count");
+       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;
 
-       smb2_util_close(tree, baseh);
-       ZERO_STRUCT(baseh);
+       if (server_is_macos) {
+               expected_status = NT_STATUS_SHARING_VIOLATION;
+       } else {
+               expected_status = NT_STATUS_ACCESS_DENIED;
+       }
 
-       /*
-        * 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.
-        */
+       status = smb2_setinfo_file(tree, &sinfo);
+       torture_assert_ntstatus_equal_goto(
+               tctx, status, expected_status, ret, done,
+               "smb2_setinfo_file failed");
 
-       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;
+       smb2_util_close(tree, h2);
 
-       status = smb2_setinfo_file(tree, &setfinfo);
+       status = smb2_util_write(tree, h1, "foo", 0, 3);
        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");
+                                       "write failed\n");
 
        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
-       };
+       smb2_util_unlink(tree, fname_renamed);
 
-       torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
+       return ret;
+}
 
-       torture_comment(tctx, "Checking whether create with delete-on-close is ignored for AFP_AfpResource\n");
+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, &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");
+       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);
 
-       ZERO_STRUCT(create);
-       create.in.create_disposition = NTCREATEX_DISP_OPEN;
-       create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; /* stat open */
-       create.in.fname = sname;
+       torture_comment(tctx, "Enabling AAPL\n");
 
-       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 = enable_aapl(tctx, tree);
+       torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
 
-       ZERO_STRUCT(create);
-       create.in.create_disposition = NTCREATEX_DISP_OPEN;
-       create.in.desired_access = SEC_FILE_ALL;
-       create.in.fname = sname;
+       /*
+        * Now that Requested AAPL extensions are enabled, setup some
+        * Mac files with metadata and resource fork
+        */
 
-       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");
+       torture_comment(tctx, "Preparing file\n");
 
-       ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+       ret = torture_setup_file(mem_ctx, tree, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed");
 
-       torture_comment(tctx, "Trying to delete AFP_AfpResource via create with delete-on-close\n");
+       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, 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");
+                          0, 3, "foo");
+       torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed");
 
-       ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+       /*
+        * Ok, file is prepared, now call smb2/find
+        */
 
-       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;
+       torture_comment(tctx, "Issue find\n");
 
-       status = smb2_create(tree, mem_ctx, &create);
+       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");
 
-       h1 = create.out.file.handle;
-       smb2_util_close(tree, h1);
+       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;
 
-       ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+       status = smb2_find_level(tree, tree, &f, &count, &d);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_level 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");
+       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_util_rmdir(tree, BASEDIR);
+       smb2_deltree(tree, BASEDIR);
+       talloc_free(mem_ctx);
        return ret;
 }
 
-static bool test_setinfo_delete_on_close_resource(struct torture_context *tctx,
-                                                 struct smb2_tree *tree)
+static bool test_invalid_afpinfo(struct torture_context *tctx,
+                                struct smb2_tree *tree1,
+                                struct smb2_tree *tree2)
 {
-       bool ret = true;
-       NTSTATUS status;
+       const char *fname = "filtest_invalid_afpinfo";
+       const char *sname = "filtest_invalid_afpinfo" AFPINFO_STREAM_NAME;
        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[] = {
+       const char *streams_basic[] = {
+               "::$DATA"
+       };
+       const char *streams_afpinfo[] = {
                "::$DATA",
-               AFPRESOURCE_STREAM
+               AFPINFO_STREAM
        };
+       NTSTATUS status;
+       bool ret = true;
 
-       torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
+       if (tree2 == NULL) {
+               torture_skip_goto(tctx, done, "need second share without fruit\n");
+       }
 
-       torture_comment(tctx, "Trying to delete AFP_AfpResource via setinfo with delete-on-close\n");
+       torture_comment(tctx, "Testing invalid AFP_AfpInfo 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");
-       smb2_util_close(tree, h1);
-       ret = torture_setup_file(mem_ctx, tree, fname, false);
+       ret = torture_setup_file(tctx, tree2, 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;
+       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");
 
-       /* 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");
+       ret = check_stream_list(tree2, tctx, fname, 2, streams_afpinfo, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
-       smb2_util_close(tree, h1);
+       torture_comment(tctx, "Listing streams, bad AFPINFO stream must not be present\n");
 
-       ret = check_stream_list(tree, tctx, fname, 2, streams_afpresource, false);
+       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.create_disposition = NTCREATEX_DISP_OPEN;
        create.in.desired_access = SEC_FILE_ALL;
-       create.in.fname = sname;
+       create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
        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");
+       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(tree, fname);
-       smb2_util_rmdir(tree, BASEDIR);
+       smb2_util_unlink(tree1, fname);
        return ret;
 }
 
-static bool test_setinfo_eof_resource(struct torture_context *tctx,
-                                     struct smb2_tree *tree)
+static bool test_writing_afpinfo(struct torture_context *tctx,
+                                struct smb2_tree *tree)
 {
+       const char *fname = "filtest_invalid_afpinfo";
+       const char *sname = "filtest_invalid_afpinfo" AFPINFO_STREAM;
+       const char *streams_afpinfo[] = {
+               "::$DATA",
+               AFPINFO_STREAM
+       };
        bool ret = true;
+       static AfpInfo *afpi = NULL;
+       char *buf = NULL;
+       char *afpi_buf = NULL;
+       char *zero_buf = NULL;
+       bool broken_osx = torture_setting_bool(tctx, "broken_osx_45759458", false);
+       off_t min_offset_for_2streams = 16;
+       int i;
        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"
+       struct test_sizes {
+               off_t offset;
+               size_t size;
+               bool expected_result;
+       } test_sizes[] = {
+               { 0, 1, false},
+               { 0, 2, false},
+               { 0, 3, true},
+               { 0, 4, true},
+               { 0, 14, true},
+               { 0, 15, true},
+               { 0, 16, true},
+               { 0, 24, true},
+               { 0, 34, true},
+               { 0, 44, true},
+               { 0, 54, true},
+               { 0, 55, true},
+               { 0, 56, true},
+               { 0, 57, true},
+               { 0, 58, true},
+               { 0, 59, true},
+               { 0, 60, true},
+               { 0, 61, true},
+               { 0, 64, true},
+               { 0, 1024, true},
+               { 0, 10064, true},
+
+               { 1, 1, false},
+               { 1, 2, false},
+               { 1, 3, false},
+               { 1, 4, false},
+               { 1, 14, false},
+               { 1, 15, false},
+               { 1, 16, false},
+               { 1, 24, false},
+               { 1, 34, false},
+               { 1, 44, false},
+               { 1, 54, false},
+               { 1, 55, false},
+               { 1, 56, false},
+               { 1, 57, false},
+               { 1, 58, false},
+               { 1, 59, false},
+               { 1, 60, true},
+               { 1, 61, true},
+               { 1, 1024, true},
+               { 1, 10064, true},
+
+               { 30, 1, false},
+               { 30, 2, false},
+               { 30, 3, false},
+               { 30, 4, false},
+               { 30, 14, false},
+               { 30, 15, false},
+               { 30, 16, false},
+               { 30, 24, false},
+               { 30, 34, false},
+               { 30, 44, false},
+               { 30, 54, false},
+               { 30, 55, false},
+               { 30, 56, false},
+               { 30, 57, false},
+               { 30, 58, false},
+               { 30, 59, false},
+               { 30, 60, true},
+               { 30, 61, true},
+               { 30, 1024, true},
+               { 30, 10064, true},
+
+               { 58, 1, false},
+               { 58, 2, false},
+               { 58, 3, false},
+               { 58, 4, false},
+               { 58, 14, false},
+               { 58, 15, false},
+               { 58, 16, false},
+               { 58, 24, false},
+               { 58, 34, false},
+               { 58, 44, false},
+               { 58, 54, false},
+               { 58, 55, false},
+               { 58, 56, false},
+               { 58, 57, false},
+               { 58, 58, false},
+               { 58, 59, false},
+               { 58, 60, true},
+               { 58, 61, true},
+               { 58, 1024, true},
+               { 58, 10064, true},
+
+               { 59, 1, false},
+               { 59, 2, false},
+               { 59, 3, false},
+               { 59, 4, false},
+               { 59, 14, false},
+               { 59, 15, false},
+               { 59, 16, false},
+               { 59, 24, false},
+               { 59, 34, false},
+               { 59, 44, false},
+               { 59, 54, false},
+               { 59, 55, false},
+               { 59, 56, false},
+               { 59, 57, false},
+               { 59, 58, false},
+               { 59, 59, false},
+               { 59, 60, true},
+               { 59, 61, true},
+               { 59, 1024, true},
+               { 59, 10064, true},
+
+               { 60, 1, false},
+               { 60, 2, false},
+               { 60, 3, false},
+               { 60, 4, false},
+               { 60, 14, false},
+               { 60, 15, false},
+               { 60, 16, false},
+               { 60, 24, false},
+               { 60, 34, false},
+               { 60, 44, false},
+               { 60, 54, false},
+               { 60, 55, false},
+               { 60, 56, false},
+               { 60, 57, false},
+               { 60, 58, false},
+               { 60, 59, false},
+               { 60, 60, true},
+               { 60, 61, true},
+               { 60, 1024, true},
+               { 60, 10064, true},
+
+               { 61, 1, false},
+               { 61, 2, false},
+               { 61, 3, false},
+               { 61, 4, false},
+               { 61, 14, false},
+               { 61, 15, false},
+               { 61, 16, false},
+               { 61, 24, false},
+               { 61, 34, false},
+               { 61, 44, false},
+               { 61, 54, false},
+               { 61, 55, false},
+               { 61, 56, false},
+               { 61, 57, false},
+               { 61, 58, false},
+               { 61, 59, false},
+               { 61, 60, true},
+               { 61, 61, true},
+               { 61, 1024, true},
+               { 61, 10064, true},
+
+               { 10000, 1, false},
+               { 10000, 2, false},
+               { 10000, 3, false},
+               { 10000, 4, false},
+               { 10000, 14, false},
+               { 10000, 15, false},
+               { 10000, 16, false},
+               { 10000, 24, false},
+               { 10000, 34, false},
+               { 10000, 44, false},
+               { 10000, 54, false},
+               { 10000, 55, false},
+               { 10000, 56, false},
+               { 10000, 57, false},
+               { 10000, 58, false},
+               { 10000, 59, false},
+               { 10000, 60, true},
+               { 10000, 61, true},
+               { 10000, 1024, true},
+               { 10000, 10064, true},
+
+               { -1, 0, false},
        };
 
-       torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new");
+       afpi = torture_afpinfo_new(tctx);
+       torture_assert_not_null_goto(tctx, afpi, ret, done,
+                                    "torture_afpinfo_new failed\n");
 
-       torture_comment(tctx, "Set AFP_AfpResource EOF to 1 and 0\n");
+       memcpy(afpi->afpi_FinderInfo, "FOO BAR ", 8);
+
+       buf = torture_afpinfo_pack(afpi, afpi);
+       torture_assert_not_null_goto(tctx, buf, ret, done,
+                                    "torture_afpinfo_pack failed\n");
+
+       afpi_buf = talloc_zero_array(tctx, char, 10064);
+       torture_assert_not_null_goto(tctx, afpi_buf, ret, done,
+                                    "talloc_zero_array failed\n");
+       memcpy(afpi_buf, buf, 60);
+
+       zero_buf = talloc_zero_array(tctx, char, 10064);
+       torture_assert_not_null_goto(tctx, zero_buf, ret, done,
+                                    "talloc_zero_array failed\n");
+
+       ret = torture_setup_file(tctx, tree, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_setup_file\n");
+
+       for (i = 0; test_sizes[i].offset != -1; i++) {
+               struct smb2_handle h;
+               struct smb2_create c;
+               int expected_num_streams;
+               size_t fi_check_size;
+
+               torture_comment(tctx,
+                               "Test %d: offset=%jd size=%zu result=%s\n",
+                               i,
+                               (intmax_t)test_sizes[i].offset,
+                               test_sizes[i].size,
+                               test_sizes[i].expected_result ? "true":"false");
+
+
+               c = (struct smb2_create) {
+                       .in.desired_access = SEC_FILE_WRITE_DATA,
+                       .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+                       .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+                       .in.fname = sname,
+               };
+
+               status = smb2_create(tree, tree, &c);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_create\n");
+               h = c.out.file.handle;
+
+               status = smb2_util_write(tree,
+                                        h,
+                                        zero_buf,
+                                        test_sizes[i].offset,
+                                        test_sizes[i].size);
+               torture_assert_ntstatus_equal_goto(
+                       tctx, status, NT_STATUS_INVALID_PARAMETER,
+                       ret, done, "smb2_util_write\n");
+
+               status = smb2_util_write(tree,
+                                        h,
+                                        afpi_buf,
+                                        test_sizes[i].offset,
+                                        test_sizes[i].size);
+               smb2_util_close(tree, h);
+               if (test_sizes[i].expected_result == true) {
+                       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                                       "smb2_util_write\n");
+               } else {
+                       torture_assert_ntstatus_equal_goto(
+                               tctx, status, NT_STATUS_INVALID_PARAMETER,
+                               ret, done, "smb2_util_write\n");
+               }
+
+               if (broken_osx) {
+                       /*
+                        * Currently macOS has a bug (Radar #45759458) where it
+                        * writes more bytes then requested from uninitialized
+                        * memory to the filesystem. That means it will likely
+                        * write data to FinderInfo so the stream is not empty
+                        * and thus listed when the number of streams is
+                        * queried.
+                        */
+                       min_offset_for_2streams = 2;
+               }
 
-       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");
+               if ((test_sizes[i].expected_result == true) &&
+                   (test_sizes[i].size > min_offset_for_2streams))
+               {
+                       expected_num_streams = 2;
+               } else {
+                       expected_num_streams = 1;
+               }
 
-       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");
+               ret = check_stream_list(tree, tctx, fname,
+                                       expected_num_streams,
+                                       streams_afpinfo, false);
+               torture_assert_goto(tctx, ret == true, ret, done,
+                                   "Bad streams\n");
 
-       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;
+               if (test_sizes[i].expected_result == false) {
+                       continue;
+               }
 
-       status = smb2_create(tree, mem_ctx, &create);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
+               if (test_sizes[i].size <= 16) {
+                       /*
+                        * FinderInfo with the "FOO BAR " string we wrote above
+                        * would start at offset 16. Check whether this test
+                        * wrote 1 byte or more.
+                        */
+                       goto next;
+               }
 
-       h1 = create.out.file.handle;
+               fi_check_size = test_sizes[i].size - 16;
+               fi_check_size = MIN(fi_check_size, 8);
 
-       torture_comment(tctx, "Set AFP_AfpResource EOF to 1\n");
+               ret = check_stream(tree, __location__,
+                                  tctx, tctx,
+                                  fname, AFPINFO_STREAM,
+                                  0, 60, 16, fi_check_size, "FOO BAR ");
+               torture_assert_goto(tctx, ret == true, ret, done,
+                                   "Bad streams\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");
+next:
+               status = smb2_util_unlink(tree, sname);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+                       bool missing_ok;
 
-       smb2_util_close(tree, h1);
+                       missing_ok = test_sizes[i].expected_result == false;
+                       missing_ok |= test_sizes[i].size <= 16;
 
-       /* 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");
+                       torture_assert_goto(tctx, missing_ok,
+                                           ret, done, "smb2_util_unlink\n");
+               }
+       }
 
-       h1 = create.out.file.handle;
+done:
+       smb2_util_unlink(tree, fname);
+       return ret;
+}
 
-       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");
+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};
 
-       smb2_util_close(tree, h1);
+       torture_comment(tctx, "Testing zero file id\n");
 
-       torture_assert_goto(tctx, finfo.all_info.out.size == 1, ret, done, "size != 1");
+       ret = torture_setup_file(tctx, tree, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
 
        ZERO_STRUCT(create);
-       create.in.create_disposition = NTCREATEX_DISP_OPEN;
-       create.in.desired_access = SEC_FILE_ALL;
-       create.in.fname = sname;
+       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.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;
+       create.in.create_disposition = NTCREATEX_DISP_OPEN;
+       create.in.fname = fname;
+       create.in.query_on_disk_id = true;
 
-       /*
-        * 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");
+       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, h1);
+       smb2_util_close(tree, create.out.file.handle);
 
-       ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+       ret = enable_aapl(tctx, tree);
+       torture_assert(tctx, ret == true, "enable_aapl failed");
 
        ZERO_STRUCT(create);
-       create.in.create_disposition = NTCREATEX_DISP_OPEN;
-       create.in.desired_access = SEC_FILE_ALL;
-       create.in.fname = sname;
+       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.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
+       create.in.create_disposition = NTCREATEX_DISP_OPEN;
+       create.in.fname = fname;
+       create.in.query_on_disk_id = true;
 
-       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");
+       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);
-       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)
+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)
 {
-       TALLOC_CTX *mem_ctx = talloc_new(tctx);
-       const char *fname = "test_null_afpinfo";
-       const char *sname = "test_null_afpinfo" AFPINFO_STREAM_NAME;
+       struct smb2_handle src_h = {{0}};
+       struct smb2_handle dest_h = {{0}};
        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!";
+       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;
 
-       torture_comment(tctx, "Checking create of AfpInfo stream\n");
+       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");
 
-       smb2_util_unlink(tree, fname);
+       /* 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;
 
-       ret = torture_setup_file(mem_ctx, tree, fname, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed");
+       ndr_ret = ndr_push_struct_blob(
+               &io.smb2.in.out, tmp_ctx, &cc_copy,
+               (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
 
-       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;
+       torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+                                  "ndr_push_srv_copychunk_copy\n");
 
-       smb2_transport_compound_start(tree->session->transport, 2);
+       status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+       torture_assert_ntstatus_ok_goto(torture, status, ok, done,
+                                       "FSCTL_SRV_COPYCHUNK\n");
 
-       req[0] = smb2_create_send(tree, &create);
+       ndr_ret = ndr_pull_struct_blob(
+               &io.smb2.out.out, tmp_ctx, &cc_rsp,
+               (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
 
-       handle.data[0] = UINT64_MAX;
-       handle.data[1] = UINT64_MAX;
+       torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+                                  "ndr_pull_srv_copychunk_rsp\n");
 
-       smb2_transport_compound_set_related(tree->session->transport, true);
+       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");
 
-       ZERO_STRUCT(read);
-       read.in.file.handle = handle;
-       read.in.length = AFP_INFO_SIZE;
-       req[1] = smb2_read_send(tree, &read);
+       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
+       if (!ok) {
+               torture_fail(torture, "inconsistent file data\n");
+       }
 
-       status = smb2_create_recv(req[0], tree, &create);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_recv failed");
+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);
+       }
 
-       handle = create.out.file.handle;
+       return ok;
+}
 
-       status = smb2_read_recv(req[1], tree, &read);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_read_recv failed");
+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;
 
-       afpinfo = torture_afpinfo_new(mem_ctx);
-       torture_assert_goto(tctx, afpinfo != NULL, ret, done, "torture_afpinfo_new failed");
+       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");
 
-       memcpy(afpinfo->afpi_FinderInfo, type_creator, 8);
+       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");
 
-       afpinfo_buf = torture_afpinfo_pack(tctx, afpinfo);
-       torture_assert_goto(tctx, afpinfo_buf != NULL, ret, done, "torture_afpinfo_new failed");
+       info = torture_afpinfo_new(tmp_ctx);
+       torture_assert_not_null_goto(torture, info, ok, done,
+                                    "torture_afpinfo_new failed\n");
 
-       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");
+       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");
 
-       smb2_util_close(tree, handle);
+       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");
 
-       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");
+       /* 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;
 
-done:
-       smb2_util_unlink(tree, fname);
-       talloc_free(mem_ctx);
-       return ret;
-}
+       ndr_ret = ndr_push_struct_blob(
+               &io.smb2.in.out, tmp_ctx, &cc_copy,
+               (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
 
-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;
+       torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+                                  "ndr_push_srv_copychunk_copy\n");
 
-       smb2_util_unlink(tree, fname);
+       status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+       torture_assert_ntstatus_ok_goto(torture, status, ok, done,
+                                       "FSCTL_SRV_COPYCHUNK\n");
 
-       torture_comment(tctx, "Test deleting file with resource fork\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);
 
-       ret = torture_setup_file(tctx, tree, fname, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed\n");
+       torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+                                  "ndr_pull_srv_copychunk_rsp\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");
+       smb2_util_close(tree, src_h);
+       ZERO_STRUCT(src_h);
+       smb2_util_close(tree, dest_h);
+       ZERO_STRUCT(dest_h);
 
-       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");
+       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");
 
-       status = smb2_util_unlink(tree, fname);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "check_stream failed\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:
-       return ret;
+       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_rename_and_read_rsrc(struct torture_context *tctx,
-                                     struct smb2_tree *tree)
+static bool test_copy_chunk_streams(struct torture_context *torture,
+                                   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");
+       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;
 
-       h1 = create.out.file.handle;
+       tmp_ctx = talloc_new(tree);
+       torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
+                                    "torture_setup_file\n");
 
-       torture_comment(tctx, "Rename base file\n");
+       smb2_util_unlink(tree, src_name);
+       smb2_util_unlink(tree, dst_name);
 
-       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;
+       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");
 
-       status = smb2_create(tree, tctx, &create2);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
+       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");
+       }
 
-       h2 = create2.out.file.handle;
+       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");
 
-       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;
+done:
+       smb2_util_unlink(tree, src_name);
+       smb2_util_unlink(tree, dst_name);
+       talloc_free(tmp_ctx);
+       return ok;
+}
 
-       status = smb2_setinfo_file(tree, &sinfo);
-       torture_assert_ntstatus_equal_goto(
-               tctx, status, NT_STATUS_ACCESS_DENIED, ret, done,
-               "smb2_setinfo_file failed");
+/*
+ * Ensure this security descriptor has exactly one mode, uid
+ * and gid.
+ */
 
-       smb2_util_close(tree, h1);
-       smb2_util_close(tree, h2);
+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;
 
-done:
-       smb2_util_unlink(tree, fname);
-       smb2_util_unlink(tree, fname_renamed);
+       if (psd->dacl == NULL) {
+               return NT_STATUS_INVALID_SECURITY_DESCR;
+       }
 
-       return ret;
+       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_readdir_attr_illegal_ntfs(struct torture_context *tctx,
-                                          struct smb2_tree *tree)
+
+static bool test_nfs_aces(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" */
+       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;
-       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);
+       bool is_osx = torture_setting_bool(tctx, "osx", false);
 
-       torture_comment(tctx, "Enabling AAPL\n");
+       if (is_osx) {
+               torture_skip(tctx, "Test only works with Samba\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");
+       torture_assert(tctx, ret == true, "enable_aapl failed");
 
-       info = torture_afpinfo_new(mem_ctx);
-       torture_assert_not_null_goto(tctx, info, ret, done, "torture_afpinfo_new failed");
+       /* clean slate ...*/
+       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, fname);
+       smb2_deltree(tree, BASEDIR);
 
-       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");
+       status = torture_smb2_testdir(tree, BASEDIR, &h);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       smb2_util_close(tree, h);
 
-       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");
+       /* 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);
 
-       /*
-        * Ok, file is prepared, now call smb2/find
-        */
+       /* 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);
 
-       torture_comment(tctx, "Issue find\n");
+       psd = finfo2.query_secdesc.out.sd;
 
-       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");
+       /* 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);
 
-       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;
+       /* 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);
 
-       status = smb2_find_level(tree, tree, &f, &count, &d);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_level failed");
+       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);
 
-       status = smb2_util_close(tree, io.out.file.handle);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed");
+       /* 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);
 
-       torture_comment(tctx, "Checking find response with enriched macOS metadata\n");
+       /* 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);
 
-       for (i = 0; i < count; i++) {
-               const char *found = d[i].id_both_directory_info.name.s;
+       psd = finfo2.query_secdesc.out.sd;
 
-               if (!strcmp(found, ".") || !strcmp(found, ".."))
-                       continue;
-               if (strncmp(found, "._", 2) == 0) {
-                       continue;
-               }
-               break;
+       /* 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);
 
-       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:
+       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_invalid_afpinfo(struct torture_context *tctx,
-                                struct smb2_tree *tree1,
-                                struct smb2_tree *tree2)
+static bool test_setinfo_stream_eof(struct torture_context *tctx,
+                                   struct smb2_tree *tree)
 {
-       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;
+       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";
 
-       if (tree2 == NULL) {
-               torture_skip_goto(tctx, done, "need second share without fruit\n");
-       }
+       torture_assert_goto(tctx, mem_ctx != NULL, ret, done,
+                           "talloc_new failed\n");
 
-       torture_comment(tctx, "Testing invalid AFP_AfpInfo stream\n");
+       ret = enable_aapl(tctx, tree);
+       torture_assert(tctx, ret == true, "enable_aapl failed");
 
-       ret = torture_setup_file(tctx, tree2, fname, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
+       torture_comment(tctx, "Test setting EOF on a stream\n");
 
-       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");
+       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);
 
-       ret = check_stream_list(tree2, tctx, fname, 2, streams_afpinfo, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+       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);
 
-       torture_comment(tctx, "Listing streams, bad AFPINFO stream must not be present\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");
 
-       ret = check_stream_list(tree1, tctx, fname, 1, streams_basic, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+       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);
 
-       torture_comment(tctx, "Try to open AFPINFO stream, must fail\n");
+       /*
+        * 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");
 
-       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;
+       smb2_util_close(tree, h1);
 
-       status = smb2_create(tree1, tctx, &create);
-       torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
-                                          ret, done, "Stream still around?");
+       torture_assert_goto(tctx, finfo.standard_info.out.size == 21,
+                           ret, done, "size != 21\n");
 
-done:
-       smb2_util_unlink(tree1, fname);
-       return ret;
-}
+       /*
+        * Test setting EOF to 0
+        */
 
-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, "Setting stream EOF to 0\n");
 
-       torture_comment(tctx, "Testing zero file id\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");
 
-       ret = torture_setup_file(tctx, tree, fname, false);
-       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
+       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");
 
        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;
+       create.in.fname = sname;
 
        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);
+       torture_assert_ntstatus_equal_goto(
+               tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done,
+               "Unexpected status\n");
 
-       ret = enable_aapl(tctx, tree);
-       torture_assert(tctx, ret == true, "enable_aapl failed");
+       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 = fname;
-       create.in.query_on_disk_id = true;
+       create.in.fname = sname;
 
        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;
-}
+               tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done,
+               "Unexpected status\n");
 
-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;
+       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");
 
-       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");
+       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");
 
-       /* 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;
+       smb2_util_close(tree, h1);
 
-       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_goto(tctx, finfo.standard_info.out.size == 0,
+                           ret, done, "size != 0\n");
 
-       torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
-                                  "ndr_push_srv_copychunk_copy\n");
+       /*
+        * Test setinfo end-of-file info to 1
+        */
 
-       status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
-       torture_assert_ntstatus_ok_goto(torture, status, ok, done,
-                                       "FSCTL_SRV_COPYCHUNK\n");
+       torture_comment(tctx, "Setting stream EOF to 1\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);
+       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");
 
-       torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
-                                  "ndr_pull_srv_copychunk_rsp\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");
 
-       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");
+       smb2_util_close(tree, h1);
 
-       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
-       if (!ok) {
-               torture_fail(torture, "inconsistent file data\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");
 
-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);
-       }
+       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");
 
-       return ok;
-}
+       smb2_util_close(tree, h1);
 
-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;
+       torture_assert_goto(tctx, finfo.standard_info.out.size == 1,
+                           ret, done, "size != 1\n");
 
-       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");
+       /*
+        * Test setting EOF to 0 with AAPL enabled, should delete stream
+        */
 
-       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");
+       torture_comment(tctx, "Enabling AAPL extensions\n");
 
-       info = torture_afpinfo_new(tmp_ctx);
-       torture_assert_not_null_goto(torture, info, ok, done,
-                                    "torture_afpinfo_new failed\n");
+       ret = enable_aapl(tctx, tree);
+       torture_assert(tctx, ret == true, "enable_aapl 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");
+       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");
 
-       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");
+       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");
 
-       /* 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;
+       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;
 
-       ndr_ret = ndr_push_struct_blob(
-               &io.smb2.in.out, tmp_ctx, &cc_copy,
-               (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+       status = smb2_create(tree, tctx, &create);
+       torture_assert_ntstatus_equal_goto(
+               tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done,
+               "Unexpected status\n");
 
-       torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
-                                  "ndr_push_srv_copychunk_copy\n");
+       smb2_util_close(tree, h1);
 
-       status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
-       torture_assert_ntstatus_ok_goto(torture, status, ok, done,
-                                       "FSCTL_SRV_COPYCHUNK\n");
+       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;
 
-       ndr_ret = ndr_pull_struct_blob(
-               &io.smb2.out.out, tmp_ctx, &cc_rsp,
-               (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+       status = smb2_create(tree, tctx, &create);
+       torture_assert_ntstatus_equal_goto(
+               tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done,
+               "Unexpected status\n");
 
-       torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
-                                  "ndr_pull_srv_copychunk_rsp\n");
+       torture_comment(
+               tctx, "Setting main file EOF to 1 to force 0-truncate\n");
 
-       smb2_util_close(tree, src_h);
-       ZERO_STRUCT(src_h);
-       smb2_util_close(tree, dest_h);
-       ZERO_STRUCT(dest_h);
+       status = torture_smb2_testfile_access(
+               tree,
+               fname,
+               &h1,
+               SEC_FILE_WRITE_DATA);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testfile failed\n");
 
-       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");
+       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");
 
-       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");
+       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");
 
-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);
-       }
+        smb2_util_close(tree, h1);
 
-       return ok;
-}
+       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;
 
-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;
+       status = smb2_create(tree, tctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testfile failed\n");
+       smb2_util_close(tree, h1);
 
-       tmp_ctx = talloc_new(tree);
-       torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
-                                    "torture_setup_file\n");
+       torture_comment(tctx, "Writing to stream after setting 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");
 
-       smb2_util_unlink(tree, src_name);
-       smb2_util_unlink(tree, dst_name);
+       status = smb2_util_write(tree, h1, "1234567890", 0, 10);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_write failed\n");
 
-       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");
+       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");
 
-       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");
-       }
+       status = smb2_util_write(tree, h1, "1234567890", 0, 10);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_write 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");
+       smb2_util_close(tree, h1);
 
 done:
-       smb2_util_unlink(tree, src_name);
-       smb2_util_unlink(tree, dst_name);
-       talloc_free(tmp_ctx);
-       return ok;
+       smb2_util_unlink(tree, fname);
+       smb2_util_rmdir(tree, BASEDIR);
+       return ret;
 }
 
-/*
- * Ensure this security descriptor has exactly one mode, uid
- * and gid.
- */
+#define MAX_STREAMS 16
+
+struct tcase {
+       const char *name;
+       uint32_t access;
+       const char *write_data;
+       size_t write_size;
+       struct tcase_results {
+               size_t size;
+               NTSTATUS initial_status;
+               NTSTATUS final_status;
+               int num_streams_open_handle;
+               const char *streams_open_handle[MAX_STREAMS];
+               int num_streams_closed_handle;
+               const char *streams_closed_handle[MAX_STREAMS];
+       } create, write, overwrite, eof, doc;
+};
 
-static NTSTATUS check_nfs_sd(const struct security_descriptor *psd)
+typedef enum {T_CREATE, T_WRITE, T_OVERWRITE, T_EOF, T_DOC} subtcase_t;
+
+static bool test_empty_stream_do_checks(
+       struct torture_context *tctx,
+       struct smb2_tree *tree,
+       struct smb2_tree *tree2,
+       struct tcase *tcase,
+       TALLOC_CTX *mem_ctx,
+       struct smb2_handle baseh,
+       struct smb2_handle streamh,
+       subtcase_t subcase)
 {
-       uint32_t i;
-       bool got_one_mode = false;
-       bool got_one_uid = false;
-       bool got_one_gid = false;
+       bool ret = false;
+       NTSTATUS status;
+       struct smb2_handle h1;
+       union smb_fileinfo finfo;
+       struct tcase_results *tcase_results = NULL;
 
-       if (psd->dacl == NULL) {
-               return NT_STATUS_INVALID_SECURITY_DESCR;
+       switch (subcase) {
+       case T_CREATE:
+               tcase_results = &tcase->create;
+               break;
+       case T_OVERWRITE:
+               tcase_results = &tcase->overwrite;
+               break;
+       case T_WRITE:
+               tcase_results = &tcase->write;
+               break;
+       case T_EOF:
+               tcase_results = &tcase->eof;
+               break;
+       case T_DOC:
+               tcase_results = &tcase->doc;
+               break;
        }
 
-       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;
+       finfo = (union smb_fileinfo) {
+               .generic.level = RAW_FILEINFO_STANDARD_INFORMATION,
+               .generic.in.file.handle = streamh,
+       };
+
+       /*
+        * Test: check size, same client
+        */
+
+       status = smb2_getinfo_file(tree, mem_ctx, &finfo);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testfile failed\n");
+
+       torture_assert_int_equal_goto(tctx, finfo.standard_info.out.size,
+                                     tcase_results->size,
+                                     ret, done, "Wrong size\n");
+
+       /*
+        * Test: open, same client
+        */
+
+       status = torture_smb2_open(tree, tcase->name,
+                                  SEC_FILE_READ_ATTRIBUTE, &h1);
+       torture_assert_ntstatus_equal_goto(tctx, status,
+                                          tcase_results->initial_status,
+                                          ret, done,
+                                          "smb2_create failed\n");
+       if (NT_STATUS_IS_OK(status)) {
+               status = smb2_util_close(tree, h1);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_util_close failed\n");
        }
-       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;
+       /*
+        * Test: check streams, same client
+        */
 
-       ret = enable_aapl(tctx, tree);
-       torture_assert(tctx, ret == true, "enable_aapl failed");
+       ret = check_stream_list_handle(tree, tctx, baseh,
+                                      tcase_results->num_streams_open_handle,
+                                      tcase_results->streams_open_handle,
+                                      false);
+       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
-       /* clean slate ...*/
-       smb2_util_unlink(tree, fname);
-       smb2_deltree(tree, fname);
-       smb2_deltree(tree, BASEDIR);
+       /*
+        * Test: open, different client
+        */
 
-       status = torture_smb2_testdir(tree, BASEDIR, &h);
-       CHECK_STATUS(status, NT_STATUS_OK);
-       smb2_util_close(tree, h);
+       status = torture_smb2_open(tree2, tcase->name,
+                                  SEC_FILE_READ_ATTRIBUTE, &h1);
+       torture_assert_ntstatus_equal_goto(tctx, status,
+                                          tcase_results->initial_status,
+                                          ret, done,
+                                          "smb2_create failed\n");
+       if (NT_STATUS_IS_OK(status)) {
+               finfo = (union smb_fileinfo) {
+                       .generic.level = RAW_FILEINFO_STANDARD_INFORMATION,
+                       .generic.in.file.handle = h1,
+               };
+
+               /*
+                * Test: check size, different client
+                */
 
-       /* 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);
+               status = smb2_getinfo_file(tree2, mem_ctx, &finfo);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_getinfo_file failed\n");
 
-       /* 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);
+               torture_assert_int_equal_goto(tctx, finfo.standard_info.out.size,
+                                             tcase_results->size,
+                                             ret, done, "Wrong size\n");
 
-       psd = finfo2.query_secdesc.out.sd;
+               /*
+                * Test: check streams, different client
+                */
 
-       /* 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);
+               ret = check_stream_list(tree2, tctx, BASEDIR "\\file",
+                                       tcase_results->num_streams_open_handle,
+                                       tcase_results->streams_open_handle,
+                                       false);
+               torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
 
-       /* 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);
+               status = smb2_util_close(tree2, h1);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_util_close failed\n");
+       }
 
-       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);
+       status = smb2_util_close(tree, streamh);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_close failed\n");
 
-       /* 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);
+       /*
+        * Test: open after close, same client
+        */
 
-       /* 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);
+       status = torture_smb2_open(tree, tcase->name,
+                                  SEC_FILE_READ_DATA, &h1);
+       torture_assert_ntstatus_equal_goto(tctx, status,
+                                          tcase_results->final_status,
+                                          ret, done,
+                                          "smb2_create failed\n");
+       if (NT_STATUS_IS_OK(status)) {
+               status = smb2_util_close(tree, h1);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_util_close failed\n");
+       }
 
-       psd = finfo2.query_secdesc.out.sd;
+       /*
+        * Test: open after close, different client
+        */
 
-       /* 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));
+       status = torture_smb2_open(tree2, tcase->name,
+                                  SEC_FILE_READ_DATA, &h1);
+       torture_assert_ntstatus_equal_goto(tctx, status,
+                                          tcase_results->final_status,
+                                          ret, done,
+                                          "smb2_create failed\n");
+       if (NT_STATUS_IS_OK(status)) {
+               status = smb2_util_close(tree2, h1);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_util_close failed\n");
        }
-       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /*
+        * Test: check streams after close, same client
+        */
+
+       ret = check_stream_list_handle(tree, tctx, baseh,
+                                      tcase_results->num_streams_closed_handle,
+                                      tcase_results->streams_closed_handle,
+                                      false);
+       torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+
+       ret = true;
 
 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);
+       smb2_util_close(tree, streamh);
+       smb2_util_close(tree, baseh);
        return ret;
 }
 
-static bool test_setinfo_stream_eof(struct torture_context *tctx,
-                                   struct smb2_tree *tree)
+static bool test_empty_stream_do_one(
+       struct torture_context *tctx,
+       struct smb2_tree *tree,
+       struct smb2_tree *tree2,
+       struct tcase *tcase)
 {
-       bool ret = true;
+       bool ret = false;
        NTSTATUS status;
+       struct smb2_handle baseh = {{0}};
+       struct smb2_handle streamh;
        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, "Testing stream [%s]\n", tcase->name);
 
-       torture_comment(tctx, "Test setting EOF on a stream\n");
+       torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new\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);
+       /*
+        * Subtest: create
+        */
+       torture_comment(tctx, "Subtest: T_CREATE\n");
 
-       status = torture_smb2_testfile(tree, fname, &h1);
+       status = smb2_util_unlink(tree, BASEDIR "\\file");
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
-       smb2_util_close(tree, h1);
+                                       "smb2_util_unlink failed\n");
 
-       status = torture_smb2_testfile_access(tree, sname, &h1,
-                                             SEC_FILE_WRITE_DATA);
+       status = torture_smb2_testfile_access(tree, BASEDIR "\\file",
+                                             &baseh, SEC_FILE_ALL);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
+                                       "torture_smb2_testfile_access failed\n");
 
-       status = smb2_util_write(tree, h1, "1234567890", 0, 10);
+       status = torture_smb2_testfile_access(tree, tcase->name, &streamh,
+                                             tcase->access);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "smb2_util_write failed\n");
-       smb2_util_close(tree, h1);
+                                       "torture_smb2_testfile_access failed\n");
+
+       ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase,
+                                         mem_ctx, baseh, streamh, T_CREATE);
+       torture_assert_goto(tctx, ret, ret, done, "test failed\n");
+
+       if (!(tcase->access & SEC_FILE_WRITE_DATA)) {
+               /*
+                * All subsequent tests require write access
+                */
+               ret = true;
+               goto done;
+       }
 
        /*
-        * Test setting EOF to 21
+        * Subtest: create and write
         */
+       torture_comment(tctx, "Subtest: T_WRITE\n");
 
-       torture_comment(tctx, "Setting stream EOF to 21\n");
-
-       status = torture_smb2_testfile_access(tree, sname, &h1,
-                                             SEC_FILE_WRITE_DATA);
+       status = smb2_util_unlink(tree, BASEDIR "\\file");
        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);
+                                       "smb2_util_unlink failed\n");
 
-       status = torture_smb2_testfile_access(tree, sname, &h1,
-                                             SEC_FILE_WRITE_DATA);
+       status = torture_smb2_testfile_access(tree, BASEDIR "\\file",
+                                             &baseh, SEC_FILE_ALL);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
+                                       "torture_smb2_testfile_access 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);
+       status = torture_smb2_testfile_access(tree, tcase->name, &streamh,
+                                             tcase->access);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "smb2_getinfo_file failed");
+                                       "torture_smb2_testfile_access failed\n");
 
-       smb2_util_close(tree, h1);
+       status = smb2_util_write(tree, streamh, tcase->write_data, 0,
+                                tcase->write_size);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_open failed\n");
 
-       torture_assert_goto(tctx, finfo.standard_info.out.size == 21,
-                           ret, done, "size != 21\n");
+       ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase,
+                                         mem_ctx, baseh, streamh, T_WRITE);
+       torture_assert_goto(tctx, ret, ret, done, "test failed\n");
 
        /*
-        * Test setting EOF to 0
+        * Subtest: overwrite
         */
+       torture_comment(tctx, "Subtest: T_OVERWRITE\n");
 
-       torture_comment(tctx, "Setting stream EOF to 0\n");
-
-       status = torture_smb2_testfile_access(tree, sname, &h1,
-                                             SEC_FILE_WRITE_DATA);
+       status = smb2_util_unlink(tree, BASEDIR "\\file");
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
+                                       "smb2_util_unlink 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);
+       status = torture_smb2_testfile_access(tree, BASEDIR "\\file",
+                                             &baseh, SEC_FILE_ALL);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "set eof 0 failed\n");
+                                       "torture_smb2_testfile_access failed\n");
 
-       smb2_util_close(tree, h1);
+       create = (struct smb2_create) {
+               .in.desired_access = SEC_FILE_ALL,
+               .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+               .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+               .in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF,
+               .in.fname = tcase->name,
+       };
 
-       status = torture_smb2_testfile_access(tree, sname, &h1,
-                                             SEC_FILE_WRITE_DATA);
+       status = smb2_create(tree, tctx, &create);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
                                        "torture_smb2_testfile failed\n");
+       streamh = create.out.file.handle;
 
-       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");
+       ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase,
+                                         mem_ctx, baseh, streamh, T_OVERWRITE);
+       torture_assert_goto(tctx, ret, ret, done, "test failed\n");
 
        /*
-        * Test setinfo end-of-file info to 1
+        * Subtest: setinfo EOF 0
         */
+       torture_comment(tctx, "Subtest: T_EOF\n");
 
-       torture_comment(tctx, "Setting stream EOF to 1\n");
-
-       status = torture_smb2_testfile_access(tree, sname, &h1,
-                                             SEC_FILE_WRITE_DATA);
+       status = smb2_util_unlink(tree, BASEDIR "\\file");
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
+                                       "smb2_util_unlink 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);
+       status = torture_smb2_testfile_access(tree, BASEDIR "\\file",
+                                             &baseh, SEC_FILE_ALL);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "set EOF 1 failed\n");
-
-       smb2_util_close(tree, h1);
+                                       "torture_smb2_testfile_access failed\n");
 
-       status = torture_smb2_testfile_access(tree, sname, &h1,
-                                             SEC_FILE_WRITE_DATA);
+       status = torture_smb2_testfile_access(tree, tcase->name, &streamh,
+                                             tcase->access);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
+                                       "torture_smb2_testfile_access 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);
+       status = smb2_util_write(tree, streamh, tcase->write_data, 0,
+                                tcase->write_size);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "smb2_getinfo_file failed\n");
+                                       "torture_smb2_open failed\n");
 
-       smb2_util_close(tree, h1);
+       sfinfo = (union smb_setfileinfo) {
+               .end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION,
+               .end_of_file_info.in.file.handle = streamh,
+               .end_of_file_info.in.size = 0,
+       };
+       status = smb2_setinfo_file(tree, &sfinfo);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "set eof 0 failed\n");
 
-       torture_assert_goto(tctx, finfo.standard_info.out.size == 1,
-                           ret, done, "size != 1\n");
+       ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase,
+                                         mem_ctx, baseh, streamh, T_EOF);
+       torture_assert_goto(tctx, ret, ret, done, "test failed\n");
 
        /*
-        * Test setting EOF to 0 with AAPL enabled, should delete stream
+        * Subtest: delete-on-close
         */
+       torture_comment(tctx, "Subtest: T_DOC\n");
 
-       torture_comment(tctx, "Enabling AAPL extensions\n");
+       status = smb2_util_unlink(tree, BASEDIR "\\file");
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_unlink failed\n");
 
-       ret = enable_aapl(tctx, tree);
-       torture_assert(tctx, ret == true, "enable_aapl failed\n");
+       status = torture_smb2_testfile_access(tree, BASEDIR "\\file",
+                                             &baseh, SEC_FILE_ALL);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testfile_access failed\n");
 
-       torture_comment(tctx, "Setting stream EOF to 0\n");
-       status = torture_smb2_testfile_access(tree, sname, &h1,
-                                             SEC_FILE_WRITE_DATA);
+       status = torture_smb2_testfile_access(tree, tcase->name, &streamh,
+                                             tcase->access);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
+                                       "torture_smb2_testfile_access 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_util_write(tree, streamh, tcase->write_data, 0,
+                                tcase->write_size);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_open failed\n");
+
+       sfinfo = (union smb_setfileinfo) {
+               .disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFORMATION,
+               .disposition_info.in.file.handle = streamh,
+               .disposition_info.in.delete_on_close = true,
+       };
        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);
+       ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase,
+                                         mem_ctx, baseh, streamh,
+                                         T_DOC);
+       torture_assert_goto(tctx, ret, ret, done, "test failed\n");
 
-       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;
+       ret = true;
 
-       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_close(tree, baseh);
+       TALLOC_FREE(mem_ctx);
+       return ret;
+}
+
+static bool test_empty_stream(struct torture_context *tctx,
+                             struct smb2_tree *tree)
+{
+       struct smb2_tree *tree2 = NULL;
+       struct tcase *tcase = NULL;
+       const char *fname = BASEDIR "\\file";
+       struct smb2_handle h1;
+       bool ret = true;
+       NTSTATUS status;
+       AfpInfo ai = (AfpInfo) {
+               .afpi_Signature = AFP_Signature,
+               .afpi_Version = AFP_Version,
+               .afpi_BackupTime = AFP_BackupTime,
+               .afpi_FinderInfo = "FOO BAR ",
+       };
+       char *ai_blob = torture_afpinfo_pack(tctx, &ai);
+       struct tcase tcase_afpinfo_ro = (struct tcase) {
+               .name = BASEDIR "\\file" AFPINFO_STREAM,
+               .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE,
+               .create = {
+                       .size = 60,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+       };
+       struct tcase tcase_afpinfo_rw = (struct tcase) {
+               .name = BASEDIR "\\file" AFPINFO_STREAM,
+               .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE,
+               .write_data = ai_blob,
+               .write_size = AFP_INFO_SIZE,
+               .create = {
+                       .size = 60,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+               .write = {
+                       .size = 60,
+                       .initial_status = NT_STATUS_OK,
+                       .final_status = NT_STATUS_OK,
+                       .num_streams_open_handle = 2,
+                       .num_streams_closed_handle = 2,
+                       .streams_open_handle = {"::$DATA", AFPINFO_STREAM},
+                       .streams_closed_handle = {"::$DATA", AFPINFO_STREAM},
+               },
+               .overwrite = {
+                       .size = 60,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+               .eof = {
+                       .size = 60,
+                       .initial_status = NT_STATUS_OK,
+                       .final_status = NT_STATUS_OK,
+                       .num_streams_open_handle = 2,
+                       .num_streams_closed_handle = 2,
+                       .streams_open_handle = {"::$DATA", AFPINFO_STREAM},
+                       .streams_closed_handle = {"::$DATA", AFPINFO_STREAM},
+               },
+               .doc = {
+                       .size = 60,
+                       .initial_status = NT_STATUS_DELETE_PENDING,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 2,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA", AFPINFO_STREAM},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+       };
+
+       struct tcase tcase_afpresource_ro = (struct tcase) {
+               .name = BASEDIR "\\file" AFPRESOURCE_STREAM,
+               .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE,
+               .create = {
+                       .size = 0,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+       };
+       struct tcase tcase_afpresource_rw = (struct tcase) {
+               .name = BASEDIR "\\file" AFPRESOURCE_STREAM,
+               .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE,
+               .write_data = "foo",
+               .write_size = 3,
+               .create = {
+                       .size = 0,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+               .write = {
+                       .size = 3,
+                       .initial_status = NT_STATUS_OK,
+                       .final_status = NT_STATUS_OK,
+                       .num_streams_open_handle = 2,
+                       .num_streams_closed_handle = 2,
+                       .streams_open_handle = {"::$DATA", AFPRESOURCE_STREAM},
+                       .streams_closed_handle = {"::$DATA", AFPRESOURCE_STREAM},
+               },
+               .overwrite = {
+                       .size = 0,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+               .eof = {
+                       .size = 0,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+               .doc = {
+                       .size = 3,
+                       .initial_status = NT_STATUS_DELETE_PENDING,
+                       .final_status = NT_STATUS_OK,
+                       .num_streams_open_handle = 2,
+                       .num_streams_closed_handle = 2,
+                       .streams_open_handle = {"::$DATA", AFPRESOURCE_STREAM},
+                       .streams_closed_handle = {"::$DATA", AFPRESOURCE_STREAM},
+               },
+       };
 
-       torture_comment(
-               tctx, "Setting main file EOF to 1 to force 0-truncate\n");
+       struct tcase tcase_foo_ro = (struct tcase) {
+               .name = BASEDIR "\\file:foo",
+               .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE,
+               .write_data = "foo",
+               .write_size = 3,
+               .create = {
+                       .size = 0,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+       };
 
-       status = torture_smb2_testfile_access(
-               tree,
-               fname,
-               &h1,
-               SEC_FILE_WRITE_DATA);
-       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
+       struct tcase tcase_foo_rw = (struct tcase) {
+               .name = BASEDIR "\\file:foo",
+               .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE,
+               .write_data = "foo",
+               .write_size = 3,
+               .create = {
+                       .size = 0,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+               .write = {
+                       .size = 3,
+                       .initial_status = NT_STATUS_OK,
+                       .final_status = NT_STATUS_OK,
+                       .num_streams_open_handle = 2,
+                       .num_streams_closed_handle = 2,
+                       .streams_open_handle = {"::$DATA", ":foo:$DATA"},
+                       .streams_closed_handle = {"::$DATA", ":foo:$DATA"},
+               },
+               .overwrite = {
+                       .size = 0,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+               .eof = {
+                       .size = 0,
+                       .initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 1,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+               .doc = {
+                       .size = 3,
+                       .initial_status = NT_STATUS_DELETE_PENDING,
+                       .final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND,
+                       .num_streams_open_handle = 2,
+                       .num_streams_closed_handle = 1,
+                       .streams_open_handle = {"::$DATA", ":foo:$DATA"},
+                       .streams_closed_handle = {"::$DATA"},
+               },
+       };
 
-       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");
+       struct tcase tcases[] = {
+               tcase_afpinfo_ro,
+               tcase_afpinfo_rw,
+               tcase_afpresource_ro,
+               tcase_afpresource_rw,
+               tcase_foo_ro,
+               tcase_foo_rw,
+               {0}
+       };
 
-       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");
+       ret = torture_smb2_connection(tctx, &tree2);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_smb2_connection failed\n");
 
-        smb2_util_close(tree, h1);
+       ret = enable_aapl(tctx, tree);
+       torture_assert(tctx, ret == true, "enable_aapl failed\n");
 
-       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;
+       ret = enable_aapl(tctx, tree2);
+       torture_assert(tctx, ret == true, "enable_aapl failed\n");
 
-       status = smb2_create(tree, tctx, &create);
+       smb2_deltree(tree, BASEDIR);
+
+       status = torture_smb2_testdir(tree, BASEDIR, &h1);
        torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
-                                       "torture_smb2_testfile failed\n");
+                                       "torture_smb2_testdir\n");
        smb2_util_close(tree, h1);
+
+       for (tcase = &tcases[0]; tcase->name != NULL; tcase++) {
+               ret = torture_setup_file(tctx, tree, fname, false);
+               torture_assert_goto(tctx, ret == true, ret, done,
+                                   "torture_setup_file failed\n");
+
+               ret = test_empty_stream_do_one(tctx, tree, tree2, tcase);
+               torture_assert_goto(tctx, ret == true, ret, done,
+                                   "subtest failed\n");
+
+               status = smb2_util_unlink(tree, fname);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_util_unlink failed\n");
+       }
+
 done:
-       smb2_util_unlink(tree, fname);
-       smb2_util_rmdir(tree, BASEDIR);
+       smb2_deltree(tree, BASEDIR);
+       TALLOC_FREE(tree2);
        return ret;
 }
 
@@ -4866,6 +6980,9 @@ struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx)
        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);
+       torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion without embedded xattr", test_adouble_conversion_wo_xattr);
+       torture_suite_add_1smb2_test(suite, "empty_stream", test_empty_stream);
+       torture_suite_add_1smb2_test(suite, "writing_afpinfo", test_writing_afpinfo);
 
        return suite;
 }
@@ -4918,6 +7035,11 @@ static bool test_stream_names_local(struct torture_context *tctx,
 
        status = smb2_create(tree, mem_ctx, &create);
        CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smb2_util_write(tree, create.out.file.handle, "foo", 0, 3);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_write failed\n");
+
        smb2_util_close(tree, create.out.file.handle);
 
        ret = torture_setup_local_xattr(tctx, "localdir", BASEDIR "/stream_names.txt",
@@ -4937,7 +7059,8 @@ done:
 }
 
 static bool test_fruit_locking_conflict(struct torture_context *tctx,
-                                       struct smb2_tree *tree)
+                                       struct smb2_tree *tree,
+                                       struct smb2_tree *tree2)
 {
        TALLOC_CTX *mem_ctx;
        struct smb2_create create;
@@ -4975,6 +7098,7 @@ static bool test_fruit_locking_conflict(struct torture_context *tctx,
        CHECK_STATUS(status, NT_STATUS_OK);
        h = create.out.file.handle;
 
+       /* Add AD_FILELOCK_RSRC_DENY_WR lock. */
        el = (struct smb2_lock_element) {
                .offset = 0xfffffffffffffffc,
                .length = 1,
@@ -4986,12 +7110,21 @@ static bool test_fruit_locking_conflict(struct torture_context *tctx,
                .in.locks = &el,
        };
 
+       /*
+        * Lock up to and including:
+        * AD_FILELOCK_OPEN_WR
+        * AD_FILELOCK_OPEN_RD
+        * This is designed to cause a NetAtalk
+        * locking conflict on the next open,
+        * even though the share modes are
+        * compatible.
+        */
        status = smb2_lock(tree, &lck);
        CHECK_STATUS(status, NT_STATUS_OK);
 
        el = (struct smb2_lock_element) {
                .offset = 0,
-               .length = 0x7fffffffffffffff,
+               .length = 0x7ffffffffffffff7,
                .flags = SMB2_LOCK_FLAG_EXCLUSIVE,
        };
        status = smb2_lock(tree, &lck);
@@ -5007,8 +7140,13 @@ static bool test_fruit_locking_conflict(struct torture_context *tctx,
                .in.fname = fname,
        };
 
-       status = smb2_create(tree, mem_ctx, &create);
-       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+       /*
+        * Open on the second tree - ensure we are
+        * emulating trying to access with a NetATalk
+        * process with an existing open/deny mode.
+        */
+       status = smb2_create(tree2, mem_ctx, &create);
+       CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
 
        {
                struct smb2_close cl = {
@@ -5032,7 +7170,7 @@ struct torture_suite *torture_vfs_fruit_netatalk(TALLOC_CTX *ctx)
 
        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(
+       torture_suite_add_2smb2_test(
                suite, "locking conflict", test_fruit_locking_conflict);
 
        return suite;
@@ -5154,3 +7292,445 @@ struct torture_suite *torture_vfs_fruit_timemachine(TALLOC_CTX *ctx)
 
        return suite;
 }
+
+static bool test_convert_xattr_and_empty_rfork_then_delete(
+       struct torture_context *tctx,
+       struct smb2_tree *tree1,
+       struct smb2_tree *tree2)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       const char *fname = BASEDIR "\\test_adouble_conversion";
+       const char *adname = BASEDIR "/._test_adouble_conversion";
+       const char *rfork = BASEDIR "\\test_adouble_conversion" AFPRESOURCE_STREAM_NAME;
+       NTSTATUS status;
+       struct smb2_handle testdirh;
+       bool ret = true;
+       const char *streams[] = {
+               "::$DATA",
+               AFPINFO_STREAM,
+               ":com.apple.metadata" "\xef\x80\xa2" "_kMDItemUserTags:$DATA",
+               ":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */
+       };
+       struct smb2_create create;
+       struct smb2_find find;
+       unsigned int count;
+       union smb_search_data *d;
+       bool delete_empty_adfiles;
+       int expected_num_files;
+
+       delete_empty_adfiles = torture_setting_bool(tctx,
+                                                   "delete_empty_adfiles",
+                                                   false);
+
+       smb2_deltree(tree1, BASEDIR);
+
+       status = torture_smb2_testdir(tree1, BASEDIR, &testdirh);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testdir failed\n");
+       smb2_util_close(tree1, testdirh);
+
+       ret = torture_setup_file(tctx, tree1, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_setup_file failed\n");
+
+       ret = torture_setup_file(tctx, tree1, adname, false);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_setup_file failed\n");
+
+       ret = write_stream(tree1, __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");
+
+       ret = enable_aapl(tctx, tree2);
+       torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
+
+       /*
+        * Issue a smb2_find(), this triggers the server-side conversion
+        */
+
+       create = (struct smb2_create) {
+               .in.desired_access = SEC_RIGHTS_DIR_READ,
+               .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+               .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+               .in.share_access = NTCREATEX_SHARE_ACCESS_READ,
+               .in.create_disposition = NTCREATEX_DISP_OPEN,
+               .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
+               .in.fname = BASEDIR,
+       };
+
+       status = smb2_create(tree2, tctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
+
+       find = (struct smb2_find) {
+               .in.file.handle = create.out.file.handle,
+               .in.pattern = "*",
+               .in.max_response_size = 0x1000,
+               .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+       };
+
+       status = smb2_find_level(tree2, tree2, &find, &count, &d);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_find_level failed\n");
+
+       status = smb2_util_close(tree2, create.out.file.handle);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_close failed");
+
+       /*
+        * Check number of streams
+        */
+
+       ret = check_stream_list(tree2, tctx, fname, 4, streams, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list");
+
+       /*
+        * Check Resource Fork is gone
+        */
+
+       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 = rfork,
+       };
+
+       status = smb2_create(tree2, mem_ctx, &create);
+       torture_assert_ntstatus_equal_goto(
+               tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+               ret, done, "Bad smb2_create return\n");
+
+       /*
+        * Check xattr data has been migrated from the AppleDouble file to
+        * streams.
+        */
+
+       ret = check_stream(tree2, __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(tree2, __location__, tctx, mem_ctx,
+                          fname, ":foo" "\xef\x80\xa2" "bar", /* foo:bar */
+                          0, 3, 0, 3, "baz");
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "check foo stream failed\n");
+
+       /*
+        * Now check number of files. If delete_empty_adfiles is set, the
+        * AppleDouble files should have been deleted.
+        */
+
+       create = (struct smb2_create) {
+               .in.desired_access = SEC_RIGHTS_DIR_READ,
+               .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+               .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+               .in.share_access = NTCREATEX_SHARE_ACCESS_READ,
+               .in.create_disposition = NTCREATEX_DISP_OPEN,
+               .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
+               .in.fname = BASEDIR,
+       };
+
+       status = smb2_create(tree2, tctx, &create);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
+
+       find = (struct smb2_find) {
+               .in.file.handle = create.out.file.handle,
+               .in.pattern = "*",
+               .in.max_response_size = 0x1000,
+               .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+       };
+
+       status = smb2_find_level(tree2, tree2, &find, &count, &d);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_find_level failed\n");
+
+       status = smb2_util_close(tree2, create.out.file.handle);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_close failed");
+
+       if (delete_empty_adfiles) {
+               expected_num_files = 3;
+       } else {
+               expected_num_files = 4;
+       }
+       torture_assert_int_equal_goto(tctx, count, expected_num_files, ret, done,
+                                     "Wrong number of files\n");
+
+done:
+       smb2_deltree(tree1, BASEDIR);
+       talloc_free(mem_ctx);
+       return ret;
+}
+
+struct torture_suite *torture_vfs_fruit_conversion(TALLOC_CTX *ctx)
+{
+       struct torture_suite *suite = torture_suite_create(
+               ctx, "fruit_conversion");
+
+       suite->description = talloc_strdup(
+               suite, "vfs_fruit conversion tests");
+
+       torture_suite_add_2ns_smb2_test(
+               suite, "convert_xattr_and_empty_rfork_then_delete",
+               test_convert_xattr_and_empty_rfork_then_delete);
+
+       return suite;
+}
+
+/*
+ * 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   : 00000002 : Resource Fork
+ * Offset     : 0000009A : 154
+ * Length     : 00000004 : 4
+ *
+ * -RAW DUMP--:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F : (ASCII)
+ * 00000000   : 62 61 72 00                                     : bar.
+ *
+ * -----------------------------------------------------------------------------
+ * Entry ID   : 00000009 : Finder Info
+ * Offset     : 00000032 : 50
+ * Length     : 00000068 : 104
+ *
+ * -FInfo-----:
+ * Type       : 464F4F20 : FOO
+ * Creator    : 42415220 : BAR
+ * 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      : 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
+ *
+ * -EA--------:
+ * pad        : 0000     :
+ * magic      : 41545452 : ATTR
+ * debug_tag  : 00000000 : 0
+ * total_size : 0000009A : 154
+ * data_start : 00000096 : 150
+ * data_length: 00000004 : 4
+ * reserved[0]: 00000000 : ....
+ * reserved[1]: 00000000 : ....
+ * reserved[2]: 00000000 : ....
+ * flags      : 0000     : ..
+ * num_attrs  : 0001     : 1
+ * -EA ENTRY--:
+ * offset     : 00000096 : 150
+ * length     : 00000004 : 4
+ * flags      : 0000     : ..
+ * namelen    : 13       : 19
+ * -EA NAME---:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F : (ASCII)
+ * 00000000   : 6F 72 67 2E 73 61 6D 62 61 EF 80 A2 77 6F 6F 68 : org.samba...wooh
+ * 00000010   : 6F 6F 00                                        : oo.
+ * -EA VALUE--:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F : (ASCII)
+ * 00000000   : 62 61 72 00                                     : bar.
+ *
+ * -RAW DUMP--:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F : (ASCII)
+ * 00000000   : 46 4F 4F 20 42 41 52 20 00 00 00 00 00 00 00 00 : FOO BAR ........
+ * 00000010   : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................
+ * 00000020   : 00 00 41 54 54 52 00 00 00 00 00 00 00 9A 00 00 : baATTR..........
+ * 00000030   : 00 96 00 00 00 04 00 00 00 00 00 00 00 00 00 00 : ................
+ * 00000040   : 00 00 00 00 00 01 00 00 00 96 00 00 00 04 00 00 : ................
+ * 00000050   : 13 6F 72 67 2E 73 61 6D 62 61 EF 80 A2 77 6F 6F : .org.samba...woo
+ * 00000060   : 68 6F 6F 00 62 61 72 00                         : hoo.bar.
+ *
+ * It was created with:
+ *
+ * $ hexdump -ve '"\t" 7/1 "0x%02x, " 1/1 " 0x%02x," "\n"'
+ */
+static char unconvert_adfile_data[] = {
+       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, 0x02, 0x00, 0x00,
+       0x00, 0x98, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+       0x00, 0x09, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
+       0x00, 0x66, 0x46, 0x4f, 0x4f, 0x20, 0x42, 0x41,
+       0x52, 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, 0x41, 0x54, 0x54, 0x52,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98,
+       0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x04,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+       0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x04,
+       0x00, 0x00, 0x11, 0x6f, 0x72, 0x67, 0x2e, 0x73,
+       0x61, 0x6d, 0x62, 0x61, 0x3a, 0x77, 0x6f, 0x6f,
+       0x68, 0x6f, 0x6f, 0x00, 0x62, 0x61, 0x72, 0x00,
+       0x62, 0x61, 0x72, 0x00
+};
+
+static bool test_unconvert(struct torture_context *tctx,
+                          struct smb2_tree *tree1,
+                          struct smb2_tree *tree2)
+{
+       const char *fname = BASEDIR "\\unconvert";
+       const char *adname = BASEDIR "\\._unconvert";
+       const char *net = NULL;
+       const char *share = NULL;
+       AfpInfo *afpi = NULL;
+       char *cmd = NULL;
+       struct smb2_handle h1;
+       union smb_fileinfo finfo;
+       size_t adsize;
+       NTSTATUS status;
+       int result;
+       bool ret = true;
+
+       torture_assert_not_null_goto(tctx, tree2, ret, done,
+                                    "Need a second share without fruit\n");
+
+       net = torture_setting_string(tctx, "net", NULL);
+       torture_assert_not_null_goto(tctx, net, ret, done,
+                                    "Need path to 'net'");
+
+       share = torture_setting_string(tctx, "sharename", NULL);
+       torture_assert_not_null_goto(tctx, share, ret, done,
+                                    "Need sharename");
+
+       torture_comment(tctx, "Testing unconvert\n");
+
+       smb2_deltree(tree1, BASEDIR);
+
+       status = torture_smb2_testdir(tree1, BASEDIR, &h1);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testdir\n");
+       smb2_util_close(tree1, h1);
+
+       ret = torture_setup_file(tctx, tree1, fname, false);
+       torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
+
+       afpi = torture_afpinfo_new(tctx);
+       torture_assert_not_null_goto(tctx, afpi, ret, done,
+                                    "torture_afpinfo_new failed\n");
+
+       memcpy(afpi->afpi_FinderInfo, "FOO BAR ", 8);
+
+       ret = torture_write_afpinfo(tree1, tctx, tctx, fname, afpi);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "torture_write_afpinfo failed\n");
+
+       ret = write_stream(tree1, __location__, tctx, tctx,
+                          fname,
+                          /*
+                           * \xef\x80\xa2 is ':' mapped to Unicoe private range
+                           */
+                          ":org.samba" "\xef\x80\xa2" "woohoo",
+                          0, 4, "bar");
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "write_stream failed\n");
+
+       ret = write_stream(tree1, __location__, tctx, tctx,
+                          fname, AFPRESOURCE_STREAM_NAME,
+                          0, 4, "bar");
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "write_stream failed\n");
+
+       cmd = talloc_asprintf(tctx,
+                             "%s --recursive vfs stream2adouble %s %s/",
+                             net,
+                             share,
+                             BASEDIR);
+       torture_assert_not_null_goto(tctx, cmd, ret, done,
+                                    "talloc_asprintf failed\n");
+
+       torture_comment(tctx, "cmd: %s\n", cmd);
+
+       result = system(cmd);
+       torture_assert_int_equal_goto(tctx, result, 0, ret, done,
+                           "command failed\n");
+
+       status = torture_smb2_testfile(tree2, adname, &h1);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testfile failed\n");
+
+       finfo = (union smb_fileinfo) {
+               .generic.level = RAW_FILEINFO_ALL_INFORMATION,
+               .generic.in.file.handle = h1,
+       };
+
+       status = smb2_getinfo_file(tree2, tctx, &finfo);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "torture_smb2_testdir\n");
+       smb2_util_close(tree2, h1);
+
+       adsize = finfo.all_info.out.size;
+       torture_assert_int_equal_goto(tctx, adsize,
+                                     sizeof(unconvert_adfile_data),
+                                     ret, done, "wrong size\n");
+
+       ret = check_stream(tree2, __location__, tctx, tctx,
+                          adname, "", 0, adsize, 0, adsize,
+                          unconvert_adfile_data);
+       torture_assert_goto(tctx, ret == true, ret, done,
+                           "check_stream failed\n");
+
+done:
+//     smb2_deltree(tree1, BASEDIR);
+       return ret;
+}
+
+struct torture_suite *torture_vfs_fruit_unfruit(TALLOC_CTX *ctx)
+{
+       struct torture_suite *suite = torture_suite_create(
+               ctx, "unfruit");
+
+       suite->description = talloc_strdup(
+               suite, "test converting back to AppleDouble");
+
+       torture_suite_add_2ns_smb2_test(suite,
+                                       "unconvert",
+                                       test_unconvert);
+
+       return suite;
+}