📄 fs_fs.c
字号:
/* Same as unlock_proto_rev(), but requires that the transaction list lock is already held. */static svn_error_t *unlock_proto_rev_list_locked(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 unlock_proto_rev_body(fs, &b, pool);}/* A structure used by get_writable_proto_rev() and get_writable_proto_rev_body(), which see. */struct get_writable_proto_rev_baton{ apr_file_t **file; void **lockcookie; const char *txn_id;};/* Callback used in the implementation of get_writable_proto_rev(). */static svn_error_t *get_writable_proto_rev_body(svn_fs_t *fs, void *baton, apr_pool_t *pool){ struct get_writable_proto_rev_baton *b = baton; apr_file_t **file = b->file; void **lockcookie = b->lockcookie; const char *txn_id = b->txn_id; svn_error_t *err; fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, txn_id, TRUE); /* First, ensure that no thread in this process (including this one) is currently writing to this transaction's proto-rev file. */ if (txn->being_written) return svn_error_createf(SVN_ERR_FS_TRANSACTION_NOT_MUTABLE, NULL, _("Cannot write to the prototype revision file " "of transaction '%s' because a previous " "representation is currently being written by " "this process"), txn_id); /* We know that no thread in this process is writing to the proto-rev file, and by extension, that no thread in this process is holding a lock on the prototype revision lock file. It is therefore safe for us to attempt to lock this file, to see if any other process is holding a lock. */ { apr_file_t *lockfile; apr_status_t apr_err; const char *lockfile_path = path_txn_proto_rev_lock(fs, txn_id, pool); /* Open the proto-rev lockfile, creating it if necessary, as it may not exist if the transaction dates from before the lockfiles were introduced. ### We'd also like to use something like svn_io_file_lock2(), but that forces us to create a subpool just to be able to unlock the file, which seems a waste. */ SVN_ERR(svn_io_file_open(&lockfile, lockfile_path, APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pool)); apr_err = apr_file_lock(lockfile, APR_FLOCK_EXCLUSIVE | APR_FLOCK_NONBLOCK); if (apr_err) { svn_error_clear(svn_io_file_close(lockfile, pool)); if (APR_STATUS_IS_EAGAIN(apr_err)) return svn_error_createf(SVN_ERR_FS_TRANSACTION_NOT_MUTABLE, NULL, _("Cannot write to the prototype revision " "file of transaction '%s' because a " "previous representation is currently " "being written by another process"), txn_id); return svn_error_wrap_apr(apr_err, _("Can't get exclusive lock on file '%s'"), svn_path_local_style(lockfile_path, pool)); } *lockcookie = lockfile; } /* We've successfully locked the transaction; mark it as such. */ txn->being_written = TRUE; /* Now open the prototype revision file and seek to the end. */ err = svn_io_file_open(file, path_txn_proto_rev(fs, txn_id, pool), APR_WRITE | APR_BUFFERED, APR_OS_DEFAULT, pool); /* You might expect that we could dispense with the following seek and achieve the same thing by opening the file using APR_APPEND. Unfortunately, APR's buffered file implementation unconditionally places its initial file pointer at the start of the file (even for files opened with APR_APPEND), so we need this seek to reconcile the APR file pointer to the OS file pointer (since we need to be able to read the current file position later). */ if (!err) { apr_off_t offset = 0; err = svn_io_file_seek(*file, APR_END, &offset, 0); } if (err) { svn_error_clear(unlock_proto_rev_list_locked(fs, txn_id, *lockcookie, pool)); *lockcookie = NULL; } return err;}/* Get a handle to the prototype revision file for transaction TXN_ID in filesystem FS, and lock it for writing. Return FILE, a file handle positioned at the end of the file, and LOCKCOOKIE, a cookie that should be passed to unlock_proto_rev() to unlock the file once FILE has been closed. If the prototype revision file is already locked, return error SVN_ERR_FS_TRANSACTION_NOT_MUTABLE. Perform all allocations in POOL. */static svn_error_t *get_writable_proto_rev(apr_file_t **file, void **lockcookie, svn_fs_t *fs, const char *txn_id, apr_pool_t *pool){ struct get_writable_proto_rev_baton b; b.file = file; b.lockcookie = lockcookie; b.txn_id = txn_id; return with_txnlist_lock(fs, get_writable_proto_rev_body, &b, pool);}/* Callback used in the implementation of purge_shared_txn(). */static svn_error_t *purge_shared_txn_body(svn_fs_t *fs, void *baton, apr_pool_t *pool){ const char *txn_id = *(const char **)baton; free_shared_txn(fs, txn_id); return SVN_NO_ERROR;}/* Purge the shared data for transaction TXN_ID in filesystem FS. Perform all allocations in POOL. */static svn_error_t *purge_shared_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool){ return with_txnlist_lock(fs, purge_shared_txn_body, &txn_id, pool);}/* Fetch the current offset of FILE into *OFFSET_P. */static svn_error_t *get_file_offset(apr_off_t *offset_p, apr_file_t *file, apr_pool_t *pool){ apr_off_t offset; /* Note that, for buffered files, one (possibly surprising) side-effect of this call is to flush any unwritten data to disk. */ offset = 0; SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool)); *offset_p = offset; return SVN_NO_ERROR;}/* Read the format version from FILE and return it in *PFORMAT. Use POOL for temporary allocation. */static svn_error_t *read_format(int *pformat, const char *file, apr_pool_t *pool){ svn_error_t *err = svn_io_read_version_file(pformat, file, pool); if (err && APR_STATUS_IS_ENOENT(err->apr_err)) { /* Treat an absent format file as format 1. Do not try to create the format file on the fly, because the repository might be read-only for us, or this might be a read-only operation, and the spirit of FSFS is to make no changes whatseover in read-only operations. See thread starting at http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=97600 for more. */ svn_error_clear(err); err = SVN_NO_ERROR; *pformat = 1; } return err;}/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format number is not the same as the format number supported by this Subversion. */static svn_error_t *check_format(int format){ /* We support format 1 and 2 simultaneously */ if (format == 1 && SVN_FS_FS__FORMAT_NUMBER == 2) return SVN_NO_ERROR; if (format != SVN_FS_FS__FORMAT_NUMBER) { return svn_error_createf (SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, _("Expected FS format '%d'; found format '%d'"), SVN_FS_FS__FORMAT_NUMBER, format); } return SVN_NO_ERROR;}svn_error_t *svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool){ fs_fs_data_t *ffd = fs->fsap_data; apr_file_t *current_file, *uuid_file; int format; char buf[APR_UUID_FORMATTED_LENGTH + 2]; apr_size_t limit; fs->path = apr_pstrdup(fs->pool, path); /* Attempt to open the 'current' file of this repository. There isn't much need for specific state associated with an open fs_fs repository. */ SVN_ERR(svn_io_file_open(¤t_file, path_current(fs, pool), APR_READ, APR_OS_DEFAULT, pool)); SVN_ERR(svn_io_file_close(current_file, pool)); /* Read the FS format number. */ SVN_ERR(read_format(&format, path_format(fs, pool), pool)); /* Now we've got a format number no matter what. */ ffd->format = format; SVN_ERR(check_format(format)); /* Read in and cache the repository uuid. */ SVN_ERR(svn_io_file_open(&uuid_file, path_uuid(fs, pool), APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool)); limit = sizeof(buf); SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, pool)); ffd->uuid = apr_pstrdup(fs->pool, buf); SVN_ERR(svn_io_file_close(uuid_file, pool)); return SVN_NO_ERROR;}/* Find the youngest revision in a repository at path FS_PATH and return it in *YOUNGEST_P. Perform temporary allocations in POOL. */static svn_error_t *get_youngest(svn_revnum_t *youngest_p, const char *fs_path, apr_pool_t *pool){ apr_file_t *current_file; char buf[81]; apr_size_t len; SVN_ERR(svn_io_file_open(¤t_file, svn_path_join(fs_path, PATH_CURRENT, pool), APR_READ, APR_OS_DEFAULT, pool)); len = sizeof(buf) - 1; SVN_ERR(svn_io_file_read(current_file, buf, &len, pool)); buf[len] = '\0'; *youngest_p = SVN_STR_TO_REV(buf); SVN_ERR(svn_io_file_close(current_file, pool)); return SVN_NO_ERROR;}svn_error_t *svn_fs_fs__hotcopy(const char *src_path, const char *dst_path, apr_pool_t *pool){ const char *src_subdir, *dst_subdir; svn_revnum_t youngest, rev; apr_pool_t *iterpool; svn_node_kind_t kind; int format; /* Check format to be sure we know how to hotcopy this FS. */ SVN_ERR(read_format(&format, svn_path_join(src_path, PATH_FORMAT, pool), pool)); SVN_ERR(check_format(format)); /* Copy the current file. */ SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_CURRENT, pool)); /* Copy the uuid. */ SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_UUID, pool)); /* Find the youngest revision from this current file. */ SVN_ERR(get_youngest(&youngest, dst_path, pool)); /* Copy the necessary rev files. */ src_subdir = svn_path_join(src_path, PATH_REVS_DIR, pool); dst_subdir = svn_path_join(dst_path, PATH_REVS_DIR, pool); SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool)); iterpool = svn_pool_create(pool); for (rev = 0; rev <= youngest; rev++) { SVN_ERR(svn_io_dir_file_copy(src_subdir, dst_subdir, apr_psprintf(iterpool, "%ld", rev), iterpool)); svn_pool_clear(iterpool); } /* Copy the necessary revprop files. */ src_subdir = svn_path_join(src_path, PATH_REVPROPS_DIR, pool); dst_subdir = svn_path_join(dst_path, PATH_REVPROPS_DIR, pool); SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool)); for (rev = 0; rev <= youngest; rev++) { svn_pool_clear(iterpool); SVN_ERR(svn_io_dir_file_copy(src_subdir, dst_subdir, apr_psprintf(iterpool, "%ld", rev), iterpool)); } svn_pool_destroy(iterpool); /* Make an empty transactions directory for now. Eventually some method of copying in progress transactions will need to be developed.*/ dst_subdir = svn_path_join(dst_path, PATH_TXNS_DIR, pool); SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool)); /* Now copy the locks tree. */ src_subdir = svn_path_join(src_path, PATH_LOCKS_DIR, pool); SVN_ERR(svn_io_check_path(src_subdir, &kind, pool)); if (kind == svn_node_dir) SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path, PATH_LOCKS_DIR, TRUE, NULL, NULL, pool)); /* Hotcopied FS is complete. Stamp it with a format file. */ SVN_ERR(svn_io_write_version_file (svn_path_join(dst_path, PATH_FORMAT, pool), format, pool)); return SVN_NO_ERROR;}svn_error_t *svn_fs_fs__youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool){ SVN_ERR(get_youngest(youngest_p, fs->path, pool)); return SVN_NO_ERROR;}/* Given a revision file FILE that has been pre-positioned at the beginning of a Node-Rev header block, read in that header block and store it in the apr_hash_t HEADERS. All allocations will be from POOL. */static svn_error_t * read_header_block(apr_hash_t **headers, apr_file_t *file, apr_pool_t *pool){ *headers = apr_hash_make(pool); while (1) { char header_str[1024]; const char *name, *value; apr_size_t i = 0, header_len; apr_size_t limit; char *local_name, *local_value; limit = sizeof(header_str); SVN_ERR(svn_io_read_length_line(file, header_str, &limit, pool)); if (strlen(header_str) == 0) break; /* end of header block */ header_len = strlen(header_str); while (header_str[i] != ':') { if (header_str[i] == '\0') return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Found malformed header in "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -