diff --git a/api/swagger.yaml b/api/swagger.yaml index 52f7712..33f477d 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -58,12 +58,12 @@ components: description: The path of the file. type: string example: ./logs/last.log - objectType: - description: The type of the object (file). + entryType: + description: The type of the object (file). See the man page `info ls` for all the meanings. type: string minLength: 1 maxLength: 1 - enum: ["d", "l", "-"] + enum: ["-", "a", "A", "b", "c", "C", "d", "D", "l", "M", "n", "p", "P", "s", "w", "?"] default: "-" size: description: The size of the file in bytes. diff --git a/pkg/dto/dto.go b/pkg/dto/dto.go index 8ad29dd..702d767 100644 --- a/pkg/dto/dto.go +++ b/pkg/dto/dto.go @@ -114,12 +114,25 @@ type UpdateFileSystemRequest struct { // FilePath specifies the path of a file and is part of the UpdateFileSystemRequest. type FilePath string +// EntryType specifies the type of the object (file/link/directory/..) +type EntryType string + +// These are the common entry types. You find others in the man pages `info ls`. +const ( + EntryTypeRegularFile EntryType = "-" + EntryTypeLink EntryType = "l" +) + // FileHeader specifies the information provided for listing a File. type FileHeader struct { - Name FilePath `json:"name"` - ObjectType string `json:"objectType"` - Size int `json:"size"` - ModificationTime int `json:"modificationTime"` + Name FilePath `json:"name"` + EntryType EntryType `json:"entryType"` + LinkTarget FilePath `json:"linkTarget,omitempty"` + Size int `json:"size"` + ModificationTime int `json:"modificationTime"` + Permissions string `json:"permissions"` + Owner string `json:"owner"` + Group string `json:"group"` } // File is a DTO for transmitting file contents. It is part of the UpdateFileSystemRequest. diff --git a/pkg/nullio/ls2json.go b/pkg/nullio/ls2json.go index f0e5789..8a30ab4 100644 --- a/pkg/nullio/ls2json.go +++ b/pkg/nullio/ls2json.go @@ -9,12 +9,13 @@ import ( "io" "regexp" "strconv" + "strings" ) var ( log = logging.GetLogger("nullio") pathLineRegex = regexp.MustCompile(`(.*):$`) - headerLineRegex = regexp.MustCompile(`([dl-])[-rwxXsS]{9} \d* .*? .*? +(\d+) (\d+) (.*)$`) + headerLineRegex = regexp.MustCompile(`([-aAbcCdDlMnpPsw?])([-rwxXsStT]{9})([+ ])\d+ (.+?) (.+?) +(\d+) (\d+) (.*)$`) ) // Ls2JsonWriter implements io.Writer. @@ -93,20 +94,9 @@ func (w *Ls2JsonWriter) writeLine(line []byte) (count int, err error) { matches = headerLineRegex.FindSubmatch(line) if matches != nil { - size, err1 := strconv.Atoi(string(matches[2])) - timestamp, err2 := strconv.Atoi(string(matches[3])) - if err1 != nil || err2 != nil { - return 0, fmt.Errorf("could not parse file details: %w %+v", err1, err2) - } - - response, err1 := json.Marshal(dto.FileHeader{ - Name: dto.FilePath(append(w.latestPath, matches[4]...)), - ObjectType: string(matches[1][0]), - Size: size, - ModificationTime: timestamp, - }) + response, err1 := w.parseFileHeader(matches) if err1 != nil { - return 0, fmt.Errorf("could not marshal file header: %w", err) + return 0, err1 } // Skip the first leading comma @@ -118,7 +108,7 @@ func (w *Ls2JsonWriter) writeLine(line []byte) (count int, err error) { count, err1 = w.Target.Write(response) if err1 != nil { - err = fmt.Errorf("could not write to target: %w", err) + err = fmt.Errorf("could not write to target: %w", err1) } else if count == len(response) { count = len(line) } @@ -126,3 +116,48 @@ func (w *Ls2JsonWriter) writeLine(line []byte) (count int, err error) { return count, err } + +func (w *Ls2JsonWriter) parseFileHeader(matches [][]byte) ([]byte, error) { + entryType := dto.EntryType(matches[1][0]) + permissions := string(matches[2]) + acl := string(matches[3]) + if acl == "+" { + permissions += "+" + } + + owner := string(matches[4]) + group := string(matches[5]) + size, err1 := strconv.Atoi(string(matches[6])) + timestamp, err2 := strconv.Atoi(string(matches[7])) + if err1 != nil || err2 != nil { + return nil, fmt.Errorf("could not parse file details: %w %+v", err1, err2) + } + + name := dto.FilePath(append(w.latestPath, matches[8]...)) + linkTarget := dto.FilePath("") + if entryType == dto.EntryTypeLink { + parts := strings.Split(string(name), " -> ") + const NumberOfPartsInALink = 2 + if len(parts) == NumberOfPartsInALink { + name = dto.FilePath(parts[0]) + linkTarget = dto.FilePath(parts[1]) + } else { + log.Error("could not split link into name and target") + } + } + + response, err := json.Marshal(dto.FileHeader{ + Name: name, + EntryType: entryType, + LinkTarget: linkTarget, + Size: size, + ModificationTime: timestamp, + Permissions: permissions, + Owner: owner, + Group: group, + }) + if err != nil { + return nil, fmt.Errorf("could not marshal file header: %w", err) + } + return response, nil +} diff --git a/pkg/nullio/ls2json_test.go b/pkg/nullio/ls2json_test.go index 8287e8c..efd5ce7 100644 --- a/pkg/nullio/ls2json_test.go +++ b/pkg/nullio/ls2json_test.go @@ -39,7 +39,8 @@ func (s *Ls2JsonTestSuite) TestLs2JsonWriter_WriteFile() { s.NoError(err) s.writer.Close() - s.Equal("{\"files\": [{\"name\":\"flag\",\"objectType\":\"-\",\"size\":0,\"modificationTime\":1660763446}]}", + s.Equal("{\"files\": [{\"name\":\"flag\",\"entryType\":\"-\",\"size\":0,\"modificationTime\":1660763446"+ + ",\"permissions\":\"rw-rw-r--\",\"owner\":\"kali\",\"group\":\"kali\"}]}", s.buf.String()) } @@ -52,23 +53,40 @@ func (s *Ls2JsonTestSuite) TestLs2JsonWriter_WriteRecursive() { s.NoError(err) s.writer.Close() - s.Equal("{\"files\": [{\"name\":\"./dir\",\"objectType\":\"d\",\"size\":4096,\"modificationTime\":1660764411},"+ - "{\"name\":\"./flag\",\"objectType\":\"-\",\"size\":0,\"modificationTime\":1660763446},"+ - "{\"name\":\"./dir/another.txt\",\"objectType\":\"-\",\"size\":3,\"modificationTime\":1660764366}]}", + s.Equal("{\"files\": ["+ + "{\"name\":\"./dir\",\"entryType\":\"d\",\"size\":4096,\"modificationTime\":1660764411,"+ + "\"permissions\":\"rwxrwxr-x\",\"owner\":\"kali\",\"group\":\"kali\"},"+ + "{\"name\":\"./flag\",\"entryType\":\"-\",\"size\":0,\"modificationTime\":1660763446,"+ + "\"permissions\":\"rw-rw-r--\",\"owner\":\"kali\",\"group\":\"kali\"},"+ + "{\"name\":\"./dir/another.txt\",\"entryType\":\"-\",\"size\":3,\"modificationTime\":1660764366,"+ + "\"permissions\":\"rw-rw-r--\",\"owner\":\"kali\",\"group\":\"kali\"}"+ + "]}", s.buf.String()) } func (s *Ls2JsonTestSuite) TestLs2JsonWriter_WriteRemaining() { - input1 := "total 4\n-rw-rw-r-- 1 kali kali 3 1660764366 another.txt\n-rw-rw-r-- 1 kal" + input1 := "total 4\n-rw-rw-r-- 1 kali kali 3 1660764366 an.txt\n-rw-rw-r-- 1 kal" _, err := s.writer.Write([]byte(input1)) s.NoError(err) - s.Equal("{\"files\": [{\"name\":\"another.txt\",\"objectType\":\"-\",\"size\":3,\"modificationTime\":1660764366}", - s.buf.String()) + s.Equal("{\"files\": [{\"name\":\"an.txt\",\"entryType\":\"-\",\"size\":3,\"modificationTime\":1660764366,"+ + "\"permissions\":\"rw-rw-r--\",\"owner\":\"kali\",\"group\":\"kali\"}", s.buf.String()) input2 := "i kali 0 1660763446 flag\n" _, err = s.writer.Write([]byte(input2)) s.NoError(err) s.writer.Close() - s.Equal("{\"files\": [{\"name\":\"another.txt\",\"objectType\":\"-\",\"size\":3,\"modificationTime\":1660764366},"+ - "{\"name\":\"flag\",\"objectType\":\"-\",\"size\":0,\"modificationTime\":1660763446}]}", s.buf.String()) + s.Equal("{\"files\": [{\"name\":\"an.txt\",\"entryType\":\"-\",\"size\":3,\"modificationTime\":1660764366,"+ + "\"permissions\":\"rw-rw-r--\",\"owner\":\"kali\",\"group\":\"kali\"},"+ + "{\"name\":\"flag\",\"entryType\":\"-\",\"size\":0,\"modificationTime\":1660763446,"+ + "\"permissions\":\"rw-rw-r--\",\"owner\":\"kali\",\"group\":\"kali\"}]}", s.buf.String()) +} + +func (s *Ls2JsonTestSuite) TestLs2JsonWriter_WriteLink() { + input1 := "total 4\nlrw-rw-r-- 1 kali kali 3 1660764366 another.txt -> /bin/bash\n" + _, err := s.writer.Write([]byte(input1)) + s.NoError(err) + s.writer.Close() + s.Equal("{\"files\": [{\"name\":\"another.txt\",\"entryType\":\"l\",\"linkTarget\":\"/bin/bash\",\"size\":3,"+ + "\"modificationTime\":1660764366,\"permissions\":\"rw-rw-r--\",\"owner\":\"kali\",\"group\":\"kali\"}]}", + s.buf.String()) } diff --git a/tests/e2e/runners_test.go b/tests/e2e/runners_test.go index acd72ad..9c6cfcf 100644 --- a/tests/e2e/runners_test.go +++ b/tests/e2e/runners_test.go @@ -155,8 +155,10 @@ func (s *E2ETestSuite) TestListFileSystem_Nomad() { s.Require().Equal(len(listFilesResponse.Files), 1) fileHeader := listFilesResponse.Files[0] s.Equal(dto.FilePath("./"+tests.DefaultFileName), fileHeader.Name) - s.Equal("-", fileHeader.ObjectType) - s.Equal(0, fileHeader.Size) + s.Equal(dto.EntryTypeRegularFile, fileHeader.EntryType) + s.Equal("user", fileHeader.Owner) + s.Equal("user", fileHeader.Group) + s.Equal("rwxr--r--", fileHeader.Permissions) }) }