mirror of
https://github.com/git/git
synced 2024-09-13 21:34:42 +00:00
Merge branch 'hv/mingw-fs-funnies'
* hv/mingw-fs-funnies: mingw_rmdir: set errno=ENOTEMPTY when appropriate mingw: add fallback for rmdir in case directory is in use mingw: make failures to unlink or move raise a question mingw: work around irregular failures of unlink on windows mingw: move unlink wrapper to mingw.c
This commit is contained in:
commit
70ae2beadd
168
compat/mingw.c
168
compat/mingw.c
|
@ -2,6 +2,9 @@
|
||||||
#include "win32.h"
|
#include "win32.h"
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include "../strbuf.h"
|
#include "../strbuf.h"
|
||||||
|
#include "../run-command.h"
|
||||||
|
|
||||||
|
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||||
|
|
||||||
int err_win_to_posix(DWORD winerr)
|
int err_win_to_posix(DWORD winerr)
|
||||||
{
|
{
|
||||||
|
@ -116,6 +119,165 @@ int err_win_to_posix(DWORD winerr)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int is_file_in_use_error(DWORD errcode)
|
||||||
|
{
|
||||||
|
switch (errcode) {
|
||||||
|
case ERROR_SHARING_VIOLATION:
|
||||||
|
case ERROR_ACCESS_DENIED:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_yes_no_answer(void)
|
||||||
|
{
|
||||||
|
char answer[1024];
|
||||||
|
|
||||||
|
if (fgets(answer, sizeof(answer), stdin)) {
|
||||||
|
size_t answer_len = strlen(answer);
|
||||||
|
int got_full_line = 0, c;
|
||||||
|
|
||||||
|
/* remove the newline */
|
||||||
|
if (answer_len >= 2 && answer[answer_len-2] == '\r') {
|
||||||
|
answer[answer_len-2] = '\0';
|
||||||
|
got_full_line = 1;
|
||||||
|
} else if (answer_len >= 1 && answer[answer_len-1] == '\n') {
|
||||||
|
answer[answer_len-1] = '\0';
|
||||||
|
got_full_line = 1;
|
||||||
|
}
|
||||||
|
/* flush the buffer in case we did not get the full line */
|
||||||
|
if (!got_full_line)
|
||||||
|
while ((c = getchar()) != EOF && c != '\n')
|
||||||
|
;
|
||||||
|
} else
|
||||||
|
/* we could not read, return the
|
||||||
|
* default answer which is no */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (tolower(answer[0]) == 'y' && !answer[1])
|
||||||
|
return 1;
|
||||||
|
if (!strncasecmp(answer, "yes", sizeof(answer)))
|
||||||
|
return 1;
|
||||||
|
if (tolower(answer[0]) == 'n' && !answer[1])
|
||||||
|
return 0;
|
||||||
|
if (!strncasecmp(answer, "no", sizeof(answer)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* did not find an answer we understand */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ask_yes_no_if_possible(const char *format, ...)
|
||||||
|
{
|
||||||
|
char question[4096];
|
||||||
|
const char *retry_hook[] = { NULL, NULL, NULL };
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(question, sizeof(question), format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if ((retry_hook[0] = getenv("GIT_ASK_YESNO"))) {
|
||||||
|
retry_hook[1] = question;
|
||||||
|
return !run_command_v_opt(retry_hook, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int answer;
|
||||||
|
fprintf(stderr, "%s (y/n) ", question);
|
||||||
|
|
||||||
|
if ((answer = read_yes_no_answer()) >= 0)
|
||||||
|
return answer;
|
||||||
|
|
||||||
|
fprintf(stderr, "Sorry, I did not understand your answer. "
|
||||||
|
"Please type 'y' or 'n'\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef unlink
|
||||||
|
int mingw_unlink(const char *pathname)
|
||||||
|
{
|
||||||
|
int ret, tries = 0;
|
||||||
|
|
||||||
|
/* read-only files cannot be removed */
|
||||||
|
chmod(pathname, 0666);
|
||||||
|
while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
||||||
|
if (!is_file_in_use_error(GetLastError()))
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
* We assume that some other process had the source or
|
||||||
|
* destination file open at the wrong moment and retry.
|
||||||
|
* In order to give the other process a higher chance to
|
||||||
|
* complete its operation, we give up our time slice now.
|
||||||
|
* If we have to retry again, we do sleep a bit.
|
||||||
|
*/
|
||||||
|
Sleep(delay[tries]);
|
||||||
|
tries++;
|
||||||
|
}
|
||||||
|
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
|
||||||
|
ask_yes_no_if_possible("Unlink of file '%s' failed. "
|
||||||
|
"Should I try again?", pathname))
|
||||||
|
ret = unlink(pathname);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_dir_empty(const char *path)
|
||||||
|
{
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
WIN32_FIND_DATAA findbuf;
|
||||||
|
HANDLE handle;
|
||||||
|
|
||||||
|
strbuf_addf(&buf, "%s\\*", path);
|
||||||
|
handle = FindFirstFileA(buf.buf, &findbuf);
|
||||||
|
if (handle == INVALID_HANDLE_VALUE) {
|
||||||
|
strbuf_release(&buf);
|
||||||
|
return GetLastError() == ERROR_NO_MORE_FILES;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!strcmp(findbuf.cFileName, ".") ||
|
||||||
|
!strcmp(findbuf.cFileName, ".."))
|
||||||
|
if (!FindNextFile(handle, &findbuf)) {
|
||||||
|
strbuf_release(&buf);
|
||||||
|
return GetLastError() == ERROR_NO_MORE_FILES;
|
||||||
|
}
|
||||||
|
FindClose(handle);
|
||||||
|
strbuf_release(&buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef rmdir
|
||||||
|
int mingw_rmdir(const char *pathname)
|
||||||
|
{
|
||||||
|
int ret, tries = 0;
|
||||||
|
|
||||||
|
while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
||||||
|
if (!is_file_in_use_error(GetLastError()))
|
||||||
|
break;
|
||||||
|
if (!is_dir_empty(pathname)) {
|
||||||
|
errno = ENOTEMPTY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We assume that some other process had the source or
|
||||||
|
* destination file open at the wrong moment and retry.
|
||||||
|
* In order to give the other process a higher chance to
|
||||||
|
* complete its operation, we give up our time slice now.
|
||||||
|
* If we have to retry again, we do sleep a bit.
|
||||||
|
*/
|
||||||
|
Sleep(delay[tries]);
|
||||||
|
tries++;
|
||||||
|
}
|
||||||
|
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
|
||||||
|
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
|
||||||
|
"Should I try again?", pathname))
|
||||||
|
ret = rmdir(pathname);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#undef open
|
#undef open
|
||||||
int mingw_open (const char *filename, int oflags, ...)
|
int mingw_open (const char *filename, int oflags, ...)
|
||||||
{
|
{
|
||||||
|
@ -1249,7 +1411,6 @@ int mingw_rename(const char *pold, const char *pnew)
|
||||||
{
|
{
|
||||||
DWORD attrs, gle;
|
DWORD attrs, gle;
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try native rename() first to get errno right.
|
* Try native rename() first to get errno right.
|
||||||
|
@ -1291,6 +1452,11 @@ int mingw_rename(const char *pold, const char *pnew)
|
||||||
tries++;
|
tries++;
|
||||||
goto repeat;
|
goto repeat;
|
||||||
}
|
}
|
||||||
|
if (gle == ERROR_ACCESS_DENIED &&
|
||||||
|
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
|
||||||
|
"Should I try again?", pold, pnew))
|
||||||
|
goto repeat;
|
||||||
|
|
||||||
errno = EACCES;
|
errno = EACCES;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,14 +119,6 @@ static inline int mingw_mkdir(const char *path, int mode)
|
||||||
}
|
}
|
||||||
#define mkdir mingw_mkdir
|
#define mkdir mingw_mkdir
|
||||||
|
|
||||||
static inline int mingw_unlink(const char *pathname)
|
|
||||||
{
|
|
||||||
/* read-only files cannot be removed */
|
|
||||||
chmod(pathname, 0666);
|
|
||||||
return unlink(pathname);
|
|
||||||
}
|
|
||||||
#define unlink mingw_unlink
|
|
||||||
|
|
||||||
#define WNOHANG 1
|
#define WNOHANG 1
|
||||||
pid_t waitpid(pid_t pid, int *status, unsigned options);
|
pid_t waitpid(pid_t pid, int *status, unsigned options);
|
||||||
|
|
||||||
|
@ -174,6 +166,12 @@ int link(const char *oldpath, const char *newpath);
|
||||||
* replacements of existing functions
|
* replacements of existing functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
int mingw_unlink(const char *pathname);
|
||||||
|
#define unlink mingw_unlink
|
||||||
|
|
||||||
|
int mingw_rmdir(const char *path);
|
||||||
|
#define rmdir mingw_rmdir
|
||||||
|
|
||||||
int mingw_open (const char *filename, int oflags, ...);
|
int mingw_open (const char *filename, int oflags, ...);
|
||||||
#define open mingw_open
|
#define open mingw_open
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue