@@ -326,12 +326,13 @@ static int refdb_fs_backend__exists(
326326{
327327 refdb_fs_backend * backend = (refdb_fs_backend * )_backend ;
328328 git_buf ref_path = GIT_BUF_INIT ;
329+ int error ;
329330
330331 assert (backend );
331332
332- if (packed_reload (backend ) < 0 ||
333- git_buf_joinpath (& ref_path , backend -> path , ref_name ) < 0 )
334- return -1 ;
333+ if (( error = packed_reload (backend ) ) < 0 ||
334+ ( error = git_buf_joinpath (& ref_path , backend -> path , ref_name ) ) < 0 )
335+ return error ;
335336
336337 * exists = git_path_isfile (ref_path .ptr ) ||
337338 (git_sortedcache_lookup (backend -> refcache , ref_name ) != NULL );
@@ -409,8 +410,8 @@ static int packed_lookup(
409410 int error = 0 ;
410411 struct packref * entry ;
411412
412- if (packed_reload (backend ) < 0 )
413- return -1 ;
413+ if (( error = packed_reload (backend ) ) < 0 )
414+ return error ;
414415
415416 if (git_sortedcache_rlock (backend -> refcache ) < 0 )
416417 return -1 ;
@@ -615,13 +616,14 @@ static int refdb_fs_backend__iterator_next_name(
615616static int refdb_fs_backend__iterator (
616617 git_reference_iterator * * out , git_refdb_backend * _backend , const char * glob )
617618{
619+ int error ;
618620 refdb_fs_iter * iter ;
619621 refdb_fs_backend * backend = (refdb_fs_backend * )_backend ;
620622
621623 assert (backend );
622624
623- if (packed_reload (backend ) < 0 )
624- return -1 ;
625+ if (( error = packed_reload (backend ) ) < 0 )
626+ return error ;
625627
626628 iter = git__calloc (1 , sizeof (refdb_fs_iter ));
627629 GITERR_CHECK_ALLOC (iter );
@@ -674,16 +676,18 @@ static int reference_path_available(
674676 int force )
675677{
676678 size_t i ;
679+ int error ;
677680
678- if (packed_reload (backend ) < 0 )
679- return -1 ;
681+ if (( error = packed_reload (backend ) ) < 0 )
682+ return error ;
680683
681684 if (!force ) {
682685 int exists ;
683686
684- if (refdb_fs_backend__exists (
685- & exists , (git_refdb_backend * )backend , new_ref ) < 0 )
686- return -1 ;
687+ if ((error = refdb_fs_backend__exists (
688+ & exists , (git_refdb_backend * )backend , new_ref )) < 0 ) {
689+ return error ;
690+ }
687691
688692 if (exists ) {
689693 giterr_set (GITERR_REFERENCE ,
@@ -901,40 +905,60 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
901905static int packed_remove_loose (refdb_fs_backend * backend )
902906{
903907 size_t i ;
904- git_buf full_path = GIT_BUF_INIT ;
905- int failed = 0 ;
908+ git_filebuf lock = GIT_FILEBUF_INIT ;
909+ git_buf ref_content = GIT_BUF_INIT ;
910+ int error = 0 ;
906911
907912 /* backend->refcache is already locked when this is called */
908913
909914 for (i = 0 ; i < git_sortedcache_entrycount (backend -> refcache ); ++ i ) {
910915 struct packref * ref = git_sortedcache_entry (backend -> refcache , i );
916+ git_oid current_id ;
911917
912918 if (!ref || !(ref -> flags & PACKREF_WAS_LOOSE ))
913919 continue ;
914920
915- if (git_buf_joinpath (& full_path , backend -> path , ref -> name ) < 0 )
916- return -1 ; /* critical; do not try to recover on oom */
921+ git_filebuf_cleanup (& lock );
917922
918- if (git_path_exists (full_path .ptr ) && p_unlink (full_path .ptr ) < 0 ) {
919- if (failed )
920- continue ;
923+ /* We need to stop anybody from updating the ref while we try to do a safe delete */
924+ error = loose_lock (& lock , backend , ref -> name );
925+ /* If someone else is updating it, let them do it */
926+ if (error == GIT_EEXISTS || error == GIT_ENOTFOUND )
927+ continue ;
921928
922- giterr_set (GITERR_REFERENCE ,
923- "Failed to remove loose reference '%s' after packing: %s" ,
924- full_path .ptr , strerror (errno ));
925- failed = 1 ;
929+ if (error < 0 ) {
930+ giterr_set (GITERR_REFERENCE , "failed to lock loose reference '%s'" , ref -> name );
931+ return error ;
926932 }
927933
934+ error = git_futils_readbuffer (& ref_content , lock .path_original );
935+ /* Someone else beat us to cleaning up the ref, let's simply continue */
936+ if (error == GIT_ENOTFOUND )
937+ continue ;
938+
939+ /* This became a symref between us packing and trying to delete it, so ignore it */
940+ if (!git__prefixcmp (ref_content .ptr , GIT_SYMREF ))
941+ continue ;
942+
943+ /* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
944+ if (loose_parse_oid (& current_id , lock .path_original , & ref_content ) < 0 )
945+ continue ;
946+
947+ /* If the ref moved since we packed it, we must not delete it */
948+ if (!git_oid_equal (& current_id , & ref -> oid ))
949+ continue ;
950+
928951 /*
929952 * if we fail to remove a single file, this is *not* good,
930953 * but we should keep going and remove as many as possible.
931- * After we've removed as many files as possible, we return
932- * the error code anyway .
954+ * If we fail to remove, the ref is still in the old state, so
955+ * we haven't lost information .
933956 */
957+ p_unlink (lock .path_original );
934958 }
935959
936- git_buf_free ( & full_path );
937- return failed ? -1 : 0 ;
960+ git_filebuf_cleanup ( & lock );
961+ return 0 ;
938962}
939963
940964/*
@@ -944,41 +968,42 @@ static int packed_write(refdb_fs_backend *backend)
944968{
945969 git_sortedcache * refcache = backend -> refcache ;
946970 git_filebuf pack_file = GIT_FILEBUF_INIT ;
971+ int error ;
947972 size_t i ;
948973
949974 /* lock the cache to updates while we do this */
950- if (git_sortedcache_wlock (refcache ) < 0 )
951- return -1 ;
975+ if (( error = git_sortedcache_wlock (refcache ) ) < 0 )
976+ return error ;
952977
953978 /* Open the file! */
954- if (git_filebuf_open (& pack_file , git_sortedcache_path (refcache ), 0 , GIT_PACKEDREFS_FILE_MODE ) < 0 )
979+ if (( error = git_filebuf_open (& pack_file , git_sortedcache_path (refcache ), 0 , GIT_PACKEDREFS_FILE_MODE ) ) < 0 )
955980 goto fail ;
956981
957982 /* Packfiles have a header... apparently
958983 * This is in fact not required, but we might as well print it
959984 * just for kicks */
960- if (git_filebuf_printf (& pack_file , "%s\n" , GIT_PACKEDREFS_HEADER ) < 0 )
985+ if (( error = git_filebuf_printf (& pack_file , "%s\n" , GIT_PACKEDREFS_HEADER ) ) < 0 )
961986 goto fail ;
962987
963988 for (i = 0 ; i < git_sortedcache_entrycount (refcache ); ++ i ) {
964989 struct packref * ref = git_sortedcache_entry (refcache , i );
965990 assert (ref );
966991
967- if (packed_find_peel (backend , ref ) < 0 )
992+ if (( error = packed_find_peel (backend , ref ) ) < 0 )
968993 goto fail ;
969994
970- if (packed_write_ref (ref , & pack_file ) < 0 )
995+ if (( error = packed_write_ref (ref , & pack_file ) ) < 0 )
971996 goto fail ;
972997 }
973998
974999 /* if we've written all the references properly, we can commit
9751000 * the packfile to make the changes effective */
976- if (git_filebuf_commit (& pack_file ) < 0 )
1001+ if (( error = git_filebuf_commit (& pack_file ) ) < 0 )
9771002 goto fail ;
9781003
9791004 /* when and only when the packfile has been properly written,
9801005 * we can go ahead and remove the loose refs */
981- if (packed_remove_loose (backend ) < 0 )
1006+ if (( error = packed_remove_loose (backend ) ) < 0 )
9821007 goto fail ;
9831008
9841009 git_sortedcache_updated (refcache );
@@ -991,7 +1016,7 @@ static int packed_write(refdb_fs_backend *backend)
9911016 git_filebuf_cleanup (& pack_file );
9921017 git_sortedcache_wunlock (refcache );
9931018
994- return -1 ;
1019+ return error ;
9951020}
9961021
9971022static int reflog_append (refdb_fs_backend * backend , const git_reference * ref , const git_oid * old , const git_oid * new , const git_signature * author , const char * message );
@@ -1143,8 +1168,7 @@ static int refdb_fs_backend__write(
11431168
11441169 assert (backend );
11451170
1146- error = reference_path_available (backend , ref -> name , NULL , force );
1147- if (error < 0 )
1171+ if ((error = reference_path_available (backend , ref -> name , NULL , force )) < 0 )
11481172 return error ;
11491173
11501174 /* We need to perform the reflog append and old value check under the ref's lock */
@@ -1260,15 +1284,14 @@ static int refdb_fs_backend__delete_tail(
12601284 if (git_buf_joinpath (& loose_path , backend -> path , ref_name ) < 0 )
12611285 return -1 ;
12621286
1263- if (git_path_isfile (loose_path .ptr )) {
1264- error = p_unlink (loose_path .ptr );
1265- loose_deleted = 1 ;
1266- }
1267-
1268- git_buf_free (& loose_path );
12691287
1270- if (error != 0 )
1288+ error = p_unlink (loose_path .ptr );
1289+ if (error < 0 && errno == ENOENT )
1290+ error = 0 ;
1291+ else if (error < 0 )
12711292 goto cleanup ;
1293+ else if (error == 0 )
1294+ loose_deleted = 1 ;
12721295
12731296 if ((error = packed_reload (backend )) < 0 )
12741297 goto cleanup ;
@@ -1291,6 +1314,7 @@ static int refdb_fs_backend__delete_tail(
12911314 error = packed_write (backend );
12921315
12931316cleanup :
1317+ git_buf_free (& loose_path );
12941318 git_filebuf_cleanup (file );
12951319
12961320 return error ;
@@ -1362,14 +1386,15 @@ static int refdb_fs_backend__rename(
13621386
13631387static int refdb_fs_backend__compress (git_refdb_backend * _backend )
13641388{
1389+ int error ;
13651390 refdb_fs_backend * backend = (refdb_fs_backend * )_backend ;
13661391
13671392 assert (backend );
13681393
1369- if (packed_reload (backend ) < 0 || /* load the existing packfile */
1370- packed_loadloose (backend ) < 0 || /* add all the loose refs */
1371- packed_write (backend ) < 0 ) /* write back to disk */
1372- return -1 ;
1394+ if (( error = packed_reload (backend ) ) < 0 || /* load the existing packfile */
1395+ ( error = packed_loadloose (backend ) ) < 0 || /* add all the loose refs */
1396+ ( error = packed_write (backend ) ) < 0 ) /* write back to disk */
1397+ return error ;
13731398
13741399 return 0 ;
13751400}
@@ -1789,9 +1814,10 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co
17891814 * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
17901815 */
17911816 if (git_path_isdir (git_buf_cstr (& path ))) {
1792- if ((git_futils_rmdir_r (git_buf_cstr (& path ), NULL , GIT_RMDIR_SKIP_NONEMPTY ) < 0 ))
1793- error = -1 ;
1794- else if (git_path_isdir (git_buf_cstr (& path ))) {
1817+ if ((error = git_futils_rmdir_r (git_buf_cstr (& path ), NULL , GIT_RMDIR_SKIP_NONEMPTY )) < 0 ) {
1818+ if (error == GIT_ENOTFOUND )
1819+ error = 0 ;
1820+ } else if (git_path_isdir (git_buf_cstr (& path ))) {
17951821 giterr_set (GITERR_REFERENCE , "cannot create reflog at '%s', there are reflogs beneath that folder" ,
17961822 ref -> name );
17971823 error = GIT_EDIRECTORY ;
0 commit comments