ClamAV is an open source (GPLv2) anti-virus toolkit.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
clamav/win32/compat/w32_stat.c

243 lines
7.4 KiB

/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc.
*
* Authors: aCaB <acab@clamav.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <io.h>
#include <Windows.h>
#include <WinNls.h>
#include "clamav-types.h"
#include "dirent.h"
#include "w32_stat.h"
static int is_abspath(const char *path)
{
int len = strlen(path);
return (len > 2 && path[0] == '\\' && path[1] == '\\') || (len >= 2 && ((*path >= 'a' && *path <= 'z') || (*path >= 'A' && *path <= 'Z')) && path[1] == ':');
}
wchar_t *uncpath(const char *path)
{
DWORD len = 0;
char utf8[PATH_MAX + 1];
wchar_t *stripme, *strip_from, *dest = malloc((PATH_MAX + 1) * sizeof(wchar_t));
if (!dest)
return NULL;
if (strncmp(path, "\\\\", 2)) {
/* NOT already UNC */
memcpy(dest, L"\\\\?\\", 8);
if (!is_abspath(path)) {
/* Relative path */
len = GetCurrentDirectoryW(PATH_MAX - 5, &dest[4]);
if (!len || len > PATH_MAX - 5) {
free(dest);
errno = (len || (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) ? ENAMETOOLONG : ENOENT;
return NULL;
}
if (*path == '\\')
len = 6; /* Current drive root */
else {
len += 4; /* A 'really' relative path */
dest[len] = L'\\';
len++;
}
} else {
/* C:\ and friends */
len = 4;
}
} else {
/* UNC already */
len = 0;
}
/* TODO: DROP THE ACP STUFF ONCE WE'RE ALL CONVERTED TO UTF-8 */
if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, &dest[len], PATH_MAX - len) &&
WideCharToMultiByte(CP_UTF8, 0, &dest[len], -1, utf8, PATH_MAX, NULL, NULL) &&
!strcmp(path, utf8)) {
} else if (!(len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, path, -1, &dest[len], PATH_MAX - len)) || len > PATH_MAX - len) {
free(dest);
errno = (len || (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) ? ENAMETOOLONG : ENOENT;
return NULL;
}
len = wcslen(dest);
strip_from = &dest[3];
/* append a backslash to naked drives and get rid of . and .. */
if (!wcsncmp(dest, L"\\\\?\\", 4) && (dest[5] == L':') && ((dest[4] >= L'A' && dest[4] <= L'Z') || (dest[4] >= L'a' && dest[4] <= L'z'))) {
if (len == 6) {
dest[6] = L'\\';
dest[7] = L'\0';
}
strip_from = &dest[6];
}
while ((stripme = wcsstr(strip_from, L"\\."))) {
wchar_t *copy_from, *copy_to;
if (!stripme[2] || stripme[2] == L'\\') {
copy_from = &stripme[2];
copy_to = stripme;
} else if (stripme[2] == L'.' && (!stripme[3] || stripme[3] == L'\\')) {
*stripme = L'\0';
copy_from = &stripme[3];
copy_to = wcsrchr(strip_from, L'\\');
if (!copy_to)
copy_to = stripme;
} else {
strip_from = &stripme[1];
continue;
}
while (1) {
*copy_to = *copy_from;
if (!*copy_from) break;
copy_to++;
copy_from++;
}
}
/* strip double slashes */
if ((stripme = wcsstr(&dest[4], L"\\\\"))) {
strip_from = stripme;
while (1) {
wchar_t c = *strip_from;
strip_from++;
if (c == L'\\' && *strip_from == L'\\')
continue;
*stripme = c;
stripme++;
if (!c)
break;
}
}
if (wcslen(dest) == 6 && !wcsncmp(dest, L"\\\\?\\", 4) && (dest[5] == L':') && ((dest[4] >= L'A' && dest[4] <= L'Z') || (dest[4] >= L'a' && dest[4] <= L'z'))) {
dest[6] = L'\\';
dest[7] = L'\0';
}
return dest;
}
int safe_open(const char *path, int flags, ...)
{
wchar_t *wpath = uncpath(path);
int ret;
if (!wpath)
return -1;
if (flags & O_CREAT) {
int mode;
va_list ap;
va_start(ap, flags);
mode = va_arg(ap, int);
va_end(ap);
ret = _wopen(wpath, flags, mode);
} else
ret = _wopen(wpath, flags);
free(wpath);
return ret;
}
static time_t FileTimeToUnixTime(FILETIME t)
{
LONGLONG ll = ((LONGLONG)t.dwHighDateTime << 32) | t.dwLowDateTime;
ll -= 116444736000000000;
return (time_t)(ll / 10000000);
}
int w32_stat(const char *path, struct stat *buf)
{
int len;
wchar_t *wpath = uncpath(path);
WIN32_FILE_ATTRIBUTE_DATA attrs;
if (!wpath) {
errno = ENOMEM;
return -1;
}
len = GetFileAttributesExW(wpath, GetFileExInfoStandard, &attrs);
free(wpath);
if (!len) {
errno = ENOENT;
return -1;
}
buf->st_dev = 1;
buf->st_rdev = 1;
buf->st_uid = 0;
buf->st_gid = 0;
buf->st_ino = 1;
buf->st_atime = FileTimeToUnixTime(attrs.ftLastAccessTime);
buf->st_ctime = FileTimeToUnixTime(attrs.ftCreationTime);
buf->st_mtime = FileTimeToUnixTime(attrs.ftLastWriteTime);
buf->st_mode = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IRUSR : S_IRWXU;
buf->st_mode |= (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR : _S_IFREG;
buf->st_nlink = 1;
buf->st_size = ((uint64_t)attrs.nFileSizeHigh << (sizeof(attrs.nFileSizeLow) * 8)) | attrs.nFileSizeLow;
return 0;
}
int w32_access(const char *pathname, int mode)
{
int ret;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwDesiredAccess = GENERIC_READ;
if (W_OK & mode) {
dwDesiredAccess = dwDesiredAccess | GENERIC_WRITE;
}
hFile = CreateFileA(pathname, // file to open
dwDesiredAccess,
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_FLAG_BACKUP_SEMANTICS, // may be a directory
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_ACCESS_DENIED) {
_set_errno(EACCES);
} else {
_set_errno(ENOENT);
}
ret = -1;
} else {
CloseHandle(hFile);
ret = 0;
}
return ret;
}
#undef rename
int w32_rename(const char *oldname, const char *newname)
{
DWORD dwAttrs = GetFileAttributes(newname);
SetFileAttributes(newname, dwAttrs & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN));
if (!DeleteFileA(newname) && (GetLastError() != ERROR_FILE_NOT_FOUND)) {
return -1;
}
return rename(oldname, newname);
}