Skip to content

Commit

Permalink
Marshal extended data if file info supports it
Browse files Browse the repository at this point in the history
If the file information returned by ListAt supports the
FileInfoExtendedData interface, use it to retrieve extended data and
pass it back to the client. In similar fashion, we add the
FileInfoUidGid interface to allow for implementations to return uid and
gid data, even if the os lacks support in syscall.Stat_t.

Signed-off-by: Peter Verraedt <peter@verraedt.be>
  • Loading branch information
peterverraedt committed Jul 14, 2023
1 parent 971c283 commit 551612f
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 1 deletion.
31 changes: 31 additions & 0 deletions attrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ func fileInfoFromStat(stat *FileStat, name string) os.FileInfo {
}
}

// FileInfoUidGid extends os.FileInfo and adds callbacks for Uid and Gid retrieval,
// as an alternative to *syscall.Stat_t objects on unix systems.
type FileInfoUidGid interface {
os.FileInfo
Uid() uint32
Gid() uint32
}

// FileInfoUidGid extends os.FileInfo and adds a callbacks for extended data retrieval.
type FileInfoExtendedData interface {
os.FileInfo
Extended() []StatExtended
}

func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
mtime := fi.ModTime().Unix()
atime := mtime
Expand All @@ -86,5 +100,22 @@ func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
// os specific file stat decoding
fileStatFromInfoOs(fi, &flags, fileStat)

// The call above will include the sshFileXferAttrUIDGID in case
// the os.FileInfo can be casted to *syscall.Stat_t on unix.
// If fi implements FileInfoUidGid, retrieve Uid, Gid from it instead.
if fiExt, ok := fi.(FileInfoUidGid); ok {
flags |= sshFileXferAttrUIDGID
fileStat.UID = fiExt.Uid()
fileStat.GID = fiExt.Gid()
}

// if fi implements FileInfoExtendedData, retrieve extended data from it
if fiExt, ok := fi.(FileInfoExtendedData); ok {
fileStat.Extended = fiExt.Extended()
if len(fileStat.Extended) > 0 {
flags |= sshFileXferAttrExtended
}
}

return flags, fileStat
}
7 changes: 7 additions & 0 deletions ls_formatting.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ func runLs(idLookup NameLookupFileLister, dirent os.FileInfo) string {
uid = lsFormatID(sys.UID)
gid = lsFormatID(sys.GID)
default:
if fiExt, ok := dirent.(FileInfoUidGid); ok {
uid = lsFormatID(fiExt.Uid())
gid = lsFormatID(fiExt.Gid())

break
}

numLinks, uid, gid = lsLinksUIDGID(dirent)
}

Expand Down
9 changes: 9 additions & 0 deletions packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
b = marshalUint32(b, fileStat.Mtime)
}

if flags&sshFileXferAttrExtended != 0 {
b = marshalUint32(b, uint32(len(fileStat.Extended)))

for _, attr := range fileStat.Extended {
b = marshalString(b, attr.ExtType)
b = marshalString(b, attr.ExtData)
}
}

return b
}

Expand Down
9 changes: 8 additions & 1 deletion request-interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ type StatVFSFileCmder interface {
// Note in cases of an error, the error text will be sent to the client.
// Called for Methods: List, Stat, Readlink
//
// Since Filelist returns an os.FileInfo, this can make it non-ideal for impelmenting Readlink.
// Since Filelist returns an os.FileInfo, this can make it non-ideal for implementing Readlink.
// This is because the Name receiver method defined by that interface defines that it should only return the base name.
// However, Readlink is required to be capable of returning essentially any arbitrary valid path relative or absolute.
// In order to implement this more expressive requirement, implement [ReadlinkFileLister] which will then be used instead.
Expand Down Expand Up @@ -136,6 +136,13 @@ type NameLookupFileLister interface {
// error if at end of list. This is testable by comparing how many you
// copied to how many could be copied (eg. n < len(ls) below).
// The copy() builtin is best for the copying.
// ListerAt provides entries that satisfy the os.FileInfo interface.
// Uid and gid information will on unix systems be retrieved from
// [os.FileInfo.Sys] if this function returns a [syscall.Stat_t].
// Alternatively, if the entry implements [FileInfoUidGid], it will be
// used for uid and gid information.
// If the entry implements [FileInfoExtended], extended attributes will
// also be returned to the client.
// Note in cases of an error, the error text will be sent to the client.
type ListerAt interface {
ListAt([]os.FileInfo, int64) (int, error)
Expand Down

0 comments on commit 551612f

Please sign in to comment.