From 420ae905b56a0a918fa7fc33a0c31e567edb1e84 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 25 Nov 2019 11:31:44 +0000 Subject: [PATCH] vfs: make sure existing files opened for write show correct size Before this change if an existing file was opened for write without truncate its size would show as 0 rather than the full size of the file. --- vfs/file.go | 8 ++- vfs/read_write_test.go | 118 +++++++++++++++++++++++++++++++++++------ 2 files changed, 110 insertions(+), 16 deletions(-) diff --git a/vfs/file.go b/vfs/file.go index eeecfddb1..ab6dbee40 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -37,13 +37,19 @@ type File struct { } // newFile creates a new File +// +// o may be nil func newFile(d *Dir, o fs.Object, leaf string) *File { - return &File{ + f := &File{ d: d, o: o, leaf: leaf, inode: newInode(), } + if o != nil { + f.size = o.Size() + } + return f } // String converts it to printable diff --git a/vfs/read_write_test.go b/vfs/read_write_test.go index 4bdfca579..948d8b082 100644 --- a/vfs/read_write_test.go +++ b/vfs/read_write_test.go @@ -21,16 +21,18 @@ func cleanup(t *testing.T, r *fstest.Run, vfs *VFS) { r.Finalise() } -// Open a file for write -func rwHandleCreateReadOnly(t *testing.T, r *fstest.Run) (*VFS, *RWFileHandle) { +// Create a file and open it with the flags passed in +func rwHandleCreateFlags(t *testing.T, r *fstest.Run, create bool, filename string, flags int) (*VFS, *RWFileHandle) { opt := DefaultOpt opt.CacheMode = CacheModeFull vfs := New(r.Fremote, &opt) - file1 := r.WriteObject(context.Background(), "dir/file1", "0123456789abcdef", t1) - fstest.CheckItems(t, r.Fremote, file1) + if create { + file1 := r.WriteObject(context.Background(), filename, "0123456789abcdef", t1) + fstest.CheckItems(t, r.Fremote, file1) + } - h, err := vfs.OpenFile("dir/file1", os.O_RDONLY, 0777) + h, err := vfs.OpenFile(filename, flags, 0777) require.NoError(t, err) fh, ok := h.(*RWFileHandle) require.True(t, ok) @@ -38,18 +40,14 @@ func rwHandleCreateReadOnly(t *testing.T, r *fstest.Run) (*VFS, *RWFileHandle) { return vfs, fh } +// Open a file for read +func rwHandleCreateReadOnly(t *testing.T, r *fstest.Run) (*VFS, *RWFileHandle) { + return rwHandleCreateFlags(t, r, true, "dir/file1", os.O_RDONLY) +} + // Open a file for write func rwHandleCreateWriteOnly(t *testing.T, r *fstest.Run) (*VFS, *RWFileHandle) { - opt := DefaultOpt - opt.CacheMode = CacheModeFull - vfs := New(r.Fremote, &opt) - - h, err := vfs.OpenFile("file1", os.O_WRONLY|os.O_CREATE, 0777) - require.NoError(t, err) - fh, ok := h.(*RWFileHandle) - require.True(t, ok) - - return vfs, fh + return rwHandleCreateFlags(t, r, false, "file1", os.O_WRONLY|os.O_CREATE) } // read data from the string @@ -494,6 +492,96 @@ func TestRWFileHandleReleaseWrite(t *testing.T) { assert.True(t, fh.closed) } +// check the size of the file through the open file (if not nil) and via stat +func assertSize(t *testing.T, vfs *VFS, fh *RWFileHandle, filepath string, size int64) { + if fh != nil { + assert.Equal(t, size, fh.Size()) + } + fi, err := vfs.Stat(filepath) + require.NoError(t, err) + assert.Equal(t, size, fi.Size()) +} + +func TestRWFileHandleSizeTruncateExisting(t *testing.T) { + r := fstest.NewRun(t) + vfs, fh := rwHandleCreateFlags(t, r, true, "dir/file1", os.O_WRONLY|os.O_TRUNC) + defer cleanup(t, r, vfs) + + // check initial size after opening + assertSize(t, vfs, fh, "dir/file1", 0) + + // write some bytes + n, err := fh.Write([]byte("hello")) + assert.NoError(t, err) + assert.Equal(t, 5, n) + + // check size after writing + assertSize(t, vfs, fh, "dir/file1", 5) + + // close + assert.NoError(t, fh.Close()) + + // check size after close + assertSize(t, vfs, nil, "dir/file1", 5) +} + +func TestRWFileHandleSizeCreateExisting(t *testing.T) { + r := fstest.NewRun(t) + vfs, fh := rwHandleCreateFlags(t, r, true, "dir/file1", os.O_WRONLY|os.O_CREATE) + defer cleanup(t, r, vfs) + + // check initial size after opening + assertSize(t, vfs, fh, "dir/file1", 16) + + // write some bytes + n, err := fh.Write([]byte("hello")) + assert.NoError(t, err) + assert.Equal(t, 5, n) + + // check size after writing + assertSize(t, vfs, fh, "dir/file1", 16) + + // write some more bytes + n, err = fh.Write([]byte("helloHELLOhello")) + assert.NoError(t, err) + assert.Equal(t, 15, n) + + // check size after writing + assertSize(t, vfs, fh, "dir/file1", 20) + + // close + assert.NoError(t, fh.Close()) + + // check size after close + assertSize(t, vfs, nil, "dir/file1", 20) +} + +func TestRWFileHandleSizeCreateNew(t *testing.T) { + r := fstest.NewRun(t) + vfs, fh := rwHandleCreateFlags(t, r, false, "file1", os.O_WRONLY|os.O_CREATE) + defer cleanup(t, r, vfs) + + // check initial size after opening + assertSize(t, vfs, fh, "file1", 0) + + // write some bytes + n, err := fh.Write([]byte("hello")) + assert.NoError(t, err) + assert.Equal(t, 5, n) + + // check size after writing + assertSize(t, vfs, fh, "file1", 5) + + // check size after writing + assertSize(t, vfs, fh, "file1", 5) + + // close + assert.NoError(t, fh.Close()) + + // check size after close + assertSize(t, vfs, nil, "file1", 5) +} + func testRWFileHandleOpenTest(t *testing.T, vfs *VFS, test *openTest) { fileName := "open-test-file"