]> git.ipfire.org Git - thirdparty/bash.git/blob - builtins/cd.def
Bash-5.2 patch 17: fix for optimizing forks when using the . builtin in a subshell
[thirdparty/bash.git] / builtins / cd.def
1 This file is cd.def, from which is created cd.c. It implements the
2 builtins "cd" and "pwd" in Bash.
3
4 Copyright (C) 1987-2022 Free Software Foundation, Inc.
5
6 This file is part of GNU Bash, the Bourne Again SHell.
7
8 Bash is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 Bash is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Bash. If not, see <http://www.gnu.org/licenses/>.
20
21 $PRODUCES cd.c
22 #include <config.h>
23
24 #if defined (HAVE_UNISTD_H)
25 # ifdef _MINIX
26 # include <sys/types.h>
27 # endif
28 # include <unistd.h>
29 #endif
30
31 #include "../bashtypes.h"
32 #include "posixdir.h"
33 #include "posixstat.h"
34 #if defined (HAVE_SYS_PARAM_H)
35 #include <sys/param.h>
36 #endif
37 #include <fcntl.h>
38
39 #include <stdio.h>
40
41 #include "../bashansi.h"
42 #include "../bashintl.h"
43
44 #include <errno.h>
45 #include <tilde/tilde.h>
46
47 #include "../shell.h"
48 #include "../flags.h"
49 #include "maxpath.h"
50 #include "common.h"
51 #include "bashgetopt.h"
52
53 #if !defined (errno)
54 extern int errno;
55 #endif /* !errno */
56
57 extern const char * const bash_getcwd_errstr;
58
59 static int bindpwd PARAMS((int));
60 static int setpwd PARAMS((char *));
61 static char *resetpwd PARAMS((char *));
62 static int change_to_directory PARAMS((char *, int, int));
63
64 static int cdxattr PARAMS((char *, char **));
65 static void resetxattr PARAMS((void));
66
67 /* Change this to 1 to get cd spelling correction by default. */
68 int cdspelling = 0;
69
70 int cdable_vars;
71
72 static int eflag; /* file scope so bindpwd() can see it */
73 static int xattrflag; /* O_XATTR support for openat */
74 static int xattrfd = -1;
75
76 $BUILTIN cd
77 $FUNCTION cd_builtin
78 $SHORT_DOC cd [-L|[-P [-e]] [-@]] [dir]
79 Change the shell working directory.
80
81 Change the current directory to DIR. The default DIR is the value of the
82 HOME shell variable. If DIR is "-", it is converted to $OLDPWD.
83
84 The variable CDPATH defines the search path for the directory containing
85 DIR. Alternative directory names in CDPATH are separated by a colon (:).
86 A null directory name is the same as the current directory. If DIR begins
87 with a slash (/), then CDPATH is not used.
88
89 If the directory is not found, and the shell option `cdable_vars' is set,
90 the word is assumed to be a variable name. If that variable has a value,
91 its value is used for DIR.
92
93 Options:
94 -L force symbolic links to be followed: resolve symbolic
95 links in DIR after processing instances of `..'
96 -P use the physical directory structure without following
97 symbolic links: resolve symbolic links in DIR before
98 processing instances of `..'
99 -e if the -P option is supplied, and the current working
100 directory cannot be determined successfully, exit with
101 a non-zero status
102 #if defined (O_XATTR)
103 -@ on systems that support it, present a file with extended
104 attributes as a directory containing the file attributes
105 #endif
106
107 The default is to follow symbolic links, as if `-L' were specified.
108 `..' is processed by removing the immediately previous pathname component
109 back to a slash or the beginning of DIR.
110
111 Exit Status:
112 Returns 0 if the directory is changed, and if $PWD is set successfully when
113 -P is used; non-zero otherwise.
114 $END
115
116 /* Just set $PWD, don't change OLDPWD. Used by `pwd -P' in posix mode. */
117 static int
118 setpwd (dirname)
119 char *dirname;
120 {
121 int old_anm;
122 SHELL_VAR *tvar;
123
124 old_anm = array_needs_making;
125 tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
126 if (tvar && readonly_p (tvar))
127 return EXECUTION_FAILURE;
128 if (tvar && old_anm == 0 && array_needs_making && exported_p (tvar))
129 {
130 update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
131 array_needs_making = 0;
132 }
133 return EXECUTION_SUCCESS;
134 }
135
136 static int
137 bindpwd (no_symlinks)
138 int no_symlinks;
139 {
140 char *dirname, *pwdvar;
141 int old_anm, r, canon_failed;
142 SHELL_VAR *tvar;
143
144 r = sh_chkwrite (EXECUTION_SUCCESS);
145
146 #define tcwd the_current_working_directory
147 dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd)
148 : get_working_directory ("cd");
149 #undef tcwd
150
151 /* If canonicalization fails, reset dirname to the_current_working_directory */
152 canon_failed = 0;
153 if (dirname == 0)
154 {
155 canon_failed = 1;
156 dirname = the_current_working_directory;
157 }
158
159 old_anm = array_needs_making;
160 pwdvar = get_string_value ("PWD");
161
162 tvar = bind_variable ("OLDPWD", pwdvar, 0);
163 if (tvar && readonly_p (tvar))
164 r = EXECUTION_FAILURE;
165
166 if (old_anm == 0 && array_needs_making && exported_p (tvar))
167 {
168 update_export_env_inplace ("OLDPWD=", 7, pwdvar);
169 array_needs_making = 0;
170 }
171
172 if (setpwd (dirname) == EXECUTION_FAILURE)
173 r = EXECUTION_FAILURE;
174 if (canon_failed && eflag)
175 r = EXECUTION_FAILURE;
176
177 if (dirname && dirname != the_current_working_directory)
178 free (dirname);
179
180 return (r);
181 }
182
183 /* Call get_working_directory to reset the value of
184 the_current_working_directory () */
185 static char *
186 resetpwd (caller)
187 char *caller;
188 {
189 char *tdir;
190
191 FREE (the_current_working_directory);
192 the_current_working_directory = (char *)NULL;
193 tdir = get_working_directory (caller);
194 return (tdir);
195 }
196
197 static int
198 cdxattr (dir, ndirp)
199 char *dir; /* don't assume we can always free DIR */
200 char **ndirp; /* return new constructed directory name */
201 {
202 #if defined (O_XATTR)
203 int apfd, fd, r, e;
204 char buf[11+40+40]; /* construct new `fake' path for pwd */
205
206 apfd = openat (AT_FDCWD, dir, O_RDONLY|O_NONBLOCK);
207 if (apfd < 0)
208 return -1;
209 fd = openat (apfd, ".", O_XATTR);
210 e = errno;
211 close (apfd); /* ignore close error for now */
212 errno = e;
213 if (fd < 0)
214 return -1;
215 r = fchdir (fd); /* assume fchdir exists everywhere with O_XATTR */
216 if (r < 0)
217 {
218 close (fd);
219 return -1;
220 }
221 /* NFSv4 and ZFS extended attribute directories do not have names which are
222 visible in the standard Unix directory tree structure. To ensure we have
223 a valid name for $PWD, we synthesize one under /proc, but to keep that
224 path valid, we need to keep the file descriptor open as long as we are in
225 this directory. This imposes a certain structure on /proc. */
226 if (ndirp)
227 {
228 sprintf (buf, "/proc/%d/fd/%d", getpid(), fd);
229 *ndirp = savestring (buf);
230 }
231
232 if (xattrfd >= 0)
233 close (xattrfd);
234 xattrfd = fd;
235
236 return r;
237 #else
238 return -1;
239 #endif
240 }
241
242 /* Clean up the O_XATTR baggage. Currently only closes xattrfd */
243 static void
244 resetxattr ()
245 {
246 #if defined (O_XATTR)
247 if (xattrfd >= 0)
248 {
249 close (xattrfd);
250 xattrfd = -1;
251 }
252 #else
253 xattrfd = -1; /* not strictly necessary */
254 #endif
255 }
256
257 #define LCD_DOVARS 0x001
258 #define LCD_DOSPELL 0x002
259 #define LCD_PRINTPATH 0x004
260 #define LCD_FREEDIRNAME 0x008
261
262 /* This builtin is ultimately the way that all user-visible commands should
263 change the current working directory. It is called by cd_to_string (),
264 so the programming interface is simple, and it handles errors and
265 restrictions properly. */
266 int
267 cd_builtin (list)
268 WORD_LIST *list;
269 {
270 char *dirname, *cdpath, *path, *temp;
271 int path_index, no_symlinks, opt, lflag, e;
272
273 #if defined (RESTRICTED_SHELL)
274 if (restricted)
275 {
276 sh_restricted ((char *)NULL);
277 return (EXECUTION_FAILURE);
278 }
279 #endif /* RESTRICTED_SHELL */
280
281 eflag = 0;
282 no_symlinks = no_symbolic_links;
283 xattrflag = 0;
284 reset_internal_getopt ();
285 #if defined (O_XATTR)
286 while ((opt = internal_getopt (list, "eLP@")) != -1)
287 #else
288 while ((opt = internal_getopt (list, "eLP")) != -1)
289 #endif
290 {
291 switch (opt)
292 {
293 case 'P':
294 no_symlinks = 1;
295 break;
296 case 'L':
297 no_symlinks = 0;
298 break;
299 case 'e':
300 eflag = 1;
301 break;
302 #if defined (O_XATTR)
303 case '@':
304 xattrflag = 1;
305 break;
306 #endif
307 CASE_HELPOPT;
308 default:
309 builtin_usage ();
310 return (EX_USAGE);
311 }
312 }
313 list = loptend;
314
315 lflag = (cdable_vars ? LCD_DOVARS : 0) |
316 ((interactive && cdspelling) ? LCD_DOSPELL : 0);
317 if (eflag && no_symlinks == 0)
318 eflag = 0;
319
320 if (list == 0)
321 {
322 /* `cd' without arguments is equivalent to `cd $HOME' */
323 dirname = get_string_value ("HOME");
324
325 if (dirname == 0)
326 {
327 builtin_error (_("HOME not set"));
328 return (EXECUTION_FAILURE);
329 }
330 lflag = 0;
331 }
332 #if defined (CD_COMPLAINS)
333 else if (list->next)
334 {
335 builtin_error (_("too many arguments"));
336 return (EXECUTION_FAILURE);
337 }
338 #endif
339 #if 0
340 else if (list->word->word[0] == '\0')
341 {
342 builtin_error (_("null directory"));
343 return (EXECUTION_FAILURE);
344 }
345 #endif
346 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
347 {
348 /* This is `cd -', equivalent to `cd $OLDPWD' */
349 dirname = get_string_value ("OLDPWD");
350
351 if (dirname == 0)
352 {
353 builtin_error (_("OLDPWD not set"));
354 return (EXECUTION_FAILURE);
355 }
356 #if 0
357 lflag = interactive ? LCD_PRINTPATH : 0;
358 #else
359 lflag = LCD_PRINTPATH; /* According to SUSv3 */
360 #endif
361 }
362 else if (absolute_pathname (list->word->word))
363 dirname = list->word->word;
364 else if (privileged_mode == 0 && (cdpath = get_string_value ("CDPATH")))
365 {
366 dirname = list->word->word;
367
368 /* Find directory in $CDPATH. */
369 path_index = 0;
370 while (path = extract_colon_unit (cdpath, &path_index))
371 {
372 /* OPT is 1 if the path element is non-empty */
373 opt = path[0] != '\0';
374 temp = sh_makepath (path, dirname, MP_DOTILDE);
375 free (path);
376
377 if (change_to_directory (temp, no_symlinks, xattrflag))
378 {
379 /* POSIX.2 says that if a nonempty directory from CDPATH
380 is used to find the directory to change to, the new
381 directory name is echoed to stdout, whether or not
382 the shell is interactive. */
383 if (opt && (path = no_symlinks ? temp : the_current_working_directory))
384 printf ("%s\n", path);
385
386 free (temp);
387 #if 0
388 /* Posix.2 says that after using CDPATH, the resultant
389 value of $PWD will not contain `.' or `..'. */
390 return (bindpwd (posixly_correct || no_symlinks));
391 #else
392 return (bindpwd (no_symlinks));
393 #endif
394 }
395 else
396 free (temp);
397 }
398
399 #if 0
400 /* changed for bash-4.2 Posix cd description steps 5-6 */
401 /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
402 try the current directory, so we just punt now with an error
403 message if POSIXLY_CORRECT is non-zero. The check for cdpath[0]
404 is so we don't mistakenly treat a CDPATH value of "" as not
405 specifying the current directory. */
406 if (posixly_correct && cdpath[0])
407 {
408 builtin_error ("%s: %s", dirname, strerror (ENOENT));
409 return (EXECUTION_FAILURE);
410 }
411 #endif
412 }
413 else
414 dirname = list->word->word;
415
416 /* When we get here, DIRNAME is the directory to change to. If we
417 chdir successfully, just return. */
418 if (change_to_directory (dirname, no_symlinks, xattrflag))
419 {
420 if (lflag & LCD_PRINTPATH)
421 printf ("%s\n", dirname);
422 return (bindpwd (no_symlinks));
423 }
424
425 /* If the user requests it, then perhaps this is the name of
426 a shell variable, whose value contains the directory to
427 change to. */
428 if (lflag & LCD_DOVARS)
429 {
430 temp = get_string_value (dirname);
431 if (temp && change_to_directory (temp, no_symlinks, xattrflag))
432 {
433 printf ("%s\n", temp);
434 return (bindpwd (no_symlinks));
435 }
436 }
437
438 /* If the user requests it, try to find a directory name similar in
439 spelling to the one requested, in case the user made a simple
440 typo. This is similar to the UNIX 8th and 9th Edition shells. */
441 if (lflag & LCD_DOSPELL)
442 {
443 temp = dirspell (dirname);
444 if (temp && change_to_directory (temp, no_symlinks, xattrflag))
445 {
446 printf ("%s\n", temp);
447 free (temp);
448 return (bindpwd (no_symlinks));
449 }
450 else
451 FREE (temp);
452 }
453
454 e = errno;
455 temp = printable_filename (dirname, 0);
456 builtin_error ("%s: %s", temp, strerror (e));
457 if (temp != dirname)
458 free (temp);
459 return (EXECUTION_FAILURE);
460 }
461
462 $BUILTIN pwd
463 $FUNCTION pwd_builtin
464 $SHORT_DOC pwd [-LP]
465 Print the name of the current working directory.
466
467 Options:
468 -L print the value of $PWD if it names the current working
469 directory
470 -P print the physical directory, without any symbolic links
471
472 By default, `pwd' behaves as if `-L' were specified.
473
474 Exit Status:
475 Returns 0 unless an invalid option is given or the current directory
476 cannot be read.
477 $END
478
479 /* Non-zero means that pwd always prints the physical directory, without
480 symbolic links. */
481 static int verbatim_pwd;
482
483 /* Print the name of the current working directory. */
484 int
485 pwd_builtin (list)
486 WORD_LIST *list;
487 {
488 char *directory;
489 int opt, pflag;
490
491 verbatim_pwd = no_symbolic_links;
492 pflag = 0;
493 reset_internal_getopt ();
494 while ((opt = internal_getopt (list, "LP")) != -1)
495 {
496 switch (opt)
497 {
498 case 'P':
499 verbatim_pwd = pflag = 1;
500 break;
501 case 'L':
502 verbatim_pwd = 0;
503 break;
504 CASE_HELPOPT;
505 default:
506 builtin_usage ();
507 return (EX_USAGE);
508 }
509 }
510 list = loptend;
511
512 #define tcwd the_current_working_directory
513
514 directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
515 : get_working_directory ("pwd");
516
517 /* Try again using getcwd() if canonicalization fails (for instance, if
518 the file system has changed state underneath bash). */
519 if ((tcwd && directory == 0) ||
520 (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
521 {
522 if (directory && directory != tcwd)
523 free (directory);
524 directory = resetpwd ("pwd");
525 }
526
527 #undef tcwd
528
529 if (directory)
530 {
531 opt = EXECUTION_SUCCESS;
532 printf ("%s\n", directory);
533 /* This is dumb but posix-mandated. */
534 if (posixly_correct && pflag)
535 opt = setpwd (directory);
536 if (directory != the_current_working_directory)
537 free (directory);
538 return (sh_chkwrite (opt));
539 }
540 else
541 return (EXECUTION_FAILURE);
542 }
543
544 /* Do the work of changing to the directory NEWDIR. Handle symbolic
545 link following, etc. This function *must* return with
546 the_current_working_directory either set to NULL (in which case
547 getcwd() will eventually be called), or set to a string corresponding
548 to the working directory. Return 1 on success, 0 on failure. */
549
550 static int
551 change_to_directory (newdir, nolinks, xattr)
552 char *newdir;
553 int nolinks, xattr;
554 {
555 char *t, *tdir, *ndir;
556 int err, canon_failed, r, ndlen;
557
558 tdir = (char *)NULL;
559
560 if (the_current_working_directory == 0)
561 {
562 t = get_working_directory ("chdir");
563 FREE (t);
564 }
565
566 t = make_absolute (newdir, the_current_working_directory);
567
568 /* TDIR is either the canonicalized absolute pathname of NEWDIR
569 (nolinks == 0) or the absolute physical pathname of NEWDIR
570 (nolinks != 0). */
571 tdir = nolinks ? sh_physpath (t, 0)
572 : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
573
574 ndlen = strlen (newdir);
575
576 /* Use the canonicalized version of NEWDIR, or, if canonicalization
577 failed, use the non-canonical form. */
578 canon_failed = 0;
579 if (tdir && *tdir)
580 free (t);
581 else
582 {
583 FREE (tdir);
584 tdir = t;
585 canon_failed = 1;
586 }
587
588 /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath
589 returns NULL (because it checks the path, it will return NULL if the
590 resolved path doesn't exist), fail immediately. */
591 #if defined (ENAMETOOLONG)
592 if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX))
593 #else
594 if (posixly_correct && nolinks == 0 && canon_failed && ndlen > PATH_MAX)
595 #endif
596 {
597 #if defined ENAMETOOLONG
598 if (errno != ENOENT && errno != ENAMETOOLONG)
599 #else
600 if (errno != ENOENT)
601 #endif
602 errno = ENOTDIR;
603 free (tdir);
604 return (0);
605 }
606
607 #if defined (O_XATTR)
608 if (xattrflag)
609 {
610 r = cdxattr (nolinks ? newdir : tdir, &ndir);
611 if (r >= 0)
612 {
613 canon_failed = 0;
614 free (tdir);
615 tdir = ndir;
616 }
617 else
618 {
619 err = errno;
620 free (tdir);
621 errno = err;
622 return (0); /* no xattr */
623 }
624 }
625 else
626 #endif
627 {
628 r = chdir (nolinks ? newdir : tdir);
629 if (r >= 0)
630 resetxattr ();
631 }
632
633 /* If the chdir succeeds, update the_current_working_directory. */
634 if (r == 0)
635 {
636 /* If canonicalization failed, but the chdir succeeded, reset the
637 shell's idea of the_current_working_directory. */
638 if (canon_failed)
639 {
640 t = resetpwd ("cd");
641 if (t == 0)
642 set_working_directory (tdir);
643 else
644 free (t);
645 }
646 else
647 set_working_directory (tdir);
648
649 free (tdir);
650 return (1);
651 }
652
653 /* We failed to change to the appropriate directory name. If we tried
654 what the user passed (nolinks != 0), punt now. */
655 if (nolinks)
656 {
657 free (tdir);
658 return (0);
659 }
660
661 err = errno;
662
663 /* We're not in physical mode (nolinks == 0), but we failed to change to
664 the canonicalized directory name (TDIR). Try what the user passed
665 verbatim. If we succeed, reinitialize the_current_working_directory.
666 POSIX requires that we just fail here, so we do in posix mode. */
667 if (posixly_correct == 0 && chdir (newdir) == 0)
668 {
669 t = resetpwd ("cd");
670 if (t == 0)
671 set_working_directory (tdir);
672 else
673 free (t);
674
675 r = 1;
676 }
677 else
678 {
679 errno = err;
680 r = 0;
681 }
682
683 free (tdir);
684 return r;
685 }