From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:04
This is v4 of this patch series. Thanks to Peff, Junio, Jake, and
Philip for their feedback about v3. I believe I have addressed all of
the comments about v1 [1], v2 [2], and v3 [3].
Changes since v3:
* Insert a new first patch correcting the docstring of
`refname_is_safe()`.
* In "raceproof_create_file(): new function", tweak a comment for
clarity as suggested by Peff.
* In "log_ref_setup(): separate code for create vs non-create", add a
semicolon to an otherwise empty code block as requested by Junio.
* Drop the patch "try_remove_empty_parents(): don't accommodate
consecutive slashes", for reasons discussed on the ML [4].
Michael
[1] http://public-inbox.org/git/cover.1455626201.git.mhagger@alum.mit.edu/T/#u
[2] http://public-inbox.org/git/cover.1456405698.git.mhagger@alum.mit.edu/T/#u
[3] http://public-inbox.org/git/cover.1483153436.git.mhagger@alum.mit.edu/T/#u
[4] http://public-inbox.org/git/5051c78e-51f9-becd-e1a6-9c0b781d6912@alum.mit.edu/
and surrounding thread.
Michael Haggerty (23):
files_rename_ref(): tidy up whitespace
refname_is_safe(): correct docstring
t5505: use "for-each-ref" to test for the non-existence of references
safe_create_leading_directories_const(): preserve errno
safe_create_leading_directories(): set errno on SCLD_EXISTS
raceproof_create_file(): new function
lock_ref_sha1_basic(): inline constant
lock_ref_sha1_basic(): use raceproof_create_file()
rename_tmp_log(): use raceproof_create_file()
rename_tmp_log(): improve error reporting
log_ref_write(): inline function
log_ref_setup(): separate code for create vs non-create
log_ref_setup(): improve robustness against races
log_ref_setup(): pass the open file descriptor back to the caller
log_ref_write_1(): don't depend on logfile argument
log_ref_setup(): manage the name of the reflog file internally
log_ref_write_1(): inline function
delete_ref_loose(): derive loose reference path from lock
delete_ref_loose(): inline function
try_remove_empty_parents(): rename parameter "name" -> "refname"
try_remove_empty_parents(): don't trash argument contents
try_remove_empty_parents(): teach to remove parents of reflogs, too
files_transaction_commit(): clean up empty directories
cache.h | 48 ++++++-
refs/files-backend.c | 371 +++++++++++++++++++++++++-------------------------
refs/refs-internal.h | 22 ++-
sha1_file.c | 76 ++++++++++-
t/t1400-update-ref.sh | 27 ++++
t/t5505-remote.sh | 2 +-
6 files changed, 351 insertions(+), 195 deletions(-)
--
2.9.3
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:19
Instead of looking on the filesystem inside ".git/refs/remotes/origin",
use "git for-each-ref" to check for leftover references under the
remote's old name.
Signed-off-by: Michael Haggerty <redacted>
---
t/t5505-remote.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:21
Add a function that tries to create a file and any containing
directories in a way that is robust against races with other processes
that might be cleaning up empty directories at the same time.
The actual file creation is done by a callback function, which, if it
fails, should set errno to EISDIR or ENOENT according to the convention
of open(). raceproof_create_file() detects such failures, and
respectively either tries to delete empty directories that might be in
the way of the file or tries to create the containing directories. Then
it retries the callback function.
This function is not yet used.
Signed-off-by: Michael Haggerty <redacted>
---
cache.h | 43 ++++++++++++++++++++++++++++++++++++++
sha1_file.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 111 insertions(+)
@@ -179,6 +179,74 @@ enum scld_error safe_create_leading_directories_const(const char *path)returnresult;}+intraceproof_create_file(constchar*path,create_file_fnfn,void*cb)+{+/*+*Thenumberoftimeswewilltrytoremoveemptydirectories+*inthewayofpath.Thisisonly1becauseifanother+*processisracilycreatingdirectoriesthatconflictwith+*us,wedon'twanttofightagainstthem.+*/+intremove_directories_remaining=1;++/*+*Thenumberoftimesthatwewilltrytocreatethe+*directoriescontainingpath.Wearewillingtoattemptthis+*morethanonce,becauseanotherprocesscouldbetryingto+*cleanupemptydirectoriesatthesametimeasweare+*tryingtocreatethem.+*/+intcreate_directories_remaining=3;++/* A scratch copy of path, filled lazily if we need it: */+structstrbufpath_copy=STRBUF_INIT;++intret,save_errno;++/* Sanity check: */+assert(*path);++retry_fn:+ret=fn(path,cb);+save_errno=errno;+if(!ret)+gotoout;++if(errno==EISDIR&&remove_directories_remaining-->0){+/*+*Adirectoryisintheway.Maybeitisempty;try+*toremoveit:+*/+if(!path_copy.len)+strbuf_addstr(&path_copy,path);++if(!remove_dir_recursively(&path_copy,REMOVE_DIR_EMPTY_ONLY))+gotoretry_fn;+}elseif(errno==ENOENT&&create_directories_remaining-->0){+/*+*Maybethecontainingdirectorydidn'texist,or+*maybeitwasjustdeletedbyaprocessthatis+*racingwithustocleanupemptydirectories.Try+*tocreateit:+*/+enumscld_errorscld_result;++if(!path_copy.len)+strbuf_addstr(&path_copy,path);++do{+scld_result=safe_create_leading_directories(path_copy.buf);+if(scld_result==SCLD_OK)+gotoretry_fn;+}while(scld_result==SCLD_VANISHED&&create_directories_remaining-->0);+}++out:+strbuf_release(&path_copy);+errno=save_errno;+returnret;+}+staticvoidfill_sha1_path(structstrbuf*buf,constunsignedchar*sha1){inti;
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:22
The behavior of refname_is_safe() was changed in
e40f355 "refname_is_safe(): insist that the refname already be normalized", 2016-04-27
without a corresponding update to its docstring. The function is in fact
stricter than documented, because it now insists that the result of
normalizing the part of a refname following "refs/" is identical to that
part of the original refname. Fix the docstring.
Signed-off-by: Michael Haggerty <redacted>
---
refs/refs-internal.h | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:28
The exit path for SCLD_EXISTS wasn't setting errno, which some callers
use to generate error messages for the user. Fix the problem and
document that the function sets errno correctly to help avoid similar
regressions in the future.
Signed-off-by: Michael Haggerty <redacted>
---
cache.h | 5 +++--
sha1_file.c | 4 +++-
2 files changed, 6 insertions(+), 3 deletions(-)
@@ -2518,10 +2518,11 @@ static int rename_tmp_log(const char *newrefname)ret=raceproof_create_file(path,rename_tmp_log_callback,&true_errno);if(ret){if(errno==EISDIR)-error("Directory not empty: %s",path);+error("directory not empty: %s",path);else-error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",-newrefname,strerror(true_errno));+error("unable to move logfile %s to %s: %s",+git_path(TMP_RENAMED_LOG),path,+strerror(true_errno));}free(path);
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:32
Besides shortening the code, this saves an unnecessary call to
safe_create_leading_directories_const() in almost all cases.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 73 +++++++++++++++++++++-------------------------------
1 file changed, 30 insertions(+), 43 deletions(-)
@@ -2489,55 +2489,42 @@ static int files_delete_refs(struct ref_store *ref_store,*/#define TMP_RENAMED_LOG "logs/refs/.tmp-renamed-log"-staticintrename_tmp_log(constchar*newrefname)+staticintrename_tmp_log_callback(constchar*path,void*cb){-intattempts_remaining=4;-structstrbufpath=STRBUF_INIT;-intret=-1;+int*true_errno=cb;-retry:-strbuf_reset(&path);-strbuf_git_path(&path,"logs/%s",newrefname);-switch(safe_create_leading_directories_const(path.buf)){-caseSCLD_OK:-break;/* success */-caseSCLD_VANISHED:-if(--attempts_remaining>0)-gotoretry;-/* fall through */-default:-error("unable to create directory for %s",newrefname);-gotoout;+if(rename(git_path(TMP_RENAMED_LOG),path)){+/*+*rename(a,b)whenbisanexistingdirectoryought+*toresultinISDIR,butSolaris5.8givesENOTDIR.+*Sheesh.Recordthetrueerrnoforerrorreporting,+*butreportEISDIRtoraceproof_create_file()so+*thatitknowstoretry.+*/+*true_errno=errno;+if(errno==ENOTDIR)+errno=EISDIR;+return-1;+}else{+return0;}+}-if(rename(git_path(TMP_RENAMED_LOG),path.buf)){-if((errno==EISDIR||errno==ENOTDIR)&&--attempts_remaining>0){-/*-*rename(a,b)whenbisanexisting-*directoryoughttoresultinISDIR,but-*Solaris5.8givesENOTDIR.Sheesh.-*/-if(remove_empty_directories(&path)){-error("Directory not empty: logs/%s",newrefname);-gotoout;-}-gotoretry;-}elseif(errno==ENOENT&&--attempts_remaining>0){-/*-*Maybeanotherprocessjustdeletedoneof-*thedirectoriesinthepathtonewrefname.-*Tryagainfromthebeginning.-*/-gotoretry;-}else{+staticintrename_tmp_log(constchar*newrefname)+{+char*path=git_pathdup("logs/%s",newrefname);+intret,true_errno;++ret=raceproof_create_file(path,rename_tmp_log_callback,&true_errno);+if(ret){+if(errno==EISDIR)+error("Directory not empty: %s",path);+elseerror("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",-newrefname,strerror(errno));-gotoout;-}+newrefname,strerror(true_errno));}-ret=0;-out:-strbuf_release(&path);++free(path);returnret;}
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:33
Some implementations of free() change errno (even thought they
shouldn't):
https://sourceware.org/bugzilla/show_bug.cgi?id=17924
So preserve the errno from safe_create_leading_directories() across the
call to free().
Signed-off-by: Michael Haggerty <redacted>
---
sha1_file.c | 4 ++++
1 file changed, 4 insertions(+)
@@ -166,10 +166,14 @@ enum scld_error safe_create_leading_directories(char *path)enumscld_errorsafe_create_leading_directories_const(constchar*path){+intsave_errno;/* path points to cache entries, so xstrdup before messing with it */char*buf=xstrdup(path);enumscld_errorresult=safe_create_leading_directories(buf);++save_errno=errno;free(buf);+errno=save_errno;returnresult;}
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:36
`lflags` is set a single time then never changed, so just inline it.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:39
Change log_ref_setup() to use raceproof_create_file() to create the new
logfile. This makes it more robust against a race against another
process that might be trying to clean up empty directories while we are
trying to create a new logfile.
This also means that it will only call create_leading_directories() if
open() fails, which should be a net win. Even in the cases where we are
willing to create a new logfile, it will usually be the case that the
logfile already exists, or if not then that the directory containing the
logfile already exists. In such cases, we will save some work that was
previously done unconditionally.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 41 ++++++++++++++++++-----------------------
1 file changed, 18 insertions(+), 23 deletions(-)
@@ -2710,6 +2710,14 @@ static int commit_ref(struct ref_lock *lock)return0;}+staticintopen_or_create_logfile(constchar*path,void*cb)+{+int*fd=cb;++*fd=open(path,O_APPEND|O_WRONLY|O_CREAT,0666);+return(*fd<0)?-1:0;+}+/**Createareflogforaref.Ifforce_create=0,thereflogwill*onlybecreatedforcertainrefs(thoseforwhich
@@ -2723,31 +2731,18 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strstrbuf_git_path(logfile,"logs/%s",refname);if(force_create||should_autocreate_reflog(refname)){-if(safe_create_leading_directories(logfile->buf)<0){-strbuf_addf(err,"unable to create directory for '%s': "-"%s",logfile->buf,strerror(errno));-return-1;-}-logfd=open(logfile->buf,O_APPEND|O_WRONLY|O_CREAT,0666);-if(logfd<0){-if(errno==EISDIR){-/*-*Thedirectorythatisinthewaymightbe-*empty.Trytoremoveit.-*/-if(remove_empty_directories(logfile)){-strbuf_addf(err,"there are still logs under "-"'%s'",logfile->buf);-return-1;-}-logfd=open(logfile->buf,O_APPEND|O_WRONLY|O_CREAT,0666);-}--if(logfd<0){+if(raceproof_create_file(logfile->buf,open_or_create_logfile,&logfd)){+if(errno==ENOENT)+strbuf_addf(err,"unable to create directory for '%s': "+"%s",logfile->buf,strerror(errno));+elseif(errno==EISDIR)+strbuf_addf(err,"there are still logs under '%s'",+logfile->buf);+elsestrbuf_addf(err,"unable to append to '%s': %s",logfile->buf,strerror(errno));-return-1;-}++return-1;}}else{logfd=open(logfile->buf,O_APPEND|O_WRONLY,0666);
From: Michael Haggerty <hidden> Date: 2017-01-06 16:23:43
Instead of coding the retry loop inline, use raceproof_create_file() to
make lock acquisition safe against directory creation/deletion races.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 35 +++++++++--------------------------
1 file changed, 9 insertions(+), 26 deletions(-)
@@ -2067,35 +2073,12 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,lock->ref_name=xstrdup(refname);-retry:-switch(safe_create_leading_directories_const(ref_file.buf)){-caseSCLD_OK:-break;/* success */-caseSCLD_VANISHED:-if(--attempts_remaining>0)-gotoretry;-/* fall through */-default:+if(raceproof_create_file(ref_file.buf,create_reflock,lock->lk)){last_errno=errno;-strbuf_addf(err,"unable to create directory for '%s'",-ref_file.buf);+unable_to_lock_message(ref_file.buf,errno,err);gotoerror_return;}-if(hold_lock_file_for_update(lock->lk,ref_file.buf,LOCK_NO_DEREF)<0){-last_errno=errno;-if(errno==ENOENT&&--attempts_remaining>0)-/*-*Maybesomebodyjustdeletedoneofthe-*directoriesleadingtoref_file.Try-*again:-*/-gotoretry;-else{-unable_to_lock_message(ref_file.buf,errno,err);-gotoerror_return;-}-}if(verify_lock(lock,old_sha1,mustexist,err)){last_errno=errno;gotoerror_return;
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:02
This function doesn't do anything beyond call files_log_ref_write(), so
replace it with the latter at its call sites.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 24 ++++++++++--------------
1 file changed, 10 insertions(+), 14 deletions(-)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:05
When deleting/pruning references, remove any directories that are made
empty by the deletion of loose references or of reflogs. Otherwise such
empty directories can survive forever and accumulate over time. (Even
'pack-refs', which is smart enough to remove the parent directories of
loose references that it prunes, leaves directories that were already
empty.)
And now that files_transaction_commit() takes care of deleting the
parent directories of loose references that it prunes, we don't have to
do that in prune_ref() anymore.
This change would be unwise if the *creation* of these directories could
race with our deletion of them. But the earlier changes in this patch
series made the creation paths robust against races, so now it is safe
to tidy them up more aggressively.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 34 ++++++++++++++++++++++++++++------
refs/refs-internal.h | 11 +++++++++--
t/t1400-update-ref.sh | 27 +++++++++++++++++++++++++++
3 files changed, 64 insertions(+), 8 deletions(-)
@@ -3794,6 +3793,7 @@ static int files_transaction_commit(struct ref_store *ref_store,ret=TRANSACTION_GENERIC_ERROR;gotocleanup;}+update->flags|=REF_DELETED_LOOSE;}if(!(update->flags&REF_ISPRUNING))
@@ -3806,16 +3806,38 @@ static int files_transaction_commit(struct ref_store *ref_store,ret=TRANSACTION_GENERIC_ERROR;gotocleanup;}-for_each_string_list_item(ref_to_delete,&refs_to_delete)-unlink_or_warn(git_path("logs/%s",ref_to_delete->string));++/* Delete the reflogs of any references that were deleted: */+for_each_string_list_item(ref_to_delete,&refs_to_delete){+if(!unlink_or_warn(git_path("logs/%s",ref_to_delete->string)))+try_remove_empty_parents(ref_to_delete->string,+REMOVE_EMPTY_PARENTS_REFLOG);+}+clear_loose_ref_cache(refs);cleanup:transaction->state=REF_TRANSACTION_CLOSED;-for(i=0;i<transaction->nr;i++)-if(transaction->updates[i]->backend_data)-unlock_ref(transaction->updates[i]->backend_data);+for(i=0;i<transaction->nr;i++){+structref_update*update=transaction->updates[i];+structref_lock*lock=update->backend_data;++if(lock)+unlock_ref(lock);++if(update->flags&REF_DELETED_LOOSE){+/*+*Theloosereferencewasdeleted.Deleteany+*emptyparentdirectories.(Notethatthis+*canonlyworkbecausewehavealready+*removedthelockfile.)+*/+try_remove_empty_parents(update->refname,+REMOVE_EMPTY_PARENTS_REF);+}+}+string_list_clear(&refs_to_delete,0);free(head_ref);string_list_clear(&affected_refnames,0);
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:06
It is simpler to derive the path to the file that must be deleted from
"lock->ref_name" than from the lock_file object.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:08
It was hardly doing anything anymore, and had only one caller.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
@@ -2421,21 +2421,6 @@ static int repack_without_refs(struct files_ref_store *refs,returnret;}-staticintdelete_ref_loose(structref_lock*lock,intflag,structstrbuf*err)-{-assert(err);--if(!(flag&REF_ISPACKED)||flag&REF_ISSYMREF){-/*-*loose.Theloosefilenameisthesameasthe-*lockfilename,minus".lock":-*/-if(unlink_or_msg(git_path("%s",lock->ref_name),err))-return1;-}-return0;-}-staticintfiles_delete_refs(structref_store*ref_store,structstring_list*refnames,unsignedintflags){
@@ -3788,9 +3773,13 @@ static int files_transaction_commit(struct ref_store *ref_store,if(update->flags&REF_DELETING&&!(update->flags&REF_LOG_ONLY)){-if(delete_ref_loose(lock,update->type,err)){-ret=TRANSACTION_GENERIC_ERROR;-gotocleanup;+if(!(update->type&REF_ISPACKED)||+update->type&REF_ISSYMREF){+/* It is a loose reference. */+if(unlink_or_msg(git_path("%s",lock->ref_name),err)){+ret=TRANSACTION_GENERIC_ERROR;+gotocleanup;+}}if(!(update->flags&REF_ISPRUNING))
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:09
Add a new "flags" parameter that tells the function whether to remove
empty parent directories of the loose reference file, of the reflog
file, or both. The new functionality is not yet used.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:11
This is the standard nomenclature.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:21
Instead of writing the name of the reflog file into a strbuf that is
supplied by the caller but not needed there, write it into a local
temporary buffer and remove the strbuf parameter entirely.
And while we're adjusting the function signature, reorder the arguments
to move the input parameters before the output parameters.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 69 ++++++++++++++++++++++++++--------------------------
1 file changed, 34 insertions(+), 35 deletions(-)
@@ -2719,37 +2719,36 @@ static int open_or_create_logfile(const char *path, void *cb)}/*-*Createareflogforaref.Storeitspathto*logfile.If-*force_create=0,onlycreatethereflogforcertainrefs(those-*forwhichshould_autocreate_reflogreturnsnon-zero).Otherwise,-*createitregardlessofthereferencename.Ifthelogfilealready-*existedorwascreated,return0andset*logfdtothefile-*descriptoropenedforappendingtothefile.Ifnologfileexists-*andwedecidednottocreateone,return0andset*logfdto-1.On-*failure,fillin*err,set*logfdto-1,andreturn-1.+*Createareflogforaref.Ifforce_create=0,onlycreatethe+*reflogforcertainrefs(thoseforwhichshould_autocreate_reflog+*returnsnon-zero).Otherwise,createitregardlessofthereference+*name.Ifthelogfilealreadyexistedorwascreated,return0and+*set*logfdtothefiledescriptoropenedforappendingtothefile.+*Ifnologfileexistsandwedecidednottocreateone,return0and+*set*logfdto-1.Onfailure,fillin*err,set*logfdto-1,and+*return-1.*/-staticintlog_ref_setup(constchar*refname,-structstrbuf*logfile,int*logfd,-structstrbuf*err,intforce_create)+staticintlog_ref_setup(constchar*refname,intforce_create,+int*logfd,structstrbuf*err){-strbuf_git_path(logfile,"logs/%s",refname);+char*logfile=git_pathdup("logs/%s",refname);if(force_create||should_autocreate_reflog(refname)){-if(raceproof_create_file(logfile->buf,open_or_create_logfile,logfd)){+if(raceproof_create_file(logfile,open_or_create_logfile,logfd)){if(errno==ENOENT)strbuf_addf(err,"unable to create directory for '%s': "-"%s",logfile->buf,strerror(errno));+"%s",logfile,strerror(errno));elseif(errno==EISDIR)strbuf_addf(err,"there are still logs under '%s'",-logfile->buf);+logfile);elsestrbuf_addf(err,"unable to append to '%s': %s",-logfile->buf,strerror(errno));+logfile,strerror(errno));-return-1;+gotoerror;}}else{-*logfd=open(logfile->buf,O_APPEND|O_WRONLY,0666);+*logfd=open(logfile,O_APPEND|O_WRONLY,0666);if(*logfd<0){if(errno==ENOENT||errno==EISDIR){/*
@@ -2761,34 +2760,39 @@ static int log_ref_setup(const char *refname,;}else{strbuf_addf(err,"unable to append to '%s': %s",-logfile->buf,strerror(errno));-return-1;+logfile,strerror(errno));+gotoerror;}}}if(*logfd>=0)-adjust_shared_perm(logfile->buf);+adjust_shared_perm(logfile);+free(logfile);return0;++error:+free(logfile);+return-1;}staticintfiles_create_reflog(structref_store*ref_store,constchar*refname,intforce_create,structstrbuf*err){-intret;-structstrbufsb=STRBUF_INIT;intfd;/* Check validity (but we don't need the result): */files_downcast(ref_store,0,"create_reflog");-ret=log_ref_setup(refname,&sb,&fd,err,force_create);+if(log_ref_setup(refname,force_create,&fd,err))+return-1;+if(fd>=0)close(fd);-strbuf_release(&sb);-returnret;++return0;}staticintlog_ref_write_fd(intfd,constunsignedchar*old_sha1,
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:24
The behavior of this function (especially how it handles errors) is
quite different depending on whether we are willing to create the reflog
vs. whether we are only trying to open an existing reflog. So separate
the code paths.
This also simplifies the next steps.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 59 ++++++++++++++++++++++++++++++++++------------------
1 file changed, 39 insertions(+), 20 deletions(-)
@@ -2718,45 +2718,64 @@ static int commit_ref(struct ref_lock *lock)*/staticintlog_ref_setup(constchar*refname,structstrbuf*logfile,structstrbuf*err,intforce_create){-intlogfd,oflags=O_APPEND|O_WRONLY;+intlogfd;strbuf_git_path(logfile,"logs/%s",refname);+if(force_create||should_autocreate_reflog(refname)){if(safe_create_leading_directories(logfile->buf)<0){strbuf_addf(err,"unable to create directory for '%s': ""%s",logfile->buf,strerror(errno));return-1;}-oflags|=O_CREAT;-}--logfd=open(logfile->buf,oflags,0666);-if(logfd<0){-if(!(oflags&O_CREAT)&&(errno==ENOENT||errno==EISDIR))-return0;+logfd=open(logfile->buf,O_APPEND|O_WRONLY|O_CREAT,0666);+if(logfd<0){+if(errno==EISDIR){+/*+*Thedirectorythatisinthewaymightbe+*empty.Trytoremoveit.+*/+if(remove_empty_directories(logfile)){+strbuf_addf(err,"there are still logs under "+"'%s'",logfile->buf);+return-1;+}+logfd=open(logfile->buf,O_APPEND|O_WRONLY|O_CREAT,0666);+}-if(errno==EISDIR){-if(remove_empty_directories(logfile)){-strbuf_addf(err,"there are still logs under "-"'%s'",logfile->buf);+if(logfd<0){+strbuf_addf(err,"unable to append to '%s': %s",+logfile->buf,strerror(errno));return-1;}-logfd=open(logfile->buf,oflags,0666);}-+}else{+logfd=open(logfile->buf,O_APPEND|O_WRONLY,0666);if(logfd<0){-strbuf_addf(err,"unable to append to '%s': %s",-logfile->buf,strerror(errno));-return-1;+if(errno==ENOENT||errno==EISDIR){+/*+*Thelogfiledoesn'talreadyexist,+*butthatisnotanerror;itonly+*meansthatwewon'twritelog+*entriestoit.+*/+;+}else{+strbuf_addf(err,"unable to append to '%s': %s",+logfile->buf,strerror(errno));+return-1;+}}}-adjust_shared_perm(logfile->buf);-close(logfd);+if(logfd>=0){+adjust_shared_perm(logfile->buf);+close(logfd);+}+return0;}-staticintfiles_create_reflog(structref_store*ref_store,constchar*refname,intforce_create,structstrbuf*err)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:25
It's unnecessary to pass a strbuf holding the reflog path up and down
the call stack now that it is hardly needed by the callers. Remove the
places where log_ref_write_1() uses it, in preparation for making it
internal to log_ref_setup().
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
@@ -2838,14 +2838,18 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,result=log_ref_write_fd(logfd,old_sha1,new_sha1,git_committer_info(0),msg);if(result){-strbuf_addf(err,"unable to append to '%s': %s",logfile->buf,-strerror(errno));+intsave_errno=errno;++strbuf_addf(err,"unable to append to '%s': %s",+git_path("logs/%s",refname),strerror(save_errno));close(logfd);return-1;}if(close(logfd)){-strbuf_addf(err,"unable to append to '%s': %s",logfile->buf,-strerror(errno));+intsave_errno=errno;++strbuf_addf(err,"unable to append to '%s': %s",+git_path("logs/%s",refname),strerror(save_errno));return-1;}return0;
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:26
Now files_log_ref_write() doesn't do anything beyond call
log_ref_write_1(), so inline the latter into the former.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
From: Michael Haggerty <hidden> Date: 2017-01-06 16:24:28
This function will most often be called by log_ref_write_1(), which
wants to append to the reflog file. In that case, it is silly to close
the file only for the caller to reopen it immediately. So, in the case
that the file was opened, pass the open file descriptor back to the
caller.
Signed-off-by: Michael Haggerty <redacted>
---
refs/files-backend.c | 39 ++++++++++++++++++++++-----------------
1 file changed, 22 insertions(+), 17 deletions(-)
@@ -2777,11 +2779,14 @@ static int files_create_reflog(struct ref_store *ref_store,{intret;structstrbufsb=STRBUF_INIT;+intfd;/* Check validity (but we don't need the result): */files_downcast(ref_store,0,"create_reflog");-ret=log_ref_setup(refname,&sb,err,force_create);+ret=log_ref_setup(refname,&sb,&fd,err,force_create);+if(fd>=0)+close(fd);strbuf_release(&sb);returnret;}
From: Jeff King <hidden> Date: 2017-01-06 19:45:03
On Fri, Jan 06, 2017 at 05:22:20PM +0100, Michael Haggerty wrote:
This is v4 of this patch series. Thanks to Peff, Junio, Jake, and
Philip for their feedback about v3. I believe I have addressed all of
the comments about v1 [1], v2 [2], and v3 [3].