Alien-uv
view release on metacpan or search on metacpan
libuv/src/win/fs.c view on Meta::CPAN
pos = buf;
if (path != NULL) {
DWORD r = MultiByteToWideChar(CP_UTF8,
0,
path,
-1,
(WCHAR*) pos,
pathw_len);
assert(r == (DWORD) pathw_len);
req->file.pathw = (WCHAR*) pos;
pos += r * sizeof(WCHAR);
} else {
req->file.pathw = NULL;
}
if (new_path != NULL) {
DWORD r = MultiByteToWideChar(CP_UTF8,
0,
new_path,
-1,
(WCHAR*) pos,
new_pathw_len);
assert(r == (DWORD) new_pathw_len);
req->fs.info.new_pathw = (WCHAR*) pos;
pos += r * sizeof(WCHAR);
} else {
req->fs.info.new_pathw = NULL;
}
req->path = path;
if (path != NULL && copy_path) {
memcpy(pos, path, path_len);
assert(path_len == buf_sz - (pos - buf));
req->path = pos;
}
req->flags |= UV_FS_FREE_PATHS;
return 0;
}
INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req,
uv_fs_type fs_type, const uv_fs_cb cb) {
uv__once_init();
UV_REQ_INIT(req, UV_FS);
req->loop = loop;
req->flags = 0;
req->fs_type = fs_type;
req->result = 0;
req->ptr = NULL;
req->path = NULL;
req->cb = cb;
memset(&req->fs, 0, sizeof(req->fs));
}
static int fs__wide_to_utf8(WCHAR* w_source_ptr,
DWORD w_source_len,
char** target_ptr,
uint64_t* target_len_ptr) {
int r;
int target_len;
char* target;
target_len = WideCharToMultiByte(CP_UTF8,
0,
w_source_ptr,
w_source_len,
NULL,
0,
NULL,
NULL);
if (target_len == 0) {
return -1;
}
if (target_len_ptr != NULL) {
*target_len_ptr = target_len;
}
if (target_ptr == NULL) {
return 0;
}
target = uv__malloc(target_len + 1);
if (target == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
return -1;
}
r = WideCharToMultiByte(CP_UTF8,
0,
w_source_ptr,
w_source_len,
target,
target_len,
NULL,
NULL);
assert(r == target_len);
target[target_len] = '\0';
*target_ptr = target;
return 0;
}
INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr,
uint64_t* target_len_ptr) {
char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer;
WCHAR* w_target;
DWORD w_target_len;
DWORD bytes;
if (!DeviceIoControl(handle,
FSCTL_GET_REPARSE_POINT,
NULL,
0,
libuv/src/win/fs.c view on Meta::CPAN
w_target[3] == L'\\') {
/* Starts with \??\ */
if (w_target_len >= 6 &&
((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
(w_target[4] >= L'a' && w_target[4] <= L'z')) &&
w_target[5] == L':' &&
(w_target_len == 6 || w_target[6] == L'\\')) {
/* \??\<drive>:\ */
w_target += 4;
w_target_len -= 4;
} else if (w_target_len >= 8 &&
(w_target[4] == L'U' || w_target[4] == L'u') &&
(w_target[5] == L'N' || w_target[5] == L'n') &&
(w_target[6] == L'C' || w_target[6] == L'c') &&
w_target[7] == L'\\') {
/* \??\UNC\<server>\<share>\ - make sure the final path looks like
* \\<server>\<share>\ */
w_target += 6;
w_target[0] = L'\\';
w_target_len -= 6;
}
}
} else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
/* Junction. */
w_target = reparse_data->MountPointReparseBuffer.PathBuffer +
(reparse_data->MountPointReparseBuffer.SubstituteNameOffset /
sizeof(WCHAR));
w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength /
sizeof(WCHAR);
/* Only treat junctions that look like \??\<drive>:\ as symlink. Junctions
* can also be used as mount points, like \??\Volume{<guid>}, but that's
* confusing for programs since they wouldn't be able to actually
* understand such a path when returned by uv_readlink(). UNC paths are
* never valid for junctions so we don't care about them. */
if (!(w_target_len >= 6 &&
w_target[0] == L'\\' &&
w_target[1] == L'?' &&
w_target[2] == L'?' &&
w_target[3] == L'\\' &&
((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
(w_target[4] >= L'a' && w_target[4] <= L'z')) &&
w_target[5] == L':' &&
(w_target_len == 6 || w_target[6] == L'\\'))) {
SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
return -1;
}
/* Remove leading \??\ */
w_target += 4;
w_target_len -= 4;
} else {
/* Reparse tag does not indicate a symlink. */
SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
return -1;
}
return fs__wide_to_utf8(w_target, w_target_len, target_ptr, target_len_ptr);
}
void fs__open(uv_fs_t* req) {
DWORD access;
DWORD share;
DWORD disposition;
DWORD attributes = 0;
HANDLE file;
int fd, current_umask;
int flags = req->fs.info.file_flags;
/* Obtain the active umask. umask() never fails and returns the previous
* umask. */
current_umask = umask(0);
umask(current_umask);
/* convert flags and mode to CreateFile parameters */
switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) {
case UV_FS_O_RDONLY:
access = FILE_GENERIC_READ;
break;
case UV_FS_O_WRONLY:
access = FILE_GENERIC_WRITE;
break;
case UV_FS_O_RDWR:
access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
break;
default:
goto einval;
}
if (flags & UV_FS_O_APPEND) {
access &= ~FILE_WRITE_DATA;
access |= FILE_APPEND_DATA;
}
/*
* Here is where we deviate significantly from what CRT's _open()
* does. We indiscriminately use all the sharing modes, to match
* UNIX semantics. In particular, this ensures that the file can
* be deleted even whilst it's open, fixing issue #1449.
* We still support exclusive sharing mode, since it is necessary
* for opening raw block devices, otherwise Windows will prevent
* any attempt to write past the master boot record.
*/
if (flags & UV_FS_O_EXLOCK) {
share = 0;
} else {
share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
switch (flags & (UV_FS_O_CREAT | UV_FS_O_EXCL | UV_FS_O_TRUNC)) {
case 0:
case UV_FS_O_EXCL:
disposition = OPEN_EXISTING;
break;
case UV_FS_O_CREAT:
disposition = OPEN_ALWAYS;
break;
libuv/src/win/fs.c view on Meta::CPAN
size_t dirents_used = 0;
IO_STATUS_BLOCK iosb;
NTSTATUS status;
/* Buffer to hold directory entries returned by NtQueryDirectoryFile.
* It's important that this buffer can hold at least one entry, regardless
* of the length of the file names present in the enumerated directory.
* A file name is at most 256 WCHARs long.
* According to MSDN, the buffer must be aligned at an 8-byte boundary.
*/
#if _MSC_VER
__declspec(align(8)) char buffer[8192];
#else
__attribute__ ((aligned (8))) char buffer[8192];
#endif
STATIC_ASSERT(sizeof buffer >=
sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR));
/* Open the directory. */
dir_handle =
CreateFileW(req->file.pathw,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (dir_handle == INVALID_HANDLE_VALUE)
goto win32_error;
/* Read the first chunk. */
status = pNtQueryDirectoryFile(dir_handle,
NULL,
NULL,
NULL,
&iosb,
&buffer,
sizeof buffer,
FileDirectoryInformation,
FALSE,
NULL,
TRUE);
/* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER.
* This should be reported back as UV_ENOTDIR.
*/
if (status == STATUS_INVALID_PARAMETER)
goto not_a_directory_error;
while (NT_SUCCESS(status)) {
char* position = buffer;
size_t next_entry_offset = 0;
do {
FILE_DIRECTORY_INFORMATION* info;
uv__dirent_t* dirent;
size_t wchar_len;
size_t utf8_len;
/* Obtain a pointer to the current directory entry. */
position += next_entry_offset;
info = (FILE_DIRECTORY_INFORMATION*) position;
/* Fetch the offset to the next directory entry. */
next_entry_offset = info->NextEntryOffset;
/* Compute the length of the filename in WCHARs. */
wchar_len = info->FileNameLength / sizeof info->FileName[0];
/* Skip over '.' and '..' entries. It has been reported that
* the SharePoint driver includes the terminating zero byte in
* the filename length. Strip those first.
*/
while (wchar_len > 0 && info->FileName[wchar_len - 1] == L'\0')
wchar_len -= 1;
if (wchar_len == 0)
continue;
if (wchar_len == 1 && info->FileName[0] == L'.')
continue;
if (wchar_len == 2 && info->FileName[0] == L'.' &&
info->FileName[1] == L'.')
continue;
/* Compute the space required to store the filename as UTF-8. */
utf8_len = WideCharToMultiByte(
CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL);
if (utf8_len == 0)
goto win32_error;
/* Resize the dirent array if needed. */
if (dirents_used >= dirents_size) {
size_t new_dirents_size =
dirents_size == 0 ? dirents_initial_size : dirents_size << 1;
uv__dirent_t** new_dirents =
uv__realloc(dirents, new_dirents_size * sizeof *dirents);
if (new_dirents == NULL)
goto out_of_memory_error;
dirents_size = new_dirents_size;
dirents = new_dirents;
}
/* Allocate space for the uv dirent structure. The dirent structure
* includes room for the first character of the filename, but `utf8_len`
* doesn't count the NULL terminator at this point.
*/
dirent = uv__malloc(sizeof *dirent + utf8_len);
if (dirent == NULL)
goto out_of_memory_error;
dirents[dirents_used++] = dirent;
/* Convert file name to UTF-8. */
if (WideCharToMultiByte(CP_UTF8,
0,
&info->FileName[0],
wchar_len,
&dirent->d_name[0],
utf8_len,
NULL,
NULL) == 0)
goto win32_error;
/* Add a null terminator to the filename. */
dirent->d_name[utf8_len] = '\0';
/* Fill out the type field. */
if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE)
dirent->d_type = UV__DT_CHAR;
else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
dirent->d_type = UV__DT_LINK;
else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
dirent->d_type = UV__DT_DIR;
else
dirent->d_type = UV__DT_FILE;
} while (next_entry_offset != 0);
/* Read the next chunk. */
status = pNtQueryDirectoryFile(dir_handle,
NULL,
NULL,
NULL,
&iosb,
&buffer,
sizeof buffer,
FileDirectoryInformation,
FALSE,
NULL,
FALSE);
/* After the first pNtQueryDirectoryFile call, the function may return
* STATUS_SUCCESS even if the buffer was too small to hold at least one
* directory entry.
*/
if (status == STATUS_SUCCESS && iosb.Information == 0)
status = STATUS_BUFFER_OVERFLOW;
}
if (status != STATUS_NO_MORE_FILES)
goto nt_error;
CloseHandle(dir_handle);
/* Store the result in the request object. */
req->ptr = dirents;
if (dirents != NULL)
req->flags |= UV_FS_FREE_PTR;
SET_REQ_RESULT(req, dirents_used);
/* `nbufs` will be used as index by uv_fs_scandir_next. */
req->fs.info.nbufs = 0;
return;
nt_error:
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
goto cleanup;
win32_error:
SET_REQ_WIN32_ERROR(req, GetLastError());
goto cleanup;
not_a_directory_error:
SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
libuv/src/win/fs.c view on Meta::CPAN
find_path = uv__malloc(sizeof(WCHAR) * (len + 4));
if (find_path == NULL) {
SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
goto error;
}
_snwprintf(find_path, len + 3, fmt, pathw);
dir->dir_handle = FindFirstFileW(find_path, &dir->find_data);
uv__free(find_path);
find_path = NULL;
if (dir->dir_handle == INVALID_HANDLE_VALUE &&
GetLastError() != ERROR_FILE_NOT_FOUND) {
SET_REQ_WIN32_ERROR(req, GetLastError());
goto error;
}
dir->need_find_call = FALSE;
req->ptr = dir;
SET_REQ_RESULT(req, 0);
return;
error:
uv__free(dir);
uv__free(find_path);
req->ptr = NULL;
}
void fs__readdir(uv_fs_t* req) {
uv_dir_t* dir;
uv_dirent_t* dirents;
uv__dirent_t dent;
unsigned int dirent_idx;
PWIN32_FIND_DATAW find_data;
unsigned int i;
int r;
req->flags |= UV_FS_FREE_PTR;
dir = req->ptr;
dirents = dir->dirents;
memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
find_data = &dir->find_data;
dirent_idx = 0;
while (dirent_idx < dir->nentries) {
if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) {
if (GetLastError() == ERROR_NO_MORE_FILES)
break;
goto error;
}
/* Skip "." and ".." entries. */
if (find_data->cFileName[0] == L'.' &&
(find_data->cFileName[1] == L'\0' ||
(find_data->cFileName[1] == L'.' &&
find_data->cFileName[2] == L'\0'))) {
dir->need_find_call = TRUE;
continue;
}
r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName,
-1,
(char**) &dirents[dirent_idx].name);
if (r != 0)
goto error;
/* Copy file type. */
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
dent.d_type = UV__DT_DIR;
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
dent.d_type = UV__DT_LINK;
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
dent.d_type = UV__DT_CHAR;
else
dent.d_type = UV__DT_FILE;
dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent);
dir->need_find_call = TRUE;
++dirent_idx;
}
SET_REQ_RESULT(req, dirent_idx);
return;
error:
SET_REQ_WIN32_ERROR(req, GetLastError());
for (i = 0; i < dirent_idx; ++i) {
uv__free((char*) dirents[i].name);
dirents[i].name = NULL;
}
}
void fs__closedir(uv_fs_t* req) {
uv_dir_t* dir;
dir = req->ptr;
FindClose(dir->dir_handle);
uv__free(req->ptr);
SET_REQ_RESULT(req, 0);
}
INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
int do_lstat) {
FILE_ALL_INFORMATION file_info;
FILE_FS_VOLUME_INFORMATION volume_info;
NTSTATUS nt_status;
IO_STATUS_BLOCK io_status;
nt_status = pNtQueryInformationFile(handle,
&io_status,
&file_info,
sizeof file_info,
FileAllInformation);
/* Buffer overflow (a warning status code) is expected here. */
if (NT_ERROR(nt_status)) {
SetLastError(pRtlNtStatusToDosError(nt_status));
return -1;
}
nt_status = pNtQueryVolumeInformationFile(handle,
libuv/src/win/fs.c view on Meta::CPAN
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
SET_REQ_WIN32_ERROR(req, GetLastError());
CloseHandle(handle);
return;
}
req->flags |= UV_FS_FREE_PTR;
SET_REQ_RESULT(req, 0);
CloseHandle(handle);
}
static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) {
int r;
DWORD w_realpath_len;
WCHAR* w_realpath_ptr = NULL;
WCHAR* w_realpath_buf;
w_realpath_len = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS);
if (w_realpath_len == 0) {
return -1;
}
w_realpath_buf = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
if (w_realpath_buf == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
return -1;
}
w_realpath_ptr = w_realpath_buf;
if (GetFinalPathNameByHandleW(
handle, w_realpath_ptr, w_realpath_len, VOLUME_NAME_DOS) == 0) {
uv__free(w_realpath_buf);
SetLastError(ERROR_INVALID_HANDLE);
return -1;
}
/* convert UNC path to long path */
if (wcsncmp(w_realpath_ptr,
UNC_PATH_PREFIX,
UNC_PATH_PREFIX_LEN) == 0) {
w_realpath_ptr += 6;
*w_realpath_ptr = L'\\';
w_realpath_len -= 6;
} else if (wcsncmp(w_realpath_ptr,
LONG_PATH_PREFIX,
LONG_PATH_PREFIX_LEN) == 0) {
w_realpath_ptr += 4;
w_realpath_len -= 4;
} else {
uv__free(w_realpath_buf);
SetLastError(ERROR_INVALID_HANDLE);
return -1;
}
r = fs__wide_to_utf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL);
uv__free(w_realpath_buf);
return r;
}
static void fs__realpath(uv_fs_t* req) {
HANDLE handle;
handle = CreateFileW(req->file.pathw,
0,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (handle == INVALID_HANDLE_VALUE) {
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) {
CloseHandle(handle);
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
CloseHandle(handle);
req->flags |= UV_FS_FREE_PTR;
SET_REQ_RESULT(req, 0);
}
static void fs__chown(uv_fs_t* req) {
req->result = 0;
}
static void fs__fchown(uv_fs_t* req) {
req->result = 0;
}
static void fs__lchown(uv_fs_t* req) {
req->result = 0;
}
static void uv__fs_work(struct uv__work* w) {
uv_fs_t* req;
req = container_of(w, uv_fs_t, work_req);
assert(req->type == UV_FS);
#define XX(uc, lc) case UV_FS_##uc: fs__##lc(req); break;
switch (req->fs_type) {
XX(OPEN, open)
XX(CLOSE, close)
XX(READ, read)
XX(WRITE, write)
XX(COPYFILE, copyfile)
XX(SENDFILE, sendfile)
XX(STAT, stat)
( run in 0.707 second using v1.01-cache-2.11-cpan-acebb50784d )