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
This commit is contained in:
Ingo Weinhold
2007-04-08 02:19:01 +00:00
parent 055fa4f22f
commit 4f4e5272fc
7 changed files with 1315 additions and 617 deletions
+6
View File
@@ -482,6 +482,12 @@ if $(HOST_PLATFORM_BEOS_COMPATIBLE) {
HOST_DEFINES += HOST_DEFINES +=
_LARGEFILE_SOURCE _LARGEFILE64_SOURCE _FILE_OFFSET_BITS=64 _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 # define the executable MIME type
Vendored
+10 -3
View File
@@ -39,6 +39,10 @@ options:
valid targets=r5,bone,dano,haiku valid targets=r5,bone,dano,haiku
--use-gcc-pipe Build with GCC option -pipe. Speeds up the build --use-gcc-pipe Build with GCC option -pipe. Speeds up the build
process, but uses more memory. 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: environment variables:
HAIKU_AR The static library archiver. Defaults to "ar". HAIKU_AR The static library archiver. Defaults to "ar".
@@ -207,6 +211,7 @@ bochs_debug=0
include_gpl_addons=0 include_gpl_addons=0
target=haiku target=haiku
use_gcc_pipe=0 use_gcc_pipe=0
use_xattr=0
crossToolsPrefix= crossToolsPrefix=
buildCrossTools= buildCrossTools=
buildCrossToolsScript="$sourceDir/build/scripts/build_cross_tools" buildCrossToolsScript="$sourceDir/build/scripts/build_cross_tools"
@@ -248,6 +253,7 @@ while [ $# -gt 0 ] ; do
--include-gpl-addons) include_gpl_addons=1; shift 1;; --include-gpl-addons) include_gpl_addons=1; shift 1;;
--target=*) target=`echo $1 | cut -d'=' -f2-`; shift 1;; --target=*) target=`echo $1 | cut -d'=' -f2-`; shift 1;;
--use-gcc-pipe) use_gcc_pipe=1; shift 1;; --use-gcc-pipe) use_gcc_pipe=1; shift 1;;
--use-xattr) use_xattr=1; shift 1;;
*) echo Invalid argument: \`$1\'; exit 1;; *) echo Invalid argument: \`$1\'; exit 1;;
esac esac
done done
@@ -318,9 +324,10 @@ cat << EOF > "$buildOutputDir/BuildConfig"
TARGET_PLATFORM ?= "${target}" ; TARGET_PLATFORM ?= "${target}" ;
HOST_PLATFORM ?= "${buildPlatform}" ; HOST_PLATFORM ?= "${buildPlatform}" ;
BOCHS_DEBUG_HACK ?= "${bochs_debug}" ; BOCHS_DEBUG_HACK ?= "${bochs_debug}" ;
INCLUDE_GPL_ADDONS ?= "${include_gpl_addons}" ; INCLUDE_GPL_ADDONS ?= "${include_gpl_addons}" ;
HAIKU_USE_GCC_PIPE ?= "${use_gcc_pipe}" ; HAIKU_USE_GCC_PIPE ?= "${use_gcc_pipe}" ;
HAIKU_HOST_USE_XATTR ?= "${use_xattr}" ;
HAIKU_GCC_RAW_VERSION ?= ${haikuGCCVersion} ; HAIKU_GCC_RAW_VERSION ?= ${haikuGCCVersion} ;
HAIKU_GCC_MACHINE ?= ${haikuGCCMachine} ; HAIKU_GCC_MACHINE ?= ${haikuGCCMachine} ;
+3 -612
View File
@@ -1,614 +1,5 @@
#ifdef HAIKU_HOST_USE_XATTR
#ifdef BUILDING_FS_SHELL # include "fs_attr_xattr.cpp"
# include "compat.h"
# define B_OK 0
# define B_BAD_VALUE EINVAL
# define B_FILE_ERROR EBADF
#else #else
# include <BeOSBuildCompatibility.h> # include "fs_attr_generic.cpp"
# include <syscalls.h>
#endif #endif
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string>
#include <fs_attr.h>
#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
+614
View File
@@ -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 <BeOSBuildCompatibility.h>
# include <syscalls.h>
#endif
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string>
#include <fs_attr.h>
#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
+638
View File
@@ -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 <BeOSBuildCompatibility.h>
# include <syscalls.h>
# include "fs_impl.h"
# include "fs_descriptors.h"
#endif
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <attr/xattr.h>
#include <map>
#include <string>
#include <fs_attr.h>
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<DIR*, AttributeDirectory*> 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
+36 -1
View File
@@ -1,5 +1,11 @@
#include <BeOSBuildCompatibility.h> #ifdef BUILDING_FS_SHELL
# include "compat.h"
# define B_OK 0
# define B_FILE_ERROR EBADF
#else
# include <BeOSBuildCompatibility.h>
#endif
#include "fs_descriptors.h" #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 // GetNodeRef
status_t status_t
Descriptor::GetNodeRef(NodeRef &ref) Descriptor::GetNodeRef(NodeRef &ref)
@@ -95,6 +115,13 @@ FileDescriptor::GetStat(bool traverseLink, struct stat *st)
return B_OK; return B_OK;
} }
// IsSystemFD
bool
FileDescriptor::IsSystemFD() const
{
return true;
}
// #pragma mark - DirectoryDescriptor // #pragma mark - DirectoryDescriptor
@@ -214,6 +241,14 @@ SymlinkDescriptor::GetStat(bool traverseLink, struct stat *st)
return B_OK; return B_OK;
} }
// GetPath
status_t
SymlinkDescriptor::GetPath(string& path) const
{
path = this->path;
return B_OK;
}
// #pragma mark - AttrDirDescriptor // #pragma mark - AttrDirDescriptor
+8 -1
View File
@@ -24,7 +24,10 @@ struct Descriptor {
virtual status_t Close() = 0; virtual status_t Close() = 0;
virtual status_t Dup(Descriptor *&clone) = 0; virtual status_t Dup(Descriptor *&clone) = 0;
virtual status_t GetStat(bool traverseLink, struct stat *st) = 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); virtual status_t GetNodeRef(NodeRef &ref);
}; };
@@ -36,6 +39,8 @@ struct FileDescriptor : Descriptor {
virtual status_t Close(); virtual status_t Close();
virtual status_t Dup(Descriptor *&clone); virtual status_t Dup(Descriptor *&clone);
virtual status_t GetStat(bool traverseLink, struct stat *st); virtual status_t GetStat(bool traverseLink, struct stat *st);
virtual bool IsSystemFD() const;
}; };
// DirectoryDescriptor // DirectoryDescriptor
@@ -62,6 +67,8 @@ struct SymlinkDescriptor : Descriptor {
virtual status_t Close(); virtual status_t Close();
virtual status_t Dup(Descriptor *&clone); virtual status_t Dup(Descriptor *&clone);
virtual status_t GetStat(bool traverseLink, struct stat *st); virtual status_t GetStat(bool traverseLink, struct stat *st);
virtual status_t GetPath(string& path) const;
}; };
// AttrDirDescriptor // AttrDirDescriptor