📄 lock.c
字号:
/* * lock.c: routines for locking working copy subdirectories. * * ==================================================================== * 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 <assert.h>#include <apr_pools.h>#include <apr_time.h>#include "svn_pools.h"#include "svn_path.h"#include "svn_sorts.h"#include "wc.h"#include "adm_files.h"#include "lock.h"#include "questions.h"#include "props.h"#include "log.h"#include "entries.h"#include "svn_private_config.h"struct svn_wc_adm_access_t{ /* PATH to directory which contains the administrative area */ const char *path; enum svn_wc__adm_access_type { /* SVN_WC__ADM_ACCESS_UNLOCKED indicates no lock is held allowing read-only access */ svn_wc__adm_access_unlocked, /* SVN_WC__ADM_ACCESS_WRITE_LOCK indicates that a write lock is held allowing read-write access */ svn_wc__adm_access_write_lock, /* SVN_WC__ADM_ACCESS_CLOSED indicates that the baton has been closed. */ svn_wc__adm_access_closed } type; /* LOCK_EXISTS is set TRUE when the write lock exists */ svn_boolean_t lock_exists; /* SET_OWNER is TRUE if SET is allocated from this access baton */ svn_boolean_t set_owner; /* The working copy format version number for the directory */ int wc_format; /* SET is a hash of svn_wc_adm_access_t* keyed on char* representing the path to directories that are open. */ apr_hash_t *set; /* ENTRIES is the cached entries for PATH, without those in state deleted. ENTRIES_HIDDEN is the cached entries including those in state deleted or state absent. Either may be NULL. */ apr_hash_t *entries; apr_hash_t *entries_hidden; /* A hash mapping const char * entry names to hashes of wcprops. These hashes map const char * names to svn_string_t * values. NULL of the wcprops hasn't been read into memory. ### Since there are typically just one or two wcprops per entry, ### we could use a more compact way of storing them. */ apr_hash_t *wcprops; /* POOL is used to allocate cached items, they need to persist for the lifetime of this access baton */ apr_pool_t *pool;};/* This is a placeholder used in the set hash to represent missing directories. Only its address is important, it contains no useful data. */static svn_wc_adm_access_t missing;static svn_error_t *do_close(svn_wc_adm_access_t *adm_access, svn_boolean_t preserve_lock, svn_boolean_t recurse);/* Defining this conditional will result in a client that will refuse to upgrade working copies. This can be useful if you want to avoid problems caused by accidentally running a development version of SVN on a working copy that you typically use with an older version. */#ifndef SVN_DISABLE_WC_UPGRADE/* Write, to LOG_ACCUM, log entries to convert an old WC that did not have propcaching into a WC that uses propcaching. Do this conversion for the directory of ADM_ACCESS and its file children. Use POOL for temporary allocations. */static svn_error_t *introduce_propcaching(svn_stringbuf_t *log_accum, svn_wc_adm_access_t *adm_access, apr_pool_t *pool){ apr_hash_t *entries; apr_hash_index_t *hi; apr_pool_t *subpool = svn_pool_create(pool); SVN_ERR(svn_wc_entries_read(&entries, adm_access, FALSE, pool)); /* Reinstall the properties for each file and this dir; subdirs are handled when they're opened. */ for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { void *val; const svn_wc_entry_t *entry; svn_wc_entry_t tmpentry; apr_hash_t *base_props, *props; apr_hash_this(hi, NULL, NULL, &val); entry = val; if (entry->kind != svn_node_file && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) != 0) continue; svn_pool_clear(subpool); SVN_ERR(svn_wc__load_props(&base_props, &props, adm_access, entry->name, subpool)); SVN_ERR(svn_wc__install_props(&log_accum, adm_access, entry->name, base_props, props, TRUE, subpool)); /* Make sure we get rid of that prop-time field. It only wastes space in new WCs. */ tmpentry.prop_time = 0; SVN_ERR(svn_wc__loggy_entry_modify(&log_accum, adm_access, entry->name, &tmpentry, SVN_WC__ENTRY_MODIFY_PROP_TIME, pool)); } return SVN_NO_ERROR;}/* Write, to LOG_ACCUM, commands to convert a WC that has wcprops in individual files to use one wcprops file per directory. Do this for ADM_ACCESS and its file children, using POOL for temporary allocations. */static svn_error_t *convert_wcprops(svn_stringbuf_t *log_accum, svn_wc_adm_access_t *adm_access, apr_pool_t *pool){ apr_hash_t *entries; apr_hash_index_t *hi; apr_pool_t *subpool = svn_pool_create(pool); SVN_ERR(svn_wc_entries_read(&entries, adm_access, FALSE, pool)); /* Walk over the entries, adding a modify-wcprop command for each wcprop. Note that the modifications happen in memory and are just written once at the end of the log execution, so this isn't as inefficient as it might sound. */ for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { void *val; const svn_wc_entry_t *entry; apr_hash_t *wcprops; apr_hash_index_t *hj; apr_hash_this(hi, NULL, NULL, &val); entry = val; if (entry->kind != svn_node_file && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) != 0) continue; svn_pool_clear(subpool); SVN_ERR(svn_wc__wcprop_list(&wcprops, entry->name, adm_access, subpool)); /* Create a subsubpool for the inner loop... No, just kidding. There are typically just one or two wcprops per entry... */ for (hj = apr_hash_first(subpool, wcprops); hj; hj = apr_hash_next(hj)) { const void *key2; void *val2; const char *propname; svn_string_t *propval; apr_hash_this(hj, &key2, NULL, &val2); propname = key2; propval = val2; SVN_ERR(svn_wc__loggy_modify_wcprop(&log_accum, adm_access, entry->name, propname, propval->data, subpool)); } } return SVN_NO_ERROR;}/* Maybe upgrade the working copy directory represented by ADM_ACCESS to the latest 'SVN_WC__VERSION'. ADM_ACCESS must contain a write lock. Use POOL for all temporary allocation. Not all upgrade paths are necessarily supported. For example, upgrading a version 1 working copy results in an error. Sometimes the format file can contain "0" while the administrative directory is being constructed; calling this on a format 0 working copy has no effect and returns no error. */static svn_error_t *maybe_upgrade_format(svn_wc_adm_access_t *adm_access, apr_pool_t *pool){ SVN_ERR(svn_wc__check_format(adm_access->wc_format, adm_access->path, pool)); /* We can upgrade all formats that are accepted by svn_wc__check_format. */ if (adm_access->wc_format != SVN_WC__VERSION) { svn_boolean_t cleanup_required; svn_stringbuf_t *log_accum = svn_stringbuf_create("", pool); /* Don't try to mess with the WC if there are old log files left. */ SVN_ERR(svn_wc__adm_is_cleanup_required(&cleanup_required, adm_access, pool)); if (cleanup_required) return SVN_NO_ERROR; /* First, loggily upgrade the format file. */ SVN_ERR(svn_wc__loggy_upgrade_format(&log_accum, adm_access, SVN_WC__VERSION, pool)); /* Possibly convert an old WC that doesn't use propcaching. */ if (adm_access->wc_format <= SVN_WC__NO_PROPCACHING_VERSION) SVN_ERR(introduce_propcaching(log_accum, adm_access, pool)); /* If the WC uses one file per entry for wcprops, give back some inodes to the poor user. */ if (adm_access->wc_format <= SVN_WC__WCPROPS_MANY_FILES_VERSION) SVN_ERR(convert_wcprops(log_accum, adm_access, pool)); SVN_ERR(svn_wc__write_log(adm_access, 0, log_accum, pool)); if (adm_access->wc_format <= SVN_WC__WCPROPS_MANY_FILES_VERSION) { const char *access_path = svn_wc_adm_access_path(adm_access); /* Remove wcprops directory, dir-props, README.txt and empty-file files. We just silently ignore errors, because keeping these files is not catastrophic. */ svn_error_clear(svn_io_remove_dir (svn_wc__adm_path(access_path, FALSE, pool, SVN_WC__ADM_WCPROPS, NULL), pool)); svn_error_clear(svn_io_remove_file (svn_wc__adm_path(access_path, FALSE, pool, SVN_WC__ADM_DIR_WCPROPS, NULL), pool)); svn_error_clear(svn_io_remove_file (svn_wc__adm_path(access_path, FALSE, pool, SVN_WC__ADM_EMPTY_FILE, NULL), pool)); svn_error_clear(svn_io_remove_file (svn_wc__adm_path(access_path, FALSE, pool, SVN_WC__ADM_README, NULL), pool)); } SVN_ERR(svn_wc__run_log(adm_access, NULL, pool)); } return SVN_NO_ERROR;}#else/* Alternate version of the above for use when working copy upgrades are disabled. Return an error if the working copy described by ADM_ACCESS is not at the latest 'SVN_WC__VERSION'. Use POOL for all temporary allocation. */static svn_error_t *maybe_upgrade_format(svn_wc_adm_access_t *adm_access, apr_pool_t *pool){ SVN_ERR(svn_wc__check_format(adm_access->wc_format, adm_access->path, pool)); if (adm_access->wc_format != SVN_WC__VERSION) { return svn_error_createf(SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, "Would upgrade working copy '%s' from old " "format (%d) to current format (%d), " "but automatic upgrade has been disabled", svn_path_local_style(adm_access->path, pool), adm_access->wc_format, SVN_WC__VERSION); } return SVN_NO_ERROR;}#endif/* Create a physical lock file in the admin directory for ADM_ACCESS. Wait up to WAIT_FOR seconds if the lock already exists retrying every second. Note: most callers of this function determine the wc_format for the lock soon afterwards. We recommend calling maybe_upgrade_format() as soon as you have the wc_format for a lock, since that's a good opportunity to drag old working directories into the modern era. */static svn_error_t *create_lock(svn_wc_adm_access_t *adm_access, int wait_for, apr_pool_t *pool){ svn_error_t *err; for (;;) { err = svn_wc__make_adm_thing(adm_access, SVN_WC__ADM_LOCK, svn_node_file, APR_OS_DEFAULT, 0, pool); if (err) { if (APR_STATUS_IS_EEXIST(err->apr_err)) { svn_error_clear(err); if (wait_for <= 0) break; wait_for--; apr_sleep(apr_time_from_sec(1)); /* micro-seconds */ } else return err; } else return SVN_NO_ERROR; } return svn_error_createf(SVN_ERR_WC_LOCKED, NULL, _("Working copy '%s' locked"), svn_path_local_style(adm_access->path, pool));}/* Remove the physical lock in the admin directory for PATH. It is acceptable for the administrative area to have disappeared, such as when the directory is removed from the working copy. It is an error for the lock to have disappeared if the administrative area still exists. */static svn_error_t *remove_lock(const char *path, apr_pool_t *pool){ svn_error_t *err = svn_wc__remove_adm_file(path, pool, SVN_WC__ADM_LOCK, NULL); if (err) { if (svn_wc__adm_path_exists(path, FALSE, pool, NULL)) return err; svn_error_clear(err); } return SVN_NO_ERROR;}/* An APR pool cleanup handler. This handles access batons that have not been closed when their pool gets destroyed. The physical locks associated with such batons remain in the working copy if they are protecting a log file. */static apr_status_tpool_cleanup(void *p){ svn_wc_adm_access_t *lock = p; svn_boolean_t cleanup; svn_error_t *err; if (lock->type == svn_wc__adm_access_closed) return SVN_NO_ERROR; err = svn_wc__adm_is_cleanup_required(&cleanup, lock, lock->pool); if (!err)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -