Fix #8879. Add os.link support to Windows.
Additionally, the st_ino attribute of stat structures was not being filled
in. This was left out of the fix to #10027 and was noticed due to
test_tarfile failing when applying the patch for this issue. An earlier
version of the fix to #10027 included st_ino, but that attribute got lost
in the shuffle of a few review/fix cycles. All tests pass.
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index cb9d9c4..b2c439c 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -1058,7 +1058,10 @@
Create a hard link pointing to *source* named *link_name*.
- Availability: Unix.
+ Availability: Unix, Windows.
+
+ .. versionchanged:: 3.2
+ Added Windows support.
.. function:: listdir(path='.')
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 129367e..978364c 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -860,6 +860,33 @@
if hasattr(os, "write"):
self.check(os.write, b" ")
+
+class LinkTests(unittest.TestCase):
+ def setUp(self):
+ self.file1 = support.TESTFN
+ self.file2 = os.path.join(support.TESTFN + "2")
+
+ for file in (self.file1, self.file2):
+ if os.path.exists(file):
+ os.unlink(file)
+
+ tearDown = setUp
+
+ def _test_link(self, file1, file2):
+ with open(file1, "w") as f1:
+ f1.write("test")
+
+ os.link(file1, file2)
+ with open(file1, "r") as f1, open(file2, "r") as f2:
+ self.assertTrue(os.path.sameopenfile(f1.fileno(), f2.fileno()))
+
+ def test_link(self):
+ self._test_link(self.file1, self.file2)
+
+ def test_link_bytes(self):
+ self._test_link(bytes(self.file1, sys.getfilesystemencoding()),
+ bytes(self.file2, sys.getfilesystemencoding()))
+
if sys.platform != 'win32':
class Win32ErrorTests(unittest.TestCase):
pass
@@ -1221,6 +1248,7 @@
FSEncodingTests,
PidTests,
LoginTests,
+ LinkTests,
)
if __name__ == "__main__":
diff --git a/Misc/NEWS b/Misc/NEWS
index fb61ac8..6891d1e 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,8 @@
Core and Builtins
-----------------
+- Issue #8879. Add os.link support for Windows.
+
- Issue #10027. st_nlink was not being set on Windows calls to os.stat or
os.lstat. Patch by Hirokazu Yamamoto.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index cba8a9d..8b7d7e9 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1035,6 +1035,7 @@
FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
result->st_nlink = info->nNumberOfLinks;
+ result->st_ino = (((__int64)info->nFileIndexHigh)<<32) + info->nFileIndexLow;
return 0;
}
@@ -2239,6 +2240,36 @@
}
#endif /* HAVE_LINK */
+#ifdef MS_WINDOWS
+PyDoc_STRVAR(win32_link__doc__,
+"link(src, dst)\n\n\
+Create a hard link to a file.");
+
+static PyObject *
+win32_link(PyObject *self, PyObject *args)
+{
+ PyObject *osrc, *odst;
+ char *src, *dst;
+ BOOL rslt;
+
+ if (!PyArg_ParseTuple(args, "O&O&:link", PyUnicode_FSConverter, &osrc,
+ PyUnicode_FSConverter, &odst))
+ return NULL;
+
+ src = PyBytes_AsString(osrc);
+ dst = PyBytes_AsString(odst);
+
+ Py_BEGIN_ALLOW_THREADS
+ rslt = CreateHardLink(dst, src, NULL);
+ Py_END_ALLOW_THREADS
+
+ if (rslt == 0)
+ return posix_error();
+
+ Py_RETURN_NONE;
+}
+#endif /* MS_WINDOWS */
+
PyDoc_STRVAR(posix_listdir__doc__,
"listdir([path]) -> list_of_strings\n\n\
@@ -7808,6 +7839,7 @@
#ifdef MS_WINDOWS
{"startfile", win32_startfile, METH_VARARGS, win32_startfile__doc__},
{"kill", win32_kill, METH_VARARGS, win32_kill__doc__},
+ {"link", win32_link, METH_VARARGS, win32_link__doc__},
#endif
#ifdef HAVE_SETUID
{"setuid", posix_setuid, METH_VARARGS, posix_setuid__doc__},