github.com/pingcap/badger@v1.5.1-0.20230103063557-828f39b09b6d/dir_windows.go (about) 1 // +build windows 2 3 /* 4 * Copyright 2017 Dgraph Labs, Inc. and Contributors 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package badger 20 21 // OpenDir opens a directory in windows with write access for syncing. 22 import ( 23 "fmt" 24 "os" 25 "path/filepath" 26 "syscall" 27 "unsafe" 28 29 "github.com/pingcap/errors" 30 "golang.org/x/sys/windows" 31 ) 32 33 func openDir(path string) (*os.File, error) { 34 fd, err := openDirWin(path) 35 if err != nil { 36 return nil, err 37 } 38 return os.NewFile(uintptr(fd), path), nil 39 } 40 41 func openDirWin(path string) (fd syscall.Handle, err error) { 42 if len(path) == 0 { 43 return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND 44 } 45 pathp, err := syscall.UTF16PtrFromString(path) 46 if err != nil { 47 return syscall.InvalidHandle, err 48 } 49 access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE) 50 sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE) 51 createmode := uint32(syscall.OPEN_EXISTING) 52 fl := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) 53 return syscall.CreateFile(pathp, access, sharemode, nil, createmode, fl, 0) 54 } 55 56 // DirectoryLockGuard holds a lock on the directory. 57 type directoryLockGuard struct { 58 fd *os.File 59 } 60 61 // AcquireDirectoryLock acquires exclusive access to a directory. 62 func acquireDirectoryLock(dirPath string, pidFileName string, readOnly bool) (*directoryLockGuard, error) { 63 // Convert to absolute path so that Release still works even if we do an unbalanced 64 // chdir in the meantime. 65 absLockFilePath, err := filepath.Abs(filepath.Join(dirPath, pidFileName)) 66 if err != nil { 67 return nil, errors.Wrap(err, "Cannot get absolute path for pid lock file") 68 } 69 70 file, err := createFileAndLock(absLockFilePath, readOnly, false) 71 if err != nil { 72 return nil, err 73 } 74 if file == nil { 75 file, err = createFileAndLock(absLockFilePath, readOnly, true) 76 } 77 if err != nil { 78 return nil, err 79 } 80 81 if !readOnly { 82 _, err = file.WriteAt([]byte(fmt.Sprintf("%d\n", os.Getpid())), 0) 83 if err != nil { 84 _ = file.Close() 85 return nil, errors.Wrap(err, "Cannot write to pid lock file") 86 } 87 } 88 return &directoryLockGuard{fd: file}, nil 89 } 90 91 var ( 92 mod = windows.NewLazyDLL("kernel32.dll") 93 proc = mod.NewProc("CreateFileW") 94 ) 95 96 func createFileAndLock(path string, share bool, create bool) (*os.File, error) { 97 dwShareMode := uint32(0) // Exclusive (no sharing) 98 if share { 99 dwShareMode = windows.FILE_SHARE_READ 100 } 101 op := windows.OPEN_EXISTING 102 if create { 103 op = windows.CREATE_NEW 104 } 105 106 a, _, err := proc.Call( 107 uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))), 108 uintptr(windows.GENERIC_READ|windows.GENERIC_WRITE), 109 uintptr(dwShareMode), 110 uintptr(0), // No security attributes. 111 uintptr(op), 112 uintptr(windows.FILE_ATTRIBUTE_NORMAL), 113 0, // No template file. 114 ) 115 switch err.(windows.Errno) { 116 case 0: 117 return os.NewFile(a, path), nil 118 case windows.ERROR_FILE_NOT_FOUND: 119 return nil, nil 120 case windows.ERROR_SHARING_VIOLATION, windows.ERROR_FILE_EXISTS: 121 return nil, errors.New(fmt.Sprintf("Cannot acquire directory lock on %q, Another process is using this Badger database.", path)) 122 default: 123 _ = windows.Close(windows.Handle(a)) 124 return nil, errors.WithStack(err) 125 } 126 } 127 128 // Release removes the directory lock. 129 func (g *directoryLockGuard) release() error { 130 if g.fd == nil { 131 return nil 132 } 133 return g.fd.Close() 134 }