This file is cd.def, from which is created cd.c. It implements the
builtins "cd" and "pwd" in Bash.
-Copyright (C) 1987-2010 Free Software Foundation, Inc.
+Copyright (C) 1987-2013 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include "../bashtypes.h"
#include "posixdir.h"
#include "posixstat.h"
-#ifndef _MINIX
+#if defined (HAVE_SYS_PARAM_H)
#include <sys/param.h>
#endif
+#include <fcntl.h>
#include <stdio.h>
static int bindpwd __P((int));
static int setpwd __P((char *));
static char *resetpwd __P((char *));
-static int change_to_directory __P((char *, int));
+static int change_to_directory __P((char *, int, int));
+
+static int cdxattr __P((char *, char **));
+static void resetxattr __P((void));
/* Change this to 1 to get cd spelling correction by default. */
int cdspelling = 0;
int cdable_vars;
static int eflag; /* file scope so bindpwd() can see it */
+static int xattrflag; /* O_XATTR support for openat */
+static int xattrfd = -1;
$BUILTIN cd
$FUNCTION cd_builtin
-$SHORT_DOC cd [-L|[-P [-e]]] [dir]
+$SHORT_DOC cd [-L|[-P [-e]] [-@]] [dir]
Change the shell working directory.
Change the current directory to DIR. The default DIR is the value of the
its value is used for DIR.
Options:
- -L force symbolic links to be followed
+ -L force symbolic links to be followed: resolve symbolic links in
+ DIR after processing instances of `..'
-P use the physical directory structure without following symbolic
- links
+ links: resolve symbolic links in DIR before processing instances
+ of `..'
-e if the -P option is supplied, and the current working directory
cannot be determined successfully, exit with a non-zero status
+#if defined (O_XATTR)
+ -@ on systems that support it, present a file with extended attributes
+ as a directory containing the file attributes
+#endif
The default is to follow symbolic links, as if `-L' were specified.
+`..' is processed by removing the immediately previous pathname component
+back to a slash or the beginning of DIR.
Exit Status:
Returns 0 if the directory is changed, and if $PWD is set successfully when
return (tdir);
}
+static int
+cdxattr (dir, ndirp)
+ char *dir; /* don't assume we can always free DIR */
+ char **ndirp; /* return new constructed directory name */
+{
+#if defined (O_XATTR)
+ int apfd, fd, r, e;
+ char buf[11+40+40]; /* construct new `fake' path for pwd */
+
+ apfd = openat (AT_FDCWD, dir, O_RDONLY|O_NONBLOCK);
+ if (apfd < 0)
+ return -1;
+ fd = openat (apfd, ".", O_XATTR);
+ e = errno;
+ close (apfd); /* ignore close error for now */
+ errno = e;
+ if (fd < 0)
+ return -1;
+ r = fchdir (fd); /* assume fchdir exists everywhere with O_XATTR */
+ if (r < 0)
+ {
+ close (fd);
+ return -1;
+ }
+ /* NFSv4 and ZFS extended attribute directories do not have names which are
+ visible in the standard Unix directory tree structure. To ensure we have
+ a valid name for $PWD, we synthesize one under /proc, but to keep that
+ path valid, we need to keep the file descriptor open as long as we are in
+ this directory. This imposes a certain structure on /proc. */
+ if (ndirp)
+ {
+ sprintf (buf, "/proc/%d/fd/%d", getpid(), fd);
+ *ndirp = savestring (buf);
+ }
+
+ if (xattrfd >= 0)
+ close (xattrfd);
+ xattrfd = fd;
+
+ return r;
+#else
+ return -1;
+#endif
+}
+
+/* Clean up the O_XATTR baggage. Currently only closes xattrfd */
+static void
+resetxattr ()
+{
+#if defined (O_XATTR)
+ if (xattrfd >= 0)
+ {
+ close (xattrfd);
+ xattrfd = -1;
+ }
+#else
+ xattrfd = -1; /* not strictly necessary */
+#endif
+}
+
#define LCD_DOVARS 0x001
#define LCD_DOSPELL 0x002
#define LCD_PRINTPATH 0x004
eflag = 0;
no_symlinks = no_symbolic_links;
+ xattrflag = 0;
reset_internal_getopt ();
- while ((opt = internal_getopt (list, "LP")) != -1)
+#if defined (O_XATTR)
+ while ((opt = internal_getopt (list, "eLP@")) != -1)
+#else
+ while ((opt = internal_getopt (list, "eLP")) != -1)
+#endif
{
switch (opt)
{
case 'e':
eflag = 1;
break;
+#if defined (O_XATTR)
+ case '@':
+ xattrflag = 1;
+ break;
+#endif
default:
builtin_usage ();
- return (EXECUTION_FAILURE);
+ return (EX_USAGE);
}
}
list = loptend;
}
lflag = 0;
}
+#if defined (CD_COMPLAINS)
+ else if (list->next)
+ {
+ builtin_error (_("too many arguments"));
+ return (EXECUTION_FAILURE);
+ }
+#endif
else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
{
/* This is `cd -', equivalent to `cd $OLDPWD' */
temp = sh_makepath (path, dirname, MP_DOTILDE);
free (path);
- if (change_to_directory (temp, no_symlinks))
+ if (change_to_directory (temp, no_symlinks, xattrflag))
{
/* POSIX.2 says that if a nonempty directory from CDPATH
is used to find the directory to change to, the new
free (temp);
}
-#if 0 /* changed for bash-4.2 Posix cd description steps 5-6 */
+#if 0
+ /* changed for bash-4.2 Posix cd description steps 5-6 */
/* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
try the current directory, so we just punt now with an error
message if POSIXLY_CORRECT is non-zero. The check for cdpath[0]
/* When we get here, DIRNAME is the directory to change to. If we
chdir successfully, just return. */
- if (change_to_directory (dirname, no_symlinks))
+ if (change_to_directory (dirname, no_symlinks, xattrflag))
{
if (lflag & LCD_PRINTPATH)
printf ("%s\n", dirname);
if (lflag & LCD_DOVARS)
{
temp = get_string_value (dirname);
- if (temp && change_to_directory (temp, no_symlinks))
+ if (temp && change_to_directory (temp, no_symlinks, xattrflag))
{
printf ("%s\n", temp);
return (bindpwd (no_symlinks));
if (lflag & LCD_DOSPELL)
{
temp = dirspell (dirname);
- if (temp && change_to_directory (temp, no_symlinks))
+ if (temp && change_to_directory (temp, no_symlinks, xattrflag))
{
printf ("%s\n", temp);
+ free (temp);
return (bindpwd (no_symlinks));
}
else
break;
default:
builtin_usage ();
- return (EXECUTION_FAILURE);
+ return (EX_USAGE);
}
}
list = loptend;
the file system has changed state underneath bash). */
if ((tcwd && directory == 0) ||
(posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
- directory = resetpwd ("pwd");
+ {
+ if (directory && directory != tcwd)
+ free (directory);
+ directory = resetpwd ("pwd");
+ }
#undef tcwd
to the working directory. Return 1 on success, 0 on failure. */
static int
-change_to_directory (newdir, nolinks)
+change_to_directory (newdir, nolinks, xattr)
char *newdir;
- int nolinks;
+ int nolinks, xattr;
{
- char *t, *tdir;
+ char *t, *tdir, *ndir;
int err, canon_failed, r, ndlen, dlen;
tdir = (char *)NULL;
return (0);
}
+#if defined (O_XATTR)
+ if (xattrflag)
+ {
+ r = cdxattr (nolinks ? newdir : tdir, &ndir);
+ if (r >= 0)
+ {
+ canon_failed = 0;
+ free (tdir);
+ tdir = ndir;
+ }
+ else
+ {
+ err = errno;
+ free (tdir);
+ errno = err;
+ return (0); /* no xattr */
+ }
+ }
+ else
+#endif
+ {
+ r = chdir (nolinks ? newdir : tdir);
+ if (r >= 0)
+ resetxattr ();
+ }
+
/* If the chdir succeeds, update the_current_working_directory. */
- if (chdir (nolinks ? newdir : tdir) == 0)
+ if (r == 0)
{
/* If canonicalization failed, but the chdir succeeded, reset the
shell's idea of the_current_working_directory. */
t = resetpwd ("cd");
if (t == 0)
set_working_directory (tdir);
+ else
+ free (t);
}
else
set_working_directory (tdir);