github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/lock/lock.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 // Package lock - implements filesystem locking wrappers around an 19 // open file descriptor. 20 package lock 21 22 import ( 23 "errors" 24 "os" 25 "sync" 26 ) 27 28 // ErrAlreadyLocked is returned if the underlying fd is already locked. 29 var ErrAlreadyLocked = errors.New("file already locked") 30 31 // RLockedFile represents a read locked file, implements a special 32 // closer which only closes the associated *os.File when the ref count. 33 // has reached zero, i.e when all the readers have given up their locks. 34 type RLockedFile struct { 35 *LockedFile 36 mutex sync.Mutex 37 refs int // Holds read lock refs. 38 } 39 40 // IsClosed - Check if the rlocked file is already closed. 41 func (r *RLockedFile) IsClosed() bool { 42 r.mutex.Lock() 43 defer r.mutex.Unlock() 44 return r.refs == 0 45 } 46 47 // IncLockRef - is used by called to indicate lock refs. 48 func (r *RLockedFile) IncLockRef() { 49 r.mutex.Lock() 50 r.refs++ 51 r.mutex.Unlock() 52 } 53 54 // Close - this closer implements a special closer 55 // closes the underlying fd only when the refs 56 // reach zero. 57 func (r *RLockedFile) Close() (err error) { 58 r.mutex.Lock() 59 defer r.mutex.Unlock() 60 61 if r.refs == 0 { 62 return os.ErrInvalid 63 } 64 65 r.refs-- 66 if r.refs == 0 { 67 err = r.File.Close() 68 } 69 70 return err 71 } 72 73 // Provides a new initialized read locked struct from *os.File 74 func newRLockedFile(lkFile *LockedFile) (*RLockedFile, error) { 75 if lkFile == nil { 76 return nil, os.ErrInvalid 77 } 78 79 return &RLockedFile{ 80 LockedFile: lkFile, 81 refs: 1, 82 }, nil 83 } 84 85 // RLockedOpenFile - returns a wrapped read locked file, if the file 86 // doesn't exist at path returns an error. 87 func RLockedOpenFile(path string) (*RLockedFile, error) { 88 lkFile, err := LockedOpenFile(path, os.O_RDONLY, 0o666) 89 if err != nil { 90 return nil, err 91 } 92 93 return newRLockedFile(lkFile) 94 } 95 96 // LockedFile represents a locked file 97 type LockedFile struct { 98 *os.File 99 }