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