(root)/
coreutils-9.4/
lib/
same.c
       1  /* Determine whether two file names refer to the same file.
       2  
       3     Copyright (C) 1997-2000, 2002-2006, 2009-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 Jim Meyering */
      19  
      20  #include <config.h>
      21  
      22  #include <fcntl.h>
      23  #include <stdio.h>
      24  #include <unistd.h>
      25  #include <stdlib.h>
      26  #include <sys/types.h>
      27  #include <sys/stat.h>
      28  #include <ctype.h>
      29  #include <errno.h>
      30  
      31  #include <string.h>
      32  
      33  #include <limits.h>
      34  #ifndef _POSIX_NAME_MAX
      35  # define _POSIX_NAME_MAX 14
      36  #endif
      37  
      38  #include "same.h"
      39  #include "dirname.h"
      40  #include "error.h"
      41  #include "same-inode.h"
      42  
      43  #ifndef MIN
      44  # define MIN(a, b) ((a) < (b) ? (a) : (b))
      45  #endif
      46  
      47  /* Whether file name components are silently truncated (behavior that
      48     POSIX stopped allowing in 2008).  This enables checks whether
      49     truncated base names are the same, while checking the directories.  */
      50  #if !_POSIX_NO_TRUNC && HAVE_FPATHCONF && defined _PC_NAME_MAX
      51  # define CHECK_TRUNCATION true
      52  #else
      53  # define CHECK_TRUNCATION false
      54  #endif
      55  
      56  /* Return nonzero if SOURCE and DEST point to the same name in the same
      57     directory.  */
      58  
      59  bool
      60  same_name (const char *source, const char *dest)
      61  {
      62    return same_nameat (AT_FDCWD, source, AT_FDCWD, dest);
      63  }
      64  
      65  /* Likewise, but interpret the file names relative to SOURCE_FD and DEST_FD,
      66     in the style of openat.  */
      67  
      68  bool
      69  same_nameat (int source_dfd, char const *source,
      70               int dest_dfd, char const *dest)
      71  {
      72    /* Compare the basenames.  */
      73    char const *source_basename = last_component (source);
      74    char const *dest_basename = last_component (dest);
      75    size_t source_baselen = base_len (source_basename);
      76    size_t dest_baselen = base_len (dest_basename);
      77    bool identical_basenames =
      78      (source_baselen == dest_baselen
      79       && memcmp (source_basename, dest_basename, dest_baselen) == 0);
      80    bool compare_dirs = identical_basenames;
      81    bool same = false;
      82  
      83  #if CHECK_TRUNCATION
      84    size_t slen_max = HAVE_LONG_FILE_NAMES ? 255 : _POSIX_NAME_MAX;
      85    size_t min_baselen = MIN (source_baselen, dest_baselen);
      86    if (slen_max <= min_baselen
      87        && memcmp (source_basename, dest_basename, slen_max) == 0)
      88      compare_dirs = true;
      89  #endif
      90  
      91    if (compare_dirs)
      92      {
      93        struct stat source_dir_stats;
      94        struct stat dest_dir_stats;
      95  
      96        /* Compare the parent directories (via the device and inode numbers).  */
      97        char *source_dirname = dir_name (source);
      98        int flags = AT_SYMLINK_NOFOLLOW;
      99        if (fstatat (source_dfd, source_dirname, &source_dir_stats, flags) != 0)
     100          {
     101            /* Shouldn't happen.  */
     102            error (1, errno, "%s", source_dirname);
     103          }
     104        free (source_dirname);
     105  
     106        char *dest_dirname = dir_name (dest);
     107  
     108  #if CHECK_TRUNCATION
     109        int destdir_errno = 0;
     110        int open_flags = O_SEARCH | O_CLOEXEC | O_DIRECTORY;
     111        int destdir_fd = openat (dest_dfd, dest_dirname, open_flags);
     112        if (destdir_fd < 0 || fstat (destdir_fd, &dest_dir_stats) != 0)
     113          destdir_errno = errno;
     114        else if (SAME_INODE (source_dir_stats, dest_dir_stats))
     115          {
     116            same = identical_basenames;
     117            if (! same)
     118              {
     119                errno = 0;
     120                long name_max = fpathconf (destdir_fd, _PC_NAME_MAX);
     121                if (name_max < 0)
     122                  destdir_errno = errno;
     123                else
     124                  same = (name_max <= min_baselen
     125                          && (memcmp (source_basename, dest_basename, name_max)
     126                              == 0));
     127              }
     128          }
     129        close (destdir_fd);
     130        if (destdir_errno != 0)
     131          {
     132            /* Shouldn't happen.  */
     133            error (1, destdir_errno, "%s", dest_dirname);
     134          }
     135  #else
     136        if (fstatat (dest_dfd, dest_dirname, &dest_dir_stats, flags) != 0)
     137          {
     138            /* Shouldn't happen.  */
     139            error (1, errno, "%s", dest_dirname);
     140          }
     141        same = SAME_INODE (source_dir_stats, dest_dir_stats);
     142  #endif
     143  
     144        free (dest_dirname);
     145      }
     146  
     147    return same;
     148  }