diff --git a/fuse/fsinode.go b/fuse/fsinode.go index 7609b201c3d2af044713d2ad5d38ecda82123783..997d60e67dc0dda3418b85c426020da8dd19a7f3 100644 --- a/fuse/fsinode.go +++ b/fuse/fsinode.go @@ -92,6 +92,10 @@ func (me *fsInode) SetInode(node *inode) { me.inode = node } +func (me *fsInode) Inode() *inode { + return me.inode +} + //////////////////////////////////////////////////////////////// func (me *fsInode) Readlink(c *Context) ([]byte, Status) { @@ -188,8 +192,19 @@ func (me *fsInode) Rename(oldName string, newParent *fsInode, newName string, co return me.fs.Rename(oldPath, newPath, context) } -func (me *fsInode) Link(name string, existing *fsInode, context *Context) (code Status) { - return me.fs.Link(existing.GetPath(), filepath.Join(me.GetPath(), name), context) +func (me *fsInode) Link(name string, existing *fsInode, context *Context) (fi *os.FileInfo, newNode *fsInode, code Status) { + newPath := filepath.Join(me.GetPath(), name) + oldPath := existing.GetPath() + code = me.fs.Link(oldPath, newPath, context) + if code.Ok() { + oldFi, _ := me.fs.GetAttr(oldPath, context) + fi, _ = me.fs.GetAttr(newPath, context) + if oldFi != nil && fi != nil && oldFi.Ino != 0 && oldFi.Ino == fi.Ino { + return fi, existing, OK + } + newNode = me.createChild(name) + } + return } func (me *fsInode) Create(name string, flags uint32, mode uint32, context *Context) (file File, fi *os.FileInfo, newNode *fsInode, code Status) { diff --git a/fuse/fsmount.go b/fuse/fsmount.go index 3902b4c315787f279d5edd2ed6afde54306db74d..e493d5652c38684d2535287e93bd9bdb4ae22fe0 100644 --- a/fuse/fsmount.go +++ b/fuse/fsmount.go @@ -47,17 +47,17 @@ func (me *fileSystemMount) setOwner(attr *Attr) { attr.Owner = *me.options.Owner } } -func (me *fileSystemMount) fileInfoToEntry(fi *os.FileInfo, out *EntryOut) { +func (me *fileSystemMount) fileInfoToEntry(fi *os.FileInfo) (out *EntryOut) { + out = &EntryOut{} SplitNs(me.options.EntryTimeout, &out.EntryValid, &out.EntryValidNsec) SplitNs(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec) + CopyFileInfo(fi, &out.Attr) + me.setOwner(&out.Attr) if !fi.IsDirectory() { fi.Nlink = 1 } - - CopyFileInfo(fi, &out.Attr) - me.setOwner(&out.Attr) + return out } - func (me *fileSystemMount) fileInfoToAttr(fi *os.FileInfo, out *AttrOut) { CopyFileInfo(fi, &out.Attr) diff --git a/fuse/fsops.go b/fuse/fsops.go index 6e654f0de1696c5f4991d96feeab8afce5774b65..4fcea9145472f0150c764121cb98df08a59d1e2c 100644 --- a/fuse/fsops.go +++ b/fuse/fsops.go @@ -33,11 +33,11 @@ func (me *FileSystemConnector) internalMountLookup(mount *fileSystemMount, looku mount.treeLock.Lock() defer mount.treeLock.Unlock() mount.mountInode.lookupCount += lookupCount - out = &EntryOut{ - NodeId: mount.mountInode.nodeId, - Generation: 1, // where to get the generation? - } - mount.fileInfoToEntry(fi, out) + out = mount.fileInfoToEntry(fi) + out.NodeId = mount.mountInode.nodeId + + // We don't do NFS. + out.Generation = 1 return out, OK, mount.mountInode } @@ -60,11 +60,9 @@ func (me *FileSystemConnector) internalLookup(parent *inode, name string, lookup } if child != nil && code.Ok() { - out = &EntryOut{ - NodeId: child.nodeId, - Generation: 1, // where to get the generation? - } - parent.mount.fileInfoToEntry(fi, out) + out = parent.mount.fileInfoToEntry(fi) + out.NodeId = child.nodeId + out.Generation = 1 return out, OK, child } @@ -203,8 +201,7 @@ func (me *FileSystemConnector) Mknod(header *InHeader, input *MknodIn, name stri func (me *FileSystemConnector) createChild(parent *inode, name string, fi *os.FileInfo, fsi *fsInode) (out *EntryOut, child *inode) { child = parent.createChild(name, fi.IsDirectory(), fsi, me) - out = &EntryOut{} - parent.mount.fileInfoToEntry(fi, out) + out = parent.mount.fileInfoToEntry(fi) out.Ino = child.nodeId out.NodeId = child.nodeId return out, child @@ -270,20 +267,26 @@ func (me *FileSystemConnector) Rename(header *InHeader, input *RenameIn, oldName return code } -func (me *FileSystemConnector) Link(header *InHeader, input *LinkIn, filename string) (out *EntryOut, code Status) { +func (me *FileSystemConnector) Link(header *InHeader, input *LinkIn, name string) (out *EntryOut, code Status) { existing := me.getInodeData(input.Oldnodeid) parent := me.getInodeData(header.NodeId) if existing.mount != parent.mount { return nil, EXDEV } - - code = parent.fsInode.Link(filename, existing.fsInode, &header.Context) + + fi, fsInode, code := parent.fsInode.Link(name, existing.fsInode, &header.Context) if !code.Ok() { return nil, code } - // TODO - revise this for real hardlinks? - out, code, _ = me.internalLookup(parent, filename, 1, &header.Context) + + if fsInode.Inode() == nil { + out, _ = me.createChild(parent, name, fi, fsInode) + } else { + out = parent.mount.fileInfoToEntry(fi) + out.Ino = fsInode.Inode().nodeId + out.NodeId = out.Ino + } return out, code } diff --git a/fuse/loopback_test.go b/fuse/loopback_test.go index ee6163eb1a6913286a7f265a7f0adcdc118438db..b4cb79c30fde64d59efafbe8cf2878c2358e55a3 100644 --- a/fuse/loopback_test.go +++ b/fuse/loopback_test.go @@ -243,11 +243,17 @@ func TestLink(t *testing.T) { err = os.Link(me.mountFile, mountSubfile) CheckSuccess(err) + subfi, err := os.Lstat(mountSubfile) + CheckSuccess(err) fi, err := os.Lstat(me.origFile) + CheckSuccess(err) + if fi.Nlink != 2 { t.Errorf("Expect 2 links: %v", fi) } - + if fi.Ino != subfi.Ino { + t.Errorf("Link succeeded, but inode numbers different: %v %v", fi.Ino, subfi.Ino) + } f, err := os.Open(mountSubfile) var buf [1024]byte