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