func lockFcntl(name string) (io.Closer, error) { fi, err := os.Stat(name) if err == nil && fi.Size() > 0 { return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name) } f, err := os.Create(name) if err != nil { return nil, fmt.Errorf("Lock Create of %s failed: %v", name, err) } err = unix.FcntlFlock(f.Fd(), unix.F_SETLK, &unix.Flock_t{ Type: unix.F_WRLCK, Whence: int16(os.SEEK_SET), Start: 0, Len: 0, // 0 means to lock the entire file. Pid: 0, // only used by F_GETLK }) if err != nil { f.Close() return nil, fmt.Errorf("Lock FcntlFlock of %s failed: %v", name, err) } return &unlocker{f: f, abs: name}, nil }
// Acquire acquires a lock on a file for the duration of the process. This method // is reentrant. func Acquire(path string) error { lock.Lock() defer lock.Unlock() var err error if lockfile, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600); err != nil { return err } opts := unix.Flock_t{Type: unix.F_WRLCK} if err := unix.FcntlFlock(lockfile.Fd(), unix.F_SETLKW, &opts); err != nil { return err } return nil }
// 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 := unix.Open(name, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0) if err != nil { t.Fatalf("Open failed: %v", err) } defer unix.Unlink(name) defer unix.Close(fd) flock := unix.Flock_t{ Type: unix.F_RDLCK, Start: 0, Len: 0, Whence: 1, } if err := unix.FcntlFlock(uintptr(fd), unix.F_GETLK, &flock); err != nil { t.Fatalf("FcntlFlock failed: %v", err) } }