diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 309ad652d4af34a..b37904c9809d71c 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -57,6 +57,8 @@ descriptor. ``FICLONERANGE`` constants, which allow to share some data of one file with another file by reflinking on some filesystems (e.g., btrfs, OCFS2, and XFS). This behavior is commonly referred to as "copy-on-write". + On FreeBSD >= 13.1, the :mod:`fcntl` module exposes the ``F_KINFO`` + constant which allow to get the path of the file descriptor. The module defines the following functions: diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 203dd6fe57dcd99..6cb5c30c2b60747 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -193,6 +193,15 @@ def test_fcntl_f_getpath(self): res = fcntl.fcntl(self.f.fileno(), fcntl.F_GETPATH, bytes(len(expected))) self.assertEqual(expected, res) + @unittest.skipUnless(sys.platform.startswith("freebsd") and hasattr(fcntl, "F_KINFO"), "F_KINFO is only available on freebsd") + def test_fcntl_f_kinfo(self): + self.f = open(TESTFN, 'wb') + buffer = fcntl.kinfoalloc() + expected = os.path.abspath(TESTFN).encode('utf-8') + res = fcntl.fcntl(self.f.fileno(), fcntl.F_KINFO, buffer) + path = fcntl.kinfodict(res)["path"] + self.assertEqual(expected, path) + @unittest.skipUnless( hasattr(fcntl, "F_SETPIPE_SZ") and hasattr(fcntl, "F_GETPIPE_SZ"), "F_SETPIPE_SZ and F_GETPIPE_SZ are not available on all platforms.") diff --git a/Misc/NEWS.d/next/Library/2022-12-04-12-39-46.gh-issue-99987.RYtvKZ.rst b/Misc/NEWS.d/next/Library/2022-12-04-12-39-46.gh-issue-99987.RYtvKZ.rst new file mode 100644 index 000000000000000..61339b2a2e717ec --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-04-12-39-46.gh-issue-99987.RYtvKZ.rst @@ -0,0 +1 @@ +Expose ``F_KINFO`` constant in :mod:`fcntl`. Patch by David Carlier. diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index 5dc2fc068d0f7e4..a99a64b935b275e 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -246,4 +246,70 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=732e33ba92042031 input=a9049054013a1b77]*/ + +#if defined(F_KINFO) + +PyDoc_STRVAR(fcntl_kinfoalloc__doc__, +"kinfoalloc($module, /)\n" +"--\n" +"\n" +"Return a FreeBSD\'s kinfo_file buffer with the `kf_structsize` field pre-initialised."); + +#define FCNTL_KINFOALLOC_METHODDEF \ + {"kinfoalloc", (PyCFunction)fcntl_kinfoalloc, METH_NOARGS, fcntl_kinfoalloc__doc__}, + +static PyObject * +fcntl_kinfoalloc_impl(PyObject *module); + +static PyObject * +fcntl_kinfoalloc(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return fcntl_kinfoalloc_impl(module); +} + +#endif /* defined(F_KINFO) */ + +#if defined(F_KINFO) + +PyDoc_STRVAR(fcntl_kinfodict__doc__, +"kinfodict($module, arg=0, /)\n" +"--\n" +"\n" +"Return a FreeBSD\'s kinfo_file as dictionary."); + +#define FCNTL_KINFODICT_METHODDEF \ + {"kinfodict", _PyCFunction_CAST(fcntl_kinfodict), METH_FASTCALL, fcntl_kinfodict__doc__}, + +static PyObject * +fcntl_kinfodict_impl(PyObject *module, PyObject *arg); + +static PyObject * +fcntl_kinfodict(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *arg = NULL; + + if (!_PyArg_CheckPositional("kinfodict", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + arg = args[0]; +skip_optional: + return_value = fcntl_kinfodict_impl(module, arg); + +exit: + return return_value; +} + +#endif /* defined(F_KINFO) */ + +#ifndef FCNTL_KINFOALLOC_METHODDEF + #define FCNTL_KINFOALLOC_METHODDEF +#endif /* !defined(FCNTL_KINFOALLOC_METHODDEF) */ + +#ifndef FCNTL_KINFODICT_METHODDEF + #define FCNTL_KINFODICT_METHODDEF +#endif /* !defined(FCNTL_KINFODICT_METHODDEF) */ +/*[clinic end generated code: output=e449fd5d17f369d3 input=a9049054013a1b77]*/ diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index fd03abf0561da62..556a024f3fe98c9 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -19,6 +19,10 @@ #include #endif +#ifdef HAVE_SYS_USER_H +#include +#endif + /*[clinic input] module fcntl [clinic start generated code]*/ @@ -55,7 +59,6 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) int ret; char *str; Py_ssize_t len; - char buf[1024]; int async_err = 0; if (PySys_Audit("fcntl.fcntl", "iiO", fd, code, arg ? arg : Py_None) < 0) { @@ -66,6 +69,7 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) int parse_result; if (PyArg_Parse(arg, "s#", &str, &len)) { + char buf[4096]; if ((size_t)len > sizeof buf) { PyErr_SetString(PyExc_ValueError, "fcntl string arg too long"); @@ -438,6 +442,61 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, Py_RETURN_NONE; } +#ifdef F_KINFO +/*[clinic input] +fcntl.kinfoalloc + +Return a FreeBSD's kinfo_file buffer with the `kf_structsize` field pre-initialised. + +[clinic start generated code]*/ + +static PyObject * +fcntl_kinfoalloc_impl(PyObject *module) +/*[clinic end generated code: output=c61603aeb3d91a0e input=28e2e82cc296f82c]*/ +{ + char buf[KINFO_FILE_SIZE+1]; + ((struct kinfo_file *)buf)->kf_structsize = KINFO_FILE_SIZE; + return PyBytes_FromStringAndSize(buf, KINFO_FILE_SIZE); +} + +/*[clinic input] +fcntl.kinfodict + + arg: object(c_default='NULL') = 0 + / + +Return a FreeBSD's kinfo_file as dictionary. + +[clinic start generated code]*/ + +static PyObject * +fcntl_kinfodict_impl(PyObject *module, PyObject *arg) +/*[clinic end generated code: output=3873ae40aeac8e7f input=faba1f2ce099752f]*/ +{ + PyObject *dict; + + if (PySys_Audit("fcntl.kinfodict", "O", arg) < 0 || arg == NULL) { + return NULL; + } + + dict = PyDict_New(); + if (dict) { + struct kinfo_file *kf; + kf = (struct kinfo_file *)PyBytes_AsString(arg); + + PyDict_SetItemString(dict, "status", PyLong_FromLong(kf->kf_status)); + PyDict_SetItemString(dict, "type", PyLong_FromLong(kf->kf_type)); + PyDict_SetItemString(dict, "offset", PyLong_FromLongLong(kf->kf_offset)); + PyDict_SetItemString(dict, "path", PyBytes_FromString(kf->kf_path)); + return dict; + } + + PyErr_SetString(PyExc_ValueError, "fcntl.kinfodict could not create its dictionary"); + return NULL; +} + +#endif + /* List of functions */ static PyMethodDef fcntl_methods[] = { @@ -445,6 +504,8 @@ static PyMethodDef fcntl_methods[] = { FCNTL_IOCTL_METHODDEF FCNTL_FLOCK_METHODDEF FCNTL_LOCKF_METHODDEF + FCNTL_KINFOALLOC_METHODDEF + FCNTL_KINFODICT_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -485,6 +546,9 @@ all_ins(PyObject* m) #ifdef F_DUPFD_CLOEXEC if (PyModule_AddIntMacro(m, F_DUPFD_CLOEXEC)) return -1; #endif +#ifdef F_KINFO + if (PyModule_AddIntMacro(m, F_KINFO)) return -1; +#endif #ifdef F_GETFD if (PyModule_AddIntMacro(m, F_GETFD)) return -1; #endif diff --git a/configure b/configure index 7e50abc29d0c1a9..52fd9588793d6dd 100755 --- a/configure +++ b/configure @@ -10754,6 +10754,12 @@ if test "x$ac_cv_header_sys_un_h" = xyes then : printf "%s\n" "#define HAVE_SYS_UN_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "sys/user.h" "ac_cv_header_sys_user_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_user_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_USER_H 1" >>confdefs.h + fi ac_fn_c_check_header_compile "$LINENO" "sys/utsname.h" "ac_cv_header_sys_utsname_h" "$ac_includes_default" if test "x$ac_cv_header_sys_utsname_h" = xyes diff --git a/configure.ac b/configure.ac index e064848af9ed1b6..9dd2e8257d5851e 100644 --- a/configure.ac +++ b/configure.ac @@ -2702,7 +2702,7 @@ AC_CHECK_HEADERS([ \ sys/loadavg.h sys/lock.h sys/memfd.h sys/mkdev.h sys/mman.h sys/modem.h sys/param.h sys/poll.h \ sys/random.h sys/resource.h sys/select.h sys/sendfile.h sys/socket.h sys/soundcard.h sys/stat.h \ sys/statvfs.h sys/sys_domain.h sys/syscall.h sys/sysmacros.h sys/termio.h sys/time.h sys/times.h sys/timerfd.h \ - sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h sys/xattr.h sysexits.h syslog.h \ + sys/types.h sys/uio.h sys/un.h sys/user.h sys/utsname.h sys/wait.h sys/xattr.h sysexits.h syslog.h \ termios.h util.h utime.h utmp.h \ ]) AC_HEADER_DIRENT diff --git a/pyconfig.h.in b/pyconfig.h.in index d8a9f68951afbd1..6f5cee086e6abcc 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1391,6 +1391,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UN_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_USER_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UTSNAME_H