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 <conio.h>
|
||||
#include "../strbuf.h"
|
||||
#include "../run-command.h"
|
||||
|
||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||
|
||||
int err_win_to_posix(DWORD winerr)
|
||||
{
|
||||
@ -116,6 +119,165 @@ int err_win_to_posix(DWORD winerr)
|
||||
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
|
||||
int mingw_open (const char *filename, int oflags, ...)
|
||||
{
|
||||
@ -1249,7 +1411,6 @@ int mingw_rename(const char *pold, const char *pnew)
|
||||
{
|
||||
DWORD attrs, gle;
|
||||
int tries = 0;
|
||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||
|
||||
/*
|
||||
* Try native rename() first to get errno right.
|
||||
@ -1291,6 +1452,11 @@ repeat:
|
||||
tries++;
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
|
@ -119,14 +119,6 @@ static inline int mingw_mkdir(const char *path, int mode)
|
||||
}
|
||||
#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
|
||||
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
|
||||
*/
|
||||
|
||||
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, ...);
|
||||
#define open mingw_open
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user