Vadim Zeitlin <vz-git@xxxxxxxxxxxx> writes: > From: Vadim Zeitlin <vz-git@xxxxxxxxxxxx> > Subject: [PATCH] fetch: allow running as different users in shared repositories This pretends the change to affect ONLY "git fetch", but ... > The function fopen_for_writing(), which was added in 79d7582e32 (commit: > allow editing the commit message even in shared repos, 2016-01-06) and > used for overwriting FETCH_HEAD since ea56518dfe (Handle more file > writes correctly in shared repos, 2016-01-11), didn't do it correctly in > shared repositories under Linux. ... fopen_for_writing() is not only about FETCH_HEAD. In fact, the author of this patch knows "git fetch" was not the primary target. So, we need to make sure that (1) this change is beneficial to those other codepaths that use the helper function, and (2) describe the (good) effect of the patch on these other users in the log message. We also need to retitle the commit. Hits from "git grep fopen_for_writing" are builtin/commit.c:812: s->fp = fopen_for_writing(git_path_commit_editmsg()); That's .git/COMMIT_EDITMSG file. builtin/fast-export.c:1049: f = fopen_for_writing(file); This is inside export_marks() to create the marks file. builtin/fetch.c:1191: FILE *fp = fopen_for_writing(filename); This is the .git/FETCH_HEAD. > This happened because in this situation the file FETCH_HEAD has mode 644 > and attempting to overwrite it when running git-fetch under an account > different from the one that was had originally created it, failed with > EACCES, and not EPERM. Isn't that because FETCH_HEAD and others are not concluded with adjust_shared_perm()? The fopen_for_writing() that removes and recreates the target file sounds like a band-aid to me. The right fix we should have done when we did 79d7582e (commit: allow editing the commit message even in shared repos, 2016-01-06) would have been to open(2) with 0666 (and let the umask(2) adjust it), and then use adjust_shared_perm() to give it the desired protection bits. With the existing band-aid, we won't be able to fix incorrectly created append-only files, for example, as the band-aid depends on the contents in the existing file being expendable. Having said all that, I agree that EACCES is the right errno to detect for this band-aid, at least for FETCH_HEAD. I think COMMIT_EDITMSG is also left after "git commit" finishes, so it will share the same issue with FETCH_HEAD and the same fix should apply (this is just a hint for you to write an updated proposed log message for the patch). I haven't looked at or analysed how fast-export will get affected. I think it is used to create and leave a "marks" file, to be later read by another instance of the fast-export process, which may (or may not) further write new contents to the (same?) "marks" file, but I do not know the ramifications of unlinking and recreating. In any case, even if that is broken, it is not a new breakage this patch is introducing. You may want to look at it further to make sure you are not breaking things, though. So, here are the things I would like to see in this area: - The same patch text, but with updated commit log message, to tell readers that we have looked at all the callers that are affected, and retitle it (e.g. "fopen_for_writing: detect the reason why fopen() failed correctly" or something like that, perhaps?). - Audit other codepaths that create .git/ALL_CAPS_FILE (e.g. I see that "git branch --edit-description" creates a temporary file to edit without fopen_for_writing() band-aid and it does not use adjust_shared_perm(), but I think it should) and fix them. - The existing repositories have these files created and left whose permission bits were set according to the then-current umask without taking "core.sharedrepository" into account, so we have to keep the "if unable to open for writing, unlink and recreate" trick to salvage them. But it does not mean we need to keep creating the files with wrong mode. Update fopen_for_writing() and its users to leave the file created in the right mode by calling adjust_shared_perm(). I think fopen_for_writing() should switch from calling fopen(3) to calling open(2) and then fdopen(3) on the result as the first step. The first one is better done by you to tie the loose ends for this discussion. Other two items do not have to be done by you. Anybody interested can do them as a clean-up (only if people agree that it is a good idea to do so---so I won't mark this as a left-over-bits yet). Thanks. > diff --git a/wrapper.c b/wrapper.c > index e1eaef2e16..f5607241da 100644 > --- a/wrapper.c > +++ b/wrapper.c > @@ -373,11 +373,12 @@ FILE *fopen_for_writing(const char *path) > { > FILE *ret = fopen(path, "w"); > > - if (!ret && errno == EPERM) { > + if (!ret && (errno == EACCES || errno == EPERM)) { > + int open_error = errno; > if (!unlink(path)) > ret = fopen(path, "w"); > else > - errno = EPERM; > + errno = open_error; > } > return ret; > }