From 228384530e2b08f22cfb84c62694278067ca5133 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 3 Jun 2022 06:33:48 +0000 Subject: [PATCH] storage/filesystem: atomically check for IfNoneMatch Using a separate os.Stat() call may result in a race where another request handler running concurrently creates the file in-between the os.Stat() call and the os.Create() call. Use O_EXCL to avoid this situation. --- storage/filesystem.go | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/storage/filesystem.go b/storage/filesystem.go index 0d9f09d..fc614e4 100644 --- a/storage/filesystem.go +++ b/storage/filesystem.go @@ -425,13 +425,16 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string return "", err } - // TODO handle IfMatch - if _, err := os.Stat(localPath); !os.IsNotExist(err) && opts.IfNoneMatch { - return "", carddav.NewPreconditionError(carddav.PreconditionNoUIDConflict) + flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC + if opts.IfNoneMatch { + flags |= os.O_EXCL } - f, err := os.Create(localPath) - if err != nil { + // TODO handle IfMatch + f, err := os.OpenFile(localPath, flags, 0666) + if os.IsExist(err) { + return "", carddav.NewPreconditionError(carddav.PreconditionNoUIDConflict) + } else if err != nil { return "", err } defer f.Close() @@ -602,13 +605,16 @@ func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath strin return "", err } - // TODO handle IfMatch - if _, err := os.Stat(localPath); !os.IsNotExist(err) && opts.IfNoneMatch { - return "", caldav.NewPreconditionError(caldav.PreconditionNoUIDConflict) + flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC + if opts.IfNoneMatch { + flags |= os.O_EXCL } - f, err := os.Create(localPath) - if err != nil { + // TODO handle IfMatch + f, err := os.OpenFile(localPath, flags, 0666) + if os.IsExist(err) { + return "", caldav.NewPreconditionError(caldav.PreconditionNoUIDConflict) + } else if err != nil { return "", err } defer f.Close() @@ -620,5 +626,4 @@ func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath strin } return objPath, nil - return "", nil }