From: loreb <loreb@users.noreply.github.com>
To: ml@inbox.vuxu.org
Subject: [PR PATCH] recutils: patch to fix recins not working outside of $TMPDIR
Date: Wed, 28 Apr 2021 23:30:23 +0200 [thread overview]
Message-ID: <gh-mailinglist-notifications-41a7ca26-5023-4802-975b-f1789d68868e-void-packages-30567@inbox.vuxu.org> (raw)
[-- Attachment #1: Type: text/plain, Size: 2208 bytes --]
There is a new pull request by loreb against master on the void-packages repository
https://github.com/loreb/void-packages master
https://github.com/void-linux/void-packages/pull/30567
recutils: patch to fix recins not working outside of $TMPDIR
Details at https://git.savannah.gnu.org/cgit/recutils.git/commit/utils/recutl.c?id=86f662a8202408134a235572ec60141d3082f975
Will no longer be necessary once 1.9 is released; fixes bug #30546
Thanks to ericonr for all the assistance.
note to self: bootstrap.conf is not shipped
add *temporary* dependency on automake
adding gnulib pieces is a real chore...
And then the GNU tools (rightfully) decided that the manpages were out
of date, because a file has been modified, and insisted on running
help2man - I simply told pre_configure to update the timestamps of the
manpages.
<!-- Mark items with [x] where applicable -->
#### General
- [ ] This is a new package and it conforms to the [quality requirements](https://github.com/void-linux/void-packages/blob/master/Manual.md#quality-requirements)
#### Have the results of the proposed changes been tested?
- [ ] I use the packages affected by the proposed changes on a regular basis and confirm this PR works for me
- [ ] I generally don't use the affected packages but briefly tested this PR
<!--
If GitHub CI cannot be used to validate the build result (for example, if the
build is likely to take several hours), make sure to
[skip CI](https://github.com/void-linux/void-packages/blob/master/CONTRIBUTING.md#continuous-integration).
When skipping CI, uncomment and fill out the following section.
Note: for builds that are likely to complete in less than 2 hours, it is not
acceptable to skip CI.
-->
<!--
#### Does it build and run successfully?
(Please choose at least one native build and, if supported, at least one cross build. More are better.)
- [ ] I built this PR locally for my native architecture, (ARCH-LIBC)
- [ ] I built this PR locally for these architectures (if supported. mark crossbuilds):
- [ ] aarch64-musl
- [ ] armv7l
- [ ] armv6l-musl
-->
A patch file from https://github.com/void-linux/void-packages/pull/30567.patch is attached
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-master-30567.patch --]
[-- Type: text/x-diff, Size: 69177 bytes --]
From f6de7e727ed410b5d4ed782c464758c77147a336 Mon Sep 17 00:00:00 2001
From: Lorenzo Beretta <vc.net.loreb@gmail.com>
Date: Wed, 28 Apr 2021 14:20:53 +0200
Subject: [PATCH] recutils: patch to fix recins not working outside of $TMPDIR
Details at https://git.savannah.gnu.org/cgit/recutils.git/commit/utils/recutl.c?id=86f662a8202408134a235572ec60141d3082f975
Will no longer be necessary once 1.9 is released; fixes bug #30546
Thanks to ericonr for all the assistance.
note to self: bootstrap.conf is not shipped
add *temporary* dependency on automake
adding gnulib pieces is a real chore...
And then the GNU tools (rightfully) decided that the manpages were out
of date, because a file has been modified, and insisted on running
help2man - I simply told pre_configure to update the timestamps of the
manpages.
---
srcpkgs/recutils/patches/copyfile.patch | 1929 +++++++++++++++++++++++
srcpkgs/recutils/patches/exdev.patch | 53 +
srcpkgs/recutils/template | 12 +-
3 files changed, 1992 insertions(+), 2 deletions(-)
create mode 100644 srcpkgs/recutils/patches/copyfile.patch
create mode 100644 srcpkgs/recutils/patches/exdev.patch
diff --git a/srcpkgs/recutils/patches/copyfile.patch b/srcpkgs/recutils/patches/copyfile.patch
new file mode 100644
index 000000000000..c2f289f6f41d
--- /dev/null
+++ b/srcpkgs/recutils/patches/copyfile.patch
@@ -0,0 +1,1929 @@
+diff --git a/lib/copy-file.c b/lib/copy-file.c
+new file mode 100644
+index 0000000..15e17ae
+--- /dev/null
++++ b/lib/copy-file.c
+@@ -0,0 +1,221 @@
++/* Copying of files.
++ Copyright (C) 2001-2003, 2006-2007, 2009-2021 Free Software Foundation, Inc.
++ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++
++#include <config.h>
++
++/* Specification. */
++#include "copy-file.h"
++
++#include <errno.h>
++#include <fcntl.h>
++#include <stddef.h>
++#include <stdlib.h>
++#include <sys/stat.h>
++#include <unistd.h>
++
++#include "error.h"
++#include "ignore-value.h"
++#include "safe-read.h"
++#include "full-write.h"
++#include "stat-time.h"
++#include "utimens.h"
++#include "acl.h"
++#include "binary-io.h"
++#include "quote.h"
++#include "gettext.h"
++
++#define _(str) gettext (str)
++
++enum { IO_SIZE = 32 * 1024 };
++
++int
++qcopy_file_preserving (const char *src_filename, const char *dest_filename)
++{
++ int err = 0;
++ int src_fd;
++ struct stat statbuf;
++ int mode;
++ int dest_fd;
++
++ src_fd = open (src_filename, O_RDONLY | O_BINARY | O_CLOEXEC);
++ if (src_fd < 0)
++ return GL_COPY_ERR_OPEN_READ;
++ if (fstat (src_fd, &statbuf) < 0)
++ {
++ err = GL_COPY_ERR_OPEN_READ;
++ goto error_src;
++ }
++
++ mode = statbuf.st_mode & 07777;
++ off_t inbytes = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1;
++ bool empty_regular_file = inbytes == 0;
++
++ dest_fd = open (dest_filename,
++ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC,
++ 0600);
++ if (dest_fd < 0)
++ {
++ err = GL_COPY_ERR_OPEN_BACKUP_WRITE;
++ goto error_src;
++ }
++
++ /* Copy the file contents. FIXME: Do not copy holes. */
++ while (0 < inbytes)
++ {
++ size_t copy_max = -1;
++ copy_max -= copy_max % IO_SIZE;
++ size_t len = inbytes < copy_max ? inbytes : copy_max;
++ ssize_t copied = copy_file_range (src_fd, NULL, dest_fd, NULL, len, 0);
++ if (copied <= 0)
++ break;
++ inbytes -= copied;
++ }
++
++ /* Finish up with read/write, in case the file was not a regular
++ file, or the file shrank or had I/O errors (in which case find
++ whether it was a read or write error). Read empty regular files
++ since they might be in /proc with their true sizes unknown until
++ they are read. */
++ if (inbytes != 0 || empty_regular_file)
++ {
++ char smallbuf[1024];
++ int bufsize = IO_SIZE;
++ char *buf = malloc (bufsize);
++ if (!buf)
++ buf = smallbuf, bufsize = sizeof smallbuf;
++
++ while (true)
++ {
++ size_t n_read = safe_read (src_fd, buf, bufsize);
++ if (n_read == 0)
++ break;
++ if (n_read == SAFE_READ_ERROR)
++ {
++ err = GL_COPY_ERR_READ;
++ break;
++ }
++ if (full_write (dest_fd, buf, n_read) < n_read)
++ {
++ err = GL_COPY_ERR_WRITE;
++ break;
++ }
++ }
++
++ if (buf != smallbuf)
++ free (buf);
++ if (err)
++ goto error_src_dest;
++ }
++
++#if !USE_ACL
++ if (close (dest_fd) < 0)
++ {
++ err = GL_COPY_ERR_WRITE;
++ goto error_src;
++ }
++ if (close (src_fd) < 0)
++ return GL_COPY_ERR_AFTER_READ;
++#endif
++
++ /* Preserve the access and modification times. */
++ {
++ struct timespec ts[2];
++
++ ts[0] = get_stat_atime (&statbuf);
++ ts[1] = get_stat_mtime (&statbuf);
++ utimens (dest_filename, ts);
++ }
++
++#if HAVE_CHOWN
++ /* Preserve the owner and group. */
++ ignore_value (chown (dest_filename, statbuf.st_uid, statbuf.st_gid));
++#endif
++
++ /* Preserve the access permissions. */
++#if USE_ACL
++ switch (qcopy_acl (src_filename, src_fd, dest_filename, dest_fd, mode))
++ {
++ case -2:
++ err = GL_COPY_ERR_GET_ACL;
++ goto error_src_dest;
++ case -1:
++ err = GL_COPY_ERR_SET_ACL;
++ goto error_src_dest;
++ }
++#else
++ chmod (dest_filename, mode);
++#endif
++
++#if USE_ACL
++ if (close (dest_fd) < 0)
++ {
++ err = GL_COPY_ERR_WRITE;
++ goto error_src;
++ }
++ if (close (src_fd) < 0)
++ return GL_COPY_ERR_AFTER_READ;
++#endif
++
++ return 0;
++
++ error_src_dest:
++ close (dest_fd);
++ error_src:
++ close (src_fd);
++ return err;
++}
++
++void
++copy_file_preserving (const char *src_filename, const char *dest_filename)
++{
++ switch (qcopy_file_preserving (src_filename, dest_filename))
++ {
++ case 0:
++ return;
++
++ case GL_COPY_ERR_OPEN_READ:
++ error (EXIT_FAILURE, errno, _("error while opening %s for reading"),
++ quote (src_filename));
++
++ case GL_COPY_ERR_OPEN_BACKUP_WRITE:
++ error (EXIT_FAILURE, errno, _("cannot open backup file %s for writing"),
++ quote (dest_filename));
++
++ case GL_COPY_ERR_READ:
++ error (EXIT_FAILURE, errno, _("error reading %s"),
++ quote (src_filename));
++
++ case GL_COPY_ERR_WRITE:
++ error (EXIT_FAILURE, errno, _("error writing %s"),
++ quote (dest_filename));
++
++ case GL_COPY_ERR_AFTER_READ:
++ error (EXIT_FAILURE, errno, _("error after reading %s"),
++ quote (src_filename));
++
++ case GL_COPY_ERR_GET_ACL:
++ error (EXIT_FAILURE, errno, "%s", quote (src_filename));
++
++ case GL_COPY_ERR_SET_ACL:
++ error (EXIT_FAILURE, errno, _("preserving permissions for %s"),
++ quote (dest_filename));
++
++ default:
++ abort ();
++ }
++}
+diff --git a/lib/copy-file.h b/lib/copy-file.h
+new file mode 100644
+index 0000000..09281d6
+--- /dev/null
++++ b/lib/copy-file.h
+@@ -0,0 +1,54 @@
++/* Copying of files.
++ Copyright (C) 2001-2003, 2009-2021 Free Software Foundation, Inc.
++ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++/* Error codes returned by qcopy_file_preserving. */
++enum
++{
++ GL_COPY_ERR_OPEN_READ = -1,
++ GL_COPY_ERR_OPEN_BACKUP_WRITE = -2,
++ GL_COPY_ERR_READ = -3,
++ GL_COPY_ERR_WRITE = -4,
++ GL_COPY_ERR_AFTER_READ = -5,
++ GL_COPY_ERR_GET_ACL = -6,
++ GL_COPY_ERR_SET_ACL = -7
++};
++
++/* Copy a regular file: from src_filename to dest_filename.
++ The destination file is assumed to be a backup file.
++ Modification times, owner, group and access permissions are preserved as
++ far as possible.
++ Return 0 if successful, otherwise set errno and return one of the error
++ codes above. */
++extern int qcopy_file_preserving (const char *src_filename, const char *dest_filename);
++
++/* Copy a regular file: from src_filename to dest_filename.
++ The destination file is assumed to be a backup file.
++ Modification times, owner, group and access permissions are preserved as
++ far as possible.
++ Exit upon failure. */
++extern void copy_file_preserving (const char *src_filename, const char *dest_filename);
++
++
++#ifdef __cplusplus
++}
++#endif
+diff --git a/m4/copy-file.m4 b/m4/copy-file.m4
+new file mode 100644
+index 0000000..6211171
+--- /dev/null
++++ b/m4/copy-file.m4
+@@ -0,0 +1,11 @@
++# copy-file.m4 serial 5
++dnl Copyright (C) 2003, 2009-2021 Free Software Foundation, Inc.
++dnl This file is free software; the Free Software Foundation
++dnl gives unlimited permission to copy and/or distribute it,
++dnl with or without modifications, as long as this notice is preserved.
++
++AC_DEFUN([gl_COPY_FILE],
++[
++ AC_CHECK_HEADERS_ONCE([unistd.h])
++ AC_CHECK_FUNCS([chown])
++])
+
+diff --git a/lib/Makefile.am b/lib/Makefile.am
+index a6ec30b..f363b86 100644
+--- a/lib/Makefile.am
++++ b/lib/Makefile.am
+@@ -219,6 +225,12 @@
+
+ ## end gnulib module base64
+
++## begin gnulib module binary-io
++
++librecutils_la_SOURCES += binary-io.h binary-io.c
++
++## end gnulib module binary-io
++
+ ## begin gnulib module btowc
+
+
+@@ -289,6 +301,21 @@
+
+ ## end gnulib module closeout
+
++## begin gnulib module copy-file
++
++librecutils_la_SOURCES += copy-file.h copy-file.c
++
++## end gnulib module copy-file
++
++## begin gnulib module copy-file-range
++
++
++EXTRA_DIST += copy-file-range.c
++
++EXTRA_librecutils_la_SOURCES += copy-file-range.c
++
++## end gnulib module copy-file-range
++
+ ## begin gnulib module crc
+
+ librecutils_la_SOURCES += crc.c
+@@ -605,6 +634,12 @@
+
+ ## end gnulib module ftello
+
++## begin gnulib module full-write
++
++librecutils_la_SOURCES += full-write.h full-write.c
++
++## end gnulib module full-write
++
+ ## begin gnulib module fwriting
+
+
+@@ -778,6 +813,13 @@
+
+ ## end gnulib module havelib
+
++## begin gnulib module ignore-value
++
++
++EXTRA_DIST += ignore-value.h
++
++## end gnulib module ignore-value
++
+ ## begin gnulib module intprops
+
+
+@@ -1761,6 +1827,32 @@
+
+ ## end gnulib module root-uid
+
++## begin gnulib module safe-read
++
++librecutils_la_SOURCES += safe-read.c
++
++EXTRA_DIST += safe-read.h sys-limits.h
++
++## end gnulib module safe-read
++
++## begin gnulib module safe-write
++
++librecutils_la_SOURCES += safe-write.c
++
++EXTRA_DIST += safe-read.c safe-write.h sys-limits.h
++
++EXTRA_librecutils_la_SOURCES += safe-read.c
++
++## end gnulib module safe-write
++
++## begin gnulib module utimens
++
++librecutils_la_SOURCES += utimens.c
++
++EXTRA_DIST += utimens.h
++
++## end gnulib module utimens
++
+ ## begin gnulib module same-inode
+
+
+diff --git a/lib/ignore-value.h b/lib/ignore-value.h
+new file mode 100644
+index 0000000..0a3cf1e
+--- /dev/null
++++ b/lib/ignore-value.h
+@@ -0,0 +1,51 @@
++/* ignore a function return without a compiler warning. -*- coding: utf-8 -*-
++
++ Copyright (C) 2008-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++/* Written by Jim Meyering, Eric Blake and Pádraig Brady. */
++
++/* Use "ignore_value" to avoid a warning when using a function declared with
++ gcc's warn_unused_result attribute, but for which you really do want to
++ ignore the result. Traditionally, people have used a "(void)" cast to
++ indicate that a function's return value is deliberately unused. However,
++ if the function is declared with __attribute__((warn_unused_result)),
++ gcc issues a warning even with the cast.
++
++ Caution: most of the time, you really should heed gcc's warning, and
++ check the return value. However, in those exceptional cases in which
++ you're sure you know what you're doing, use this function.
++
++ For the record, here's one of the ignorable warnings:
++ "copy.c:233: warning: ignoring return value of 'fchown',
++ declared with attribute warn_unused_result". */
++
++#ifndef _GL_IGNORE_VALUE_H
++#define _GL_IGNORE_VALUE_H
++
++/* Normally casting an expression to void discards its value, but GCC
++ versions 3.4 and newer have __attribute__ ((__warn_unused_result__))
++ which may cause unwanted diagnostics in that case. Use __typeof__
++ and __extension__ to work around the problem, if the workaround is
++ known to be needed.
++ The workaround is not needed with clang. */
++#if (3 < __GNUC__ + (4 <= __GNUC_MINOR__)) && !defined __clang__
++# define ignore_value(x) \
++ (__extension__ ({ __typeof__ (x) __x = (x); (void) __x; }))
++#else
++# define ignore_value(x) ((void) (x))
++#endif
++
++#endif
+diff --git a/lib/safe-read.c b/lib/safe-read.c
+new file mode 100644
+index 0000000..be9c33c
+--- /dev/null
++++ b/lib/safe-read.c
+@@ -0,0 +1,71 @@
++/* An interface to read and write that retries after interrupts.
++
++ Copyright (C) 1993-1994, 1998, 2002-2006, 2009-2021 Free Software
++ Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++#include <config.h>
++
++/* Specification. */
++#ifdef SAFE_WRITE
++# include "safe-write.h"
++#else
++# include "safe-read.h"
++#endif
++
++/* Get ssize_t. */
++#include <sys/types.h>
++#include <unistd.h>
++
++#include <errno.h>
++
++#ifdef EINTR
++# define IS_EINTR(x) ((x) == EINTR)
++#else
++# define IS_EINTR(x) 0
++#endif
++
++#include "sys-limits.h"
++
++#ifdef SAFE_WRITE
++# define safe_rw safe_write
++# define rw write
++#else
++# define safe_rw safe_read
++# define rw read
++# undef const
++# define const /* empty */
++#endif
++
++/* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if
++ interrupted. Return the actual number of bytes read(written), zero for EOF,
++ or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error. */
++size_t
++safe_rw (int fd, void const *buf, size_t count)
++{
++ for (;;)
++ {
++ ssize_t result = rw (fd, buf, count);
++
++ if (0 <= result)
++ return result;
++ else if (IS_EINTR (errno))
++ continue;
++ else if (errno == EINVAL && SYS_BUFSIZE_MAX < count)
++ count = SYS_BUFSIZE_MAX;
++ else
++ return result;
++ }
++}
+diff --git a/lib/safe-read.h b/lib/safe-read.h
+new file mode 100644
+index 0000000..5482906
+--- /dev/null
++++ b/lib/safe-read.h
+@@ -0,0 +1,47 @@
++/* An interface to read() that retries after interrupts.
++ Copyright (C) 2002, 2006, 2009-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++/* Some system calls may be interrupted and fail with errno = EINTR in the
++ following situations:
++ - The process is stopped and restarted (signal SIGSTOP and SIGCONT, user
++ types Ctrl-Z) on some platforms: Mac OS X.
++ - The process receives a signal for which a signal handler was installed
++ with sigaction() with an sa_flags field that does not contain
++ SA_RESTART.
++ - The process receives a signal for which a signal handler was installed
++ with signal() and for which no call to siginterrupt(sig,0) was done,
++ on some platforms: AIX, HP-UX, IRIX, OSF/1, Solaris.
++
++ This module provides a wrapper around read() that handles EINTR. */
++
++#include <stddef.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++#define SAFE_READ_ERROR ((size_t) -1)
++
++/* Read up to COUNT bytes at BUF from descriptor FD, retrying if interrupted.
++ Return the actual number of bytes read, zero for EOF, or SAFE_READ_ERROR
++ upon error. */
++extern size_t safe_read (int fd, void *buf, size_t count);
++
++
++#ifdef __cplusplus
++}
++#endif
+diff --git a/lib/sys-limits.h b/lib/sys-limits.h
+new file mode 100644
+index 0000000..cd05a81
+--- /dev/null
++++ b/lib/sys-limits.h
+@@ -0,0 +1,42 @@
++/* System call limits
++
++ Copyright 2018-2021 Free Software Foundation, Inc.
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2, or (at your option)
++ any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, see <https://www.gnu.org/licenses/>. */
++
++#ifndef _GL_SYS_LIMITS_H
++#define _GL_SYS_LIMITS_H
++
++#include <limits.h>
++
++/* Maximum number of bytes to read or write in a single system call.
++ This can be useful for system calls like sendfile on GNU/Linux,
++ which do not handle more than MAX_RW_COUNT bytes correctly.
++ The Linux kernel MAX_RW_COUNT is at least INT_MAX >> 20 << 20,
++ where the 20 comes from the Hexagon port with 1 MiB pages; use that
++ as an approximation, as the exact value may not be available to us.
++
++ Using this also works around a serious Linux bug before 2.6.16; see
++ <https://bugzilla.redhat.com/show_bug.cgi?id=612839>.
++
++ Using this also works around a Tru64 5.1 bug, where attempting
++ to read INT_MAX bytes fails with errno == EINVAL. See
++ <https://lists.gnu.org/r/bug-gnu-utils/2002-04/msg00010.html>.
++
++ Using this is likely to work around similar bugs in other operating
++ systems. */
++
++enum { SYS_BUFSIZE_MAX = INT_MAX >> 20 << 20 };
++
++#endif
+diff --git a/m4/safe-read.m4 b/m4/safe-read.m4
+new file mode 100644
+index 0000000..3cba288
+--- /dev/null
++++ b/m4/safe-read.m4
+@@ -0,0 +1,12 @@
++# safe-read.m4 serial 6
++dnl Copyright (C) 2002-2003, 2005-2006, 2009-2021 Free Software Foundation,
++dnl Inc.
++dnl This file is free software; the Free Software Foundation
++dnl gives unlimited permission to copy and/or distribute it,
++dnl with or without modifications, as long as this notice is preserved.
++
++# Prerequisites of lib/safe-read.c.
++AC_DEFUN([gl_PREREQ_SAFE_READ],
++[
++ AC_REQUIRE([gt_TYPE_SSIZE_T])
++])
+diff --git a/lib/full-write.c b/lib/full-write.c
+new file mode 100644
+index 0000000..2c0e06b
+--- /dev/null
++++ b/lib/full-write.c
+@@ -0,0 +1,79 @@
++/* An interface to read and write that retries (if necessary) until complete.
++
++ Copyright (C) 1993-1994, 1997-2006, 2009-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++#include <config.h>
++
++/* Specification. */
++#ifdef FULL_READ
++# include "full-read.h"
++#else
++# include "full-write.h"
++#endif
++
++#include <errno.h>
++
++#ifdef FULL_READ
++# include "safe-read.h"
++# define safe_rw safe_read
++# define full_rw full_read
++# undef const
++# define const /* empty */
++#else
++# include "safe-write.h"
++# define safe_rw safe_write
++# define full_rw full_write
++#endif
++
++#ifdef FULL_READ
++/* Set errno to zero upon EOF. */
++# define ZERO_BYTE_TRANSFER_ERRNO 0
++#else
++/* Some buggy drivers return 0 when one tries to write beyond
++ a device's end. (Example: Linux 1.2.13 on /dev/fd0.)
++ Set errno to ENOSPC so they get a sensible diagnostic. */
++# define ZERO_BYTE_TRANSFER_ERRNO ENOSPC
++#endif
++
++/* Write(read) COUNT bytes at BUF to(from) descriptor FD, retrying if
++ interrupted or if a partial write(read) occurs. Return the number
++ of bytes transferred.
++ When writing, set errno if fewer than COUNT bytes are written.
++ When reading, if fewer than COUNT bytes are read, you must examine
++ errno to distinguish failure from EOF (errno == 0). */
++size_t
++full_rw (int fd, const void *buf, size_t count)
++{
++ size_t total = 0;
++ const char *ptr = (const char *) buf;
++
++ while (count > 0)
++ {
++ size_t n_rw = safe_rw (fd, ptr, count);
++ if (n_rw == (size_t) -1)
++ break;
++ if (n_rw == 0)
++ {
++ errno = ZERO_BYTE_TRANSFER_ERRNO;
++ break;
++ }
++ total += n_rw;
++ ptr += n_rw;
++ count -= n_rw;
++ }
++
++ return total;
++}
+diff --git a/lib/full-write.h b/lib/full-write.h
+new file mode 100644
+index 0000000..6c4e28a
+--- /dev/null
++++ b/lib/full-write.h
+@@ -0,0 +1,34 @@
++/* An interface to write() that writes all it is asked to write.
++
++ Copyright (C) 2002-2003, 2009-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++#include <stddef.h>
++
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++/* Write COUNT bytes at BUF to descriptor FD, retrying if interrupted
++ or if partial writes occur. Return the number of bytes successfully
++ written, setting errno if that is less than COUNT. */
++extern size_t full_write (int fd, const void *buf, size_t count);
++
++
++#ifdef __cplusplus
++}
++#endif
+diff --git a/lib/utimens.c b/lib/utimens.c
+new file mode 100644
+index 0000000..06d1aa3
+--- /dev/null
++++ b/lib/utimens.c
+@@ -0,0 +1,647 @@
++/* Set file access and modification times.
++
++ Copyright (C) 2003-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify it
++ under the terms of the GNU General Public License as published by the
++ Free Software Foundation; either version 3 of the License, or any
++ later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++/* Written by Paul Eggert. */
++
++/* derived from a function in touch.c */
++
++#include <config.h>
++
++#define _GL_UTIMENS_INLINE _GL_EXTERN_INLINE
++#include "utimens.h"
++
++#include <errno.h>
++#include <fcntl.h>
++#include <stdbool.h>
++#include <string.h>
++#include <sys/stat.h>
++#include <sys/time.h>
++#include <unistd.h>
++#include <utime.h>
++
++#include "stat-time.h"
++#include "timespec.h"
++
++/* On native Windows, use SetFileTime; but avoid this when compiling
++ GNU Emacs, which arranges for this in some other way and which
++ defines WIN32_LEAN_AND_MEAN itself. */
++
++#if defined _WIN32 && ! defined __CYGWIN__ && ! defined EMACS_CONFIGURATION
++# define USE_SETFILETIME
++# define WIN32_LEAN_AND_MEAN
++# include <windows.h>
++# if GNULIB_MSVC_NOTHROW
++# include "msvc-nothrow.h"
++# else
++# include <io.h>
++# endif
++#endif
++
++/* Avoid recursion with rpl_futimens or rpl_utimensat. */
++#undef futimens
++#if !HAVE_NEARLY_WORKING_UTIMENSAT
++# undef utimensat
++#endif
++
++/* Solaris 9 mistakenly succeeds when given a non-directory with a
++ trailing slash. Force the use of rpl_stat for a fix. */
++#ifndef REPLACE_FUNC_STAT_FILE
++# define REPLACE_FUNC_STAT_FILE 0
++#endif
++
++#if HAVE_UTIMENSAT || HAVE_FUTIMENS
++/* Cache variables for whether the utimensat syscall works; used to
++ avoid calling the syscall if we know it will just fail with ENOSYS,
++ and to avoid unnecessary work in massaging timestamps if the
++ syscall will work. Multiple variables are needed, to distinguish
++ between the following scenarios on Linux:
++ utimensat doesn't exist, or is in glibc but kernel 2.6.18 fails with ENOSYS
++ kernel 2.6.22 and earlier rejects AT_SYMLINK_NOFOLLOW
++ kernel 2.6.25 and earlier reject UTIME_NOW/UTIME_OMIT with non-zero tv_sec
++ kernel 2.6.32 used with xfs or ntfs-3g fail to honor UTIME_OMIT
++ utimensat completely works
++ For each cache variable: 0 = unknown, 1 = yes, -1 = no. */
++static int utimensat_works_really;
++static int lutimensat_works_really;
++#endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */
++
++/* Validate the requested timestamps. Return 0 if the resulting
++ timespec can be used for utimensat (after possibly modifying it to
++ work around bugs in utimensat). Return a positive value if the
++ timespec needs further adjustment based on stat results: 1 if any
++ adjustment is needed for utimes, and 2 if any adjustment is needed
++ for Linux utimensat. Return -1, with errno set to EINVAL, if
++ timespec is out of range. */
++static int
++validate_timespec (struct timespec timespec[2])
++{
++ int result = 0;
++ int utime_omit_count = 0;
++ if ((timespec[0].tv_nsec != UTIME_NOW
++ && timespec[0].tv_nsec != UTIME_OMIT
++ && ! (0 <= timespec[0].tv_nsec
++ && timespec[0].tv_nsec < TIMESPEC_HZ))
++ || (timespec[1].tv_nsec != UTIME_NOW
++ && timespec[1].tv_nsec != UTIME_OMIT
++ && ! (0 <= timespec[1].tv_nsec
++ && timespec[1].tv_nsec < TIMESPEC_HZ)))
++ {
++ errno = EINVAL;
++ return -1;
++ }
++ /* Work around Linux kernel 2.6.25 bug, where utimensat fails with
++ EINVAL if tv_sec is not 0 when using the flag values of tv_nsec.
++ Flag a Linux kernel 2.6.32 bug, where an mtime of UTIME_OMIT
++ fails to bump ctime. */
++ if (timespec[0].tv_nsec == UTIME_NOW
++ || timespec[0].tv_nsec == UTIME_OMIT)
++ {
++ timespec[0].tv_sec = 0;
++ result = 1;
++ if (timespec[0].tv_nsec == UTIME_OMIT)
++ utime_omit_count++;
++ }
++ if (timespec[1].tv_nsec == UTIME_NOW
++ || timespec[1].tv_nsec == UTIME_OMIT)
++ {
++ timespec[1].tv_sec = 0;
++ result = 1;
++ if (timespec[1].tv_nsec == UTIME_OMIT)
++ utime_omit_count++;
++ }
++ return result + (utime_omit_count == 1);
++}
++
++/* Normalize any UTIME_NOW or UTIME_OMIT values in (*TS)[0] and (*TS)[1],
++ using STATBUF to obtain the current timestamps of the file. If
++ both times are UTIME_NOW, set *TS to NULL (as this can avoid some
++ permissions issues). If both times are UTIME_OMIT, return true
++ (nothing further beyond the prior collection of STATBUF is
++ necessary); otherwise return false. */
++static bool
++update_timespec (struct stat const *statbuf, struct timespec **ts)
++{
++ struct timespec *timespec = *ts;
++ if (timespec[0].tv_nsec == UTIME_OMIT
++ && timespec[1].tv_nsec == UTIME_OMIT)
++ return true;
++ if (timespec[0].tv_nsec == UTIME_NOW
++ && timespec[1].tv_nsec == UTIME_NOW)
++ {
++ *ts = NULL;
++ return false;
++ }
++
++ if (timespec[0].tv_nsec == UTIME_OMIT)
++ timespec[0] = get_stat_atime (statbuf);
++ else if (timespec[0].tv_nsec == UTIME_NOW)
++ gettime (×pec[0]);
++
++ if (timespec[1].tv_nsec == UTIME_OMIT)
++ timespec[1] = get_stat_mtime (statbuf);
++ else if (timespec[1].tv_nsec == UTIME_NOW)
++ gettime (×pec[1]);
++
++ return false;
++}
++
++/* Set the access and modification timestamps of FD (a.k.a. FILE) to be
++ TIMESPEC[0] and TIMESPEC[1], respectively.
++ FD must be either negative -- in which case it is ignored --
++ or a file descriptor that is open on FILE.
++ If FD is nonnegative, then FILE can be NULL, which means
++ use just futimes (or equivalent) instead of utimes (or equivalent),
++ and fail if on an old system without futimes (or equivalent).
++ If TIMESPEC is null, set the timestamps to the current time.
++ Return 0 on success, -1 (setting errno) on failure. */
++
++int
++fdutimens (int fd, char const *file, struct timespec const timespec[2])
++{
++ struct timespec adjusted_timespec[2];
++ struct timespec *ts = timespec ? adjusted_timespec : NULL;
++ int adjustment_needed = 0;
++ struct stat st;
++
++ if (ts)
++ {
++ adjusted_timespec[0] = timespec[0];
++ adjusted_timespec[1] = timespec[1];
++ adjustment_needed = validate_timespec (ts);
++ }
++ if (adjustment_needed < 0)
++ return -1;
++
++ /* Require that at least one of FD or FILE are potentially valid, to avoid
++ a Linux bug where futimens (AT_FDCWD, NULL) changes "." rather
++ than failing. */
++ if (fd < 0 && !file)
++ {
++ errno = EBADF;
++ return -1;
++ }
++
++ /* Some Linux-based NFS clients are buggy, and mishandle timestamps
++ of files in NFS file systems in some cases. We have no
++ configure-time test for this, but please see
++ <https://bugs.gentoo.org/show_bug.cgi?id=132673> for references to
++ some of the problems with Linux 2.6.16. If this affects you,
++ compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to
++ help in some cases, albeit at a cost in performance. But you
++ really should upgrade your kernel to a fixed version, since the
++ problem affects many applications. */
++
++#if HAVE_BUGGY_NFS_TIME_STAMPS
++ if (fd < 0)
++ sync ();
++ else
++ fsync (fd);
++#endif
++
++ /* POSIX 2008 added two interfaces to set file timestamps with
++ nanosecond resolution; newer Linux implements both functions via
++ a single syscall. We provide a fallback for ENOSYS (for example,
++ compiling against Linux 2.6.25 kernel headers and glibc 2.7, but
++ running on Linux 2.6.18 kernel). */
++#if HAVE_UTIMENSAT || HAVE_FUTIMENS
++ if (0 <= utimensat_works_really)
++ {
++ int result;
++# if __linux__ || __sun
++ /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
++ systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
++ but work if both times are either explicitly specified or
++ UTIME_NOW. Work around it with a preparatory [f]stat prior
++ to calling futimens/utimensat; fortunately, there is not much
++ timing impact due to the extra syscall even on file systems
++ where UTIME_OMIT would have worked.
++
++ The same bug occurs in Solaris 11.1 (Apr 2013).
++
++ FIXME: Simplify this for Linux in 2016 and for Solaris in
++ 2024, when file system bugs are no longer common. */
++ if (adjustment_needed == 2)
++ {
++ if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
++ return -1;
++ if (ts[0].tv_nsec == UTIME_OMIT)
++ ts[0] = get_stat_atime (&st);
++ else if (ts[1].tv_nsec == UTIME_OMIT)
++ ts[1] = get_stat_mtime (&st);
++ /* Note that st is good, in case utimensat gives ENOSYS. */
++ adjustment_needed++;
++ }
++# endif
++# if HAVE_UTIMENSAT
++ if (fd < 0)
++ {
++# if defined __APPLE__ && defined __MACH__
++ size_t len = strlen (file);
++ if (len > 0 && file[len - 1] == '/')
++ {
++ struct stat statbuf;
++ if (stat (file, &statbuf) < 0)
++ return -1;
++ if (!S_ISDIR (statbuf.st_mode))
++ {
++ errno = ENOTDIR;
++ return -1;
++ }
++ }
++# endif
++ result = utimensat (AT_FDCWD, file, ts, 0);
++# ifdef __linux__
++ /* Work around a kernel bug:
++ https://bugzilla.redhat.com/show_bug.cgi?id=442352
++ https://bugzilla.redhat.com/show_bug.cgi?id=449910
++ It appears that utimensat can mistakenly return 280 rather
++ than -1 upon ENOSYS failure.
++ FIXME: remove in 2010 or whenever the offending kernels
++ are no longer in common use. */
++ if (0 < result)
++ errno = ENOSYS;
++# endif /* __linux__ */
++ if (result == 0 || errno != ENOSYS)
++ {
++ utimensat_works_really = 1;
++ return result;
++ }
++ }
++# endif /* HAVE_UTIMENSAT */
++# if HAVE_FUTIMENS
++ if (0 <= fd)
++ {
++ result = futimens (fd, ts);
++# ifdef __linux__
++ /* Work around the same bug as above. */
++ if (0 < result)
++ errno = ENOSYS;
++# endif /* __linux__ */
++ if (result == 0 || errno != ENOSYS)
++ {
++ utimensat_works_really = 1;
++ return result;
++ }
++ }
++# endif /* HAVE_FUTIMENS */
++ }
++ utimensat_works_really = -1;
++ lutimensat_works_really = -1;
++#endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */
++
++#ifdef USE_SETFILETIME
++ /* On native Windows, use SetFileTime(). See
++ <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfiletime>
++ <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime> */
++ if (0 <= fd)
++ {
++ HANDLE handle;
++ FILETIME current_time;
++ FILETIME last_access_time;
++ FILETIME last_write_time;
++
++ handle = (HANDLE) _get_osfhandle (fd);
++ if (handle == INVALID_HANDLE_VALUE)
++ {
++ errno = EBADF;
++ return -1;
++ }
++
++ if (ts == NULL || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW)
++ {
++ /* GetSystemTimeAsFileTime
++ <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime>.
++ It would be overkill to use
++ GetSystemTimePreciseAsFileTime
++ <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime>. */
++ GetSystemTimeAsFileTime (¤t_time);
++ }
++
++ if (ts == NULL || ts[0].tv_nsec == UTIME_NOW)
++ {
++ last_access_time = current_time;
++ }
++ else if (ts[0].tv_nsec == UTIME_OMIT)
++ {
++ last_access_time.dwLowDateTime = 0;
++ last_access_time.dwHighDateTime = 0;
++ }
++ else
++ {
++ ULONGLONG time_since_16010101 =
++ (ULONGLONG) ts[0].tv_sec * 10000000 + ts[0].tv_nsec / 100 + 116444736000000000LL;
++ last_access_time.dwLowDateTime = (DWORD) time_since_16010101;
++ last_access_time.dwHighDateTime = time_since_16010101 >> 32;
++ }
++
++ if (ts == NULL || ts[1].tv_nsec == UTIME_NOW)
++ {
++ last_write_time = current_time;
++ }
++ else if (ts[1].tv_nsec == UTIME_OMIT)
++ {
++ last_write_time.dwLowDateTime = 0;
++ last_write_time.dwHighDateTime = 0;
++ }
++ else
++ {
++ ULONGLONG time_since_16010101 =
++ (ULONGLONG) ts[1].tv_sec * 10000000 + ts[1].tv_nsec / 100 + 116444736000000000LL;
++ last_write_time.dwLowDateTime = (DWORD) time_since_16010101;
++ last_write_time.dwHighDateTime = time_since_16010101 >> 32;
++ }
++
++ if (SetFileTime (handle, NULL, &last_access_time, &last_write_time))
++ return 0;
++ else
++ {
++ DWORD sft_error = GetLastError ();
++ #if 0
++ fprintf (stderr, "fdutimens SetFileTime error 0x%x\n", (unsigned int) sft_error);
++ #endif
++ switch (sft_error)
++ {
++ case ERROR_ACCESS_DENIED: /* fd was opened without O_RDWR */
++ errno = EACCES; /* not specified by POSIX */
++ break;
++ default:
++ errno = EINVAL;
++ break;
++ }
++ return -1;
++ }
++ }
++#endif
++
++ /* The platform lacks an interface to set file timestamps with
++ nanosecond resolution, so do the best we can, discarding any
++ fractional part of the timestamp. */
++
++ if (adjustment_needed || (REPLACE_FUNC_STAT_FILE && fd < 0))
++ {
++ if (adjustment_needed != 3
++ && (fd < 0 ? stat (file, &st) : fstat (fd, &st)))
++ return -1;
++ if (ts && update_timespec (&st, &ts))
++ return 0;
++ }
++
++ {
++#if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
++ struct timeval timeval[2];
++ struct timeval *t;
++ if (ts)
++ {
++ timeval[0].tv_sec = ts[0].tv_sec;
++ timeval[0].tv_usec = ts[0].tv_nsec / 1000;
++ timeval[1].tv_sec = ts[1].tv_sec;
++ timeval[1].tv_usec = ts[1].tv_nsec / 1000;
++ t = timeval;
++ }
++ else
++ t = NULL;
++
++ if (fd < 0)
++ {
++# if HAVE_FUTIMESAT
++ return futimesat (AT_FDCWD, file, t);
++# endif
++ }
++ else
++ {
++ /* If futimesat or futimes fails here, don't try to speed things
++ up by returning right away. glibc can incorrectly fail with
++ errno == ENOENT if /proc isn't mounted. Also, Mandrake 10.0
++ in high security mode doesn't allow ordinary users to read
++ /proc/self, so glibc incorrectly fails with errno == EACCES.
++ If errno == EIO, EPERM, or EROFS, it's probably safe to fail
++ right away, but these cases are rare enough that they're not
++ worth optimizing, and who knows what other messed-up systems
++ are out there? So play it safe and fall back on the code
++ below. */
++
++# if (HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) || HAVE_FUTIMES
++# if HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG
++# undef futimes
++# define futimes(fd, t) futimesat (fd, NULL, t)
++# endif
++ if (futimes (fd, t) == 0)
++ {
++# if __linux__ && __GLIBC__
++ /* Work around a longstanding glibc bug, still present as
++ of 2010-12-27. On older Linux kernels that lack both
++ utimensat and utimes, glibc's futimes rounds instead of
++ truncating when falling back on utime. The same bug
++ occurs in futimesat with a null 2nd arg. */
++ if (t)
++ {
++ bool abig = 500000 <= t[0].tv_usec;
++ bool mbig = 500000 <= t[1].tv_usec;
++ if ((abig | mbig) && fstat (fd, &st) == 0)
++ {
++ /* If these two subtractions overflow, they'll
++ track the overflows inside the buggy glibc. */
++ time_t adiff = st.st_atime - t[0].tv_sec;
++ time_t mdiff = st.st_mtime - t[1].tv_sec;
++
++ struct timeval *tt = NULL;
++ struct timeval truncated_timeval[2];
++ truncated_timeval[0] = t[0];
++ truncated_timeval[1] = t[1];
++ if (abig && adiff == 1 && get_stat_atime_ns (&st) == 0)
++ {
++ tt = truncated_timeval;
++ tt[0].tv_usec = 0;
++ }
++ if (mbig && mdiff == 1 && get_stat_mtime_ns (&st) == 0)
++ {
++ tt = truncated_timeval;
++ tt[1].tv_usec = 0;
++ }
++ if (tt)
++ futimes (fd, tt);
++ }
++ }
++# endif
++
++ return 0;
++ }
++# endif
++ }
++#endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */
++
++ if (!file)
++ {
++#if ! ((HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) \
++ || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
++ errno = ENOSYS;
++#endif
++ return -1;
++ }
++
++#ifdef USE_SETFILETIME
++ return _gl_utimens_windows (file, ts);
++#elif HAVE_WORKING_UTIMES
++ return utimes (file, t);
++#else
++ {
++ struct utimbuf utimbuf;
++ struct utimbuf *ut;
++ if (ts)
++ {
++ utimbuf.actime = ts[0].tv_sec;
++ utimbuf.modtime = ts[1].tv_sec;
++ ut = &utimbuf;
++ }
++ else
++ ut = NULL;
++
++ return utime (file, ut);
++ }
++#endif /* !HAVE_WORKING_UTIMES */
++ }
++}
++
++/* Set the access and modification timestamps of FILE to be
++ TIMESPEC[0] and TIMESPEC[1], respectively. */
++int
++utimens (char const *file, struct timespec const timespec[2])
++{
++ return fdutimens (-1, file, timespec);
++}
++
++/* Set the access and modification timestamps of FILE to be
++ TIMESPEC[0] and TIMESPEC[1], respectively, without dereferencing
++ symlinks. Fail with ENOSYS if the platform does not support
++ changing symlink timestamps, but FILE was a symlink. */
++int
++lutimens (char const *file, struct timespec const timespec[2])
++{
++ struct timespec adjusted_timespec[2];
++ struct timespec *ts = timespec ? adjusted_timespec : NULL;
++ int adjustment_needed = 0;
++ struct stat st;
++
++ if (ts)
++ {
++ adjusted_timespec[0] = timespec[0];
++ adjusted_timespec[1] = timespec[1];
++ adjustment_needed = validate_timespec (ts);
++ }
++ if (adjustment_needed < 0)
++ return -1;
++
++ /* The Linux kernel did not support symlink timestamps until
++ utimensat, in version 2.6.22, so we don't need to mimic
++ fdutimens' worry about buggy NFS clients. But we do have to
++ worry about bogus return values. */
++
++#if HAVE_UTIMENSAT
++ if (0 <= lutimensat_works_really)
++ {
++ int result;
++# if __linux__ || __sun
++ /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
++ systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
++ but work if both times are either explicitly specified or
++ UTIME_NOW. Work around it with a preparatory lstat prior to
++ calling utimensat; fortunately, there is not much timing
++ impact due to the extra syscall even on file systems where
++ UTIME_OMIT would have worked.
++
++ The same bug occurs in Solaris 11.1 (Apr 2013).
++
++ FIXME: Simplify this for Linux in 2016 and for Solaris in
++ 2024, when file system bugs are no longer common. */
++ if (adjustment_needed == 2)
++ {
++ if (lstat (file, &st))
++ return -1;
++ if (ts[0].tv_nsec == UTIME_OMIT)
++ ts[0] = get_stat_atime (&st);
++ else if (ts[1].tv_nsec == UTIME_OMIT)
++ ts[1] = get_stat_mtime (&st);
++ /* Note that st is good, in case utimensat gives ENOSYS. */
++ adjustment_needed++;
++ }
++# endif
++ result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW);
++# ifdef __linux__
++ /* Work around a kernel bug:
++ https://bugzilla.redhat.com/show_bug.cgi?id=442352
++ https://bugzilla.redhat.com/show_bug.cgi?id=449910
++ It appears that utimensat can mistakenly return 280 rather
++ than -1 upon ENOSYS failure.
++ FIXME: remove in 2010 or whenever the offending kernels
++ are no longer in common use. */
++ if (0 < result)
++ errno = ENOSYS;
++# endif
++ if (result == 0 || errno != ENOSYS)
++ {
++ utimensat_works_really = 1;
++ lutimensat_works_really = 1;
++ return result;
++ }
++ }
++ lutimensat_works_really = -1;
++#endif /* HAVE_UTIMENSAT */
++
++ /* The platform lacks an interface to set file timestamps with
++ nanosecond resolution, so do the best we can, discarding any
++ fractional part of the timestamp. */
++
++ if (adjustment_needed || REPLACE_FUNC_STAT_FILE)
++ {
++ if (adjustment_needed != 3 && lstat (file, &st))
++ return -1;
++ if (ts && update_timespec (&st, &ts))
++ return 0;
++ }
++
++ /* On Linux, lutimes is a thin wrapper around utimensat, so there is
++ no point trying lutimes if utimensat failed with ENOSYS. */
++#if HAVE_LUTIMES && !HAVE_UTIMENSAT
++ {
++ struct timeval timeval[2];
++ struct timeval *t;
++ int result;
++ if (ts)
++ {
++ timeval[0].tv_sec = ts[0].tv_sec;
++ timeval[0].tv_usec = ts[0].tv_nsec / 1000;
++ timeval[1].tv_sec = ts[1].tv_sec;
++ timeval[1].tv_usec = ts[1].tv_nsec / 1000;
++ t = timeval;
++ }
++ else
++ t = NULL;
++
++ result = lutimes (file, t);
++ if (result == 0 || errno != ENOSYS)
++ return result;
++ }
++#endif /* HAVE_LUTIMES && !HAVE_UTIMENSAT */
++
++ /* Out of luck for symlinks, but we still handle regular files. */
++ if (!(adjustment_needed || REPLACE_FUNC_STAT_FILE) && lstat (file, &st))
++ return -1;
++ if (!S_ISLNK (st.st_mode))
++ return fdutimens (-1, file, ts);
++ errno = ENOSYS;
++ return -1;
++}
+diff --git a/lib/utimens.h b/lib/utimens.h
+new file mode 100644
+index 0000000..295d3d7
+--- /dev/null
++++ b/lib/utimens.h
+@@ -0,0 +1,49 @@
++/* Set file access and modification times.
++
++ Copyright 2012-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify it
++ under the terms of the GNU General Public License as published by the
++ Free Software Foundation; either version 3 of the License, or any
++ later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++/* Written by Paul Eggert. */
++
++#include <time.h>
++int fdutimens (int, char const *, struct timespec const [2]);
++int utimens (char const *, struct timespec const [2]);
++int lutimens (char const *, struct timespec const [2]);
++
++#if GNULIB_FDUTIMENSAT
++# include <fcntl.h>
++# include <sys/stat.h>
++
++#ifndef _GL_INLINE_HEADER_BEGIN
++ #error "Please include config.h first."
++#endif
++_GL_INLINE_HEADER_BEGIN
++#ifndef _GL_UTIMENS_INLINE
++# define _GL_UTIMENS_INLINE _GL_INLINE
++#endif
++
++int fdutimensat (int fd, int dir, char const *name, struct timespec const [2],
++ int atflag);
++
++/* Using this function makes application code slightly more readable. */
++_GL_UTIMENS_INLINE int
++lutimensat (int dir, char const *file, struct timespec const times[2])
++{
++ return utimensat (dir, file, times, AT_SYMLINK_NOFOLLOW);
++}
++
++_GL_INLINE_HEADER_END
++
++#endif
+diff --git a/m4/utimens.m4 b/m4/utimens.m4
+new file mode 100644
+index 0000000..2ee4ef9
+--- /dev/null
++++ b/m4/utimens.m4
+@@ -0,0 +1,52 @@
++dnl Copyright (C) 2003-2021 Free Software Foundation, Inc.
++dnl This file is free software; the Free Software Foundation
++dnl gives unlimited permission to copy and/or distribute it,
++dnl with or without modifications, as long as this notice is preserved.
++
++dnl serial 11
++
++AC_DEFUN([gl_UTIMENS],
++[
++ dnl Prerequisites of lib/utimens.c.
++ AC_REQUIRE([gl_FUNC_UTIMES])
++ AC_REQUIRE([gl_CHECK_TYPE_STRUCT_TIMESPEC])
++ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
++ AC_CHECK_FUNCS_ONCE([futimes futimesat futimens utimensat lutimes])
++
++ if test $ac_cv_func_futimens = no && test $ac_cv_func_futimesat = yes; then
++ dnl FreeBSD 8.0-rc2 mishandles futimesat(fd,NULL,time). It is not
++ dnl standardized, but Solaris implemented it first and uses it as
++ dnl its only means to set fd time.
++ AC_CACHE_CHECK([whether futimesat handles NULL file],
++ [gl_cv_func_futimesat_works],
++ [touch conftest.file
++ AC_RUN_IFELSE([AC_LANG_PROGRAM([[
++#include <stddef.h>
++#include <sys/times.h>
++#include <fcntl.h>
++]GL_MDA_DEFINES],
++ [[int fd = open ("conftest.file", O_RDWR);
++ if (fd < 0) return 1;
++ if (futimesat (fd, NULL, NULL)) return 2;
++ ]])],
++ [gl_cv_func_futimesat_works=yes],
++ [gl_cv_func_futimesat_works=no],
++ [case "$host_os" in
++ # Guess yes on Linux systems.
++ linux-* | linux) gl_cv_func_futimesat_works="guessing yes" ;;
++ # Guess yes on glibc systems.
++ *-gnu*) gl_cv_func_futimesat_works="guessing yes" ;;
++ # If we don't know, obey --enable-cross-guesses.
++ *) gl_cv_func_futimesat_works="$gl_cross_guess_normal" ;;
++ esac
++ ])
++ rm -f conftest.file])
++ case "$gl_cv_func_futimesat_works" in
++ *yes) ;;
++ *)
++ AC_DEFINE([FUTIMESAT_NULL_BUG], [1],
++ [Define to 1 if futimesat mishandles a NULL file name.])
++ ;;
++ esac
++ fi
++])
+diff --git a/m4/utimes.m4 b/m4/utimes.m4
+new file mode 100644
+index 0000000..0440e78
+--- /dev/null
++++ b/m4/utimes.m4
+@@ -0,0 +1,161 @@
++# Detect some bugs in glibc's implementation of utimes.
++# serial 8
++
++dnl Copyright (C) 2003-2005, 2009-2021 Free Software Foundation, Inc.
++dnl This file is free software; the Free Software Foundation
++dnl gives unlimited permission to copy and/or distribute it,
++dnl with or without modifications, as long as this notice is preserved.
++
++# See if we need to work around bugs in glibc's implementation of
++# utimes from 2003-07-12 to 2003-09-17.
++# First, there was a bug that would make utimes set mtime
++# and atime to zero (1970-01-01) unconditionally.
++# Then, there was code to round rather than truncate.
++# Then, there was an implementation (sparc64, Linux-2.4.28, glibc-2.3.3)
++# that didn't honor the NULL-means-set-to-current-time semantics.
++# Finally, there was also a version of utimes that failed on read-only
++# files, while utime worked fine (linux-2.2.20, glibc-2.2.5).
++#
++# From Jim Meyering, with suggestions from Paul Eggert.
++
++AC_DEFUN([gl_FUNC_UTIMES],
++[
++ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
++ AC_CACHE_CHECK([whether the utimes function works],
++ [gl_cv_func_working_utimes],
++ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <sys/time.h>
++#include <time.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <utime.h>
++#include <errno.h>
++]GL_MDA_DEFINES[
++
++static int
++inorder (time_t a, time_t b, time_t c)
++{
++ return a <= b && b <= c;
++}
++
++int
++main ()
++{
++ int result = 0;
++ char const *file = "conftest.utimes";
++ /* On OS/2, file timestamps must be on or after 1980 in local time,
++ with an even number of seconds. */
++ static struct timeval timeval[2] = {{315620000 + 10, 10},
++ {315620000 + 1000000, 999998}};
++
++ /* Test whether utimes() essentially works. */
++ {
++ struct stat sbuf;
++ FILE *f = fopen (file, "w");
++ if (f == NULL)
++ result |= 1;
++ else if (fclose (f) != 0)
++ result |= 1;
++ else if (utimes (file, timeval) != 0)
++ result |= 2;
++ else if (lstat (file, &sbuf) != 0)
++ result |= 1;
++ else if (!(sbuf.st_atime == timeval[0].tv_sec
++ && sbuf.st_mtime == timeval[1].tv_sec))
++ result |= 4;
++ if (unlink (file) != 0)
++ result |= 1;
++ }
++
++ /* Test whether utimes() with a NULL argument sets the file's timestamp
++ to the current time. Use 'fstat' as well as 'time' to
++ determine the "current" time, to accommodate NFS file systems
++ if there is a time skew between the host and the NFS server. */
++ {
++ int fd = open (file, O_WRONLY|O_CREAT, 0644);
++ if (fd < 0)
++ result |= 1;
++ else
++ {
++ time_t t0, t2;
++ struct stat st0, st1, st2;
++ if (time (&t0) == (time_t) -1)
++ result |= 1;
++ else if (fstat (fd, &st0) != 0)
++ result |= 1;
++ else if (utimes (file, timeval) != 0
++ && (errno != EACCES
++ /* OS/2 kLIBC utimes fails on opened files. */
++ || close (fd) != 0
++ || utimes (file, timeval) != 0
++ || (fd = open (file, O_WRONLY)) < 0))
++ result |= 2;
++ else if (utimes (file, NULL) != 0
++ && (errno != EACCES
++ /* OS/2 kLIBC utimes fails on opened files. */
++ || close (fd) != 0
++ || utimes (file, NULL) != 0
++ || (fd = open (file, O_WRONLY)) < 0))
++ result |= 8;
++ else if (fstat (fd, &st1) != 0)
++ result |= 1;
++ else if (write (fd, "\n", 1) != 1)
++ result |= 1;
++ else if (fstat (fd, &st2) != 0)
++ result |= 1;
++ else if (time (&t2) == (time_t) -1)
++ result |= 1;
++ else
++ {
++ int m_ok_POSIX = inorder (t0, st1.st_mtime, t2);
++ int m_ok_NFS = inorder (st0.st_mtime, st1.st_mtime, st2.st_mtime);
++ if (! (st1.st_atime == st1.st_mtime))
++ result |= 16;
++ if (! (m_ok_POSIX || m_ok_NFS))
++ result |= 32;
++ }
++ if (close (fd) != 0)
++ result |= 1;
++ }
++ if (unlink (file) != 0)
++ result |= 1;
++ }
++
++ /* Test whether utimes() with a NULL argument works on read-only files. */
++ {
++ int fd = open (file, O_WRONLY|O_CREAT, 0444);
++ if (fd < 0)
++ result |= 1;
++ else if (close (fd) != 0)
++ result |= 1;
++ else if (utimes (file, NULL) != 0)
++ result |= 64;
++ if (unlink (file) != 0)
++ result |= 1;
++ }
++
++ return result;
++}
++ ]])],
++ [gl_cv_func_working_utimes=yes],
++ [gl_cv_func_working_utimes=no],
++ [case "$host_os" in
++ # Guess yes on musl systems.
++ *-musl*) gl_cv_func_working_utimes="guessing yes" ;;
++ # Guess no on native Windows.
++ mingw*) gl_cv_func_working_utimes="guessing no" ;;
++ *) gl_cv_func_working_utimes="$gl_cross_guess_normal" ;;
++ esac
++ ])
++ ])
++
++ case "$gl_cv_func_working_utimes" in
++ *yes)
++ AC_DEFINE([HAVE_WORKING_UTIMES], [1], [Define if utimes works properly.])
++ ;;
++ esac
++])
+diff --git a/lib/binary-io.c b/lib/binary-io.c
+new file mode 100644
+index 0000000..f267897
+--- /dev/null
++++ b/lib/binary-io.c
+@@ -0,0 +1,39 @@
++/* Binary mode I/O.
++ Copyright 2017-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++#include <config.h>
++
++#define BINARY_IO_INLINE _GL_EXTERN_INLINE
++#include "binary-io.h"
++
++#if defined __DJGPP__ || defined __EMX__
++# include <unistd.h>
++
++int
++set_binary_mode (int fd, int mode)
++{
++ if (isatty (fd))
++ /* If FD refers to a console (not a pipe, not a regular file),
++ O_TEXT is the only reasonable mode, both on input and on output.
++ Silently ignore the request. If we were to return -1 here,
++ all programs that use xset_binary_mode would fail when run
++ with console input or console output. */
++ return O_TEXT;
++ else
++ return __gl_setmode (fd, mode);
++}
++
++#endif
+diff --git a/lib/binary-io.h b/lib/binary-io.h
+new file mode 100644
+index 0000000..8654fd2
+--- /dev/null
++++ b/lib/binary-io.h
+@@ -0,0 +1,77 @@
++/* Binary mode I/O.
++ Copyright (C) 2001, 2003, 2005, 2008-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++#ifndef _BINARY_H
++#define _BINARY_H
++
++/* For systems that distinguish between text and binary I/O.
++ O_BINARY is guaranteed by the gnulib <fcntl.h>. */
++#include <fcntl.h>
++
++/* The MSVC7 <stdio.h> doesn't like to be included after '#define fileno ...',
++ so we include it here first. */
++#include <stdio.h>
++
++#ifndef _GL_INLINE_HEADER_BEGIN
++ #error "Please include config.h first."
++#endif
++_GL_INLINE_HEADER_BEGIN
++#ifndef BINARY_IO_INLINE
++# define BINARY_IO_INLINE _GL_INLINE
++#endif
++
++#if O_BINARY
++# if defined __EMX__ || defined __DJGPP__ || defined __CYGWIN__
++# include <io.h> /* declares setmode() */
++# define __gl_setmode setmode
++# else
++# define __gl_setmode _setmode
++# undef fileno
++# define fileno _fileno
++# endif
++#else
++ /* On reasonable systems, binary I/O is the only choice. */
++ /* Use a function rather than a macro, to avoid gcc warnings
++ "warning: statement with no effect". */
++BINARY_IO_INLINE int
++__gl_setmode (int fd _GL_UNUSED, int mode _GL_UNUSED)
++{
++ return O_BINARY;
++}
++#endif
++
++/* Set FD's mode to MODE, which should be either O_TEXT or O_BINARY.
++ Return the old mode if successful, -1 (setting errno) on failure.
++ Ordinarily this function would be called 'setmode', since that is
++ its old name on MS-Windows, but it is called 'set_binary_mode' here
++ to avoid colliding with a BSD function of another name. */
++
++#if defined __DJGPP__ || defined __EMX__
++extern int set_binary_mode (int fd, int mode);
++#else
++BINARY_IO_INLINE int
++set_binary_mode (int fd, int mode)
++{
++ return __gl_setmode (fd, mode);
++}
++#endif
++
++/* This macro is obsolescent. */
++#define SET_BINARY(fd) ((void) set_binary_mode (fd, O_BINARY))
++
++_GL_INLINE_HEADER_END
++
++#endif /* _BINARY_H */
+diff --git a/lib/safe-write.c b/lib/safe-write.c
+new file mode 100644
+index 0000000..a460dc9
+--- /dev/null
++++ b/lib/safe-write.c
+@@ -0,0 +1,18 @@
++/* An interface to write that retries after interrupts.
++ Copyright (C) 2002, 2009-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++#define SAFE_WRITE
++#include "safe-read.c"
+diff --git a/lib/safe-write.h b/lib/safe-write.h
+new file mode 100644
+index 0000000..4307ffe
+--- /dev/null
++++ b/lib/safe-write.h
+@@ -0,0 +1,37 @@
++/* An interface to write() that retries after interrupts.
++ Copyright (C) 2002, 2009-2021 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>. */
++
++/* Some system calls may be interrupted and fail with errno = EINTR in the
++ following situations:
++ - The process is stopped and restarted (signal SIGSTOP and SIGCONT, user
++ types Ctrl-Z) on some platforms: Mac OS X.
++ - The process receives a signal for which a signal handler was installed
++ with sigaction() with an sa_flags field that does not contain
++ SA_RESTART.
++ - The process receives a signal for which a signal handler was installed
++ with signal() and for which no call to siginterrupt(sig,0) was done,
++ on some platforms: AIX, HP-UX, IRIX, OSF/1, Solaris.
++
++ This module provides a wrapper around write() that handles EINTR. */
++
++#include <stddef.h>
++
++#define SAFE_WRITE_ERROR ((size_t) -1)
++
++/* Write up to COUNT bytes at BUF to descriptor FD, retrying if interrupted.
++ Return the actual number of bytes written, zero for EOF, or SAFE_WRITE_ERROR
++ upon error. */
++extern size_t safe_write (int fd, const void *buf, size_t count);
+diff --git a/m4/safe-write.m4 b/m4/safe-write.m4
+new file mode 100644
+index 0000000..ef10d96
+--- /dev/null
++++ b/m4/safe-write.m4
+@@ -0,0 +1,11 @@
++# safe-write.m4 serial 4
++dnl Copyright (C) 2002, 2005-2006, 2009-2021 Free Software Foundation, Inc.
++dnl This file is free software; the Free Software Foundation
++dnl gives unlimited permission to copy and/or distribute it,
++dnl with or without modifications, as long as this notice is preserved.
++
++# Prerequisites of lib/safe-write.c.
++AC_DEFUN([gl_PREREQ_SAFE_WRITE],
++[
++ gl_PREREQ_SAFE_READ
++])
+
+
diff --git a/srcpkgs/recutils/patches/exdev.patch b/srcpkgs/recutils/patches/exdev.patch
new file mode 100644
index 000000000000..6d1f8bb213a1
--- /dev/null
+++ b/srcpkgs/recutils/patches/exdev.patch
@@ -0,0 +1,53 @@
+From 86f662a8202408134a235572ec60141d3082f975 Mon Sep 17 00:00:00 2001
+From: "Jose E. Marchesi" <jose.marchesi@oracle.com>
+Date: Tue, 28 Jan 2020 12:13:42 +0100
+Subject: utils: make utilitis to work with temporary files in a different
+ filesystem
+
+2020-01-28 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * bootstrap.conf (gnulib_modules): Import the modules copy-file
+ and remove.
+ * utils/recutl.c (recutl_write_db_to_file): Copy and remove
+ instead of rename temporary files.
+---
+ utils/recutl.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+(limited to 'utils/recutl.c')
+
+diff --git a/utils/recutl.c b/utils/recutl.c
+index 9cf823d..a814fd8 100644
+--- a/utils/recutl.c
++++ b/utils/recutl.c
+@@ -58,6 +58,7 @@
+ #include <rec.h>
+ #include <recutl.h>
+ #include "read-file.h"
++#include "copy-file.h"
+
+ /*
+ * Global variables.
+@@ -432,12 +433,13 @@ recutl_write_db_to_file (rec_db_t db,
+
+ if (file_name)
+ {
+- /* Rename the temporary file to file_name. */
+- if (rename (tmp_file_name, file_name) == -1)
+- {
+- remove (tmp_file_name);
+- recutl_fatal (_("renaming file %s to %s\n"), tmp_file_name, file_name);
+- }
++ /* Rename the temporary file to file_name. We copy and remove
++ instead of renaming because the later doesn't work across
++ different mount points, and it is getting common for /tmp to
++ be mounted in its own filesystem. */
++ if (qcopy_file_preserving (tmp_file_name, file_name) != 0)
++ recutl_fatal (_("renaming file %s to %s\n"), tmp_file_name, file_name);
++ remove (tmp_file_name);
+
+ /* Restore the attributes of the original file. */
+ if (stat_result != -1)
+--
+cgit v1.2.1
+
diff --git a/srcpkgs/recutils/template b/srcpkgs/recutils/template
index 3e71312c4e25..76e6da4c5848 100644
--- a/srcpkgs/recutils/template
+++ b/srcpkgs/recutils/template
@@ -1,17 +1,25 @@
# Template file for 'recutils'
pkgname=recutils
version=1.8
-revision=1
+revision=2
build_style=gnu-configure
configure_args="--with-bash-headers --disable-rpath"
-hostmakedepends="pkg-config"
+# TODO remove automake in 1.9; only needed for copy-file
+hostmakedepends="automake pkg-config"
makedepends="acl-devel libgcrypt-devel libuuid-devel libcurl-devel"
+pre_configure() {
+ # XXX horrible kludge to avoid help2man - remove in 1.9
+ touch man/*.1
+ # XXX ONLY needed for copy-file - remove in 1.9
+ aclocal && automake
+}
short_desc="Utilities to deal with recfiles"
maintainer="Orphaned <orphan@voidlinux.org>"
license="GPL-3.0-or-later"
homepage="https://www.gnu.org/software/recutils/"
distfiles="${GNU_SITE}/recutils/recutils-${version}.tar.gz"
checksum=df8eae69593fdba53e264cbf4b2307dfb82120c09b6fab23e2dad51a89a5b193
+patch_args=-Np1
librec1_package() {
short_desc+=" - rec1 library"
next reply other threads:[~2021-04-28 21:30 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-04-28 21:30 loreb [this message]
2021-04-28 21:38 ` [PR REVIEW] " ericonr
2021-04-28 21:46 ` [PR PATCH] [Updated] " loreb
2021-04-28 21:48 ` [PR REVIEW] " loreb
2021-04-28 21:52 ` ericonr
2021-04-28 21:55 ` loreb
2021-04-28 21:58 ` loreb
2021-04-28 21:58 ` [PR PATCH] [Updated] " loreb
2021-05-17 13:12 ` loreb
2021-06-07 15:44 ` loreb
2021-06-08 2:29 ` ericonr
2021-06-08 12:54 ` [PR PATCH] [Updated] " loreb
2021-06-08 13:02 ` loreb
2021-06-08 13:21 ` [PR REVIEW] " ericonr
2021-06-08 13:21 ` ericonr
2021-06-08 13:21 ` ericonr
2021-06-08 14:33 ` [PR PATCH] [Updated] " loreb
2021-06-08 14:34 ` [PR REVIEW] " loreb
2021-06-08 14:35 ` loreb
2021-06-08 15:17 ` [PR PATCH] [Updated] " loreb
2021-06-08 15:19 ` [PR REVIEW] " loreb
2022-05-18 2:09 ` github-actions
2022-05-18 14:46 ` loreb
2022-05-18 14:46 ` [PR PATCH] [Closed]: " loreb
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=gh-mailinglist-notifications-41a7ca26-5023-4802-975b-f1789d68868e-void-packages-30567@inbox.vuxu.org \
--to=loreb@users.noreply.github.com \
--cc=ml@inbox.vuxu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).