diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -798,7 +798,7 @@ Availability: Unix. -.. function:: fsync(fd) +.. function:: fsync(fd, full_fsync=False) Force write of file with filedescriptor *fd* to disk. On Unix, this calls the native :c:func:`fsync` function; on Windows, the MS :c:func:`_commit` function. @@ -807,6 +807,13 @@ ``f.flush()``, and then do ``os.fsync(f.fileno())``, to ensure that all internal buffers associated with *f* are written to disk. + Note that :c:func:`fsync` only ensures that the OS flushs all data to + the disk device, *not* that the data is actually written by the + device itself. For this to happen, use the *full_fsync* argument. + If this is true Python tries to accomplish even the device write, + e.g. on Mac OS X by calling :c:func:`fcntl` with the + :data:`F_FULLFSYNC` command. + Availability: Unix, and Windows. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2119,12 +2119,38 @@ #ifdef HAVE_FSYNC PyDoc_STRVAR(posix_fsync__doc__, -"fsync(fildes)\n\n\ -force write of file with filedescriptor to disk."); - -static PyObject * -posix_fsync(PyObject *self, PyObject *fdobj) -{ +"fsync(fildes, full_fsync=False)\n\n" +"force write of file with filedescriptor to disk.\n" +"if full_fsync is True it also ensures write to physical backing store."); + +static PyObject * +posix_fsync(PyObject *self, PyObject *args, PyObject *kwargs) +{ + auto PyObject *fdobj; + auto int full_fsync = 1; + static char *keywords[] = {"fd", "full_fsync", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i", keywords, + &fdobj, &full_fsync)) + return NULL; + +# ifdef __APPLE__ + /* See issue 11877 discussion and issue 11277 for OS X sparse file bug */ + if (full_fsync != 0) { + int res, fd = PyObject_AsFileDescriptor(fdobj); + if (fd < 0) + return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); + Py_BEGIN_ALLOW_THREADS + res = fcntl(fd, F_FULLFSYNC); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } +# endif return posix_fildes(fdobj, fsync); } #endif /* HAVE_FSYNC */ @@ -9484,7 +9510,8 @@ {"fchdir", posix_fchdir, METH_O, posix_fchdir__doc__}, #endif #ifdef HAVE_FSYNC - {"fsync", posix_fsync, METH_O, posix_fsync__doc__}, + {"fsync", (PyCFunction)posix_fsync, METH_VARARGS|METH_KEYWORDS, + posix_fsync__doc__}, #endif #ifdef HAVE_SYNC {"sync", posix_sync, METH_NOARGS, posix_sync__doc__},