Github messages for voidlinux
 help / color / mirror / Atom feed
* [PR PATCH] recutils: patch to fix recins not working outside of $TMPDIR
@ 2021-04-28 21:30 loreb
  2021-04-28 21:38 ` [PR REVIEW] " ericonr
                   ` (22 more replies)
  0 siblings, 23 replies; 24+ messages in thread
From: loreb @ 2021-04-28 21:30 UTC (permalink / raw)
  To: ml

[-- 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 (&timespec[0]);
++
++  if (timespec[1].tv_nsec == UTIME_OMIT)
++    timespec[1] = get_stat_mtime (statbuf);
++  else if (timespec[1].tv_nsec == UTIME_NOW)
++    gettime (&timespec[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 (&current_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"

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2022-05-18 14:46 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-28 21:30 [PR PATCH] recutils: patch to fix recins not working outside of $TMPDIR loreb
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 ` [PR PATCH] [Closed]: " loreb
2022-05-18 14:46 ` loreb

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).