diff --git a/metanode/partition.go b/metanode/partition.go index 897be9d44..495930555 100644 --- a/metanode/partition.go +++ b/metanode/partition.go @@ -39,6 +39,7 @@ import ( "github.com/cubefs/cubefs/util" "github.com/cubefs/cubefs/util/atomicutil" "github.com/cubefs/cubefs/util/errors" + "github.com/cubefs/cubefs/util/fileutil" "github.com/cubefs/cubefs/util/log" "github.com/cubefs/cubefs/util/timeutil" ) @@ -1367,7 +1368,7 @@ func (mp *metaPartition) store(sm *storeMsg) (err error) { } // write crc to file - if err = os.WriteFile(path.Join(tmpDir, SnapshotSign), crcBuffer.Bytes(), 0o775); err != nil { + if err = fileutil.WriteFileWithSync(path.Join(tmpDir, SnapshotSign), crcBuffer.Bytes(), 0o775); err != nil { return } snapshotDir := path.Join(mp.config.RootDir, snapshotDir) diff --git a/metanode/partition_test.go b/metanode/partition_test.go index 42d05041c..7474f5349 100644 --- a/metanode/partition_test.go +++ b/metanode/partition_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/require" "github.com/cubefs/cubefs/proto" + "github.com/cubefs/cubefs/util/fileutil" ) func TestMetaPartition_LoadSnapshot(t *testing.T) { @@ -132,7 +133,7 @@ func TestMetaPartition_LoadSnapshot(t *testing.T) { require.True(t, len(crcData) != 0) crcData[0] = '0' crcData[1] = '1' - err = os.WriteFile(path.Join(snapshotPath, SnapshotSign), crcData, 0o644) + err = fileutil.WriteFileWithSync(path.Join(snapshotPath, SnapshotSign), crcData, 0o644) require.Nil(t, err) err = partition.LoadSnapshot(snapshotPath) require.Equal(t, ErrSnapshotCrcMismatch, err) diff --git a/util/fileutil/write.go b/util/fileutil/write.go new file mode 100644 index 000000000..200f7169e --- /dev/null +++ b/util/fileutil/write.go @@ -0,0 +1,35 @@ +// Copyright 2024 The CubeFS Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. + +package fileutil + +import ( + "io/fs" + "os" +) + +// WriteFileWithSync implements os.WriteFile() with fsync. +func WriteFileWithSync(filename string, data []byte, perm fs.FileMode) error { + file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + if err != nil { + return err + } + if _, err = file.Write(data); err == nil { + err = file.Sync() + } + if err1 := file.Close(); err1 != nil && err == nil { + err = err1 + } + return err +} diff --git a/util/fileutil/write_test.go b/util/fileutil/write_test.go new file mode 100644 index 000000000..70ddc97ea --- /dev/null +++ b/util/fileutil/write_test.go @@ -0,0 +1,31 @@ +// Copyright 2024 The CubeFS Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. + +package fileutil_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/cubefs/cubefs/util/fileutil" + "github.com/stretchr/testify/require" +) + +func TestWriteFileWithSync(t *testing.T) { + require.Error(t, fileutil.WriteFileWithSync(os.TempDir(), nil, 0o644)) + filename := filepath.Join(os.TempDir(), "TestWriteFileWithSync.file") + defer os.RemoveAll(filename) + require.NoError(t, fileutil.WriteFileWithSync(filename, make([]byte, 32), 0o644)) +}