windows: reimplement uv_fs_stat using NT syscalls
This improves the output of uv_fs_stat: * `st_ctime` now contains the change time, not the creation time. * `st_ino` is now filled in with an fs-specific unique number. * `st_dev` is set to the serial number of the containing file system. * `st_blocks` now gets set. * `st_blksize` is no longer zero, but set to a reasonable default.
This commit is contained in:
parent
1cc6f96f0c
commit
20a8e58adb
129
src/win/fs.c
129
src/win/fs.c
@ -241,7 +241,7 @@ static int is_path_dir(const WCHAR* path) {
|
||||
|
||||
|
||||
INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr,
|
||||
int64_t* target_len_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;
|
||||
@ -809,56 +809,117 @@ void fs__readdir(uv_fs_t* req) {
|
||||
|
||||
|
||||
INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) {
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
FILE_ALL_INFORMATION file_info;
|
||||
FILE_FS_VOLUME_INFORMATION volume_info;
|
||||
NTSTATUS nt_status;
|
||||
IO_STATUS_BLOCK io_status;
|
||||
|
||||
if (!GetFileInformationByHandle(handle, &info)) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* TODO: set st_dev, st_rdev and st_ino to something meaningful. */
|
||||
statbuf->st_ino = 0;
|
||||
statbuf->st_dev = 0;
|
||||
statbuf->st_rdev = 0;
|
||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||
&io_status,
|
||||
&volume_info,
|
||||
sizeof volume_info,
|
||||
FileFsVolumeInformation);
|
||||
|
||||
statbuf->st_gid = 0;
|
||||
statbuf->st_uid = 0;
|
||||
/* Buffer overflow (a warning status code) is expected here. */
|
||||
if (NT_ERROR(nt_status)) {
|
||||
SetLastError(pRtlNtStatusToDosError(nt_status));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Todo: st_mode should probably always be 0666 for everyone. We might also
|
||||
* want to report 0777 if the file is a .exe or a directory.
|
||||
*
|
||||
* Currently it's based on whether the 'readonly' attribute is set, which
|
||||
* makes little sense because the semantics are so different: the 'read-only'
|
||||
* flag is just a way for a user to protect against accidental deleteion, and
|
||||
* serves no security purpose. Windows uses ACLs for that.
|
||||
*
|
||||
* Also people now use uv_fs_chmod() to take away the writable bit for good
|
||||
* reasons. Windows however just makes the file read-only, which makes it
|
||||
* impossible to delete the file afterwards, since read-only files can't be
|
||||
* deleted.
|
||||
*
|
||||
* IOW it's all just a clusterfuck and we should think of something that
|
||||
* makes slighty more sense.
|
||||
*
|
||||
* And uv_fs_chmod should probably just fail on windows or be a total no-op.
|
||||
* There's nothing sensible it can do anyway.
|
||||
*/
|
||||
statbuf->st_mode = 0;
|
||||
|
||||
statbuf->st_blksize = 0;
|
||||
statbuf->st_blocks = 0;
|
||||
|
||||
statbuf->st_flags = 0;
|
||||
statbuf->st_gen = 0;
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
statbuf->st_mode |= S_IFLNK;
|
||||
} else if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0)
|
||||
return -1;
|
||||
|
||||
} else if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
statbuf->st_mode |= _S_IFDIR;
|
||||
statbuf->st_size = 0;
|
||||
|
||||
} else {
|
||||
statbuf->st_mode |= _S_IFREG;
|
||||
statbuf->st_size = ((int64_t) info.nFileSizeHigh << 32) +
|
||||
(int64_t) info.nFileSizeLow;
|
||||
statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart;
|
||||
}
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||||
statbuf->st_mode |= (_S_IREAD + (_S_IREAD >> 3) + (_S_IREAD >> 6));
|
||||
} else {
|
||||
statbuf->st_mode |= ((_S_IREAD|_S_IWRITE) + ((_S_IREAD|_S_IWRITE) >> 3) +
|
||||
((_S_IREAD|_S_IWRITE) >> 6));
|
||||
}
|
||||
if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
|
||||
else
|
||||
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
||||
((_S_IREAD | _S_IWRITE) >> 6);
|
||||
|
||||
FILETIME_TO_TIMESPEC(statbuf->st_mtim, info.ftLastWriteTime);
|
||||
FILETIME_TO_TIMESPEC(statbuf->st_atim, info.ftLastAccessTime);
|
||||
FILETIME_TO_TIMESPEC(statbuf->st_ctim, info.ftCreationTime);
|
||||
FILETIME_TO_TIMESPEC(statbuf->st_birthtim, info.ftCreationTime);
|
||||
FILETIME_TO_TIMESPEC(statbuf->st_atim, file_info.BasicInformation.LastAccessTime);
|
||||
FILETIME_TO_TIMESPEC(statbuf->st_ctim, file_info.BasicInformation.ChangeTime);
|
||||
FILETIME_TO_TIMESPEC(statbuf->st_mtim, file_info.BasicInformation.LastWriteTime);
|
||||
FILETIME_TO_TIMESPEC(statbuf->st_birthtim, file_info.BasicInformation.CreationTime);
|
||||
|
||||
statbuf->st_nlink = (info.nNumberOfLinks <= SHRT_MAX) ?
|
||||
(short) info.nNumberOfLinks : SHRT_MAX;
|
||||
statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart;
|
||||
|
||||
/* st_blocks contains the on-disk allocation size in 512-byte units. */
|
||||
statbuf->st_blocks =
|
||||
file_info.StandardInformation.AllocationSize.QuadPart >> 9ULL;
|
||||
|
||||
statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks;
|
||||
|
||||
statbuf->st_dev = volume_info.VolumeSerialNumber;
|
||||
|
||||
/* The st_blksize is supposed to be the 'optimal' number of bytes for reading
|
||||
* and writing to the disk. That is, for any definition of 'optimal' - it's
|
||||
* supposed to at least avoid read-update-write behavior when writing to the
|
||||
* disk.
|
||||
*
|
||||
* However nobody knows this and even fewer people actually use this value,
|
||||
* and in order to fill it out we'd have to make another syscall to query the
|
||||
* volume for FILE_FS_SECTOR_SIZE_INFORMATION.
|
||||
*
|
||||
* Therefore we'll just report a sensible value that's quite commonly okay
|
||||
* on modern hardware.
|
||||
*/
|
||||
statbuf->st_blksize = 2048;
|
||||
|
||||
/* Todo: set st_flags to something meaningful. Also provide a wrapper for
|
||||
* chattr(2).
|
||||
*/
|
||||
statbuf->st_flags = 0;
|
||||
|
||||
/* Windows has nothing sensible to say about these values, so they'll just
|
||||
* remain empty.
|
||||
*/
|
||||
statbuf->st_gid = 0;
|
||||
statbuf->st_uid = 0;
|
||||
statbuf->st_rdev = 0;
|
||||
statbuf->st_gen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user