Issue #11216 has been updated by Usaku NAKAMURA.


go ahead!

----------------------------------------
Feature #11216: inode for Windows
https://bugs.ruby-lang.org/issues/11216#change-52828

* Author: Yui NARUSE
* Status: Open
* Priority: Normal
* Assignee: 
----------------------------------------
現在WindowsではFile::Stat#inodeは常に0を返しますが、
例えばあるlogrotate的な運用の行われるファイルを監視・開きっぱなしにして、追記されれば差分を読み、
rotateされた場合は検出して最新のログを読む…というような場合に、rotateを検出する際にinodeは使われます。

WindowsにはGetFileInformationByHandle()というAPIで得られるBY_HANDLE_FILE_INFORMATION構造体のメンバ、
nFileIndexHigh/Lowから64bitのファイルシステムuniqueなIDがあるので、これをinodeとして代用が可能です。

問題はこれをFile::Statにいれるにはもとのstruct stat.inoが16bitで小さすぎる点ですが、
幸いstruct stati64にはアライメントの都合で隙間があるため、そこにつっこむことにしています。
(devとudevが常に同じとか、uidやgidが常に0など無意味な値は他にもありますが)
https://msdn.microsoft.com/ja-jp/library/ms350241%28v=vs.71%29.aspx

```
diff --git a/file.c b/file.c
index ac99de6..761ff1f 100644
--- a/file.c
+++ b/file.c
@@ -548,7 +548,18 @@ rb_stat_dev_minor(VALUE self)
 static VALUE
 rb_stat_ino(VALUE self)
 {
-#if SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
+#ifdef _WIN32
+    struct stat *st = get_stat(self);
+    unsigned short *p2 = (unsigned short *)st;
+    unsigned int *p4 = (unsigned int *)st;
+    uint64_t r;
+    r = p2[2];
+    r <<= 16;
+    r |= p2[7];
+    r <<= 32;
+    r |= p4[5];
+    return ULL2NUM(r);
+#elif SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
     return ULL2NUM(get_stat(self)->st_ino);
 #else
     return ULONG2NUM(get_stat(self)->st_ino);
diff --git a/win32/win32.c b/win32/win32.c
index b23e9af..fcfc0a6 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -4975,6 +4975,48 @@ static time_t filetime_to_unixtime(const FILETIME *ft);
 static WCHAR *name_for_stat(WCHAR *buf, const WCHAR *path);
 static DWORD stati64_handle(HANDLE h, struct stati64 *st);
 
+/* License: Ruby's */
+static void
+stati64_set_inode(PBY_HANDLE_FILE_INFORMATION pinfo, struct stati64 *st)
+{
+    /* struct stati64 layout
+     *
+     * dev: 0-3
+     * ino: 4-5
+     * mode: 6-7
+     * nlink: 8-9
+     * uid: 10-11
+     * gid: 12-13
+     * _: 14-15
+     * rdev: 16-19
+     * _: 20-23
+     * size: 24-31
+     * atime: 32-39
+     * mtime: 40-47
+     * ctime: 48-55
+     *
+     */
+    unsigned short *p2 = (unsigned short *)st;
+    unsigned int *p4 = (unsigned int *)st;
+    DWORD high = pinfo->nFileIndexHigh;
+    p2[2] = high >> 16;
+    p2[7] = high & 0xFFFF;
+    p4[5] = pinfo->nFileIndexLow;
+}
+
+/* License: Ruby's */
+static DWORD
+stati64_set_inode_handle(HANDLE h, struct stati64 *st)
+{
+    BY_HANDLE_FILE_INFORMATION info;
+    DWORD attr = (DWORD)-1;
+
+    if (GetFileInformationByHandle(h, &info)) {
+	stati64_set_inode(&info, st);
+    }
+    return attr;
+}
+
 #undef fstat
 /* License: Ruby's */
 int
@@ -5000,7 +5042,11 @@ rb_w32_fstati64(int fd, struct stati64 *st)
     struct stat tmp;
     int ret;
 
-    if (GetEnvironmentVariableW(L"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) return _fstati64(fd, st);
+    if (GetEnvironmentVariableW(L"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+	ret = _fstati64(fd, st);
+	stati64_set_inode_handle((HANDLE)_get_osfhandle(fd), st);
+	return ret;
+    }
     ret = fstat(fd, &tmp);
 
     if (ret) return ret;
@@ -5022,6 +5068,7 @@ stati64_handle(HANDLE h, struct stati64 *st)
 	st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
 	st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
 	st->st_nlink = info.nNumberOfLinks;
+	stati64_set_inode(&info, st);
 	attr = info.dwFileAttributes;
     }
     return attr;
```



-- 
https://bugs.ruby-lang.org/