446 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			446 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2014 The Go Authors. All rights reserved.
							 | 
						||
| 
								 | 
							
								// Use of this source code is governed by a BSD-style
							 | 
						||
| 
								 | 
							
								// license that can be found in the LICENSE file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package webdav
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"container/heap"
							 | 
						||
| 
								 | 
							
									"errors"
							 | 
						||
| 
								 | 
							
									"strconv"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
									"time"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var (
							 | 
						||
| 
								 | 
							
									// ErrConfirmationFailed is returned by a LockSystem's Confirm method.
							 | 
						||
| 
								 | 
							
									ErrConfirmationFailed = errors.New("webdav: confirmation failed")
							 | 
						||
| 
								 | 
							
									// ErrForbidden is returned by a LockSystem's Unlock method.
							 | 
						||
| 
								 | 
							
									ErrForbidden = errors.New("webdav: forbidden")
							 | 
						||
| 
								 | 
							
									// ErrLocked is returned by a LockSystem's Create, Refresh and Unlock methods.
							 | 
						||
| 
								 | 
							
									ErrLocked = errors.New("webdav: locked")
							 | 
						||
| 
								 | 
							
									// ErrNoSuchLock is returned by a LockSystem's Refresh and Unlock methods.
							 | 
						||
| 
								 | 
							
									ErrNoSuchLock = errors.New("webdav: no such lock")
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Condition can match a WebDAV resource, based on a token or ETag.
							 | 
						||
| 
								 | 
							
								// Exactly one of Token and ETag should be non-empty.
							 | 
						||
| 
								 | 
							
								type Condition struct {
							 | 
						||
| 
								 | 
							
									Not   bool
							 | 
						||
| 
								 | 
							
									Token string
							 | 
						||
| 
								 | 
							
									ETag  string
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// LockSystem manages access to a collection of named resources. The elements
							 | 
						||
| 
								 | 
							
								// in a lock name are separated by slash ('/', U+002F) characters, regardless
							 | 
						||
| 
								 | 
							
								// of host operating system convention.
							 | 
						||
| 
								 | 
							
								type LockSystem interface {
							 | 
						||
| 
								 | 
							
									// Confirm confirms that the caller can claim all of the locks specified by
							 | 
						||
| 
								 | 
							
									// the given conditions, and that holding the union of all of those locks
							 | 
						||
| 
								 | 
							
									// gives exclusive access to all of the named resources. Up to two resources
							 | 
						||
| 
								 | 
							
									// can be named. Empty names are ignored.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// Exactly one of release and err will be non-nil. If release is non-nil,
							 | 
						||
| 
								 | 
							
									// all of the requested locks are held until release is called. Calling
							 | 
						||
| 
								 | 
							
									// release does not unlock the lock, in the WebDAV UNLOCK sense, but once
							 | 
						||
| 
								 | 
							
									// Confirm has confirmed that a lock claim is valid, that lock cannot be
							 | 
						||
| 
								 | 
							
									// Confirmed again until it has been released.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// If Confirm returns ErrConfirmationFailed then the Handler will continue
							 | 
						||
| 
								 | 
							
									// to try any other set of locks presented (a WebDAV HTTP request can
							 | 
						||
| 
								 | 
							
									// present more than one set of locks). If it returns any other non-nil
							 | 
						||
| 
								 | 
							
									// error, the Handler will write a "500 Internal Server Error" HTTP status.
							 | 
						||
| 
								 | 
							
									Confirm(now time.Time, name0, name1 string, conditions ...Condition) (release func(), err error)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Create creates a lock with the given depth, duration, owner and root
							 | 
						||
| 
								 | 
							
									// (name). The depth will either be negative (meaning infinite) or zero.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// If Create returns ErrLocked then the Handler will write a "423 Locked"
							 | 
						||
| 
								 | 
							
									// HTTP status. If it returns any other non-nil error, the Handler will
							 | 
						||
| 
								 | 
							
									// write a "500 Internal Server Error" HTTP status.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for
							 | 
						||
| 
								 | 
							
									// when to use each error.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// The token returned identifies the created lock. It should be an absolute
							 | 
						||
| 
								 | 
							
									// URI as defined by RFC 3986, Section 4.3. In particular, it should not
							 | 
						||
| 
								 | 
							
									// contain whitespace.
							 | 
						||
| 
								 | 
							
									Create(now time.Time, details LockDetails) (token string, err error)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Refresh refreshes the lock with the given token.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// If Refresh returns ErrLocked then the Handler will write a "423 Locked"
							 | 
						||
| 
								 | 
							
									// HTTP Status. If Refresh returns ErrNoSuchLock then the Handler will write
							 | 
						||
| 
								 | 
							
									// a "412 Precondition Failed" HTTP Status. If it returns any other non-nil
							 | 
						||
| 
								 | 
							
									// error, the Handler will write a "500 Internal Server Error" HTTP status.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for
							 | 
						||
| 
								 | 
							
									// when to use each error.
							 | 
						||
| 
								 | 
							
									Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Unlock unlocks the lock with the given token.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// If Unlock returns ErrForbidden then the Handler will write a "403
							 | 
						||
| 
								 | 
							
									// Forbidden" HTTP Status. If Unlock returns ErrLocked then the Handler
							 | 
						||
| 
								 | 
							
									// will write a "423 Locked" HTTP status. If Unlock returns ErrNoSuchLock
							 | 
						||
| 
								 | 
							
									// then the Handler will write a "409 Conflict" HTTP Status. If it returns
							 | 
						||
| 
								 | 
							
									// any other non-nil error, the Handler will write a "500 Internal Server
							 | 
						||
| 
								 | 
							
									// Error" HTTP status.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.11.1 for
							 | 
						||
| 
								 | 
							
									// when to use each error.
							 | 
						||
| 
								 | 
							
									Unlock(now time.Time, token string) error
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// LockDetails are a lock's metadata.
							 | 
						||
| 
								 | 
							
								type LockDetails struct {
							 | 
						||
| 
								 | 
							
									// Root is the root resource name being locked. For a zero-depth lock, the
							 | 
						||
| 
								 | 
							
									// root is the only resource being locked.
							 | 
						||
| 
								 | 
							
									Root string
							 | 
						||
| 
								 | 
							
									// Duration is the lock timeout. A negative duration means infinite.
							 | 
						||
| 
								 | 
							
									Duration time.Duration
							 | 
						||
| 
								 | 
							
									// OwnerXML is the verbatim <owner> XML given in a LOCK HTTP request.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// TODO: does the "verbatim" nature play well with XML namespaces?
							 | 
						||
| 
								 | 
							
									// Does the OwnerXML field need to have more structure? See
							 | 
						||
| 
								 | 
							
									// https://codereview.appspot.com/175140043/#msg2
							 | 
						||
| 
								 | 
							
									OwnerXML string
							 | 
						||
| 
								 | 
							
									// ZeroDepth is whether the lock has zero depth. If it does not have zero
							 | 
						||
| 
								 | 
							
									// depth, it has infinite depth.
							 | 
						||
| 
								 | 
							
									ZeroDepth bool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewMemLS returns a new in-memory LockSystem.
							 | 
						||
| 
								 | 
							
								func NewMemLS() LockSystem {
							 | 
						||
| 
								 | 
							
									return &memLS{
							 | 
						||
| 
								 | 
							
										byName:  make(map[string]*memLSNode),
							 | 
						||
| 
								 | 
							
										byToken: make(map[string]*memLSNode),
							 | 
						||
| 
								 | 
							
										gen:     uint64(time.Now().Unix()),
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type memLS struct {
							 | 
						||
| 
								 | 
							
									mu      sync.Mutex
							 | 
						||
| 
								 | 
							
									byName  map[string]*memLSNode
							 | 
						||
| 
								 | 
							
									byToken map[string]*memLSNode
							 | 
						||
| 
								 | 
							
									gen     uint64
							 | 
						||
| 
								 | 
							
									// byExpiry only contains those nodes whose LockDetails have a finite
							 | 
						||
| 
								 | 
							
									// Duration and are yet to expire.
							 | 
						||
| 
								 | 
							
									byExpiry byExpiry
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) nextToken() string {
							 | 
						||
| 
								 | 
							
									m.gen++
							 | 
						||
| 
								 | 
							
									return strconv.FormatUint(m.gen, 10)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) collectExpiredNodes(now time.Time) {
							 | 
						||
| 
								 | 
							
									for len(m.byExpiry) > 0 {
							 | 
						||
| 
								 | 
							
										if now.Before(m.byExpiry[0].expiry) {
							 | 
						||
| 
								 | 
							
											break
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										m.remove(m.byExpiry[0])
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) Confirm(now time.Time, name0, name1 string, conditions ...Condition) (func(), error) {
							 | 
						||
| 
								 | 
							
									m.mu.Lock()
							 | 
						||
| 
								 | 
							
									defer m.mu.Unlock()
							 | 
						||
| 
								 | 
							
									m.collectExpiredNodes(now)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var n0, n1 *memLSNode
							 | 
						||
| 
								 | 
							
									if name0 != "" {
							 | 
						||
| 
								 | 
							
										if n0 = m.lookup(slashClean(name0), conditions...); n0 == nil {
							 | 
						||
| 
								 | 
							
											return nil, ErrConfirmationFailed
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if name1 != "" {
							 | 
						||
| 
								 | 
							
										if n1 = m.lookup(slashClean(name1), conditions...); n1 == nil {
							 | 
						||
| 
								 | 
							
											return nil, ErrConfirmationFailed
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Don't hold the same node twice.
							 | 
						||
| 
								 | 
							
									if n1 == n0 {
							 | 
						||
| 
								 | 
							
										n1 = nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if n0 != nil {
							 | 
						||
| 
								 | 
							
										m.hold(n0)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if n1 != nil {
							 | 
						||
| 
								 | 
							
										m.hold(n1)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return func() {
							 | 
						||
| 
								 | 
							
										m.mu.Lock()
							 | 
						||
| 
								 | 
							
										defer m.mu.Unlock()
							 | 
						||
| 
								 | 
							
										if n1 != nil {
							 | 
						||
| 
								 | 
							
											m.unhold(n1)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if n0 != nil {
							 | 
						||
| 
								 | 
							
											m.unhold(n0)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// lookup returns the node n that locks the named resource, provided that n
							 | 
						||
| 
								 | 
							
								// matches at least one of the given conditions and that lock isn't held by
							 | 
						||
| 
								 | 
							
								// another party. Otherwise, it returns nil.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// n may be a parent of the named resource, if n is an infinite depth lock.
							 | 
						||
| 
								 | 
							
								func (m *memLS) lookup(name string, conditions ...Condition) (n *memLSNode) {
							 | 
						||
| 
								 | 
							
									// TODO: support Condition.Not and Condition.ETag.
							 | 
						||
| 
								 | 
							
									for _, c := range conditions {
							 | 
						||
| 
								 | 
							
										n = m.byToken[c.Token]
							 | 
						||
| 
								 | 
							
										if n == nil || n.held {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if name == n.details.Root {
							 | 
						||
| 
								 | 
							
											return n
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if n.details.ZeroDepth {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if n.details.Root == "/" || strings.HasPrefix(name, n.details.Root+"/") {
							 | 
						||
| 
								 | 
							
											return n
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) hold(n *memLSNode) {
							 | 
						||
| 
								 | 
							
									if n.held {
							 | 
						||
| 
								 | 
							
										panic("webdav: memLS inconsistent held state")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									n.held = true
							 | 
						||
| 
								 | 
							
									if n.details.Duration >= 0 && n.byExpiryIndex >= 0 {
							 | 
						||
| 
								 | 
							
										heap.Remove(&m.byExpiry, n.byExpiryIndex)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) unhold(n *memLSNode) {
							 | 
						||
| 
								 | 
							
									if !n.held {
							 | 
						||
| 
								 | 
							
										panic("webdav: memLS inconsistent held state")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									n.held = false
							 | 
						||
| 
								 | 
							
									if n.details.Duration >= 0 {
							 | 
						||
| 
								 | 
							
										heap.Push(&m.byExpiry, n)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) Create(now time.Time, details LockDetails) (string, error) {
							 | 
						||
| 
								 | 
							
									m.mu.Lock()
							 | 
						||
| 
								 | 
							
									defer m.mu.Unlock()
							 | 
						||
| 
								 | 
							
									m.collectExpiredNodes(now)
							 | 
						||
| 
								 | 
							
									details.Root = slashClean(details.Root)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if !m.canCreate(details.Root, details.ZeroDepth) {
							 | 
						||
| 
								 | 
							
										return "", ErrLocked
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									n := m.create(details.Root)
							 | 
						||
| 
								 | 
							
									n.token = m.nextToken()
							 | 
						||
| 
								 | 
							
									m.byToken[n.token] = n
							 | 
						||
| 
								 | 
							
									n.details = details
							 | 
						||
| 
								 | 
							
									if n.details.Duration >= 0 {
							 | 
						||
| 
								 | 
							
										n.expiry = now.Add(n.details.Duration)
							 | 
						||
| 
								 | 
							
										heap.Push(&m.byExpiry, n)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return n.token, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error) {
							 | 
						||
| 
								 | 
							
									m.mu.Lock()
							 | 
						||
| 
								 | 
							
									defer m.mu.Unlock()
							 | 
						||
| 
								 | 
							
									m.collectExpiredNodes(now)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									n := m.byToken[token]
							 | 
						||
| 
								 | 
							
									if n == nil {
							 | 
						||
| 
								 | 
							
										return LockDetails{}, ErrNoSuchLock
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if n.held {
							 | 
						||
| 
								 | 
							
										return LockDetails{}, ErrLocked
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if n.byExpiryIndex >= 0 {
							 | 
						||
| 
								 | 
							
										heap.Remove(&m.byExpiry, n.byExpiryIndex)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									n.details.Duration = duration
							 | 
						||
| 
								 | 
							
									if n.details.Duration >= 0 {
							 | 
						||
| 
								 | 
							
										n.expiry = now.Add(n.details.Duration)
							 | 
						||
| 
								 | 
							
										heap.Push(&m.byExpiry, n)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return n.details, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) Unlock(now time.Time, token string) error {
							 | 
						||
| 
								 | 
							
									m.mu.Lock()
							 | 
						||
| 
								 | 
							
									defer m.mu.Unlock()
							 | 
						||
| 
								 | 
							
									m.collectExpiredNodes(now)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									n := m.byToken[token]
							 | 
						||
| 
								 | 
							
									if n == nil {
							 | 
						||
| 
								 | 
							
										return ErrNoSuchLock
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if n.held {
							 | 
						||
| 
								 | 
							
										return ErrLocked
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									m.remove(n)
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) canCreate(name string, zeroDepth bool) bool {
							 | 
						||
| 
								 | 
							
									return walkToRoot(name, func(name0 string, first bool) bool {
							 | 
						||
| 
								 | 
							
										n := m.byName[name0]
							 | 
						||
| 
								 | 
							
										if n == nil {
							 | 
						||
| 
								 | 
							
											return true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if first {
							 | 
						||
| 
								 | 
							
											if n.token != "" {
							 | 
						||
| 
								 | 
							
												// The target node is already locked.
							 | 
						||
| 
								 | 
							
												return false
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if !zeroDepth {
							 | 
						||
| 
								 | 
							
												// The requested lock depth is infinite, and the fact that n exists
							 | 
						||
| 
								 | 
							
												// (n != nil) means that a descendent of the target node is locked.
							 | 
						||
| 
								 | 
							
												return false
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else if n.token != "" && !n.details.ZeroDepth {
							 | 
						||
| 
								 | 
							
											// An ancestor of the target node is locked with infinite depth.
							 | 
						||
| 
								 | 
							
											return false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return true
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) create(name string) (ret *memLSNode) {
							 | 
						||
| 
								 | 
							
									walkToRoot(name, func(name0 string, first bool) bool {
							 | 
						||
| 
								 | 
							
										n := m.byName[name0]
							 | 
						||
| 
								 | 
							
										if n == nil {
							 | 
						||
| 
								 | 
							
											n = &memLSNode{
							 | 
						||
| 
								 | 
							
												details: LockDetails{
							 | 
						||
| 
								 | 
							
													Root: name0,
							 | 
						||
| 
								 | 
							
												},
							 | 
						||
| 
								 | 
							
												byExpiryIndex: -1,
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											m.byName[name0] = n
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										n.refCount++
							 | 
						||
| 
								 | 
							
										if first {
							 | 
						||
| 
								 | 
							
											ret = n
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return true
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									return ret
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *memLS) remove(n *memLSNode) {
							 | 
						||
| 
								 | 
							
									delete(m.byToken, n.token)
							 | 
						||
| 
								 | 
							
									n.token = ""
							 | 
						||
| 
								 | 
							
									walkToRoot(n.details.Root, func(name0 string, first bool) bool {
							 | 
						||
| 
								 | 
							
										x := m.byName[name0]
							 | 
						||
| 
								 | 
							
										x.refCount--
							 | 
						||
| 
								 | 
							
										if x.refCount == 0 {
							 | 
						||
| 
								 | 
							
											delete(m.byName, name0)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return true
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									if n.byExpiryIndex >= 0 {
							 | 
						||
| 
								 | 
							
										heap.Remove(&m.byExpiry, n.byExpiryIndex)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func walkToRoot(name string, f func(name0 string, first bool) bool) bool {
							 | 
						||
| 
								 | 
							
									for first := true; ; first = false {
							 | 
						||
| 
								 | 
							
										if !f(name, first) {
							 | 
						||
| 
								 | 
							
											return false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if name == "/" {
							 | 
						||
| 
								 | 
							
											break
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										name = name[:strings.LastIndex(name, "/")]
							 | 
						||
| 
								 | 
							
										if name == "" {
							 | 
						||
| 
								 | 
							
											name = "/"
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type memLSNode struct {
							 | 
						||
| 
								 | 
							
									// details are the lock metadata. Even if this node's name is not explicitly locked,
							 | 
						||
| 
								 | 
							
									// details.Root will still equal the node's name.
							 | 
						||
| 
								 | 
							
									details LockDetails
							 | 
						||
| 
								 | 
							
									// token is the unique identifier for this node's lock. An empty token means that
							 | 
						||
| 
								 | 
							
									// this node is not explicitly locked.
							 | 
						||
| 
								 | 
							
									token string
							 | 
						||
| 
								 | 
							
									// refCount is the number of self-or-descendent nodes that are explicitly locked.
							 | 
						||
| 
								 | 
							
									refCount int
							 | 
						||
| 
								 | 
							
									// expiry is when this node's lock expires.
							 | 
						||
| 
								 | 
							
									expiry time.Time
							 | 
						||
| 
								 | 
							
									// byExpiryIndex is the index of this node in memLS.byExpiry. It is -1
							 | 
						||
| 
								 | 
							
									// if this node does not expire, or has expired.
							 | 
						||
| 
								 | 
							
									byExpiryIndex int
							 | 
						||
| 
								 | 
							
									// held is whether this node's lock is actively held by a Confirm call.
							 | 
						||
| 
								 | 
							
									held bool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type byExpiry []*memLSNode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (b *byExpiry) Len() int {
							 | 
						||
| 
								 | 
							
									return len(*b)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (b *byExpiry) Less(i, j int) bool {
							 | 
						||
| 
								 | 
							
									return (*b)[i].expiry.Before((*b)[j].expiry)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (b *byExpiry) Swap(i, j int) {
							 | 
						||
| 
								 | 
							
									(*b)[i], (*b)[j] = (*b)[j], (*b)[i]
							 | 
						||
| 
								 | 
							
									(*b)[i].byExpiryIndex = i
							 | 
						||
| 
								 | 
							
									(*b)[j].byExpiryIndex = j
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (b *byExpiry) Push(x interface{}) {
							 | 
						||
| 
								 | 
							
									n := x.(*memLSNode)
							 | 
						||
| 
								 | 
							
									n.byExpiryIndex = len(*b)
							 | 
						||
| 
								 | 
							
									*b = append(*b, n)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (b *byExpiry) Pop() interface{} {
							 | 
						||
| 
								 | 
							
									i := len(*b) - 1
							 | 
						||
| 
								 | 
							
									n := (*b)[i]
							 | 
						||
| 
								 | 
							
									(*b)[i] = nil
							 | 
						||
| 
								 | 
							
									n.byExpiryIndex = -1
							 | 
						||
| 
								 | 
							
									*b = (*b)[:i]
							 | 
						||
| 
								 | 
							
									return n
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const infiniteTimeout = -1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// parseTimeout parses the Timeout HTTP header, as per section 10.7. If s is
							 | 
						||
| 
								 | 
							
								// empty, an infiniteTimeout is returned.
							 | 
						||
| 
								 | 
							
								func parseTimeout(s string) (time.Duration, error) {
							 | 
						||
| 
								 | 
							
									if s == "" {
							 | 
						||
| 
								 | 
							
										return infiniteTimeout, nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if i := strings.IndexByte(s, ','); i >= 0 {
							 | 
						||
| 
								 | 
							
										s = s[:i]
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									s = strings.TrimSpace(s)
							 | 
						||
| 
								 | 
							
									if s == "Infinite" {
							 | 
						||
| 
								 | 
							
										return infiniteTimeout, nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									const pre = "Second-"
							 | 
						||
| 
								 | 
							
									if !strings.HasPrefix(s, pre) {
							 | 
						||
| 
								 | 
							
										return 0, errInvalidTimeout
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									s = s[len(pre):]
							 | 
						||
| 
								 | 
							
									if s == "" || s[0] < '0' || '9' < s[0] {
							 | 
						||
| 
								 | 
							
										return 0, errInvalidTimeout
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									n, err := strconv.ParseInt(s, 10, 64)
							 | 
						||
| 
								 | 
							
									if err != nil || 1<<32-1 < n {
							 | 
						||
| 
								 | 
							
										return 0, errInvalidTimeout
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return time.Duration(n) * time.Second, nil
							 | 
						||
| 
								 | 
							
								}
							 |