github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vfs/file.go (about) 1 package vfs 2 3 import ( 4 "errors" 5 "io" 6 "io/fs" 7 "os" 8 "path/filepath" 9 "runtime" 10 "syscall" 11 12 "github.com/ncruces/go-sqlite3/util/osutil" 13 ) 14 15 type vfsOS struct{} 16 17 func (vfsOS) FullPathname(path string) (string, error) { 18 path, err := filepath.Abs(path) 19 if err != nil { 20 return "", err 21 } 22 fi, err := os.Lstat(path) 23 if err != nil { 24 if errors.Is(err, fs.ErrNotExist) { 25 return path, nil 26 } 27 return "", err 28 } 29 if fi.Mode()&fs.ModeSymlink != 0 { 30 err = _OK_SYMLINK 31 } 32 return path, err 33 } 34 35 func (vfsOS) Delete(path string, syncDir bool) error { 36 err := os.Remove(path) 37 if err != nil { 38 if errors.Is(err, fs.ErrNotExist) { 39 return _IOERR_DELETE_NOENT 40 } 41 return err 42 } 43 if runtime.GOOS != "windows" && syncDir { 44 f, err := os.Open(filepath.Dir(path)) 45 if err != nil { 46 return _OK 47 } 48 defer f.Close() 49 err = osSync(f, false, false) 50 if err != nil { 51 return _IOERR_DIR_FSYNC 52 } 53 } 54 return nil 55 } 56 57 func (vfsOS) Access(name string, flags AccessFlag) (bool, error) { 58 err := osAccess(name, flags) 59 if flags == ACCESS_EXISTS { 60 if errors.Is(err, fs.ErrNotExist) { 61 return false, nil 62 } 63 } else { 64 if errors.Is(err, fs.ErrPermission) { 65 return false, nil 66 } 67 } 68 return err == nil, err 69 } 70 71 func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) { 72 return nil, 0, _CANTOPEN 73 } 74 75 func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) { 76 var oflags int 77 if flags&OPEN_EXCLUSIVE != 0 { 78 oflags |= os.O_EXCL 79 } 80 if flags&OPEN_CREATE != 0 { 81 oflags |= os.O_CREATE 82 } 83 if flags&OPEN_READONLY != 0 { 84 oflags |= os.O_RDONLY 85 } 86 if flags&OPEN_READWRITE != 0 { 87 oflags |= os.O_RDWR 88 } 89 90 var err error 91 var f *os.File 92 if name == nil { 93 f, err = os.CreateTemp("", "*.db") 94 } else { 95 f, err = osutil.OpenFile(name.String(), oflags, 0666) 96 } 97 if err != nil { 98 if errors.Is(err, syscall.EISDIR) { 99 return nil, flags, _CANTOPEN_ISDIR 100 } 101 return nil, flags, err 102 } 103 104 if modeof := name.URIParameter("modeof"); modeof != "" { 105 if err = osSetMode(f, modeof); err != nil { 106 f.Close() 107 return nil, flags, _IOERR_FSTAT 108 } 109 } 110 if flags&OPEN_DELETEONCLOSE != 0 { 111 os.Remove(f.Name()) 112 } 113 114 file := vfsFile{ 115 File: f, 116 psow: true, 117 readOnly: flags&OPEN_READONLY != 0, 118 syncDir: runtime.GOOS != "windows" && 119 flags&(OPEN_CREATE) != 0 && 120 flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0, 121 shm: NewSharedMemory(name.String()+"-shm", flags), 122 } 123 return &file, flags, nil 124 } 125 126 type vfsFile struct { 127 *os.File 128 shm SharedMemory 129 lock LockLevel 130 readOnly bool 131 keepWAL bool 132 syncDir bool 133 psow bool 134 } 135 136 var ( 137 // Ensure these interfaces are implemented: 138 _ FileLockState = &vfsFile{} 139 _ FileHasMoved = &vfsFile{} 140 _ FileSizeHint = &vfsFile{} 141 _ FilePersistentWAL = &vfsFile{} 142 _ FilePowersafeOverwrite = &vfsFile{} 143 ) 144 145 func (f *vfsFile) Close() error { 146 if f.shm != nil { 147 f.shm.Close() 148 } 149 return f.File.Close() 150 } 151 152 func (f *vfsFile) Sync(flags SyncFlag) error { 153 dataonly := (flags & SYNC_DATAONLY) != 0 154 fullsync := (flags & 0x0f) == SYNC_FULL 155 156 err := osSync(f.File, fullsync, dataonly) 157 if err != nil { 158 return err 159 } 160 if runtime.GOOS != "windows" && f.syncDir { 161 f.syncDir = false 162 d, err := os.Open(filepath.Dir(f.File.Name())) 163 if err != nil { 164 return nil 165 } 166 defer d.Close() 167 err = osSync(d, false, false) 168 if err != nil { 169 return _IOERR_DIR_FSYNC 170 } 171 } 172 return nil 173 } 174 175 func (f *vfsFile) Size() (int64, error) { 176 return f.Seek(0, io.SeekEnd) 177 } 178 179 func (f *vfsFile) SectorSize() int { 180 return _DEFAULT_SECTOR_SIZE 181 } 182 183 func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic { 184 var res DeviceCharacteristic 185 if osBatchAtomic(f.File) { 186 res |= IOCAP_BATCH_ATOMIC 187 } 188 if f.psow { 189 res |= IOCAP_POWERSAFE_OVERWRITE 190 } 191 return res 192 } 193 194 func (f *vfsFile) SizeHint(size int64) error { 195 return osAllocate(f.File, size) 196 } 197 198 func (f *vfsFile) HasMoved() (bool, error) { 199 fi, err := f.Stat() 200 if err != nil { 201 return false, err 202 } 203 pi, err := os.Stat(f.Name()) 204 if err != nil { 205 if errors.Is(err, fs.ErrNotExist) { 206 return true, nil 207 } 208 return false, err 209 } 210 return !os.SameFile(fi, pi), nil 211 } 212 213 func (f *vfsFile) LockState() LockLevel { return f.lock } 214 func (f *vfsFile) PowersafeOverwrite() bool { return f.psow } 215 func (f *vfsFile) PersistentWAL() bool { return f.keepWAL } 216 func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow } 217 func (f *vfsFile) SetPersistentWAL(keepWAL bool) { f.keepWAL = keepWAL }