From 4f4e5272fc99dc6b2c6004c2c4f5dcff565ff6a4 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sun, 8 Apr 2007 02:19:01 +0000 Subject: [PATCH] Under Linux it is now possible to emulate the BeOS attribute support via xattrs. It can be enabled with the configure switch "--use-xattr". Note that the amount of data stored in attributes may be limited by the used file system -- e.g. AFAIK ext3 has a limit of one block (usually 4 KB) for all attributes of a file, which might not suffice. XFS should be fine, as should ReiserFS 3.6 (or any FS which stores attributes in hidden files). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20609 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- build/jam/BuildSetup | 6 + configure | 13 +- src/build/libroot/fs_attr.cpp | 615 +------------------------ src/build/libroot/fs_attr_generic.cpp | 614 +++++++++++++++++++++++++ src/build/libroot/fs_attr_xattr.cpp | 638 ++++++++++++++++++++++++++ src/build/libroot/fs_descriptors.cpp | 37 +- src/build/libroot/fs_descriptors.h | 9 +- 7 files changed, 1315 insertions(+), 617 deletions(-) create mode 100644 src/build/libroot/fs_attr_generic.cpp create mode 100644 src/build/libroot/fs_attr_xattr.cpp diff --git a/build/jam/BuildSetup b/build/jam/BuildSetup index 040d84dc34..933cb9bb43 100644 --- a/build/jam/BuildSetup +++ b/build/jam/BuildSetup @@ -482,6 +482,12 @@ if $(HOST_PLATFORM_BEOS_COMPATIBLE) { HOST_DEFINES += _LARGEFILE_SOURCE _LARGEFILE64_SOURCE _FILE_OFFSET_BITS=64 ; + + # On Linux with xattr support we can use it for our attribute emulation, + # which is somewhat more robust. + if $(HAIKU_HOST_USE_XATTR) = 1 { + HOST_DEFINES += HAIKU_HOST_USE_XATTR ; + } } # define the executable MIME type diff --git a/configure b/configure index 3091911045..6d4d04f0f7 100755 --- a/configure +++ b/configure @@ -39,6 +39,10 @@ options: valid targets=r5,bone,dano,haiku --use-gcc-pipe Build with GCC option -pipe. Speeds up the build process, but uses more memory. + --use-xattr Use Linux xattr support for BeOS attribute + emulation. Warning: Make sure your file system + supports sufficient attribute sizes (4 KB per + file for all attributes won't suffice). environment variables: HAIKU_AR The static library archiver. Defaults to "ar". @@ -207,6 +211,7 @@ bochs_debug=0 include_gpl_addons=0 target=haiku use_gcc_pipe=0 +use_xattr=0 crossToolsPrefix= buildCrossTools= buildCrossToolsScript="$sourceDir/build/scripts/build_cross_tools" @@ -248,6 +253,7 @@ while [ $# -gt 0 ] ; do --include-gpl-addons) include_gpl_addons=1; shift 1;; --target=*) target=`echo $1 | cut -d'=' -f2-`; shift 1;; --use-gcc-pipe) use_gcc_pipe=1; shift 1;; + --use-xattr) use_xattr=1; shift 1;; *) echo Invalid argument: \`$1\'; exit 1;; esac done @@ -318,9 +324,10 @@ cat << EOF > "$buildOutputDir/BuildConfig" TARGET_PLATFORM ?= "${target}" ; HOST_PLATFORM ?= "${buildPlatform}" ; -BOCHS_DEBUG_HACK ?= "${bochs_debug}" ; -INCLUDE_GPL_ADDONS ?= "${include_gpl_addons}" ; -HAIKU_USE_GCC_PIPE ?= "${use_gcc_pipe}" ; +BOCHS_DEBUG_HACK ?= "${bochs_debug}" ; +INCLUDE_GPL_ADDONS ?= "${include_gpl_addons}" ; +HAIKU_USE_GCC_PIPE ?= "${use_gcc_pipe}" ; +HAIKU_HOST_USE_XATTR ?= "${use_xattr}" ; HAIKU_GCC_RAW_VERSION ?= ${haikuGCCVersion} ; HAIKU_GCC_MACHINE ?= ${haikuGCCMachine} ; diff --git a/src/build/libroot/fs_attr.cpp b/src/build/libroot/fs_attr.cpp index 4fd1680372..8da88f2a7c 100644 --- a/src/build/libroot/fs_attr.cpp +++ b/src/build/libroot/fs_attr.cpp @@ -1,614 +1,5 @@ - -#ifdef BUILDING_FS_SHELL -# include "compat.h" -# define B_OK 0 -# define B_BAD_VALUE EINVAL -# define B_FILE_ERROR EBADF +#ifdef HAIKU_HOST_USE_XATTR +# include "fs_attr_xattr.cpp" #else -# include -# include +# include "fs_attr_generic.cpp" #endif - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "fs_impl.h" -#include "fs_descriptors.h" - - -using namespace std; -using namespace BPrivate; - -static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR; - -// init_attribute_dir_base_dir -static status_t -init_attribute_dir_base_dir() -{ - static bool initialized = false; - static status_t initError; - - if (initialized) - return initError; - - // stat the dir - struct stat st; - initError = B_OK; - if (lstat(sAttributeDirBasePath, &st) == 0) { - if (!S_ISDIR(st.st_mode)) { - // the attribute dir base dir is no directory - fprintf(stderr, "init_attribute_dir_base_dir(): The Attribute " - "directory base directory exists, but is no directory!\n"); - initError = B_FILE_ERROR; - } - - } else { - // doesn't exist yet: create it - if (mkdir(sAttributeDirBasePath, S_IRWXU | S_IRWXG | S_IRWXO) < 0) - initError = errno; - } - - initialized = true; - return initError; -} - -// escape_attr_name -static string -escape_attr_name(const char *name) -{ - string escapedName("_"); - while (*name != '\0') { - // we replace '/' with "_s" and '_' with "__" - if (*name == '/') - escapedName += "_s"; - else if (*name == '_') - escapedName += "__"; - else - escapedName += *name; - - name++; - } - - return escapedName; -} - -// deescape_attr_name -static string -deescape_attr_name(const char *name) -{ - if (name[0] != '_') { - debugger("deescape_attr_name(): name doesn't start with '_'!\n"); - return "___"; - } - name++; - - string deescapedName; - while (*name != '\0') { - if (*name == '_') { - name++; - if (*name == 's') { - deescapedName += '/'; - } else if (*name == '_') { - deescapedName += '_'; - } else { - debugger("deescape_attr_name(): name contains invalid escaped " - "sequence!\n"); - name--; - } - } else - deescapedName += *name; - - name++; - } - - return deescapedName; -} - -// get_attribute_dir_path -static string -get_attribute_dir_path(NodeRef ref) -{ - string attrDirPath(sAttributeDirBasePath); - char buffer[32]; - sprintf(buffer, "/%lld", (int64)ref.node); - attrDirPath += buffer; - return attrDirPath; -} - -// ensure_attribute_dir_exists -static status_t -ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd) -{ - // init the base directory here - status_t error = init_attribute_dir_base_dir(); - if (error != B_OK) - return error; - - // stat the dir - string attrDirPath(get_attribute_dir_path(ref)); - struct stat st; - if (lstat(attrDirPath.c_str(), &st) == 0) { - if (!S_ISDIR(st.st_mode)) { - // the attribute dir is no directory - fprintf(stderr, "ensure_attribute_dir_exists(): Attribute " - "directory for node %lld exists, but is no directory!\n", - ref.node); - return B_FILE_ERROR; - } - - return B_OK; - } - - // doesn't exist yet: create it - if (mkdir(attrDirPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0) - return errno; - - return B_OK; -} - -// open_attr_dir -static DIR * -open_attr_dir(NodeRef ref, const char *path, int fd) -{ - // make sure the directory exists - status_t error = ensure_attribute_dir_exists(ref, path, fd); - if (error != B_OK) { - errno = error; - return NULL; - } - - // open it - string dirPath(get_attribute_dir_path(ref)); - return opendir(dirPath.c_str()); -} - -// get_attribute_path -static status_t -get_attribute_path(NodeRef ref, const char *path, int fd, - const char *attribute, string &attrPath, string &typePath) -{ - if (!attribute || strlen(attribute) == 0) - return B_BAD_VALUE; - - // make sure the attribute dir for the node exits - status_t error = ensure_attribute_dir_exists(ref, path, fd); - if (error != B_OK) { - errno = error; - return -1; - } - - // construct the attribute path - attrPath = get_attribute_dir_path(ref) + '/'; - string attrName(escape_attr_name(attribute)); - typePath = attrPath + "t" + attrName; - attrPath += attrName; - - return B_OK; -} - -// get_attribute_path -static status_t -get_attribute_path(int fd, const char *attribute, string &attrPath, - string &typePath) -{ - // stat the file to get a NodeRef - struct stat st; - if (fstat(fd, &st) < 0) - return errno; - NodeRef ref(st); - - return get_attribute_path(ref, NULL, fd, attribute, attrPath, typePath); -} - - -#ifndef BUILDING_FS_SHELL - -// get_attribute_path_virtual_fd -static status_t -get_attribute_path_virtual_fd(int fd, const char *attribute, string &attrPath, - string &typePath) -{ - // stat the file to get a NodeRef - struct stat st; - status_t error = _kern_read_stat(fd, NULL, false, &st, sizeof(st)); - if (error != B_OK) - return error; - NodeRef ref(st); - - // Try to get a path. If we can't get a path, this is must be a "real" - // (i.e. system) file descriptor, which is just as well. - string path; - bool pathValid = (get_path(fd, NULL, path) == B_OK); - - // get the attribute path - return get_attribute_path(ref, (pathValid ? path.c_str() : NULL), - (pathValid ? -1 : fd), attribute, attrPath, typePath); -} - -#endif // ! BUILDING_FS_SHELL - - -// # pragma mark - Public API - - -// fs_open_attr_dir -DIR * -fs_open_attr_dir(const char *path) -{ - struct stat st; - if (lstat(path, &st)) - return NULL; - - return open_attr_dir(NodeRef(st), path, -1); -} - -// fs_fopen_attr_dir -DIR * -fs_fopen_attr_dir(int fd) -{ - struct stat st; - -#ifdef BUILDING_FS_SHELL - - if (fstat(fd, &st) < 0) - return NULL; - - return open_attr_dir(NodeRef(st), NULL, fd); - -#else - - status_t error = _kern_read_stat(fd, NULL, false, &st, - sizeof(struct stat)); - if (error != B_OK) { - errno = error; - return NULL; - } - - // Try to get a path. If we can't get a path, this is must be a "real" - // (i.e. system) file descriptor, which is just as well. - string path; - bool pathValid = (get_path(fd, NULL, path) == B_OK); - - // get the attribute path - return open_attr_dir(NodeRef(st), (pathValid ? path.c_str() : NULL), - (pathValid ? -1 : fd)); - -#endif -} - -// fs_close_attr_dir -int -fs_close_attr_dir(DIR *dir) -{ - return closedir(dir); -} - -// fs_read_attr_dir -struct dirent * -fs_read_attr_dir(DIR *dir) -{ - struct dirent *entry = NULL; - while (true) { - // read the next entry - entry = readdir(dir); - if (!entry) - return NULL; - - // ignore administrative entries; the - if (entry->d_name[0] == '_') { - string attrName = deescape_attr_name(entry->d_name); - strcpy(entry->d_name, attrName.c_str()); - return entry; - } - } -} - -// fs_rewind_attr_dir -void -fs_rewind_attr_dir(DIR *dir) -{ - rewinddir(dir); -} - -// fs_open_attr -int -fs_open_attr(int fd, const char *attribute, uint32 type, int openMode) -{ - if (!attribute) { - errno = B_BAD_VALUE; - return -1; - } - - // get the attribute path - string attrPath; - string typePath; - status_t error = get_attribute_path(fd, attribute, attrPath, typePath); - if (error != B_OK) { - errno = error; - return -1; - } - - // check, if the attribute already exists - struct stat st; - bool exists = (lstat(attrPath.c_str(), &st) == 0); - - // open the attribute - int attrFD = open(attrPath.c_str(), openMode, S_IRWXU | S_IRWXG | S_IRWXO); - if (attrFD < 0) - return -1; - - // set the type, if the attribute didn't exist yet - if (!exists) { - // create a file prefixed "t" - int typeFD = creat(typePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); - if (typeFD >= 0) { - // write the type into the file - if (write(typeFD, &type, sizeof(type)) < 0) - error = errno; - - close(typeFD); - - } else - error = errno; - - // remove type and attribute file, if something went wrong - if (error != B_OK) { - if (typeFD > 0) { - unlink(typePath.c_str()); - } - - close(attrFD); - unlink(attrPath.c_str()); - - errno = error; - return -1; - } - } - - return attrFD; -} - -// fs_close_attr -int -fs_close_attr(int fd) -{ - return close(fd); -} - -// fs_read_attr -ssize_t -fs_read_attr(int fd, const char *attribute, uint32 type, off_t pos, - void *buffer, size_t readBytes) -{ - // open the attribute - int attrFD = fs_open_attr(fd, attribute, type, O_RDONLY); - if (attrFD < 0) - return attrFD; - - // read - ssize_t bytesRead = read_pos(attrFD, pos, buffer, readBytes); - status_t error = errno; - - // close the attribute - fs_close_attr(attrFD); - - if (bytesRead < 0) { - errno = error; - return -1; - } - - return bytesRead; -} - -// fs_write_attr -ssize_t -fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos, - const void *buffer, size_t readBytes) -{ - // open the attribute - int attrFD = fs_open_attr(fd, attribute, type, - O_WRONLY | O_CREAT | O_TRUNC); - if (attrFD < 0) - return attrFD; - - // read - ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes); - status_t error = errno; - - // close the attribute - fs_close_attr(attrFD); - - if (bytesWritten < 0) { - errno = error; - return -1; - } - - return bytesWritten; -} - -// fs_remove_attr -int -fs_remove_attr(int fd, const char *attribute) -{ - if (!attribute) { - errno = B_BAD_VALUE; - return -1; - } - - // get the attribute path - string attrPath; - string typePath; - status_t error = get_attribute_path(fd, attribute, attrPath, typePath); - if (error != B_OK) { - errno = error; - return -1; - } - - // remove the attribute - if (unlink(attrPath.c_str()) < 0) - return -1; - - unlink(typePath.c_str()); - - return B_OK; -} - -// fs_stat_attr -int -fs_stat_attr(int fd, const char *attribute, struct attr_info *attrInfo) -{ - if (!attribute || !attrInfo) { - errno = B_BAD_VALUE; - return -1; - } - - // get the attribute path - string attrPath; - string typePath; - status_t error = get_attribute_path(fd, attribute, attrPath, typePath); - if (error != B_OK) { - errno = error; - return -1; - } - - // stat the attribute file to get the size of the attribute - struct stat st; - if (lstat(attrPath.c_str(), &st) < 0) - return -1; - - attrInfo->size = st.st_size; - - // now open the attribute type file and read the attribute's type - int typeFD = open(typePath.c_str(), O_RDONLY); - if (typeFD < 0) - return -1; - - ssize_t bytesRead = read(typeFD, &attrInfo->type, sizeof(attrInfo->type)); - if (bytesRead < 0) - error = errno; - else if (bytesRead < (ssize_t)sizeof(attrInfo->type)) - error = B_FILE_ERROR; - - close(typeFD); - - // fail on error - if (error != B_OK) { - errno = error; - return -1; - } - - return 0; -} - - -// #pragma mark - Private Syscalls - - -#ifndef BUILDING_FS_SHELL - -// _kern_open_attr_dir -int -_kern_open_attr_dir(int fd, const char *path) -{ - // get node ref for the node - struct stat st; - status_t error = _kern_read_stat(fd, path, false, &st, - sizeof(struct stat)); - if (error != B_OK) { - errno = error; - return -1; - } - NodeRef ref(st); - - // If a path was given, get a usable path. - string realPath; - if (path) { - error = get_path(fd, path, realPath); - if (error != B_OK) - return error; - } - - // open the attr dir - DIR *dir = open_attr_dir(ref, (path ? realPath.c_str() : NULL), - (path ? -1 : fd)); - if (!dir) - return errno; - - // create descriptor - AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref); - return add_descriptor(descriptor); -} - -// _kern_rename_attr -status_t -_kern_rename_attr(int fromFile, const char *fromName, int toFile, - const char *toName) -{ - if (!fromName || !toName) - return B_BAD_VALUE; - - // get the attribute paths - string fromAttrPath; - string fromTypePath; - status_t error = get_attribute_path_virtual_fd(fromFile, fromName, - fromAttrPath, fromTypePath); - if (error != B_OK) - return error; - - string toAttrPath; - string toTypePath; - error = get_attribute_path_virtual_fd(toFile, toName, toAttrPath, - toTypePath); - if (error != B_OK) - return error; - - // rename the attribute and type files - if (rename(fromAttrPath.c_str(), toAttrPath.c_str()) < 0) - return errno; - - if (rename(fromTypePath.c_str(), toTypePath.c_str()) < 0) { - // renaming the type file failed: try to rename back the attribute file - error = errno; - - rename(toAttrPath.c_str(), fromAttrPath.c_str()); - - return error; - } - - return B_OK; -} - -// _kern_remove_attr -status_t -_kern_remove_attr(int fd, const char *name) -{ - if (!name) - return B_BAD_VALUE; - - // get the attribute path - string attrPath; - string typePath; - status_t error = get_attribute_path_virtual_fd(fd, name, attrPath, - typePath); - if (error != B_OK) - return error; - - // remove the attribute - if (unlink(attrPath.c_str()) < 0) - return errno; - - unlink(typePath.c_str()); - - return B_OK; -} - -#endif // ! BUILDING_FS_SHELL diff --git a/src/build/libroot/fs_attr_generic.cpp b/src/build/libroot/fs_attr_generic.cpp new file mode 100644 index 0000000000..4fd1680372 --- /dev/null +++ b/src/build/libroot/fs_attr_generic.cpp @@ -0,0 +1,614 @@ + +#ifdef BUILDING_FS_SHELL +# include "compat.h" +# define B_OK 0 +# define B_BAD_VALUE EINVAL +# define B_FILE_ERROR EBADF +#else +# include +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "fs_impl.h" +#include "fs_descriptors.h" + + +using namespace std; +using namespace BPrivate; + +static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR; + +// init_attribute_dir_base_dir +static status_t +init_attribute_dir_base_dir() +{ + static bool initialized = false; + static status_t initError; + + if (initialized) + return initError; + + // stat the dir + struct stat st; + initError = B_OK; + if (lstat(sAttributeDirBasePath, &st) == 0) { + if (!S_ISDIR(st.st_mode)) { + // the attribute dir base dir is no directory + fprintf(stderr, "init_attribute_dir_base_dir(): The Attribute " + "directory base directory exists, but is no directory!\n"); + initError = B_FILE_ERROR; + } + + } else { + // doesn't exist yet: create it + if (mkdir(sAttributeDirBasePath, S_IRWXU | S_IRWXG | S_IRWXO) < 0) + initError = errno; + } + + initialized = true; + return initError; +} + +// escape_attr_name +static string +escape_attr_name(const char *name) +{ + string escapedName("_"); + while (*name != '\0') { + // we replace '/' with "_s" and '_' with "__" + if (*name == '/') + escapedName += "_s"; + else if (*name == '_') + escapedName += "__"; + else + escapedName += *name; + + name++; + } + + return escapedName; +} + +// deescape_attr_name +static string +deescape_attr_name(const char *name) +{ + if (name[0] != '_') { + debugger("deescape_attr_name(): name doesn't start with '_'!\n"); + return "___"; + } + name++; + + string deescapedName; + while (*name != '\0') { + if (*name == '_') { + name++; + if (*name == 's') { + deescapedName += '/'; + } else if (*name == '_') { + deescapedName += '_'; + } else { + debugger("deescape_attr_name(): name contains invalid escaped " + "sequence!\n"); + name--; + } + } else + deescapedName += *name; + + name++; + } + + return deescapedName; +} + +// get_attribute_dir_path +static string +get_attribute_dir_path(NodeRef ref) +{ + string attrDirPath(sAttributeDirBasePath); + char buffer[32]; + sprintf(buffer, "/%lld", (int64)ref.node); + attrDirPath += buffer; + return attrDirPath; +} + +// ensure_attribute_dir_exists +static status_t +ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd) +{ + // init the base directory here + status_t error = init_attribute_dir_base_dir(); + if (error != B_OK) + return error; + + // stat the dir + string attrDirPath(get_attribute_dir_path(ref)); + struct stat st; + if (lstat(attrDirPath.c_str(), &st) == 0) { + if (!S_ISDIR(st.st_mode)) { + // the attribute dir is no directory + fprintf(stderr, "ensure_attribute_dir_exists(): Attribute " + "directory for node %lld exists, but is no directory!\n", + ref.node); + return B_FILE_ERROR; + } + + return B_OK; + } + + // doesn't exist yet: create it + if (mkdir(attrDirPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0) + return errno; + + return B_OK; +} + +// open_attr_dir +static DIR * +open_attr_dir(NodeRef ref, const char *path, int fd) +{ + // make sure the directory exists + status_t error = ensure_attribute_dir_exists(ref, path, fd); + if (error != B_OK) { + errno = error; + return NULL; + } + + // open it + string dirPath(get_attribute_dir_path(ref)); + return opendir(dirPath.c_str()); +} + +// get_attribute_path +static status_t +get_attribute_path(NodeRef ref, const char *path, int fd, + const char *attribute, string &attrPath, string &typePath) +{ + if (!attribute || strlen(attribute) == 0) + return B_BAD_VALUE; + + // make sure the attribute dir for the node exits + status_t error = ensure_attribute_dir_exists(ref, path, fd); + if (error != B_OK) { + errno = error; + return -1; + } + + // construct the attribute path + attrPath = get_attribute_dir_path(ref) + '/'; + string attrName(escape_attr_name(attribute)); + typePath = attrPath + "t" + attrName; + attrPath += attrName; + + return B_OK; +} + +// get_attribute_path +static status_t +get_attribute_path(int fd, const char *attribute, string &attrPath, + string &typePath) +{ + // stat the file to get a NodeRef + struct stat st; + if (fstat(fd, &st) < 0) + return errno; + NodeRef ref(st); + + return get_attribute_path(ref, NULL, fd, attribute, attrPath, typePath); +} + + +#ifndef BUILDING_FS_SHELL + +// get_attribute_path_virtual_fd +static status_t +get_attribute_path_virtual_fd(int fd, const char *attribute, string &attrPath, + string &typePath) +{ + // stat the file to get a NodeRef + struct stat st; + status_t error = _kern_read_stat(fd, NULL, false, &st, sizeof(st)); + if (error != B_OK) + return error; + NodeRef ref(st); + + // Try to get a path. If we can't get a path, this is must be a "real" + // (i.e. system) file descriptor, which is just as well. + string path; + bool pathValid = (get_path(fd, NULL, path) == B_OK); + + // get the attribute path + return get_attribute_path(ref, (pathValid ? path.c_str() : NULL), + (pathValid ? -1 : fd), attribute, attrPath, typePath); +} + +#endif // ! BUILDING_FS_SHELL + + +// # pragma mark - Public API + + +// fs_open_attr_dir +DIR * +fs_open_attr_dir(const char *path) +{ + struct stat st; + if (lstat(path, &st)) + return NULL; + + return open_attr_dir(NodeRef(st), path, -1); +} + +// fs_fopen_attr_dir +DIR * +fs_fopen_attr_dir(int fd) +{ + struct stat st; + +#ifdef BUILDING_FS_SHELL + + if (fstat(fd, &st) < 0) + return NULL; + + return open_attr_dir(NodeRef(st), NULL, fd); + +#else + + status_t error = _kern_read_stat(fd, NULL, false, &st, + sizeof(struct stat)); + if (error != B_OK) { + errno = error; + return NULL; + } + + // Try to get a path. If we can't get a path, this is must be a "real" + // (i.e. system) file descriptor, which is just as well. + string path; + bool pathValid = (get_path(fd, NULL, path) == B_OK); + + // get the attribute path + return open_attr_dir(NodeRef(st), (pathValid ? path.c_str() : NULL), + (pathValid ? -1 : fd)); + +#endif +} + +// fs_close_attr_dir +int +fs_close_attr_dir(DIR *dir) +{ + return closedir(dir); +} + +// fs_read_attr_dir +struct dirent * +fs_read_attr_dir(DIR *dir) +{ + struct dirent *entry = NULL; + while (true) { + // read the next entry + entry = readdir(dir); + if (!entry) + return NULL; + + // ignore administrative entries; the + if (entry->d_name[0] == '_') { + string attrName = deescape_attr_name(entry->d_name); + strcpy(entry->d_name, attrName.c_str()); + return entry; + } + } +} + +// fs_rewind_attr_dir +void +fs_rewind_attr_dir(DIR *dir) +{ + rewinddir(dir); +} + +// fs_open_attr +int +fs_open_attr(int fd, const char *attribute, uint32 type, int openMode) +{ + if (!attribute) { + errno = B_BAD_VALUE; + return -1; + } + + // get the attribute path + string attrPath; + string typePath; + status_t error = get_attribute_path(fd, attribute, attrPath, typePath); + if (error != B_OK) { + errno = error; + return -1; + } + + // check, if the attribute already exists + struct stat st; + bool exists = (lstat(attrPath.c_str(), &st) == 0); + + // open the attribute + int attrFD = open(attrPath.c_str(), openMode, S_IRWXU | S_IRWXG | S_IRWXO); + if (attrFD < 0) + return -1; + + // set the type, if the attribute didn't exist yet + if (!exists) { + // create a file prefixed "t" + int typeFD = creat(typePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); + if (typeFD >= 0) { + // write the type into the file + if (write(typeFD, &type, sizeof(type)) < 0) + error = errno; + + close(typeFD); + + } else + error = errno; + + // remove type and attribute file, if something went wrong + if (error != B_OK) { + if (typeFD > 0) { + unlink(typePath.c_str()); + } + + close(attrFD); + unlink(attrPath.c_str()); + + errno = error; + return -1; + } + } + + return attrFD; +} + +// fs_close_attr +int +fs_close_attr(int fd) +{ + return close(fd); +} + +// fs_read_attr +ssize_t +fs_read_attr(int fd, const char *attribute, uint32 type, off_t pos, + void *buffer, size_t readBytes) +{ + // open the attribute + int attrFD = fs_open_attr(fd, attribute, type, O_RDONLY); + if (attrFD < 0) + return attrFD; + + // read + ssize_t bytesRead = read_pos(attrFD, pos, buffer, readBytes); + status_t error = errno; + + // close the attribute + fs_close_attr(attrFD); + + if (bytesRead < 0) { + errno = error; + return -1; + } + + return bytesRead; +} + +// fs_write_attr +ssize_t +fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos, + const void *buffer, size_t readBytes) +{ + // open the attribute + int attrFD = fs_open_attr(fd, attribute, type, + O_WRONLY | O_CREAT | O_TRUNC); + if (attrFD < 0) + return attrFD; + + // read + ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes); + status_t error = errno; + + // close the attribute + fs_close_attr(attrFD); + + if (bytesWritten < 0) { + errno = error; + return -1; + } + + return bytesWritten; +} + +// fs_remove_attr +int +fs_remove_attr(int fd, const char *attribute) +{ + if (!attribute) { + errno = B_BAD_VALUE; + return -1; + } + + // get the attribute path + string attrPath; + string typePath; + status_t error = get_attribute_path(fd, attribute, attrPath, typePath); + if (error != B_OK) { + errno = error; + return -1; + } + + // remove the attribute + if (unlink(attrPath.c_str()) < 0) + return -1; + + unlink(typePath.c_str()); + + return B_OK; +} + +// fs_stat_attr +int +fs_stat_attr(int fd, const char *attribute, struct attr_info *attrInfo) +{ + if (!attribute || !attrInfo) { + errno = B_BAD_VALUE; + return -1; + } + + // get the attribute path + string attrPath; + string typePath; + status_t error = get_attribute_path(fd, attribute, attrPath, typePath); + if (error != B_OK) { + errno = error; + return -1; + } + + // stat the attribute file to get the size of the attribute + struct stat st; + if (lstat(attrPath.c_str(), &st) < 0) + return -1; + + attrInfo->size = st.st_size; + + // now open the attribute type file and read the attribute's type + int typeFD = open(typePath.c_str(), O_RDONLY); + if (typeFD < 0) + return -1; + + ssize_t bytesRead = read(typeFD, &attrInfo->type, sizeof(attrInfo->type)); + if (bytesRead < 0) + error = errno; + else if (bytesRead < (ssize_t)sizeof(attrInfo->type)) + error = B_FILE_ERROR; + + close(typeFD); + + // fail on error + if (error != B_OK) { + errno = error; + return -1; + } + + return 0; +} + + +// #pragma mark - Private Syscalls + + +#ifndef BUILDING_FS_SHELL + +// _kern_open_attr_dir +int +_kern_open_attr_dir(int fd, const char *path) +{ + // get node ref for the node + struct stat st; + status_t error = _kern_read_stat(fd, path, false, &st, + sizeof(struct stat)); + if (error != B_OK) { + errno = error; + return -1; + } + NodeRef ref(st); + + // If a path was given, get a usable path. + string realPath; + if (path) { + error = get_path(fd, path, realPath); + if (error != B_OK) + return error; + } + + // open the attr dir + DIR *dir = open_attr_dir(ref, (path ? realPath.c_str() : NULL), + (path ? -1 : fd)); + if (!dir) + return errno; + + // create descriptor + AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref); + return add_descriptor(descriptor); +} + +// _kern_rename_attr +status_t +_kern_rename_attr(int fromFile, const char *fromName, int toFile, + const char *toName) +{ + if (!fromName || !toName) + return B_BAD_VALUE; + + // get the attribute paths + string fromAttrPath; + string fromTypePath; + status_t error = get_attribute_path_virtual_fd(fromFile, fromName, + fromAttrPath, fromTypePath); + if (error != B_OK) + return error; + + string toAttrPath; + string toTypePath; + error = get_attribute_path_virtual_fd(toFile, toName, toAttrPath, + toTypePath); + if (error != B_OK) + return error; + + // rename the attribute and type files + if (rename(fromAttrPath.c_str(), toAttrPath.c_str()) < 0) + return errno; + + if (rename(fromTypePath.c_str(), toTypePath.c_str()) < 0) { + // renaming the type file failed: try to rename back the attribute file + error = errno; + + rename(toAttrPath.c_str(), fromAttrPath.c_str()); + + return error; + } + + return B_OK; +} + +// _kern_remove_attr +status_t +_kern_remove_attr(int fd, const char *name) +{ + if (!name) + return B_BAD_VALUE; + + // get the attribute path + string attrPath; + string typePath; + status_t error = get_attribute_path_virtual_fd(fd, name, attrPath, + typePath); + if (error != B_OK) + return error; + + // remove the attribute + if (unlink(attrPath.c_str()) < 0) + return errno; + + unlink(typePath.c_str()); + + return B_OK; +} + +#endif // ! BUILDING_FS_SHELL diff --git a/src/build/libroot/fs_attr_xattr.cpp b/src/build/libroot/fs_attr_xattr.cpp new file mode 100644 index 0000000000..ca9bcdf391 --- /dev/null +++ b/src/build/libroot/fs_attr_xattr.cpp @@ -0,0 +1,638 @@ + +#ifdef BUILDING_FS_SHELL +# include "compat.h" +# define B_OK 0 +# define B_BAD_VALUE EINVAL +# define B_FILE_ERROR EBADF +# define B_ERROR EINVAL +# define B_ENTRY_NOT_FOUND ENOENT +# define B_NO_MEMORY ENOMEM +#else +# include +# include + +# include "fs_impl.h" +# include "fs_descriptors.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +namespace BPrivate {} +using namespace BPrivate; +using std::map; +using std::string; + +// the namespace all attributes live in +static const char* kAttributeNamespace = "user."; +static const int kAttributeNamespaceLen = 5; + +// the maximum length of an attribute listing we support +static const int kMaxAttributeListingLength = 10240; + +// the maximum attribute length we support +static const int kMaxAttributeLength = 10240 * 4; + + + +namespace { + +class AttributeDirectory; + +typedef map AttrDirMap; +static AttrDirMap sAttributeDirectories; + +// LongDirent +struct LongDirent : dirent { + char name[B_FILE_NAME_LENGTH]; +}; + +// AttributeHeader +struct AttributeHeader { + uint32 type; +}; + +// AttributeDirectory +class AttributeDirectory { +public: + AttributeDirectory() + : fFileFD(-1), + fFakeDir(NULL), + fListing(NULL), + fListingLength(-1), + fListingIndex(0) + { + } + + ~AttributeDirectory() + { + if (fFileFD >= 0) + close(fFileFD); + + if (fFakeDir) { + AttrDirMap::iterator it = sAttributeDirectories.find(fFakeDir); + if (it != sAttributeDirectories.end()) + sAttributeDirectories.erase(it); + + closedir(fFakeDir); + } + + free(fListing); + } + + static AttributeDirectory* Get(DIR* dir) + { + AttrDirMap::iterator it = sAttributeDirectories.find(dir); + if (it == sAttributeDirectories.end()) + return NULL; + return it->second; + } + + status_t Init(const char* path, int fileFD) + { + // open a fake DIR + if (!fFakeDir) { + fFakeDir = opendir("."); + if (!fFakeDir) + return B_ERROR; + + sAttributeDirectories[fFakeDir] = this; + } + + if (path) { + // A path was given -- check, if it's a symlink. If so we need to + // keep the path, otherwise we open a FD. + struct stat st; + if (lstat(path, &st)) + return B_ENTRY_NOT_FOUND; + + if (S_ISLNK(st.st_mode)) { + fFileFD = -1; + fPath = path; + } else { + fFileFD = open(path, O_RDONLY); + if (fFileFD < 0) + return errno; + fPath = ""; + } + } else { + // FD was given -- dup it. + fFileFD = dup(fileFD); + if (fFileFD < 0) + return errno; + fPath = ""; + } + + fListingLength = -1; + fListingIndex = 0; + + return B_OK; + } + + DIR* FakeDir() const { return fFakeDir; } + + status_t ReadDir(struct dirent** _entry) + { + // make sure we have a current listing + status_t error = _CheckListing(); + if (error != B_OK) + return error; + + // keep going until we find an entry or hit the end of dir + while (fListingIndex < fListingLength) { + // get next entry name + const char* name = fListing + fListingIndex; + int nameLen = strlen(name); + fListingIndex += nameLen + 1; + + // check the attribute namespace + if (strncmp(name, kAttributeNamespace, kAttributeNamespaceLen) != 0) + continue; + name += kAttributeNamespaceLen; + nameLen -= kAttributeNamespaceLen; + + if (nameLen == 0) { + // Uh, weird attribute. + return B_ERROR; + } + + // prepare the dirent + strcpy(fDirent.d_name, name); + fDirent.d_ino = 0; +// TODO: We need the node ID! + + *_entry = &fDirent; + return B_OK; + } + + // end of dir + *_entry = NULL; + return B_OK; + } + + void RewindDir() + { + fListingIndex = 0; + } + +private: + status_t _CheckListing() + { + if (fListing && fListingLength >= 0) + return B_OK; + + char listing[kMaxAttributeListingLength]; + + // get the listing + ssize_t length; + if (fFileFD >= 0) { + length = flistxattr(fFileFD, listing, kMaxAttributeListingLength); + } else { + length = llistxattr(fPath.c_str(), listing, + kMaxAttributeListingLength); + } + if (length < 0) + return errno; + + // clone the on-stack listing + char* newListing = (char*)realloc(fListing, length); + if (!newListing) + return B_NO_MEMORY; + memcpy(newListing, listing, length); + + fListing = newListing; + fListingLength = length; + fListingIndex = 0; + + return B_OK; + } + +private: + int fFileFD; + string fPath; + DIR* fFakeDir; + LongDirent fDirent; + char* fListing; + int fListingLength; + int fListingIndex; +}; + +// LocalFD +class LocalFD { +public: + LocalFD() + { + } + + ~LocalFD() + { + } + + status_t Init(int fd) + { +#ifndef BUILDING_FS_SHELL + Descriptor* descriptor = get_descriptor(fd); + if (descriptor && !descriptor->IsSystemFD()) { + // we need to get a path + fFD = -1; + return descriptor->GetPath(fPath); + } +#endif + + fFD = fd; + fPath = ""; + return B_OK; + } + + int FD() const + { + return fFD; + } + + const char* Path() const + { + return (fFD < 0 ? fPath.c_str() : NULL); + } + +private: + string fPath; + int fFD; +}; + +} // unnamed namspace + + +// # pragma mark - Public API + + +// fs_open_attr_dir +DIR * +fs_open_attr_dir(const char *path) +{ + AttributeDirectory* attrDir = new AttributeDirectory; + + status_t error = attrDir->Init(path, -1); + if (error != B_OK) { + errno = error; + delete attrDir; + return NULL; + } + + return attrDir->FakeDir(); +} + +// fs_fopen_attr_dir +DIR * +fs_fopen_attr_dir(int fd) +{ + AttributeDirectory* attrDir = new AttributeDirectory; + + status_t error = attrDir->Init(NULL, fd); + if (error != B_OK) { + errno = error; + delete attrDir; + return NULL; + } + + return attrDir->FakeDir(); +} + +// fs_close_attr_dir +int +fs_close_attr_dir(DIR *dir) +{ + AttributeDirectory* attrDir = AttributeDirectory::Get(dir); + if (!attrDir) { + errno = B_BAD_VALUE; + return -1; + } + + delete attrDir; + return 0; +} + +// fs_read_attr_dir +struct dirent * +fs_read_attr_dir(DIR *dir) +{ + // get attr dir + AttributeDirectory* attrDir = AttributeDirectory::Get(dir); + if (!attrDir) { + errno = B_BAD_VALUE; + return NULL; + } + + // read attr dir + dirent* entry = NULL; + status_t error = attrDir->ReadDir(&entry); + if (error != B_OK) { + errno = error; + return NULL; + } + + return entry; +} + +// fs_rewind_attr_dir +void +fs_rewind_attr_dir(DIR *dir) +{ + // get attr dir + AttributeDirectory* attrDir = AttributeDirectory::Get(dir); + if (attrDir) + attrDir->RewindDir(); +} + +// fs_open_attr +int +fs_open_attr(int fd, const char *attribute, uint32 type, int openMode) +{ + // not supported ATM + errno = B_BAD_VALUE; + return -1; +} + +// fs_close_attr +int +fs_close_attr(int fd) +{ + // not supported ATM + errno = B_BAD_VALUE; + return -1; +} + +// fs_read_attr +ssize_t +fs_read_attr(int fd, const char *_attribute, uint32 type, off_t pos, + void *buffer, size_t readBytes) +{ + // check params + if (pos < 0 || pos + readBytes > kMaxAttributeLength + || !_attribute || !buffer) { + errno = B_BAD_VALUE; + return -1; + } + + // resolve FD + LocalFD localFD; + status_t error = localFD.Init(fd); + if (error != B_OK) { + errno = error; + return -1; + } + + // prepend the attribute namespace + string attribute = kAttributeNamespace; + attribute += _attribute; + + // read the attribute + char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; + ssize_t bytesRead = min_c((size_t)kMaxAttributeLength, readBytes) + + sizeof(AttributeHeader); + if (localFD.Path()) { + bytesRead = lgetxattr(localFD.Path(), attribute.c_str(), + attributeBuffer, bytesRead); + } else { + bytesRead = fgetxattr(localFD.FD(), attribute.c_str(), attributeBuffer, + bytesRead); + } + if (bytesRead < 0) { + // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute + // doesn't exist. + if (errno == ENOATTR || errno == ENODATA) + errno = B_ENTRY_NOT_FOUND; + return -1; + } + + // check length for sanity + if ((size_t)bytesRead < sizeof(AttributeHeader)) { + fprintf(stderr, "fs_read_attr(): attribute \"%s\" is shorter than the " + "AttributeHeader!\n", attribute.c_str()); + errno = B_ERROR; + return -1; + } + + // copy the result into the provided buffer + bytesRead -= sizeof(AttributeHeader); + if (bytesRead > pos) { + memcpy(buffer, attributeBuffer + sizeof(AttributeHeader) + pos, + bytesRead - pos); + } else + bytesRead = 0; + + return bytesRead; +} + +// fs_write_attr +ssize_t +fs_write_attr(int fd, const char *_attribute, uint32 type, off_t pos, + const void *buffer, size_t writeBytes) +{ + // check params + if (pos < 0 || pos + writeBytes > kMaxAttributeLength + || !_attribute || !buffer) { + errno = B_BAD_VALUE; + return -1; + } + + // resolve FD + LocalFD localFD; + status_t error = localFD.Init(fd); + if (error != B_OK) { + errno = error; + return -1; + } + + // prepend the attribute namespace + string attribute = kAttributeNamespace; + attribute += _attribute; + + // prepare an attribute buffer + char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; + AttributeHeader* header = (AttributeHeader*)attributeBuffer; + header->type = type; + memset(attributeBuffer + sizeof(AttributeHeader), 0, pos); + memcpy(attributeBuffer + sizeof(AttributeHeader) + pos, buffer, writeBytes); + + // write the attribute + ssize_t toWrite = sizeof(AttributeHeader) + pos + writeBytes; + int result; + if (localFD.Path()) { + result = lsetxattr(localFD.Path(), attribute.c_str(), attributeBuffer, + toWrite, 0); + } else { + result = fsetxattr(localFD.FD(), attribute.c_str(), attributeBuffer, + toWrite, 0); + } + if (result < 0) + return -1; + + return writeBytes; +} + +// fs_remove_attr +int +fs_remove_attr(int fd, const char *_attribute) +{ + // check params + if (!_attribute) { + errno = B_BAD_VALUE; + return -1; + } + + // resolve FD + LocalFD localFD; + status_t error = localFD.Init(fd); + if (error != B_OK) { + errno = error; + return -1; + } + + // prepend the attribute namespace + string attribute = kAttributeNamespace; + attribute += _attribute; + + // remove attribute + int result; + if (localFD.Path()) + result = lremovexattr(localFD.Path(), attribute.c_str()); + else + result = fremovexattr(localFD.FD(), attribute.c_str()); + + if (result < 0) { + // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute + // doesn't exist. + if (errno == ENOATTR || errno == ENODATA) + errno = B_ENTRY_NOT_FOUND; + return -1; + } + return 0; +} + +// fs_stat_attr +int +fs_stat_attr(int fd, const char *_attribute, struct attr_info *attrInfo) +{ + if (!_attribute || !attrInfo) { + errno = B_BAD_VALUE; + return -1; + } + + // resolve FD + LocalFD localFD; + status_t error = localFD.Init(fd); + if (error != B_OK) { + errno = error; + return -1; + } + + // prepend the attribute namespace + string attribute = kAttributeNamespace; + attribute += _attribute; + + // read the attribute + char attributeBuffer[sizeof(AttributeHeader) + kMaxAttributeLength]; + ssize_t bytesRead = sizeof(AttributeHeader) + kMaxAttributeLength; + if (localFD.Path()) { + bytesRead = lgetxattr(localFD.Path(), attribute.c_str(), + attributeBuffer, bytesRead); + } else { + bytesRead = fgetxattr(localFD.FD(), attribute.c_str(), attributeBuffer, + bytesRead); + } + if (bytesRead < 0) { + // Make sure, the error code is B_ENTRY_NOT_FOUND, if the attribute + // doesn't exist. + if (errno == ENOATTR || errno == ENODATA) + errno = B_ENTRY_NOT_FOUND; + return -1; + } + + // check length for sanity + if ((size_t)bytesRead < sizeof(AttributeHeader)) { + fprintf(stderr, "fs_stat_attr(): attribute \"%s\" is shorter than the " + "AttributeHeader!\n", attribute.c_str()); + errno = B_ERROR; + return -1; + } + + attrInfo->size = bytesRead - sizeof(AttributeHeader); + attrInfo->type = ((AttributeHeader*)attributeBuffer)->type; + + return 0; +} + + +// #pragma mark - Private Syscalls + + +#ifndef BUILDING_FS_SHELL + +// _kern_open_attr_dir +int +_kern_open_attr_dir(int fd, const char *path) +{ + // get node ref for the node + struct stat st; + status_t error = _kern_read_stat(fd, path, false, &st, + sizeof(struct stat)); + if (error != B_OK) { + errno = error; + return -1; + } + NodeRef ref(st); + + DIR* dir; + if (path) { + // If a path was given, get a usable path. + string realPath; + status_t error = get_path(fd, path, realPath); + if (error != B_OK) + return error; + + dir = fs_open_attr_dir(realPath.c_str()); + } else + dir = fs_fopen_attr_dir(fd); + + if (!dir) + return errno; + + // create descriptor + AttrDirDescriptor *descriptor = new AttrDirDescriptor(dir, ref); + return add_descriptor(descriptor); +} + +// _kern_rename_attr +status_t +_kern_rename_attr(int fromFile, const char *fromName, int toFile, + const char *toName) +{ + // not supported ATM + return B_BAD_VALUE; +} + +// _kern_remove_attr +status_t +_kern_remove_attr(int fd, const char *name) +{ + if (!name) + return B_BAD_VALUE; + + if (fs_remove_attr(fd, name) < 0) + return errno; + return B_OK; +} + +#endif // ! BUILDING_FS_SHELL diff --git a/src/build/libroot/fs_descriptors.cpp b/src/build/libroot/fs_descriptors.cpp index 454b8e16f5..f871b18aaf 100644 --- a/src/build/libroot/fs_descriptors.cpp +++ b/src/build/libroot/fs_descriptors.cpp @@ -1,5 +1,11 @@ -#include +#ifdef BUILDING_FS_SHELL +# include "compat.h" +# define B_OK 0 +# define B_FILE_ERROR EBADF +#else +# include +#endif #include "fs_descriptors.h" @@ -30,6 +36,20 @@ Descriptor::~Descriptor() { } +// IsSystemFD +bool +Descriptor::IsSystemFD() const +{ + return false; +} + +// GetPath +status_t +Descriptor::GetPath(string& path) const +{ + return get_path(fd, NULL, path); +} + // GetNodeRef status_t Descriptor::GetNodeRef(NodeRef &ref) @@ -95,6 +115,13 @@ FileDescriptor::GetStat(bool traverseLink, struct stat *st) return B_OK; } +// IsSystemFD +bool +FileDescriptor::IsSystemFD() const +{ + return true; +} + // #pragma mark - DirectoryDescriptor @@ -214,6 +241,14 @@ SymlinkDescriptor::GetStat(bool traverseLink, struct stat *st) return B_OK; } +// GetPath +status_t +SymlinkDescriptor::GetPath(string& path) const +{ + path = this->path; + return B_OK; +} + // #pragma mark - AttrDirDescriptor diff --git a/src/build/libroot/fs_descriptors.h b/src/build/libroot/fs_descriptors.h index cf85ece5ca..56dcb4c1d6 100644 --- a/src/build/libroot/fs_descriptors.h +++ b/src/build/libroot/fs_descriptors.h @@ -24,7 +24,10 @@ struct Descriptor { virtual status_t Close() = 0; virtual status_t Dup(Descriptor *&clone) = 0; virtual status_t GetStat(bool traverseLink, struct stat *st) = 0; - + + virtual bool IsSystemFD() const; + virtual status_t GetPath(string& path) const; + virtual status_t GetNodeRef(NodeRef &ref); }; @@ -36,6 +39,8 @@ struct FileDescriptor : Descriptor { virtual status_t Close(); virtual status_t Dup(Descriptor *&clone); virtual status_t GetStat(bool traverseLink, struct stat *st); + + virtual bool IsSystemFD() const; }; // DirectoryDescriptor @@ -62,6 +67,8 @@ struct SymlinkDescriptor : Descriptor { virtual status_t Close(); virtual status_t Dup(Descriptor *&clone); virtual status_t GetStat(bool traverseLink, struct stat *st); + + virtual status_t GetPath(string& path) const; }; // AttrDirDescriptor