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
Michael Haggerty <mhagger@alum.mit.edu>
Date
Feb 19, 2016, 16:07 UTC
Message-ID
<56C73DDE.1040606@alum.mit.edu>
In-Reply-To
<xmqqmvqzhxdf.fsf@gitster.mtv.corp.google.com>
On 02/17/2016 09:53 PM, Junio C Hamano wrote:
Show 35 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> 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.
Thanks; will fix.
> More importantly, is ENOTDIR expected only on a buggy platform?  

Here I was just mimicking the old behavior, which I think was correct, but let's check more carefully...

Show 27 quoted lines
> [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...

We just created TMP_RENAMED_LOG ourselves, so I don't think we need to expect errors from that argument. (Though I don't recall that there is any locking to prevent two `git branch -m` processes from clobbering each others' temporary files. Oh well; renaming branches is relatively rare and probably interactive, so I'll declare that potential problem to be out of scope for this patch series.)

So let's consider the cases where we can get ENOTDIR for `path`:
> A component of either path prefix names an existing file that is
> neither a directory nor a symbolic link to a directory.

This can certainly happen for `path`, but it is not a case that can be rescued by raceproof_create_file().

> or the old argument names a directory [...]
This is not the case.
> or the old argument contains [...]
Also not interesting.
> 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

The new argument doesn't end with trailing <slash> characters, so this can't happen.

> 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
Ditto.

So while it is true that a non-buggy implementation can give ENOTDIR, it is for a case that we can't rescue. So if it weren't for the buggy implementation, we could just leave ENOTDIR un-handled.

Now, we have to consider the opposite case, namely that we are calling a non-buggy implementation of `rename()`, and we artificially change ENOTDIR to EISDIR. Can that cause any bad effects?

I don't think so, because the case where a non-buggy implementation can yield ENOTDIR is a case, the consequent call to `remove_dir_recursively()` would fail with ENOTDIR too, and `raceproof_create_file()` would give up immediately.

So I think everything is OK, though I admit that it is not especially elegant. We could limit ourselves to doing the workaround only on Solaris 5.8, but that seems like a lot of effort for not much benefit. Or we could drop the workaround; after all, Solaris 5.8 was released in 2000 and end-of-lifed in 2012. (Though I don't know whether the behavior was fixed in later versions of Solaris.)

> [...]
Michael
-- 
Michael Haggerty
mhagger@alum.mit.edu
Previous: Junio C HamanoNext: Junio C Hamano
Message 13 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.