storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/lock/lock_nix.go (about)

     1  //go:build !windows && !plan9 && !solaris
     2  // +build !windows,!plan9,!solaris
     3  
     4  /*
     5   * MinIO Cloud Storage, (C) 2016 MinIO, Inc.
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *
    11   *     http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   */
    19  
    20  package lock
    21  
    22  import (
    23  	"os"
    24  	"syscall"
    25  )
    26  
    27  // Internal function implements support for both
    28  // blocking and non blocking lock type.
    29  func lockedOpenFile(path string, flag int, perm os.FileMode, lockType int) (*LockedFile, error) {
    30  	switch flag {
    31  	case syscall.O_RDONLY:
    32  		lockType |= syscall.LOCK_SH
    33  	case syscall.O_WRONLY:
    34  		fallthrough
    35  	case syscall.O_RDWR:
    36  		fallthrough
    37  	case syscall.O_WRONLY | syscall.O_CREAT:
    38  		fallthrough
    39  	case syscall.O_RDWR | syscall.O_CREAT:
    40  		lockType |= syscall.LOCK_EX
    41  	default:
    42  		return nil, &os.PathError{
    43  			Op:   "open",
    44  			Path: path,
    45  			Err:  syscall.EINVAL,
    46  		}
    47  	}
    48  
    49  	f, err := os.OpenFile(path, flag|syscall.O_SYNC, perm)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	if err = syscall.Flock(int(f.Fd()), lockType); err != nil {
    55  		f.Close()
    56  		if err == syscall.EWOULDBLOCK {
    57  			err = ErrAlreadyLocked
    58  		}
    59  		return nil, err
    60  	}
    61  
    62  	st, err := os.Stat(path)
    63  	if err != nil {
    64  		f.Close()
    65  		return nil, err
    66  	}
    67  
    68  	if st.IsDir() {
    69  		f.Close()
    70  		return nil, &os.PathError{
    71  			Op:   "open",
    72  			Path: path,
    73  			Err:  syscall.EISDIR,
    74  		}
    75  	}
    76  
    77  	return &LockedFile{File: f}, nil
    78  }
    79  
    80  // TryLockedOpenFile - tries a new write lock, functionality
    81  // it is similar to LockedOpenFile with with syscall.LOCK_EX
    82  // mode but along with syscall.LOCK_NB such that the function
    83  // doesn't wait forever but instead returns if it cannot
    84  // acquire a write lock.
    85  func TryLockedOpenFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
    86  	return lockedOpenFile(path, flag, perm, syscall.LOCK_NB)
    87  }
    88  
    89  // LockedOpenFile - initializes a new lock and protects
    90  // the file from concurrent access across mount points.
    91  // This implementation doesn't support all the open
    92  // flags and shouldn't be considered as replacement
    93  // for os.OpenFile().
    94  func LockedOpenFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
    95  	return lockedOpenFile(path, flag, perm, 0)
    96  }
    97  
    98  // Open - Call os.OpenFile
    99  func Open(path string, flag int, perm os.FileMode) (*os.File, error) {
   100  	return os.OpenFile(path, flag, perm)
   101  }