git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 05/20] rename_tmp_log(): use raceproof_create_file()

From
Junio C Hamano <gitster@pobox.com>
Date
Feb 17, 2016, 20:53 UTC
Message-ID
<xmqqmvqzhxdf.fsf@gitster.mtv.corp.google.com>
In-Reply-To
<a19192cb3219de9304af7d9b97be45a5e6f0895f.1455626201.git.mhagger@alum.mit.edu>
Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 31 quoted lines
> Besides shortening the code, this saves an unnecessary call to
> safe_create_leading_directories_const() in almost all cases.
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
>  refs/files-backend.c | 76 ++++++++++++++++++++++------------------------------
>  1 file changed, 32 insertions(+), 44 deletions(-)
>
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index a549942..e5f964c 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -2400,55 +2400,43 @@ out:
>   */
>  #define TMP_RENAMED_LOG  "logs/refs/.tmp-renamed-log"
>  
> +static int rename_tmp_log_callback(const char *path, void *cb)
> +{
> +	int *true_errno = cb;
> +
> +	if (rename(git_path(TMP_RENAMED_LOG), path)) {
> +		/*
> +		 * rename(a, b) when b is an existing directory ought
> +		 * to result in ISDIR, but Solaris 5.8 gives ENOTDIR.
> +		 * Sheesh. Record the true errno for error reporting,
> +		 * but report EISDIR to raceproof_create_file() so
> +		 * that it knows to retry.
> +		 */
> +		*true_errno = errno;
> +		if (errno==ENOTDIR)
> +			errno = EISDIR;
Style: SP on both sides of a binary operator.
More importantly, is ENOTDIR expected only on a buggy platform?  
[ENOTDIR]
    A component of either path prefix names an existing file that is
    neither a directory nor a symbolic link to a directory; or the old
    argument names a directory and the new argument names a
    non-directory file; or the old argument contains at least one non-
    <slash> character and ends with one or more trailing <slash>
    characters and the last pathname component names an existing file
    that is neither a directory nor a symbolic link to a directory; or
    the old argument names an existing non-directory file and the new
    argument names a nonexistent file, contains at least one non-
    <slash> character, and ends with one or more trailing <slash>
    characters; or the new argument names an existing non-directory
    file, contains at least one non- <slash> character, and ends with
    one or more trailing <slash> characters.

i.e. when a leading component of "path" or TMP_RENAMED_LOG is an existing non-directory, we could get ENOTDIR on a valid system.

If another instance of Git created a file A/B when this process is trying to rename the temporary thing to its final location A/B/C, isn't that the errno we would see here?

[EISDIR]
    The new argument points to a directory and the old argument
    points to a file that is not a directory.
Puzzled...
Show 68 quoted lines
> +		return -1;
> +	} else {
> +		return 0;
> +	}
> +}
> +
>  static int rename_tmp_log(const char *newrefname)
>  {
> -	int attempts_remaining = 4;
> -	struct strbuf path = STRBUF_INIT;
> -	int ret = -1;
> +	char *path = git_pathdup("logs/%s", newrefname);
> +	int true_errno;
> +	int ret;
>  
> - retry:
> -	strbuf_reset(&path);
> -	strbuf_git_path(&path, "logs/%s", newrefname);
> -	switch (safe_create_leading_directories_const(path.buf)) {
> -	case SCLD_OK:
> -		break; /* success */
> -	case SCLD_VANISHED:
> -		if (--attempts_remaining > 0)
> -			goto retry;
> -		/* fall through */
> -	default:
> -		error("unable to create directory for %s", newrefname);
> -		goto out;
> -	}
> -
> -	if (rename(git_path(TMP_RENAMED_LOG), path.buf)) {
> -		if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {
> -			/*
> -			 * rename(a, b) when b is an existing
> -			 * directory ought to result in ISDIR, but
> -			 * Solaris 5.8 gives ENOTDIR.  Sheesh.
> -			 */
> -			if (remove_empty_directories(&path)) {
> -				error("Directory not empty: logs/%s", newrefname);
> -				goto out;
> -			}
> -			goto retry;
> -		} else if (errno == ENOENT && --attempts_remaining > 0) {
> -			/*
> -			 * Maybe another process just deleted one of
> -			 * the directories in the path to newrefname.
> -			 * Try again from the beginning.
> -			 */
> -			goto retry;
> -		} else {
> +	ret = raceproof_create_file(path, rename_tmp_log_callback, &true_errno);
> +	if (ret) {
> +		if (true_errno==EISDIR)
> +			error("Directory not empty: %s", path);
> +		else
>  			error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
> -				newrefname, strerror(errno));
> -			goto out;
> -		}
> +				newrefname, strerror(true_errno));
>  	}
> -	ret = 0;
> -out:
> -	strbuf_release(&path);
> +
> +	free(path);
>  	return ret;
>  }
Previous: Michael HaggertyNext: Michael Haggerty
Message 12 of 34 in “Delete directories left empty after ref deletion”
  1. 00/20 Delete directories left empty after ref deletionMichael Haggerty, Feb 16, 2016
  2. 01/20 safe_create_leading_directories_const(): preserve errnoMichael Haggerty, Feb 16, 2016
  3. Jeff KingFeb 16, 2016
  4. 02/20 safe_create_leading_directories(): set errno on SCLD_EXISTSMichael Haggerty, Feb 16, 2016
  5. Junio C HamanoFeb 17, 2016
  6. Michael HaggertyFeb 18, 2016
  7. 03/20 raceproof_create_file(): new functionMichael Haggerty, Feb 16, 2016
  8. Junio C HamanoFeb 17, 2016
  9. 04/20 lock_ref_sha1_basic(): use raceproof_create_file()Michael Haggerty, Feb 16, 2016
  10. Junio C HamanoFeb 17, 2016
  11. 05/20 rename_tmp_log(): use raceproof_create_file()Michael Haggerty, Feb 16, 2016
  12. Junio C HamanoFeb 17, 2016
  13. Michael HaggertyFeb 19, 2016
  14. Junio C HamanoFeb 19, 2016
  15. 06/20 rename_tmp_log(): improve error reportingMichael Haggerty, Feb 16, 2016
  16. Junio C HamanoFeb 18, 2016
  17. 07/20 log_ref_setup(): separate code for create vs non-createMichael Haggerty, Feb 16, 2016
  18. 08/20 log_ref_setup(): improve robustness against racesMichael Haggerty, Feb 16, 2016
  19. Junio C HamanoFeb 18, 2016
  20. 09/20 log_ref_setup(): pass the open file descriptor back to the callerMichael Haggerty, Feb 16, 2016
  21. Junio C HamanoFeb 18, 2016
  22. 10/20 log_ref_write_1(): don't depend on logfileMichael Haggerty, Feb 16, 2016
  23. 11/20 log_ref_setup(): manage the name of the reflog file internallyMichael Haggerty, Feb 16, 2016
  24. 12/20 log_ref_write_1(): inline functionMichael Haggerty, Feb 16, 2016
  25. Junio C HamanoFeb 18, 2016
  26. 13/20 try_remove_empty_parents(): rename parameter "name" -> "refname"Michael Haggerty, Feb 16, 2016
  27. 14/20 try_remove_empty_parents(): don't trash argument contentsMichael Haggerty, Feb 16, 2016
  28. 15/20 try_remove_empty_parents(): don't accommodate consecutive slashesMichael Haggerty, Feb 16, 2016
  29. 16/20 t5505: use "for-each-ref" to test for the non-existence of referencesMichael Haggerty, Feb 16, 2016
  30. 17/20 delete_ref_loose(): derive loose reference path from lockMichael Haggerty, Feb 16, 2016
  31. 18/20 delete_ref_loose(): inline functionMichael Haggerty, Feb 16, 2016
  32. 19/20 try_remove_empty_parents(): teach to remove parents of reflogs, tooMichael Haggerty, Feb 16, 2016
  33. 20/20 ref_transaction_commit(): clean up empty directoriesMichael Haggerty, Feb 16, 2016
  34. Junio C HamanoFeb 17, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.