On Sat, Nov 14, 2020 at 01:00:00PM +0300, Alexander Lakhin wrote:
> As noted in [1], a sensible solution would be putting the same "retry on
> ERROR_ACCESS_DENIED" action in a wrapper for stat().
> And bed90759f brought in master the _pgstat64() function, where such
> error handling should be placed.
> So please look at the patch (based on the previous one made to fix
> bug#16161), that makes the attached test pass.

Your patch introduces a "loops", but doesn't use it to escape the loop.

> diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
> index d34182a7b0..922df49125 100644
> --- a/src/backend/utils/adt/genfile.c
> +++ b/src/backend/utils/adt/genfile.c
> @@ -28,6 +28,7 @@
>  #include "funcapi.h"
>  #include "mb/pg_wchar.h"
>  #include "miscadmin.h"
> +#include "port.h"
>  #include "postmaster/syslogger.h"
>  #include "storage/fd.h"
>  #include "utils/acl.h"
> diff --git a/src/port/win32stat.c b/src/port/win32stat.c
> index 4351aa4d08..c2b55c7fa6 100644
> --- a/src/port/win32stat.c
> +++ b/src/port/win32stat.c
> @@ -170,6 +170,7 @@ _pgstat64(const char *name, struct stat *buf)
>       SECURITY_ATTRIBUTES sa;
>       HANDLE          hFile;
>       int                     ret;
> +     int                     loops = 0;
>  #if _WIN32_WINNT < 0x0600
>       IO_STATUS_BLOCK ioStatus;
>       FILE_STANDARD_INFORMATION standardInfo;
> @@ -182,31 +183,60 @@ _pgstat64(const char *name, struct stat *buf)
>               errno = EINVAL;
>               return -1;
>       }
> -
>       /* fast not-exists check */
>       if (GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES)
>       {
> -             _dosmaperr(GetLastError());
> -             return -1;
> +             DWORD           err = GetLastError();
> +
> +             if (err != ERROR_ACCESS_DENIED) {
> +                     _dosmaperr(err);
> +                     return -1;
> +             }
>       }
>  
>       /* get a file handle as lightweight as we can */
>       sa.nLength = sizeof(SECURITY_ATTRIBUTES);
>       sa.bInheritHandle = TRUE;
>       sa.lpSecurityDescriptor = NULL;
> -     hFile = CreateFile(name,
> -                                        GENERIC_READ,
> -                                        (FILE_SHARE_READ | FILE_SHARE_WRITE 
> | FILE_SHARE_DELETE),
> -                                        &sa,
> -                                        OPEN_EXISTING,
> -                                        (FILE_FLAG_NO_BUFFERING | 
> FILE_FLAG_BACKUP_SEMANTICS |
> -                                             FILE_FLAG_OVERLAPPED),
> -                                        NULL);
> -     if (hFile == INVALID_HANDLE_VALUE)
> +     while ((hFile = CreateFile(name,
> +                                                        GENERIC_READ,
> +                                                        (FILE_SHARE_READ | 
> FILE_SHARE_WRITE | FILE_SHARE_DELETE),
> +                                                        &sa,
> +                                                        OPEN_EXISTING,
> +                                                        
> (FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS |
> +                                                        
> FILE_FLAG_OVERLAPPED),
> +                                                        NULL)) == 
> INVALID_HANDLE_VALUE)
>       {
>               DWORD           err = GetLastError();
>  
> -             CloseHandle(hFile);
> +             /*
> +              * ERROR_ACCESS_DENIED is returned if the file is deleted but 
> not yet
> +              * gone (Windows NT status code is STATUS_DELETE_PENDING).  In 
> that
> +              * case we want to wait a bit and try again, giving up after 1 
> second
> +              * (since this condition should never persist very long).  
> However,
> +              * there are other commonly-hit cases that return 
> ERROR_ACCESS_DENIED,
> +              * so care is needed.  In particular that happens if we try to 
> open a
> +              * directory, or of course if there's an actual file-permissions
> +              * problem.  To distinguish these cases, try a stat().  In the
> +              * delete-pending case, it will either also get 
> STATUS_DELETE_PENDING,
> +              * or it will see the file as gone and fail with ENOENT.  In 
> other
> +              * cases it will usually succeed.  The only somewhat-likely 
> case where
> +              * this coding will uselessly wait is if there's a permissions 
> problem
> +              * with a containing directory, which we hope will never happen 
> in any
> +              * performance-critical code paths.
> +              */
> +             if (err == ERROR_ACCESS_DENIED)
> +             {
> +                     struct microsoft_native_stat st;
> +
> +                     if (microsoft_native_stat(name, &st) != 0)
> +                     {
> +                             pg_usleep(100000);
> +                             loops++;
> +                             continue;
> +                     }
> +             }
> +
>               _dosmaperr(err);
>               return -1;
>       }


Reply via email to