Implementation for "Locking" feature This document describes proposed implementation details for a new locking system in Subversion. I. Introduction II. Client Implementation A. Overview B. The "svn:needs-lock" property 1. Property as enforcement system 2. Property as communication system C. Lock manipulation via client 1. Lock Tokens Stored as 'entryprops'. That is, just like last-changed-rev, last-author, etc., tokens come into the client with an svn:entry: prefix, are filtered by libsvn_wc, and stored in .svn/entries. We'll call the new entryprops "svn:entry:lock-token", "svn:entry:lock-owner", "svn:entry:lock-comment and "svn:entry:lock-creation-date". (In DAV parlance, what we call 'entryprops' are called 'live props'.) libsvn_wc stores the other fields of a lock (owner, comment, creation-date) in the entries file, so that they are available for the info command. Note that this information is only stored if the current WC has a lock on the file. 2. New client subcommands a. Creating a lock 'svn lock' calls new RA->lock() function, which marshals BASE rev of file to svn_fs_lock(). FS does a complimentary out-of-dateness check before creating the lock. The lock is marshaled back to client, stored in .svn/entries file. b. Using a lock 1. Using a lock to Commit A new RA layer get_commit_editor2() function will be created. It takes a hash of path -> lock token from the working copy. This will be used by the server to check that the WC locks match the current locks on the paths. A flag keep_locks will also be added, specifying whether the committables should be unlocked after a successful commit. libsvn_client will collect lock tokens during the harvesting of commit items. If --no-unlock was not specified, unmodified objects will be treated as commit items if the WC has a lock on them. A new status flag in svn_client_commit_info_t indicates that the object has a lock token. A new svn_wc_process_committed2 WC function will be created with a flag indicating whether the lock toke should be removed as part of the post-commit entry update. 2. Releasing a lock svn unlock uses the lock token stored in the WC and issues an ra->unlock command to the server. c. Breaking a lock svn unlock --force will first ask the server for a lock token and use it in the ra->unlock command to break the lock. d. Stealing a lock svn lock --force uses ra->lock with the force argument set to TRUE to steal a lock. e. Discovering/examining locks 1. seeing lock tokens in a working copy The client uses the lock information stored in the entries file to show lock information with svn info and svn status. 2. seeing locks in a repository The server will marshal the lock information as entryprops when calling the status editor. svn info URL will use RA->get_lock to get the lock for the path specified. 3. 'svn update' behavior A. At the start of an update, a new version of the 'reporter' vtable is used to describe not only mixed revnums to the server, but also existing locktokens. We need to be careful with protocols when marshaling this new information to older or newer servers. In ra_svn a new command will be added to the report command set for this purpose. B. If a locktoken is defunct (expired, broken, whatever), then the server sends a 'deletion' of the locktoken entryprop, through normal means: the prop deletion comes into the update_editor, and thus is removed from .svn/entries. III. Server Implementation A. Overview B. Tracking locks 1. Define a lock-token: UUID owner comment [optional] creation-date expiration-date [optional] 2. Define a lock-table that maps [fs-path --> lock-token] Beware the "deletion problem": if a certain path is locked, then no parent directory of that path can be deleted. The bad way to solve this problem is to do an O(N) recursive search of the directory being deleted, making sure no child is locked. The good way to solve this problem is to implement the 'lock table' as a tree. When an object is locked, we create the locked path in the lock-tree. Then, the mere existence of a directory in the lock-tree means it has at least one locked child, and cannot be deleted. This is a much more acceptable O(logN) search. C. How to implement locks in libsvn_fs This option implies that both BDB and FSFS would need to implement the 'lock tree' in their own way. Any user of libsvn_fs would automatically get lock-enforcement. 1. Define an API for associating a user with an open filesystem. Locks cannot be created/destroyed without a username, except that the filesystem allows breaking a lock without a username. 2. New fs functions for locks: svn_fs_lock() --> locks a file svn_fs_unlock() --> unlocks a file svn_fs_get_locks() --> returns list of locked paths svn_fs_get_lock() --> discover if a path is locked These functions don't do anything special, other than allow one to create/release/examine locks. BDB and FSFS need to implement these functions independently. 3. Wrap two of the functions in libsvn_repos, to invoke hooks. svn_repos_fs_lock() svn_repos_fs_unlock() As usually, encourage "good citizens" to use these wrappers, since they'll invoke the new hook scripts. The only thing which calls the fs functions directly (and circumvents hooks) would be a tool like svnadmin (see 'svnadmin unlock' in UI document.) 4. Teach a number of fs functions to check for locks, and deal with them: svn_fs_node_prop() svn_fs_apply_textdelta() svn_fs_apply_text() svn_fs_make_file() svn_fs_make_dir() Check to see if the incoming path is locked. If so, use the access descriptor to see if the caller has the lock token. 1. check that the lock-token correctly matches the lock. (i.e. that the caller isn't using some defunct or malformed token). 2. check that the lock owner matches whatever authenticated username is currently attached to the fs. svn_fs_copy() svn_fs_revision_link() svn_fs_delete() Same logic as above, except that because these operations can operate on entire trees, *multiple* lock-tokens might need to be checked in the access descriptor. svn_fs_commit_txn() Same logic, but this is the "final" check. This function already briefly locks the revisions-table in order to do a final out-of-date check on everything. In that same vein, it needs to briefly lock the locks-table, and verify every single lock. 5. auto-expiration of locks The common code which reads locks should be implemented in a special way: whenever a lock is read, lock expiration should be checked. If a lock has expired, then the lock should be removed, and the caller (doing the read operation) should get nothing back. As discussed on the list -- the svn_fs_lock() command should take an optional argument for the 'expiration-date' field. But this field should *never* be used by anything other than mod_dav_svn responding to generic DAV clients. We don't want to expose this feature to the svn client. D. Configurable Mechanisms 1. New "pre-" hook scripts a. pre-lock b. pre-unlock 2. New "post-" hook scripts a. post-lock b. post-unlock E. Lock manipulation with server tools 1. 'svnlook listlocks' 2. 'svnadmin unlock'