Commit 439e1904 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Implement hard-links in PathNodeFs.

To use hard-links, the FileSystem should return consistent
FileInfo.Ino data from the GetAttr call.
parent a0010493
...@@ -257,17 +257,18 @@ func TestLink(t *testing.T) { ...@@ -257,17 +257,18 @@ func TestLink(t *testing.T) {
if fi.Ino != subfi.Ino { if fi.Ino != subfi.Ino {
t.Errorf("Link succeeded, but inode numbers different: %v %v", fi.Ino, subfi.Ino) t.Errorf("Link succeeded, but inode numbers different: %v %v", fi.Ino, subfi.Ino)
} }
f, err := os.Open(mountSubfile) readback, err := ioutil.ReadFile(mountSubfile)
CheckSuccess(err)
var buf [1024]byte
slice := buf[:]
n, err := f.Read(slice)
f.Close()
strContents := string(slice[:n]) if string(readback) != contents {
if strContents != contents { t.Errorf("Content error: got %q want %q", string(readback), contents)
t.Errorf("Content error: %v", slice[:n])
} }
err = os.Remove(me.mountFile)
CheckSuccess(err)
_, err = ioutil.ReadFile(mountSubfile)
CheckSuccess(err)
} }
func TestSymlink(t *testing.T) { func TestSymlink(t *testing.T) {
......
...@@ -5,14 +5,25 @@ import ( ...@@ -5,14 +5,25 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
) )
var _ = log.Println var _ = log.Println
type clientInodePath struct {
parent *pathInode
name string
node *pathInode
}
type PathNodeFs struct { type PathNodeFs struct {
fs FileSystem fs FileSystem
root *pathInode root *pathInode
connector *FileSystemConnector connector *FileSystemConnector
// Used for dealing with hardlinks.
clientInodeMapMutex sync.Mutex
clientInodeMap map[uint64][]*clientInodePath
} }
func (me *PathNodeFs) Mount(parent *Inode, name string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status { func (me *PathNodeFs) Mount(parent *Inode, name string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status {
...@@ -63,6 +74,7 @@ func NewPathNodeFs(fs FileSystem) *PathNodeFs { ...@@ -63,6 +74,7 @@ func NewPathNodeFs(fs FileSystem) *PathNodeFs {
me := &PathNodeFs{ me := &PathNodeFs{
fs: fs, fs: fs,
root: root, root: root,
clientInodeMap: map[uint64][]*clientInodePath{},
} }
root.ifs = me root.ifs = me
return me return me
...@@ -74,8 +86,7 @@ func (me *PathNodeFs) Root() FsNode { ...@@ -74,8 +86,7 @@ func (me *PathNodeFs) Root() FsNode {
// This is a combination of dentry (entry in the file/directory and // This is a combination of dentry (entry in the file/directory and
// the inode). This structure is used to implement glue for FSes where // the inode). This structure is used to implement glue for FSes where
// there is a one-to-one mapping of paths and inodes, ie. FSes that // there is a one-to-one mapping of paths and inodes.
// disallow hardlinks.
type pathInode struct { type pathInode struct {
ifs *PathNodeFs ifs *PathNodeFs
fs FileSystem fs FileSystem
...@@ -84,9 +95,22 @@ type pathInode struct { ...@@ -84,9 +95,22 @@ type pathInode struct {
// This is nil at the root of the mount. // This is nil at the root of the mount.
Parent *pathInode Parent *pathInode
// This is to correctly resolve hardlinks of the underlying
// real filesystem.
clientInode uint64
DefaultFsNode DefaultFsNode
} }
func (me *pathInode) fillNewChildAttr(path string, child *pathInode, c *Context) (fi *os.FileInfo) {
fi, _ = me.fs.GetAttr(path, c)
if fi != nil && fi.Ino > 0 {
child.clientInode = fi.Ino
}
return fi
}
// GetPath returns the path relative to the mount governing this // GetPath returns the path relative to the mount governing this
// inode. It returns nil for mount if the file was deleted or the // inode. It returns nil for mount if the file was deleted or the
// filesystem unmounted. This will take the treeLock for the mount, // filesystem unmounted. This will take the treeLock for the mount,
...@@ -107,22 +131,65 @@ func (me *pathInode) GetPath() (path string) { ...@@ -107,22 +131,65 @@ func (me *pathInode) GetPath() (path string) {
func (me *pathInode) AddChild(name string, child FsNode) { func (me *pathInode) AddChild(name string, child FsNode) {
ch := child.(*pathInode) ch := child.(*pathInode)
if ch.inode.mountPoint == nil {
ch.Parent = me ch.Parent = me
} else {
log.Printf("name %q", name)
panic("should have no mounts")
}
ch.Name = name ch.Name = name
if ch.clientInode > 0 {
me.ifs.clientInodeMapMutex.Lock()
defer me.ifs.clientInodeMapMutex.Unlock()
m := me.ifs.clientInodeMap[ch.clientInode]
e := &clientInodePath{
me, name, child.(*pathInode),
}
m = append(m, e)
me.ifs.clientInodeMap[ch.clientInode] = m
}
} }
func (me *pathInode) RmChild(name string, child FsNode) { func (me *pathInode) RmChild(name string, child FsNode) {
ch := child.(*pathInode) ch := child.(*pathInode)
if ch.clientInode > 0 {
me.ifs.clientInodeMapMutex.Lock()
defer me.ifs.clientInodeMapMutex.Unlock()
m := me.ifs.clientInodeMap[ch.clientInode]
idx := -1
for i, v := range m {
if v.parent == me && v.name == name {
idx = i
break
}
}
if idx >= 0 {
m[idx] = m[len(m)-1]
m = m[:len(m)-1]
}
if len(m) > 0 {
ch.Parent = m[0].parent
ch.Name = m[0].name
return
} else {
me.ifs.clientInodeMap[ch.clientInode] = nil, false
}
}
ch.Name = ".deleted" ch.Name = ".deleted"
ch.Parent = nil ch.Parent = nil
} }
func (me *pathInode) OnForget() {
if me.clientInode == 0 {
return
}
me.ifs.clientInodeMapMutex.Lock()
defer me.ifs.clientInodeMapMutex.Unlock()
me.ifs.clientInodeMap[me.clientInode] = nil, false
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// FS operations
func (me *pathInode) Readlink(c *Context) ([]byte, Status) { func (me *pathInode) Readlink(c *Context) ([]byte, Status) {
path := me.GetPath() path := me.GetPath()
...@@ -169,24 +236,23 @@ func (me *pathInode) OpenDir(context *Context) (chan DirEntry, Status) { ...@@ -169,24 +236,23 @@ func (me *pathInode) OpenDir(context *Context) (chan DirEntry, Status) {
} }
func (me *pathInode) Mknod(name string, mode uint32, dev uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) { func (me *pathInode) Mknod(name string, mode uint32, dev uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) {
p := me.GetPath() fullPath := filepath.Join(me.GetPath(), name)
code = me.fs.Mknod(filepath.Join(p, name), mode, dev, context) code = me.fs.Mknod(fullPath, mode, dev, context)
if code.Ok() { if code.Ok() {
newNode = me.createChild(name) pNode := me.createChild(name)
fi = &os.FileInfo{ newNode = pNode
Mode: S_IFIFO | mode, // TODO fi = me.fillNewChildAttr(fullPath, pNode, context)
}
} }
return return
} }
func (me *pathInode) Mkdir(name string, mode uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) { func (me *pathInode) Mkdir(name string, mode uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) {
code = me.fs.Mkdir(filepath.Join(me.GetPath(), name), mode, context) fullPath := filepath.Join(me.GetPath(), name)
code = me.fs.Mkdir(fullPath, mode, context)
if code.Ok() { if code.Ok() {
newNode = me.createChild(name) pNode := me.createChild(name)
fi = &os.FileInfo{ newNode = pNode
Mode: S_IFDIR | mode, fi = me.fillNewChildAttr(fullPath, pNode, context)
}
} }
return return
} }
...@@ -200,12 +266,12 @@ func (me *pathInode) Rmdir(name string, context *Context) (code Status) { ...@@ -200,12 +266,12 @@ func (me *pathInode) Rmdir(name string, context *Context) (code Status) {
} }
func (me *pathInode) Symlink(name string, content string, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) { func (me *pathInode) Symlink(name string, content string, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) {
code = me.fs.Symlink(content, filepath.Join(me.GetPath(), name), context) fullPath := filepath.Join(me.GetPath(), name)
code = me.fs.Symlink(content, fullPath, context)
if code.Ok() { if code.Ok() {
newNode = me.createChild(name) pNode := me.createChild(name)
fi = &os.FileInfo{ newNode = pNode
Mode: S_IFLNK | 0666, // TODO fi = me.fillNewChildAttr(fullPath, pNode, context)
}
} }
return return
} }
...@@ -219,19 +285,18 @@ func (me *pathInode) Rename(oldName string, newParent FsNode, newName string, co ...@@ -219,19 +285,18 @@ func (me *pathInode) Rename(oldName string, newParent FsNode, newName string, co
} }
func (me *pathInode) Link(name string, existing FsNode, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) { func (me *pathInode) Link(name string, existing FsNode, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) {
newPath := filepath.Join(me.GetPath(), name) newPath := filepath.Join(me.GetPath(), name)
e := existing.(*pathInode) e := existing.(*pathInode)
oldPath := e.GetPath() oldPath := e.GetPath()
code = me.fs.Link(oldPath, newPath, context) code = me.fs.Link(oldPath, newPath, context)
if code.Ok() { if code.Ok() {
oldFi, _ := me.fs.GetAttr(oldPath, context)
fi, _ = me.fs.GetAttr(newPath, context) fi, _ = me.fs.GetAttr(newPath, context)
if oldFi != nil && fi != nil && oldFi.Ino != 0 && oldFi.Ino == fi.Ino { if fi != nil && e.clientInode != 0 && e.clientInode == fi.Ino {
return fi, existing, OK newNode = existing
} } else {
newNode = me.createChild(name) newNode = me.createChild(name)
} }
}
return return
} }
...@@ -239,11 +304,9 @@ func (me *pathInode) Create(name string, flags uint32, mode uint32, context *Con ...@@ -239,11 +304,9 @@ func (me *pathInode) Create(name string, flags uint32, mode uint32, context *Con
fullPath := filepath.Join(me.GetPath(), name) fullPath := filepath.Join(me.GetPath(), name)
file, code = me.fs.Create(fullPath, flags, mode, context) file, code = me.fs.Create(fullPath, flags, mode, context)
if code.Ok() { if code.Ok() {
newNode = me.createChild(name) pNode := me.createChild(name)
fi = &os.FileInfo{ newNode = pNode
Mode: S_IFREG | mode, fi = me.fillNewChildAttr(fullPath, pNode, context)
// TODO - ctime, mtime, atime?
}
} }
return return
} }
...@@ -266,7 +329,11 @@ func (me *pathInode) Lookup(name string) (fi *os.FileInfo, node FsNode, code Sta ...@@ -266,7 +329,11 @@ func (me *pathInode) Lookup(name string) (fi *os.FileInfo, node FsNode, code Sta
fullPath := filepath.Join(me.GetPath(), name) fullPath := filepath.Join(me.GetPath(), name)
fi, code = me.fs.GetAttr(fullPath, nil) fi, code = me.fs.GetAttr(fullPath, nil)
if code.Ok() { if code.Ok() {
node = me.createChild(name) pNode := me.createChild(name)
node = pNode
if fi.Ino > 0 {
pNode.clientInode = fi.Ino
}
} }
return return
......
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