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        old_umask = os.umask(0o22)
106        try:
107            server = self.connect_to_debug_monitor()
108        finally:
109            os.umask(old_umask)
110        self.assertIsNotNone(server)
111
112        # create a temporary file with some data
113        test_data = 'some test data longer than 16 bytes\n'
114        if creat:
115            temp_dir = tempfile.TemporaryDirectory()
116        else:
117            temp_file = tempfile.NamedTemporaryFile()
118
119        try:
120            if creat:
121                temp_path = os.path.join(temp_dir.name, "test")
122                self.assertFalse(os.path.exists(temp_path))
123            else:
124                temp_file.write(test_data.encode())
125                temp_file.flush()
126                temp_path = temp_file.name
127
128            # open the file for reading
129            self.do_handshake()
130            self.test_sequence.add_log_lines(
131                ["read packet: $vFile:open:%s,%x,1a0#00" % (
132                    binascii.b2a_hex(temp_path.encode()).decode(),
133                    mode),
134                 {"direction": "send",
135                 "regex": r"^\$F([0-9a-fA-F]+)#[0-9a-fA-F]{2}$",
136                 "capture": {1: "fd"}}],
137                True)
138
139            context = self.expect_gdbremote_sequence()
140            self.assertIsNotNone(context)
141            fd = int(context["fd"], 16)
142
143            # read data from the file
144            self.reset_test_sequence()
145            self.test_sequence.add_log_lines(
146                ["read packet: $vFile:pread:%x,11,10#00" % (fd,)],
147                True)
148            if read:
149                self.test_sequence.add_log_lines(
150                    [{"direction": "send",
151                     "regex": r"^\$F([0-9a-fA-F]+);(.*)#[0-9a-fA-F]{2}$",
152                     "capture": {1: "size", 2: "data"}}],
153                    True)
154                context = self.expect_gdbremote_sequence()
155                self.assertIsNotNone(context)
156                if trunc:
157                    self.assertEqual(context["size"], "0")
158                    self.assertEqual(context["data"], "")
159                else:
160                    self.assertEqual(context["size"], "11")  # hex
161                    self.assertEqual(context["data"], test_data[0x10:0x10 + 0x11])
162            else:
163                self.expect_error()
164
165            # another offset
166            if read and not trunc:
167                self.reset_test_sequence()
168                self.test_sequence.add_log_lines(
169                    ["read packet: $vFile:pread:%x,6,3#00" % (fd,),
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                self.assertEqual(context["size"], "6")  # hex
177                self.assertEqual(context["data"], test_data[3:3 + 6])
178
179            # write data to the file
180            self.reset_test_sequence()
181            self.test_sequence.add_log_lines(
182                ["read packet: $vFile:pwrite:%x,6,somedata#00" % (fd,)],
183                True)
184            if write:
185                self.test_sequence.add_log_lines(
186                    ["send packet: $F8#00"],
187                    True)
188                self.expect_gdbremote_sequence()
189            else:
190                self.expect_error()
191
192            # close the file
193            self.reset_test_sequence()
194            self.test_sequence.add_log_lines(
195                ["read packet: $vFile:close:%x#00" % (fd,),
196                 "send packet: $F0#00"],
197                True)
198            self.expect_gdbremote_sequence()
199
200            if write:
201                # check if the data was actually written
202                if creat:
203                    temp_file = open(temp_path, "rb")
204                    self.assertEqual(os.fstat(temp_file.fileno()).st_mode & 0o7777,
205                                     0o640)
206                temp_file.seek(0)
207                data = test_data.encode()
208                if trunc or creat:
209                    data = b"\0" * 6 + b"somedata"
210                elif append:
211                    data += b"somedata"
212                else:
213                    data = data[:6] + b"somedata" + data[6 + 8:]
214                self.assertEqual(temp_file.read(), data)
215        finally:
216            if creat:
217                temp_dir.cleanup()
218            else:
219                temp_file.close()
220