Documentation
¶
Overview ¶
Package davlock is the WebDAV lock system, and it exists because the one filex shipped with was a lie.
What was wrong ¶
/dav used webdav.NewMemLS(). It implements RFC 4918 class-2 locking correctly — the semantics below are its algorithm — but it holds every lock in a Go map, so the locks last exactly as long as the process. Restart filex, deploy a new version, let the container be rescheduled, and EVERY lock silently ceases to exist.
That matters because of what a client does with a lock. Windows Explorer and macOS Finder take one before they write, hold it across the whole edit, and present the token on the PUT. After a restart the token names nothing, the PUT gets 412 Precondition Failed, and the person editing the file sees their save fail with no explanation — while the server, having forgotten, would happily let a SECOND client lock and write the same file. The lock said "exclusive" and stopped being true without telling anybody.
What this does instead ¶
The same algorithm, with the lock table written to a file in the data directory on every change and read back at boot — TOKENS INCLUDED, which is the whole point: a client's token has to keep meaning the same lock across a restart or the durability buys nothing.
⚠ What it does NOT do, stated plainly rather than implied: it is not a distributed lock. Two filex processes serving the same storage each keep their own file and would each grant a lock the other does not know about. One process per deployment is filex's shape today; if that ever changes, this is the thing that has to move into the database, and the honest place to say so is here rather than in an incident.
⚠ WebDAV locks are advisory in the first place (RFC 4918 §6.3): they coordinate polite clients, they do not stop the S3 endpoint or the web explorer from writing the same file. That is the protocol's design, not a gap here.
Index ¶
- type Lock
- type System
- func (s *System) Confirm(now time.Time, name0, name1 string, conditions ...webdav.Condition) (func(), error)
- func (s *System) Create(now time.Time, details webdav.LockDetails) (string, error)
- func (s *System) Len() int
- func (s *System) Refresh(now time.Time, token string, duration time.Duration) (webdav.LockDetails, error)
- func (s *System) Unlock(now time.Time, token string) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Lock ¶
type Lock struct {
Token string `json:"token"`
Root string `json:"root"`
Duration int64 `json:"duration_ns"`
OwnerXML string `json:"owner_xml"`
ZeroDepth bool `json:"zero_depth"`
Expiry time.Time `json:"expiry"`
// contains filtered or unexported fields
}
Lock is one held lock. It is also the on-disk record, so the JSON tags are part of the file format.
type System ¶
type System struct {
// contains filtered or unexported fields
}
System is a durable webdav.LockSystem.
func New ¶
New opens (or creates) a lock system backed by `<dir>/dav-locks.json`.
A missing or unreadable file is not an error: the locks are then simply empty, which is exactly what memLS gave and no worse. Refusing to start /dav because a cache file is corrupt would trade a small problem for an outage.
func NewMemory ¶
func NewMemory() *System
NewMemory returns a lock system that never touches the disk. For tests, and for a deployment that would rather have memLS's behaviour back.
func (*System) Confirm ¶
func (s *System) Confirm(now time.Time, name0, name1 string, conditions ...webdav.Condition) (func(), error)
Confirm claims the locks that cover name0/name1 for one request.