| 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' |
| |