Extend ls2json reader

by also parsing the link target, permissions, group and owner.
This commit is contained in:
Maximilian Paß
2022-08-30 16:23:17 +02:00
parent 3469d0ce77
commit 847e5cda65
5 changed files with 101 additions and 33 deletions

View File

@ -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.

View File

@ -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"`
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.

View File

@ -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
}

View File

@ -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())
}

View File

@ -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)
})
}