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 +=
|
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
|
||||||
|
|||||||
@@ -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} ;
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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"
|
#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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user