diff --git a/cmdline/mingwapp.c b/cmdline/mingwapp.c index cad6fc48..5873bd4a 100644 --- a/cmdline/mingwapp.c +++ b/cmdline/mingwapp.c @@ -60,7 +60,9 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har strcpy(dir, path); /* get the file attributes */ - attr = GetFileAttributesW(convert(conv_buf, dir)); + if (!convert(conv_buf, dir)) + return -1; + attr = GetFileAttributesW(conv_buf); if (attr == INVALID_FILE_ATTRIBUTES) { DWORD error = GetLastError(); @@ -98,7 +100,9 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har * Get the free space of the directory * note that it must be a directory */ - if (!GetDiskFreeSpaceExW(convert(conv_buf, dir), 0, &total_bytes, &total_free_bytes)) { + if (!convert(conv_buf, dir)) + return -1; + if (!GetDiskFreeSpaceExW(conv_buf, 0, &total_bytes, &total_free_bytes)) { windows_errno(GetLastError()); return -1; } @@ -120,7 +124,9 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har if (fslabel && fslabel_size > 0) fslabel[0] = 0; - if (GetVolumePathNameW(convert(conv_buf, path), volume_root, PATH_MAX)) { + if (!convert(conv_buf, path)) + return -1; + if (GetVolumePathNameW(conv_buf, volume_root, PATH_MAX)) { if (GetVolumeInformationW(volume_root, vol_name, PATH_MAX, 0, 0, 0, fs_name, PATH_MAX)) { has_volume_info = 1; } @@ -556,7 +562,9 @@ int fssnapshot_mount(const char* dir, struct fssnapshot_struct* fss) * Make the configured path absolute without resolving reparse points. * Unlike realpath(), GetFullPathNameW() preserves volume mount points. */ - DWORD full_len = GetFullPathNameW(convert_if_required(conv_buf_vol, dir), PATH_MAX, full_path, 0); + if (!convert_if_required(conv_buf_vol, dir)) + return -1; + DWORD full_len = GetFullPathNameW(conv_buf_vol, PATH_MAX, full_path, 0); if (full_len == 0) { windows_errno(GetLastError()); log_error(errno, "Error resolving full path '%s'. %s.\n", dir, strerror(errno)); @@ -569,7 +577,9 @@ int fssnapshot_mount(const char* dir, struct fssnapshot_struct* fss) } /* use pathimport to convert backslashes to slashes */ - pathimport(resolved_dir, sizeof(resolved_dir), u16tou8(conv_buf_root, full_path)); + if (!u16tou8(conv_buf_root, full_path)) + return -1; + pathimport(resolved_dir, sizeof(resolved_dir), conv_buf_root); pathslash(resolved_dir, sizeof(resolved_dir)); /* @@ -580,7 +590,9 @@ int fssnapshot_mount(const char* dir, struct fssnapshot_struct* fss) * Use convert_if_required() to avoid the automatic addition of \\?\ * made by convert() that is propagated in the resulting volume_root */ - if (!GetVolumePathNameW(convert_if_required(conv_buf_vol, resolved_dir), volume_root, PATH_MAX)) { + if (!convert_if_required(conv_buf_vol, resolved_dir)) + return -1; + if (!GetVolumePathNameW(conv_buf_vol, volume_root, PATH_MAX)) { windows_errno(GetLastError()); log_error(errno, "Error getting VolumeRoot from '%s'. %s.\n", resolved_dir, strerror(errno)); return -1; @@ -588,7 +600,7 @@ int fssnapshot_mount(const char* dir, struct fssnapshot_struct* fss) if (!GetVolumeInformationW(volume_root, 0, 0, 0, 0, 0, fs_name, 32)) { windows_errno(GetLastError()); - log_error(errno, "Error getting information of VolumeRoot '%s'. %s.\n", u16tou8(conv_buf_root, volume_root), strerror(errno)); + log_error(errno, "Error getting information of VolumeRoot for '%s'. %s.\n", dir, strerror(errno)); return -1; } if (wcscmp(fs_name, L"NTFS") == 0) @@ -610,15 +622,19 @@ int fssnapshot_mount(const char* dir, struct fssnapshot_struct* fss) */ if (!GetVolumeNameForVolumeMountPointW(volume_root, volume_name, PATH_MAX)) { windows_errno(GetLastError()); - log_error(errno, "Error getting VolumeName of VolumeRoot '%s'. %s.\n", u16tou8(conv_buf_root, volume_root), strerror(errno)); + log_error(errno, "Error getting VolumeName of VolumeRoot for '%s'. %s.\n", dir, strerror(errno)); return -1; } /* don't use pathimport for dataset to keep backslashes */ - pathcpy(fss->dataset, sizeof(fss->dataset), u16tou8(conv_buf_root, volume_name)); + if (!u16tou8(conv_buf_root, volume_name)) + return -1; + pathcpy(fss->dataset, sizeof(fss->dataset), conv_buf_root); /* use pathimport to convert backslashes to slashes */ - pathimport(fss->root_dir, sizeof(fss->root_dir), u16tou8(conv_buf_root, volume_root)); + if (!u16tou8(conv_buf_root, volume_root)) + return -1; + pathimport(fss->root_dir, sizeof(fss->root_dir), conv_buf_root); /* the returned root_dir should match the start of the resolved dir */ if (pathncmp(fss->root_dir, resolved_dir, strlen(fss->root_dir)) != 0) { @@ -932,7 +948,9 @@ static int devresolve(const char* mount, char* file, size_t file_size, char* wfi char* p; /* get the volume mount point from the disk path */ - if (!GetVolumePathNameW(convert(conv_buf_mount, mount), volume_mount, sizeof(volume_mount) / sizeof(WCHAR))) { + if (!convert(conv_buf_mount, mount)) + return -1; + if (!GetVolumePathNameW(conv_buf_mount, volume_mount, sizeof(volume_mount) / sizeof(WCHAR))) { windows_errno(GetLastError()); return -1; } @@ -953,7 +971,9 @@ static int devresolve(const char* mount, char* file, size_t file_size, char* wfi if (i != 0 && volume_guid[i - 1] == '\\') volume_guid[i - 1] = 0; - pathcpy(wfile, wfile_size, u16tou8(conv_buf_volume_guid, volume_guid)); + if (!u16tou8(conv_buf_volume_guid, volume_guid)) + return -1; + pathcpy(wfile, wfile_size, conv_buf_volume_guid); /* get the GUID start { */ p = strchr(wfile, '{'); @@ -1006,7 +1026,9 @@ static int devtree(devinfo_t* parent, tommy_list* list) DWORD i; /* open the volume */ - h = CreateFileW(convert(conv_buf, parent->wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if (!convert(conv_buf, parent->wfile)) + return -1; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -1091,7 +1113,9 @@ static int devstat(uint64_t device, const char* name, const char* wfile, uint64_ snprintf(file, sizeof(file), "/dev/pd%" PRIu64, device); /* open the volume */ - h = CreateFileW(convert(conv_buf, wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if (!convert(conv_buf, wfile)) + return -1; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); windows_errno(error); @@ -1139,7 +1163,9 @@ static int smartctl_executable(char* path, size_t size) return -1; } - ret = snprintf(path, size, "%ssmartctl.exe", u16tou8(conv_buf, dir)); + if (!u16tou8(conv_buf, dir)) + return -1; + ret = snprintf(path, size, "%ssmartctl.exe", conv_buf); if (ret < 0 || (size_t)ret >= size) { errno = ENAMETOOLONG; return -1; @@ -1488,7 +1514,7 @@ static void devattr_rotational(HANDLE h, uint64_t* rotational) /** * Get device attributes. */ -static void devattr(uint64_t device, const char* name, const char* wfile, uint64_t* info, char* serial, char* family, char* model, char* interf) +static int devattr(uint64_t device, const char* name, const char* wfile, uint64_t* info, char* serial, char* family, char* model, char* interf) { HANDLE h; wchar_t conv_buf[CONV_MAX]; @@ -1499,12 +1525,15 @@ static void devattr(uint64_t device, const char* name, const char* wfile, uint64 (void)family; /* not available, smartctl uses an internal database to get it */ /* open the volume */ - h = CreateFileW(convert(conv_buf, wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + /* an unavailable attribute is optional, but an unrepresentable path must fail */ + if (!convert(conv_buf, wfile)) + return -1; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); windows_errno(error); log_tag("device:%s:%s:error:%lu\n", file, name, error); - return; + return 0; } if (info[INFO_SIZE] == SMART_UNASSIGNED) @@ -1525,8 +1554,9 @@ static void devattr(uint64_t device, const char* name, const char* wfile, uint64 DWORD error = GetLastError(); windows_errno(error); log_tag("device:%s:%s:error:%lu\n", file, name, error); - return; + return 0; } + return 0; } /** @@ -1545,7 +1575,10 @@ static int devpower(uint64_t device, const char* name, const char* wfile) snprintf(file, sizeof(file), "/dev/pd%" PRIu64, device); /* open the volume */ - h = CreateFileW(convert(conv_buf, wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + /* don't treat an unrepresentable path as an unknown rotational device */ + if (!convert(conv_buf, wfile)) + return -1; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); windows_errno(error); @@ -1902,7 +1935,9 @@ static int devup(uint64_t device, const char* name, const char* wfile) snprintf(file, sizeof(file), "/dev/pd%" PRIu64, device); /* open the volume */ - h = CreateFileW(convert(conv_buf, wfile), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, 0); + if (!convert(conv_buf, wfile)) + return -1; + h = CreateFileW(conv_buf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, 0); if (h == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); windows_errno(error); @@ -1963,7 +1998,10 @@ static void* thread_spinup(void* arg) int ret; /* only for members of the array and rotational devices */ - if (devinfo->is_array && devpower(devinfo->device, devinfo->name, devinfo->wfile) != 0) { + ret = devinfo->is_array ? devpower(devinfo->device, devinfo->name, devinfo->wfile) : 0; + if (ret < 0) + return (void*)-1; + if (ret != 0) { uint64_t start = os_tick_ms(); if (devup(devinfo->device, devinfo->name, devinfo->wfile) != 0) { @@ -1996,7 +2034,8 @@ static void* thread_spinup(void* arg) * smartctl intentionally skips queries on devices in standby mode * to prevent accidentally spinning them up. */ - devattr(devinfo->device, devinfo->name, devinfo->wfile, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf); + if (devattr(devinfo->device, devinfo->name, devinfo->wfile, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) + return (void*)-1; return 0; } @@ -2007,9 +2046,13 @@ static void* thread_spinup(void* arg) static void* thread_spindown(void* arg) { devinfo_t* devinfo = arg; + int ret; /* only for members of the array and rotational devices */ - if (devinfo->is_array && devpower(devinfo->device, devinfo->name, devinfo->wfile) != 0) { + ret = devinfo->is_array ? devpower(devinfo->device, devinfo->name, devinfo->wfile) : 0; + if (ret < 0) + return (void*)-1; + if (ret != 0) { uint64_t start = os_tick_ms(); if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) { @@ -2030,9 +2073,13 @@ static void* thread_spindown(void* arg) static void* thread_spindownifup(void* arg) { devinfo_t* devinfo = arg; + int ret; /* only for members of the array and rotational devices */ - if (devinfo->is_array && devpower(devinfo->device, devinfo->name, devinfo->wfile) != 0) { + ret = devinfo->is_array ? devpower(devinfo->device, devinfo->name, devinfo->wfile) : 0; + if (ret < 0) + return (void*)-1; + if (ret != 0) { uint64_t start = os_tick_ms(); int power; @@ -2068,7 +2115,8 @@ static void* thread_smart(void* arg) * smartctl intentionally skips queries on devices in standby mode * to prevent accidentally spinning them up. */ - devattr(devinfo->device, devinfo->name, devinfo->wfile, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf); + if (devattr(devinfo->device, devinfo->name, devinfo->wfile, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) + return (void*)-1; return 0; } @@ -2092,7 +2140,8 @@ static void* thread_probe(void* arg) * smartctl intentionally skips queries on devices in standby mode * to prevent accidentally spinning them up. */ - devattr(devinfo->device, devinfo->name, devinfo->wfile, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf); + if (devattr(devinfo->device, devinfo->name, devinfo->wfile, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) + return (void*)-1; return 0; } @@ -2201,7 +2250,9 @@ int devmap(void) HANDLE h; /* open the volume */ - h = CreateFileW(convert(conv_buf, wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if (!convert(conv_buf, wfile)) + return -1; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); if (error != ERROR_FILE_NOT_FOUND) diff --git a/os/mingw.c b/os/mingw.c index 0d161f1b..b482f021 100644 --- a/os/mingw.c +++ b/os/mingw.c @@ -389,8 +389,10 @@ wchar_t* u8tou16_mayfail(wchar_t* conv_buf, size_t number_of_wchar, const char* { /* MB_ERR_INVALID_CHARS forces the API to fail on malformed UTF-8 sequences */ int ret = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, number_of_char, conv_buf, number_of_wchar); - if (ret <= 0) + if (ret <= 0) { + windows_errno(GetLastError()); return 0; + } if (result_length_without_terminator) *result_length_without_terminator = ret; @@ -402,8 +404,10 @@ char* u16tou8_mayfail(char* conv_buf, size_t number_of_char, const wchar_t* src, { /* WC_ERR_INVALID_CHARS forces the API to fail on malformed UTF-16 sequences */ int ret = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, number_of_wchar, conv_buf, number_of_char, 0, 0); - if (ret <= 0) + if (ret <= 0) { + windows_errno(GetLastError()); return 0; + } if (result_length_without_terminator) *result_length_without_terminator = ret; @@ -416,13 +420,8 @@ static wchar_t* u8tou16_force(wchar_t* conv_buf, size_t number_of_wchar, const c /* flags at 0 forces the API to never fail on malformed UTF-8 sequences */ int ret = MultiByteToWideChar(CP_UTF8, 0, src, number_of_char, conv_buf, number_of_wchar); if (ret <= 0) { - DWORD error = GetLastError(); - if (error == ERROR_INSUFFICIENT_BUFFER) { - os_syslog(OS_LVL_CRITICAL, "path too long converting '%s' from UTF-8 to UTF-16", src); - } else { - os_syslog(OS_LVL_CRITICAL, "error %u converting '%s' from UTF-8 to UTF-16", (unsigned)error, src); - } - os_abort(); + windows_errno(GetLastError()); + return 0; } if (result_length_without_terminator) @@ -436,18 +435,8 @@ static char* u16tou8_force(char* conv_buf, size_t number_of_char, const wchar_t* /* flags at 0 forces the API to never fail on malformed UTF-16 sequences */ int ret = WideCharToMultiByte(CP_UTF8, 0, src, number_of_wchar, conv_buf, number_of_char, 0, 0); if (ret <= 0) { - DWORD error = GetLastError(); - if (error == ERROR_INSUFFICIENT_BUFFER) { - os_syslog(OS_LVL_CRITICAL, "path too long converting from UTF-16 to UTF-8 with len %u", (unsigned)number_of_wchar); - } else { - os_syslog(OS_LVL_CRITICAL, "error %u converting from UTF-16 to UTF-8 with len %u", (unsigned)error, (unsigned)number_of_wchar); - if (src != 0) { - for (size_t i = 0; i < number_of_wchar; ++i) { - os_syslog(OS_LVL_CRITICAL, "%4u: %04x", (unsigned)i, src[i]); - } - } - } - os_abort(); + windows_errno(GetLastError()); + return 0; } if (result_length_without_terminator) @@ -492,8 +481,8 @@ wchar_t* convert_arg(wchar_t* conv_buf, const char* src, int only_if_required) if (only_if_required && strlen(src) < 260 - 12) { /* * It's a short path - * 260 is the MAX_PATH, note that it includes the space for the terminating NUL - * 12 is an additional space for filename, required when creating directory + * 260 is the legacy MAX_PATH, including the terminating NUL + * 12 leaves room to append an 8.3 filename to the new directory's path */ /* do nothing */ @@ -531,13 +520,8 @@ wchar_t* convert_arg(wchar_t* conv_buf, const char* src, int only_if_required) ret = MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, CONV_MAX - count); if (ret <= 0) { - DWORD error = GetLastError(); - if (error == ERROR_INSUFFICIENT_BUFFER) { - os_syslog(OS_LVL_CRITICAL, "oath too long converting '%s' from UTF-8 to UTF-16", src); - } else { - os_syslog(OS_LVL_CRITICAL, "error %u converting '%s' from UTF-8 to UTF-16", (unsigned)error, src); - } - os_abort(); + windows_errno(GetLastError()); + return 0; } /* @@ -827,23 +811,26 @@ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_s st->st_sync = 0; } -static void windows_finddata2dirent(const WIN32_FIND_DATAW* info, struct windows_dirent* dirent) +static int windows_finddata2dirent(const WIN32_FIND_DATAW* info, struct windows_dirent* dirent) { char conv_buf[CONV_MAX]; const char* name; size_t len; name = u16tou8_force(conv_buf, CONV_MAX, info->cFileName, wcslen(info->cFileName), &len); + if (!name) + return -1; if (len + 1 >= sizeof(dirent->d_name)) { - os_syslog(OS_LVL_CRITICAL, "name too long"); - os_exit(); + errno = ENAMETOOLONG; + return -1; } memcpy(dirent->d_name, name, len); dirent->d_name[len] = 0; windows_finddata2stat(info, &dirent->d_stat); + return 0; } static int windows_stream2dirent(const BY_HANDLE_FILE_INFORMATION* info, const FILE_ID_BOTH_DIR_INFO* stream, struct windows_dirent* dirent) @@ -853,10 +840,12 @@ static int windows_stream2dirent(const BY_HANDLE_FILE_INFORMATION* info, const F size_t len; name = u16tou8_force(conv_buf, CONV_MAX, stream->FileName, stream->FileNameLength / 2, &len); + if (!name) + return -1; if (len + 1 >= sizeof(dirent->d_name)) { - os_syslog(OS_LVL_CRITICAL, "name too long"); - os_exit(); + errno = ENAMETOOLONG; + return -1; } memcpy(dirent->d_name, name, len); @@ -880,6 +869,8 @@ void windows_errno(DWORD error) errno = EINVAL; break; case ERROR_HANDLE_EOF : /* in ReadFile() over the end of the file */ + case ERROR_INVALID_PARAMETER : + case ERROR_INVALID_FLAGS : errno = EINVAL; break; case ERROR_FILE_NOT_FOUND : @@ -898,9 +889,14 @@ void windows_errno(DWORD error) case ERROR_DISK_FULL : errno = ENOSPC; break; + case ERROR_FILENAME_EXCED_RANGE : case ERROR_BUFFER_OVERFLOW : + case ERROR_INSUFFICIENT_BUFFER : /* in MultiByteToWideChar() and WideCharToMultiByte() */ errno = ENAMETOOLONG; break; + case ERROR_NO_UNICODE_TRANSLATION : /* in MultiByteToWideChar() and WideCharToMultiByte() */ + errno = EILSEQ; + break; case ERROR_NOT_ENOUGH_MEMORY : errno = ENOMEM; break; @@ -960,7 +956,10 @@ int windows_get_file_attributes(const char* file) { wchar_t conv_buf[CONV_MAX]; - DWORD ret = GetFileAttributesW(convert(conv_buf, file)); + if (!convert(conv_buf, file)) + return -1; + + DWORD ret = GetFileAttributesW(conv_buf); if (ret == INVALID_FILE_ATTRIBUTES) { windows_errno(GetLastError()); return -1; @@ -973,7 +972,10 @@ int windows_set_file_attributes(const char* file, int attributes) { wchar_t conv_buf[CONV_MAX]; - if (!SetFileAttributesW(convert(conv_buf, file), attributes)) { + if (!convert(conv_buf, file)) + return -1; + + if (!SetFileAttributesW(conv_buf, attributes)) { windows_errno(GetLastError()); return -1; } @@ -997,7 +999,9 @@ int windows_lstat(const char* file, struct windows_stat* st) return windows_stat(file, st); /* FindFirstFileW by default gets information of symbolic links and not of their targets */ - h = FindFirstFileW(convert(conv_buf, file), &data); + if (!convert(conv_buf, file)) + return -1; + h = FindFirstFileW(conv_buf, &data); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -1022,7 +1026,10 @@ int windows_mkdir(const char* file) { wchar_t conv_buf[CONV_MAX]; - if (!CreateDirectoryW(convert(conv_buf, file), 0)) { + if (!convert(conv_buf, file)) + return -1; + + if (!CreateDirectoryW(conv_buf, 0)) { windows_errno(GetLastError()); return -1; } @@ -1034,7 +1041,10 @@ int windows_rmdir(const char* file) { wchar_t conv_buf[CONV_MAX]; - if (!RemoveDirectoryW(convert(conv_buf, file))) { + if (!convert(conv_buf, file)) + return -1; + + if (!RemoveDirectoryW(conv_buf)) { windows_errno(GetLastError()); return -1; } @@ -1054,7 +1064,9 @@ int windows_stat(const char* file, struct windows_stat* st) * * Use FILE_FLAG_BACKUP_SEMANTICS to open directories (it's just ignored for files). */ - h = CreateFileW(convert(conv_buf, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (!convert(conv_buf, file)) + return -1; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -1407,7 +1419,9 @@ int windows_utimensat(int fd, const char* file, struct windows_timespec tv[2], i wflags = FILE_FLAG_BACKUP_SEMANTICS; if ((flags & AT_SYMLINK_NOFOLLOW) != 0) wflags |= FILE_FLAG_OPEN_REPARSE_POINT; - h = CreateFileW(convert(conv_buf, file), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, wflags, 0); + if (!convert(conv_buf, file)) + return -1; + h = CreateFileW(conv_buf, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, wflags, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -1445,6 +1459,8 @@ int windows_rename(const char* from, const char* to) { wchar_t conv_buf_from[CONV_MAX]; wchar_t conv_buf_to[CONV_MAX]; + if (!convert(conv_buf_from, from) || !convert(conv_buf_to, to)) + return -1; /* * Implements an atomic rename in Windows. @@ -1453,7 +1469,7 @@ int windows_rename(const char* from, const char* to) * Is an atomic file rename (with overwrite) possible on Windows? * http://stackoverflow.com/questions/167414/is-an-atomic-file-rename-with-overwrite-possible-on-windows */ - if (!MoveFileExW(convert(conv_buf_from, from), convert(conv_buf_to, to), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { + if (!MoveFileExW(conv_buf_from, conv_buf_to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { windows_errno(GetLastError()); return -1; } @@ -1465,7 +1481,10 @@ int windows_remove(const char* file) { wchar_t conv_buf[CONV_MAX]; - if (!DeleteFileW(convert(conv_buf, file))) { + if (!convert(conv_buf, file)) + return -1; + + if (!DeleteFileW(conv_buf)) { windows_errno(GetLastError()); return -1; } @@ -1478,7 +1497,9 @@ FILE* windows_fopen(const char* file, const char* mode) wchar_t conv_buf_file[CONV_MAX]; wchar_t conv_buf_mode[CONV_MAX]; - return _wfopen(convert(conv_buf_file, file), u8tou16(conv_buf_mode, mode)); + if (!convert(conv_buf_file, file) || !u8tou16(conv_buf_mode, mode)) + return 0; + return _wfopen(conv_buf_file, conv_buf_mode); } int windows_open(const char* file, int flags, ...) @@ -1566,7 +1587,9 @@ int windows_open(const char* file, int flags, ...) if ((flags & O_NOFOLLOW) != 0) attr |= FILE_FLAG_OPEN_REPARSE_POINT; - h = CreateFileW(convert(conv_buf, file), access, share, 0, create, attr, 0); + if (!convert(conv_buf, file)) + return -1; + h = CreateFileW(conv_buf, access, share, 0, create, attr, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -1609,7 +1632,7 @@ int windows_open(const char* file, int flags, ...) } attr &= ~FILE_FLAG_OPEN_REPARSE_POINT; - h = CreateFileW(convert(conv_buf, file), access, share, 0, create, attr, 0); + h = CreateFileW(conv_buf, access, share, 0, create, attr, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -1699,6 +1722,10 @@ static windows_dir* windows_opendir_find(const char* dir) } wdir = convert(conv_buf, dir); + if (!wdir) { + free(dirstream); + return 0; + } /* add final \ and * */ len = wcslen(wdir); @@ -1727,7 +1754,11 @@ static windows_dir* windows_opendir_find(const char* dir) return 0; } - windows_finddata2dirent(&dirstream->find, &dirstream->entry); + if (windows_finddata2dirent(&dirstream->find, &dirstream->entry) != 0) { + FindClose(dirstream->h); + free(dirstream); + return 0; + } dirstream->state = DIR_STATE_FILLED; return dirstream; @@ -1746,7 +1777,8 @@ static struct windows_dirent* windows_readdir_find(windows_dir* dirstream) dirstream->state = DIR_STATE_EOF; } else { - windows_finddata2dirent(&dirstream->find, &dirstream->entry); + if (windows_finddata2dirent(&dirstream->find, &dirstream->entry) != 0) + return 0; dirstream->state = DIR_STATE_FILLED; } } @@ -1846,7 +1878,12 @@ static windows_dir* windows_opendir_stream(const char* dir) return 0; } - dirstream->h = CreateFileW(convert(conv_buf, dir), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (!convert(conv_buf, dir)) { + free(dirstream->buffer); + free(dirstream); + return 0; + } + dirstream->h = CreateFileW(conv_buf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); if (dirstream->h == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); free(dirstream->buffer); @@ -1968,7 +2005,9 @@ int windows_link(const char* existing, const char* file) wchar_t conv_buf_file[CONV_MAX]; wchar_t conv_buf_existing[CONV_MAX]; - if (!CreateHardLinkW(convert(conv_buf_file, file), convert(conv_buf_existing, existing), 0)) { + if (!convert(conv_buf_file, file) || !convert(conv_buf_existing, existing)) + return -1; + if (!CreateHardLinkW(conv_buf_file, conv_buf_existing, 0)) { windows_errno(GetLastError()); return -1; } @@ -1991,6 +2030,8 @@ static int windows_symlink_flags(const char* existing, const char* file, DWORD f { wchar_t conv_buf_file[CONV_MAX]; wchar_t conv_buf_existing[CONV_MAX]; + if (!convert(conv_buf_file, file) || !convert_if_required(conv_buf_existing, existing)) + return -1; /* * We must convert to the extended-length \\?\ format if the path is too long @@ -1998,7 +2039,7 @@ static int windows_symlink_flags(const char* existing, const char* file, DWORD f * But we don't want to always convert it, to avoid to recreate * user symlinks different than they were before */ - if (!CreateSymbolicLinkW(convert(conv_buf_file, file), convert_if_required(conv_buf_existing, existing), + if (!CreateSymbolicLinkW(conv_buf_file, conv_buf_existing, flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) ) { DWORD error = GetLastError(); @@ -2008,7 +2049,7 @@ static int windows_symlink_flags(const char* existing, const char* file, DWORD f } /* retry without the new flag SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE */ - if (!CreateSymbolicLinkW(convert(conv_buf_file, file), convert_if_required(conv_buf_existing, existing), flags)) { + if (!CreateSymbolicLinkW(conv_buf_file, conv_buf_existing, flags)) { windows_errno(GetLastError()); return -1; } @@ -2077,7 +2118,9 @@ int windows_readlink(const char* file, char* buffer, size_t size) * Use FILE_FLAG_BACKUP_SEMANTICS to open directories (it's just ignored for files). * Use FILE_FLAG_OPEN_REPARSE_POINT to open symbolic links and not the their target. */ - h = CreateFileW(convert(conv_buf_file, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); + if (!convert(conv_buf_file, file)) + return -1; + h = CreateFileW(conv_buf_file, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -2130,6 +2173,8 @@ int windows_readlink(const char* file, char* buffer, size_t size) /* convert the name to UTF-8 */ name = u16tou8_force(conv_buf_name, CONV_MAX, print, print_len, &len); + if (!name) + return -1; /* check for overflow */ if (len > size) { @@ -2405,22 +2450,31 @@ struct tm* windows_localtime_r(const time_t* timer, struct tm* result) char* windows_realpath(const char* path, char* resolved_path) { wchar_t conv_buf[CONV_MAX]; - HANDLE h = CreateFileW(convert(conv_buf, path), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + HANDLE h; + if (!convert(conv_buf, path)) + return 0; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return 0; } DWORD result = GetFinalPathNameByHandleW(h, conv_buf, CONV_MAX, FILE_NAME_NORMALIZED); + DWORD error = GetLastError(); CloseHandle(h); - if (result == 0 || result >= CONV_MAX) { - windows_errno(GetLastError()); + if (result >= CONV_MAX) { + errno = ENAMETOOLONG; + return 0; + } + if (result == 0) { + windows_errno(error); return 0; } - resolved_path = u16tou8(resolved_path, conv_buf); + if (!u16tou8(resolved_path, conv_buf)) + return 0; /* * Windows prefixes normalized paths with "\\?\" (for local drives) @@ -2452,10 +2506,8 @@ char* absolutepath(const char* restrict path, char* restrict resolved_path) wchar_t wpath[CONV_MAX]; wchar_t wfull[CONV_MAX]; - if (!u8tou16_mayfail(wpath, CONV_MAX, path, strlen(path) + 1, 0)) { - errno = EINVAL; + if (!u8tou16_mayfail(wpath, CONV_MAX, path, strlen(path) + 1, 0)) return 0; - } DWORD len = GetFullPathNameW(wpath, CONV_MAX, wfull, 0); if (len == 0) { @@ -2468,10 +2520,8 @@ char* absolutepath(const char* restrict path, char* restrict resolved_path) return 0; } - if (!u16tou8_mayfail(resolved_path, CONV_MAX, wfull, len + 1, 0)) { - errno = ENAMETOOLONG; + if (!u16tou8_mayfail(resolved_path, CONV_MAX, wfull, len + 1, 0)) return 0; - } return resolved_path; } @@ -2547,7 +2597,9 @@ int lstat_sync(const char* file, struct windows_stat* st, uint64_t* physical) * cannot be opened like "C:\System Volume Information" resulting * in error ERROR_ACCESS_DENIED. */ - h = CreateFileW(convert(conv_buf, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); + if (!convert(conv_buf, file)) + return -1; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -2593,7 +2645,9 @@ int filephy(const char* file, uint64_t size, uint64_t* physical) (void)size; /* open the handle of the file */ - h = CreateFileW(convert(conv_buf, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0); + if (!convert(conv_buf, file)) + return -1; + h = CreateFileW(conv_buf, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -3221,10 +3275,21 @@ pid_t os_spawn(char** argv, int* stdout_read_int, int* stderr_read_int, const ch /* prepare command line string (Windows uses a single string, not an array) */ WCHAR cmd_buffer[COMMAND_LINE_MAX]; - char cmd_buffer_conv[COMMAND_LINE_MAX * 3]; /* * 3 is needed because a single UTF-16 character can take up to 3 bytes in UTF-8 */ int pos = 0; for (int i = 0; argv[i]; ++i) { - pos = argcat(cmd_buffer, COMMAND_LINE_MAX, pos, u8tou16(conv, argv[i])); + if (!u8tou16(conv, argv[i])) { + CloseHandle(nul_handle); + if (has_out) { + CloseHandle(stdout_write_handle); + close(out_f); + } + if (has_err) { + CloseHandle(stderr_write_handle); + close(err_f); + } + return -1; + } + pos = argcat(cmd_buffer, COMMAND_LINE_MAX, pos, conv); if (pos < 0) { os_syslog(OS_LVL_INFO, "command to long for spawn"); CloseHandle(nul_handle); @@ -3313,7 +3378,8 @@ pid_t os_spawn(char** argv, int* stdout_read_int, int* stderr_read_int, const ch return -1; } - if (!LogonUserW(u8tou16(conv, run_as_user), L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, &h_token)) { + const wchar_t* wuser = _stricmp(run_as_user, "LocalService") == 0 ? L"LocalService" : L"NetworkService"; + if (!LogonUserW(wuser, L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, &h_token)) { windows_errno(GetLastError()); os_syslog(OS_LVL_INFO, "failed to logon user %s, errno=%s(%d)", run_as_user, strerror(errno), errno); pid_unpublish(pid_slot, 0); @@ -3370,7 +3436,7 @@ pid_t os_spawn(char** argv, int* stdout_read_int, int* stderr_read_int, const ch process_startup_done(&startup); if (!ret) { windows_errno(create_error); - os_syslog(OS_LVL_INFO, "failed to create process '%s' for spawn, errno=%s(%d)", u16tou8_force(cmd_buffer_conv, sizeof(cmd_buffer_conv), cmd_buffer, wcslen(cmd_buffer) + 1, 0), strerror(errno), errno); + os_syslog(OS_LVL_INFO, "failed to create process '%s' for spawn, errno=%s(%d)", argv[0], strerror(errno), errno); pid_unpublish(pid_slot, 0); CloseHandle(nul_handle); if (has_out) { @@ -3607,6 +3673,9 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t DWORD create_error = ERROR_SUCCESS; int64_t start, stop; + if (!u8tou16(conv, command)) + return -1; + start = os_tick_sec(); sa.nLength = sizeof(SECURITY_ATTRIBUTES); @@ -3677,7 +3746,7 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t /* create the child process */ ret = CreateProcessW( NULL, - u8tou16(conv, command), + conv, NULL, NULL, TRUE, /* inherit handles from the explicit list */ CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, @@ -3706,7 +3775,8 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t return -1; } - if (!LogonUserW(u8tou16(conv, run_as_user), L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, &h_token)) { + const wchar_t* wuser = _stricmp(run_as_user, "LocalService") == 0 ? L"LocalService" : L"NetworkService"; + if (!LogonUserW(wuser, L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, &h_token)) { windows_errno(GetLastError()); os_syslog(OS_LVL_INFO, "failed to logon user %s, errno=%s(%d)", run_as_user, strerror(errno), errno); pid_unpublish(pid_slot, 0); @@ -3736,7 +3806,7 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t ret = CreateProcessAsUserW( h_token, NULL, - u8tou16(conv, command), + conv, NULL, NULL, TRUE, /* inherit handles from the explicit list */ CREATE_SUSPENDED | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, @@ -3922,8 +3992,10 @@ static WCHAR* env_combine(const WCHAR* base_env, char** envp) while (*p != 0) { size_t len = wcslen(p); struct env_item* item = malloc(sizeof(struct env_item) + len * sizeof(WCHAR)); - if (!item) + if (!item) { + errno = ENOMEM; goto bail; + } memcpy(item->str, p, (len + 1) * sizeof(WCHAR)); tommy_list_insert_tail(&list, &item->node, item); total_len += len + 1; @@ -3935,11 +4007,14 @@ static WCHAR* env_combine(const WCHAR* base_env, char** envp) if (envp) { for (int i = 0; envp[i] != NULL; ++i) { wchar_t conv[CONV_MAX]; - u8tou16(conv, envp[i]); + if (!u8tou16(conv, envp[i])) + goto bail; size_t len = wcslen(conv); struct env_item* item = malloc(sizeof(struct env_item) + len * sizeof(WCHAR)); - if (!item) + if (!item) { + errno = ENOMEM; goto bail; + } memcpy(item->str, conv, (len + 1) * sizeof(WCHAR)); tommy_list_insert_tail(&list, &item->node, item); total_len += len + 1; @@ -3948,6 +4023,8 @@ static WCHAR* env_combine(const WCHAR* base_env, char** envp) if (total_len == 0) { WCHAR* empty_env = malloc(2 * sizeof(WCHAR)); + if (!empty_env) + errno = ENOMEM; if (empty_env) { empty_env[0] = 0; empty_env[1] = 0; @@ -3958,6 +4035,8 @@ static WCHAR* env_combine(const WCHAR* base_env, char** envp) tommy_list_sort(&list, env_item_compare); new_env = malloc((total_len + 1) * sizeof(WCHAR)); + if (!new_env) + errno = ENOMEM; if (new_env) { WCHAR* dst = new_env; for (tommy_node* node = tommy_list_head(&list); node; node = node->next) { @@ -4016,7 +4095,6 @@ int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeou /* prepare command line string (Windows uses a single string, not an array) */ WCHAR cmd_buffer[COMMAND_LINE_MAX]; - char cmd_buffer_conv[COMMAND_LINE_MAX * 3]; /* * 3 is needed because a single UTF-16 character can take up to 3 bytes in UTF-8 */ int pos = 0; /* @@ -4026,7 +4104,9 @@ int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeou * /v:off explicitly disables delayed environment variable expansion (!VAR!). */ pos = fixcat(cmd_buffer, COMMAND_LINE_MAX, pos, L"cmd.exe /d /v:off /c \" "); - pos = scriptcat(cmd_buffer, COMMAND_LINE_MAX, pos, u8tou16(conv, resolved_path)); + if (!u8tou16(conv, resolved_path)) + return -1; + pos = scriptcat(cmd_buffer, COMMAND_LINE_MAX, pos, conv); if (pos < 0) { if (errno == EINVAL) os_syslog(OS_LVL_INFO, "unsupported embedded double quote in script path"); @@ -4035,7 +4115,9 @@ int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeou return -1; } for (int i = 1; argv[i]; ++i) { - pos = scriptcat(cmd_buffer, COMMAND_LINE_MAX, pos, u8tou16(conv, argv[i])); + if (!u8tou16(conv, argv[i])) + return -1; + pos = scriptcat(cmd_buffer, COMMAND_LINE_MAX, pos, conv); if (pos < 0) { if (errno == EINVAL) os_syslog(OS_LVL_INFO, "unsupported embedded double quote in script argument"); @@ -4081,8 +4163,7 @@ int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeou combined_env = env_combine(base_env, envp); FreeEnvironmentStringsW(base_env); if (!combined_env) { - errno = ENOMEM; - os_syslog(OS_LVL_INFO, "failed to combine environment strings (out of memory)"); + os_syslog(OS_LVL_INFO, "failed to combine environment strings, errno=%s(%d)", strerror(errno), errno); pid_unpublish(pid_slot, 0); return -1; } @@ -4118,7 +4199,8 @@ int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeou return -1; } - if (!LogonUserW(u8tou16(conv, run_as_user), L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, &h_token)) { + const wchar_t* wuser = _stricmp(run_as_user, "LocalService") == 0 ? L"LocalService" : L"NetworkService"; + if (!LogonUserW(wuser, L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, &h_token)) { windows_errno(GetLastError()); os_syslog(OS_LVL_INFO, "failed to logon user %s, errno=%s(%d)", run_as_user, strerror(errno), errno); pid_unpublish(pid_slot, 0); @@ -4141,8 +4223,7 @@ int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeou if (envp != NULL) { combined_env = env_combine((const WCHAR*)env, envp); if (!combined_env) { - errno = ENOMEM; - os_syslog(OS_LVL_INFO, "failed to combine environment strings (out of memory)"); + os_syslog(OS_LVL_INFO, "failed to combine environment strings, errno=%s(%d)", strerror(errno), errno); pid_unpublish(pid_slot, 0); DestroyEnvironmentBlock(env); CloseHandle(h_token); @@ -4169,7 +4250,7 @@ int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeou } if (!ret) { windows_errno(GetLastError()); - os_syslog(OS_LVL_INFO, "failed to create process '%s' for script, errno=%s(%d)", u16tou8_force(cmd_buffer_conv, sizeof(cmd_buffer_conv), cmd_buffer, wcslen(cmd_buffer) + 1, 0), strerror(errno), errno); + os_syslog(OS_LVL_INFO, "failed to create process '%s' for script, errno=%s(%d)", resolved_path, strerror(errno), errno); pid_unpublish(pid_slot, 0); return -1; } @@ -4600,4 +4681,3 @@ void os_done(void) } #endif - diff --git a/os/mingw.h b/os/mingw.h index 2e128ead..11d3a072 100644 --- a/os/mingw.h +++ b/os/mingw.h @@ -30,17 +30,21 @@ /** * Convert a path to the Windows format. * - * If only_is_required is 1, the extended-length format is used only if required. + * If only_if_required is 1, paths shorter than 248 UTF-8 bytes are not given + * an extended-length prefix. Longer paths follow the prefix rules below. + * This preserves compatibility with the legacy Win32 MAX_PATH limit for + * creating a directory: its path must leave room for an appended 8.3 filename. * * The exact operation done is: * - If it's a '\\?\' or '\\.\' path, convert any '/' to '\'. * - If it's a disk designator path, like 'D:\' or 'D:/', it prepends '\\?\' to the path and convert any '/' to '\'. * - If it's a UNC path, like ''\\server'', it prepends '\\?\UNC\' to the path and convert any '/' to '\'. - * - Otherwise, only the UTF conversion is done. In this case Windows imposes a limit of 260 chars, and automatically convert any '/' to '\'. + * - Otherwise, only UTF conversion and '/' to '\' replacement are done. Relative paths remain limited to MAX_PATH. * * For more details see: * Naming Files, Paths, and Namespaces * http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#maxpath + * \return The converted path, or 0 with errno set from the conversion error. */ wchar_t* convert_arg(wchar_t* conv_buf, const char* src, int only_if_required); @@ -51,7 +55,7 @@ wchar_t* convert_arg(wchar_t* conv_buf, const char* src, int only_if_required); * Convert a generic string from UTF16 to UTF8. * \param conv_buf Destination UTF-8 buffer of size CONV_MAX. * \param src Source UTF-16 null-terminated string. - * \return The converted string. It never fails. + * \return The converted string, or 0 with errno set from the conversion error. */ char* u16tou8(char* conv_buf, const wchar_t* src); @@ -59,7 +63,7 @@ char* u16tou8(char* conv_buf, const wchar_t* src); * Convert a generic string from UTF8 to UTF16. * \param conv_buf Destination UTF-16 buffer of size CONV_MAX. * \param src Source UTF-8 null-terminated string. - * \return The converted string. It never fails. + * \return The converted string, or 0 with errno set from the conversion error. */ wchar_t* u8tou16(wchar_t* conv_buf, const char* src); @@ -70,7 +74,7 @@ wchar_t* u8tou16(wchar_t* conv_buf, const char* src); * \param src Source UTF-16 string. * \param number_of_wchar Number of wide characters to convert. * \param result_length_without_terminator Optional pointer to receive the converted length. - * \return The converted string, or 0 on failure. + * \return The converted string, or 0 with errno set from the conversion error. */ char* u16tou8_mayfail(char* conv_buf, size_t number_of_char, const wchar_t* src, size_t number_of_wchar, size_t* result_length_without_terminator); @@ -81,7 +85,7 @@ char* u16tou8_mayfail(char* conv_buf, size_t number_of_char, const wchar_t* src, * \param src Source UTF-8 string. * \param number_of_char Number of bytes to convert (or -1 for null-terminated string). * \param result_length_without_terminator Optional pointer to receive the converted length. - * \return The converted string, or 0 on failure. + * \return The converted string, or 0 with errno set from the conversion error. */ wchar_t* u8tou16_mayfail(wchar_t* conv_buf, size_t number_of_wchar, const char* src, size_t number_of_char, size_t* result_length_without_terminator); @@ -439,7 +443,6 @@ unsigned windows_sleep(unsigned seconds); */ int windows_readlink(const char* file, char* buffer, size_t size); - /** * Like symlink(). * Return ENOSYS if symlinks are not supported.