1from __future__ import print_function
2
3# lldb test suite imports
4from lldbsuite.test.decorators import *
5from lldbsuite.test.lldbtest import TestBase
6
7# gdb-remote-specific imports
8import lldbgdbserverutils
9from gdbremote_testcase import GdbRemoteTestCaseBase
10
11import binascii
12import stat
13import tempfile
14
15
16class TestGdbRemotePlatformFile(GdbRemoteTestCaseBase):
17
18    mydir = TestBase.compute_mydir(__file__)
19
20    @expectedFailureAll(oslist=["windows"])
21    def test_platform_file_rdonly(self):
22        self.vFile_test(read=True)
23
24    @expectedFailureAll(oslist=["windows"])
25    def test_platform_file_wronly(self):
26        self.vFile_test(write=True)
27
28    @expectedFailureAll(oslist=["windows"])
29    def test_platform_file_rdwr(self):
30        self.vFile_test(read=True, write=True)
31
32    @expectedFailureAll(oslist=["windows"])
33    def test_platform_file_wronly_append(self):
34        self.vFile_test(write=True, append=True)
35
36    @expectedFailureAll(oslist=["windows"])
37    def test_platform_file_rdwr_append(self):
38        self.vFile_test(read=True, write=True, append=True)
39
40    @expectedFailureAll(oslist=["windows"])
41    def test_platform_file_wronly_trunc(self):
42        self.vFile_test(write=True, trunc=True)
43
44    @expectedFailureAll(oslist=["windows"])
45    def test_platform_file_rdwr_trunc(self):
46        self.vFile_test(read=True, write=True, trunc=True)
47
48    @expectedFailureAll(oslist=["windows"])
49    def test_platform_file_wronly_creat(self):
50        self.vFile_test(write=True, creat=True)
51
52    @expectedFailureAll(oslist=["windows"])
53    def test_platform_file_wronly_creat_excl(self):
54        self.vFile_test(write=True, creat=True, excl=True)
55
56    def test_platform_file_wronly_fail(self):
57        server = self.connect_to_debug_monitor()
58        self.assertIsNotNone(server)
59
60        # create a temporary directory
61        with tempfile.TemporaryDirectory() as temp_dir:
62            temp_path = os.path.join(temp_dir, "test")
63            self.assertFalse(os.path.exists(temp_path))
64
65            # attempt to open the file without O_CREAT
66            self.do_handshake()
67            self.test_sequence.add_log_lines(
68                ["read packet: $vFile:open:%s,1,0#00" % (
69                    binascii.b2a_hex(temp_path.encode()).decode(),),
70                 {"direction": "send",
71                 "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}],
72                True)
73            self.expect_gdbremote_sequence()
74
75    def test_platform_file_wronly_creat_excl_fail(self):
76        server = self.connect_to_debug_monitor()
77        self.assertIsNotNone(server)
78
79        with tempfile.NamedTemporaryFile() as temp_file:
80            # attempt to open the file with O_CREAT|O_EXCL
81            self.do_handshake()
82            self.test_sequence.add_log_lines(
83                ["read packet: $vFile:open:%s,a01,0#00" % (
84                    binascii.b2a_hex(temp_file.name.encode()).decode(),),
85                 {"direction": "send",
86                 "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}],
87                True)
88            self.expect_gdbremote_sequence()
89
90    def expect_error(self):
91        self.test_sequence.add_log_lines(
92            [{"direction": "send",
93             "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}],
94            True)
95        self.expect_gdbremote_sequence()
96
97    def vFile_test(self, read=False, write=False, append=False, trunc=False,
98                   creat=False, excl=False):
99        if read and write:
100            mode = 2
101        elif write:
102            mode = 1
103        else:  # read
104            mode = 0
105        if append:
106            mode |= 8
107        if creat:
108            mode |= 0x200
109        if trunc:
110            mode |= 0x400
111        if excl:
112            mode |= 0x800
113
114        old_umask = os.umask(0o22)
115        try:
116            server = self.connect_to_debug_monitor()
117        finally:
118            os.umask(old_umask)
119        self.assertIsNotNone(server)
120
121        # create a temporary file with some data
122        test_data = 'some test data longer than 16 bytes\n'
123        if creat:
124            temp_dir = tempfile.TemporaryDirectory()
125        else:
126            temp_file = tempfile.NamedTemporaryFile()
127
128        try:
129            if creat:
130                temp_path = os.path.join(temp_dir.name, "test")
131                self.assertFalse(os.path.exists(temp_path))
132            else:
133                temp_file.write(test_data.encode())
134                temp_file.flush()
135                temp_path = temp_file.name
136
137            # open the file for reading
138            self.do_handshake()
139            self.test_sequence.add_log_lines(
140                ["read packet: $vFile:open:%s,%x,1a0#00" % (
141                    binascii.b2a_hex(temp_path.encode()).decode(),
142                    mode),
143                 {"direction": "send",
144                 "regex": r"^\$F([0-9a-fA-F]+)#[0-9a-fA-F]{2}$",
145                 "capture": {1: "fd"}}],
146                True)
147
148            context = self.expect_gdbremote_sequence()
149            self.assertIsNotNone(context)
150            fd = int(context["fd"], 16)
151
152            # read data from the file
153            self.reset_test_sequence()
154            self.test_sequence.add_log_lines(
155                ["read packet: $vFile:pread:%x,11,10#00" % (fd,)],
156                True)
157            if read:
158                self.test_sequence.add_log_lines(
159                    [{"direction": "send",
160                     "regex": r"^\$F([0-9a-fA-F]+);(.*)#[0-9a-fA-F]{2}$",
161                     "capture": {1: "size", 2: "data"}}],
162                    True)
163                context = self.expect_gdbremote_sequence()
164                self.assertIsNotNone(context)
165                if trunc:
166                    self.assertEqual(context["size"], "0")
167                    self.assertEqual(context["data"], "")
168                else:
169                    self.assertEqual(context["size"], "11")  # hex
170                    self.assertEqual(context["data"], test_data[0x10:0x10 + 0x11])
171            else:
172                self.expect_error()
173
174            # another offset
175            if read and not trunc:
176                self.reset_test_sequence()
177                self.test_sequence.add_log_lines(
178                    ["read packet: $vFile:pread:%x,6,3#00" % (fd,),
179                     {"direction": "send",
180                     "regex": r"^\$F([0-9a-fA-F]+);(.+)#[0-9a-fA-F]{2}$",
181                     "capture": {1: "size", 2: "data"}}],
182                    True)
183                context = self.expect_gdbremote_sequence()
184                self.assertIsNotNone(context)
185                self.assertEqual(context["size"], "6")  # hex
186                self.assertEqual(context["data"], test_data[3:3 + 6])
187
188            # write data to the file
189            self.reset_test_sequence()
190            self.test_sequence.add_log_lines(
191                ["read packet: $vFile:pwrite:%x,6,somedata#00" % (fd,)],
192                True)
193            if write:
194                self.test_sequence.add_log_lines(
195                    ["send packet: $F8#00"],
196                    True)
197                self.expect_gdbremote_sequence()
198            else:
199                self.expect_error()
200
201            # close the file
202            self.reset_test_sequence()
203            self.test_sequence.add_log_lines(
204                ["read packet: $vFile:close:%x#00" % (fd,),
205                 "send packet: $F0#00"],
206                True)
207            self.expect_gdbremote_sequence()
208
209            if write:
210                # check if the data was actually written
211                if creat:
212                    temp_file = open(temp_path, "rb")
213                    self.assertEqual(os.fstat(temp_file.fileno()).st_mode & 0o7777,
214                                     0o640)
215                temp_file.seek(0)
216                data = test_data.encode()
217                if trunc or creat:
218                    data = b"\0" * 6 + b"somedata"
219                elif append:
220                    data += b"somedata"
221                else:
222                    data = data[:6] + b"somedata" + data[6 + 8:]
223                self.assertEqual(temp_file.read(), data)
224        finally:
225            if creat:
226                temp_dir.cleanup()
227            else:
228                temp_file.close()
229