libsmb: Use clistr_smb2_extract_snapshot_token() in cli_smb2_create_fnum_send()
[Samba.git] / python / samba / tests / smb2symlink.py
blob14f6056ac15b81f8c782369e73a4d5460d97633f
1 # Unix SMB/CIFS implementation.
2 # Copyright Volker Lendecke <vl@samba.org> 2022
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 from samba.samba3 import libsmb_samba_internal as libsmb
19 from samba import reparse_symlink
20 from samba import (ntstatus,NTSTATUSError)
21 from samba.dcerpc import security as sec
22 import samba.tests.libsmb
24 class Smb2SymlinkTests(samba.tests.libsmb.LibsmbTests):
26 def connections(self):
27 share = samba.tests.env_get_var_value(
28 "SMB1_SHARE", allow_missing=True)
29 if not share:
30 share = "nosymlinks_smb1allow"
32 try:
33 smb1 = libsmb.Conn(
34 self.server_ip,
35 share,
36 self.lp,
37 self.creds)
38 except NTSTATUSError as e:
39 if e.args[0] != ntstatus.NT_STATUS_CONNECTION_RESET:
40 raise
42 share = samba.tests.env_get_var_value(
43 "SMB2_SHARE", allow_missing=True)
44 if not share:
45 share = "nosymlinks"
46 smb2 = libsmb.Conn(
47 self.server_ip,
48 share,
49 self.lp,
50 self.creds)
51 return (smb1, smb2)
53 def clean_file(self, conn, filename):
54 try:
55 conn.unlink(filename)
56 except NTSTATUSError as e:
57 if e.args[0] != ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND:
58 raise
60 def create_symlink(self, conn, target, symlink):
61 self.clean_file(conn, symlink)
62 if (conn.protocol() < libsmb.PROTOCOL_SMB2_02 and conn.have_posix()):
63 conn.smb1_symlink(target, symlink)
64 else:
65 flags = 0 if target[0]=='/' else 1
66 syml = conn.create(
67 symlink,
68 DesiredAccess=sec.SEC_FILE_READ_ATTRIBUTE|
69 sec.SEC_FILE_WRITE_ATTRIBUTE|
70 sec.SEC_STD_DELETE,
71 FileAttributes=libsmb.FILE_ATTRIBUTE_NORMAL,
72 CreateDisposition=libsmb.FILE_OPEN_IF,
73 CreateOptions=libsmb.FILE_OPEN_REPARSE_POINT)
74 b = reparse_symlink.symlink_put(target, target, 0, 1)
75 conn.fsctl(syml, libsmb.FSCTL_SET_REPARSE_POINT, b, 0)
76 conn.close(syml)
78 def assert_symlink_exception(self, e, expect):
79 self.assertEqual(e.args[0], ntstatus.NT_STATUS_STOPPED_ON_SYMLINK)
80 for k,v in expect.items():
81 if (k == "flags"):
82 # Ignore symlink trust flags for now
83 expected = v & ~libsmb.SYMLINK_TRUST_MASK
84 got = e.args[2].get(k) & ~libsmb.SYMLINK_TRUST_MASK
85 self.assertEqual((k,got), (k,expected))
86 else:
87 self.assertEqual((k,e.args[2].get(k)), (k,v))
89 def test_symlinkerror_directory(self):
90 """Test a symlink in a nonterminal path component"""
91 (smb1,smb2) = self.connections()
92 symlink="syml"
93 target="foo"
94 suffix="bar"
96 self.create_symlink(smb1, target, symlink);
98 with self.assertRaises(NTSTATUSError) as e:
99 fd = smb2.create_ex(f'{symlink}\\{suffix}')
101 self.assert_symlink_exception(
102 e.exception,
103 { 'unparsed_path_length' : len(suffix)+1,
104 'substitute_name' : target,
105 'print_name' : target,
106 'flags' : 0x20000001 })
108 self.clean_file(smb1, symlink)
110 def test_symlinkerror_file(self):
111 """Test a simple symlink in a terminal path"""
112 (smb1,smb2) = self.connections()
113 symlink="syml"
114 target="foo"
116 self.create_symlink(smb1, target, symlink);
118 with self.assertRaises(NTSTATUSError) as e:
119 fd = smb2.create_ex(f'{symlink}')
121 self.assert_symlink_exception(
122 e.exception,
123 { 'unparsed_path_length' : 0,
124 'substitute_name' : target,
125 'print_name' : target,
126 'flags' : 0x20000001 })
128 self.clean_file(smb1, symlink)
130 def test_symlinkerror_absolute_outside_share(self):
132 Test symlinks to outside of the share
133 We return the contents 1:1
135 (smb1,smb2) = self.connections()
136 symlink="syml"
138 for target in ["/etc", "//foo/bar", "/"]:
140 self.create_symlink(smb1, target, symlink)
142 with self.assertRaises(NTSTATUSError) as e:
143 fd = smb2.create_ex(f'{symlink}')
145 self.assert_symlink_exception(
146 e.exception,
147 { 'unparsed_path_length' : 0,
148 'substitute_name' : target,
149 'print_name' : target,
150 'flags' : 0 })
152 self.clean_file(smb1, symlink)
154 def test_symlinkerror_absolute_inshare(self):
155 """Test an absolute symlink inside the share"""
156 (smb1,smb2) = self.connections()
157 symlink="syml"
159 localpath=samba.tests.env_get_var_value("LOCAL_PATH")
160 shareroot=f'{localpath}/nosymlinks'
161 rel_dest="dst"
162 target=f'{shareroot}/{rel_dest}'
164 self.create_symlink(smb1, target, symlink)
166 with self.assertRaises(NTSTATUSError) as e:
167 fd = smb2.create_ex(f'{symlink}')
169 self.assert_symlink_exception(
170 e.exception,
171 { 'unparsed_path_length' : 0,
172 'substitute_name' : rel_dest,
173 'print_name' : rel_dest,
174 'flags' : 0 })
176 self.clean_file(smb1, symlink)
178 if __name__ == '__main__':
179 import unittest
180 unittest.main()