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 * ====================================================================
26 #include "svn_error.h"
31 #include "svn_repos.h"
32 #include "private/svn_fs_private.h"
37 /* Escape ACTIVITY_ID to be safely usable as a filename. Simply
38 returns the MD5 checksum of the id.
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. */
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
),
57 /* Return the transaction name of the activity stored in file
58 PATHNAME, or NULL if PATHNAME cannot be read for any reason. */
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
);
65 svn_error_t
*err
= SVN_NO_ERROR
;
66 char *txn_name
= apr_palloc(pool
, SVN_FS__TXN_MAX_LEN
+1);
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
++)
76 svn_pool_clear(iterpool
);
78 err
= svn_io_file_open(&activity_file
, pathname
,
79 APR_READ
| APR_BUFFERED
,
80 APR_OS_DEFAULT
, iterpool
);
84 if (APR_TO_OS_ERROR(err
->apr_err
) == ESTALE
)
85 /* Retry on ESTALE... */
92 len
= SVN_FS__TXN_MAX_LEN
;
93 err
= svn_io_read_length_line(activity_file
, txn_name
, &len
, iterpool
);
97 if (APR_TO_OS_ERROR(err
->apr_err
) == ESTALE
)
103 err
= svn_io_file_close(activity_file
, iterpool
);
107 if (APR_TO_OS_ERROR(err
->apr_err
) == ESTALE
)
109 /* No retry, just completely ignore this ESTALE. */
110 svn_error_clear(err
);
116 /* We have a txn_name or had a non-ESTALE close failure; either
117 way, we're finished. */
120 svn_pool_destroy(iterpool
);
122 /* ### let's just assume that any error means the
123 ### activity/transaction doesn't exist */
126 svn_error_clear(err
);
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
);
142 dav_svn__delete_activity(const dav_svn_repos
*repos
, const char *activity_id
)
144 dav_error
*err
= NULL
;
145 const char *pathname
;
147 const char *txn_name
;
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. */
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
);
180 err
= dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
181 "could not open transaction.",
188 serr
= svn_fs_abort_txn(txn
, repos
->pool
);
191 err
= dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
192 "could not abort transaction.",
199 /* Finally, we remove the activity from the activities database. */
200 serr
= svn_io_remove_file(pathname
, repos
->pool
);
202 err
= dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
203 "unable to remove activity.",
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
;
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
);
222 return dav_svn__convert_err(err
, HTTP_INTERNAL_SERVER_ERROR
,
223 "could not initialize activity db.",
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
);
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.",
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
);
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.",
254 err
= svn_io_file_close(activity_file
, repos
->pool
);
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.",
263 err
= svn_io_file_rename(tmp_path
, final_path
, repos
->pool
);
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.",
277 dav_svn__create_activity(const dav_svn_repos
*repos
,
278 const char **ptxn_name
,
285 serr
= svn_fs_youngest_rev(&rev
, repos
->fs
, pool
);
288 return dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
289 "could not determine youngest revision",
293 serr
= svn_repos_fs_begin_txn_for_commit(&txn
, repos
->repos
, rev
,
294 repos
->username
, NULL
,
298 return dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
299 "could not begin a transaction",
303 serr
= svn_fs_txn_name(ptxn_name
, txn
, pool
);
306 return dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
307 "could not fetch transaction name",