From cdd7512a2e45bdce415d4d6113b1b49c867bf398 Mon Sep 17 00:00:00 2001 From: Poorna Date: Wed, 11 Sep 2024 09:08:51 -0700 Subject: [PATCH] use rename() safety for in-place 'xl.meta' updates (#20414) --- cmd/xl-storage.go | 46 +++++++++++++++++++++++++++++++++++++++++++--- go.mod | 3 ++- go.sum | 4 ++-- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/cmd/xl-storage.go b/cmd/xl-storage.go index 570cc7d2e..f1b51fe6b 100644 --- a/cmd/xl-storage.go +++ b/cmd/xl-storage.go @@ -45,6 +45,7 @@ import ( "github.com/minio/minio/internal/bucket/lifecycle" "github.com/minio/minio/internal/cachevalue" "github.com/minio/minio/internal/config/storageclass" + "github.com/minio/minio/internal/disk" xioutil "github.com/minio/minio/internal/ioutil" "github.com/minio/minio/internal/logger" @@ -1404,7 +1405,7 @@ func (s *xlStorage) DeleteVersion(ctx context.Context, volume, path string, fi F return err } - return s.WriteAll(ctx, volume, pathJoin(path, xlStorageFormatFile), buf) + return s.writeAllMeta(ctx, volume, pathJoin(path, xlStorageFormatFile), buf, true) } if opts.UndoWrite && opts.OldDataDir != "" { @@ -1459,7 +1460,7 @@ func (s *xlStorage) UpdateMetadata(ctx context.Context, volume, path string, fi } defer metaDataPoolPut(wbuf) - return s.writeAll(ctx, volume, pathJoin(path, xlStorageFormatFile), wbuf, !opts.NoPersistence, volumeDir) + return s.writeAllMeta(ctx, volume, pathJoin(path, xlStorageFormatFile), wbuf, !opts.NoPersistence) } // WriteMetadata - writes FileInfo metadata for path at `xl.meta` @@ -2210,7 +2211,8 @@ func (s *xlStorage) writeAllDirect(ctx context.Context, filePath string, fileSiz return w.Close() } -func (s *xlStorage) writeAll(ctx context.Context, volume string, path string, b []byte, sync bool, skipParent string) (err error) { +// writeAllMeta - writes all metadata to a temp file and then links it to the final destination. +func (s *xlStorage) writeAllMeta(ctx context.Context, volume string, path string, b []byte, sync bool) (err error) { if contextCanceled(ctx) { return ctx.Err() } @@ -2225,6 +2227,26 @@ func (s *xlStorage) writeAll(ctx context.Context, volume string, path string, b return err } + tmpVolumeDir, err := s.getVolDir(minioMetaTmpBucket) + if err != nil { + return err + } + + tmpFilePath := pathJoin(tmpVolumeDir, mustGetUUID()) + defer func() { + if err != nil { + Remove(tmpFilePath) + } + }() + + if err = s.writeAllInternal(ctx, tmpFilePath, b, sync, tmpVolumeDir); err != nil { + return err + } + + return renameAll(tmpFilePath, filePath, volumeDir) +} + +func (s *xlStorage) writeAllInternal(ctx context.Context, filePath string, b []byte, sync bool, skipParent string) (err error) { flags := os.O_CREATE | os.O_WRONLY | os.O_TRUNC var w *os.File @@ -2269,6 +2291,24 @@ func (s *xlStorage) writeAll(ctx context.Context, volume string, path string, b return w.Close() } +func (s *xlStorage) writeAll(ctx context.Context, volume string, path string, b []byte, sync bool, skipParent string) (err error) { + if contextCanceled(ctx) { + return ctx.Err() + } + + volumeDir, err := s.getVolDir(volume) + if err != nil { + return err + } + + filePath := pathJoin(volumeDir, path) + if err = checkPathLength(filePath); err != nil { + return err + } + + return s.writeAllInternal(ctx, filePath, b, sync, skipParent) +} + func (s *xlStorage) WriteAll(ctx context.Context, volume string, path string, b []byte) (err error) { // Specific optimization to avoid re-read from the drives for `format.json` // in-case the caller is a network operation. diff --git a/go.mod b/go.mod index 2d3f3bb53..9342ae9dc 100644 --- a/go.mod +++ b/go.mod @@ -94,7 +94,7 @@ require ( golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 golang.org/x/oauth2 v0.22.0 golang.org/x/sync v0.8.0 - golang.org/x/sys v0.24.0 + golang.org/x/sys v0.25.0 golang.org/x/term v0.23.0 golang.org/x/time v0.6.0 google.golang.org/api v0.194.0 @@ -254,4 +254,5 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240823204242-4ba0660f739c // indirect google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + ) diff --git a/go.sum b/go.sum index 77dbad028..ecc0ef87b 100644 --- a/go.sum +++ b/go.sum @@ -781,8 +781,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=