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 *
find_mount_point(char const * file,struct stat const * file_stat)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 }
112