diff --git a/component/file_cache/file_cache.go b/component/file_cache/file_cache.go index 0a7ca7c6b..ff016f481 100644 --- a/component/file_cache/file_cache.go +++ b/component/file_cache/file_cache.go @@ -965,6 +965,11 @@ func (fc *FileCache) OpenFile(options internal.OpenFileOptions) (*handlemap.Hand // create handle and record openFileOptions for later handle := handlemap.NewHandle(options.Name) handle.SetValue("openFileOptions", openFileOptions{flags: options.Flags, fMode: options.Mode}) + + if options.Flags&os.O_APPEND != 0 { + handle.Flags.Set(handlemap.HandleOpenedAppend) + } + // Increment the handle count in this lock item as there is one handle open for this now flock.Inc() @@ -1117,7 +1122,13 @@ func (fc *FileCache) WriteFile(options internal.WriteFileOptions) (int, error) { // Removing Pwrite as it is not supported on Windows // bytesWritten, err := syscall.Pwrite(options.Handle.FD(), options.Data, options.Offset) - bytesWritten, err := f.WriteAt(options.Data, options.Offset) + + var bytesWritten int + if options.Handle.Flags.IsSet(handlemap.HandleOpenedAppend) { + bytesWritten, err = f.Write(options.Data) + } else { + bytesWritten, err = f.WriteAt(options.Data, options.Offset) + } if err == nil { // Mark the handle dirty so the file is written back to storage on FlushFile. diff --git a/internal/handlemap/handle_map.go b/internal/handlemap/handle_map.go index ba82ba3c0..28e50a4ff 100644 --- a/internal/handlemap/handle_map.go +++ b/internal/handlemap/handle_map.go @@ -43,10 +43,11 @@ const InvalidHandleID HandleID = 0 // Flags represented in BitMap for various flags in the handle const ( - HandleFlagUnknown uint16 = iota - HandleFlagDirty // File has been modified with write operation or is a new file - HandleFlagFSynced // User has called fsync on the file explicitly - HandleFlagCached // File is cached in the local system by cloudfuse + HandleFlagUnknown uint16 = iota + HandleFlagDirty // File has been modified with write operation or is a new file + HandleFlagFSynced // User has called fsync on the file explicitly + HandleFlagCached // File is cached in the local system by cloudfuse + HandleOpenedAppend // File is opened for Append ) // Structure to hold in memory cache for streaming layer diff --git a/test/e2e_tests/file_test.go b/test/e2e_tests/file_test.go index 81a1e8ce4..3f71ea345 100644 --- a/test/e2e_tests/file_test.go +++ b/test/e2e_tests/file_test.go @@ -254,6 +254,34 @@ func (suite *fileTestSuite) TestFileCreateLabel() { suite.fileTestCleanup([]string{fileName}) } +func (suite *fileTestSuite) TestFileAppend() { + fileName := filepath.Join(suite.testPath, "append_test.txt") + initialContent := []byte("Initial content\n") + appendContent := []byte("Appended content\n") + + // Create and write initial content to the file + srcFile, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY, 0777) + suite.NoError(err) + _, err = srcFile.Write(initialContent) + suite.NoError(err) + srcFile.Close() + + // Open the file with O_APPEND and append new content + appendFile, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0777) + suite.NoError(err) + _, err = appendFile.Write(appendContent) + suite.NoError(err) + appendFile.Close() + + // Read the file and verify the content + data, err := os.ReadFile(fileName) + suite.NoError(err) + expectedContent := append(initialContent, appendContent...) + suite.Equal(expectedContent, data) + + suite.fileTestCleanup([]string{fileName}) +} + // # Write a small file func (suite *fileTestSuite) TestFileWriteSmall() { fileName := filepath.Join(suite.testPath, "small_write.txt")