Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions Doc/library/fcntl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ descriptor.
On Linux >= 6.1, the :mod:`!fcntl` module exposes the ``F_DUPFD_QUERY``
to query a file descriptor pointing to the same file.

.. versionchanged:: next
On macOS, the :mod:`!fcntl` module exposes the ``F_PREALLOCATE``,
``F_ALLOCATECONTIG``, ``F_ALLOCATEALL``, ``F_ALLOCATEPERSIST``,
``F_PEOFPOSMODE``, and ``F_VOLPOSMODE`` constants for file preallocation
operations, and the :func:`preallocate` function to preallocate file
storage space.

The module defines the following functions:


Expand Down Expand Up @@ -248,6 +255,47 @@ The module defines the following functions:

.. audit-event:: fcntl.lockf fd,cmd,len,start,whence fcntl.lockf


.. function:: preallocate(fd, flags, posmode, offset, length, /)

Preallocate file storage space.

This is a wrapper around the ``F_PREALLOCATE`` fcntl command.

*fd* is the file descriptor of the file to preallocate space for.
*flags* specifies the allocation behavior and can be one of:

.. data:: F_ALLOCATECONTIG

Allocate contiguous space.

.. data:: F_ALLOCATEALL

Allocate all requested space or none at all.

.. data:: F_ALLOCATEPERSIST

Do not deallocate space on close.

*posmode* specifies the positioning mode and can be one of:

.. data:: F_PEOFPOSMODE

Allocate space relative to the end of the file.

.. data:: F_VOLPOSMODE

Allocate space relative to the volume start.

*offset* is the starting offset for the allocation.
*length* is the number of bytes to allocate.

Returns the number of bytes actually allocated.

.. availability:: macOS.

.. audit-event:: fcntl.preallocate fd,flags,posmode,offset,length fcntl.preallocate

Examples (all on a SVR4 compliant system)::

import struct, fcntl, os
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_fcntl.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,23 @@ def test_bad_fd(self):
with self.assertRaises(OSError):
fcntl.fcntl(fd, fcntl.F_DUPFD, b'\0' * 2048)

@unittest.skipUnless(hasattr(fcntl, 'preallocate'), 'need fcntl.preallocate')
def test_preallocate(self):
self.f = open(TESTFN, 'wb+')
fd = self.f.fileno()

result = fcntl.preallocate(fd, fcntl.F_ALLOCATECONTIG, fcntl.F_PEOFPOSMODE, 0, 128)
self.assertIsInstance(result, int)
self.assertEqual(result, 128)

result = fcntl.preallocate(fd, fcntl.F_ALLOCATEALL, fcntl.F_PEOFPOSMODE, 0, 256)
self.assertIsInstance(result, int)
self.assertEqual(result, 256)

result = fcntl.preallocate(fd, fcntl.F_ALLOCATEPERSIST, fcntl.F_PEOFPOSMODE, 0, 512)
self.assertIsInstance(result, int)
self.assertEqual(result, 512)


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :func:`fcntl.preallocate` function and related constants for macOS.
64 changes: 63 additions & 1 deletion Modules/clinic/fcntlmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions Modules/fcntlmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -502,13 +502,67 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj,
Py_RETURN_NONE;
}


#ifdef F_PREALLOCATE

/*[clinic input]
fcntl.preallocate

fd: fildes
flags: int
posmode: int
offset: long
length: long
/

Preallocate file storage space.

This is a wrapper around the F_PREALLOCATE fcntl command.
[clinic start generated code]*/

static PyObject *
fcntl_preallocate_impl(PyObject *module, int fd, int flags, int posmode,
long offset, long length)
/*[clinic end generated code: output=4934b8a4dc1f5dc1 input=4c1a9d46551420ed]*/
{
int ret;
int async_err = 0;

if (PySys_Audit("fcntl.preallocate", "iiill", fd, flags, posmode, offset, length) < 0) {
return NULL;
}

struct fstore fstore = {
.fst_flags = (unsigned int)flags,
.fst_posmode = posmode,
.fst_offset = (off_t)offset,
.fst_length = (off_t)length,
.fst_bytesalloc = 0
};

do {
Py_BEGIN_ALLOW_THREADS
ret = fcntl(fd, F_PREALLOCATE, &fstore);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

if (ret < 0) {
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
}

return PyLong_FromLong((long)fstore.fst_bytesalloc);
}

#endif /* F_PREALLOCATE */

/* List of functions */

static PyMethodDef fcntl_methods[] = {
FCNTL_FCNTL_METHODDEF
FCNTL_IOCTL_METHODDEF
FCNTL_FLOCK_METHODDEF
FCNTL_LOCKF_METHODDEF
FCNTL_PREALLOCATE_METHODDEF
{NULL, NULL} /* sentinel */
};

Expand Down Expand Up @@ -687,6 +741,24 @@ all_ins(PyObject* m)
#ifdef F_NOCACHE
if (PyModule_AddIntMacro(m, F_NOCACHE)) return -1;
#endif
#ifdef F_PREALLOCATE
if (PyModule_AddIntMacro(m, F_PREALLOCATE)) return -1;
#endif
#ifdef F_ALLOCATECONTIG
if (PyModule_AddIntMacro(m, F_ALLOCATECONTIG)) return -1;
#endif
#ifdef F_ALLOCATEALL
if (PyModule_AddIntMacro(m, F_ALLOCATEALL)) return -1;
#endif
#ifdef F_ALLOCATEPERSIST
if (PyModule_AddIntMacro(m, F_ALLOCATEPERSIST)) return -1;
#endif
#ifdef F_PEOFPOSMODE
if (PyModule_AddIntMacro(m, F_PEOFPOSMODE)) return -1;
#endif
#ifdef F_VOLPOSMODE
if (PyModule_AddIntMacro(m, F_VOLPOSMODE)) return -1;
#endif

/* FreeBSD specifics */
#ifdef F_DUP2FD
Expand Down
Loading