Skip to content

Commit ec1c8ca

Browse files
authored
Merge pull request #553 from peterverraedt/marshal-extended
Marshal extended data if file info supports it
2 parents 72a484f + c632ce6 commit ec1c8ca

File tree

4 files changed

+60
-5
lines changed

4 files changed

+60
-5
lines changed

attrs.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ func fileInfoFromStat(stat *FileStat, name string) os.FileInfo {
6969
}
7070
}
7171

72+
// FileInfoUidGid extends os.FileInfo and adds callbacks for Uid and Gid retrieval,
73+
// as an alternative to *syscall.Stat_t objects on unix systems.
74+
type FileInfoUidGid interface {
75+
os.FileInfo
76+
Uid() uint32
77+
Gid() uint32
78+
}
79+
80+
// FileInfoUidGid extends os.FileInfo and adds a callbacks for extended data retrieval.
81+
type FileInfoExtendedData interface {
82+
os.FileInfo
83+
Extended() []StatExtended
84+
}
85+
7286
func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
7387
mtime := fi.ModTime().Unix()
7488
atime := mtime
@@ -86,5 +100,22 @@ func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
86100
// os specific file stat decoding
87101
fileStatFromInfoOs(fi, &flags, fileStat)
88102

103+
// The call above will include the sshFileXferAttrUIDGID in case
104+
// the os.FileInfo can be casted to *syscall.Stat_t on unix.
105+
// If fi implements FileInfoUidGid, retrieve Uid, Gid from it instead.
106+
if fiExt, ok := fi.(FileInfoUidGid); ok {
107+
flags |= sshFileXferAttrUIDGID
108+
fileStat.UID = fiExt.Uid()
109+
fileStat.GID = fiExt.Gid()
110+
}
111+
112+
// if fi implements FileInfoExtendedData, retrieve extended data from it
113+
if fiExt, ok := fi.(FileInfoExtendedData); ok {
114+
fileStat.Extended = fiExt.Extended()
115+
if len(fileStat.Extended) > 0 {
116+
flags |= sshFileXferAttrExtended
117+
}
118+
}
119+
89120
return flags, fileStat
90121
}

ls_formatting.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ func runLs(idLookup NameLookupFileLister, dirent os.FileInfo) string {
6060
uid = lsFormatID(sys.UID)
6161
gid = lsFormatID(sys.GID)
6262
default:
63+
if fiExt, ok := dirent.(FileInfoUidGid); ok {
64+
uid = lsFormatID(fiExt.Uid())
65+
gid = lsFormatID(fiExt.Gid())
66+
67+
break
68+
}
69+
6370
numLinks, uid, gid = lsLinksUIDGID(dirent)
6471
}
6572

packet.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
7171
b = marshalUint32(b, fileStat.Mtime)
7272
}
7373

74+
if flags&sshFileXferAttrExtended != 0 {
75+
b = marshalUint32(b, uint32(len(fileStat.Extended)))
76+
77+
for _, attr := range fileStat.Extended {
78+
b = marshalString(b, attr.ExtType)
79+
b = marshalString(b, attr.ExtData)
80+
}
81+
}
82+
7483
return b
7584
}
7685

request-interfaces.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type StatVFSFileCmder interface {
7575
// Note in cases of an error, the error text will be sent to the client.
7676
// Called for Methods: List, Stat, Readlink
7777
//
78-
// Since Filelist returns an os.FileInfo, this can make it non-ideal for impelmenting Readlink.
78+
// Since Filelist returns an os.FileInfo, this can make it non-ideal for implementing Readlink.
7979
// This is because the Name receiver method defined by that interface defines that it should only return the base name.
8080
// However, Readlink is required to be capable of returning essentially any arbitrary valid path relative or absolute.
8181
// In order to implement this more expressive requirement, implement [ReadlinkFileLister] which will then be used instead.
@@ -131,11 +131,19 @@ type NameLookupFileLister interface {
131131
LookupGroupName(string) string
132132
}
133133

134-
// ListerAt does for file lists what io.ReaderAt does for files.
135-
// ListAt should return the number of entries copied and an io.EOF
136-
// error if at end of list. This is testable by comparing how many you
137-
// copied to how many could be copied (eg. n < len(ls) below).
134+
// ListerAt does for file lists what io.ReaderAt does for files, i.e. a []os.FileInfo buffer is passed to the ListAt function
135+
// and the entries that are populated in the buffer will be passed to the client.
136+
//
137+
// ListAt should return the number of entries copied and an io.EOF error if at end of list.
138+
// This is testable by comparing how many you copied to how many could be copied (eg. n < len(ls) below).
138139
// The copy() builtin is best for the copying.
140+
//
141+
// Uid and gid information will on unix systems be retrieved from [os.FileInfo.Sys]
142+
// if this function returns a [syscall.Stat_t] when called on a populated entry.
143+
// Alternatively, if the entry implements [FileInfoUidGid], it will be used for uid and gid information.
144+
//
145+
// If a populated entry implements [FileInfoExtendedData], extended attributes will also be returned to the client.
146+
//
139147
// Note in cases of an error, the error text will be sent to the client.
140148
type ListerAt interface {
141149
ListAt([]os.FileInfo, int64) (int, error)

0 commit comments

Comments
 (0)