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
b.addNewChild(n, e.Name, child, nil, 0, entryOut)
child.setEntryOut(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.
out.FixMode(child.stableAttr.Mode)
}
......
......@@ -37,8 +37,9 @@ func (d DirEntry) String() string {
// opcodes.
type DirEntryList struct {
buf []byte
size int
offset uint64
size int // capacity of the underlying buffer
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
......@@ -77,7 +78,7 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) b
dirent.Off = l.offset + 1
dirent.Ino = inode
dirent.NameLen = uint32(len(name))
dirent.Typ = (mode & 0170000) >> 12
dirent.Typ = modeToType(mode)
oldLen += direntSize
copy(l.buf[oldLen:], name)
oldLen += len(name)
......@@ -90,27 +91,42 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) b
return true
}
// AddDirLookupEntry is used for ReadDirPlus. It serializes a DirEntry
// and returns the space for entry. If no space is left, returns a nil
// pointer.
// AddDirLookupEntry is used for ReadDirPlus. If reserves and zeroizes space
// for an EntryOut struct and serializes a DirEntry.
// 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 {
lastStart := len(l.buf)
ok := l.Add(int(unsafe.Sizeof(EntryOut{})), e.Name,
e.Ino, e.Mode)
const entryOutSize = int(unsafe.Sizeof(EntryOut{}))
oldLen := len(l.buf)
ok := l.Add(entryOutSize, e.Name, e.Ino, e.Mode)
if !ok {
return nil
}
result := (*EntryOut)(unsafe.Pointer(&l.buf[lastStart]))
*result = EntryOut{}
return result
l.lastDirent = (*_Dirent)(unsafe.Pointer(&l.buf[oldLen+entryOutSize]))
entryOut := (*EntryOut)(unsafe.Pointer(&l.buf[oldLen]))
*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.
// Only the file type bits of mode are considered, the rest is masked out.
func (l *DirEntryList) FixMode(mode uint32) {
oldLen := len(l.buf) - int(unsafe.Sizeof(_Dirent{}))
dirent := (*_Dirent)(unsafe.Pointer(&l.buf[oldLen]))
dirent.Typ = (mode & 0170000) >> 12
l.lastDirent.Typ = modeToType(mode)
}
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