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