📄 fs_fs.c
字号:
/* fs_fs.c --- filesystem operations specific to fs_fs * * ==================================================================== * Copyright (c) 2000-2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <stdlib.h>#include <stdio.h>#include <string.h>#include <ctype.h>#include <assert.h>#include <apr_general.h>#include <apr_pools.h>#include <apr_file_io.h>#include <apr_uuid.h>#include <apr_md5.h>#include <apr_thread_mutex.h>#include "svn_pools.h"#include "svn_fs.h"#include "svn_path.h"#include "svn_hash.h"#include "svn_md5.h"#include "svn_sorts.h"#include "svn_time.h"#include "fs.h"#include "err.h"#include "tree.h"#include "lock.h"#include "revs-txns.h"#include "key-gen.h"#include "fs_fs.h"#include "id.h"#include "../libsvn_fs/fs-loader.h"#include "svn_private_config.h"/* Following are defines that specify the textual elements of the native filesystem directories and revision files. *//* Names of special files in the fs_fs filesystem. */#define PATH_FORMAT "format" /* Contains format number */#define PATH_UUID "uuid" /* Contains UUID */#define PATH_CURRENT "current" /* Youngest revision */#define PATH_LOCK_FILE "write-lock" /* Revision lock file */#define PATH_REVS_DIR "revs" /* Directory of revisions */#define PATH_REVPROPS_DIR "revprops" /* Directory of revprops */#define PATH_TXNS_DIR "transactions" /* Directory of transactions */#define PATH_LOCKS_DIR "locks" /* Directory of locks *//* Names of special files and file extensions for transactions */#define PATH_CHANGES "changes" /* Records changes made so far */#define PATH_TXN_PROPS "props" /* Transaction properties */#define PATH_NEXT_IDS "next-ids" /* Next temporary ID assignments */#define PATH_REV "rev" /* Proto rev file */#define PATH_REV_LOCK "rev-lock" /* Proto rev (write) lock file */#define PATH_PREFIX_NODE "node." /* Prefix for node filename */#define PATH_EXT_TXN ".txn" /* Extension of txn dir */#define PATH_EXT_CHILDREN ".children" /* Extension for dir contents */#define PATH_EXT_PROPS ".props" /* Extension for node props *//* Headers used to describe node-revision in the revision file. */#define HEADER_ID "id"#define HEADER_TYPE "type"#define HEADER_COUNT "count"#define HEADER_PROPS "props"#define HEADER_TEXT "text"#define HEADER_CPATH "cpath"#define HEADER_PRED "pred"#define HEADER_COPYFROM "copyfrom"#define HEADER_COPYROOT "copyroot"/* Kinds that a change can be. */#define ACTION_MODIFY "modify"#define ACTION_ADD "add"#define ACTION_DELETE "delete"#define ACTION_REPLACE "replace"#define ACTION_RESET "reset"/* True and False flags. */#define FLAG_TRUE "true"#define FLAG_FALSE "false"/* Kinds that a node-rev can be. */#define KIND_FILE "file"#define KIND_DIR "dir"/* Kinds of representation. */#define REP_PLAIN "PLAIN"#define REP_DELTA "DELTA"/* Notes:To avoid opening and closing the rev-files all the time, it wouldprobably be advantageous to keep each rev-file open for thelifetime of the transaction object. I'll leave that as a lateroptimization for now.I didn't keep track of pool lifetimes at all in this code. Thereare likely some errors because of that. *//* The vtable associated with an open transaction object. */static txn_vtable_t txn_vtable = { svn_fs_fs__commit_txn, svn_fs_fs__abort_txn, svn_fs_fs__txn_prop, svn_fs_fs__txn_proplist, svn_fs_fs__change_txn_prop, svn_fs_fs__txn_root};/* Pathname helper functions */static const char *path_format(svn_fs_t *fs, apr_pool_t *pool){ return svn_path_join(fs->path, PATH_FORMAT, pool);}static const char *path_uuid(svn_fs_t *fs, apr_pool_t *pool){ return svn_path_join(fs->path, PATH_UUID, pool);}static const char *path_current(svn_fs_t *fs, apr_pool_t *pool){ return svn_path_join(fs->path, PATH_CURRENT, pool);}static const char *path_lock(svn_fs_t *fs, apr_pool_t *pool){ return svn_path_join(fs->path, PATH_LOCK_FILE, pool);}const char *svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool){ return svn_path_join_many(pool, fs->path, PATH_REVS_DIR, apr_psprintf(pool, "%ld", rev), NULL);}static const char *path_revprops(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool){ return svn_path_join_many(pool, fs->path, PATH_REVPROPS_DIR, apr_psprintf(pool, "%ld", rev), NULL);}static const char *path_txn_dir(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool){ return svn_path_join_many(pool, fs->path, PATH_TXNS_DIR, apr_pstrcat(pool, txn_id, PATH_EXT_TXN, NULL), NULL);}static const char *path_txn_changes(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool){ return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_CHANGES, pool);}static const char *path_txn_props(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool){ return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_TXN_PROPS, pool);}static const char *path_txn_next_ids(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool){ return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_NEXT_IDS, pool);}static const char *path_txn_proto_rev(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool){ return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_REV, pool);}static const char *path_txn_proto_rev_lock(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool){ return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_REV_LOCK, pool);}static const char *path_txn_node_rev(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool){ const char *txn_id = svn_fs_fs__id_txn_id(id); const char *node_id = svn_fs_fs__id_node_id(id); const char *copy_id = svn_fs_fs__id_copy_id(id); const char *name = apr_psprintf(pool, PATH_PREFIX_NODE "%s.%s", node_id, copy_id); return svn_path_join(path_txn_dir(fs, txn_id, pool), name, pool);}static const char *path_txn_node_props(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool){ return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool), PATH_EXT_PROPS, NULL);}static const char *path_txn_node_children(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool){ return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool), PATH_EXT_CHILDREN, NULL);}/* Functions for working with shared transaction data. *//* Return the transaction object for transaction TXN_ID from the transaction list of filesystem FS (which must already be locked via the txn_list_lock mutex). If the transaction does not exist in the list, then create a new transaction object and return it (if CREATE_NEW is true) or return NULL (otherwise). */static fs_fs_shared_txn_data_t *get_shared_txn(svn_fs_t *fs, const char *txn_id, svn_boolean_t create_new){ fs_fs_data_t *ffd = fs->fsap_data; fs_fs_shared_data_t *ffsd = ffd->shared; fs_fs_shared_txn_data_t *txn; for (txn = ffsd->txns; txn; txn = txn->next) if (strcmp(txn->txn_id, txn_id) == 0) break; if (txn || !create_new) return txn; /* Use the transaction object from the (single-object) freelist, if one is available, or otherwise create a new object. */ if (ffsd->free_txn) { txn = ffsd->free_txn; ffsd->free_txn = NULL; } else { apr_pool_t *subpool = svn_pool_create(ffsd->common_pool); txn = apr_palloc(subpool, sizeof(*txn)); txn->pool = subpool; } assert(strlen(txn_id) < sizeof(txn->txn_id)); strcpy(txn->txn_id, txn_id); txn->being_written = FALSE; /* Link this transaction into the head of the list. We will typically be dealing with only one active transaction at a time, so it makes sense for searches through the transaction list to look at the newest transactions first. */ txn->next = ffsd->txns; ffsd->txns = txn; return txn;}/* Free the transaction object for transaction TXN_ID, and remove it from the transaction list of filesystem FS (which must already be locked via the txn_list_lock mutex). Do nothing if the transaction does not exist. */static voidfree_shared_txn(svn_fs_t *fs, const char *txn_id){ fs_fs_data_t *ffd = fs->fsap_data; fs_fs_shared_data_t *ffsd = ffd->shared; fs_fs_shared_txn_data_t *txn, *prev = NULL; for (txn = ffsd->txns; txn; prev = txn, txn = txn->next) if (strcmp(txn->txn_id, txn_id) == 0) break; if (!txn) return; if (prev) prev->next = txn->next; else ffsd->txns = txn->next; /* As we typically will be dealing with one transaction after another, we will maintain a single-object free list so that we can hopefully keep reusing the same transaction object. */ if (!ffsd->free_txn) ffsd->free_txn = txn; else svn_pool_destroy(txn->pool);}/* Obtain a lock on the transaction list of filesystem FS, call BODY with FS, BATON, and POOL, and then unlock the transaction list. Return what BODY returned. */static svn_error_t *with_txnlist_lock(svn_fs_t *fs, svn_error_t *(*body)(svn_fs_t *fs, void *baton, apr_pool_t *pool), void *baton, apr_pool_t *pool){ svn_error_t *err;#if APR_HAS_THREADS fs_fs_data_t *ffd = fs->fsap_data; fs_fs_shared_data_t *ffsd = ffd->shared; apr_status_t apr_err; apr_err = apr_thread_mutex_lock(ffsd->txn_list_lock); if (apr_err) return svn_error_wrap_apr(apr_err, _("Can't grab FSFS txn list mutex"));#endif err = body(fs, baton, pool);#if APR_HAS_THREADS apr_err = apr_thread_mutex_unlock(ffsd->txn_list_lock); if (apr_err && !err) return svn_error_wrap_apr(apr_err, _("Can't ungrab FSFS txn list mutex"));#endif return err;}/* A structure used by unlock_proto_rev() and unlock_proto_rev_body(), which see. */struct unlock_proto_rev_baton{ const char *txn_id; void *lockcookie;};/* Callback used in the implementation of unlock_proto_rev(). */static svn_error_t *unlock_proto_rev_body(svn_fs_t *fs, void *baton, apr_pool_t *pool){ struct unlock_proto_rev_baton *b = baton; const char *txn_id = b->txn_id; apr_file_t *lockfile = b->lockcookie; fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, txn_id, FALSE); apr_status_t apr_err; if (!txn) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Can't unlock unknown transaction '%s'"), txn_id); if (!txn->being_written) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Can't unlock nonlocked transaction '%s'"), txn_id); apr_err = apr_file_unlock(lockfile); if (apr_err) return svn_error_wrap_apr (apr_err, _("Can't unlock prototype revision lockfile for transaction '%s'"), txn_id); apr_err = apr_file_close(lockfile); if (apr_err) return svn_error_wrap_apr (apr_err, _("Can't close prototype revision lockfile for transaction '%s'"), txn_id); txn->being_written = FALSE; return SVN_NO_ERROR;}/* Unlock the prototype revision file for transaction TXN_ID in filesystem FS using cookie LOCKCOOKIE. The original prototype revision file must have been closed _before_ calling this function. Perform temporary allocations in POOL. */static svn_error_t *unlock_proto_rev(svn_fs_t *fs, const char *txn_id, void *lockcookie, apr_pool_t *pool){ struct unlock_proto_rev_baton b; b.txn_id = txn_id; b.lockcookie = lockcookie; return with_txnlist_lock(fs, unlock_proto_rev_body, &b, pool);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -