atomic/fs: use safe package for atomic writes, even in multipart.

This commit is contained in:
Harshavardhana
2016-04-06 16:05:30 -07:00
parent dfba4ff397
commit ff4e04d942
6 changed files with 100 additions and 65 deletions

View File

@@ -30,8 +30,8 @@ import (
"sync"
"github.com/fatih/structs"
"github.com/minio/minio/pkg/atomic"
"github.com/minio/minio/pkg/probe"
"github.com/minio/minio/pkg/safe"
)
// Config - generic config interface functions
@@ -178,15 +178,15 @@ func (d config) Version() string {
// If the file does not exist, writeFile creates it;
// otherwise writeFile truncates it before writing.
func writeFile(filename string, data []byte) *probe.Error {
atomicFile, e := atomic.FileCreate(filename)
safeFile, e := safe.CreateFile(filename)
if e != nil {
return probe.NewError(e)
}
_, e = atomicFile.Write(data)
_, e = safeFile.Write(data)
if e != nil {
return probe.NewError(e)
}
e = atomicFile.Close()
e = safeFile.Close()
if e != nil {
return probe.NewError(e)
}

View File

@@ -14,10 +14,10 @@
* limitations under the License.
*/
// NOTE - Rename() not guaranteed to be atomic on all filesystems which are not fully POSIX compatible
// NOTE - Rename() not guaranteed to be safe on all filesystems which are not fully POSIX compatible
// Package atomic provides atomic file write semantics by leveraging Rename's() atomicity.
package atomic
// Package safe provides safe file write semantics by leveraging Rename's() safeity.
package safe
import (
"io/ioutil"
@@ -25,14 +25,14 @@ import (
"path/filepath"
)
// File container provided for atomic file writes
// File provides for safe file writes.
type File struct {
*os.File
file string
}
// CloseAndSync sync file to disk and close, returns an error if any
func (f *File) CloseAndSync() error {
// SyncClose sync file to disk and close, returns an error if any
func (f *File) SyncClose() error {
// sync to the disk
if err := f.File.Sync(); err != nil {
return err
@@ -49,15 +49,16 @@ func (f *File) Close() error {
if err := f.File.Close(); err != nil {
return err
}
// atomic rename to final destination
// safe rename to final destination
if err := os.Rename(f.Name(), f.file); err != nil {
return err
}
return nil
}
// CloseAndPurge removes the temp file, closes the transaction and returns an error if any
func (f *File) CloseAndPurge() error {
// CloseAndRemove closes the temp file, and safely removes it. Returns
// error if any.
func (f *File) CloseAndRemove() error {
// close the embedded fd
if err := f.File.Close(); err != nil {
return err
@@ -68,15 +69,39 @@ func (f *File) CloseAndPurge() error {
return nil
}
// FileCreate creates a new file at filePath for atomic writes, it also creates parent directories if they don't exist
func FileCreate(filePath string) (*File, error) {
return FileCreateWithPrefix(filePath, "$deleteme.")
// CreateFile creates a new file at filePath for safe writes, it also
// creates parent directories if they don't exist.
func CreateFile(filePath string) (*File, error) {
return CreateFileWithPrefix(filePath, "$deleteme.")
}
// FileCreateWithPrefix creates a new file at filePath for atomic writes, it also creates parent directories if they don't exist
// prefix specifies the prefix of the temporary files so that cleaning stale temp files is easy
func FileCreateWithPrefix(filePath string, prefix string) (*File, error) {
// if parent directories do not exist, ioutil.TempFile doesn't create them
// CreateFileWithSuffix is similar to CreateFileWithPrefix, but the
// second argument is treated as suffix for the temporary files.
func CreateFileWithSuffix(filePath string, suffix string) (*File, error) {
// If parent directories do not exist, ioutil.TempFile doesn't create them
// handle such a case with os.MkdirAll()
if err := os.MkdirAll(filepath.Dir(filePath), 0700); err != nil {
return nil, err
}
f, err := ioutil.TempFile(filepath.Dir(filePath), filepath.Base(filePath)+suffix)
if err != nil {
return nil, err
}
if err = os.Chmod(f.Name(), 0600); err != nil {
if err = os.Remove(f.Name()); err != nil {
return nil, err
}
return nil, err
}
return &File{File: f, file: filePath}, nil
}
// CreateFileWithPrefix creates a new file at filePath for safe
// writes, it also creates parent directories if they don't exist.
// prefix specifies the prefix of the temporary files so that cleaning
// stale temp files is easy.
func CreateFileWithPrefix(filePath string, prefix string) (*File, error) {
// If parent directories do not exist, ioutil.TempFile doesn't create them
// handle such a case with os.MkdirAll()
if err := os.MkdirAll(filepath.Dir(filePath), 0700); err != nil {
return nil, err

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package atomic
package safe
import (
"io/ioutil"
@@ -34,7 +34,7 @@ type MySuite struct {
var _ = Suite(&MySuite{})
func (s *MySuite) SetUpSuite(c *C) {
root, err := ioutil.TempDir(os.TempDir(), "atomic-")
root, err := ioutil.TempDir(os.TempDir(), "safe-")
c.Assert(err, IsNil)
s.root = root
}
@@ -43,8 +43,8 @@ func (s *MySuite) TearDownSuite(c *C) {
os.RemoveAll(s.root)
}
func (s *MySuite) TestAtomic(c *C) {
f, err := FileCreate(filepath.Join(s.root, "testfile"))
func (s *MySuite) TestSafe(c *C) {
f, err := CreateFile(filepath.Join(s.root, "testfile"))
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.root, "testfile"))
c.Assert(err, Not(IsNil))
@@ -54,12 +54,12 @@ func (s *MySuite) TestAtomic(c *C) {
c.Assert(err, IsNil)
}
func (s *MySuite) TestAtomicPurge(c *C) {
f, err := FileCreate(filepath.Join(s.root, "purgefile"))
func (s *MySuite) TestSafePurge(c *C) {
f, err := CreateFile(filepath.Join(s.root, "purgefile"))
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.root, "purgefile"))
c.Assert(err, Not(IsNil))
err = f.CloseAndPurge()
err = f.CloseAndRemove()
c.Assert(err, IsNil)
err = f.Close()
c.Assert(err, Not(IsNil))