Commit be5a8417 authored by Jakob Unterwurzacher's avatar Jakob Unterwurzacher Committed by Han-Wen Nienhuys

fuse: replace DirEntryList.FixMode() pointer arithmetic

As pointed out by @slackner at
https://github.com/hanwen/go-fuse/commit/8581b7e950b2d520370eba8997ddd9493192e9c7#r35845728
FixMode() failed to take into account the length
of the file name and the padding.

To fix this without adding a lot of additional
pointer arithmetic, add the "lastDirent" field
to DirEntryList. This stores the last serialized
_Dirent, so _Dirent.Typ can be accessed easily
by FixMode().

Change-Id: I6ca3fe551e1a072779b1d5ed5293add057e55287
parent c9fd0958
...@@ -899,7 +899,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out ...@@ -899,7 +899,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
b.addNewChild(n, e.Name, child, nil, 0, entryOut) b.addNewChild(n, e.Name, child, nil, 0, entryOut)
child.setEntryOut(entryOut) child.setEntryOut(entryOut)
b.setEntryOutTimeout(entryOut) b.setEntryOutTimeout(entryOut)
if (e.Mode &^ 07777) != (child.stableAttr.Mode &^ 07777) { if e.Mode&syscall.S_IFMT != child.stableAttr.Mode&syscall.S_IFMT {
// The file type has changed behind our back. Use the new value. // The file type has changed behind our back. Use the new value.
out.FixMode(child.stableAttr.Mode) out.FixMode(child.stableAttr.Mode)
} }
......
...@@ -36,9 +36,10 @@ func (d DirEntry) String() string { ...@@ -36,9 +36,10 @@ func (d DirEntry) String() string {
// DirEntryList holds the return value for READDIR and READDIRPLUS // DirEntryList holds the return value for READDIR and READDIRPLUS
// opcodes. // opcodes.
type DirEntryList struct { type DirEntryList struct {
buf []byte buf []byte
size int size int // capacity of the underlying buffer
offset uint64 offset uint64 // entry count (NOT a byte offset)
lastDirent *_Dirent // pointer to the last serialized _Dirent. Used by FixMode().
} }
// NewDirEntryList creates a DirEntryList with the given data buffer // NewDirEntryList creates a DirEntryList with the given data buffer
...@@ -77,7 +78,7 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) b ...@@ -77,7 +78,7 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) b
dirent.Off = l.offset + 1 dirent.Off = l.offset + 1
dirent.Ino = inode dirent.Ino = inode
dirent.NameLen = uint32(len(name)) dirent.NameLen = uint32(len(name))
dirent.Typ = (mode & 0170000) >> 12 dirent.Typ = modeToType(mode)
oldLen += direntSize oldLen += direntSize
copy(l.buf[oldLen:], name) copy(l.buf[oldLen:], name)
oldLen += len(name) oldLen += len(name)
...@@ -90,27 +91,42 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) b ...@@ -90,27 +91,42 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) b
return true return true
} }
// AddDirLookupEntry is used for ReadDirPlus. It serializes a DirEntry // AddDirLookupEntry is used for ReadDirPlus. If reserves and zeroizes space
// and returns the space for entry. If no space is left, returns a nil // for an EntryOut struct and serializes a DirEntry.
// pointer. // On success, it returns pointers to both structs.
// If not enough space was left, it returns two nil pointers.
//
// The resulting READDIRPLUS output buffer looks like this in memory:
// 1) EntryOut{}
// 2) _Dirent{}
// 3) Name (null-terminated)
// 4) Padding to align to 8 bytes
// [repeat]
func (l *DirEntryList) AddDirLookupEntry(e DirEntry) *EntryOut { func (l *DirEntryList) AddDirLookupEntry(e DirEntry) *EntryOut {
lastStart := len(l.buf) const entryOutSize = int(unsafe.Sizeof(EntryOut{}))
ok := l.Add(int(unsafe.Sizeof(EntryOut{})), e.Name, oldLen := len(l.buf)
e.Ino, e.Mode) ok := l.Add(entryOutSize, e.Name, e.Ino, e.Mode)
if !ok { if !ok {
return nil return nil
} }
result := (*EntryOut)(unsafe.Pointer(&l.buf[lastStart])) l.lastDirent = (*_Dirent)(unsafe.Pointer(&l.buf[oldLen+entryOutSize]))
*result = EntryOut{} entryOut := (*EntryOut)(unsafe.Pointer(&l.buf[oldLen]))
return result *entryOut = EntryOut{} // zeroize
return entryOut
}
// modeToType converts a file *mode* (as used in syscall.Stat_t.Mode)
// to a file *type* (as used in _Dirent.Typ).
// Equivalent to IFTODT() in libc (see man 5 dirent).
func modeToType(mode uint32) uint32 {
return (mode & 0170000) >> 12
} }
// FixMode overrides the mode of the last direntry that was added. This can // FixMode overrides the file mode of the last direntry that was added. This can
// be needed when a directory changes while READDIRPLUS is running. // be needed when a directory changes while READDIRPLUS is running.
// Only the file type bits of mode are considered, the rest is masked out.
func (l *DirEntryList) FixMode(mode uint32) { func (l *DirEntryList) FixMode(mode uint32) {
oldLen := len(l.buf) - int(unsafe.Sizeof(_Dirent{})) l.lastDirent.Typ = modeToType(mode)
dirent := (*_Dirent)(unsafe.Pointer(&l.buf[oldLen]))
dirent.Typ = (mode & 0170000) >> 12
} }
func (l *DirEntryList) bytes() []byte { func (l *DirEntryList) bytes() []byte {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment