/* Write the format number and maximum number of files per directory to a new format file in PATH, overwriting a previously existing file. Use POOL for temporary allocation. This implementation is largely stolen from libsvn_fs_fs/fs_fs.c. */ static svn_error_t * write_format(const char *path, int format, int max_files_per_dir, apr_pool_t *pool) { const char *contents; path = svn_path_join(path, "format", pool); if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT) { if (max_files_per_dir) contents = apr_psprintf(pool, "%d\n" "layout sharded %d\n", format, max_files_per_dir); else contents = apr_psprintf(pool, "%d\n" "layout linear", format); } else { contents = apr_psprintf(pool, "%d\n", format); } { const char *path_tmp; SVN_ERR(svn_io_write_unique(&path_tmp, svn_path_dirname(path, pool), contents, strlen(contents), svn_io_file_del_none, pool)); #ifdef WIN32 /* make the destination writable, but only on Windows, because Windows does not let us replace read-only files. */ SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool)); #endif /* WIN32 */ /* rename the temp file as the real destination */ SVN_ERR(svn_io_file_rename(path_tmp, path, pool)); } /* And set the perms to make it read only */ return svn_io_set_file_read_only(path, FALSE, pool); }
svn_error_t * svn_wc__write_old_wcprops(const char *path, apr_hash_t *prophash, svn_node_kind_t kind, apr_pool_t *scratch_pool) { apr_pool_t *pool = scratch_pool; const char *parent_dir; const char *base_name; svn_stream_t *stream; const char *temp_dir_path; const char *temp_prop_path; const char *prop_path; int wc_format_version; if (kind == svn_node_dir) parent_dir = path; else svn_path_split(path, &parent_dir, &base_name, pool); /* At this point, we know we need to open a file in the admin area of parent_dir. First check that parent_dir is a working copy: */ SVN_ERR(svn_wc_check_wc(parent_dir, &wc_format_version, pool)); if (wc_format_version == 0) return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("'%s' is not a working copy"), svn_path_local_style(parent_dir, pool)); /* Write to a temp file, then rename into place. */ temp_dir_path = svn_wc__adm_child(parent_dir, SVN_WC__ADM_TMP, pool); SVN_ERR(svn_stream_open_unique(&stream, &temp_prop_path, temp_dir_path, svn_io_file_del_none, pool, pool)); SVN_ERR_W(svn_hash_write2(prophash, stream, SVN_HASH_TERMINATOR, pool), apr_psprintf(pool, _("Cannot write property hash for '%s'"), svn_path_local_style(path, pool))); svn_stream_close(stream); /* Close file, then do an atomic "move". */ SVN_ERR(svn_wc__prop_path(&prop_path, path, kind, svn_wc__props_wcprop, pool)); SVN_ERR(svn_io_file_rename(temp_prop_path, prop_path, pool)); return svn_io_set_file_read_only(prop_path, FALSE, pool); }
/* APR pool cleanup callback taking a set_read_only_baton_t baton and then * (trying to) set the specified file to r/o mode. */ static apr_status_t set_read_only(void *baton) { set_read_only_baton_t *ro_baton = baton; apr_status_t status = APR_SUCCESS; svn_error_t *err; err = svn_io_set_file_read_only(ro_baton->file_path, TRUE, ro_baton->pool); if (err) { status = err->apr_err; svn_error_clear(err); } return status; }
/* Write the format number and maximum number of files per directory to a new format file in PATH, overwriting a previously existing file. Use POOL for temporary allocation. (This implementation is largely stolen from libsvn_fs_fs/fs_fs.c.) */ static svn_error_t * write_format(const char *path, int format, int max_files_per_dir, apr_pool_t *pool) { const char *contents; path = svn_dirent_join(path, "format", pool); if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT) { if (max_files_per_dir) contents = apr_psprintf(pool, "%d\n" "layout sharded %d\n", format, max_files_per_dir); else contents = apr_psprintf(pool, "%d\n" "layout linear", format); } else { contents = apr_psprintf(pool, "%d\n", format); } { const char *path_tmp; SVN_ERR(svn_io_write_unique(&path_tmp, svn_dirent_dirname(path, pool), contents, strlen(contents), svn_io_file_del_none, pool)); /* rename the temp file as the real destination */ SVN_ERR(svn_io_file_rename(path_tmp, path, pool)); } /* And set the perms to make it read only */ return svn_io_set_file_read_only(path, FALSE, pool); }
svn_error_t * svn_wc__close_adm_stream(svn_stream_t *stream, const char *temp_file_path, const char *path, const char *fname, apr_pool_t *scratch_pool) { const char *tmp_path = extend_with_adm_name(path, NULL, TRUE, scratch_pool, fname, NULL); const char *dst_path = extend_with_adm_name(path, NULL, FALSE, scratch_pool, fname, NULL); /* ### eventually, just use the parameter rather than compute tmp_path */ SVN_ERR_ASSERT(strcmp(temp_file_path, tmp_path) == 0); SVN_ERR(svn_stream_close(stream)); /* Put the completed file into its intended location. */ SVN_ERR(svn_io_file_rename(tmp_path, dst_path, scratch_pool)); return svn_io_set_file_read_only(dst_path, FALSE, scratch_pool); }
/* Rename a tmp text-base file to its real text-base name. The file had better already be closed. */ svn_error_t * svn_wc__sync_text_base(const char *path, apr_pool_t *pool) { const char *parent_path; const char *base_name; const char *tmp_path; const char *base_path; svn_path_split(path, &parent_path, &base_name, pool); /* Extend tmp name. */ tmp_path = extend_with_adm_name(parent_path, SVN_WC__BASE_EXT, TRUE, pool, SVN_WC__ADM_TEXT_BASE, base_name, NULL); /* Extend real name. */ base_path = extend_with_adm_name(parent_path, SVN_WC__BASE_EXT, FALSE, pool, SVN_WC__ADM_TEXT_BASE, base_name, NULL); /* Rename. */ SVN_ERR(svn_io_file_rename(tmp_path, base_path, pool)); return svn_io_set_file_read_only(base_path, FALSE, pool); }
static svn_error_t * test_readonly(apr_pool_t *pool) { const char *path; apr_finfo_t finfo; svn_boolean_t read_only; apr_int32_t wanted = APR_FINFO_SIZE | APR_FINFO_MTIME | APR_FINFO_TYPE | APR_FINFO_LINK | APR_FINFO_PROT; SVN_ERR(svn_io_open_unique_file3(NULL, &path, NULL, svn_io_file_del_on_pool_cleanup, pool, pool)); /* File should be writable */ SVN_ERR(svn_io_stat(&finfo, path, wanted, pool)); SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, pool)); SVN_TEST_ASSERT(!read_only); /* Set read only */ SVN_ERR(svn_io_set_file_read_only(path, FALSE, pool)); /* File should be read only */ SVN_ERR(svn_io_stat(&finfo, path, wanted, pool)); SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, pool)); SVN_TEST_ASSERT(read_only); /* Set writable */ SVN_ERR(svn_io_set_file_read_write(path, FALSE, pool)); /* File should be writable */ SVN_ERR(svn_io_stat(&finfo, path, wanted, pool)); SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, pool)); SVN_TEST_ASSERT(!read_only); return SVN_NO_ERROR; }
svn_error_t * svn_wc__sync_flags_with_props(svn_boolean_t *did_set, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *scratch_pool) { svn_wc__db_status_t status; svn_node_kind_t kind; svn_wc__db_lock_t *lock; apr_hash_t *props = NULL; svn_boolean_t had_props; svn_boolean_t props_mod; if (did_set) *did_set = FALSE; /* ### We'll consolidate these info gathering statements in a future commit. */ SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &lock, NULL, NULL, NULL, NULL, NULL, &had_props, &props_mod, NULL, NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); /* We actually only care about the following flags on files, so just early-out for all other types. Also bail if there is no in-wc representation of the file. */ if (kind != svn_node_file || (status != svn_wc__db_status_normal && status != svn_wc__db_status_added)) return SVN_NO_ERROR; if (props_mod || had_props) SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool, scratch_pool)); else props = NULL; /* If we get this far, we're going to change *something*, so just set the flag appropriately. */ if (did_set) *did_set = TRUE; /* Handle the read-write bit. */ if (status != svn_wc__db_status_normal || props == NULL || ! svn_hash_gets(props, SVN_PROP_NEEDS_LOCK) || lock) { SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool)); } else { /* Special case: If we have an uncommitted svn:needs-lock, we don't set the file read_only just yet. That happens upon commit. */ apr_hash_t *pristine_props; if (! props_mod) pristine_props = props; else if (had_props) SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db, local_abspath, scratch_pool, scratch_pool)); else pristine_props = NULL; if (pristine_props && svn_hash_gets(pristine_props, SVN_PROP_NEEDS_LOCK) ) /*&& props && apr_hash_get(props, SVN_PROP_NEEDS_LOCK, APR_HASH_KEY_STRING) )*/ SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool)); } /* Windows doesn't care about the execute bit. */ #ifndef WIN32 if (props == NULL || ! svn_hash_gets(props, SVN_PROP_EXECUTABLE)) { /* Turn off the execute bit */ SVN_ERR(svn_io_set_file_executable(local_abspath, FALSE, FALSE, scratch_pool)); } else SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE, scratch_pool)); #endif return SVN_NO_ERROR; }
/* Install the pristine text described by BATON into the pristine store of * SDB. If it is already stored then just delete the new file * BATON->tempfile_abspath. * * This function expects to be executed inside a SQLite txn that has already * acquired a 'RESERVED' lock. * * Implements 'notes/wc-ng/pristine-store' section A-3(a). */ static svn_error_t * pristine_install_txn(svn_sqlite__db_t *sdb, /* The path to the source file that is to be moved into place. */ svn_stream_t *install_stream, /* The target path for the file (within the pristine store). */ const char *pristine_abspath, /* The pristine text's SHA-1 checksum. */ const svn_checksum_t *sha1_checksum, /* The pristine text's MD-5 checksum. */ const svn_checksum_t *md5_checksum, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; /* If this pristine text is already present in the store, just keep it: * delete the new one and return. */ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_PRISTINE)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); SVN_ERR(svn_sqlite__reset(stmt)); if (have_row) { #ifdef SVN_DEBUG /* Consistency checks. Verify both files exist and match. * ### We could check much more. */ { apr_finfo_t finfo1, finfo2; SVN_ERR(svn_stream__install_get_info(&finfo1, install_stream, APR_FINFO_SIZE, scratch_pool)); SVN_ERR(svn_io_stat(&finfo2, pristine_abspath, APR_FINFO_SIZE, scratch_pool)); if (finfo1.size != finfo2.size) { return svn_error_createf( SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL, _("New pristine text '%s' has different size: %s versus %s"), svn_checksum_to_cstring_display(sha1_checksum, scratch_pool), apr_off_t_toa(scratch_pool, finfo1.size), apr_off_t_toa(scratch_pool, finfo2.size)); } } #endif /* Remove the temp file: it's already there */ SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool)); return SVN_NO_ERROR; } /* Move the file to its target location. (If it is already there, it is * an orphan file and it doesn't matter if we overwrite it.) */ { apr_finfo_t finfo; SVN_ERR(svn_stream__install_get_info(&finfo, install_stream, APR_FINFO_SIZE, scratch_pool)); SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath, TRUE, scratch_pool)); SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); SVN_ERR(svn_sqlite__insert(NULL, stmt)); SVN_ERR(svn_io_set_file_read_only(pristine_abspath, FALSE, scratch_pool)); } return SVN_NO_ERROR; }