Commit 68d48f61 authored by Aaron Jacobs's avatar Aaron Jacobs

Added an Nlink field and updated tests and samples to use it.

For #2.
parents c1c54788 cd4980bf
...@@ -265,6 +265,9 @@ func init() { ...@@ -265,6 +265,9 @@ func init() {
type InodeAttributes struct { type InodeAttributes struct {
Size uint64 Size uint64
// The number of incoming hard links to this inode.
Nlink uint64
// The mode of the inode. This is exposed to the user in e.g. the result of // The mode of the inode. This is exposed to the user in e.g. the result of
// fstat(2). // fstat(2).
// //
......
...@@ -164,6 +164,7 @@ func (fs *cachingFS) rootAttrs() fuse.InodeAttributes { ...@@ -164,6 +164,7 @@ func (fs *cachingFS) rootAttrs() fuse.InodeAttributes {
// LOCKS_REQUIRED(fs.mu) // LOCKS_REQUIRED(fs.mu)
func (fs *cachingFS) fooAttrs() fuse.InodeAttributes { func (fs *cachingFS) fooAttrs() fuse.InodeAttributes {
return fuse.InodeAttributes{ return fuse.InodeAttributes{
Nlink: 1,
Size: FooSize, Size: FooSize,
Mode: 0777, Mode: 0777,
Mtime: fs.mtime, Mtime: fs.mtime,
...@@ -173,6 +174,7 @@ func (fs *cachingFS) fooAttrs() fuse.InodeAttributes { ...@@ -173,6 +174,7 @@ func (fs *cachingFS) fooAttrs() fuse.InodeAttributes {
// LOCKS_REQUIRED(fs.mu) // LOCKS_REQUIRED(fs.mu)
func (fs *cachingFS) dirAttrs() fuse.InodeAttributes { func (fs *cachingFS) dirAttrs() fuse.InodeAttributes {
return fuse.InodeAttributes{ return fuse.InodeAttributes{
Nlink: 1,
Mode: os.ModeDir | 0777, Mode: os.ModeDir | 0777,
Mtime: fs.mtime, Mtime: fs.mtime,
} }
...@@ -181,6 +183,7 @@ func (fs *cachingFS) dirAttrs() fuse.InodeAttributes { ...@@ -181,6 +183,7 @@ func (fs *cachingFS) dirAttrs() fuse.InodeAttributes {
// LOCKS_REQUIRED(fs.mu) // LOCKS_REQUIRED(fs.mu)
func (fs *cachingFS) barAttrs() fuse.InodeAttributes { func (fs *cachingFS) barAttrs() fuse.InodeAttributes {
return fuse.InodeAttributes{ return fuse.InodeAttributes{
Nlink: 1,
Size: BarSize, Size: BarSize,
Mode: 0777, Mode: 0777,
Mtime: fs.mtime, Mtime: fs.mtime,
......
...@@ -201,6 +201,7 @@ func (t *BasicsTest) StatFoo() { ...@@ -201,6 +201,7 @@ func (t *BasicsTest) StatFoo() {
ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime)) ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
ExpectFalse(fi.IsDir()) ExpectFalse(fi.IsDir())
ExpectEq(t.fs.FooID(), getInodeID(fi)) ExpectEq(t.fs.FooID(), getInodeID(fi))
ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
} }
func (t *BasicsTest) StatDir() { func (t *BasicsTest) StatDir() {
...@@ -212,6 +213,7 @@ func (t *BasicsTest) StatDir() { ...@@ -212,6 +213,7 @@ func (t *BasicsTest) StatDir() {
ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime)) ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
ExpectTrue(fi.IsDir()) ExpectTrue(fi.IsDir())
ExpectEq(t.fs.DirID(), getInodeID(fi)) ExpectEq(t.fs.DirID(), getInodeID(fi))
ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
} }
func (t *BasicsTest) StatBar() { func (t *BasicsTest) StatBar() {
...@@ -224,6 +226,7 @@ func (t *BasicsTest) StatBar() { ...@@ -224,6 +226,7 @@ func (t *BasicsTest) StatBar() {
ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime)) ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
ExpectFalse(fi.IsDir()) ExpectFalse(fi.IsDir())
ExpectEq(t.fs.BarID(), getInodeID(fi)) ExpectEq(t.fs.BarID(), getInodeID(fi))
ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
} }
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
......
...@@ -61,6 +61,7 @@ var gInodeInfo = map[fuse.InodeID]inodeInfo{ ...@@ -61,6 +61,7 @@ var gInodeInfo = map[fuse.InodeID]inodeInfo{
// root // root
rootInode: inodeInfo{ rootInode: inodeInfo{
attributes: fuse.InodeAttributes{ attributes: fuse.InodeAttributes{
Nlink: 1,
Mode: 0555 | os.ModeDir, Mode: 0555 | os.ModeDir,
}, },
dir: true, dir: true,
...@@ -83,6 +84,7 @@ var gInodeInfo = map[fuse.InodeID]inodeInfo{ ...@@ -83,6 +84,7 @@ var gInodeInfo = map[fuse.InodeID]inodeInfo{
// hello // hello
helloInode: inodeInfo{ helloInode: inodeInfo{
attributes: fuse.InodeAttributes{ attributes: fuse.InodeAttributes{
Nlink: 1,
Mode: 0444, Mode: 0444,
Size: uint64(len("Hello, world!")), Size: uint64(len("Hello, world!")),
}, },
...@@ -91,6 +93,7 @@ var gInodeInfo = map[fuse.InodeID]inodeInfo{ ...@@ -91,6 +93,7 @@ var gInodeInfo = map[fuse.InodeID]inodeInfo{
// dir // dir
dirInode: inodeInfo{ dirInode: inodeInfo{
attributes: fuse.InodeAttributes{ attributes: fuse.InodeAttributes{
Nlink: 1,
Mode: 0555 | os.ModeDir, Mode: 0555 | os.ModeDir,
}, },
dir: true, dir: true,
...@@ -107,6 +110,7 @@ var gInodeInfo = map[fuse.InodeID]inodeInfo{ ...@@ -107,6 +110,7 @@ var gInodeInfo = map[fuse.InodeID]inodeInfo{
// world // world
worldInode: inodeInfo{ worldInode: inodeInfo{
attributes: fuse.InodeAttributes{ attributes: fuse.InodeAttributes{
Nlink: 1,
Mode: 0444, Mode: 0444,
Size: uint64(len("Hello, world!")), Size: uint64(len("Hello, world!")),
}, },
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
"syscall"
"testing" "testing"
"time" "time"
...@@ -158,6 +159,7 @@ func (t *HelloFSTest) Stat_Hello() { ...@@ -158,6 +159,7 @@ func (t *HelloFSTest) Stat_Hello() {
ExpectEq(0444, fi.Mode()) ExpectEq(0444, fi.Mode())
ExpectEq(0, t.clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime()) ExpectEq(0, t.clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
ExpectFalse(fi.IsDir()) ExpectFalse(fi.IsDir())
ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
} }
func (t *HelloFSTest) Stat_Dir() { func (t *HelloFSTest) Stat_Dir() {
...@@ -169,6 +171,7 @@ func (t *HelloFSTest) Stat_Dir() { ...@@ -169,6 +171,7 @@ func (t *HelloFSTest) Stat_Dir() {
ExpectEq(0555|os.ModeDir, fi.Mode()) ExpectEq(0555|os.ModeDir, fi.Mode())
ExpectEq(0, t.clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime()) ExpectEq(0, t.clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
ExpectTrue(fi.IsDir()) ExpectTrue(fi.IsDir())
ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
} }
func (t *HelloFSTest) Stat_World() { func (t *HelloFSTest) Stat_World() {
...@@ -180,6 +183,7 @@ func (t *HelloFSTest) Stat_World() { ...@@ -180,6 +183,7 @@ func (t *HelloFSTest) Stat_World() {
ExpectEq(0444, fi.Mode()) ExpectEq(0444, fi.Mode())
ExpectEq(0, t.clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime()) ExpectEq(0, t.clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
ExpectFalse(fi.IsDir()) ExpectFalse(fi.IsDir())
ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
} }
func (t *HelloFSTest) Stat_NonExistent() { func (t *HelloFSTest) Stat_NonExistent() {
......
...@@ -305,6 +305,7 @@ func (fs *memFS) MkDir( ...@@ -305,6 +305,7 @@ func (fs *memFS) MkDir(
// Set up attributes from the child, using the credentials of the calling // Set up attributes from the child, using the credentials of the calling
// process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8). // process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8).
childAttrs := fuse.InodeAttributes{ childAttrs := fuse.InodeAttributes{
Nlink: 1,
Mode: req.Mode, Mode: req.Mode,
Uid: req.Header.Uid, Uid: req.Header.Uid,
Gid: req.Header.Gid, Gid: req.Header.Gid,
...@@ -345,6 +346,7 @@ func (fs *memFS) CreateFile( ...@@ -345,6 +346,7 @@ func (fs *memFS) CreateFile(
// process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8). // process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8).
now := fs.clock.Now() now := fs.clock.Now()
childAttrs := fuse.InodeAttributes{ childAttrs := fuse.InodeAttributes{
Nlink: 1,
Mode: req.Mode, Mode: req.Mode,
Atime: now, Atime: now,
Mtime: now, Mtime: now,
...@@ -408,7 +410,7 @@ func (fs *memFS) RmDir( ...@@ -408,7 +410,7 @@ func (fs *memFS) RmDir(
parent.RemoveChild(req.Name) parent.RemoveChild(req.Name)
// Mark the child as unlinked. // Mark the child as unlinked.
child.linkCount-- child.attributes.Nlink--
return return
} }
...@@ -440,7 +442,7 @@ func (fs *memFS) Unlink( ...@@ -440,7 +442,7 @@ func (fs *memFS) Unlink(
parent.RemoveChild(req.Name) parent.RemoveChild(req.Name)
// Mark the child as unlinked. // Mark the child as unlinked.
child.linkCount-- child.attributes.Nlink--
return return
} }
......
...@@ -47,13 +47,6 @@ type inode struct { ...@@ -47,13 +47,6 @@ type inode struct {
mu syncutil.InvariantMutex mu syncutil.InvariantMutex
// The number of times this inode is linked into a parent directory. This may
// be zero if the inode has been unlinked but not yet forgotten, because some
// process still has an open file handle.
//
// INVARIANT: linkCount >= 0
linkCount int // GUARDED_BY(mu)
// The current attributes of this inode. // The current attributes of this inode.
// //
// INVARIANT: No non-permission mode bits are set besides os.ModeDir // INVARIANT: No non-permission mode bits are set besides os.ModeDir
...@@ -87,7 +80,6 @@ type inode struct { ...@@ -87,7 +80,6 @@ type inode struct {
// Create a new inode with the supplied attributes, which need not contain // Create a new inode with the supplied attributes, which need not contain
// time-related information (the inode object will take care of that). // time-related information (the inode object will take care of that).
// Initially the link count is one.
func newInode( func newInode(
clock timeutil.Clock, clock timeutil.Clock,
attrs fuse.InodeAttributes) (in *inode) { attrs fuse.InodeAttributes) (in *inode) {
...@@ -99,7 +91,6 @@ func newInode( ...@@ -99,7 +91,6 @@ func newInode(
// Create the object. // Create the object.
in = &inode{ in = &inode{
clock: clock, clock: clock,
linkCount: 1,
dir: (attrs.Mode&os.ModeDir != 0), dir: (attrs.Mode&os.ModeDir != 0),
attributes: attrs, attributes: attrs,
} }
...@@ -109,11 +100,6 @@ func newInode( ...@@ -109,11 +100,6 @@ func newInode(
} }
func (inode *inode) checkInvariants() { func (inode *inode) checkInvariants() {
// Check the link count.
if inode.linkCount < 0 {
panic(fmt.Sprintf("Negative link count: %v", inode.linkCount))
}
// No non-permission mode bits should be set besides os.ModeDir. // No non-permission mode bits should be set besides os.ModeDir.
if inode.attributes.Mode & ^(os.ModePerm|os.ModeDir) != 0 { if inode.attributes.Mode & ^(os.ModePerm|os.ModeDir) != 0 {
panic(fmt.Sprintf("Unexpected mode: %v", inode.attributes.Mode)) panic(fmt.Sprintf("Unexpected mode: %v", inode.attributes.Mode))
......
...@@ -602,9 +602,7 @@ func (t *MemFSTest) UnlinkFile_StillOpen() { ...@@ -602,9 +602,7 @@ func (t *MemFSTest) UnlinkFile_StillOpen() {
AssertEq(nil, err) AssertEq(nil, err)
ExpectEq(4, fi.Size()) ExpectEq(4, fi.Size())
// TODO(jacobsa): Re-enable this assertion if the following issue is fixed: ExpectEq(0, fi.Sys().(*syscall.Stat_t).Nlink)
// https://github.com/bazillion/fuse/issues/66
// ExpectEq(0, fi.Sys().(*syscall.Stat_t).Nlink)
// The contents should still be available. // The contents should still be available.
buf := make([]byte, 1024) buf := make([]byte, 1024)
...@@ -724,10 +722,7 @@ func (t *MemFSTest) Rmdir_OpenedForReading() { ...@@ -724,10 +722,7 @@ func (t *MemFSTest) Rmdir_OpenedForReading() {
ExpectEq("dir", fi.Name()) ExpectEq("dir", fi.Name())
ExpectEq(0, fi.ModTime().Sub(createTime)) ExpectEq(0, fi.ModTime().Sub(createTime))
ExpectEq(0, fi.Sys().(*syscall.Stat_t).Nlink)
// TODO(jacobsa): Re-enable this assertion if the following issue is fixed:
// https://github.com/bazillion/fuse/issues/66
// ExpectEq(0, fi.Sys().(*syscall.Stat_t).Nlink)
// Attempt to read from the directory. This shouldn't see any junk from the // Attempt to read from the directory. This shouldn't see any junk from the
// new directory. It should either succeed with an empty result or should // new directory. It should either succeed with an empty result or should
......
...@@ -504,6 +504,7 @@ func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr { ...@@ -504,6 +504,7 @@ func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
Inode: uint64(inode), Inode: uint64(inode),
Size: attr.Size, Size: attr.Size,
Mode: attr.Mode, Mode: attr.Mode,
Nlink: uint32(attr.Nlink),
Atime: attr.Atime, Atime: attr.Atime,
Mtime: attr.Mtime, Mtime: attr.Mtime,
Ctime: attr.Ctime, Ctime: attr.Ctime,
......
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