Skip to content

Instantly share code, notes, and snippets.

@taichi
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save taichi/3f774b667c2602cba85b to your computer and use it in GitHub Desktop.
Save taichi/3f774b667c2602cba85b to your computer and use it in GitHub Desktop.
static int
winnt_stat(const WCHAR *path, struct stati64 *st)
{
HANDLE h;
WIN32_FIND_DATAW wfd;
WIN32_FILE_ATTRIBUTE_DATA wfa;
const WCHAR *p = path;
memset(st, 0, sizeof(*st));
st->st_nlink = 1;
if (wcsncmp(p, L"\\\\?\\", 4) == 0) p += 4;
if (wcspbrk(p, L"?*")) {
errno = ENOENT;
return -1;
}
if (GetFileAttributesExW(path, GetFileExInfoStandard, (void*)&wfa)) {
if (wfa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (check_valid_dir(path)) return -1;
st->st_size = 0;
}
else {
st->st_size = ((__int64)wfa.nFileSizeHigh << 32) | wfa.nFileSizeLow;
}
st->st_mode = fileattr_to_unixmode(wfa.dwFileAttributes, path);
st->st_atime = filetime_to_unixtime(&wfa.ftLastAccessTime);
st->st_mtime = filetime_to_unixtime(&wfa.ftLastWriteTime);
st->st_ctime = filetime_to_unixtime(&wfa.ftCreationTime);
}
else {
/* GetFileAttributesEx failed; check why. */
int e = GetLastError();
if ((e == ERROR_FILE_NOT_FOUND) || (e == ERROR_INVALID_NAME)
|| (e == ERROR_PATH_NOT_FOUND || (e == ERROR_BAD_NETPATH))) {
errno = map_errno(e);
return -1;
}
/* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
h = FindFirstFileW(path, &wfd);
if (h != INVALID_HANDLE_VALUE) {
FindClose(h);
st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path);
st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
}
else {
errno = map_errno(GetLastError());
return -1;
}
}
st->st_dev = st->st_rdev = (iswalpha(path[0]) && path[1] == L':') ?
towupper(path[0]) - L'A' : _getdrive() - 1;
return 0;
}
require "ffi"
module Win32
extend FFI::Library
ffi_lib 'kernel32'
ffi_convention :stdcall
typedef :uint , :DWORD
typedef :pointer , :HANDLE
typedef :bool , :BOOL
typedef :ulong_long , :ULONGLONG
typedef :pointer , :LPTSTR
typedef :pointer , :LPCTSTR
typedef :pointer , :LPCVOID
typedef :pointer , :LPSECURITY_ATTRIBUTES
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
class FILETIME < FFI::Struct
layout(
:dwLowDateTime , :DWORD,
:dwHighDateTime, :DWORD,
)
end
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx
class FILE_INFORMATION < FFI::Struct
layout(
:dwFileAttributes, :DWORD ,
:ftCreationTime, FILETIME,
:ftLastAccessTime, FILETIME,
:ftLastWriteTime, FILETIME,
:dwVolumeSerialNumber, :DWORD ,
:nFileSizeHigh, :DWORD ,
:nFileSizeLow, :DWORD ,
:nNumberOfLinks, :DWORD ,
:nFileIndexHigh, :DWORD ,
:nFileIndexLow, :DWORD ,
)
end
INVALID_HANDLE_VALUE = -1
# http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms724211(v=vs.85).aspx
attach_function :CloseHandle, [:HANDLE], :BOOL
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa374892(v=vs.85).aspx
GENERIC_READ = 0x80000000
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
FILE_SHARE_DELETE= 0x00000004
OPEN_EXISTING = 3
FILE_ATTRIBUTE_NORMAL = 0x00000080
attach_function :CreateFile, :CreateFileW, [
:LPCTSTR , # lpFileName,
:DWORD , # dwDesiredAccess,
:DWORD , # dwShareMode,
:LPSECURITY_ATTRIBUTES , # lpSecurityAttributes,
:DWORD , # dwCreationDisposition,
:DWORD , # dwFlagsAndAttributes,
:HANDLE , # hTemplateFile
], :HANDLE
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa364952(v=vs.85).aspx
typedef :pointer, :LPBY_HANDLE_FILE_INFORMATION
attach_function :GetFileInformationByHandle, [
:HANDLE,
:LPBY_HANDLE_FILE_INFORMATION
], :BOOL
def self.last_error(err_at, err = FFI.errno)
SystemCallError.new(err_at, err)
end
def self.to_unicode(path)
(path.tr(::File::SEPARATOR, ::File::ALT_SEPARATOR) + 0.chr).encode('UTF-16LE')
end
def self.to_large_integer(h, l)
(h << 32) | l
end
def self.to_time(ft)
t = to_large_integer ft[:dwHighDateTime], ft[:dwLowDateTime]
Time.at t / 10000000 - 11644473600
end
class File
def self.stat(path)
share = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE
wpath = Win32.to_unicode path
handle = Win32.CreateFile(wpath, GENERIC_READ, share, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nil)
if INVALID_HANDLE_VALUE == handle.address
raise Errno::ENOENT.new path
end
begin
fi = FILE_INFORMATION.new
unless Win32.GetFileInformationByHandle(handle, fi)
raise last_error("GetFileInformationByHandle")
end
Stat.new fi
ensure
Win32.CloseHandle(handle)
end
end
class Stat
attr_reader :rdev
attr_reader :ino
attr_reader :size
attr_reader :atime
attr_reader :mtime
attr_reader :ctime
def initialize(fi)
@rdev = fi[:dwVolumeSerialNumber]
@ino = Win32.to_large_integer fi[:nFileIndexHigh], fi[:nFileIndexLow]
@size = Win32.to_large_integer fi[:nFileSizeHigh] , fi[:nFileSizeLow]
@atime = Win32.to_time fi[:ftLastAccessTime]
@mtime = Win32.to_time fi[:ftLastWriteTime]
@ctime = Win32.to_time fi[:ftCreationTime]
end
def to_s
"#<Win32::File::Stat rdev=#{@rdev} ino=#{@ino} size=#{@size} atime=#{@atime} mtime=#{@mtime} ctime=#{@ctime}>"
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment