(find_dir_entry): Remove unused local, `ent_sb_valid'.
[platform/upstream/coreutils.git] / src / pwd.c
1 /* pwd - print current directory
2    Copyright (C) 1994-1997, 1999-2006 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 2, or (at your option)
7    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, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #include <config.h>
19 #include <getopt.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22
23 #include "system.h"
24 #include "dirfd.h"
25 #include "error.h"
26 #include "long-options.h"
27 #include "quote.h"
28 #include "root-dev-ino.h"
29 #include "xgetcwd.h"
30
31 /* The official name of this program (e.g., no `g' prefix).  */
32 #define PROGRAM_NAME "pwd"
33
34 #define AUTHORS "Jim Meyering"
35
36 struct file_name
37 {
38   char *buf;
39   size_t n_alloc;
40   char *start;
41 };
42
43 enum
44 {
45   NOT_AN_INODE_NUMBER = 0
46 };
47
48 #ifdef D_INO_IN_DIRENT
49 # define D_INO(dp) ((dp)->d_ino)
50 #else
51 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
52 # define D_INO(dp) NOT_AN_INODE_NUMBER
53 #endif
54
55 /* The name this program was run with. */
56 char *program_name;
57
58 void
59 usage (int status)
60 {
61   if (status != EXIT_SUCCESS)
62     fprintf (stderr, _("Try `%s --help' for more information.\n"),
63              program_name);
64   else
65     {
66       printf (_("Usage: %s [OPTION]\n"), program_name);
67       fputs (_("\
68 Print the full filename of the current working directory.\n\
69 \n\
70 "), stdout);
71       fputs (HELP_OPTION_DESCRIPTION, stdout);
72       fputs (VERSION_OPTION_DESCRIPTION, stdout);
73       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
74       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
75     }
76   exit (status);
77 }
78
79 static void
80 file_name_free (struct file_name *p)
81 {
82   free (p->buf);
83   free (p);
84 }
85
86 static struct file_name *
87 file_name_init (void)
88 {
89   struct file_name *p = xmalloc (sizeof *p);
90
91   /* Start with a buffer larger than PATH_MAX, but beware of systems
92      on which PATH_MAX is very large -- e.g., INT_MAX.  */
93   p->n_alloc = MIN (2 * PATH_MAX, 32 * 1024);
94
95   p->buf = xmalloc (p->n_alloc);
96   p->start = p->buf + (p->n_alloc - 1);
97   p->start[0] = '\0';
98   return p;
99 }
100
101 /* Prepend the name S of length S_LEN, to the growing file_name, P.  */
102 static void
103 file_name_prepend (struct file_name *p, char const *s, size_t s_len)
104 {
105   size_t n_free = p->start - p->buf;
106   if (n_free < 1 + s_len)
107     {
108       size_t half = p->n_alloc + 1 + s_len;
109       /* Use xnmalloc+free rather than xnrealloc, since with the latter
110          we'd end up copying the data twice: once via realloc, then again
111          to align it with the end of the new buffer.  With xnmalloc, we
112          copy it only once.  */
113       char *q = xnmalloc (2, half);
114       size_t n_used = p->n_alloc - n_free;
115       p->start = q + 2 * half - n_used;
116       memcpy (p->start, p->buf + n_free, n_used);
117       free (p->buf);
118       p->buf = q;
119       p->n_alloc = 2 * half;
120     }
121
122   p->start -= 1 + s_len;
123   p->start[0] = '/';
124   memcpy (p->start + 1, s, s_len);
125 }
126
127 /* Return a string (malloc'd) consisting of N `/'-separated ".." components.  */
128 static char *
129 nth_parent (size_t n)
130 {
131   char *buf = xnmalloc (3, n);
132   char *p = buf;
133   size_t i;
134
135   for (i = 0; i < n; i++)
136     {
137       memcpy (p, "../", 3);
138       p += 3;
139     }
140   p[-1] = '\0';
141   return buf;
142 }
143
144 /* Determine the basename of the current directory, where DOT_SB is the
145    result of lstat'ing "." and prepend that to the file name in *FILE_NAME.
146    Find the directory entry in `..' that matches the dev/i-node of DOT_SB.
147    Upon success, update *DOT_SB with stat information of `..', chdir to `..',
148    and prepend "/basename" to FILE_NAME.
149    Otherwise, exit with a diagnostic.
150    PARENT_HEIGHT is the number of levels `..' is above the starting directory.
151    The first time this function is called (from the initial directory),
152    PARENT_HEIGHT is 1.  This is solely for diagnostics.
153    Exit nonzero upon error.  */
154
155 static void
156 find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
157                 size_t parent_height)
158 {
159   DIR *dirp;
160   int fd;
161   struct stat parent_sb;
162   bool use_lstat;
163   bool found;
164
165   dirp = opendir ("..");
166   if (dirp == NULL)
167     error (EXIT_FAILURE, errno, _("cannot open directory %s"),
168            quote (nth_parent (parent_height)));
169
170   fd = dirfd (dirp);
171   if ((0 <= fd ? fchdir (fd) : chdir ("..")) < 0)
172     error (EXIT_FAILURE, errno, _("failed to chdir to %s"),
173            quote (nth_parent (parent_height)));
174
175   if ((0 <= fd ? fstat (fd, &parent_sb) : stat (".", &parent_sb)) < 0)
176     error (EXIT_FAILURE, errno, _("failed to stat %s"),
177            quote (nth_parent (parent_height)));
178
179   /* If parent and child directory are on different devices, then we
180      can't rely on d_ino for useful i-node numbers; use lstat instead.  */
181   use_lstat = (parent_sb.st_dev != dot_sb->st_dev);
182
183   found = false;
184   while (1)
185     {
186       struct dirent const *dp;
187       struct stat ent_sb;
188       ino_t ino;
189
190       errno = 0;
191       if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)
192         {
193           if (errno)
194             {
195               /* Save/restore errno across closedir call.  */
196               int e = errno;
197               closedir (dirp);
198               errno = e;
199
200               /* Arrange to give a diagnostic after exiting this loop.  */
201               dirp = NULL;
202             }
203           break;
204         }
205
206       ino = D_INO (dp);
207
208       if (ino == NOT_AN_INODE_NUMBER || use_lstat)
209         {
210           if (lstat (dp->d_name, &ent_sb) < 0)
211             {
212               /* Skip any entry we can't stat.  */
213               continue;
214             }
215           ino = ent_sb.st_ino;
216         }
217
218       if (ino != dot_sb->st_ino)
219         continue;
220
221       /* If we're not crossing a device boundary, then a simple i-node
222          match is enough.  */
223       if ( ! use_lstat || ent_sb.st_dev == dot_sb->st_dev)
224         {
225           file_name_prepend (file_name, dp->d_name, NLENGTH (dp));
226           found = true;
227           break;
228         }
229     }
230
231   if (dirp == NULL || CLOSEDIR (dirp) != 0)
232     {
233       /* Note that this diagnostic serves for both readdir
234          and closedir failures.  */
235       error (EXIT_FAILURE, errno, _("reading directory %s"),
236              quote (nth_parent (parent_height)));
237     }
238
239   if ( ! found)
240     error (EXIT_FAILURE, 0,
241            _("couldn't find directory entry in %s with matching i-node"),
242              quote (nth_parent (parent_height)));
243
244   *dot_sb = parent_sb;
245 }
246
247 /* Construct the full, absolute name of the current working
248    directory and store it in *FILE_NAME.
249    The getcwd function performs nearly the same task, but is typically
250    unable to handle names longer than PATH_MAX.  This function has
251    no such limitation.  However, this function *can* fail due to
252    permission problems or a lack of memory, while Linux's getcwd
253    function works regardless of restricted permissions on parent
254    directories.  Upon failure, give a diagnostic and exit nonzero.
255
256    Note: although this function is similar to getcwd, it has a fundamental
257    difference in that it gives a diagnostic and exits upon failure.
258    I would have liked a function that did not exit, and that could be
259    used as a getcwd replacement.  Unfortunately, considering all of
260    the information the caller would require in order to produce good
261    diagnostics, it doesn't seem worth the added complexity.
262    In any case, any getcwd replacement must *not* exceed the PATH_MAX
263    limitation.  Otherwise, functions like `chdir' would fail with
264    ENAMETOOLONG.
265
266    FIXME-maybe: if find_dir_entry fails due to permissions, try getcwd,
267    in case the unreadable directory is close enough to the root that
268    getcwd works from there.  */
269
270 static void
271 robust_getcwd (struct file_name *file_name)
272 {
273   size_t height = 1;
274   struct dev_ino dev_ino_buf;
275   struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);
276   struct stat dot_sb;
277
278   if (root_dev_ino == NULL)
279     error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
280            quote ("/"));
281
282   if (stat (".", &dot_sb) < 0)
283     error (EXIT_FAILURE, errno, _("failed to stat %s"), quote ("."));
284
285   while (1)
286     {
287       /* If we've reached the root, we're done.  */
288       if (SAME_INODE (dot_sb, *root_dev_ino))
289         break;
290
291       find_dir_entry (&dot_sb, file_name, height++);
292     }
293
294   if (file_name->start[0] == '\0')
295     file_name_prepend (file_name, "/", 1);
296 }
297
298 int
299 main (int argc, char **argv)
300 {
301   char *wd;
302
303   initialize_main (&argc, &argv);
304   program_name = argv[0];
305   setlocale (LC_ALL, "");
306   bindtextdomain (PACKAGE, LOCALEDIR);
307   textdomain (PACKAGE);
308
309   atexit (close_stdout);
310
311   parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
312                       usage, AUTHORS, (char const *) NULL);
313   if (getopt_long (argc, argv, "", NULL, NULL) != -1)
314     usage (EXIT_FAILURE);
315
316   if (optind < argc)
317     error (0, 0, _("ignoring non-option arguments"));
318
319   wd = xgetcwd ();
320   if (wd != NULL)
321     {
322       puts (wd);
323       free (wd);
324     }
325   else
326     {
327       struct file_name *file_name = file_name_init ();
328       robust_getcwd (file_name);
329       puts (file_name->start);
330       file_name_free (file_name);
331     }
332
333   exit (EXIT_SUCCESS);
334 }