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