Fixed mtime tracking, tightened up other tests.
commit
720e853031
|
@ -74,7 +74,7 @@ func NewMemFS(
|
|||
Mode: 0700 | os.ModeDir,
|
||||
}
|
||||
|
||||
fs.inodes[fuse.RootInodeID] = newInode(rootAttrs)
|
||||
fs.inodes[fuse.RootInodeID] = newInode(clock, rootAttrs)
|
||||
|
||||
// Set up invariant checking.
|
||||
fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)
|
||||
|
@ -163,7 +163,7 @@ func (fs *memFS) getInodeForReadingOrDie(id fuse.InodeID) (inode *inode) {
|
|||
func (fs *memFS) allocateInode(
|
||||
attrs fuse.InodeAttributes) (id fuse.InodeID, inode *inode) {
|
||||
// Create and lock the inode.
|
||||
inode = newInode(attrs)
|
||||
inode = newInode(fs.clock, attrs)
|
||||
inode.mu.Lock()
|
||||
|
||||
// Re-use a free ID if possible. Otherwise mint a new one.
|
||||
|
@ -281,15 +281,10 @@ func (fs *memFS) MkDir(
|
|||
|
||||
// Set up attributes from the child, using the credentials of the calling
|
||||
// process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8).
|
||||
now := fs.clock.Now()
|
||||
childAttrs := fuse.InodeAttributes{
|
||||
Mode: req.Mode,
|
||||
Atime: now,
|
||||
Mtime: now,
|
||||
Ctime: now,
|
||||
Crtime: now,
|
||||
Uid: req.Header.Uid,
|
||||
Gid: req.Header.Gid,
|
||||
Mode: req.Mode,
|
||||
Uid: req.Header.Uid,
|
||||
Gid: req.Header.Gid,
|
||||
}
|
||||
|
||||
// Allocate a child.
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/jacobsa/fuse"
|
||||
"github.com/jacobsa/fuse/fuseutil"
|
||||
"github.com/jacobsa/gcloud/syncutil"
|
||||
"github.com/jacobsa/gcsfuse/timeutil"
|
||||
)
|
||||
|
||||
// Common attributes for files and directories.
|
||||
|
@ -30,6 +31,12 @@ import (
|
|||
// been unlinked, including creating a new file. Make sure we don't screw up
|
||||
// and reuse an inode ID while it is still in use.
|
||||
type inode struct {
|
||||
/////////////////////////
|
||||
// Dependencies
|
||||
/////////////////////////
|
||||
|
||||
clock timeutil.Clock
|
||||
|
||||
/////////////////////////
|
||||
// Constant data
|
||||
/////////////////////////
|
||||
|
@ -84,9 +91,20 @@ type inode struct {
|
|||
// Helpers
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Create a new inode with the supplied attributes, which need not contain
|
||||
// time-related information (the inode object will take care of that).
|
||||
// Initially the link count is one.
|
||||
func newInode(attrs fuse.InodeAttributes) (in *inode) {
|
||||
func newInode(
|
||||
clock timeutil.Clock,
|
||||
attrs fuse.InodeAttributes) (in *inode) {
|
||||
// Update time info.
|
||||
now := clock.Now()
|
||||
attrs.Mtime = now
|
||||
attrs.Crtime = now
|
||||
|
||||
// Create the object.
|
||||
in = &inode{
|
||||
clock: clock,
|
||||
linkCount: 1,
|
||||
dir: (attrs.Mode&os.ModeDir != 0),
|
||||
attributes: attrs,
|
||||
|
@ -217,6 +235,9 @@ func (inode *inode) AddChild(
|
|||
dt fuseutil.DirentType) {
|
||||
var index int
|
||||
|
||||
// Update the modification time.
|
||||
inode.attributes.Mtime = inode.clock.Now()
|
||||
|
||||
// No matter where we place the entry, make sure it has the correct Offset
|
||||
// field.
|
||||
defer func() {
|
||||
|
@ -249,6 +270,9 @@ func (inode *inode) AddChild(
|
|||
// REQUIRES: An entry for the given name exists.
|
||||
// EXCLUSIVE_LOCKS_REQUIRED(inode.mu)
|
||||
func (inode *inode) RemoveChild(name string) {
|
||||
// Update the modification time.
|
||||
inode.attributes.Mtime = inode.clock.Now()
|
||||
|
||||
// Find the entry.
|
||||
i, ok := inode.findChild(name)
|
||||
if !ok {
|
||||
|
@ -324,6 +348,9 @@ func (inode *inode) WriteAt(p []byte, off int64) (n int, err error) {
|
|||
panic("WriteAt called on directory.")
|
||||
}
|
||||
|
||||
// Update the modification time.
|
||||
inode.attributes.Mtime = inode.clock.Now()
|
||||
|
||||
// Ensure that the contents slice is long enough.
|
||||
newLen := int(off) + len(p)
|
||||
if len(inode.contents) < newLen {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package memfs_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -79,6 +80,9 @@ func timespecToTime(ts syscall.Timespec) time.Time {
|
|||
type MemFSTest struct {
|
||||
clock timeutil.SimulatedClock
|
||||
mfs *fuse.MountedFileSystem
|
||||
|
||||
// Files to close when tearing down. Nil entries are skipped.
|
||||
toClose []io.Closer
|
||||
}
|
||||
|
||||
var _ SetUpInterface = &MemFSTest{}
|
||||
|
@ -110,6 +114,18 @@ func (t *MemFSTest) SetUp(ti *TestInfo) {
|
|||
}
|
||||
|
||||
func (t *MemFSTest) TearDown() {
|
||||
// Close any files we opened.
|
||||
for _, c := range t.toClose {
|
||||
if c == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err := c.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Unmount the file system. Try again on "resource busy" errors.
|
||||
delay := 10 * time.Millisecond
|
||||
for {
|
||||
|
@ -152,6 +168,9 @@ func (t *MemFSTest) Mkdir_OneLevel() {
|
|||
|
||||
dirName := path.Join(t.mfs.Dir(), "dir")
|
||||
|
||||
// Simulate time advancing.
|
||||
t.clock.AdvanceTime(time.Second)
|
||||
|
||||
// Create a directory within the root.
|
||||
createTime := t.clock.Now()
|
||||
err = os.Mkdir(dirName, 0754)
|
||||
|
@ -176,9 +195,14 @@ func (t *MemFSTest) Mkdir_OneLevel() {
|
|||
ExpectEq(currentUid(), stat.Uid)
|
||||
ExpectEq(currentGid(), stat.Gid)
|
||||
ExpectEq(0, stat.Size)
|
||||
ExpectEq(0, timespecToTime(stat.Atimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Ctimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime))
|
||||
|
||||
// Check the root's mtime.
|
||||
fi, err = os.Stat(t.mfs.Dir())
|
||||
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(0, fi.ModTime().Sub(createTime))
|
||||
|
||||
// Read the directory.
|
||||
entries, err = ioutil.ReadDir(dirName)
|
||||
|
@ -207,6 +231,9 @@ func (t *MemFSTest) Mkdir_TwoLevels() {
|
|||
err = os.Mkdir(path.Join(t.mfs.Dir(), "parent"), 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Simulate time advancing.
|
||||
t.clock.AdvanceTime(time.Second)
|
||||
|
||||
// Create a child of that directory.
|
||||
createTime := t.clock.Now()
|
||||
err = os.Mkdir(path.Join(t.mfs.Dir(), "parent/dir"), 0754)
|
||||
|
@ -231,9 +258,13 @@ func (t *MemFSTest) Mkdir_TwoLevels() {
|
|||
ExpectEq(currentUid(), stat.Uid)
|
||||
ExpectEq(currentGid(), stat.Gid)
|
||||
ExpectEq(0, stat.Size)
|
||||
ExpectEq(0, timespecToTime(stat.Atimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Ctimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime))
|
||||
|
||||
// Check the parent's mtime.
|
||||
fi, err = os.Stat(path.Join(t.mfs.Dir(), "parent"))
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(0, fi.ModTime().Sub(createTime))
|
||||
|
||||
// Read the directory.
|
||||
entries, err = ioutil.ReadDir(path.Join(t.mfs.Dir(), "parent/dir"))
|
||||
|
@ -313,10 +344,10 @@ func (t *MemFSTest) CreateNewFile_InRoot() {
|
|||
var fi os.FileInfo
|
||||
var stat *syscall.Stat_t
|
||||
|
||||
// Write a file.
|
||||
fileName := path.Join(t.mfs.Dir(), "foo")
|
||||
const contents = "Hello\x00world"
|
||||
|
||||
// Write a file.
|
||||
createTime := t.clock.Now()
|
||||
err = ioutil.WriteFile(fileName, []byte(contents), 0400)
|
||||
AssertEq(nil, err)
|
||||
|
@ -340,9 +371,8 @@ func (t *MemFSTest) CreateNewFile_InRoot() {
|
|||
ExpectEq(currentUid(), stat.Uid)
|
||||
ExpectEq(currentGid(), stat.Gid)
|
||||
ExpectEq(len(contents), stat.Size)
|
||||
ExpectEq(0, timespecToTime(stat.Atimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Ctimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime))
|
||||
|
||||
// Read it back.
|
||||
slice, err := ioutil.ReadFile(fileName)
|
||||
|
@ -351,11 +381,103 @@ func (t *MemFSTest) CreateNewFile_InRoot() {
|
|||
}
|
||||
|
||||
func (t *MemFSTest) CreateNewFile_InSubDir() {
|
||||
AssertTrue(false, "TODO")
|
||||
var err error
|
||||
var fi os.FileInfo
|
||||
var stat *syscall.Stat_t
|
||||
|
||||
// Create a sub-dir.
|
||||
dirName := path.Join(t.mfs.Dir(), "dir")
|
||||
err = os.Mkdir(dirName, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Write a file.
|
||||
fileName := path.Join(dirName, "foo")
|
||||
const contents = "Hello\x00world"
|
||||
|
||||
createTime := t.clock.Now()
|
||||
err = ioutil.WriteFile(fileName, []byte(contents), 0400)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Simulate time advancing.
|
||||
t.clock.AdvanceTime(time.Second)
|
||||
|
||||
// Stat it.
|
||||
fi, err = os.Stat(fileName)
|
||||
stat = fi.Sys().(*syscall.Stat_t)
|
||||
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("foo", fi.Name())
|
||||
ExpectEq(len(contents), fi.Size())
|
||||
ExpectEq(0400, fi.Mode())
|
||||
ExpectEq(0, fi.ModTime().Sub(createTime))
|
||||
ExpectFalse(fi.IsDir())
|
||||
|
||||
ExpectNe(0, stat.Ino)
|
||||
ExpectEq(1, stat.Nlink)
|
||||
ExpectEq(currentUid(), stat.Uid)
|
||||
ExpectEq(currentGid(), stat.Gid)
|
||||
ExpectEq(len(contents), stat.Size)
|
||||
ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime))
|
||||
ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime))
|
||||
|
||||
// Read it back.
|
||||
slice, err := ioutil.ReadFile(fileName)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(contents, string(slice))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) ModifyExistingFile_InRoot() {
|
||||
AssertTrue(false, "TODO")
|
||||
var err error
|
||||
var n int
|
||||
var fi os.FileInfo
|
||||
var stat *syscall.Stat_t
|
||||
|
||||
// Write a file.
|
||||
fileName := path.Join(t.mfs.Dir(), "foo")
|
||||
|
||||
createTime := t.clock.Now()
|
||||
err = ioutil.WriteFile(fileName, []byte("Jello, world!"), 0600)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Simulate time advancing.
|
||||
t.clock.AdvanceTime(time.Second)
|
||||
|
||||
// Open the file and modify it.
|
||||
f, err := os.OpenFile(fileName, os.O_WRONLY, 0400)
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
modifyTime := t.clock.Now()
|
||||
n, err = f.WriteAt([]byte("H"), 0)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, n)
|
||||
|
||||
// Simulate time advancing.
|
||||
t.clock.AdvanceTime(time.Second)
|
||||
|
||||
// Stat the file.
|
||||
fi, err = os.Stat(fileName)
|
||||
stat = fi.Sys().(*syscall.Stat_t)
|
||||
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("foo", fi.Name())
|
||||
ExpectEq(len("Hello, world!"), fi.Size())
|
||||
ExpectEq(0600, fi.Mode())
|
||||
ExpectEq(0, fi.ModTime().Sub(modifyTime))
|
||||
ExpectFalse(fi.IsDir())
|
||||
|
||||
ExpectNe(0, stat.Ino)
|
||||
ExpectEq(1, stat.Nlink)
|
||||
ExpectEq(currentUid(), stat.Uid)
|
||||
ExpectEq(currentGid(), stat.Gid)
|
||||
ExpectEq(len("Hello, world!"), stat.Size)
|
||||
ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(modifyTime))
|
||||
ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime))
|
||||
|
||||
// Read the file back.
|
||||
slice, err := ioutil.ReadFile(fileName)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("Hello, world!", string(slice))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) ModifyExistingFile_InSubDir() {
|
||||
|
@ -400,16 +522,28 @@ func (t *MemFSTest) Rmdir_Empty() {
|
|||
err = os.MkdirAll(path.Join(t.mfs.Dir(), "foo/bar"), 0754)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Simulate time advancing.
|
||||
t.clock.AdvanceTime(time.Second)
|
||||
|
||||
// Remove the leaf.
|
||||
rmTime := t.clock.Now()
|
||||
err = os.Remove(path.Join(t.mfs.Dir(), "foo/bar"))
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Simulate time advancing.
|
||||
t.clock.AdvanceTime(time.Second)
|
||||
|
||||
// There should be nothing left in the parent.
|
||||
entries, err = ioutil.ReadDir(path.Join(t.mfs.Dir(), "foo"))
|
||||
|
||||
AssertEq(nil, err)
|
||||
ExpectThat(entries, ElementsAre())
|
||||
|
||||
// Check the parent's mtime.
|
||||
fi, err := os.Stat(path.Join(t.mfs.Dir(), "foo"))
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(0, fi.ModTime().Sub(rmTime))
|
||||
|
||||
// Remove the parent.
|
||||
err = os.Remove(path.Join(t.mfs.Dir(), "foo"))
|
||||
AssertEq(nil, err)
|
||||
|
@ -515,6 +649,210 @@ func (t *MemFSTest) FileReadsAndWrites() {
|
|||
AssertTrue(false, "TODO")
|
||||
}
|
||||
|
||||
func (t *MemFSTest) FileReadsAndWrites_BeyondEOF() {
|
||||
AssertTrue(false, "TODO")
|
||||
func (t *MemFSTest) WriteOverlapsEndOfFile() {
|
||||
var err error
|
||||
var n int
|
||||
|
||||
// Create a file.
|
||||
f, err := os.Create(path.Join(t.mfs.Dir(), "foo"))
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Make it 4 bytes long.
|
||||
err = f.Truncate(4)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Write the range [2, 6).
|
||||
n, err = f.WriteAt([]byte("taco"), 2)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(4, n)
|
||||
|
||||
// Read the full contents of the file.
|
||||
contents, err := ioutil.ReadAll(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("\x00\x00taco", string(contents))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) WriteStartsAtEndOfFile() {
|
||||
var err error
|
||||
var n int
|
||||
|
||||
// Create a file.
|
||||
f, err := os.Create(path.Join(t.mfs.Dir(), "foo"))
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Make it 2 bytes long.
|
||||
err = f.Truncate(2)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Write the range [2, 6).
|
||||
n, err = f.WriteAt([]byte("taco"), 2)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(4, n)
|
||||
|
||||
// Read the full contents of the file.
|
||||
contents, err := ioutil.ReadAll(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("\x00\x00taco", string(contents))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) WriteStartsPastEndOfFile() {
|
||||
var err error
|
||||
var n int
|
||||
|
||||
// Create a file.
|
||||
f, err := os.Create(path.Join(t.mfs.Dir(), "foo"))
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Write the range [2, 6).
|
||||
n, err = f.WriteAt([]byte("taco"), 2)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(4, n)
|
||||
|
||||
// Read the full contents of the file.
|
||||
contents, err := ioutil.ReadAll(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("\x00\x00taco", string(contents))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) WriteAtDoesntChangeOffset_NotAppendMode() {
|
||||
var err error
|
||||
var n int
|
||||
|
||||
// Create a file.
|
||||
f, err := os.Create(path.Join(t.mfs.Dir(), "foo"))
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Make it 16 bytes long.
|
||||
err = f.Truncate(16)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Seek to offset 4.
|
||||
_, err = f.Seek(4, 0)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Write the range [10, 14).
|
||||
n, err = f.WriteAt([]byte("taco"), 2)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(4, n)
|
||||
|
||||
// We should still be at offset 4.
|
||||
offset, err := getFileOffset(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(4, offset)
|
||||
}
|
||||
|
||||
func (t *MemFSTest) WriteAtDoesntChangeOffset_AppendMode() {
|
||||
var err error
|
||||
var n int
|
||||
|
||||
// Create a file in append mode.
|
||||
f, err := os.OpenFile(
|
||||
path.Join(t.mfs.Dir(), "foo"),
|
||||
os.O_RDWR|os.O_APPEND|os.O_CREATE,
|
||||
0600)
|
||||
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Make it 16 bytes long.
|
||||
err = f.Truncate(16)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Seek to offset 4.
|
||||
_, err = f.Seek(4, 0)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Write the range [10, 14).
|
||||
n, err = f.WriteAt([]byte("taco"), 2)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(4, n)
|
||||
|
||||
// We should still be at offset 4.
|
||||
offset, err := getFileOffset(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(4, offset)
|
||||
}
|
||||
|
||||
func (t *MemFSTest) AppendMode() {
|
||||
var err error
|
||||
var n int
|
||||
var off int64
|
||||
buf := make([]byte, 1024)
|
||||
|
||||
// Create a file with some contents.
|
||||
fileName := path.Join(t.mfs.Dir(), "foo")
|
||||
err = ioutil.WriteFile(fileName, []byte("Jello, "), 0600)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Open the file in append mode.
|
||||
f, err := os.OpenFile(fileName, os.O_RDWR|os.O_APPEND, 0600)
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Seek to somewhere silly and then write.
|
||||
off, err = f.Seek(2, 0)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(2, off)
|
||||
|
||||
n, err = f.Write([]byte("world!"))
|
||||
AssertEq(nil, err)
|
||||
AssertEq(6, n)
|
||||
|
||||
// The offset should have been updated to point at the end of the file.
|
||||
off, err = getFileOffset(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(13, off)
|
||||
|
||||
// A random write should still work, without updating the offset.
|
||||
n, err = f.WriteAt([]byte("H"), 0)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, n)
|
||||
|
||||
off, err = getFileOffset(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(13, off)
|
||||
|
||||
// Read back the contents of the file, which should be correct even though we
|
||||
// seeked to a silly place before writing the world part.
|
||||
n, err = f.ReadAt(buf, 0)
|
||||
AssertEq(io.EOF, err)
|
||||
ExpectEq("Hello, world!", string(buf[:n]))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) ReadsPastEndOfFile() {
|
||||
var err error
|
||||
var n int
|
||||
buf := make([]byte, 1024)
|
||||
|
||||
// Create a file.
|
||||
f, err := os.Create(path.Join(t.mfs.Dir(), "foo"))
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Give it some contents.
|
||||
n, err = f.Write([]byte("taco"))
|
||||
AssertEq(nil, err)
|
||||
AssertEq(4, n)
|
||||
|
||||
// Read a range overlapping EOF.
|
||||
n, err = f.ReadAt(buf[:4], 2)
|
||||
AssertEq(io.EOF, err)
|
||||
ExpectEq(2, n)
|
||||
ExpectEq("co", string(buf[:n]))
|
||||
|
||||
// Read a range starting at EOF.
|
||||
n, err = f.ReadAt(buf[:4], 4)
|
||||
AssertEq(io.EOF, err)
|
||||
ExpectEq(0, n)
|
||||
ExpectEq("", string(buf[:n]))
|
||||
|
||||
// Read a range starting past EOF.
|
||||
n, err = f.ReadAt(buf[:4], 100)
|
||||
AssertEq(io.EOF, err)
|
||||
ExpectEq(0, n)
|
||||
ExpectEq("", string(buf[:n]))
|
||||
}
|
||||
|
|
|
@ -218,6 +218,52 @@ func (t *PosixTest) WriteAtDoesntChangeOffset_AppendMode() {
|
|||
ExpectEq(4, offset)
|
||||
}
|
||||
|
||||
func (t *PosixTest) AppendMode() {
|
||||
var err error
|
||||
var n int
|
||||
var off int64
|
||||
buf := make([]byte, 1024)
|
||||
|
||||
// Create a file with some contents.
|
||||
fileName := path.Join(t.dir, "foo")
|
||||
err = ioutil.WriteFile(fileName, []byte("Jello, "), 0600)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Open the file in append mode.
|
||||
f, err := os.OpenFile(fileName, os.O_RDWR|os.O_APPEND, 0600)
|
||||
t.toClose = append(t.toClose, f)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Seek to somewhere silly and then write.
|
||||
off, err = f.Seek(2, 0)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(2, off)
|
||||
|
||||
n, err = f.Write([]byte("world!"))
|
||||
AssertEq(nil, err)
|
||||
AssertEq(6, n)
|
||||
|
||||
// The offset should have been updated to point at the end of the file.
|
||||
off, err = getFileOffset(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(13, off)
|
||||
|
||||
// A random write should still work, without updating the offset.
|
||||
n, err = f.WriteAt([]byte("H"), 0)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, n)
|
||||
|
||||
off, err = getFileOffset(f)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(13, off)
|
||||
|
||||
// Read back the contents of the file, which should be correct even though we
|
||||
// seeked to a silly place before writing the world part.
|
||||
n, err = f.ReadAt(buf, 0)
|
||||
AssertEq(io.EOF, err)
|
||||
ExpectEq("Hello, world!", string(buf[:n]))
|
||||
}
|
||||
|
||||
func (t *PosixTest) ReadsPastEndOfFile() {
|
||||
var err error
|
||||
var n int
|
||||
|
|
Loading…
Reference in New Issue