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