From 913e96c7a1c138973912eb4b133a27796fd59e2e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 15 Jan 2014 12:07:06 -0600 Subject: [PATCH] win32_apply.c: Simplify and fix win32_create_file() win32_create_file() can be simplified by using CREATE_NEW and attempting to delete the existing file if it exists. This commit also adds FILE_FLAG_OPEN_REPARSE_POINT to the CreateFile() call. This is the equivalent to the O_NOFOLLOW addition to the UNIX version in the previous commit. Note that MS has contradictory documentation for the combination of CREATE_ALWAYS and FILE_FLAG_OPEN_REPARSE_POINT, which further supports the CREATE_NEW method changed to in this commit. --- src/win32_apply.c | 123 ++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 71 deletions(-) diff --git a/src/win32_apply.c b/src/win32_apply.c index df74519d..af8a4ef9 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -81,58 +81,68 @@ win32_start_extract(const wchar_t *path, struct apply_ctx *ctx) return 0; } +/* Delete a non-directory file, working around Windows quirks. */ +static BOOL +win32_delete_file_wrapper(const wchar_t *path) +{ + DWORD err; + DWORD attrib; + + if (DeleteFile(path)) + return TRUE; + + err = GetLastError(); + attrib = GetFileAttributes(path); + if ((attrib != INVALID_FILE_ATTRIBUTES) && + (attrib & FILE_ATTRIBUTE_READONLY)) + { + /* Try again with FILE_ATTRIBUTE_READONLY cleared. */ + attrib &= ~FILE_ATTRIBUTE_READONLY; + if (SetFileAttributes(path, attrib)) { + if (DeleteFile(path)) + return TRUE; + else + err = GetLastError(); + } + } + + SetLastError(err); + return FALSE; +} + + /* Create a normal file, overwriting one already present. */ static int win32_create_file(const wchar_t *path, struct apply_ctx *ctx, u64 *cookie_ret) { HANDLE h; - unsigned retry_count = 0; - DWORD dwFlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS; + /* Notes: + * + * WRITE_OWNER and WRITE_DAC privileges are required for some reason, + * even through we're creating a new file. + * + * FILE_FLAG_OPEN_REPARSE_POINT is required to prevent an existing + * reparse point from redirecting the creation of the new file + * (potentially to an arbitrary location). + * + * CREATE_ALWAYS could be used instead of CREATE_NEW. However, there + * are quirks that would need to be handled (e.g. having to set + * FILE_ATTRIBUTE_HIDDEN and/or FILE_ATTRIBUTE_SYSTEM if the existing + * file had them specified, and/or having to clear + * FILE_ATTRIBUTE_READONLY on the existing file). It's simpler to just + * call win32_delete_file_wrapper() to delete the existing file in such + * a way that already handles the FILE_ATTRIBUTE_READONLY quirk. + */ retry: - /* WRITE_OWNER and WRITE_DAC privileges are required for some reason, - * even through we're creating a new file. */ - h = CreateFile(path, WRITE_OWNER | WRITE_DAC, 0, NULL, - CREATE_ALWAYS, dwFlagsAndAttributes, NULL); + h = CreateFile(path, WRITE_OWNER | WRITE_DAC, 0, NULL, CREATE_NEW, + FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OPEN_REPARSE_POINT, NULL); if (h == INVALID_HANDLE_VALUE) { - /* File couldn't be created. */ DWORD err = GetLastError(); - if (err == ERROR_ACCESS_DENIED && retry_count == 0) { - /* Access denied error for the first time. Try - * adjusting file attributes. */ - - /* Get attributes of the existing file. */ - DWORD attribs = GetFileAttributes(path); - if (attribs != INVALID_FILE_ATTRIBUTES && - (attribs & (FILE_ATTRIBUTE_HIDDEN | - FILE_ATTRIBUTE_SYSTEM | - FILE_ATTRIBUTE_READONLY))) - { - /* If the existing file has - * FILE_ATTRIBUTE_HIDDEN and/or - * FILE_ATTRIBUTE_SYSTEM, they must be set in - * the call to CreateFile(). This is true even - * when FILE_ATTRIBUTE_NORMAL was not specified, - * contrary to the MS "documentation". */ - dwFlagsAndAttributes |= (attribs & - (FILE_ATTRIBUTE_HIDDEN | - FILE_ATTRIBUTE_SYSTEM)); - /* If the existing file has - * FILE_ATTRIBUTE_READONLY, it must be cleared - * before attempting to create a new file over - * it. This is true even when the process has - * the SE_RESTORE_NAME privilege and requested - * the FILE_FLAG_BACKUP_SEMANTICS flag to - * CreateFile(). */ - if (attribs & FILE_ATTRIBUTE_READONLY) { - SetFileAttributes(path, - attribs & ~FILE_ATTRIBUTE_READONLY); - } - retry_count++; - goto retry; - } - } + if (err == ERROR_FILE_EXISTS && win32_delete_file_wrapper(path)) + goto retry; set_errno_from_win32_error(err); return WIMLIB_ERR_OPEN; } @@ -154,35 +164,6 @@ error: return WIMLIB_ERR_MKDIR; } -/* Delete a non-directory file, working around Windows quirks. */ -static BOOL -win32_delete_file_wrapper(const wchar_t *path) -{ - DWORD err; - DWORD attrib; - - if (DeleteFile(path)) - return TRUE; - - err = GetLastError(); - attrib = GetFileAttributes(path); - if ((attrib != INVALID_FILE_ATTRIBUTES) && - (attrib & FILE_ATTRIBUTE_READONLY)) - { - /* Try again with FILE_ATTRIBUTE_READONLY cleared. */ - attrib &= ~FILE_ATTRIBUTE_READONLY; - if (SetFileAttributes(path, attrib)) { - if (DeleteFile(path)) - return TRUE; - else - err = GetLastError(); - } - } - - SetLastError(err); - return FALSE; -} - static int win32_create_hardlink(const wchar_t *oldpath, const wchar_t *newpath, struct apply_ctx *ctx) -- 2.43.0