Commit c7e29df4 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Add a SkipCheckHandles option that tries to keep uint64 handles

within 32-bits.

This is needed to deal with 32-bits programs that use stat().  If the
inode number exceeds 32 bit, that will return an ERANGE error.
parent c57ca849
......@@ -106,6 +106,12 @@ type FileSystemOptions struct {
// If set, replace all uids with given UID. NewFileSystemOptions() will set
// this to the daemon's uid/gid.
*Owner
// If set, drop extra verification bits to handles. This will
// make inode numbers (exported back to callers) stay within
// int64 (assuming the process uses less than 4G memory.).
// 64-bit inode numbers makes stat() in 32-bit programs fail.
SkipCheckHandles bool
}
type MountOptions struct {
......
......@@ -85,7 +85,7 @@ func TestCacheFs(t *testing.T) {
type nonseekFs struct {
DefaultFileSystem
Length int
Length int
}
func (me *nonseekFs) GetAttr(name string, context *Context) (fi *os.FileInfo, status Status) {
......@@ -124,7 +124,7 @@ func TestNonseekable(t *testing.T) {
f, err := os.Open(dir + "/file")
CheckSuccess(err)
defer f.Close()
b := make([]byte, 200)
n, err := f.ReadAt(b, 20)
if err == nil || n > 0 {
......
package fuse
import (
"log"
"os"
"fmt"
)
var _ = log.Println
func MountFileSystem(mountpoint string, fs FileSystem, opts *FileSystemOptions) (*MountState, *FileSystemConnector, os.Error) {
conn := NewFileSystemConnector(fs, opts)
mountState := NewMountState(conn)
......
......@@ -2,8 +2,9 @@ package fuse
import (
"fmt"
"unsafe"
"log"
"sync"
"unsafe"
)
// HandleMap translates objects in Go space to 64-bit handles that can
......@@ -13,7 +14,7 @@ import (
// in a map, so the Go runtime will not garbage collect it.
//
// The 32 bits version of this is a threadsafe wrapper around a map.
//
//
// To use it, include Handled as first member of the structure
// you wish to export.
//
......@@ -64,8 +65,11 @@ type int64HandleMap struct {
mutex sync.Mutex
handles map[uint64]*Handled
nextFree uint32
addCheck bool
}
var baseAddress uint64
func (me *int64HandleMap) verify() {
if !paranoia {
return
......@@ -80,11 +84,14 @@ func (me *int64HandleMap) verify() {
}
}
func NewHandleMap() (hm HandleMap) {
// NewHandleMap creates a new HandleMap. If verify is given, we
// use remaining bits in the handle to store sanity check bits.
func NewHandleMap(verify bool) (hm HandleMap) {
var obj *Handled
switch unsafe.Sizeof(obj) {
case 8:
return &int64HandleMap{
addCheck: verify,
handles: make(map[uint64]*Handled),
nextFree: 1, // to make tests easier.
}
......@@ -92,7 +99,10 @@ func NewHandleMap() (hm HandleMap) {
return &int32HandleMap{
handles: make(map[uint32]*Handled),
}
default:
log.Println("Unknown size.")
}
return nil
}
......@@ -109,10 +119,6 @@ func (me *int64HandleMap) Register(obj *Handled) (handle uint64) {
defer me.mutex.Unlock()
handle = uint64(uintptr(unsafe.Pointer(obj)))
check := me.nextFree
me.nextFree++
me.nextFree = me.nextFree & (1<<(64-48+3) - 1)
rest := (handle &^ (1<<48 - 1))
if rest != 0 {
......@@ -121,13 +127,21 @@ func (me *int64HandleMap) Register(obj *Handled) (handle uint64) {
if handle&0x7 != 0 {
panic("unaligned ptr")
}
handle -= baseAddress
handle >>= 3
handle |= uint64(check) << (48 - 3)
if obj.check != 0 {
panic("Object already has a handle.")
if me.addCheck {
check := me.nextFree
me.nextFree++
me.nextFree = me.nextFree & (1<<(64-48+3) - 1)
handle |= uint64(check) << (48 - 3)
if obj.check != 0 {
panic("Object already has a handle.")
}
obj.check = check
}
obj.check = check
me.handles[handle] = obj
return handle
}
......@@ -149,7 +163,7 @@ func DecodeHandle(handle uint64) (val *Handled) {
if unsafe.Sizeof(val) == 8 {
ptrBits := uintptr(handle & (1<<45 - 1))
check = uint32(handle >> 45)
val = (*Handled)(unsafe.Pointer(ptrBits << 3))
val = (*Handled)(unsafe.Pointer(ptrBits << 3 + uintptr(baseAddress)))
}
if unsafe.Sizeof(val) == 4 {
val = (*Handled)(unsafe.Pointer(uintptr(handle & ((1 << 32) - 1))))
......@@ -161,3 +175,10 @@ func DecodeHandle(handle uint64) (val *Handled) {
}
return val
}
func init() {
// TODO - figure out a way to discover this nicely. This is
// depends in a pretty fragile way on the 6g allocator
// characteristics.
baseAddress = uint64(0xf800000000)
}
......@@ -25,7 +25,7 @@ func TestHandleMapDoubleRegister(t *testing.T) {
}
log.Println("TestDoubleRegister")
defer markSeen("already has a handle")
hm := NewHandleMap()
hm := NewHandleMap(true)
hm.Register(&Handled{})
v := &Handled{}
hm.Register(v)
......@@ -38,7 +38,7 @@ func TestHandleMapUnaligned(t *testing.T) {
t.Log("skipping test for 32 bits")
return
}
hm := NewHandleMap()
hm := NewHandleMap(true)
b := make([]byte, 100)
v := (*Handled)(unsafe.Pointer(&b[1]))
......@@ -54,7 +54,7 @@ func TestHandleMapPointerLayout(t *testing.T) {
return
}
hm := NewHandleMap()
hm := NewHandleMap(true)
bogus := uint64(1) << uint32((8 * (unsafe.Sizeof(t) - 1)))
p := uintptr(bogus)
v := (*Handled)(unsafe.Pointer(p))
......@@ -69,7 +69,7 @@ func TestHandleMapBasic(t *testing.T) {
return
}
v := new(Handled)
hm := NewHandleMap()
hm := NewHandleMap(true)
h := hm.Register(v)
log.Printf("Got handle 0x%x", h)
if DecodeHandle(h) != v {
......@@ -89,7 +89,7 @@ func TestHandleMapMultiple(t *testing.T) {
t.Log("skipping test for 32 bits")
return
}
hm := NewHandleMap()
hm := NewHandleMap(true)
for i := 0; i < 10; i++ {
v := &Handled{}
h := hm.Register(v)
......@@ -110,8 +110,25 @@ func TestHandleMapCheckFail(t *testing.T) {
defer markSeen("check mismatch")
v := new(Handled)
hm := NewHandleMap()
hm := NewHandleMap(true)
h := hm.Register(v)
DecodeHandle(h | (uint64(1) << 63))
t.Error("Borked decode did not panic")
}
func TestHandleMapNoCheck(t *testing.T) {
if unsafe.Sizeof(t) < 8 {
t.Log("skipping test for 32 bits")
return
}
v := new(Handled)
hm := NewHandleMap(false)
h := hm.Register(v)
if h > uint64(0xffffffff) {
t.Errorf("handles should in 32 bit if verification switched off: %x", h)
}
v2 := DecodeHandle(h)
if v2 != v {
t.Errorf("Handle decode error.")
}
}
......@@ -158,7 +158,7 @@ type inode struct {
func (me *inode) mountFs(fs FileSystem, opts *FileSystemOptions) {
me.mountPoint = &fileSystemMount{
fs: fs,
openFiles: NewHandleMap(),
openFiles: NewHandleMap(true),
mountInode: me,
options: opts,
}
......@@ -510,16 +510,6 @@ func (me *FileSystemConnector) findInode(fullPath string) *inode {
////////////////////////////////////////////////////////////////
func EmptyFileSystemConnector() (me *FileSystemConnector) {
me = new(FileSystemConnector)
me.inodeMap = NewHandleMap()
me.rootNode = me.newInode(true)
me.rootNode.NodeId = FUSE_ROOT_ID
me.verify()
return me
}
// Mount() generates a synthetic directory node, and mounts the file
// system there. If opts is nil, the mount options of the root file
// system are inherited. The encompassing filesystem should pretend
......@@ -581,9 +571,6 @@ func (me *FileSystemConnector) Mount(mountPoint string, fs FileSystem, opts *Fil
}
func (me *FileSystemConnector) mountRoot(fs FileSystem, opts *FileSystemOptions) {
if opts == nil {
opts = NewFileSystemOptions()
}
me.rootNode.mountFs(fs, opts)
fs.Mount(me)
me.verify()
......
......@@ -12,10 +12,17 @@ import (
var _ = fmt.Println
func NewFileSystemConnector(fs FileSystem, opts *FileSystemOptions) (out *FileSystemConnector) {
out = EmptyFileSystemConnector()
out.mountRoot(fs, opts)
return out
func NewFileSystemConnector(fs FileSystem, opts *FileSystemOptions) (me *FileSystemConnector) {
me = new(FileSystemConnector)
if opts == nil {
opts = NewFileSystemOptions()
}
me.inodeMap = NewHandleMap(!opts.SkipCheckHandles)
me.rootNode = me.newInode(true)
me.rootNode.NodeId = FUSE_ROOT_ID
me.verify()
me.mountRoot(fs, opts)
return me
}
func (me *FileSystemConnector) GetPath(nodeid uint64) (path string, mount *fileSystemMount, node *inode) {
......@@ -130,7 +137,7 @@ func (me *FileSystemConnector) GetAttr(header *InHeader, input *GetAttrIn) (out
if mount == nil {
return nil, ENOENT
}
fi, err := mount.fs.GetAttr(fullPath, &header.Context)
if err != OK {
return nil, err
......
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