mirror of https://github.com/Cisco-Talos/clamav
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.
200 lines
5.3 KiB
200 lines
5.3 KiB
/*
|
|
* Copyright (C) 2009 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 "others.h"
|
|
#include "shared/misc.h"
|
|
|
|
wchar_t *uncpath(const char *path) {
|
|
DWORD len = 0;
|
|
char utf8[PATH_MAX+1];
|
|
wchar_t *stripme, *strip_from, *dest = cli_malloc((PATH_MAX + 1) * sizeof(wchar_t));
|
|
|
|
if(!dest)
|
|
return NULL;
|
|
|
|
if(strncmp(path, "\\\\", 2)) {
|
|
/* NOT already UNC */
|
|
memcpy(dest, L"\\\\?\\", 8);
|
|
|
|
if(!cli_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) {
|
|
wchar_t *wpath = uncpath(pathname);
|
|
int ret;
|
|
|
|
if(!wpath) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
ret = _waccess(wpath, mode);
|
|
free(wpath);
|
|
return ret;
|
|
}
|
|
|