| @node Server |
| @chapter Server |
| |
| The term ``server'' is ambiguous, because it has at least two different |
| meanings: it can refer to a powerful computer which offers services to |
| users on a network, or it can refer to a CPU process designed to receive |
| network requests. |
| |
| In Subversion, however, the @dfn{server} is just a set of libraries that |
| implements @dfn{repositories} and makes them available to other |
| programs. No networking is required. |
| |
| There are two main libraries: the @dfn{Subversion Filesystem} library, |
| and the @dfn{Subversion Server} library. |
| |
| @menu |
| * Filesystem:: The Subversion Filesystem. |
| * Server Library:: The Subversion Server interface. |
| @end menu |
| |
| |
| @c ---------------------------------------------------------------- |
| |
| @node Filesystem |
| @section Filesystem |
| |
| @menu |
| * Filesystem Overview:: |
| * API:: |
| * Repository Structure:: |
| * Locking:: |
| * Implementation:: |
| @end menu |
| |
| @node Filesystem Overview |
| @subsection Filesystem Overview |
| |
| @itemize @bullet |
| @item |
| @b{Requires:} |
| @itemize |
| @item |
| some writable disk space |
| @end itemize |
| @item |
| @b{Provides:} |
| @itemize |
| @item |
| a repository for storing files |
| @item |
| support for multiple concurrent accesses (via a lock manager) |
| @item |
| enforcement of user & group permissions |
| @end itemize |
| @end itemize |
| |
| This library implements a hierarchical filesystem which supports atomic |
| changes to directory trees, and records a complete history of the |
| changes. In addition to recording changes to file and directory |
| contents, the Subversion Filesystem records changes to file meta-data |
| (see discussion of @dfn{properties} in @ref{Model}). |
| |
| @node API |
| @subsection API |
| |
| This is almost the same API that the client calls, but now prefixed with |
| @code{fs_}. For a full explanation, see @xref{The repository access |
| library}. |
| |
| The only differences are the lack of the @code{status()} and |
| @code{update()} routines. Instead of receiving and returning entire |
| skeltas or deltas, the filesystem provides the ability to inquire about |
| only one node at a time via @code{fs_cmp()} and @code{fs_get_delta()}. |
| |
| @table @code |
| @item ver_t fs_latest (repos, user) |
| @item prop_t fs_get_ver_prop (repos, user, ver, propname) |
| @item proplist_t fs_get_ver_proplist (repos, user, ver) |
| @item proplist_t fs_get_ver_propnames (repos, user, ver) |
| @item node_t fs_read (repos, user, ver, path) |
| @item str_t fs_get_node_prop (repos, user, ver, path, propname) |
| @item str_t fs_get_dirent_prop (repos, user, ver, path, propname) |
| @item proplist_t fs_get_node_proplist (repos, ver, path) |
| @item proplist_t fs_get_dirent_proplist (repos, user, ver, path) |
| @item proplist_t fs_get_node_propnames (repos, user, ver, path) |
| @item proplist_t fs_get_dirent_propnames (repos, user, ver, path) |
| @item diff_t fs_get_diff (repos, user, ver1, path1, ver2, path2) |
| @item token_t fs_submit (repos, user, skelta) |
| @item ver_t fs_write (repos, user, delta, token) |
| @item bool_t fs_abandon (repos, user, token) |
| |
| @item bool_t fs_cmp (repos, user, ver1, path1, ver2, path2) |
| Compare two nodes in the repository, return whether they are identical. |
| @item delta_t fs_get_delta (repos, user, ver1, path1, ver2, path2) |
| Compare two nodes in the repository, return the difference between them. |
| |
| @end table |
| |
| Note that in addition to checking against concurrent commits, |
| @code{fs_submit()} also protects against committing changes from |
| out-of-date base versions, that is, the situation where the node in the |
| repository has changed since the base version mentioned in the skelta. |
| |
| @c ------------------------------------ |
| @node Repository Structure |
| @subsection Repository Structure |
| |
| @menu |
| * Schema:: |
| * Bubble-Up Method:: |
| * Diffy Storage:: |
| @end menu |
| |
| @node Schema |
| @subsubsection Schema |
| |
| To begin, please be sure that you're already casually familiar with |
| Subversion's ideas of files, directories, and version histories. If |
| not, @xref{Model}. We can now offer precise, technical descriptions of |
| the terms introduced there. |
| |
| @c This is taken from jimb's very first Subversion spec! |
| |
| @quotation |
| |
| A @dfn{text string} is a string of Unicode characters which is |
| canonically decomposed and ordered, according to the rules described in |
| the Unicode standard. |
| |
| A @dfn{string of bytes} is what you'd expect. |
| |
| A @dfn{property list} is an unordered list of properties. A |
| @dfn{property} is a pair @code{(@var{name}, @var{value})}, where |
| @var{name} is a text string, and @var{value} is a string of bytes. |
| No two properties in a property list have the same name. |
| |
| A @dfn{file} is a property list and a string of bytes. |
| |
| A @dfn{node} is either a file or a directory. (We define a directory |
| below.) Nodes are distinguished unions --- you can always tell whether |
| a node is a file or a directory. |
| |
| A @dfn{node table} is an array mapping some set of positive integers, |
| called @dfn{node numbers}, onto @dfn{nodes}. If a node table maps some |
| number @var{i} to some node @var{n}, then @var{i} is a @dfn{valid node |
| number} in that table, and @dfn{node @var{i}} is @var{n}. Otherwise, |
| @var{i} is an @dfn{invalid node number} in that table. |
| |
| A @dfn{directory entry} is a triple @code{(@var{name}, @var{props}, |
| @var{node})}, where @var{name} is a text string, @var{props} is a |
| property list, and @var{node} is a node number. |
| |
| A @dfn{directory} is an unordered list of directory entries, and a |
| property list. |
| |
| A @dfn{version} is a node number and a property list. |
| |
| A @dfn{history} is an array of versions, indexed by a contiguous range |
| of non-negative integers containing 0. |
| |
| A @dfn{repository} consists of node table and a history. |
| |
| @end quotation |
| |
| @c Some definitions: we say that a node @var{n} is a @dfn{direct child} |
| @c of a directory @var{d} iff @var{d} contains a directory entry whose |
| @c node number is @var{n}. A node @var{n} is a @dfn{child} of a |
| @c directory @var{d} iff @var{n} is a direct child of @var{d}, or if |
| @c there exists some directory @var{e} which is a direct child of |
| @c @var{d}, and @var{n} is a child of @var{e}. Given this definition of |
| @c ``direct child'' and ``child,'' the obvious definitions of ``direct |
| @c parent'' and ``parent'' hold. |
| |
| @c In these restrictions, let @var{r} be any repository. When we refer, |
| @c implicitly or explicitly, to a node table without further clarification, |
| @c we mean @var{r}'s node table. Thus, if we refer to ``a valid node |
| @c number'' without specifying the node table in which it is valid, we mean |
| @c ``a valid node number in @var{r}'s node table''. Similarly for |
| @c @var{r}'s history. |
| |
| Now that we've explained the form of the data, we make some restrictions |
| on that form. |
| |
| @b{Every version has a root directory.} Every version's node number is |
| a valid node number, and the node it refers to is always a directory. |
| We call this the version's @dfn{root directory}. |
| |
| @b{Version 0 always contains an empty root directory.} This baseline |
| makes it easy to check out whole projects from the repository. |
| |
| @b{Directories contain only valid links.} |
| Every directory entry's @var{node} is a valid node number. |
| |
| @b{Directory entries can be identified by name.} |
| For any directory @var{d}, every directory entry in @var{d} has a |
| distinct name. |
| |
| @b{There are no cycles of directories.} No node is its own child. |
| |
| @b{Directories can have more than one parent.} The Unix file system |
| does not allow more than one hard link to a directory, but Subversion |
| does allow the analogous situation. Thus, the directories in a |
| Subversion repository form a directed acyclic graph (@dfn{DAG}), not a |
| tree. However, it would be distracting and unhelpful to replace the |
| familiar term ``directory tree'' with the unfamiliar term ``directory |
| DAG'', so we still call it a ``directory tree'' here. |
| |
| @b{There are no dead nodes.} Every node is a child of some version's |
| root directory. |
| |
| @c </jimb> |
| |
| |
| @node Bubble-Up Method |
| @subsubsection Bubble-Up Method |
| |
| This section provides a conversational explanation of how the repository |
| actually stores and versions file trees. It's not critical knowledge |
| for a programmer using the Subversion Filesystem API, but most people |
| probably still want to know what's going on ``under the hood'' of the |
| repository. |
| |
| Suppose we have a new project, at version 1, looking like this (using |
| CVS syntax): |
| |
| @example |
| prompt$ svn checkout myproj |
| U myproj/ |
| U myproj/B |
| U myproj/A |
| U myproj/A/fish |
| U myproj/A/fish/tuna |
| prompt$ |
| @end example |
| |
| Only the file @file{tuna} is a regular file, everything else in myproj is |
| a directory. |
| |
| Let's see what this looks like as an abstract data structure in the |
| repository, and how that structure works in various operations (such |
| as update, commit, and branch). |
| |
| In the diagrams that follow, lines represent parent-to-child connections |
| in a directory hierarchy. Boxes are "nodes". A node is either a file |
| or a directory -- a letter in the upper left indicates which kind. A |
| file node has a byte-string for its content, whereas directory nodes |
| have a list of dir_entries, each pointing to another node. |
| |
| Parent-child links go both ways (i.e., a child knows who all its parents |
| are), but a node's name is stored only in its parent, because a node |
| with multiple parents may have different names in different parents. |
| |
| At the top of the repository is an array of version numbers, |
| stretching off to infinity. Since the project is at version 1, only |
| index 1 points to anything; it points to the root node of version 1 of |
| the project: |
| |
| @example |
| @group |
| ( myproj's version array ) |
| ______________________________________________________ |
| |___1_______2________3________4________5_________6_____... |
| | |
| | |
| ___|_____ |
| |D | |
| | | |
| | A | /* Two dir_entries, `A' and `B'. */ |
| | \ | |
| | B \ | |
| |__/___\__| |
| / \ |
| | \ |
| | \ |
| ___|___ ___\____ |
| |D | |D | |
| | | | | |
| | | | fish | /* One dir_entry, `fish'. */ |
| |_______| |___\____| |
| \ |
| \ |
| ___\____ |
| |D | |
| | | |
| | tuna | /* One dir_entry, `tuna'. */ |
| |___\____| |
| \ |
| \ |
| ___\____ |
| |F | |
| | | |
| | | /* (Contents of tuna not shown.) */ |
| |________| |
| |
| @end group |
| @end example |
| |
| What happens when we modify @file{tuna} and commit? First, we make a |
| new @file{tuna} node, containing the latest text. The new node is not |
| connected to anything yet, it's just hanging out there in space: |
| |
| @example |
| @group |
| ________ |
| |F | |
| | | |
| | | |
| |________| |
| @end group |
| @end example |
| |
| Next, we create a @emph{new} version of its parent directory: |
| |
| @example |
| @group |
| ________ |
| |D | |
| | | |
| | tuna | |
| |___\____| |
| \ |
| \ |
| ___\____ |
| |F | |
| | | |
| | | |
| |________| |
| @end group |
| @end example |
| |
| We continue up the line, creating a new version of the next parent |
| directory: |
| |
| @example |
| @group |
| ________ |
| |D | |
| | | |
| | fish | |
| |___\____| |
| \ |
| \ |
| ___\____ |
| |D | |
| | | |
| | tuna | |
| |___\____| |
| \ |
| \ |
| ___\____ |
| |F | |
| | | |
| | | |
| |________| |
| @end group |
| @end example |
| |
| Now it gets more tricky: we need to create a new version of the root |
| directory. This new root directory needs an entry to point to the |
| ``new'' directory A, but directory B hasn't changed at all. Therefore, |
| our new root directory also has an entry that still points to the |
| @emph{old} directory B node! |
| |
| @example |
| @group |
| ______________________________________________________ |
| |___1_______2________3________4________5_________6_____... |
| | |
| | |
| ___|_____ ________ |
| |D | |D | |
| | | | | |
| | A | | A | |
| | \ | | \ | |
| | B \ | | B \ | |
| |__/___\__| |__/___\_| |
| / \ / \ |
| | ___\_____________/ \ |
| | / \ \ |
| ___|__/ ___\____ ___\____ |
| |D | |D | |D | |
| | | | | | | |
| | | | fish | | fish | |
| |_______| |___\____| |___\____| |
| \ \ |
| \ \ |
| ___\____ ___\____ |
| |D | |D | |
| | | | | |
| | tuna | | tuna | |
| |___\____| |___\____| |
| \ \ |
| \ \ |
| ___\____ ___\____ |
| |F | |F | |
| | | | | |
| | | | | |
| |________| |________| |
| |
| @end group |
| @end example |
| |
| Finally, after all our new nodes are written, we finish the ``bubble |
| up'' process by linking this new tree to the next available version in |
| the history array. In this case, the new tree becomes version 2 in the |
| repository. |
| |
| @example |
| @group |
| ______________________________________________________ |
| |___1_______2________3________4________5_________6_____... |
| | \ |
| | \__________ |
| ___|_____ __\_____ |
| |D | |D | |
| | | | | |
| | A | | A | |
| | \ | | \ | |
| | B \ | | B \ | |
| |__/___\__| |__/___\_| |
| / \ / \ |
| | ___\_____________/ \ |
| | / \ \ |
| ___|__/ ___\____ ___\____ |
| |D | |D | |D | |
| | | | | | | |
| | | | fish | | fish | |
| |_______| |___\____| |___\____| |
| \ \ |
| \ \ |
| ___\____ ___\____ |
| |D | |D | |
| | | | | |
| | tuna | | tuna | |
| |___\____| |___\____| |
| \ \ |
| \ \ |
| ___\____ ___\____ |
| |F | |F | |
| | | | | |
| | | | | |
| |________| |________| |
| |
| @end group |
| @end example |
| |
| Generalizing on this example, you can now see that each ``version'' in |
| the repository history represents a root node of a unique tree (and an |
| atomic commit to the whole filesystem.) There are many trees in the |
| repository, and many of them share nodes. |
| |
| Many nice behaviors come from this model: |
| |
| @enumerate |
| @item |
| @b{Easy reads.} If a filesystem reader wants to locate version |
| @var{X} of file @file{foo.c}, it need only traverse the repository's |
| history, locate version @var{X}'s root node, then walk down the tree to |
| @file{foo.c}. |
| @item |
| @b{Writers don't interfere with readers.} Writers can continue to |
| create new nodes, bubbling their way up to the top, and concurrent |
| readers cannot see the work in progress. The new tree only becomes |
| visible to readers after the writer makes its final ``link'' to the |
| repository's history. |
| @item |
| @b{File structure is versioned.} Unlike CVS, the very structure of |
| each tree is being saved from version to version. File and directory |
| renames, additions, and deletions are part of the repository's history. |
| @end enumerate |
| |
| Let's demonstrate the last point by renaming the @file{tuna} to |
| @file{book}. |
| |
| We start by creating a new parent ``fish'' directory, except that this |
| parent directory has a different dir_entry, one which points the |
| @emph{same} old file node, but has a different name: |
| |
| @example |
| @group |
| ______________________________________________________ |
| |___1_______2________3________4________5_________6_____... |
| | \ |
| | \__________ |
| ___|_____ __\_____ |
| |D | |D | |
| | | | | |
| | A | | A | |
| | \ | | \ | |
| | B \ | | B \ | |
| |__/___\__| |__/___\_| |
| / \ / \ |
| | ___\_____________/ \ |
| | / \ \ |
| ___|__/ ___\____ ___\____ |
| |D | |D | |D | |
| | | | | | | |
| | | | fish | | fish | |
| |_______| |___\____| |___\____| |
| \ \ |
| \ \ |
| ___\____ ___\____ ________ |
| |D | |D | |D | |
| | | | | | | |
| | tuna | | tuna | | book | |
| |___\____| |___\____| |_/______| |
| \ \ / |
| \ \ / |
| ___\____ ___\____ / |
| |F | |F | |
| | | | | |
| | | | | |
| |________| |________| |
| @end group |
| @end example |
| |
| From here, we finish with the bubble-up process. We make new parent |
| directories up to the top, culminating in a new root directory with two |
| dir_entries (one points to the old ``B'' directory node we've had all |
| along, the other to the new version of ``A''), and finally link the new |
| tree to the history as version 3: |
| |
| @example |
| @group |
| ______________________________________________________ |
| |___1_______2________3________4________5_________6_____... |
| | \ \_________________ |
| | \__________ \ |
| ___|_____ __\_____ __\_____ |
| |D | |D | |D | |
| | | | | | | |
| | A | | A | | A | |
| | \ | | \ | | \ | |
| | B \ | | B \ | | B \ | |
| |__/___\__| |__/___\_| |__/___\_| |
| / ___________________/_____\_________/ \ |
| | / ___\_____________/ \ \ |
| | / / \ \ \ |
| ___|/_/ ___\____ ___\____ _____\__ |
| |D | |D | |D | |D | |
| | | | | | | | | |
| | | | fish | | fish | | fish | |
| |_______| |___\____| |___\____| |___\____| |
| \ \ \ |
| \ \ \ |
| ___\____ ___\____ ___\____ |
| |D | |D | |D | |
| | | | | | | |
| | tuna | | tuna | | book | |
| |___\____| |___\____| |_/______| |
| \ \ / |
| \ \ / |
| ___\____ ___\____ / |
| |F | |F | |
| | | | | |
| | | | | |
| |________| |________| |
| |
| @end group |
| @end example |
| |
| For our last example, we'll demonstrate the way ``tags'' and |
| ``branches'' are implemented in the repository. |
| |
| In a nutshell, they're one and the same thing. Because nodes are so |
| easily shared, we simply create a @emph{new} directory entry that points |
| to an existing directory node. It's an extremely cheap way of copying a |
| tree; we call this new entry a @dfn{clone}. |
| |
| Let's go back to our original tree, assuming that we're at version 6 to |
| begin with: |
| |
| @example |
| @group |
| ______________________________________________________ |
| ...___6_______7________8________9________10_________11_____... |
| | |
| | |
| ___|_____ |
| |D | |
| | | |
| | A | |
| | \ | |
| | B \ | |
| |__/___\__| |
| / \ |
| | \ |
| | \ |
| ___|___ ___\____ |
| |D | |D | |
| | | | | |
| | | | fish | |
| |_______| |___\____| |
| \ |
| \ |
| ___\____ |
| |D | |
| | | |
| | tuna | |
| |___\____| |
| \ |
| \ |
| ___\____ |
| |F | |
| | | |
| | | |
| |________| |
| |
| @end group |
| @end example |
| |
| Let's ``tag'' directory A. To make the clone, we create a new dir_entry |
| @b{T} in our root, pointing to A's node: |
| |
| @example |
| @group |
| ______________________________________________________ |
| |___6_______7________8________9________10_________11_____... |
| | \ |
| | \ |
| ___|_____ __\______ |
| |D | |D | |
| | | | | |
| | A | | A | |
| | \ | | | | |
| | B \ | | B | T | |
| |__/___\__| |_/__|__|_| |
| / \ / | | |
| | ___\__/ / / |
| | / \ / / |
| ___|__/ ___\__/_ / |
| |D | |D | |
| | | | | |
| | | | fish | |
| |_______| |___\____| |
| \ |
| \ |
| ___\____ |
| |D | |
| | | |
| | tuna | |
| |___\____| |
| \ |
| \ |
| ___\____ |
| |F | |
| | | |
| | | |
| |________| |
| |
| @end group |
| @end example |
| |
| Now we're all set. In the future, the contents of directories A and B |
| may change quite a lot. However, assuming we never make any changes to |
| directory T, it will @emph{always} point to a particular pristine version |
| of directory A. (We can use permissions to prevent anyone from writing |
| to directory T.) Thus, T is a tag. |
| |
| However, if we @emph{do} decide to allow commits in directory T, and now |
| our repository tree increments to version 8, then T becomes a branch. |
| Specifically, it's a branch of directory A which ``broke off'' from the |
| main line at version 8. |
| |
| @node Diffy Storage |
| @subsubsection Diffy Storage |
| |
| You may have been thinking, ``Gee, this bubble up method seems nice, but |
| it sure wastes a lot of space. Every commit to the repository creates |
| an entire line of new directory nodes!'' |
| |
| Like many other version control systems, Subversion stores changes as |
| differences. It doesn't make complete copies of nodes; instead, it |
| stores the @emph{latest} version as a full text, and previous versions |
| as a succession of reverse diffs (the word "diff" is used loosely here |
| -- for files, it means vdeltas, for directories, it means a format that |
| expresses changes to directories). |
| |
| @c ----------------- |
| @node Locking |
| @subsection Locking |
| |
| The Subversion Filesystem uses a locking mechanism to handle concurrency |
| issues. In the Subversion locking scheme, readers never wait for |
| writers, and writers only wait for other writers whose changes conflict |
| with theirs. |
| |
| @menu |
| * Reading:: |
| * The Lock Manager:: |
| * Write Locking:: |
| @end menu |
| |
| @node Reading |
| @subsubsection Reading |
| |
| Reading is easy; if a server process wants to execute a @code{read()} |
| call into the filesystem, it goes right through. The design of the |
| versioning filesystem allows a reader to simply skip down the version |
| history, locate the correct "root" node, and then follow the particular |
| tree downwards to the file or directory it wishes to read. Any number |
| of readers can follow this algorithm without interfering with one |
| another; and because writers don't link to the version history until |
| they're finished writing everything, there's no way for a reader to |
| accidentally stumble upon an unfinished tree. |
| |
| @node The Lock Manager |
| @subsubsection The Lock Manager |
| |
| Each Subversion filesystem has a process called a @dfn{lock manager}. |
| When a server process wishes to write to the filesystem, it must first |
| contact a lock manager, and @code{submit()} a skelta: |
| |
| |
| @example |
| @group |
| |
| S / |
| \ / |
| \ / |
| | / |
| v / |
| +--------------------|--------------+ |
| | v | |
| | Approved? | |
| | | | |
| | | | |
| | v | |
| | Concurrency Pool | |
| | +--------------------+ | |
| | | S S | | |
| | | S S S | | |
| | +--------------------+ | |
| +-----------------------------------+ |
| |
| @end group |
| @end example |
| |
| The lock manager contains a pool of "approved but uncompleted changes". |
| After receiving a new skelta from @code{submit()}, the lock manager |
| determines if it is safe to apply it at the same time as the other ones |
| in the pool.@footnote{This rule is actually quite simple: a set of |
| skeltas may be concurrently written @emph{if and only if} the order in |
| which they are applied does not matter!} |
| |
| If the lock manager decides that the requested skelta conflicts, it |
| returns a failure to the server (this usually means the client must do |
| an update). Else, the lock manager returns a transaction token to the |
| server. The server then uses this token to execute a @code{write()} on |
| the filesystem. |
| |
| After the @code{write()} is completed, the skelta is removed from the |
| lock manager's concurrency pool, and the server closes its connection. |
| |
| @node Write Locking |
| @subsubsection Write Locking |
| |
| Although it is safe for multiple commits to write their new leaf nodes |
| simultaneously, the next step -- bubbling up the directory hierarchy and |
| linking to the next version number -- must be done one commit at a time. |
| Otherwise, a tree representing a mixture of various commits might appear |
| in the repository. |
| |
| Therefore, as soon as a commit finishes writing new leaf nodes, it locks |
| the version array, creates the bubble-up hierarchy, links to the next |
| available version number, and only then releases the lock. Fortunately, |
| the first, non-locking stage (writing data to new leaf nodes) is usually |
| the more time-consuming part of a commit anyway. |
| |
| @c ----------------------- |
| @node Implementation |
| @subsection Implementation |
| |
| For the initial release of Subversion, |
| |
| @itemize @bullet |
| @item |
| The filesystem will be implemented as a library on Unix. |
| @item |
| The lock manager will communicate with other processes via Unix domain |
| sockets. |
| @item |
| The filesystem's data will probably be stored in a collection of .db |
| files, using the Berkeley Database library.@footnote{In the future, of |
| course, contributors are free modify the Subversion filesystem to |
| operate with more powerful SQL database.} (For more information, see |
| @uref{http://www.sleepycat.com, Sleepycat Software}.) |
| @end itemize |
| |
| |
| @c ---------------------------------------------------------------- |
| |
| @node Server Library |
| @section Server Library |
| |
| |
| @menu |
| * Server Library Overview:: |
| * Multiplexing:: |
| * Policy Enforcement:: |
| * Plug-Ins:: |
| * Security:: |
| @end menu |
| |
| @node Server Library Overview |
| @subsection Server Library Overview |
| |
| @itemize @bullet |
| @item |
| @b{Requires:} |
| @itemize |
| @item |
| the Subversion Filesystem interface |
| @end itemize |
| @item |
| @b{Provides:} |
| @itemize |
| @item |
| the ability to interact with any repository on a system |
| @item |
| the ability to enforce server-side policies |
| @item |
| the ability to load server-side plug-ins |
| @end itemize |
| @end itemize |
| |
| |
| @node Multiplexing |
| @subsection Multiplexing |
| |
| The Subversion Server Library (@dfn{svn_svr}), in its simplest sense, |
| acts a basic multiplexer for the repository API calls coming from the |
| Subversion client. (@xref{The repository access library}.) The |
| Subversion Server Library provides the exact same API as the client |
| repository library, prefixed now by @code{svr_}: |
| |
| @table @code |
| @item ver_t svr_latest (repos, user) |
| @item prop_t svr_get_ver_prop (repos, user, ver, propname) |
| @item proplist_t svr_get_ver_proplist (repos, user, ver) |
| @item proplist_t svr_get_ver_propnames (repos, user, ver) |
| @item node_t svr_read (repos, user, ver, path) |
| @item str_t svr_get_node_prop (repos, user, ver, path, propname) |
| @item str_t svr_get_dirent_prop (repos, user, ver, path, propname) |
| @item proplist_t svr_get_node_proplist (repos, ver, path) |
| @item proplist_t svr_get_dirent_proplist (repos, user, ver, path) |
| @item proplist_t svr_get_node_propnames (repos, user, ver, path) |
| @item proplist_t svr_get_dirent_propnames (repos, user, ver, path) |
| @item skelta_t svr_get_status (repos, user, skelta) |
| @item delta_t svr_get_update (repos, user, skelta) |
| @item delta_t svr_get_delta (repos, user, ver1, path1, ver2, path2) |
| @item diff_t svr_get_diff (repos, user, ver1, path1, ver2, path2) |
| @item token_t svr_submit (repos, user, skelta) |
| @item ver_t svr_write (repos, user, delta, token) |
| @item bool_t svr_abandon (repos, user, token) |
| @end table |
| |
| These routines are ``wrappers'' that execute filesystem calls against a |
| particular repository. |
| |
| Most of the wrappers map directly to filesystem calls, except for the |
| @code{svr_get_status()} and @code{svr_get_update()} routines. |
| @code{svr_get_status()} actually iterates over a skelta structure and |
| makes repeated calls to @code{fs_cmp()}, while @code{svr_get_update()} |
| makes repeated calls to @code{fs_get_delta()}, composing the returned |
| deltas into a single final delta. |
| |
| @node Policy Enforcement |
| @subsection Policy Enforcement |
| |
| When starting up, svn_svr will read a configuration file @file{svn.conf} |
| (much like Apache's @file{httpd.conf}) which, among other things, will |
| specify a list of server policies to implement. |
| |
| Because filesystem calls are ``wrappered'' by svn_svr, it has the |
| opportunity to intercept and interpret them according to policy. |
| |
| For example, an administrator may wish to take a repository ``off-line'' |
| for backup purposes. She can specify this in the @file{svn.conf} file, |
| and any filesystem calls to that repository are then intercepted and |
| (kindly) rejected by svn_svr. |
| |
| |
| @node Plug-Ins |
| @subsection Plug-Ins |
| |
| It's important that svn_svr be extensible; by allowing users to write |
| their own server-side libraries, the server's abilities can potentially |
| grow forever. |
| |
| We define the term @dfn{plug-in} to refer to a library designed to be |
| loaded into svn_svr's address space. This term is used intentionally |
| (instead of "module") so as not to be confused with either Apache |
| modules or ``CVS-like'' modules. Each plug-in potentially implements a |
| new set of server "methods". |
| |
| The @file{svn.conf} file specifies a list of available plug-ins, the new |
| methods they provide, and the real shared-object pathnames on disk. |
| When a client requests a particular method, svn_svr then knows which |
| plug-in to "auto-load". |
| |
| For ideas on future server-side plug-ins, @xref{Future}. |
| |
| |
| |
| |
| @node Security |
| @subsection Security |
| |
| Subversion has two levels of security, both abstracted. |
| |
| @menu |
| * Username Authentication:: |
| * Authorization:: |
| @end menu |
| |
| @node Username Authentication |
| @subsubsection Username Authentication |
| |
| First of all, the Subversion server does @emph{not} authenticate users |
| -- it never asks a client user to prove his/her identity. Instead, |
| authentication is soley the responsibility of the network layer. Every |
| Subversion user @emph{must} have an account in an external database that |
| is understood by some network layer. (@xref{Network Layer}.) |
| |
| For example, for a vanilla Apache network layer, each client user could |
| authenticate against a standard Apache access control file. More |
| complex network layers could authenticate against @file{/etc/passwd} or |
| a SQL database. |
| |
| Once the network layer has authenticated the user, it passes three |
| arguments to the Subversion server: |
| |
| @itemize @bullet |
| @item |
| @b{auth_username} -- the authenticated username |
| @item |
| @b{auth_method} -- the method in which this user was authenticated |
| @item |
| @b{auth_domain} -- the domain the user is coming from |
| @end itemize |
| |
| This information is then used to ``authorize'' a client's actions. |
| |
| |
| @node Authorization |
| @subsubsection Authorization |
| |
| By @dfn{authorization}, we mean answering the question: |
| |
| @quotation |
| |
| ``Is user X allowed to perform action Y on data Z?'' |
| |
| @end quotation |
| |
| The Subversion server provides a single interface for answering this |
| question. |
| |
| @table @code |
| @item str_t svn_authorize (auth_user, auth_method, auth_domain, action, path); |
| @end table |
| |
| @* |
| |
| This interface is implemented by a server-side plug-in library which is |
| free to define any sort of security scheme it wishes. The return value |
| of this function is either NULL (meaning that authorization was denied), |
| or @var{username}, where @var{username} is the specific username to use |
| when performing the action against the Subversion filesystem. |
| |
| The first release of Subversion will ship with a plug-in that manages |
| this information in a file called `svn_security'; each repository has |
| its own copy this file. It's worth mentioning how this file works. |
| |
| The `svn_security' file does three things: |
| |
| @enumerate |
| |
| @item |
| @b{Map authorized usernames to filesystem usernames.} For example, |
| suppose Jim Blandy is able to contact the Subversion server in many |
| different ways, using different network layers. It's important that the |
| @emph{same} username be used when talking to the filesystem. We call |
| this username the @dfn{Subversion username} or @dfn{canonical} username. |
| Here's an example of this mapping: |
| |
| @example |
| jimb : Jim Blandy : jim/unix/localhost, \ |
| jimb/smtp/jimb@@red-bean.com, \ |
| jimb/kerberos/redhat.com, \ |
| jblandy/DAV/red-bean.com |
| @end example |
| |
| @item |
| @b{Define Roles} A @dfn{role} is a set of performable actions. Here |
| are some examples of roles: |
| |
| @example |
| visitor : checkout update |
| hacker : checkout update commit |
| core-programmer : checkout update commit add rm mv |
| nitpicker : checkout update annotate |
| admin : checkout update commit add rm mv import |
| god : all |
| offlimits: none |
| @end example |
| |
| @item |
| @b{Map roles to users.} For each canonical username, specify specific |
| roles this person has for particular filesystem paths: |
| |
| @example |
| joe : visitor /trunk/gnome : nitpicker /trunk/foo |
| kfogel : admin /trunk/subversion : offlimits /trunk/subversion/foo.c |
| zeke : hacker /trunk/comp-tools : visitor /trunk/comp-tools/gdb |
| @end example |
| |
| @end enumerate |
| |
| |
| Remember, this is only one example of a back-end implementation of |
| @code{svn_authorize ()}. Programmers are free to write new server-side |
| plug-ins that implement different security schemas (for example, |
| authorizing against a SQL database.) |
| |
| |