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:
@@ -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
|
||||
|
||||
@@ -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} ;
|
||||
|
||||
@@ -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 <BeOSBuildCompatibility.h>
|
||||
# include <syscalls.h>
|
||||
# include "fs_attr_generic.cpp"
|
||||
#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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user