mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
1717 lines
65 KiB
HTML
1717 lines
65 KiB
HTML
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<title>go-sqlite3: Go Coverage Report</title>
|
|
<style>
|
|
body {
|
|
background: black;
|
|
color: rgb(80, 80, 80);
|
|
}
|
|
body, pre, #legend span {
|
|
font-family: Menlo, monospace;
|
|
font-weight: bold;
|
|
}
|
|
#topbar {
|
|
background: black;
|
|
position: fixed;
|
|
top: 0; left: 0; right: 0;
|
|
height: 42px;
|
|
border-bottom: 1px solid rgb(80, 80, 80);
|
|
}
|
|
#content {
|
|
margin-top: 50px;
|
|
}
|
|
#nav, #legend {
|
|
float: left;
|
|
margin-left: 10px;
|
|
}
|
|
#legend {
|
|
margin-top: 12px;
|
|
}
|
|
#nav {
|
|
margin-top: 10px;
|
|
}
|
|
#legend span {
|
|
margin: 0 5px;
|
|
}
|
|
.cov0 { color: rgb(192, 0, 0) }
|
|
.cov1 { color: rgb(128, 128, 128) }
|
|
.cov2 { color: rgb(116, 140, 131) }
|
|
.cov3 { color: rgb(104, 152, 134) }
|
|
.cov4 { color: rgb(92, 164, 137) }
|
|
.cov5 { color: rgb(80, 176, 140) }
|
|
.cov6 { color: rgb(68, 188, 143) }
|
|
.cov7 { color: rgb(56, 200, 146) }
|
|
.cov8 { color: rgb(44, 212, 149) }
|
|
.cov9 { color: rgb(32, 224, 152) }
|
|
.cov10 { color: rgb(20, 236, 155) }
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="topbar">
|
|
<div id="nav">
|
|
<select id="files">
|
|
|
|
<option value="file0">github.com/ncruces/go-sqlite3/api.go (73.3%)</option>
|
|
|
|
<option value="file1">github.com/ncruces/go-sqlite3/compile.go (54.5%)</option>
|
|
|
|
<option value="file2">github.com/ncruces/go-sqlite3/conn.go (86.8%)</option>
|
|
|
|
<option value="file3">github.com/ncruces/go-sqlite3/error.go (54.5%)</option>
|
|
|
|
<option value="file4">github.com/ncruces/go-sqlite3/stmt.go (27.2%)</option>
|
|
|
|
<option value="file5">github.com/ncruces/go-sqlite3/vfs.go (67.4%)</option>
|
|
|
|
<option value="file6">github.com/ncruces/go-sqlite3/vfs_files.go (70.6%)</option>
|
|
|
|
<option value="file7">github.com/ncruces/go-sqlite3/vfs_lock.go (51.6%)</option>
|
|
|
|
<option value="file8">github.com/ncruces/go-sqlite3/vfs_lock_noop.go (0.0%)</option>
|
|
|
|
<option value="file9">github.com/ncruces/go-sqlite3/vfs_unix.go (54.3%)</option>
|
|
|
|
</select>
|
|
</div>
|
|
<div id="legend">
|
|
<span>not tracked</span>
|
|
|
|
<span class="cov0">not covered</span>
|
|
<span class="cov8">covered</span>
|
|
|
|
</div>
|
|
</div>
|
|
<div id="content">
|
|
|
|
<pre class="file" id="file0" style="display: none">package sqlite3
|
|
|
|
import "github.com/tetratelabs/wazero/api"
|
|
|
|
func newConn(module api.Module) *Conn <span class="cov8" title="1">{
|
|
getFun := func(name string) api.Function </span><span class="cov8" title="1">{
|
|
f := module.ExportedFunction(name)
|
|
if f == nil </span><span class="cov0" title="0">{
|
|
panic(noFuncErr + errorString(name))</span>
|
|
}
|
|
<span class="cov8" title="1">return f</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">global := module.ExportedGlobal("malloc_destructor")
|
|
if global == nil </span><span class="cov0" title="0">{
|
|
panic(noGlobalErr + "malloc_destructor")</span>
|
|
}
|
|
<span class="cov8" title="1">destructor := uint32(global.Get())
|
|
if destructor == 0 </span><span class="cov0" title="0">{
|
|
panic(noGlobalErr + "malloc_destructor")</span>
|
|
}
|
|
<span class="cov8" title="1">destructor, ok := module.Memory().ReadUint32Le(destructor)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(noGlobalErr + "malloc_destructor")</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">return &Conn{
|
|
module: module,
|
|
memory: module.Memory(),
|
|
api: sqliteAPI{
|
|
malloc: getFun("malloc"),
|
|
free: getFun("free"),
|
|
destructor: uint64(destructor),
|
|
errcode: getFun("sqlite3_errcode"),
|
|
errstr: getFun("sqlite3_errstr"),
|
|
errmsg: getFun("sqlite3_errmsg"),
|
|
erroff: getFun("sqlite3_error_offset"),
|
|
open: getFun("sqlite3_open_v2"),
|
|
close: getFun("sqlite3_close"),
|
|
prepare: getFun("sqlite3_prepare_v3"),
|
|
finalize: getFun("sqlite3_finalize"),
|
|
reset: getFun("sqlite3_reset"),
|
|
step: getFun("sqlite3_step"),
|
|
exec: getFun("sqlite3_exec"),
|
|
clearBindings: getFun("sqlite3_clear_bindings"),
|
|
bindInteger: getFun("sqlite3_bind_int64"),
|
|
bindFloat: getFun("sqlite3_bind_double"),
|
|
bindText: getFun("sqlite3_bind_text64"),
|
|
bindBlob: getFun("sqlite3_bind_blob64"),
|
|
bindZeroBlob: getFun("sqlite3_bind_zeroblob64"),
|
|
bindNull: getFun("sqlite3_bind_null"),
|
|
columnInteger: getFun("sqlite3_column_int64"),
|
|
columnFloat: getFun("sqlite3_column_double"),
|
|
columnText: getFun("sqlite3_column_text"),
|
|
columnBlob: getFun("sqlite3_column_blob"),
|
|
columnBytes: getFun("sqlite3_column_bytes"),
|
|
columnType: getFun("sqlite3_column_type"),
|
|
},
|
|
}</span>
|
|
}
|
|
|
|
type sqliteAPI struct {
|
|
malloc api.Function
|
|
free api.Function
|
|
destructor uint64
|
|
errcode api.Function
|
|
errstr api.Function
|
|
errmsg api.Function
|
|
erroff api.Function
|
|
open api.Function
|
|
close api.Function
|
|
prepare api.Function
|
|
finalize api.Function
|
|
reset api.Function
|
|
step api.Function
|
|
exec api.Function
|
|
clearBindings api.Function
|
|
bindInteger api.Function
|
|
bindFloat api.Function
|
|
bindText api.Function
|
|
bindBlob api.Function
|
|
bindZeroBlob api.Function
|
|
bindNull api.Function
|
|
columnInteger api.Function
|
|
columnFloat api.Function
|
|
columnText api.Function
|
|
columnBlob api.Function
|
|
columnBytes api.Function
|
|
columnType api.Function
|
|
}
|
|
</pre>
|
|
|
|
<pre class="file" id="file1" style="display: none">package sqlite3
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/tetratelabs/wazero"
|
|
)
|
|
|
|
// Configure SQLite.
|
|
var (
|
|
Binary []byte // Binary to load.
|
|
Path string // Path to load the binary from.
|
|
)
|
|
|
|
var (
|
|
once sync.Once
|
|
wasm wazero.Runtime
|
|
module wazero.CompiledModule
|
|
counter atomic.Uint64
|
|
)
|
|
|
|
func compile() <span class="cov8" title="1">{
|
|
ctx := context.Background()
|
|
|
|
wasm = wazero.NewRuntime(ctx)
|
|
|
|
if err := vfsInstantiate(ctx, wasm); err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">if Binary == nil && Path != "" </span><span class="cov0" title="0">{
|
|
if bin, err := os.ReadFile(Path); err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
} else<span class="cov0" title="0"> {
|
|
Binary = bin
|
|
}</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">if m, err := wasm.CompileModule(ctx, Binary); err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
} else<span class="cov8" title="1"> {
|
|
module = m
|
|
}</span>
|
|
}
|
|
</pre>
|
|
|
|
<pre class="file" id="file2" style="display: none">package sqlite3
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"math"
|
|
"strconv"
|
|
|
|
"github.com/tetratelabs/wazero"
|
|
"github.com/tetratelabs/wazero/api"
|
|
)
|
|
|
|
type Conn struct {
|
|
ctx context.Context
|
|
handle uint32
|
|
module api.Module
|
|
memory api.Memory
|
|
api sqliteAPI
|
|
}
|
|
|
|
func Open(filename string) (conn *Conn, err error) <span class="cov8" title="1">{
|
|
return OpenFlags(filename, OPEN_READWRITE|OPEN_CREATE)
|
|
}</span>
|
|
|
|
func OpenFlags(filename string, flags OpenFlag) (conn *Conn, err error) <span class="cov8" title="1">{
|
|
once.Do(compile)
|
|
|
|
ctx := context.Background()
|
|
cfg := wazero.NewModuleConfig().
|
|
WithName("sqlite3-" + strconv.FormatUint(counter.Add(1), 10))
|
|
module, err := wasm.InstantiateModule(ctx, module, cfg)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return nil, err
|
|
}</span>
|
|
<span class="cov8" title="1">defer func() </span><span class="cov8" title="1">{
|
|
if conn == nil </span><span class="cov8" title="1">{
|
|
module.Close(ctx)
|
|
}</span>
|
|
}()
|
|
|
|
<span class="cov8" title="1">c := newConn(module)
|
|
c.ctx = context.Background()
|
|
namePtr := c.newString(filename)
|
|
connPtr := c.new(ptrlen)
|
|
defer c.free(namePtr)
|
|
defer c.free(connPtr)
|
|
|
|
r, err := c.api.open.Call(c.ctx, uint64(namePtr), uint64(connPtr), uint64(flags), 0)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return nil, err
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">c.handle, _ = c.memory.ReadUint32Le(connPtr)
|
|
|
|
if err := c.error(r[0]); err != nil </span><span class="cov8" title="1">{
|
|
return nil, err
|
|
}</span>
|
|
<span class="cov8" title="1">return c, nil</span>
|
|
}
|
|
|
|
func (c *Conn) Close() error <span class="cov8" title="1">{
|
|
r, err := c.api.close.Call(c.ctx, uint64(c.handle))
|
|
if err != nil </span><span class="cov8" title="1">{
|
|
return err
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">if err := c.error(r[0]); err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
<span class="cov8" title="1">return c.module.Close(c.ctx)</span>
|
|
}
|
|
|
|
func (c *Conn) Exec(sql string) error <span class="cov8" title="1">{
|
|
sqlPtr := c.newString(sql)
|
|
defer c.free(sqlPtr)
|
|
|
|
r, err := c.api.exec.Call(c.ctx, uint64(c.handle), uint64(sqlPtr), 0, 0, 0)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
<span class="cov8" title="1">return c.error(r[0])</span>
|
|
}
|
|
|
|
func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) <span class="cov8" title="1">{
|
|
return c.PrepareFlags(sql, 0)
|
|
}</span>
|
|
|
|
func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) <span class="cov8" title="1">{
|
|
sqlPtr := c.newString(sql)
|
|
stmtPtr := c.new(ptrlen)
|
|
tailPtr := c.new(ptrlen)
|
|
defer c.free(sqlPtr)
|
|
defer c.free(stmtPtr)
|
|
defer c.free(tailPtr)
|
|
|
|
r, err := c.api.prepare.Call(c.ctx, uint64(c.handle),
|
|
uint64(sqlPtr), uint64(len(sql)+1), uint64(flags),
|
|
uint64(stmtPtr), uint64(tailPtr))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return nil, "", err
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">stmt = &Stmt{c: c}
|
|
stmt.handle, _ = c.memory.ReadUint32Le(stmtPtr)
|
|
i, _ := c.memory.ReadUint32Le(tailPtr)
|
|
tail = sql[i-sqlPtr:]
|
|
|
|
if err := c.error(r[0]); err != nil </span><span class="cov0" title="0">{
|
|
return nil, "", err
|
|
}</span>
|
|
<span class="cov8" title="1">if stmt.handle == 0 </span><span class="cov0" title="0">{
|
|
return nil, "", nil
|
|
}</span>
|
|
<span class="cov8" title="1">return</span>
|
|
}
|
|
|
|
func (c *Conn) error(rc uint64) error <span class="cov8" title="1">{
|
|
if rc == _OK </span><span class="cov8" title="1">{
|
|
return nil
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">err := Error{
|
|
Code: ErrorCode(rc),
|
|
ExtendedCode: ExtendedErrorCode(rc),
|
|
}
|
|
|
|
if err.Code == NOMEM || err.ExtendedCode == IOERR_NOMEM </span><span class="cov0" title="0">{
|
|
panic(oomErr)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">var r []uint64
|
|
|
|
// Do this first, sqlite3_errmsg is guaranteed to never change the value of the error code.
|
|
r, _ = c.api.errmsg.Call(c.ctx, uint64(c.handle))
|
|
if r != nil </span><span class="cov8" title="1">{
|
|
err.msg = c.getString(uint32(r[0]), 512)
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">r, _ = c.api.errstr.Call(c.ctx, rc)
|
|
if r != nil </span><span class="cov8" title="1">{
|
|
err.str = c.getString(uint32(r[0]), 512)
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">if err.msg == err.str </span><span class="cov8" title="1">{
|
|
err.msg = ""
|
|
|
|
}</span>
|
|
<span class="cov8" title="1">return &err</span>
|
|
}
|
|
|
|
func (c *Conn) free(ptr uint32) <span class="cov8" title="1">{
|
|
if ptr == 0 </span><span class="cov8" title="1">{
|
|
return
|
|
}</span>
|
|
<span class="cov8" title="1">_, err := c.api.free.Call(c.ctx, uint64(ptr))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
}
|
|
|
|
func (c *Conn) new(len uint32) uint32 <span class="cov8" title="1">{
|
|
r, err := c.api.malloc.Call(c.ctx, uint64(len))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
<span class="cov8" title="1">ptr := uint32(r[0])
|
|
if ptr == 0 || ptr >= c.memory.Size() </span><span class="cov8" title="1">{
|
|
panic(oomErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return ptr</span>
|
|
}
|
|
|
|
func (c *Conn) newBytes(s []byte) uint32 <span class="cov8" title="1">{
|
|
if s == nil </span><span class="cov8" title="1">{
|
|
return 0
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">siz := uint32(len(s))
|
|
ptr := c.new(siz)
|
|
mem, ok := c.memory.Read(ptr, siz)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
c.api.free.Call(c.ctx, uint64(ptr))
|
|
panic(rangeErr)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">copy(mem, s)
|
|
return ptr</span>
|
|
}
|
|
|
|
func (c *Conn) newString(s string) uint32 <span class="cov8" title="1">{
|
|
siz := uint32(len(s) + 1)
|
|
ptr := c.new(siz)
|
|
mem, ok := c.memory.Read(ptr, siz)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
c.api.free.Call(c.ctx, uint64(ptr))
|
|
panic(rangeErr)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">mem[len(s)] = 0
|
|
copy(mem, s)
|
|
return ptr</span>
|
|
}
|
|
|
|
func (c *Conn) getString(ptr, maxlen uint32) string <span class="cov8" title="1">{
|
|
return getString(c.memory, ptr, maxlen)
|
|
}</span>
|
|
|
|
func getString(memory api.Memory, ptr, maxlen uint32) string <span class="cov8" title="1">{
|
|
if ptr == 0 </span><span class="cov8" title="1">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">switch maxlen </span>{
|
|
case 0:<span class="cov8" title="1">
|
|
return ""</span>
|
|
case math.MaxUint32:<span class="cov8" title="1"></span>
|
|
//
|
|
default:<span class="cov8" title="1">
|
|
maxlen = maxlen + 1</span>
|
|
}
|
|
<span class="cov8" title="1">mem, ok := memory.Read(ptr, maxlen)
|
|
if !ok </span><span class="cov8" title="1">{
|
|
mem, ok = memory.Read(ptr, memory.Size()-ptr)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
}
|
|
<span class="cov8" title="1">if i := bytes.IndexByte(mem, 0); i < 0 </span><span class="cov8" title="1">{
|
|
panic(noNulErr)</span>
|
|
} else<span class="cov8" title="1"> {
|
|
return string(mem[:i])
|
|
}</span>
|
|
}
|
|
</pre>
|
|
|
|
<pre class="file" id="file3" style="display: none">package sqlite3
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type Error struct {
|
|
Code ErrorCode
|
|
ExtendedCode ExtendedErrorCode
|
|
str string
|
|
msg string
|
|
}
|
|
|
|
func (e *Error) Error() string <span class="cov8" title="1">{
|
|
var b strings.Builder
|
|
b.WriteString("sqlite3: ")
|
|
|
|
if e.str != "" </span><span class="cov8" title="1">{
|
|
b.WriteString(e.str)
|
|
}</span> else<span class="cov0" title="0"> {
|
|
b.WriteString(strconv.Itoa(int(e.Code)))
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">if e.msg != "" </span><span class="cov0" title="0">{
|
|
b.WriteByte(':')
|
|
b.WriteByte(' ')
|
|
b.WriteString(e.msg)
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">return b.String()</span>
|
|
}
|
|
|
|
type errorString string
|
|
|
|
func (e errorString) Error() string <span class="cov0" title="0">{ return string(e) }</span>
|
|
|
|
const (
|
|
nilErr = errorString("sqlite3: invalid memory address or null pointer dereference")
|
|
oomErr = errorString("sqlite3: out of memory")
|
|
rangeErr = errorString("sqlite3: index out of range")
|
|
noNulErr = errorString("sqlite3: missing NUL terminator")
|
|
noGlobalErr = errorString("sqlite3: could not find global: ")
|
|
noFuncErr = errorString("sqlite3: could not find function: ")
|
|
assertErr = errorString("sqlite3: assertion failed")
|
|
)
|
|
</pre>
|
|
|
|
<pre class="file" id="file4" style="display: none">package sqlite3
|
|
|
|
import (
|
|
"math"
|
|
)
|
|
|
|
type Stmt struct {
|
|
c *Conn
|
|
handle uint32
|
|
err error
|
|
}
|
|
|
|
func (s *Stmt) Close() error <span class="cov8" title="1">{
|
|
r, err := s.c.api.finalize.Call(s.c.ctx, uint64(s.handle))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">s.handle = 0
|
|
return s.c.error(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) Reset() error <span class="cov0" title="0">{
|
|
r, err := s.c.api.reset.Call(s.c.ctx, uint64(s.handle))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
<span class="cov0" title="0">return s.c.error(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) Step() bool <span class="cov8" title="1">{
|
|
r, err := s.c.api.step.Call(s.c.ctx, uint64(s.handle))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
s.err = err
|
|
return false
|
|
}</span>
|
|
<span class="cov8" title="1">if r[0] == _ROW </span><span class="cov8" title="1">{
|
|
return true
|
|
}</span>
|
|
<span class="cov8" title="1">if r[0] == _DONE </span><span class="cov8" title="1">{
|
|
s.err = nil
|
|
}</span> else<span class="cov0" title="0"> {
|
|
s.err = s.c.error(r[0])
|
|
}</span>
|
|
<span class="cov8" title="1">return false</span>
|
|
}
|
|
|
|
func (s *Stmt) Err() error <span class="cov8" title="1">{
|
|
return s.err
|
|
}</span>
|
|
|
|
func (s *Stmt) BindBool(param int, value bool) error <span class="cov0" title="0">{
|
|
if value </span><span class="cov0" title="0">{
|
|
return s.BindInt64(param, 1)
|
|
}</span>
|
|
<span class="cov0" title="0">return s.BindInt64(param, 0)</span>
|
|
}
|
|
|
|
func (s *Stmt) BindInt(param int, value int) error <span class="cov0" title="0">{
|
|
return s.BindInt64(param, int64(value))
|
|
}</span>
|
|
|
|
func (s *Stmt) BindInt64(param int, value int64) error <span class="cov0" title="0">{
|
|
r, err := s.c.api.bindInteger.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(param), uint64(value))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
<span class="cov0" title="0">return s.c.error(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) BindFloat(param int, value float64) error <span class="cov0" title="0">{
|
|
r, err := s.c.api.bindFloat.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(param), math.Float64bits(value))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
<span class="cov0" title="0">return s.c.error(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) BindText(param int, value string) error <span class="cov0" title="0">{
|
|
ptr := s.c.newString(value)
|
|
r, err := s.c.api.bindText.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(param),
|
|
uint64(ptr), uint64(len(value)),
|
|
s.c.api.destructor, _UTF8)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
<span class="cov0" title="0">return s.c.error(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) BindBlob(param int, value []byte) error <span class="cov0" title="0">{
|
|
ptr := s.c.newBytes(value)
|
|
r, err := s.c.api.bindBlob.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(param),
|
|
uint64(ptr), uint64(len(value)),
|
|
s.c.api.destructor)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
<span class="cov0" title="0">return s.c.error(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) BindNull(param int) error <span class="cov0" title="0">{
|
|
r, err := s.c.api.bindNull.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(param))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
<span class="cov0" title="0">return s.c.error(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) ColumnBool(col int) bool <span class="cov0" title="0">{
|
|
if i := s.ColumnInt64(col); i != 0 </span><span class="cov0" title="0">{
|
|
return true
|
|
}</span>
|
|
<span class="cov0" title="0">return false</span>
|
|
}
|
|
|
|
func (s *Stmt) ColumnInt(col int) int <span class="cov8" title="1">{
|
|
return int(s.ColumnInt64(col))
|
|
}</span>
|
|
|
|
func (s *Stmt) ColumnInt64(col int) int64 <span class="cov8" title="1">{
|
|
r, err := s.c.api.columnInteger.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(col))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
<span class="cov8" title="1">return int64(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) ColumnFloat(col int) float64 <span class="cov0" title="0">{
|
|
r, err := s.c.api.columnInteger.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(col))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
<span class="cov0" title="0">return math.Float64frombits(r[0])</span>
|
|
}
|
|
|
|
func (s *Stmt) ColumnText(col int) string <span class="cov8" title="1">{
|
|
r, err := s.c.api.columnText.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(col))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">ptr := uint32(r[0])
|
|
if ptr == 0 </span><span class="cov0" title="0">{
|
|
r, err = s.c.api.errcode.Call(s.c.ctx, uint64(s.handle))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
<span class="cov0" title="0">s.err = s.c.error(r[0])
|
|
return ""</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">r, err = s.c.api.columnBytes.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(col))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">mem, ok := s.c.memory.Read(ptr, uint32(r[0]))
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return string(mem)</span>
|
|
}
|
|
|
|
func (s *Stmt) ColumnBlob(col int, buf []byte) []byte <span class="cov0" title="0">{
|
|
r, err := s.c.api.columnBlob.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(col))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
|
|
<span class="cov0" title="0">ptr := uint32(r[0])
|
|
if ptr == 0 </span><span class="cov0" title="0">{
|
|
r, err = s.c.api.errcode.Call(s.c.ctx, uint64(s.handle))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
<span class="cov0" title="0">s.err = s.c.error(r[0])
|
|
return nil</span>
|
|
}
|
|
|
|
<span class="cov0" title="0">r, err = s.c.api.columnBytes.Call(s.c.ctx,
|
|
uint64(s.handle), uint64(col))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
panic(err)</span>
|
|
}
|
|
|
|
<span class="cov0" title="0">mem, ok := s.c.memory.Read(ptr, uint32(r[0]))
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov0" title="0">return append(buf[0:0], mem...)</span>
|
|
}
|
|
</pre>
|
|
|
|
<pre class="file" id="file5" style="display: none">package sqlite3
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"io/fs"
|
|
"math/rand"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/ncruces/julianday"
|
|
"github.com/tetratelabs/wazero"
|
|
"github.com/tetratelabs/wazero/api"
|
|
"github.com/tetratelabs/wazero/sys"
|
|
)
|
|
|
|
func vfsInstantiate(ctx context.Context, r wazero.Runtime) (err error) <span class="cov8" title="1">{
|
|
wasi := r.NewHostModuleBuilder("wasi_snapshot_preview1")
|
|
wasi.NewFunctionBuilder().WithFunc(vfsExit).Export("proc_exit")
|
|
_, err = wasi.Instantiate(ctx)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return err
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">env := r.NewHostModuleBuilder("env")
|
|
env.NewFunctionBuilder().WithFunc(vfsLocaltime).Export("go_localtime")
|
|
env.NewFunctionBuilder().WithFunc(vfsRandomness).Export("go_randomness")
|
|
env.NewFunctionBuilder().WithFunc(vfsSleep).Export("go_sleep")
|
|
env.NewFunctionBuilder().WithFunc(vfsCurrentTime).Export("go_current_time")
|
|
env.NewFunctionBuilder().WithFunc(vfsCurrentTime64).Export("go_current_time_64")
|
|
env.NewFunctionBuilder().WithFunc(vfsFullPathname).Export("go_full_pathname")
|
|
env.NewFunctionBuilder().WithFunc(vfsDelete).Export("go_delete")
|
|
env.NewFunctionBuilder().WithFunc(vfsAccess).Export("go_access")
|
|
env.NewFunctionBuilder().WithFunc(vfsOpen).Export("go_open")
|
|
env.NewFunctionBuilder().WithFunc(vfsClose).Export("go_close")
|
|
env.NewFunctionBuilder().WithFunc(vfsRead).Export("go_read")
|
|
env.NewFunctionBuilder().WithFunc(vfsWrite).Export("go_write")
|
|
env.NewFunctionBuilder().WithFunc(vfsTruncate).Export("go_truncate")
|
|
env.NewFunctionBuilder().WithFunc(vfsSync).Export("go_sync")
|
|
env.NewFunctionBuilder().WithFunc(vfsFileSize).Export("go_file_size")
|
|
env.NewFunctionBuilder().WithFunc(vfsLock).Export("go_lock")
|
|
env.NewFunctionBuilder().WithFunc(vfsUnlock).Export("go_unlock")
|
|
env.NewFunctionBuilder().WithFunc(vfsCheckReservedLock).Export("go_check_reserved_lock")
|
|
_, err = env.Instantiate(ctx)
|
|
return err</span>
|
|
}
|
|
|
|
func vfsExit(ctx context.Context, mod api.Module, exitCode uint32) <span class="cov0" title="0">{
|
|
// Ensure other callers see the exit code.
|
|
_ = mod.CloseWithExitCode(ctx, exitCode)
|
|
// Prevent any code from executing after this function.
|
|
panic(sys.NewExitError(mod.Name(), exitCode))</span>
|
|
}
|
|
|
|
func vfsLocaltime(ctx context.Context, mod api.Module, t uint64, pTm uint32) uint32 <span class="cov8" title="1">{
|
|
tm := time.Unix(int64(t), 0)
|
|
var isdst int
|
|
if tm.IsDST() </span><span class="cov0" title="0">{
|
|
isdst = 1
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">if pTm == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
// https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html
|
|
<span class="cov8" title="1">if mem := mod.Memory(); true &&
|
|
mem.WriteUint32Le(pTm+0*ptrlen, uint32(tm.Second())) &&
|
|
mem.WriteUint32Le(pTm+1*ptrlen, uint32(tm.Minute())) &&
|
|
mem.WriteUint32Le(pTm+2*ptrlen, uint32(tm.Hour())) &&
|
|
mem.WriteUint32Le(pTm+3*ptrlen, uint32(tm.Day())) &&
|
|
mem.WriteUint32Le(pTm+4*ptrlen, uint32(tm.Month()-time.January)) &&
|
|
mem.WriteUint32Le(pTm+5*ptrlen, uint32(tm.Year()-1900)) &&
|
|
mem.WriteUint32Le(pTm+6*ptrlen, uint32(tm.Weekday()-time.Sunday)) &&
|
|
mem.WriteUint32Le(pTm+7*ptrlen, uint32(tm.YearDay()-1)) &&
|
|
mem.WriteUint32Le(pTm+8*ptrlen, uint32(isdst)) </span><span class="cov8" title="1">{
|
|
return _OK
|
|
}</span>
|
|
<span class="cov0" title="0">panic(rangeErr)</span>
|
|
}
|
|
|
|
func vfsRandomness(ctx context.Context, mod api.Module, pVfs, nByte, zByte uint32) uint32 <span class="cov8" title="1">{
|
|
if zByte == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">mem, ok := mod.Memory().Read(zByte, nByte)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">n, _ := rand.Read(mem)
|
|
return uint32(n)</span>
|
|
}
|
|
|
|
func vfsSleep(ctx context.Context, pVfs, nMicro uint32) uint32 <span class="cov8" title="1">{
|
|
time.Sleep(time.Duration(nMicro) * time.Microsecond)
|
|
return _OK
|
|
}</span>
|
|
|
|
func vfsCurrentTime(ctx context.Context, mod api.Module, pVfs, prNow uint32) uint32 <span class="cov8" title="1">{
|
|
day := julianday.Float(time.Now())
|
|
if prNow == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">if ok := mod.Memory().WriteFloat64Le(prNow, day); !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow uint32) uint32 <span class="cov8" title="1">{
|
|
day, nsec := julianday.Date(time.Now())
|
|
msec := day*86_400_000 + nsec/1_000_000
|
|
if piNow == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">if ok := mod.Memory().WriteUint64Le(piNow, uint64(msec)); !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative, nFull, zFull uint32) uint32 <span class="cov8" title="1">{
|
|
rel := getString(mod.Memory(), zRelative, _MAX_PATHNAME)
|
|
abs, err := filepath.Abs(rel)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(IOERR)
|
|
}</span>
|
|
|
|
// Consider either using [filepath.EvalSymlinks] to canonicalize the path (as the Unix VFS does).
|
|
// Or using [os.Readlink] to resolve a symbolic link (as the Unix VFS did).
|
|
// This might be buggy on Windows (the Windows VFS doesn't try).
|
|
|
|
<span class="cov8" title="1">siz := uint32(len(abs) + 1)
|
|
if siz > nFull </span><span class="cov0" title="0">{
|
|
return uint32(CANTOPEN_FULLPATH)
|
|
}</span>
|
|
<span class="cov8" title="1">if zFull == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">mem, ok := mod.Memory().Read(zFull, siz)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">mem[len(abs)] = 0
|
|
copy(mem, abs)
|
|
return _OK</span>
|
|
}
|
|
|
|
func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32) uint32 <span class="cov8" title="1">{
|
|
path := getString(mod.Memory(), zPath, _MAX_PATHNAME)
|
|
err := os.Remove(path)
|
|
if errors.Is(err, fs.ErrNotExist) </span><span class="cov0" title="0">{
|
|
return _OK
|
|
}</span>
|
|
<span class="cov8" title="1">if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_DELETE)
|
|
}</span>
|
|
<span class="cov8" title="1">if syncDir != 0 </span><span class="cov0" title="0">{
|
|
f, err := os.Open(filepath.Dir(path))
|
|
if err == nil </span><span class="cov0" title="0">{
|
|
err = f.Sync()
|
|
f.Close()
|
|
}</span>
|
|
<span class="cov0" title="0">if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_DELETE)
|
|
}</span>
|
|
}
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags AccessFlag, pResOut uint32) uint32 <span class="cov8" title="1">{
|
|
// Consider using [syscall.Access] for [ACCESS_READWRITE]/[ACCESS_READ]
|
|
// (as the Unix VFS does).
|
|
|
|
path := getString(mod.Memory(), zPath, _MAX_PATHNAME)
|
|
fi, err := os.Stat(path)
|
|
|
|
var res uint32
|
|
switch </span>{
|
|
case flags == ACCESS_EXISTS:<span class="cov8" title="1">
|
|
switch </span>{
|
|
case err == nil:<span class="cov0" title="0">
|
|
res = 1</span>
|
|
case errors.Is(err, fs.ErrNotExist):<span class="cov8" title="1">
|
|
res = 0</span>
|
|
default:<span class="cov0" title="0">
|
|
return uint32(IOERR_ACCESS)</span>
|
|
}
|
|
|
|
case err == nil:<span class="cov0" title="0">
|
|
var want fs.FileMode = syscall.S_IRUSR
|
|
if flags == ACCESS_READWRITE </span><span class="cov0" title="0">{
|
|
want |= syscall.S_IWUSR
|
|
}</span>
|
|
<span class="cov0" title="0">if fi.IsDir() </span><span class="cov0" title="0">{
|
|
want |= syscall.S_IXUSR
|
|
}</span>
|
|
<span class="cov0" title="0">if fi.Mode()&want == want </span><span class="cov0" title="0">{
|
|
res = 1
|
|
}</span> else<span class="cov0" title="0"> {
|
|
res = 0
|
|
}</span>
|
|
|
|
case errors.Is(err, fs.ErrPermission):<span class="cov0" title="0">
|
|
res = 0</span>
|
|
|
|
default:<span class="cov0" title="0">
|
|
return uint32(IOERR_ACCESS)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">if pResOut == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">if ok := mod.Memory().WriteUint32Le(pResOut, res); !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func vfsOpen(ctx context.Context, mod api.Module, pVfs, zName, pFile uint32, flags OpenFlag, pOutFlags uint32) uint32 <span class="cov8" title="1">{
|
|
var oflags int
|
|
if flags&OPEN_EXCLUSIVE != 0 </span><span class="cov0" title="0">{
|
|
oflags |= os.O_EXCL
|
|
}</span>
|
|
<span class="cov8" title="1">if flags&OPEN_CREATE != 0 </span><span class="cov8" title="1">{
|
|
oflags |= os.O_CREATE
|
|
}</span>
|
|
<span class="cov8" title="1">if flags&OPEN_READONLY != 0 </span><span class="cov0" title="0">{
|
|
oflags |= os.O_RDONLY
|
|
}</span>
|
|
<span class="cov8" title="1">if flags&OPEN_READWRITE != 0 </span><span class="cov8" title="1">{
|
|
oflags |= os.O_RDWR
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">var err error
|
|
var file *os.File
|
|
if zName == 0 </span><span class="cov0" title="0">{
|
|
file, err = os.CreateTemp("", "*.db")
|
|
}</span> else<span class="cov8" title="1"> {
|
|
name := getString(mod.Memory(), zName, _MAX_PATHNAME)
|
|
file, err = os.OpenFile(name, oflags, 0600)
|
|
}</span>
|
|
<span class="cov8" title="1">if err != nil </span><span class="cov8" title="1">{
|
|
return uint32(CANTOPEN)
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">if flags&OPEN_DELETEONCLOSE != 0 </span><span class="cov0" title="0">{
|
|
deleteOnClose(file)
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">info, err := file.Stat()
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(CANTOPEN)
|
|
}</span>
|
|
<span class="cov8" title="1">if info.IsDir() </span><span class="cov0" title="0">{
|
|
return uint32(CANTOPEN_ISDIR)
|
|
}</span>
|
|
<span class="cov8" title="1">id := vfsGetOpenFileID(file, info)
|
|
vfsFilePtr{mod, pFile}.SetID(id).SetLock(_NO_LOCK)
|
|
|
|
if pOutFlags == 0 </span><span class="cov8" title="1">{
|
|
return _OK
|
|
}</span>
|
|
<span class="cov8" title="1">if ok := mod.Memory().WriteUint32Le(pOutFlags, uint32(flags)); !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func vfsClose(ctx context.Context, mod api.Module, pFile uint32) uint32 <span class="cov8" title="1">{
|
|
id := vfsFilePtr{mod, pFile}.ID()
|
|
err := vfsReleaseOpenFile(id)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_CLOSE)
|
|
}</span>
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf, iAmt uint32, iOfst uint64) uint32 <span class="cov8" title="1">{
|
|
if zBuf == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">buf, ok := mod.Memory().Read(zBuf, iAmt)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">file := vfsFilePtr{mod, pFile}.OSFile()
|
|
n, err := file.ReadAt(buf, int64(iOfst))
|
|
if n == int(iAmt) </span><span class="cov0" title="0">{
|
|
return _OK
|
|
}</span>
|
|
<span class="cov8" title="1">if n == 0 && err != io.EOF </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_READ)
|
|
}</span>
|
|
<span class="cov8" title="1">for i := range buf[n:] </span><span class="cov8" title="1">{
|
|
buf[i] = 0
|
|
}</span>
|
|
<span class="cov8" title="1">return uint32(IOERR_SHORT_READ)</span>
|
|
}
|
|
|
|
func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf, iAmt uint32, iOfst uint64) uint32 <span class="cov8" title="1">{
|
|
if zBuf == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">buf, ok := mod.Memory().Read(zBuf, iAmt)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">file := vfsFilePtr{mod, pFile}.OSFile()
|
|
_, err := file.WriteAt(buf, int64(iOfst))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_WRITE)
|
|
}</span>
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte uint64) uint32 <span class="cov0" title="0">{
|
|
file := vfsFilePtr{mod, pFile}.OSFile()
|
|
err := file.Truncate(int64(nByte))
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_TRUNCATE)
|
|
}</span>
|
|
<span class="cov0" title="0">return _OK</span>
|
|
}
|
|
|
|
func vfsSync(ctx context.Context, mod api.Module, pFile, flags uint32) uint32 <span class="cov8" title="1">{
|
|
file := vfsFilePtr{mod, pFile}.OSFile()
|
|
err := file.Sync()
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_FSYNC)
|
|
}</span>
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize uint32) uint32 <span class="cov8" title="1">{
|
|
// This uses [file.Seek] because we don't care about the offset for reading/writing.
|
|
// But consider using [file.Stat] instead (as other VFSes do).
|
|
|
|
file := vfsFilePtr{mod, pFile}.OSFile()
|
|
off, err := file.Seek(0, io.SeekEnd)
|
|
if err != nil </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_SEEK)
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">if pSize == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">if ok := mod.Memory().WriteUint64Le(pSize, uint64(off)); !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
</pre>
|
|
|
|
<pre class="file" id="file6" style="display: none">package sqlite3
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
|
)
|
|
|
|
type vfsOpenFile struct {
|
|
file *os.File
|
|
info os.FileInfo
|
|
nref int
|
|
|
|
shared int
|
|
vfsLocker
|
|
}
|
|
|
|
var (
|
|
vfsOpenFiles []*vfsOpenFile
|
|
vfsOpenFilesMtx sync.Mutex
|
|
)
|
|
|
|
func vfsGetOpenFileID(file *os.File, info os.FileInfo) uint32 <span class="cov8" title="1">{
|
|
vfsOpenFilesMtx.Lock()
|
|
defer vfsOpenFilesMtx.Unlock()
|
|
|
|
// Reuse an already opened file.
|
|
for id, of := range vfsOpenFiles </span><span class="cov8" title="1">{
|
|
if of == nil </span><span class="cov0" title="0">{
|
|
continue</span>
|
|
}
|
|
<span class="cov8" title="1">if os.SameFile(info, of.info) </span><span class="cov0" title="0">{
|
|
of.nref++
|
|
_ = file.Close()
|
|
return uint32(id)
|
|
}</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">of := &vfsOpenFile{
|
|
file: file,
|
|
info: info,
|
|
nref: 1,
|
|
|
|
vfsLocker: &vfsFileLocker{file, _NO_LOCK},
|
|
}
|
|
|
|
// Find an empty slot.
|
|
for id, ptr := range vfsOpenFiles </span><span class="cov8" title="1">{
|
|
if ptr == nil </span><span class="cov0" title="0">{
|
|
vfsOpenFiles[id] = of
|
|
return uint32(id)
|
|
}</span>
|
|
}
|
|
|
|
// Add a new slot.
|
|
<span class="cov8" title="1">id := len(vfsOpenFiles)
|
|
vfsOpenFiles = append(vfsOpenFiles, of)
|
|
return uint32(id)</span>
|
|
}
|
|
|
|
func vfsReleaseOpenFile(id uint32) error <span class="cov8" title="1">{
|
|
vfsOpenFilesMtx.Lock()
|
|
defer vfsOpenFilesMtx.Unlock()
|
|
|
|
of := vfsOpenFiles[id]
|
|
if of.nref--; of.nref > 0 </span><span class="cov0" title="0">{
|
|
return nil
|
|
}</span>
|
|
<span class="cov8" title="1">err := of.file.Close()
|
|
vfsOpenFiles[id] = nil
|
|
return err</span>
|
|
}
|
|
|
|
type vfsFilePtr struct {
|
|
api.Module
|
|
ptr uint32
|
|
}
|
|
|
|
func (p vfsFilePtr) OSFile() *os.File <span class="cov8" title="1">{
|
|
id := p.ID()
|
|
vfsOpenFilesMtx.Lock()
|
|
defer vfsOpenFilesMtx.Unlock()
|
|
return vfsOpenFiles[id].file
|
|
}</span>
|
|
|
|
func (p vfsFilePtr) ID() uint32 <span class="cov8" title="1">{
|
|
if p.ptr == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">id, ok := p.Memory().ReadUint32Le(p.ptr + ptrlen)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return id</span>
|
|
}
|
|
|
|
func (p vfsFilePtr) Lock() vfsLockState <span class="cov8" title="1">{
|
|
if p.ptr == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">lk, ok := p.Memory().ReadUint32Le(p.ptr + 2*ptrlen)
|
|
if !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return vfsLockState(lk)</span>
|
|
}
|
|
|
|
func (p vfsFilePtr) SetID(id uint32) vfsFilePtr <span class="cov8" title="1">{
|
|
if p.ptr == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">if ok := p.Memory().WriteUint32Le(p.ptr+ptrlen, id); !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return p</span>
|
|
}
|
|
|
|
func (p vfsFilePtr) SetLock(lock vfsLockState) vfsFilePtr <span class="cov8" title="1">{
|
|
if p.ptr == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov8" title="1">if ok := p.Memory().WriteUint32Le(p.ptr+2*ptrlen, uint32(lock)); !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov8" title="1">return p</span>
|
|
}
|
|
</pre>
|
|
|
|
<pre class="file" id="file7" style="display: none">package sqlite3
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
|
)
|
|
|
|
const (
|
|
// No locks are held on the database.
|
|
// The database may be neither read nor written.
|
|
// Any internally cached data is considered suspect and subject to
|
|
// verification against the database file before being used.
|
|
// Other processes can read or write the database as their own locking
|
|
// states permit.
|
|
// This is the default state.
|
|
_NO_LOCK = 0
|
|
|
|
// The database may be read but not written.
|
|
// Any number of processes can hold SHARED locks at the same time,
|
|
// hence there can be many simultaneous readers.
|
|
// But no other thread or process is allowed to write to the database file
|
|
// while one or more SHARED locks are active.
|
|
_SHARED_LOCK = 1
|
|
|
|
// A RESERVED lock means that the process is planning on writing to the
|
|
// database file at some point in the future but that it is currently just
|
|
// reading from the file.
|
|
// Only a single RESERVED lock may be active at one time,
|
|
// though multiple SHARED locks can coexist with a single RESERVED lock.
|
|
// RESERVED differs from PENDING in that new SHARED locks can be acquired
|
|
// while there is a RESERVED lock.
|
|
_RESERVED_LOCK = 2
|
|
|
|
// A PENDING lock means that the process holding the lock wants to write to
|
|
// the database as soon as possible and is just waiting on all current
|
|
// SHARED locks to clear so that it can get an EXCLUSIVE lock.
|
|
// No new SHARED locks are permitted against the database if a PENDING lock
|
|
// is active, though existing SHARED locks are allowed to continue.
|
|
_PENDING_LOCK = 3
|
|
|
|
// An EXCLUSIVE lock is needed in order to write to the database file.
|
|
// Only one EXCLUSIVE lock is allowed on the file and no other locks of any
|
|
// kind are allowed to coexist with an EXCLUSIVE lock.
|
|
// In order to maximize concurrency, SQLite works to minimize the amount of
|
|
// time that EXCLUSIVE locks are held.
|
|
_EXCLUSIVE_LOCK = 4
|
|
|
|
_PENDING_BYTE = 0x40000000
|
|
_RESERVED_BYTE = (_PENDING_BYTE + 1)
|
|
_SHARED_FIRST = (_PENDING_BYTE + 2)
|
|
_SHARED_SIZE = 510
|
|
)
|
|
|
|
type (
|
|
vfsLockState uint32
|
|
xErrorCode = ExtendedErrorCode
|
|
)
|
|
|
|
type vfsLocker interface {
|
|
LockState() vfsLockState
|
|
|
|
LockShared() xErrorCode // UNLOCKED -> SHARED
|
|
LockReserved() xErrorCode // SHARED -> RESERVED
|
|
LockPending() xErrorCode // SHARED|RESERVED -> PENDING
|
|
LockExclusive() xErrorCode // PENDING -> EXCLUSIVE
|
|
DowngradeLock() xErrorCode // SHARED <- EXCLUSIVE|PENDING|RESERVED
|
|
Unlock() xErrorCode // UNLOCKED <- EXCLUSIVE|PENDING|RESERVED|SHARED
|
|
|
|
CheckReservedLock() (bool, xErrorCode)
|
|
}
|
|
|
|
type vfsFileLocker struct {
|
|
*os.File
|
|
state vfsLockState
|
|
}
|
|
|
|
func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockState) uint32 <span class="cov8" title="1">{
|
|
if assert && (eLock == _NO_LOCK || eLock == _PENDING_LOCK) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [d4oxww]")</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">ptr := vfsFilePtr{mod, pFile}
|
|
cLock := ptr.Lock()
|
|
|
|
// If we already have an equal or more restrictive lock, do nothing.
|
|
if cLock >= eLock </span><span class="cov0" title="0">{
|
|
return _OK
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">if assert </span><span class="cov8" title="1">{
|
|
switch </span>{
|
|
case cLock == _NO_LOCK && eLock > _SHARED_LOCK:<span class="cov0" title="0">
|
|
// We never move from unlocked to anything higher than shared lock.
|
|
panic(assertErr + " [pfa77m]")</span>
|
|
case cLock != _SHARED_LOCK && eLock == _RESERVED_LOCK:<span class="cov0" title="0">
|
|
// A shared lock is always held when a reserved lock is requested.
|
|
panic(assertErr + " [5cfmsp]")</span>
|
|
}
|
|
}
|
|
|
|
<span class="cov8" title="1">vfsOpenFilesMtx.Lock()
|
|
defer vfsOpenFilesMtx.Unlock()
|
|
of := vfsOpenFiles[ptr.ID()]
|
|
fLock := of.LockState()
|
|
|
|
// If some other connection has a lock that precludes the requested lock, return BUSY.
|
|
if cLock != fLock && (eLock > _SHARED_LOCK || fLock >= _PENDING_LOCK) </span><span class="cov0" title="0">{
|
|
return uint32(BUSY)
|
|
}</span>
|
|
<span class="cov8" title="1">if eLock == _EXCLUSIVE_LOCK && of.shared > 1 </span><span class="cov0" title="0">{
|
|
// We are trying for an exclusive lock but another connection in this
|
|
// same process is still holding a shared lock.
|
|
return uint32(BUSY)
|
|
}</span>
|
|
|
|
// If a SHARED lock is requested, and some other connection has a SHARED or RESERVED lock,
|
|
// then increment the reference count and return OK.
|
|
<span class="cov8" title="1">if eLock == _SHARED_LOCK && (fLock == _SHARED_LOCK || fLock == _RESERVED_LOCK) </span><span class="cov0" title="0">{
|
|
if assert && !(cLock == _NO_LOCK && of.shared > 0) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [k7coz6]")</span>
|
|
}
|
|
<span class="cov0" title="0">ptr.SetLock(_SHARED_LOCK)
|
|
of.shared++
|
|
return _OK</span>
|
|
}
|
|
|
|
// If control gets to this point, then actually go ahead and make
|
|
// operating system calls for the specified lock.
|
|
<span class="cov8" title="1">switch eLock </span>{
|
|
case _SHARED_LOCK:<span class="cov8" title="1">
|
|
if assert && !(fLock == _NO_LOCK && of.shared == 0) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [jsyttq]")</span>
|
|
}
|
|
<span class="cov8" title="1">if rc := of.LockShared(); rc != _OK </span><span class="cov0" title="0">{
|
|
return uint32(rc)
|
|
}</span>
|
|
<span class="cov8" title="1">of.shared = 1
|
|
ptr.SetLock(_SHARED_LOCK)
|
|
return _OK</span>
|
|
|
|
case _RESERVED_LOCK:<span class="cov8" title="1">
|
|
if rc := of.LockReserved(); rc != _OK </span><span class="cov0" title="0">{
|
|
return uint32(rc)
|
|
}</span>
|
|
<span class="cov8" title="1">ptr.SetLock(_RESERVED_LOCK)
|
|
return _OK</span>
|
|
|
|
case _EXCLUSIVE_LOCK:<span class="cov8" title="1">
|
|
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
|
|
if cLock < _PENDING_LOCK </span><span class="cov8" title="1">{
|
|
if rc := of.LockPending(); rc != _OK </span><span class="cov0" title="0">{
|
|
return uint32(rc)
|
|
}</span>
|
|
<span class="cov8" title="1">ptr.SetLock(_PENDING_LOCK)</span>
|
|
}
|
|
<span class="cov8" title="1">if rc := of.LockExclusive(); rc != _OK </span><span class="cov0" title="0">{
|
|
return uint32(rc)
|
|
}</span>
|
|
<span class="cov8" title="1">ptr.SetLock(_EXCLUSIVE_LOCK)
|
|
return _OK</span>
|
|
|
|
default:<span class="cov0" title="0">
|
|
panic(assertErr + " [56ng2l]")</span>
|
|
}
|
|
}
|
|
|
|
func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockState) uint32 <span class="cov8" title="1">{
|
|
if assert && (eLock != _NO_LOCK && eLock != _SHARED_LOCK) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [7i4jw3]")</span>
|
|
}
|
|
|
|
<span class="cov8" title="1">ptr := vfsFilePtr{mod, pFile}
|
|
cLock := ptr.Lock()
|
|
|
|
// If we don't have a more restrictive lock, do nothing.
|
|
if cLock <= eLock </span><span class="cov0" title="0">{
|
|
return _OK
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">vfsOpenFilesMtx.Lock()
|
|
defer vfsOpenFilesMtx.Unlock()
|
|
of := vfsOpenFiles[ptr.ID()]
|
|
fLock := of.LockState()
|
|
|
|
if assert && of.shared <= 0 </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [2bhkwg]")</span>
|
|
}
|
|
<span class="cov8" title="1">if cLock > _SHARED_LOCK </span><span class="cov8" title="1">{
|
|
if assert && cLock != fLock </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [6pmjqf]")</span>
|
|
}
|
|
<span class="cov8" title="1">if eLock == _SHARED_LOCK </span><span class="cov8" title="1">{
|
|
if rc := of.DowngradeLock(); rc != _OK </span><span class="cov0" title="0">{
|
|
// In theory, the downgrade to a SHARED cannot fail because another
|
|
// process is holding an incompatible lock. If it does, this
|
|
// indicates that the other process is not following the locking
|
|
// protocol. If this happens, return IOERR_RDLOCK. Returning
|
|
// BUSY would confuse the upper layer.
|
|
return uint32(IOERR_RDLOCK)
|
|
}</span>
|
|
<span class="cov8" title="1">ptr.SetLock(_SHARED_LOCK)
|
|
return _OK</span>
|
|
}
|
|
}
|
|
|
|
<span class="cov8" title="1">if assert && eLock != _NO_LOCK </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [gilo9p]")</span>
|
|
}
|
|
// Decrement the shared lock counter. Release the file lock
|
|
// only when all connections have released the lock.
|
|
<span class="cov8" title="1">switch </span>{
|
|
case of.shared > 1:<span class="cov0" title="0">
|
|
ptr.SetLock(_NO_LOCK)
|
|
of.shared--
|
|
return _OK</span>
|
|
|
|
case of.shared == 1:<span class="cov8" title="1">
|
|
if rc := of.Unlock(); rc != _OK </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_UNLOCK)
|
|
}</span>
|
|
<span class="cov8" title="1">ptr.SetLock(_NO_LOCK)
|
|
of.shared = 0
|
|
return _OK</span>
|
|
|
|
default:<span class="cov0" title="0">
|
|
panic(assertErr + " [50gw51]")</span>
|
|
}
|
|
}
|
|
|
|
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) uint32 <span class="cov0" title="0">{
|
|
ptr := vfsFilePtr{mod, pFile}
|
|
cLock := ptr.Lock()
|
|
|
|
if assert && cLock > _SHARED_LOCK </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [zarygt]")</span>
|
|
}
|
|
|
|
<span class="cov0" title="0">vfsOpenFilesMtx.Lock()
|
|
defer vfsOpenFilesMtx.Unlock()
|
|
of := vfsOpenFiles[ptr.ID()]
|
|
|
|
locked, rc := of.CheckReservedLock()
|
|
if rc != _OK </span><span class="cov0" title="0">{
|
|
return uint32(IOERR_CHECKRESERVEDLOCK)
|
|
}</span>
|
|
|
|
<span class="cov0" title="0">var res uint32
|
|
if locked </span><span class="cov0" title="0">{
|
|
res = 1
|
|
}</span>
|
|
<span class="cov0" title="0">if pResOut == 0 </span><span class="cov0" title="0">{
|
|
panic(nilErr)</span>
|
|
}
|
|
<span class="cov0" title="0">if ok := mod.Memory().WriteUint32Le(pResOut, res); !ok </span><span class="cov0" title="0">{
|
|
panic(rangeErr)</span>
|
|
}
|
|
<span class="cov0" title="0">return _OK</span>
|
|
}
|
|
</pre>
|
|
|
|
<pre class="file" id="file8" style="display: none">package sqlite3
|
|
|
|
type vfsNoopLocker struct {
|
|
state vfsLockState
|
|
}
|
|
|
|
var _ vfsLocker = &vfsNoopLocker{}
|
|
|
|
func (l *vfsNoopLocker) LockState() vfsLockState <span class="cov0" title="0">{
|
|
return l.state
|
|
}</span>
|
|
|
|
func (l *vfsNoopLocker) LockShared() xErrorCode <span class="cov0" title="0">{
|
|
if assert && !(l.state == _NO_LOCK) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [wz9dcw]")</span>
|
|
}
|
|
<span class="cov0" title="0">l.state = _SHARED_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsNoopLocker) LockReserved() xErrorCode <span class="cov0" title="0">{
|
|
if assert && !(l.state == _SHARED_LOCK) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [m9hcil]")</span>
|
|
}
|
|
<span class="cov0" title="0">l.state = _RESERVED_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsNoopLocker) LockPending() xErrorCode <span class="cov0" title="0">{
|
|
if assert && !(l.state == _SHARED_LOCK || l.state == _RESERVED_LOCK) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [wx8nk2]")</span>
|
|
}
|
|
<span class="cov0" title="0">l.state = _PENDING_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsNoopLocker) LockExclusive() xErrorCode <span class="cov0" title="0">{
|
|
if assert && !(l.state == _PENDING_LOCK) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [84nbax]")</span>
|
|
}
|
|
<span class="cov0" title="0">l.state = _EXCLUSIVE_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsNoopLocker) DowngradeLock() xErrorCode <span class="cov0" title="0">{
|
|
if assert && !(l.state > _SHARED_LOCK) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [je31i3]")</span>
|
|
}
|
|
<span class="cov0" title="0">l.state = _SHARED_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsNoopLocker) Unlock() xErrorCode <span class="cov0" title="0">{
|
|
if assert && !(l.state > _NO_LOCK) </span><span class="cov0" title="0">{
|
|
panic(assertErr + " [m6e9w5]")</span>
|
|
}
|
|
<span class="cov0" title="0">l.state = _NO_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsNoopLocker) CheckReservedLock() (bool, xErrorCode) <span class="cov0" title="0">{
|
|
if l.state >= _RESERVED_LOCK </span><span class="cov0" title="0">{
|
|
return true, _OK
|
|
}</span>
|
|
<span class="cov0" title="0">return false, _OK</span>
|
|
}
|
|
</pre>
|
|
|
|
<pre class="file" id="file9" style="display: none">//go:build unix
|
|
|
|
package sqlite3
|
|
|
|
import (
|
|
"os"
|
|
"runtime"
|
|
"syscall"
|
|
)
|
|
|
|
func deleteOnClose(f *os.File) <span class="cov0" title="0">{
|
|
_ = os.Remove(f.Name())
|
|
}</span>
|
|
|
|
func (l *vfsFileLocker) LockState() vfsLockState <span class="cov8" title="1">{
|
|
return l.state
|
|
}</span>
|
|
|
|
func (l *vfsFileLocker) LockShared() xErrorCode <span class="cov8" title="1">{
|
|
// A PENDING lock is needed before acquiring a SHARED lock.
|
|
if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_RDLCK,
|
|
Start: _PENDING_BYTE,
|
|
Len: 1,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_LOCK
|
|
}</span>
|
|
|
|
// Acquire the SHARED lock.
|
|
<span class="cov8" title="1">if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_RDLCK,
|
|
Start: _SHARED_FIRST,
|
|
Len: _SHARED_SIZE,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_LOCK
|
|
}</span>
|
|
<span class="cov8" title="1">l.state = _SHARED_LOCK
|
|
|
|
// Relese the PENDING lock.
|
|
if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_UNLCK,
|
|
Start: _PENDING_BYTE,
|
|
Len: 1,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_UNLOCK
|
|
}</span>
|
|
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func (l *vfsFileLocker) LockReserved() xErrorCode <span class="cov8" title="1">{
|
|
// Acquire the RESERVED lock.
|
|
if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_WRLCK,
|
|
Start: _RESERVED_BYTE,
|
|
Len: 1,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_LOCK
|
|
}</span>
|
|
<span class="cov8" title="1">l.state = _RESERVED_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsFileLocker) LockPending() xErrorCode <span class="cov8" title="1">{
|
|
// Acquire the PENDING lock.
|
|
if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_WRLCK,
|
|
Start: _PENDING_BYTE,
|
|
Len: 1,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_LOCK
|
|
}</span>
|
|
<span class="cov8" title="1">l.state = _PENDING_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsFileLocker) LockExclusive() xErrorCode <span class="cov8" title="1">{
|
|
// Acquire the EXCLUSIVE lock.
|
|
if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_WRLCK,
|
|
Start: _SHARED_FIRST,
|
|
Len: _SHARED_SIZE,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_LOCK
|
|
}</span>
|
|
<span class="cov8" title="1">l.state = _EXCLUSIVE_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsFileLocker) DowngradeLock() xErrorCode <span class="cov8" title="1">{
|
|
// Downgrade to a SHARED lock.
|
|
if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_RDLCK,
|
|
Start: _SHARED_FIRST,
|
|
Len: _SHARED_SIZE,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_RDLOCK
|
|
}</span>
|
|
<span class="cov8" title="1">l.state = _SHARED_LOCK
|
|
|
|
// Release the PENDING and RESERVED locks.
|
|
if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_UNLCK,
|
|
Start: _PENDING_BYTE,
|
|
Len: 2,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_UNLOCK
|
|
}</span>
|
|
<span class="cov8" title="1">return _OK</span>
|
|
}
|
|
|
|
func (l *vfsFileLocker) Unlock() xErrorCode <span class="cov8" title="1">{
|
|
// Release all locks.
|
|
if !l.fcntlSetLock(&syscall.Flock_t{
|
|
Type: syscall.F_UNLCK,
|
|
}) </span><span class="cov0" title="0">{
|
|
return IOERR_UNLOCK
|
|
}</span>
|
|
<span class="cov8" title="1">l.state = _NO_LOCK
|
|
return _OK</span>
|
|
}
|
|
|
|
func (l *vfsFileLocker) CheckReservedLock() (bool, xErrorCode) <span class="cov0" title="0">{
|
|
if l.state >= _RESERVED_LOCK </span><span class="cov0" title="0">{
|
|
return true, _OK
|
|
}</span>
|
|
// Test all write locks.
|
|
<span class="cov0" title="0">lock := syscall.Flock_t{
|
|
Type: syscall.F_RDLCK,
|
|
}
|
|
if !l.fcntlGetLock(&lock) </span><span class="cov0" title="0">{
|
|
return false, IOERR_CHECKRESERVEDLOCK
|
|
}</span>
|
|
<span class="cov0" title="0">return lock.Type == syscall.F_UNLCK, _OK</span>
|
|
}
|
|
|
|
func (l *vfsFileLocker) fcntlGetLock(lock *syscall.Flock_t) bool <span class="cov0" title="0">{
|
|
F_GETLK := syscall.F_GETLK
|
|
if runtime.GOOS == "linux" </span><span class="cov0" title="0">{
|
|
F_GETLK = 36 // F_OFD_GETLK
|
|
}</span>
|
|
<span class="cov0" title="0">return syscall.FcntlFlock(l.Fd(), F_GETLK, lock) == nil</span>
|
|
}
|
|
|
|
func (l *vfsFileLocker) fcntlSetLock(lock *syscall.Flock_t) bool <span class="cov8" title="1">{
|
|
F_SETLK := syscall.F_SETLK
|
|
if runtime.GOOS == "linux" </span><span class="cov0" title="0">{
|
|
F_SETLK = 37 // F_OFD_SETLK
|
|
}</span>
|
|
<span class="cov8" title="1">return syscall.FcntlFlock(l.Fd(), F_SETLK, lock) == nil</span>
|
|
}
|
|
</pre>
|
|
|
|
</div>
|
|
</body>
|
|
<script>
|
|
(function() {
|
|
var files = document.getElementById('files');
|
|
var visible;
|
|
files.addEventListener('change', onChange, false);
|
|
function select(part) {
|
|
if (visible)
|
|
visible.style.display = 'none';
|
|
visible = document.getElementById(part);
|
|
if (!visible)
|
|
return;
|
|
files.value = part;
|
|
visible.style.display = 'block';
|
|
location.hash = part;
|
|
}
|
|
function onChange() {
|
|
select(files.value);
|
|
window.scrollTo(0, 0);
|
|
}
|
|
if (location.hash != "") {
|
|
select(location.hash.substr(1));
|
|
}
|
|
if (!visible) {
|
|
select("file0");
|
|
}
|
|
})();
|
|
</script>
|
|
</html>
|