// TestFcntlFlock tests whether the file locking structure matches // the calling convention of each kernel. // On some Linux systems, glibc uses another set of values for the // commands and translates them to the correct value that the kernel // expects just before the actual fcntl syscall. As Go uses raw // syscalls directly, it must use the real value, not the glibc value. // Thus this test also verifies that the Flock_t structure can be // roundtripped with F_SETLK and F_GETLK. func TestFcntlFlock(t *testing.T) { if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { t.Skip("skipping; no child processes allowed on iOS") } flock := syscall.Flock_t{ Type: syscall.F_WRLCK, Start: 31415, Len: 271828, Whence: 1, } if os.Getenv("GO_WANT_HELPER_PROCESS") == "" { // parent name := filepath.Join(os.TempDir(), "TestFcntlFlock") fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0) if err != nil { t.Fatalf("Open failed: %v", err) } defer syscall.Unlink(name) defer syscall.Close(fd) if err := syscall.Ftruncate(fd, 1<<20); err != nil { t.Fatalf("Ftruncate(1<<20) failed: %v", err) } if err := syscall.FcntlFlock(uintptr(fd), syscall.F_SETLK, &flock); err != nil { t.Fatalf("FcntlFlock(F_SETLK) failed: %v", err) } cmd := exec.Command(os.Args[0], "-test.run=^TestFcntlFlock$") cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") cmd.ExtraFiles = []*os.File{os.NewFile(uintptr(fd), name)} out, err := cmd.CombinedOutput() if len(out) > 0 || err != nil { t.Fatalf("child process: %q, %v", out, err) } } else { // child got := flock // make sure the child lock is conflicting with the parent lock got.Start-- got.Len++ if err := syscall.FcntlFlock(3, syscall.F_GETLK, &got); err != nil { t.Fatalf("FcntlFlock(F_GETLK) failed: %v", err) } flock.Pid = int32(syscall.Getppid()) // Linux kernel always set Whence to 0 flock.Whence = 0 if got.Type == flock.Type && got.Start == flock.Start && got.Len == flock.Len && got.Pid == flock.Pid && got.Whence == flock.Whence { os.Exit(0) } t.Fatalf("FcntlFlock got %v, want %v", got, flock) } }
// A wrapper around os.OpenFile() that locks the file after opening // Will return an error if the file cannot be opened or the file cannot // be locked func SafeOpen(name string) (*os.File, *syscall.Flock_t, error) { fi, err := os.OpenFile(name, os.O_RDWR, os.ModeExclusive) if err != nil { return nil, nil, err } // Lock the file so we're responsible lk := syscall.Flock_t{ Type: syscall.F_WRLCK, // set write lock Whence: 0, // SEEK_SET Start: 0, // beginning of file Len: 0, // until EOF Pid: int32(os.Getpid()), // our PID } err = syscall.FcntlFlock(fi.Fd(), syscall.F_SETLKW, &lk) // If we can't lock the file error out to prevent corruption if err != nil { return nil, nil, err } return fi, &lk, nil }
func LockFile(file *os.File, lockType LockType) error { if file == nil { return fmt.Errorf("*os.File argument is nil.") } var syscallType int16 switch lockType { case Read: syscallType = syscall.F_RDLCK case Write: syscallType = syscall.F_WRLCK default: return fmt.Errorf("Invalid LockType %dv", lockType) } fd := file.Fd() cmd := syscall.F_SETLK lk := syscall.Flock_t{ Type: syscallType, Whence: int16(os.SEEK_SET), Start: 0, Len: math.MaxInt64, Pid: int32(os.Getpid()), } err := syscall.FcntlFlock(fd, cmd, &lk) if err != nil { return fmt.Errorf("Failed to lock %s: %s", file.Name(), err) } return nil }
// flock acquires an advisory lock on a file descriptor. func flock(f *os.File, exclusive bool, timeout time.Duration) error { var t time.Time for { // If we're beyond our timeout then return an error. // This can only occur after we've attempted a flock once. if t.IsZero() { t = time.Now() } else if timeout > 0 && time.Since(t) > timeout { return ErrTimeout } var lock syscall.Flock_t lock.Start = 0 lock.Len = 0 lock.Pid = 0 lock.Whence = 0 lock.Pid = 0 if exclusive { lock.Type = syscall.F_WRLCK } else { lock.Type = syscall.F_RDLCK } err := syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &lock) if err == nil { return nil } else if err != syscall.EAGAIN { return err } // Wait for a bit and try again. time.Sleep(50 * time.Millisecond) } }
// Unlock an open file. Unlocking errors are ignored. func Unlock(file *os.File, lk *syscall.Flock_t) error { if lk == nil || file == nil { return fmt.Errorf("file or lock are nil file: %s lk: %v", file.Name(), lk) } lk.Type = syscall.F_UNLCK return syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, lk) }
// funlock releases an advisory lock on a file descriptor. func funlock(f *os.File) error { var lock syscall.Flock_t lock.Start = 0 lock.Len = 0 lock.Type = syscall.F_UNLCK lock.Whence = 0 return syscall.FcntlFlock(uintptr(f.Fd()), syscall.F_SETLK, &lock) }
func init() { // use open file descriptor locks if the system supports it getlk := syscall.Flock_t{Type: syscall.F_RDLCK} if err := syscall.FcntlFlock(0, F_OFD_GETLK, &getlk); err == nil { linuxTryLockFile = ofdTryLockFile linuxLockFile = ofdLockFile } }
func open(path string) (*os.File, error) { var f *os.File var err error for { f, err = os.OpenFile(path, syscall.O_RDWR|syscall.O_CREAT|syscall.O_EXCL, 0644) if err != nil { if !os.IsExist(err) { return nil, err } f, err = os.OpenFile(path, syscall.O_RDWR, 0644) if err != nil { if os.IsNotExist(err) { continue } return nil, err } } err = syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &syscall.Flock_t{ Type: syscall.F_WRLCK, }) if err != nil { f.Close() return nil, err } st1 := syscall.Stat_t{} err = syscall.Fstat(int(f.Fd()), &st1) // ffs if err != nil { f.Close() return nil, err } st2 := syscall.Stat_t{} err = syscall.Stat(path, &st2) if err != nil { f.Close() if os.IsNotExist(err) { continue } return nil, err } if st1.Ino != st2.Ino { f.Close() continue } break } return f, nil }
// Lock acquires exclusivity on the lock without blocking func (l *lock) Lock() error { var lock syscall.Flock_t lock.Start = 0 lock.Len = 0 lock.Type = syscall.F_WRLCK lock.Whence = 0 lock.Pid = 0 return syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock) }
func (l *FcntlLockfile) unlock(offset int64, whence int, len int64) { l.ft.Len = len l.ft.Start = offset l.ft.Whence = int16(whence) l.ft.Type = syscall.F_UNLCK syscall.FcntlFlock(l.file.Fd(), syscall.F_SETLK, l.ft) if l.maintainFile { l.file.Close() } }
// Unlock unlocks the lock func (l *lock) Unlock() error { var lock syscall.Flock_t lock.Start = 0 lock.Len = 0 lock.Type = syscall.F_UNLCK lock.Whence = 0 err := syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock) if err != nil && err == syscall.EAGAIN { return ErrLocked } return err }
func setFileLock(f *os.File, lock bool) error { flock := syscall.Flock_t{ Type: syscall.F_UNLCK, Start: 0, Len: 0, Whence: 1, } if lock { flock.Type = syscall.F_WRLCK } return syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &flock) }
func (l *unixLock) set(lock bool) error { flock := syscall.Flock_t{ Type: syscall.F_UNLCK, Start: 0, Len: 0, Whence: 1, } if lock { flock.Type = syscall.F_WRLCK } return syscall.FcntlFlock(l.f.Fd(), syscall.F_SETLK, &flock) }
func lockFile(file string) error { fd, err := syscall.Open(file, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0660) if err != nil { return err } lock := syscall.Flock_t{ Start: 0, Len: 0, Type: syscall.F_WRLCK, Whence: int16(os.SEEK_SET), } return syscall.FcntlFlock(uintptr(fd), syscall.F_SETLK, &lock) }
func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { f, err := os.OpenFile(path, flag, perm) if err != nil { return nil, err } flock := wrlck err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLKW, &flock) if err != nil { f.Close() return nil, err } return &LockedFile{f}, err }
// TestFcntlFlock tests whether the file locking structure matches // the calling convention of each kernel. func TestFcntlFlock(t *testing.T) { name := filepath.Join(os.TempDir(), "TestFcntlFlock") fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0) if err != nil { t.Fatalf("Open failed: %v", err) } defer syscall.Unlink(name) defer syscall.Close(fd) flock := syscall.Flock_t{ Type: syscall.F_RDLCK, Start: 0, Len: 0, Whence: 1, } if err := syscall.FcntlFlock(uintptr(fd), syscall.F_GETLK, &flock); err != nil { t.Fatalf("FcntlFlock failed: %v", err) } }
func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { f, err := os.OpenFile(path, flag, perm) if err != nil { return nil, err } flock := wrlck if err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLK, &flock); err != nil { f.Close() if err == syscall.EWOULDBLOCK { err = ErrLocked } return nil, err } return &LockedFile{f}, nil }
// Owner will return the pid of the process that owns an fcntl based // lock on the file. If the file is not locked it will return -1. If // a lock is owned by the current process, it will return -1. func (l *FcntlLockfile) Owner() int { ft := &syscall.Flock_t{} *ft = *l.ft err := syscall.FcntlFlock(l.file.Fd(), syscall.F_GETLK, ft) if err != nil { fmt.Println(err) return -1 } if ft.Type == syscall.F_UNLCK { fmt.Println(err) return -1 } return int(ft.Pid) }
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { var lock syscall.Flock_t lock.Start = 0 lock.Len = 0 lock.Pid = 0 lock.Type = syscall.F_WRLCK lock.Whence = 0 f, err := os.OpenFile(path, flag, perm) if err != nil { return nil, err } if err = syscall.FcntlFlock(f.Fd(), syscall.F_SETLKW, &lock); err != nil { f.Close() return nil, err } return &LockedFile{f}, nil }
func (defFS) Lock(name string) (io.Closer, error) { f, err := os.Create(name) if err != nil { return nil, err } spec := syscall.Flock_t{ Type: syscall.F_WRLCK, Whence: int16(os.SEEK_SET), Start: 0, Len: 0, // 0 means to lock the entire file. Pid: int32(os.Getpid()), } if err := syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &spec); err != nil { f.Close() return nil, err } return lockCloser{f}, nil }
// Control the lock of file. func fcntlFlock(lockType int16, path ...string) error { var err error if lockType != syscall.F_UNLCK { mode := syscall.O_CREAT | syscall.O_WRONLY mask := syscall.Umask(0) lockFile, err = os.OpenFile(path[0], mode, 0666) syscall.Umask(mask) if err != nil { return err } } lock := syscall.Flock_t{ Start: 0, Len: 1, Type: lockType, Whence: int16(os.SEEK_SET), } return syscall.FcntlFlock(lockFile.Fd(), syscall.F_SETLK, &lock) }
func (l *FcntlLockfile) lock(exclusive, blocking bool, offset int64, whence int, len int64) error { if l.file == nil { f, err := os.OpenFile(l.Path, os.O_CREATE|os.O_RDWR, 0666) if err != nil { return err } l.file = f } ft := &syscall.Flock_t{ Whence: int16(whence), Start: offset, Len: len, Pid: int32(os.Getpid()), } l.ft = ft if exclusive { ft.Type = syscall.F_WRLCK } else { ft.Type = syscall.F_RDLCK } var flags int if blocking { flags = syscall.F_SETLKW } else { flags = syscall.F_SETLK } err := syscall.FcntlFlock(l.file.Fd(), flags, l.ft) if err != nil { if l.maintainFile { l.file.Close() } return ErrFailedToLock } return nil }
func UnlockFile(file *os.File) error { if file == nil { return fmt.Errorf("*os.File argument is nil.") } fd := file.Fd() cmd := syscall.F_SETLK lk := syscall.Flock_t{ Type: syscall.F_UNLCK, Whence: int16(os.SEEK_SET), Start: 0, Len: math.MaxInt64, Pid: int32(os.Getpid()), } err := syscall.FcntlFlock(fd, cmd, &lk) if err != nil { return fmt.Errorf("Failed to unlock %s: %s", file.Name(), err) } return nil }
func unlockFileImpl(file *os.File) (err error) { return syscall.FcntlFlock( file.Fd(), syscall.F_SETLK, createLockParams(syscall.F_UNLCK)) }