(root)/
coreutils-9.4/
src/
find-mount-point.c
       1  /* find-mount-point.c -- find the root mount point for a file.
       2     Copyright (C) 2010-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #include <config.h>
      18  #include <sys/types.h>
      19  
      20  #include "system.h"
      21  #include "save-cwd.h"
      22  #include "xgetcwd.h"
      23  #include "find-mount-point.h"
      24  
      25  /* Return the root mountpoint of the file system on which FILE exists, in
      26     malloced storage.  FILE_STAT should be the result of stating FILE.
      27     Give a diagnostic and return nullptr if unable to determine the mount point.
      28     Exit if unable to restore current working directory.  */
      29  extern char *
      30  find_mount_point (char const *file, struct stat const *file_stat)
      31  {
      32    struct saved_cwd cwd;
      33    struct stat last_stat;
      34    char *mp = nullptr;		/* The malloc'd mount point.  */
      35  
      36    if (save_cwd (&cwd) != 0)
      37      {
      38        error (0, errno, _("cannot get current directory"));
      39        return nullptr;
      40      }
      41  
      42    if (S_ISDIR (file_stat->st_mode))
      43      /* FILE is a directory, so just chdir there directly.  */
      44      {
      45        last_stat = *file_stat;
      46        if (chdir (file) < 0)
      47          {
      48            error (0, errno, _("cannot change to directory %s"), quoteaf (file));
      49            return nullptr;
      50          }
      51      }
      52    else
      53      /* FILE is some other kind of file; use its directory.  */
      54      {
      55        char *xdir = dir_name (file);
      56        char *dir;
      57        ASSIGN_STRDUPA (dir, xdir);
      58        free (xdir);
      59  
      60        if (chdir (dir) < 0)
      61          {
      62            error (0, errno, _("cannot change to directory %s"), quoteaf (dir));
      63            return nullptr;
      64          }
      65  
      66        if (stat (".", &last_stat) < 0)
      67          {
      68            error (0, errno, _("cannot stat current directory (now %s)"),
      69                   quoteaf (dir));
      70            goto done;
      71          }
      72      }
      73  
      74    /* Now walk up FILE's parents until we find another file system or /,
      75       chdiring as we go.  LAST_STAT holds stat information for the last place
      76       we visited.  */
      77    while (true)
      78      {
      79        struct stat st;
      80        if (stat ("..", &st) < 0)
      81          {
      82            error (0, errno, _("cannot stat %s"), quoteaf (".."));
      83            goto done;
      84          }
      85        if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
      86          /* cwd is the mount point.  */
      87          break;
      88        if (chdir ("..") < 0)
      89          {
      90            error (0, errno, _("cannot change to directory %s"), quoteaf (".."));
      91            goto done;
      92          }
      93        last_stat = st;
      94      }
      95  
      96    /* Finally reached a mount point, see what it's called.  */
      97    mp = xgetcwd ();
      98  
      99  done:
     100    /* Restore the original cwd.  */
     101    {
     102      int save_errno = errno;
     103      if (restore_cwd (&cwd) != 0)
     104        error (EXIT_FAILURE, errno,
     105               _("failed to return to initial working directory"));
     106      free_cwd (&cwd);
     107      errno = save_errno;
     108    }
     109  
     110    return mp;
     111  }