Avoid possible overflow in fn_identity.
[pspp] / src / data / file-name.c
index b5cc43deaa2c81b8e5137f5b6a6340c1d7a3acc1..f59b2fa4ecd9bdcc367c1acffefe017859e6ad13 100644 (file)
@@ -220,8 +220,8 @@ fn_close (const char *fn, FILE *f)
    there. */
 struct file_identity
 {
-  dev_t device;               /* Device number. */
-  ino_t inode;                /* Inode number. */
+  unsigned long long device;               /* Device number. */
+  unsigned long long inode;                /* Inode number. */
   char *name;                 /* File name, where needed, otherwise NULL. */
 };
 
@@ -262,12 +262,42 @@ fn_get_identity (const char *file_name)
       free (dir);
     }
 #else /* Windows */
-  char cname[PATH_MAX];
-  int ok = GetFullPathName (file_name, sizeof cname, cname, NULL);
-  identity->device = 0;
-  identity->inode = 0;
-  identity->name = xstrdup (ok ? cname : file_name);
-  str_lowercase (identity->name);
+  bool ok = false;
+  HANDLE h = CreateFile (file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
+  if (h != INVALID_HANDLE_VALUE)
+  {
+    BY_HANDLE_FILE_INFORMATION fi;
+    ok = GetFileInformationByHandle (h, &fi);
+    if (ok)
+      {
+       identity->device = fi.dwVolumeSerialNumber;
+       identity->inode = fi.nFileIndexHigh;
+       identity->inode <<= (sizeof fi.nFileIndexLow) * CHAR_BIT;
+       identity->inode |= fi.nFileIndexLow;
+       identity->name = 0;
+      }
+    CloseHandle (h);
+  }
+
+  if (!ok)
+    {
+      identity->device = 0;
+      identity->inode = 0;
+
+      size_t bufsize;
+      size_t pathlen = 255;
+      char *cname = NULL;
+      do 
+      {
+       bufsize = pathlen;
+       cname = xrealloc (cname, bufsize);
+       pathlen = GetFullPathName (file_name, bufsize, cname, NULL);
+      }
+      while (pathlen > bufsize);
+      identity->name = xstrdup (cname);
+      free (cname);
+      str_lowercase (identity->name);
+    }
 #endif /* Windows */
 
   return identity;