Index: Lib/ntpath.py =================================================================== --- Lib/ntpath.py (revision 75333) +++ Lib/ntpath.py (working copy) @@ -17,6 +17,7 @@ "basename","dirname","commonprefix","getsize","getmtime", "getatime","getctime", "islink","exists","lexists","isdir","isfile", "ismount","walk","expanduser","expandvars","normpath","abspath", + "samefile","sameopenfile", "splitunc","curdir","pardir","sep","pathsep","defpath","altsep", "extsep","devnull","realpath","supports_unicode_filenames","relpath"] @@ -496,3 +497,31 @@ if not rel_list: return curdir return join(*rel_list) + + +# Are two filenames really pointing to the same file? +try: + from nt import _samefile + +except ImportError: # not running on Windows - mock up something sensible + from posixpath import samefile # XXX + +else: # use native Windows method on Windows + def samefile(f1, f2): + """Test whether two pathnames reference the same actual file""" + return _samefile(f1, f2) + + +# Are two open files really referencing the same file? +# (Not necessarily the same file descriptor!) +try: + from nt import _sameopenfile + +except ImportError: # not running on Windows - mock up something sensible + from posixpath import sameopenfile # XXX + +else: # use native Windows method on Windows + def sameopenfile(f1, f2): + """Test whether two file handles reference the same actual file""" + return _sameopenfile(f1, f2) + Index: Modules/posixmodule.c =================================================================== --- Modules/posixmodule.c (revision 75333) +++ Modules/posixmodule.c (working copy) @@ -2428,6 +2428,113 @@ } return PyString_FromString(outbuf); } /* end of posix__getfullpathname */ + +static HANDLE +open_file(const char *path) +{ + return CreateFileA(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); +} + +static HANDLE +open_file_w(const wchar_t *path) +{ + return CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); +} + +static PyObject * +samefile(HANDLE h1, HANDLE h2) +{ + BY_HANDLE_FILE_INFORMATION info1, info2; + + if (!GetFileInformationByHandle(h1, &info1)) + return NULL; + if (!GetFileInformationByHandle(h2, &info2)) + return NULL; + if ((info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber) && + (info1.nFileIndexHigh == info2.nFileIndexHigh) && + (info1.nFileIndexLow == info2.nFileIndexLow)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +/* A helper function for samefile on win32 */ +static PyObject * +posix__samefile(PyObject *self, PyObject *args) +{ + PyUnicodeObject *po1, *po2; + PyObject *v = NULL; + HANDLE h1, h2; + char inbuf1[MAX_PATH+1], *inbufp1 = inbuf1; + char inbuf2[MAX_PATH+1], *inbufp2 = inbuf2; + Py_ssize_t insize1 = sizeof(inbuf1); + Py_ssize_t insize2 = sizeof(inbuf2); + + if (PyArg_ParseTuple(args, "UU:_samefile", &po1, &po2)) { + h1 = open_file_w(PyUnicode_AS_UNICODE(po1)); + if (h1 != INVALID_HANDLE_VALUE) { + h2 = open_file_w(PyUnicode_AS_UNICODE(po2)); + if (h2 != INVALID_HANDLE_VALUE) { + v = samefile(h1, h2); + CloseHandle(h2); + } + CloseHandle(h1); + } + if (!v) + return win32_error("_samefile", NULL); + return v; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + + if (!PyArg_ParseTuple(args, "et#et#:_samefile", + Py_FileSystemDefaultEncoding, &inbufp1, &insize1, + Py_FileSystemDefaultEncoding, &inbufp2, &insize2)) + return NULL; + h1 = open_file(inbuf1); + if (h1 != INVALID_HANDLE_VALUE) { + h2 = open_file(inbuf2); + if (h2 != INVALID_HANDLE_VALUE) { + v = samefile(h1, h2); + CloseHandle(h2); + } + CloseHandle(h1); + } + if (!v) + return win32_error("_samefile", NULL); + return v; +} /* end of posix__samefile */ + +/* A helper function for sameopenfile on win32 */ +static PyObject * +posix__sameopenfile(PyObject *self, PyObject *args) +{ + int fd1, fd2; + PyObject *v = NULL; + HANDLE h1, h2; + + if (!PyArg_ParseTuple(args, "ii:_samefile", &fd1, &fd2)) + return NULL; + + h1 = (HANDLE)_get_osfhandle(fd1); + h2 = (HANDLE)_get_osfhandle(fd2); + + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + + if (h1 == INVALID_HANDLE_VALUE || h2 == INVALID_HANDLE_VALUE) + /* This is really a C library error (invalid file handle). + We set the Win32 error to the closes one matching. */ + SetLastError(ERROR_INVALID_HANDLE); + else + v = samefile(h1, h2); + + if (!v) + return win32_error("_sameopenfile", NULL); + return v; +} /* end of posix__sameopenfile */ #endif /* MS_WINDOWS */ PyDoc_STRVAR(posix_mkdir__doc__, @@ -8686,6 +8793,8 @@ {"abort", posix_abort, METH_NOARGS, posix_abort__doc__}, #ifdef MS_WINDOWS {"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL}, + {"_samefile", posix__samefile, METH_VARARGS, NULL}, + {"_sameopenfile", posix__sameopenfile, METH_VARARGS, NULL}, #endif #ifdef HAVE_GETLOADAVG {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__},