From 867071d1e5e157cefd0d698589fdf6a2e2d6f4c4 Mon Sep 17 00:00:00 2001 From: Vincent Batts Date: Thu, 29 Oct 2015 15:30:47 -0400 Subject: [PATCH] verify: starts of a checking function --- tar/verify/example_xattr.go | 31 ++++++++++ tar/verify/headers.go | 109 ++++++++++++++++++++++++++++++------ tar/verify/verify.go | 38 +++++++++++-- tar/verify/xattrs_linux.go | 25 +-------- 4 files changed, 158 insertions(+), 45 deletions(-) create mode 100644 tar/verify/example_xattr.go diff --git a/tar/verify/example_xattr.go b/tar/verify/example_xattr.go new file mode 100644 index 0000000..9a4e354 --- /dev/null +++ b/tar/verify/example_xattr.go @@ -0,0 +1,31 @@ +// +build ignore + +package main + +import ( + "fmt" + "os" + + verify "." +) + +func main() { + for _, arg := range os.Args[1:] { + keys, err := verify.Listxattr(arg) + if err != nil { + fmt.Println(err) + continue + } + if len(keys) > 0 { + fmt.Printf("%s : %q\n", arg, keys) + for _, key := range keys { + buf, err := verify.Lgetxattr(arg, key) + if err != nil { + fmt.Printf(" ERROR: %s\n", err) + continue + } + fmt.Printf(" %s = %s\n", key, string(buf)) + } + } + } +} diff --git a/tar/verify/headers.go b/tar/verify/headers.go index b9ec61d..6500b73 100644 --- a/tar/verify/headers.go +++ b/tar/verify/headers.go @@ -1,23 +1,100 @@ package verify -import "time" +import ( + "os" + "path/filepath" + "strings" + "time" +) // PosixHeader is the structure from a POSIX tar header, to be marshalled from // the tar stream, and available for on-disk comparison and verification type PosixHeader struct { - Name string `json:"name,omitempty"` - Mode uint32 `json:"mode,omitempty"` - UID uint32 `json:"uid,omitempty"` - GID uint32 `json:"gid,omitempty"` - Size int `json:"size,omitempty"` - Mtime time.Time `json:"mtime,omitempty"` - Checksum []byte `json:"chksum,omitempty"` - LinkName string `json:"linkname,omitempty"` - Magic []byte `json:"magic,omitempty"` - Version string `json:"version,omitempty"` - Uname string `json:"uname,omitempty"` - Gname string `json:"gname,omitempty"` - DevMajor int `json:"devmajor,omitempty"` - DevMinor int `json:"devminor,omitempty"` - Prefix string `json:"prefix,omitempty"` + Name string `json:"name,omitempty"` + Mode os.FileMode `json:"mode,omitempty"` + UID uint32 `json:"uid,omitempty"` + GID uint32 `json:"gid,omitempty"` + Size int64 `json:"size,omitempty"` + Mtime time.Time `json:"mtime,omitempty"` + Checksum []byte `json:"chksum,omitempty"` + LinkName string `json:"linkname,omitempty"` + Magic []byte `json:"magic,omitempty"` + Version string `json:"version,omitempty"` + Uname string `json:"uname,omitempty"` + Gname string `json:"gname,omitempty"` + DevMajor int `json:"devmajor,omitempty"` + DevMinor int `json:"devminor,omitempty"` + Prefix string `json:"prefix,omitempty"` +} + +type PaxHeader struct { + Atime time.Time + Ctime time.Time + Xattrs map[string]string +} + +type Header struct { + // maybe I do not want these grouped like this. Maybe this should be an interface instead. + Posix PosixHeader + Pax PaxHeader +} + +// Size returns file size (implements Sizer) +func (hdr Header) Size() int64 { + return int64(hdr.Posix.Size) +} + +// ModTime returns file mtime (implements ModTimer) +func (hdr Header) ModTime() time.Time { + return hdr.Posix.Mtime +} + +// Mode returns file mode (implements Moder) +func (hdr Header) Mode() os.FileMode { + return hdr.Posix.Mode +} + +func (hdr Header) LinkName() string { + return hdr.Posix.LinkName +} + +// HeaderFromFile takes a relative root and the filename of the file to collect +// information on. +func HeaderFromFile(rel, filename string) (*Header, error) { + absRel, err := filepath.Abs(rel) + if err != nil { + return nil, err + } + if strings.HasPrefix(filename, "/") { + var err error + filename, err = filepath.Abs(filename) + if err != nil { + return nil, err + } + } + + stat, err := os.Lstat(filename) + if err != nil { + return nil, err + } + + name := filename + if strings.HasPrefix(filename, absRel) { + name = strings.TrimPrefix(filename, absRel) + } + + hdr := Header{ + Posix: PosixHeader{ + Name: name, + Size: stat.Size(), + Mode: stat.Mode(), + Mtime: stat.ModTime(), + }, + } + if stat.Mode()&os.ModeSymlink != 0 { + l, _ := os.Readlink(filename) // if this errors, the empty string is OK + hdr.Posix.LinkName = l + } + + return &hdr, nil } diff --git a/tar/verify/verify.go b/tar/verify/verify.go index 9037e0e..949b898 100644 --- a/tar/verify/verify.go +++ b/tar/verify/verify.go @@ -1,6 +1,10 @@ package verify -import "fmt" +import ( + "fmt" + "os" + "time" +) // CheckType is how the on disk attributes will be verified against the // recorded header information @@ -15,14 +19,15 @@ const ( CheckFileUser CheckFileGroup CheckFileMtime - CheckFileDevice - CheckFileLink - CheckFileCaps + CheckFileDevice // major/minor + CheckFileLink // linkname + CheckFileCaps // which is just a subset of xattrs on linux ) var ( // DefaultChecks is the default for verfication steps against each - // storage.VerficationEntry + // storage.VerficationEntry. + // These may need to vary from platform to platform DefaultChecks = CheckDigest | CheckFileAttributes // CheckFileAttributes are the group of file attribute checks done CheckFileAttributes = CheckFileSize | CheckFileMode | CheckFileUser | @@ -32,3 +37,26 @@ var ( // ErrNotSupportedPlatform is when the platform does not support given features ErrNotSupportedPlatform = fmt.Errorf("platform does not support this feature") ) + +// FileAttrer exposes the functions corresponding to file attribute checks +type FileAttrer interface { + Sizer + Moder + ModTimer + LinkName() string +} + +// Sizer returns the size of the file (see also os.FileInfo) +type Sizer interface { + Size() int64 +} + +// Moder returns the mode of the file (see also os.FileInfo) +type Moder interface { + Mode() os.FileMode +} + +// ModTimer returns the mtime of the file (see also os.FileInfo) +type ModTimer interface { + ModTime() time.Time +} diff --git a/tar/verify/xattrs_linux.go b/tar/verify/xattrs_linux.go index 46e1f8c..4dba865 100644 --- a/tar/verify/xattrs_linux.go +++ b/tar/verify/xattrs_linux.go @@ -13,29 +13,6 @@ import ( "unsafe" ) -/* -func main() { - for _, arg := range os.Args[1:] { - keys, err := Listxattr(arg) - if err != nil { - fmt.Println(err) - continue - } - if len(keys) > 0 { - fmt.Printf("%s : %q\n", arg, keys) - for _, key := range keys { - buf, err := Lgetxattr(arg, key) - if err != nil { - fmt.Printf(" ERROR: %s\n", err) - continue - } - fmt.Printf(" %s = %s\n", key, string(buf)) - } - } - } -} -*/ - // Listxattr is a helper around the syscall.Listxattr func Listxattr(path string) ([]string, error) { buf := make([]byte, 1024) @@ -48,7 +25,7 @@ func Listxattr(path string) ([]string, error) { sz, err = syscall.Listxattr(path, buf) } keys := []string{} - for _, key := range bytes.Split(bytes.TrimSpace(buf), []byte{0x0}) { + for _, key := range bytes.Split(bytes.Trim(buf, "\x00"), []byte{0x0}) { if string(key) != "" { keys = append(keys, string(key)) }