1// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package fs
16
17import (
18	"os"
19	"reflect"
20	"runtime"
21	"testing"
22)
23
24func TestParseDirent(t *testing.T) {
25	testCases := []struct {
26		name string
27		in   []byte
28		out  []*dirEntryInfo
29	}{
30		{
31			// Test that type DT_DIR is translated to os.ModeDir
32			name: "dir",
33			in: []byte{
34				// __ino64_t d_ino;
35				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
36				// __off64_t d_off;
37				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
38				// unsigned short int d_reclen;
39				0x28, 0x00,
40				// unsigned char d_type;
41				0x04,
42				// char d_name[];
43				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
44				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45			},
46			out: []*dirEntryInfo{
47				{".module_paths", os.ModeDir, true},
48			},
49		},
50		{
51			// Test that type DT_REG is translated to a regular file
52			name: "file",
53			in: []byte{
54				// __ino64_t d_ino;
55				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
56				// __off64_t d_off;
57				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
58				// unsigned short int d_reclen;
59				0x28, 0x00,
60				// unsigned char d_type;
61				0x08,
62				// char d_name[];
63				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
64				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65			},
66			out: []*dirEntryInfo{
67				{".module_paths", 0, true},
68			},
69		},
70		{
71			// Test that type DT_LNK is translated to a regular os.ModeSymlink
72			name: "symlink",
73			in: []byte{
74				// __ino64_t d_ino;
75				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
76				// __off64_t d_off;
77				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
78				// unsigned short int d_reclen;
79				0x28, 0x00,
80				// unsigned char d_type;
81				0x0a,
82				// char d_name[];
83				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
84				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85			},
86			out: []*dirEntryInfo{
87				{".module_paths", os.ModeSymlink, true},
88			},
89		},
90		{
91			// Test that type DT_UNKNOWN sets modeExists: false
92			name: "unknown",
93			in: []byte{
94				// __ino64_t d_ino;
95				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
96				// __off64_t d_off;
97				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
98				// unsigned short int d_reclen;
99				0x28, 0x00,
100				// unsigned char d_type;
101				0x00,
102				// char d_name[];
103				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
104				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105			},
106			out: []*dirEntryInfo{
107				{".module_paths", 0, false},
108			},
109		},
110		{
111			// Test a name with no padding after the null terminator
112			name: "no padding",
113			in: []byte{
114				// __ino64_t d_ino;
115				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
116				// __off64_t d_off;
117				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
118				// unsigned short int d_reclen;
119				0x20, 0x00,
120				// unsigned char d_type;
121				0x04,
122				// char d_name[];
123				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
124			},
125			out: []*dirEntryInfo{
126				{".module_path", os.ModeDir, true},
127			},
128		},
129		{
130			// Test two sequential entries
131			name: "two entries",
132			in: []byte{
133				// __ino64_t d_ino;
134				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
135				// __off64_t d_off;
136				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
137				// unsigned short int d_reclen;
138				0x28, 0x00,
139				// unsigned char d_type;
140				0x04,
141				// char d_name[];
142				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
143				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144
145				// __ino64_t d_ino;
146				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
147				// __off64_t d_off;
148				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
149				// unsigned short int d_reclen;
150				0x28, 0x00,
151				// unsigned char d_type;
152				0x04,
153				// char d_name[];
154				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x74,
155				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156			},
157			out: []*dirEntryInfo{
158				{".module_paths", os.ModeDir, true},
159				{".module_patht", os.ModeDir, true},
160			},
161		},
162		{
163			// Test two sequential entries with no padding between them
164			name: "two entries no padding",
165			in: []byte{
166				// __ino64_t d_ino;
167				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
168				// __off64_t d_off;
169				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
170				// unsigned short int d_reclen;
171				0x20, 0x00,
172				// unsigned char d_type;
173				0x04,
174				// char d_name[];
175				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
176
177				// __ino64_t d_ino;
178				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
179				// __off64_t d_off;
180				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
181				// unsigned short int d_reclen;
182				0x28, 0x00,
183				// unsigned char d_type;
184				0x04,
185				// char d_name[];
186				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
187				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
188			},
189			out: []*dirEntryInfo{
190				{".module_path", os.ModeDir, true},
191				{".module_paths", os.ModeDir, true},
192			},
193		},
194		{
195			// Test an empty buffer.  This shouldn't happen in practice because
196			// readdir doesn't call parseDirent if no bytes were returned.
197			name: "empty",
198			in:   []byte{},
199			out:  nil,
200		},
201		{
202			name: "missing null terminator",
203			in: []byte{
204				// __ino64_t d_ino;
205				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
206				// __off64_t d_off;
207				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
208				// unsigned short int d_reclen;
209				0x20, 0x00,
210				// unsigned char d_type;
211				0x04,
212				// char d_name[];
213				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
214			},
215			out: []*dirEntryInfo{
216				{".module_paths", os.ModeDir, true},
217			},
218		},
219		{
220			// Test two sequential entries where the first has an incorrect d_reclen.
221			// Should return with no entries.
222			name: "two entries first malformed",
223			in: []byte{
224				// __ino64_t d_ino;
225				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
226				// __off64_t d_off;
227				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
228				// unsigned short int d_reclen;
229				0x10, 0x00,
230				// unsigned char d_type;
231				0x04,
232				// char d_name[];
233				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
234
235				// __ino64_t d_ino;
236				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
237				// __off64_t d_off;
238				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
239				// unsigned short int d_reclen;
240				0x28, 0x00,
241				// unsigned char d_type;
242				0x04,
243				// char d_name[];
244				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
245				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246			},
247			out: nil,
248		},
249		{
250			// Test two sequential entries where the second has an incorrect d_reclen.
251			// Should return the first entry.
252			name: "two entries second malformed",
253			in: []byte{
254				// __ino64_t d_ino;
255				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
256				// __off64_t d_off;
257				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
258				// unsigned short int d_reclen;
259				0x28, 0x00,
260				// unsigned char d_type;
261				0x04,
262				// char d_name[];
263				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
264
265				// __ino64_t d_ino;
266				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
267				// __off64_t d_off;
268				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
269				// unsigned short int d_reclen;
270				0x10, 0x00,
271				// unsigned char d_type;
272				0x04,
273				// char d_name[];
274				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73,
275				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
276			},
277			out: []*dirEntryInfo{
278				{".module_path", os.ModeDir, true},
279			},
280		},
281		{
282			// Test a reclen that goes past the end of the buffer.
283			name: "overrun",
284			in: []byte{
285				// __ino64_t d_ino;
286				0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
287				// __off64_t d_off;
288				0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03,
289				// unsigned short int d_reclen;
290				0x30, 0x00,
291				// unsigned char d_type;
292				0x04,
293				// char d_name[];
294				0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
295			},
296			out: nil,
297		},
298	}
299
300	if runtime.GOOS != "linux" {
301		t.Skip("depends on Linux definitions of syscall.Dirent")
302	}
303
304	for _, testCase := range testCases {
305		t.Run(testCase.name, func(t *testing.T) {
306			entries := parseDirent(testCase.in, nil)
307			if !reflect.DeepEqual(testCase.out, entries) {
308				t.Fatalf("expected:\n %v\ngot:\n %v\n", testCase.out, entries)
309			}
310		})
311	}
312}
313