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