| // Copyright 2017 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package fs |
| |
| import ( |
| "os" |
| "reflect" |
| "runtime" |
| "testing" |
| ) |
| |
| func TestParseDirent(t *testing.T) { |
| testCases := []struct { |
| name string |
| in []byte |
| out []*dirEntryInfo |
| }{ |
| { |
| // Test that type DT_DIR is translated to os.ModeDir |
| name: "dir", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_paths", os.ModeDir, true}, |
| }, |
| }, |
| { |
| // Test that type DT_REG is translated to a regular file |
| name: "file", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x08, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_paths", 0, true}, |
| }, |
| }, |
| { |
| // Test that type DT_LNK is translated to a regular os.ModeSymlink |
| name: "symlink", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x0a, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_paths", os.ModeSymlink, true}, |
| }, |
| }, |
| { |
| // Test that type DT_UNKNOWN sets modeExists: false |
| name: "unknown", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x00, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_paths", 0, false}, |
| }, |
| }, |
| { |
| // Test a name with no padding after the null terminator |
| name: "no padding", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x20, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_path", os.ModeDir, true}, |
| }, |
| }, |
| { |
| // Test two sequential entries |
| name: "two entries", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x74, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_paths", os.ModeDir, true}, |
| {".module_patht", os.ModeDir, true}, |
| }, |
| }, |
| { |
| // Test two sequential entries with no padding between them |
| name: "two entries no padding", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x20, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, |
| |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_path", os.ModeDir, true}, |
| {".module_paths", os.ModeDir, true}, |
| }, |
| }, |
| { |
| // Test an empty buffer. This shouldn't happen in practice because |
| // readdir doesn't call parseDirent if no bytes were returned. |
| name: "empty", |
| in: []byte{}, |
| out: nil, |
| }, |
| { |
| name: "missing null terminator", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x20, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_paths", os.ModeDir, true}, |
| }, |
| }, |
| { |
| // Test two sequential entries where the first has an incorrect d_reclen. |
| // Should return with no entries. |
| name: "two entries first malformed", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x10, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, |
| |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }, |
| out: nil, |
| }, |
| { |
| // Test two sequential entries where the second has an incorrect d_reclen. |
| // Should return the first entry. |
| name: "two entries second malformed", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x28, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, |
| |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x10, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }, |
| out: []*dirEntryInfo{ |
| {".module_path", os.ModeDir, true}, |
| }, |
| }, |
| { |
| // Test a reclen that goes past the end of the buffer. |
| name: "overrun", |
| in: []byte{ |
| // __ino64_t d_ino; |
| 0xfb, 0x10, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // __off64_t d_off; |
| 0xeb, 0x85, 0x20, 0x91, 0xb9, 0x14, 0x34, 0x03, |
| // unsigned short int d_reclen; |
| 0x30, 0x00, |
| // unsigned char d_type; |
| 0x04, |
| // char d_name[]; |
| 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, |
| }, |
| out: nil, |
| }, |
| } |
| |
| if runtime.GOOS != "linux" { |
| t.Skip("depends on Linux definitions of syscall.Dirent") |
| } |
| |
| for _, testCase := range testCases { |
| t.Run(testCase.name, func(t *testing.T) { |
| entries := parseDirent(testCase.in, nil) |
| if !reflect.DeepEqual(testCase.out, entries) { |
| t.Fatalf("expected:\n %v\ngot:\n %v\n", testCase.out, entries) |
| } |
| }) |
| } |
| } |