Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / mod_dav_svn / activity.c
blob71a918d45efad0e53e49919e05c4130e7553fc8f
1 /*
2 * activity.c: DeltaV activity handling
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 #include <errno.h>
21 #include <apr_md5.h>
23 #include <httpd.h>
24 #include <mod_dav.h>
26 #include "svn_error.h"
27 #include "svn_io.h"
28 #include "svn_md5.h"
29 #include "svn_path.h"
30 #include "svn_fs.h"
31 #include "svn_repos.h"
32 #include "private/svn_fs_private.h"
34 #include "dav_svn.h"
37 /* Escape ACTIVITY_ID to be safely usable as a filename. Simply
38 returns the MD5 checksum of the id.
40 static const char *
41 escape_activity(const char *activity_id, apr_pool_t *pool)
43 unsigned char digest[APR_MD5_DIGESTSIZE];
44 apr_md5(digest, activity_id, strlen(activity_id));
45 return svn_md5_digest_to_cstring_display(digest, pool);
48 /* Return filename for ACTIVITY_ID under the repository in REPOS. */
49 static const char *
50 activity_pathname(const dav_svn_repos *repos, const char *activity_id)
52 return svn_path_join(repos->activities_db,
53 escape_activity(activity_id, repos->pool),
54 repos->pool);
57 /* Return the transaction name of the activity stored in file
58 PATHNAME, or NULL if PATHNAME cannot be read for any reason. */
59 static const char *
60 read_txn(const char *pathname, apr_pool_t *pool)
62 apr_file_t *activity_file;
63 apr_pool_t *iterpool = svn_pool_create(pool);
64 apr_size_t len;
65 svn_error_t *err = SVN_NO_ERROR;
66 char *txn_name = apr_palloc(pool, SVN_FS__TXN_MAX_LEN+1);
67 int i;
69 /* Try up to 10 times to read the txn name, retrying on ESTALE
70 (stale NFS file handle because of dav_svn__store_activity
71 renaming the activity file into place).
73 for (i = 0; i < 10; i++)
75 svn_error_clear(err);
76 svn_pool_clear(iterpool);
78 err = svn_io_file_open(&activity_file, pathname,
79 APR_READ | APR_BUFFERED,
80 APR_OS_DEFAULT, iterpool);
81 if (err)
83 #ifdef ESTALE
84 if (APR_TO_OS_ERROR(err->apr_err) == ESTALE)
85 /* Retry on ESTALE... */
86 continue;
87 #endif
88 /* ...else bail. */
89 break;
92 len = SVN_FS__TXN_MAX_LEN;
93 err = svn_io_read_length_line(activity_file, txn_name, &len, iterpool);
94 if (err)
96 #ifdef ESTALE
97 if (APR_TO_OS_ERROR(err->apr_err) == ESTALE)
98 continue;
99 #endif
100 break;
103 err = svn_io_file_close(activity_file, iterpool);
104 #ifdef ESTALE
105 if (err)
107 if (APR_TO_OS_ERROR(err->apr_err) == ESTALE)
109 /* No retry, just completely ignore this ESTALE. */
110 svn_error_clear(err);
111 err = SVN_NO_ERROR;
114 #endif
116 /* We have a txn_name or had a non-ESTALE close failure; either
117 way, we're finished. */
118 break;
120 svn_pool_destroy(iterpool);
122 /* ### let's just assume that any error means the
123 ### activity/transaction doesn't exist */
124 if (err)
126 svn_error_clear(err);
127 return NULL;
130 return txn_name;
134 const char *
135 dav_svn__get_txn(const dav_svn_repos *repos, const char *activity_id)
137 return read_txn(activity_pathname(repos, activity_id), repos->pool);
141 dav_error *
142 dav_svn__delete_activity(const dav_svn_repos *repos, const char *activity_id)
144 dav_error *err = NULL;
145 const char *pathname;
146 svn_fs_txn_t *txn;
147 const char *txn_name;
148 svn_error_t *serr;
150 /* gstein sez: If the activity ID is not in the database, return a
151 404. If the transaction is not present or is immutable, return a
152 204. For all other failures, return a 500. */
154 pathname = activity_pathname(repos, activity_id);
155 txn_name = read_txn(pathname, repos->pool);
156 if (txn_name == NULL)
158 return dav_new_error(repos->pool, HTTP_NOT_FOUND, 0,
159 "could not find activity.");
162 /* After this point, we have to cleanup the value and database. */
164 /* An empty txn_name indicates the transaction has been committed,
165 so don't try to clean it up. */
166 if (*txn_name)
168 /* Now, we attempt to delete TXN_NAME from the Subversion
169 repository. If we fail only because the transaction doesn't
170 exist, don't sweat it (but then, also don't try to remove it). */
171 if ((serr = svn_fs_open_txn(&txn, repos->fs, txn_name, repos->pool)))
173 if (serr->apr_err == SVN_ERR_FS_NO_SUCH_TRANSACTION)
175 svn_error_clear(serr);
176 serr = SVN_NO_ERROR;
178 else
180 err = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
181 "could not open transaction.",
182 repos->pool);
183 return err;
186 else
188 serr = svn_fs_abort_txn(txn, repos->pool);
189 if (serr)
191 err = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
192 "could not abort transaction.",
193 repos->pool);
194 return err;
199 /* Finally, we remove the activity from the activities database. */
200 serr = svn_io_remove_file(pathname, repos->pool);
201 if (serr)
202 err = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
203 "unable to remove activity.",
204 repos->pool);
206 return err;
210 dav_error *
211 dav_svn__store_activity(const dav_svn_repos *repos,
212 const char *activity_id,
213 const char *txn_name)
215 const char *final_path, *tmp_path, *activity_contents;
216 svn_error_t *err;
217 apr_file_t *activity_file;
219 /* Create activities directory if it does not yet exist. */
220 err = svn_io_make_dir_recursively(repos->activities_db, repos->pool);
221 if (err != NULL)
222 return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
223 "could not initialize activity db.",
224 repos->pool);
226 final_path = activity_pathname(repos, activity_id);
227 err = svn_io_open_unique_file2(&activity_file, &tmp_path, final_path,
228 ".tmp", svn_io_file_del_none, repos->pool);
229 if (err)
231 svn_error_t *serr = svn_error_quick_wrap(err, "Can't open activity db");
232 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
233 "could not open files.",
234 repos->pool);
237 activity_contents = apr_psprintf(repos->pool, "%s\n%s\n",
238 txn_name, activity_id);
239 err = svn_io_file_write_full(activity_file, activity_contents,
240 strlen(activity_contents), NULL, repos->pool);
241 if (err)
243 svn_error_t *serr = svn_error_quick_wrap(err,
244 "Can't write to activity db");
246 /* Try to remove the tmp file, but we already have an error... */
247 svn_error_clear(svn_io_file_close(activity_file, repos->pool));
248 svn_error_clear(svn_io_remove_file(tmp_path, repos->pool));
249 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
250 "could not write files.",
251 repos->pool);
254 err = svn_io_file_close(activity_file, repos->pool);
255 if (err)
257 svn_error_clear(svn_io_remove_file(tmp_path, repos->pool));
258 return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
259 "could not close files.",
260 repos->pool);
263 err = svn_io_file_rename(tmp_path, final_path, repos->pool);
264 if (err)
266 svn_error_clear(svn_io_remove_file(tmp_path, repos->pool));
267 return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
268 "could not replace files.",
269 repos->pool);
272 return NULL;
276 dav_error *
277 dav_svn__create_activity(const dav_svn_repos *repos,
278 const char **ptxn_name,
279 apr_pool_t *pool)
281 svn_revnum_t rev;
282 svn_fs_txn_t *txn;
283 svn_error_t *serr;
285 serr = svn_fs_youngest_rev(&rev, repos->fs, pool);
286 if (serr != NULL)
288 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
289 "could not determine youngest revision",
290 repos->pool);
293 serr = svn_repos_fs_begin_txn_for_commit(&txn, repos->repos, rev,
294 repos->username, NULL,
295 repos->pool);
296 if (serr != NULL)
298 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
299 "could not begin a transaction",
300 repos->pool);
303 serr = svn_fs_txn_name(ptxn_name, txn, pool);
304 if (serr != NULL)
306 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
307 "could not fetch transaction name",
308 repos->pool);
311 return NULL;