(root)/
coreutils-9.4/
src/
force-link.c
       1  /* Implement ln -f "atomically"
       2  
       3     Copyright 2017-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Written by Paul Eggert.  */
      19  
      20  /* A naive "ln -f A B" unlinks B and then links A to B.  This module
      21     instead links A to a randomly-named temporary T in B's directory,
      22     and then renames T to B.  This approach has a window with a
      23     randomly-named temporary, which is safer for many applications than
      24     a window where B does not exist.  */
      25  
      26  #include <config.h>
      27  #include "system.h"
      28  
      29  #include "force-link.h"
      30  
      31  #include <tempname.h>
      32  
      33  /* A basename pattern suitable for a temporary file.  It should work
      34     even on file systems like FAT that support only short names.
      35     "Cu" is short for "Coreutils" or for "Changeable unstable",
      36     take your pick....  */
      37  
      38  static char const simple_pattern[] = "CuXXXXXX";
      39  enum { x_suffix_len = sizeof "XXXXXX" - 1 };
      40  
      41  /* A size for smallish buffers containing file names.  Longer file
      42     names can use malloc.  */
      43  
      44  enum { smallsize = 256 };
      45  
      46  /* Return a template for a file in the same directory as DSTNAME.
      47     Use BUF if the template fits, otherwise use malloc and return nullptr
      48     (setting errno) if unsuccessful.  */
      49  
      50  static char *
      51  samedir_template (char const *dstname, char buf[smallsize])
      52  {
      53    ptrdiff_t dstdirlen = last_component (dstname) - dstname;
      54    size_t dsttmpsize = dstdirlen + sizeof simple_pattern;
      55    char *dsttmp;
      56    if (dsttmpsize <= smallsize)
      57      dsttmp = buf;
      58    else
      59      {
      60        dsttmp = malloc (dsttmpsize);
      61        if (!dsttmp)
      62          return dsttmp;
      63      }
      64    strcpy (mempcpy (dsttmp, dstname, dstdirlen), simple_pattern);
      65    return dsttmp;
      66  }
      67  
      68  
      69  /* Auxiliaries for force_linkat.  */
      70  
      71  struct link_arg
      72  {
      73    int srcdir;
      74    char const *srcname;
      75    int dstdir;
      76    int flags;
      77  };
      78  
      79  static int
      80  try_link (char *dest, void *arg)
      81  {
      82    struct link_arg *a = arg;
      83    return linkat (a->srcdir, a->srcname, a->dstdir, dest, a->flags);
      84  }
      85  
      86  /* Hard-link directory SRCDIR's file SRCNAME to directory DSTDIR's
      87     file DSTNAME, using linkat-style FLAGS to control the linking.
      88     If FORCE and DSTNAME already exists, replace it atomically.
      89     If LINKAT_ERRNO is 0, the hard link is already done; if positive,
      90     the hard link was tried and failed with errno == LINKAT_ERRNO.  Return
      91     -1 if successful and DSTNAME already existed,
      92     0 if successful and DSTNAME did not already exist, and
      93     a positive errno value on failure.  */
      94  extern int
      95  force_linkat (int srcdir, char const *srcname,
      96                int dstdir, char const *dstname, int flags, bool force,
      97                int linkat_errno)
      98  {
      99    if (linkat_errno < 0)
     100      linkat_errno = (linkat (srcdir, srcname, dstdir, dstname, flags) == 0
     101                      ? 0 : errno);
     102    if (!force || linkat_errno != EEXIST)
     103      return linkat_errno;
     104  
     105    char buf[smallsize];
     106    char *dsttmp = samedir_template (dstname, buf);
     107    if (! dsttmp)
     108      return errno;
     109    struct link_arg arg = { srcdir, srcname, dstdir, flags };
     110    int err;
     111  
     112    if (try_tempname_len (dsttmp, 0, &arg, try_link, x_suffix_len) != 0)
     113      err = errno;
     114    else
     115      {
     116        err = renameat (dstdir, dsttmp, dstdir, dstname) == 0 ? -1 : errno;
     117        /* Unlink DSTTMP even if renameat succeeded, in case DSTTMP
     118           and DSTNAME were already the same hard link and renameat
     119           was a no-op.  */
     120        unlinkat (dstdir, dsttmp, 0);
     121      }
     122  
     123    if (dsttmp != buf)
     124      free (dsttmp);
     125    return err;
     126  }
     127  
     128  
     129  /* Auxiliaries for force_symlinkat.  */
     130  
     131  struct symlink_arg
     132  {
     133    char const *srcname;
     134    int dstdir;
     135  };
     136  
     137  static int
     138  try_symlink (char *dest, void *arg)
     139  {
     140    struct symlink_arg *a = arg;
     141    return symlinkat (a->srcname, a->dstdir, dest);
     142  }
     143  
     144  /* Create a symlink containing SRCNAME in directory DSTDIR's file DSTNAME.
     145     If FORCE and DSTNAME already exists, replace it atomically.
     146     If SYMLINKAT_ERRNO is 0, the symlink is already done; if positive,
     147     the symlink was tried and failed with errno == SYMLINKAT_ERRNO.  Return
     148     -1 if successful and DSTNAME already existed,
     149     0 if successful and DSTNAME did not already exist, and
     150     a positive errno value on failure.  */
     151  extern int
     152  force_symlinkat (char const *srcname, int dstdir, char const *dstname,
     153                   bool force, int symlinkat_errno)
     154  {
     155    if (symlinkat_errno < 0)
     156      symlinkat_errno = symlinkat (srcname, dstdir, dstname) == 0 ? 0 : errno;
     157    if (!force || symlinkat_errno != EEXIST)
     158      return symlinkat_errno;
     159  
     160    char buf[smallsize];
     161    char *dsttmp = samedir_template (dstname, buf);
     162    if (!dsttmp)
     163      return errno;
     164    struct symlink_arg arg = { srcname, dstdir };
     165    int err;
     166  
     167    if (try_tempname_len (dsttmp, 0, &arg, try_symlink, x_suffix_len) != 0)
     168      err = errno;
     169    else if (renameat (dstdir, dsttmp, dstdir, dstname) != 0)
     170      {
     171        err = errno;
     172        unlinkat (dstdir, dsttmp, 0);
     173      }
     174    else
     175      {
     176        /* Don't worry about renameat being a no-op, since DSTTMP is
     177           newly created.  */
     178        err = -1;
     179      }
     180  
     181    if (dsttmp != buf)
     182      free (dsttmp);
     183    return err;
     184  }