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