From 1f8a66f1991dac0158642a9ac2d531714078ecc6 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:39 +0300 Subject: Win32: non-ASCII names support in autoindex (ticket #458). Notably, ngx_open_dir() now supports opening directories with non-ASCII characters, and directory entries returned by ngx_read_dir() are properly converted to UTF-8. --- src/os/win32/ngx_files.c | 255 +++++++++++++++++++++++++++++++++++++++++++---- src/os/win32/ngx_files.h | 10 +- 2 files changed, 244 insertions(+), 21 deletions(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 3017b45fe..cef677e5d 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -13,7 +13,11 @@ static ngx_int_t ngx_win32_check_filename(u_char *name, u_short *u, size_t len); -static u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len); +static u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len, + size_t reserved); +static u_char *ngx_utf16_to_utf8(u_char *utf8, u_short *utf16, size_t *len, + size_t *allocated); +uint32_t ngx_utf16_decode(u_short **u, size_t n); /* FILE_FLAG_BACKUP_SEMANTICS allows to obtain a handle to a directory */ @@ -28,7 +32,7 @@ ngx_open_file(u_char *name, u_long mode, u_long create, u_long access) u_short utf16[NGX_UTF16_BUFLEN]; len = NGX_UTF16_BUFLEN; - u = ngx_utf8_to_utf16(utf16, name, &len); + u = ngx_utf8_to_utf16(utf16, name, &len, 0); if (u == NULL) { return INVALID_HANDLE_VALUE; @@ -269,7 +273,7 @@ ngx_file_info(u_char *file, ngx_file_info_t *sb) len = NGX_UTF16_BUFLEN; - u = ngx_utf8_to_utf16(utf16, file, &len); + u = ngx_utf8_to_utf16(utf16, file, &len, 0); if (u == NULL) { return NGX_FILE_ERROR; @@ -427,58 +431,109 @@ ngx_realpath(u_char *path, u_char *resolved) ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) { - u_char *pattern, *p; + size_t len; + u_short *u, *p; ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN - 2; + u = ngx_utf8_to_utf16(utf16, name->data, &len, 2); - pattern = malloc(name->len + 3); - if (pattern == NULL) { + if (u == NULL) { return NGX_ERROR; } - p = ngx_cpymem(pattern, name->data, name->len); + if (ngx_win32_check_filename(name->data, u, len) != NGX_OK) { + goto failed; + } + + p = &u[len - 1]; *p++ = '/'; *p++ = '*'; *p = '\0'; - dir->dir = FindFirstFile((const char *) pattern, &dir->finddata); + dir->dir = FindFirstFileW(u, &dir->finddata); if (dir->dir == INVALID_HANDLE_VALUE) { - err = ngx_errno; - ngx_free(pattern); - ngx_set_errno(err); - return NGX_ERROR; + goto failed; } - ngx_free(pattern); + if (u != utf16) { + ngx_free(u); + } dir->valid_info = 1; dir->ready = 1; + dir->name = NULL; + dir->allocated = 0; return NGX_OK; + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return NGX_ERROR; } ngx_int_t ngx_read_dir(ngx_dir_t *dir) { + u_char *name; + size_t len, allocated; + if (dir->ready) { dir->ready = 0; - return NGX_OK; + goto convert; } - if (FindNextFile(dir->dir, &dir->finddata) != 0) { + if (FindNextFileW(dir->dir, &dir->finddata) != 0) { dir->type = 1; - return NGX_OK; + goto convert; } return NGX_ERROR; + +convert: + + name = dir->name; + len = dir->allocated; + + name = ngx_utf16_to_utf8(name, dir->finddata.cFileName, &len, &allocated); + + if (name == NULL) { + return NGX_ERROR; + } + + if (name != dir->name) { + + if (dir->name) { + ngx_free(dir->name); + } + + dir->name = name; + dir->allocated = allocated; + } + + dir->namelen = len - 1; + + return NGX_OK; } ngx_int_t ngx_close_dir(ngx_dir_t *dir) { + if (dir->name) { + ngx_free(dir->name); + } + if (FindClose(dir->dir) == 0) { return NGX_ERROR; } @@ -816,7 +871,7 @@ failed: static u_short * -ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len) +ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len, size_t reserved) { u_char *p; u_short *u, *last; @@ -865,7 +920,7 @@ ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len) /* the given buffer is not enough, allocate a new one */ - u = malloc(((p - utf8) + ngx_strlen(p) + 1) * sizeof(u_short)); + u = malloc(((p - utf8) + ngx_strlen(p) + 1 + reserved) * sizeof(u_short)); if (u == NULL) { return NULL; } @@ -910,3 +965,167 @@ ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len) /* unreachable */ } + + +static u_char * +ngx_utf16_to_utf8(u_char *utf8, u_short *utf16, size_t *len, size_t *allocated) +{ + u_char *p, *last; + u_short *u, *j; + uint32_t n; + + u = utf16; + p = utf8; + last = utf8 + *len; + + while (p < last) { + + if (*u < 0x80) { + *p++ = (u_char) *u; + + if (*u == 0) { + *len = p - utf8; + return utf8; + } + + u++; + + continue; + } + + if (p >= last - 4) { + *len = p - utf8; + break; + } + + n = ngx_utf16_decode(&u, 2); + + if (n > 0x10ffff) { + ngx_set_errno(NGX_EILSEQ); + return NULL; + } + + if (n >= 0x10000) { + *p++ = (u_char) (0xf0 + (n >> 18)); + *p++ = (u_char) (0x80 + ((n >> 12) & 0x3f)); + *p++ = (u_char) (0x80 + ((n >> 6) & 0x3f)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + continue; + } + + if (n >= 0x0800) { + *p++ = (u_char) (0xe0 + (n >> 12)); + *p++ = (u_char) (0x80 + ((n >> 6) & 0x3f)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + continue; + } + + *p++ = (u_char) (0xc0 + (n >> 6)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + } + + /* the given buffer is not enough, allocate a new one */ + + for (j = u; *j; j++) { /* void */ } + + p = malloc((j - utf16) * 4 + 1); + if (p == NULL) { + return NULL; + } + + if (allocated) { + *allocated = (j - utf16) * 4 + 1; + } + + ngx_memcpy(p, utf8, *len); + + utf8 = p; + p += *len; + + for ( ;; ) { + + if (*u < 0x80) { + *p++ = (u_char) *u; + + if (*u == 0) { + *len = p - utf8; + return utf8; + } + + u++; + + continue; + } + + n = ngx_utf16_decode(&u, 2); + + if (n > 0x10ffff) { + ngx_free(utf8); + ngx_set_errno(NGX_EILSEQ); + return NULL; + } + + if (n >= 0x10000) { + *p++ = (u_char) (0xf0 + (n >> 18)); + *p++ = (u_char) (0x80 + ((n >> 12) & 0x3f)); + *p++ = (u_char) (0x80 + ((n >> 6) & 0x3f)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + continue; + } + + if (n >= 0x0800) { + *p++ = (u_char) (0xe0 + (n >> 12)); + *p++ = (u_char) (0x80 + ((n >> 6) & 0x3f)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + continue; + } + + *p++ = (u_char) (0xc0 + (n >> 6)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + } + + /* unreachable */ +} + + +/* + * ngx_utf16_decode() decodes one or two UTF-16 code units + * the return values: + * 0x80 - 0x10ffff valid character + * 0x110000 - 0xfffffffd invalid sequence + * 0xfffffffe incomplete sequence + * 0xffffffff error + */ + +uint32_t +ngx_utf16_decode(u_short **u, size_t n) +{ + uint32_t k, m; + + k = **u; + + if (k < 0xd800 || k > 0xdfff) { + (*u)++; + return k; + } + + if (k > 0xdbff) { + (*u)++; + return 0xffffffff; + } + + if (n < 2) { + return 0xfffffffe; + } + + (*u)++; + + m = *(*u)++; + + if (m < 0xdc00 || m > 0xdfff) { + return 0xffffffff; + + } + + return 0x10000 + ((k - 0xd800) << 10) + (m - 0xdc00); +} diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index a10839ba4..fd197fc9f 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -30,7 +30,11 @@ typedef struct { typedef struct { HANDLE dir; - WIN32_FIND_DATA finddata; + WIN32_FIND_DATAW finddata; + + u_char *name; + size_t namelen; + size_t allocated; unsigned valid_info:1; unsigned type:1; @@ -205,8 +209,8 @@ ngx_int_t ngx_close_dir(ngx_dir_t *dir); #define ngx_dir_access(a) (a) -#define ngx_de_name(dir) ((u_char *) (dir)->finddata.cFileName) -#define ngx_de_namelen(dir) ngx_strlen((dir)->finddata.cFileName) +#define ngx_de_name(dir) (dir)->name +#define ngx_de_namelen(dir) (dir)->namelen ngx_int_t ngx_de_info(u_char *name, ngx_dir_t *dir); #define ngx_de_info_n "dummy()" -- cgit From 99d5ad72a48811c3939a74a54330d7fd5517a5ab Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:41 +0300 Subject: Win32: non-ASCII names support in "include" with wildcards. Notably, ngx_open_glob() now supports opening directories with non-ASCII characters, and pathnames returned by ngx_read_glob() are converted to UTF-8. --- src/os/win32/ngx_files.c | 96 +++++++++++++++++++++++++++++++----------------- src/os/win32/ngx_files.h | 2 +- 2 files changed, 64 insertions(+), 34 deletions(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index cef677e5d..360f14f04 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -10,6 +10,7 @@ #define NGX_UTF16_BUFLEN 256 +#define NGX_UTF8_BUFLEN 512 static ngx_int_t ngx_win32_check_filename(u_char *name, u_short *u, size_t len); @@ -547,14 +548,27 @@ ngx_open_glob(ngx_glob_t *gl) { u_char *p; size_t len; + u_short *u; ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, gl->pattern, &len, 0); + + if (u == NULL) { + return NGX_ERROR; + } - gl->dir = FindFirstFile((const char *) gl->pattern, &gl->finddata); + gl->dir = FindFirstFileW(u, &gl->finddata); if (gl->dir == INVALID_HANDLE_VALUE) { err = ngx_errno; + if (u != utf16) { + ngx_free(u); + } + if ((err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) && gl->test) { @@ -562,6 +576,8 @@ ngx_open_glob(ngx_glob_t *gl) return NGX_OK; } + ngx_set_errno(err); + return NGX_ERROR; } @@ -571,18 +587,10 @@ ngx_open_glob(ngx_glob_t *gl) } } - len = ngx_strlen(gl->finddata.cFileName); - gl->name.len = gl->last + len; - - gl->name.data = ngx_alloc(gl->name.len + 1, gl->log); - if (gl->name.data == NULL) { - return NGX_ERROR; + if (u != utf16) { + ngx_free(u); } - ngx_memcpy(gl->name.data, gl->pattern, gl->last); - ngx_cpystrn(gl->name.data + gl->last, (u_char *) gl->finddata.cFileName, - len + 1); - gl->ready = 1; return NGX_OK; @@ -592,40 +600,25 @@ ngx_open_glob(ngx_glob_t *gl) ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name) { - size_t len; - ngx_err_t err; + u_char *p; + size_t len; + ngx_err_t err; + u_char utf8[NGX_UTF8_BUFLEN]; if (gl->no_match) { return NGX_DONE; } if (gl->ready) { - *name = gl->name; - gl->ready = 0; - return NGX_OK; + goto convert; } ngx_free(gl->name.data); gl->name.data = NULL; - if (FindNextFile(gl->dir, &gl->finddata) != 0) { - - len = ngx_strlen(gl->finddata.cFileName); - gl->name.len = gl->last + len; - - gl->name.data = ngx_alloc(gl->name.len + 1, gl->log); - if (gl->name.data == NULL) { - return NGX_ERROR; - } - - ngx_memcpy(gl->name.data, gl->pattern, gl->last); - ngx_cpystrn(gl->name.data + gl->last, (u_char *) gl->finddata.cFileName, - len + 1); - - *name = gl->name; - - return NGX_OK; + if (FindNextFileW(gl->dir, &gl->finddata) != 0) { + goto convert; } err = ngx_errno; @@ -638,6 +631,43 @@ ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name) "FindNextFile(%s) failed", gl->pattern); return NGX_ERROR; + +convert: + + len = NGX_UTF8_BUFLEN; + p = ngx_utf16_to_utf8(utf8, gl->finddata.cFileName, &len, NULL); + + if (p == NULL) { + return NGX_ERROR; + } + + gl->name.len = gl->last + len - 1; + + gl->name.data = ngx_alloc(gl->name.len + 1, gl->log); + if (gl->name.data == NULL) { + goto failed; + } + + ngx_memcpy(gl->name.data, gl->pattern, gl->last); + ngx_cpystrn(gl->name.data + gl->last, p, len); + + if (p != utf8) { + ngx_free(p); + } + + *name = gl->name; + + return NGX_OK; + +failed: + + if (p != utf8) { + err = ngx_errno; + ngx_free(p); + ngx_set_errno(err); + } + + return NGX_ERROR; } diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index fd197fc9f..a8918e0fd 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -44,7 +44,7 @@ typedef struct { typedef struct { HANDLE dir; - WIN32_FIND_DATA finddata; + WIN32_FIND_DATAW finddata; unsigned ready:1; unsigned test:1; -- cgit From 1edc23cc84a62afdec2286235286a47e1259ef11 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:44 +0300 Subject: Win32: non-ASCII directory names support in ngx_getcwd(). This makes it possible to start nginx without a prefix explicitly set in a directory with non-ASCII characters in it. --- src/os/win32/ngx_files.c | 34 ++++++++++++++++++++++++++++++++++ src/os/win32/ngx_files.h | 6 +++++- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 360f14f04..c09600643 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -429,6 +429,40 @@ ngx_realpath(u_char *path, u_char *resolved) } +size_t +ngx_getcwd(u_char *buf, size_t size) +{ + u_char *p; + size_t n; + u_short utf16[NGX_MAX_PATH]; + + n = GetCurrentDirectoryW(NGX_MAX_PATH, utf16); + + if (n == 0) { + return 0; + } + + if (n > NGX_MAX_PATH) { + ngx_set_errno(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + + p = ngx_utf16_to_utf8(buf, utf16, &size, NULL); + + if (p == NULL) { + return 0; + } + + if (p != buf) { + ngx_free(p); + ngx_set_errno(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + + return size - 1; +} + + ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) { diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index a8918e0fd..bc648e4c9 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -178,8 +178,12 @@ void ngx_close_file_mapping(ngx_file_mapping_t *fm); u_char *ngx_realpath(u_char *path, u_char *resolved); #define ngx_realpath_n "" -#define ngx_getcwd(buf, size) GetCurrentDirectory(size, (char *) buf) + + +size_t ngx_getcwd(u_char *buf, size_t size); #define ngx_getcwd_n "GetCurrentDirectory()" + + #define ngx_path_separator(c) ((c) == '/' || (c) == '\\') #define NGX_HAVE_MAX_PATH 1 -- cgit From 89719dc5c1163a90b789cbac87845f7393655b70 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:45 +0300 Subject: Win32: non-ASCII directory names support in ngx_create_dir(). This makes it possible to create directories under prefix with non-ASCII characters, as well as makes it possible to create directories with non-ASCII characters when using the dav module (ticket #1433). To ensure that the dav module operations are restricted similarly to other file operations (in particular, short names are not allowed), the ngx_win32_check_filename() function is used. It improved to support checking of just dirname, and now can be used to check paths when creating files or directories. --- src/os/win32/ngx_files.c | 80 ++++++++++++++++++++++++++++++++++++++++++------ src/os/win32/ngx_files.h | 2 +- 2 files changed, 72 insertions(+), 10 deletions(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index c09600643..641b65c87 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -12,8 +12,8 @@ #define NGX_UTF16_BUFLEN 256 #define NGX_UTF8_BUFLEN 512 -static ngx_int_t ngx_win32_check_filename(u_char *name, u_short *u, - size_t len); +static ngx_int_t ngx_win32_check_filename(u_short *u, size_t len, + ngx_uint_t dirname); static u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len, size_t reserved); static u_char *ngx_utf16_to_utf8(u_char *utf8, u_short *utf16, size_t *len, @@ -42,7 +42,7 @@ ngx_open_file(u_char *name, u_long mode, u_long create, u_long access) fd = INVALID_HANDLE_VALUE; if (create == NGX_FILE_OPEN - && ngx_win32_check_filename(name, u, len) != NGX_OK) + && ngx_win32_check_filename(u, len, 0) != NGX_OK) { goto failed; } @@ -282,7 +282,7 @@ ngx_file_info(u_char *file, ngx_file_info_t *sb) rc = NGX_FILE_ERROR; - if (ngx_win32_check_filename(file, u, len) != NGX_OK) { + if (ngx_win32_check_filename(u, len, 0) != NGX_OK) { goto failed; } @@ -478,7 +478,7 @@ ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) return NGX_ERROR; } - if (ngx_win32_check_filename(name->data, u, len) != NGX_OK) { + if (ngx_win32_check_filename(u, len, 0) != NGX_OK) { goto failed; } @@ -577,6 +577,42 @@ ngx_close_dir(ngx_dir_t *dir) } +ngx_int_t +ngx_create_dir(u_char *name, ngx_uint_t access) +{ + long rc; + size_t len; + u_short *u; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + + if (ngx_win32_check_filename(u, len, 1) != NGX_OK) { + goto failed; + } + + rc = CreateDirectoryW(u, NULL); + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return rc; +} + + ngx_int_t ngx_open_glob(ngx_glob_t *gl) { @@ -791,11 +827,10 @@ ngx_fs_available(u_char *name) static ngx_int_t -ngx_win32_check_filename(u_char *name, u_short *u, size_t len) +ngx_win32_check_filename(u_short *u, size_t len, ngx_uint_t dirname) { - u_char *p, ch; u_long n; - u_short *lu; + u_short *lu, *p, *slash, ch; ngx_err_t err; enum { sw_start = 0, @@ -808,9 +843,14 @@ ngx_win32_check_filename(u_char *name, u_short *u, size_t len) /* check for NTFS streams (":"), trailing dots and spaces */ lu = NULL; + slash = NULL; state = sw_start; - for (p = name; *p; p++) { +#if (NGX_SUPPRESS_WARN) + ch = 0; +#endif + + for (p = u; *p; p++) { ch = *p; switch (state) { @@ -824,6 +864,7 @@ ngx_win32_check_filename(u_char *name, u_short *u, size_t len) if (ch == '/' || ch == '\\') { state = sw_after_slash; + slash = p; } break; @@ -842,6 +883,7 @@ ngx_win32_check_filename(u_char *name, u_short *u, size_t len) if (ch == '/' || ch == '\\') { state = sw_after_slash; + slash = p; break; } @@ -869,6 +911,7 @@ ngx_win32_check_filename(u_char *name, u_short *u, size_t len) if (ch == '/' || ch == '\\') { state = sw_after_slash; + slash = p; break; } @@ -897,6 +940,12 @@ ngx_win32_check_filename(u_char *name, u_short *u, size_t len) goto invalid; } + if (dirname && slash) { + ch = *slash; + *slash = '\0'; + len = slash - u + 1; + } + /* check if long name match */ lu = malloc(len * 2); @@ -907,6 +956,11 @@ ngx_win32_check_filename(u_char *name, u_short *u, size_t len) n = GetLongPathNameW(u, lu, len); if (n == 0) { + + if (dirname && slash && ngx_errno == NGX_ENOENT) { + ngx_set_errno(NGX_ENOPATH); + } + goto failed; } @@ -914,6 +968,10 @@ ngx_win32_check_filename(u_char *name, u_short *u, size_t len) goto invalid; } + if (dirname && slash) { + *slash = ch; + } + ngx_free(lu); return NGX_OK; @@ -924,6 +982,10 @@ invalid: failed: + if (dirname && slash) { + *slash = ch; + } + if (lu) { err = ngx_errno; ngx_free(lu); diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index bc648e4c9..10a388942 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -202,7 +202,7 @@ ngx_int_t ngx_close_dir(ngx_dir_t *dir); #define ngx_close_dir_n "FindClose()" -#define ngx_create_dir(name, access) CreateDirectory((const char *) name, NULL) +ngx_int_t ngx_create_dir(u_char *name, ngx_uint_t access); #define ngx_create_dir_n "CreateDirectory()" -- cgit From f8075f1ef5106c9f0d894f83cfa81dbb9f5ce2da Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:47 +0300 Subject: Win32: non-ASCII directory names support in ngx_delete_dir(). This makes it possible to delete directories with non-ASCII characters when using the dav module (ticket #1433). --- src/os/win32/ngx_files.c | 36 ++++++++++++++++++++++++++++++++++++ src/os/win32/ngx_files.h | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 641b65c87..2e0e5faa9 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -613,6 +613,42 @@ failed: } +ngx_int_t +ngx_delete_dir(u_char *name) +{ + long rc; + size_t len; + u_short *u; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + + if (ngx_win32_check_filename(u, len, 0) != NGX_OK) { + goto failed; + } + + rc = RemoveDirectoryW(u); + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return rc; +} + + ngx_int_t ngx_open_glob(ngx_glob_t *gl) { diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index 10a388942..0ea5f7969 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -206,7 +206,7 @@ ngx_int_t ngx_create_dir(u_char *name, ngx_uint_t access); #define ngx_create_dir_n "CreateDirectory()" -#define ngx_delete_dir(name) RemoveDirectory((const char *) name) +ngx_int_t ngx_delete_dir(u_char *name); #define ngx_delete_dir_n "RemoveDirectory()" -- cgit From 94d8cea620e0abc67f4d0fe9aaf6170f39529c8c Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:50 +0300 Subject: Win32: reworked ngx_win32_rename_file() to check errors. Previously, ngx_win32_rename_file() retried on all errors returned by MoveFile() to a temporary name. It only make sense, however, to retry when the destination file already exists, similarly to the condition when ngx_win32_rename_file() is called. Retrying on other errors is meaningless and might result in an infinite loop. --- src/os/win32/ngx_files.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 2e0e5faa9..48075b4c9 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -236,10 +236,16 @@ ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log) break; } - collision = 1; + err = ngx_errno; - ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + if (err == NGX_EEXIST || err == NGX_EEXIST_FILE) { + collision = 1; + continue; + } + + ngx_log_error(NGX_LOG_CRIT, log, err, "MoveFile() \"%s\" to \"%s\" failed", to->data, name); + goto failed; } if (MoveFile((const char *) from->data, (const char *) to->data) == 0) { @@ -254,6 +260,8 @@ ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log) "DeleteFile() \"%s\" failed", name); } +failed: + /* mutex_unlock() */ ngx_free(name); -- cgit From dc4957485e151ed11026ab827c8e5348b3d3ae01 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:52 +0300 Subject: Win32: reworked ngx_win32_rename_file() to use nginx wrappers. This ensures that ngx_win32_rename_file() will support non-ASCII names when supported by the wrappers. Notably, this is used by PUT requests in the dav module when overwriting existing files with non-ASCII names (ticket #1433). --- src/os/win32/ngx_files.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 48075b4c9..5bd08e1a9 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -232,7 +232,7 @@ ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log) ngx_sprintf(name + to->len, ".%0muA.DELETE%Z", num); - if (MoveFile((const char *) to->data, (const char *) name) != 0) { + if (ngx_rename_file(to->data, name) != NGX_FILE_ERROR) { break; } @@ -248,14 +248,14 @@ ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log) goto failed; } - if (MoveFile((const char *) from->data, (const char *) to->data) == 0) { + if (ngx_rename_file(from->data, to->data) == NGX_FILE_ERROR) { err = ngx_errno; } else { err = 0; } - if (DeleteFile((const char *) name) == 0) { + if (ngx_delete_file(name) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, "DeleteFile() \"%s\" failed", name); } -- cgit From 1a9e5c83769128eefb5ee81147b0a350efc7cd68 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:54 +0300 Subject: Win32: non-ASCII names support in ngx_delete_file(). This makes it possible to delete files with non-ASCII characters when using the dav module (ticket #1433). --- src/os/win32/ngx_files.c | 36 ++++++++++++++++++++++++++++++++++++ src/os/win32/ngx_files.h | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 5bd08e1a9..5eb976d3d 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -207,6 +207,42 @@ ngx_write_console(ngx_fd_t fd, void *buf, size_t size) } +ngx_int_t +ngx_delete_file(u_char *name) +{ + long rc; + size_t len; + u_short *u; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + + if (ngx_win32_check_filename(u, len, 0) != NGX_OK) { + goto failed; + } + + rc = DeleteFileW(u); + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return rc; +} + + ngx_err_t ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log) { diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index 0ea5f7969..77d586332 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -123,7 +123,7 @@ ssize_t ngx_write_console(ngx_fd_t fd, void *buf, size_t size); #define NGX_LINEFEED CRLF -#define ngx_delete_file(name) DeleteFile((const char *) name) +ngx_int_t ngx_delete_file(u_char *name); #define ngx_delete_file_n "DeleteFile()" -- cgit From 4d84bc492936cd047d5f589ef4bc4736093ec176 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:55 +0300 Subject: Win32: non-ASCII names support in ngx_rename_file(). This makes it possible to upload files with non-ASCII characters when using the dav module (ticket #1433). --- src/os/win32/ngx_files.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ src/os/win32/ngx_files.h | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 5eb976d3d..3a30f51dd 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -243,6 +243,61 @@ failed: } +ngx_int_t +ngx_rename_file(u_char *from, u_char *to) +{ + long rc; + size_t len; + u_short *fu, *tu; + ngx_err_t err; + u_short utf16f[NGX_UTF16_BUFLEN]; + u_short utf16t[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + fu = ngx_utf8_to_utf16(utf16f, from, &len, 0); + + if (fu == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + tu = NULL; + + if (ngx_win32_check_filename(fu, len, 0) != NGX_OK) { + goto failed; + } + + len = NGX_UTF16_BUFLEN; + tu = ngx_utf8_to_utf16(utf16t, to, &len, 0); + + if (tu == NULL) { + goto failed; + } + + if (ngx_win32_check_filename(tu, len, 1) != NGX_OK) { + goto failed; + } + + rc = MoveFileW(fu, tu); + +failed: + + if (fu != utf16f) { + err = ngx_errno; + ngx_free(fu); + ngx_set_errno(err); + } + + if (tu && tu != utf16t) { + err = ngx_errno; + ngx_free(tu); + ngx_set_errno(err); + } + + return rc; +} + + ngx_err_t ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log) { diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index 77d586332..143120647 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -127,7 +127,7 @@ ngx_int_t ngx_delete_file(u_char *name); #define ngx_delete_file_n "DeleteFile()" -#define ngx_rename_file(o, n) MoveFile((const char *) o, (const char *) n) +ngx_int_t ngx_rename_file(u_char *from, u_char *to); #define ngx_rename_file_n "MoveFile()" ngx_err_t ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log); -- cgit From 2062ddef3989525e22445b1965220ffaf8e161c9 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:49:57 +0300 Subject: Win32: non-ASCII names support in ngx_open_tempfile(). This makes it possible to use temporary directories with non-ASCII characters, either explicitly or via a prefix with non-ASCII characters in it. --- src/os/win32/ngx_files.c | 35 +++++++++++++++++++++++++++++++++++ src/os/win32/ngx_files.h | 12 ++---------- 2 files changed, 37 insertions(+), 10 deletions(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 3a30f51dd..6f57a1775 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -63,6 +63,41 @@ failed: } +ngx_fd_t +ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access) +{ + size_t len; + u_short *u; + ngx_fd_t fd; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return INVALID_HANDLE_VALUE; + } + + fd = CreateFileW(u, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + NULL, + CREATE_NEW, + persistent ? 0: + FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, + NULL); + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return fd; +} + + ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) { diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index 143120647..6e59a6fc9 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -90,16 +90,8 @@ ngx_fd_t ngx_open_file(u_char *name, u_long mode, u_long create, u_long access); #define NGX_FILE_OWNER_ACCESS 0 -#define ngx_open_tempfile(name, persistent, access) \ - CreateFile((const char *) name, \ - GENERIC_READ|GENERIC_WRITE, \ - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, \ - NULL, \ - CREATE_NEW, \ - persistent ? 0: \ - FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, \ - NULL); - +ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent, + ngx_uint_t access); #define ngx_open_tempfile_n "CreateFile()" -- cgit From 6c5fe80bc69b12cc2bd0e8ed7bc67b240795f66b Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:50:00 +0300 Subject: Win32: removed attempt to use a drive letter in ngx_fs_bsize(). Just a drive letter might not correctly represent file system being used, notably when using symlinks (as created by "mklink /d"). As such, instead of trying to call GetDiskFreeSpace() with just a drive letter, we now always use GetDiskFreeSpace() with full path. Further, it looks like the code to use just a drive letter never worked, since it tried to test name[2] instead of name[1] to be ':'. --- src/os/win32/ngx_files.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 6f57a1775..3e0037859 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -967,14 +967,8 @@ ngx_directio_off(ngx_fd_t fd) size_t ngx_fs_bsize(u_char *name) { - u_char root[4]; u_long sc, bs, nfree, ncl; - if (name[2] == ':') { - ngx_cpystrn(root, name, 4); - name = root; - } - if (GetDiskFreeSpace((const char *) name, &sc, &bs, &nfree, &ncl) == 0) { return 512; } -- cgit From 4ace957c4e08bcbf9ef5e9f83b8e43458bead77f Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Thu, 23 Feb 2023 20:50:03 +0300 Subject: Win32: non-ASCII names in ngx_fs_bsize(), ngx_fs_available(). This fixes potentially incorrect cache size calculations and non-working "min_free" when using cache in directories with non-ASCII names. --- src/os/win32/ngx_files.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'src/os') diff --git a/src/os/win32/ngx_files.c b/src/os/win32/ngx_files.c index 3e0037859..90644ad9c 100644 --- a/src/os/win32/ngx_files.c +++ b/src/os/win32/ngx_files.c @@ -967,12 +967,31 @@ ngx_directio_off(ngx_fd_t fd) size_t ngx_fs_bsize(u_char *name) { - u_long sc, bs, nfree, ncl; + u_long sc, bs, nfree, ncl; + size_t len; + u_short *u; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return 512; + } + + if (GetDiskFreeSpaceW(u, &sc, &bs, &nfree, &ncl) == 0) { + + if (u != utf16) { + ngx_free(u); + } - if (GetDiskFreeSpace((const char *) name, &sc, &bs, &nfree, &ncl) == 0) { return 512; } + if (u != utf16) { + ngx_free(u); + } + return sc * bs; } @@ -980,12 +999,31 @@ ngx_fs_bsize(u_char *name) off_t ngx_fs_available(u_char *name) { - ULARGE_INTEGER navail; + size_t len; + u_short *u; + ULARGE_INTEGER navail; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return NGX_MAX_OFF_T_VALUE; + } + + if (GetDiskFreeSpaceExW(u, &navail, NULL, NULL) == 0) { + + if (u != utf16) { + ngx_free(u); + } - if (GetDiskFreeSpaceEx((const char *) name, &navail, NULL, NULL) == 0) { return NGX_MAX_OFF_T_VALUE; } + if (u != utf16) { + ngx_free(u); + } + return (off_t) navail.QuadPart; } -- cgit