Add more robust generic attribute emulation variant
The new configure option "--use-xattr-ref" enables an xattr assisted variant of the generic attribute emulation. Instead of using the inode ID of a node to identify its attribute directory, we use a reasonably unique random 128 bit number, which we generate and attach as an attribute to the node. This way, when a node changes its inode ID (defragmentation?) or the inode ID of a removed node with a left-over attribute directory is reused, attributes won't get mixed up. The old method is still used for symlinks (since on Linux only priviledged users can write attributes on symlinks), but those usually only have a rather boring BEOS:TYPE attribute, so mix-ups wouldn't be that problematic anyway.
This commit is contained in:
parent
01176bd944
commit
bc96e8f30c
@ -914,6 +914,11 @@ if $(HOST_PLATFORM_BEOS_COMPATIBLE) {
|
|||||||
tools rm_attrs ] -f ;
|
tools rm_attrs ] -f ;
|
||||||
# assumes that rm_attrs is built with debugging disabled
|
# assumes that rm_attrs is built with debugging disabled
|
||||||
HOST_RM_ATTRS_TARGET = <build>rm_attrs ;
|
HOST_RM_ATTRS_TARGET = <build>rm_attrs ;
|
||||||
|
|
||||||
|
# If specified, use xattr support to tag files with unique IDs.
|
||||||
|
if $(HAIKU_HOST_USE_XATTR_REF) = 1 {
|
||||||
|
HOST_DEFINES += HAIKU_HOST_USE_XATTR_REF ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
configure
vendored
16
configure
vendored
@ -66,10 +66,15 @@ options:
|
|||||||
--use-gcc-graphite Build with GCC Graphite engine for loop
|
--use-gcc-graphite Build with GCC Graphite engine for loop
|
||||||
optimizations. Only for gcc 4.
|
optimizations. Only for gcc 4.
|
||||||
--use-32bit Use -m32 flag on 64bit host gcc compiler.
|
--use-32bit Use -m32 flag on 64bit host gcc compiler.
|
||||||
--use-xattr Use Linux xattr support for BeOS attribute
|
--use-xattr Use Linux xattr respectively *BSD extattr support
|
||||||
emulation. Warning: Make sure your file system
|
for BeOS attribute emulation. Warning: Make sure
|
||||||
supports sufficient attribute sizes (4 KB per
|
your file system supports sufficient attribute
|
||||||
file for all attributes won't suffice).
|
sizes (4 KB per file for all attributes won't
|
||||||
|
suffice).
|
||||||
|
--use-xattr-ref Use the generic BeOS attribute emulation, but use
|
||||||
|
Linux xattr respectively *BSD extattr support to
|
||||||
|
make it more robust (i.e. attribute mix-ups become
|
||||||
|
less likely).
|
||||||
|
|
||||||
environment variables:
|
environment variables:
|
||||||
HAIKU_AR The static library archiver. Defaults to "ar".
|
HAIKU_AR The static library archiver. Defaults to "ar".
|
||||||
@ -343,6 +348,7 @@ HAIKU_USE_GCC_PIPE=0
|
|||||||
HAIKU_USE_GCC_GRAPHITE=0
|
HAIKU_USE_GCC_GRAPHITE=0
|
||||||
HAIKU_HOST_USE_32BIT=0
|
HAIKU_HOST_USE_32BIT=0
|
||||||
HAIKU_HOST_USE_XATTR=0
|
HAIKU_HOST_USE_XATTR=0
|
||||||
|
HAIKU_HOST_USE_XATTR_REF=0
|
||||||
HOST_GCC_LD=`gcc -print-prog-name=ld`
|
HOST_GCC_LD=`gcc -print-prog-name=ld`
|
||||||
HOST_GCC_OBJCOPY=`gcc -print-prog-name=objcopy`
|
HOST_GCC_OBJCOPY=`gcc -print-prog-name=objcopy`
|
||||||
SFDISK_BINARY=sfdisk
|
SFDISK_BINARY=sfdisk
|
||||||
@ -448,6 +454,7 @@ while [ $# -gt 0 ] ; do
|
|||||||
--use-gcc-graphite) HAIKU_USE_GCC_GRAPHITE=1; shift 1;;
|
--use-gcc-graphite) HAIKU_USE_GCC_GRAPHITE=1; shift 1;;
|
||||||
--use-32bit) HAIKU_HOST_USE_32BIT=1; shift 1;;
|
--use-32bit) HAIKU_HOST_USE_32BIT=1; shift 1;;
|
||||||
--use-xattr) HAIKU_HOST_USE_XATTR=1; shift 1;;
|
--use-xattr) HAIKU_HOST_USE_XATTR=1; shift 1;;
|
||||||
|
--use-xattr-ref) HAIKU_HOST_USE_XATTR_REF=1; shift 1;;
|
||||||
*) echo Invalid argument: \`$1\'; exit 1;;
|
*) echo Invalid argument: \`$1\'; exit 1;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
@ -565,6 +572,7 @@ HAIKU_USE_GCC_PIPE ?= "${HAIKU_USE_GCC_PIPE}" ;
|
|||||||
HAIKU_USE_GCC_GRAPHITE ?= "${HAIKU_USE_GCC_GRAPHITE}" ;
|
HAIKU_USE_GCC_GRAPHITE ?= "${HAIKU_USE_GCC_GRAPHITE}" ;
|
||||||
HAIKU_HOST_USE_32BIT ?= "${HAIKU_HOST_USE_32BIT}" ;
|
HAIKU_HOST_USE_32BIT ?= "${HAIKU_HOST_USE_32BIT}" ;
|
||||||
HAIKU_HOST_USE_XATTR ?= "${HAIKU_HOST_USE_XATTR}" ;
|
HAIKU_HOST_USE_XATTR ?= "${HAIKU_HOST_USE_XATTR}" ;
|
||||||
|
HAIKU_HOST_USE_XATTR_REF ?= "${HAIKU_HOST_USE_XATTR_REF}" ;
|
||||||
|
|
||||||
HAIKU_GCC_RAW_VERSION ?= ${HAIKU_GCC_RAW_VERSION} ;
|
HAIKU_GCC_RAW_VERSION ?= ${HAIKU_GCC_RAW_VERSION} ;
|
||||||
HAIKU_GCC_MACHINE ?= ${HAIKU_GCC_MACHINE} ;
|
HAIKU_GCC_MACHINE ?= ${HAIKU_GCC_MACHINE} ;
|
||||||
|
@ -18,11 +18,30 @@
|
|||||||
#include "fs_descriptors.h"
|
#include "fs_descriptors.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Include the interface to the host platform attributes support, if it shall be
|
||||||
|
// used to tag files with unique IDs to identify their attribute directory.
|
||||||
|
#if HAIKU_HOST_USE_XATTR_REF
|
||||||
|
# if defined(HAIKU_HOST_PLATFORM_LINUX)
|
||||||
|
# include "fs_attr_xattr.h"
|
||||||
|
# elif defined(HAIKU_HOST_PLATFORM_FREEBSD)
|
||||||
|
# include "fs_attr_extattr.h"
|
||||||
|
# elif defined(HAIKU_HOST_PLATFORM_DARWIN)
|
||||||
|
# include "fs_attr_bsdxattr.h"
|
||||||
|
# else
|
||||||
|
# error No attribute support for this host platform!
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace BPrivate;
|
using namespace BPrivate;
|
||||||
|
|
||||||
static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR;
|
static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR;
|
||||||
|
|
||||||
|
#if HAIKU_HOST_USE_XATTR_REF
|
||||||
|
static const char* const kIDAttributeName = "id";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// init_attribute_dir_base_dir
|
// init_attribute_dir_base_dir
|
||||||
static status_t
|
static status_t
|
||||||
@ -107,28 +126,172 @@ deescape_attr_name(const char *name)
|
|||||||
return deescapedName;
|
return deescapedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_attribute_dir_path
|
|
||||||
|
#if HAIKU_HOST_USE_XATTR_REF
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
make_unique_node_id(string& _id)
|
||||||
|
{
|
||||||
|
// open random device
|
||||||
|
int fd = open("/dev/urandom", O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
fd = open("/dev/random", O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
return B_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read bytes
|
||||||
|
uint8 buffer[16];
|
||||||
|
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
|
||||||
|
status_t error = B_OK;
|
||||||
|
if (bytesRead < 0)
|
||||||
|
error = errno;
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (bytesRead != (ssize_t)sizeof(buffer))
|
||||||
|
error = B_ERROR;
|
||||||
|
|
||||||
|
// convert to hex string
|
||||||
|
static const char* const kHexChars = "0123456789abcdef";
|
||||||
|
_id.clear();
|
||||||
|
for (size_t i = 0; i < sizeof(buffer); i++) {
|
||||||
|
_id += kHexChars[buffer[i] >> 4];
|
||||||
|
_id += kHexChars[buffer[i] & 0xf];
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
get_id_attribute(const char *path, int fd, string& _id)
|
||||||
|
{
|
||||||
|
// list_attributes() and remove_attribute() are unused here -- prevent the
|
||||||
|
// warning
|
||||||
|
(void)list_attributes;
|
||||||
|
(void)remove_attribute;
|
||||||
|
|
||||||
|
string attributeName(kAttributeNamespace);
|
||||||
|
attributeName += kIDAttributeName;
|
||||||
|
|
||||||
|
char buffer[64];
|
||||||
|
ssize_t bytesRead = get_attribute(fd, path, attributeName.c_str(), buffer,
|
||||||
|
sizeof(buffer));
|
||||||
|
if (bytesRead < 0) {
|
||||||
|
// On Linux only priviledged users are allowed to set attributes on
|
||||||
|
// symlinks. So, if this is a symlink, we don't even try and instead
|
||||||
|
// construct a symlink specific node ID.
|
||||||
|
status_t error = errno;
|
||||||
|
struct stat st;
|
||||||
|
if (path == NULL || lstat(path, &st) < 0 || !S_ISLNK(st.st_mode))
|
||||||
|
return error;
|
||||||
|
|
||||||
|
char buffer[32];
|
||||||
|
snprintf(buffer, sizeof(buffer), "symlink-%" B_PRIdINO, st.st_ino);
|
||||||
|
_id = buffer;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
_id = string(buffer, bytesRead);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
set_id_attribute(const char *path, int fd, const char* id)
|
||||||
|
{
|
||||||
|
string attributeName(kAttributeNamespace);
|
||||||
|
attributeName += kIDAttributeName;
|
||||||
|
|
||||||
|
if (set_attribute(fd, path, attributeName.c_str(), id, strlen(id)) < 0)
|
||||||
|
return errno;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static string
|
static string
|
||||||
get_attribute_dir_path(NodeRef ref)
|
get_attribute_dir_path(NodeRef ref, const char *path, int fd)
|
||||||
|
{
|
||||||
|
string id;
|
||||||
|
status_t error = get_id_attribute(path, fd, id);
|
||||||
|
if (error != B_OK)
|
||||||
|
id = "_no_attributes_";
|
||||||
|
|
||||||
|
string attrDirPath(sAttributeDirBasePath);
|
||||||
|
attrDirPath += '/';
|
||||||
|
attrDirPath += id;
|
||||||
|
return attrDirPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
get_attribute_dir_path_needed(NodeRef ref, const char *path, int fd,
|
||||||
|
string& _attrDirPath)
|
||||||
|
{
|
||||||
|
string id;
|
||||||
|
status_t error = get_id_attribute(path, fd, id);
|
||||||
|
if (error != B_OK) {
|
||||||
|
error = make_unique_node_id(id);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = set_id_attribute(path, fd, id.c_str());
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
_attrDirPath = sAttributeDirBasePath;
|
||||||
|
_attrDirPath += '/';
|
||||||
|
_attrDirPath += id;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
|
||||||
|
static string
|
||||||
|
get_attribute_dir_path(NodeRef ref, const char *path, int fd)
|
||||||
{
|
{
|
||||||
string attrDirPath(sAttributeDirBasePath);
|
string attrDirPath(sAttributeDirBasePath);
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
sprintf(buffer, "/%lld", (int64)ref.node);
|
sprintf(buffer, "/%" B_PRIdINO, ref.node);
|
||||||
attrDirPath += buffer;
|
attrDirPath += buffer;
|
||||||
return attrDirPath;
|
return attrDirPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
get_attribute_dir_path_needed(NodeRef ref, const char *path, int fd,
|
||||||
|
string& _attrDirPath)
|
||||||
|
{
|
||||||
|
_attrDirPath = get_attribute_dir_path(ref, path, fd);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// ensure_attribute_dir_exists
|
// ensure_attribute_dir_exists
|
||||||
static status_t
|
static status_t
|
||||||
ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd)
|
ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd)
|
||||||
{
|
{
|
||||||
// init the base directory here
|
// init the base directory and get the attribute directory path
|
||||||
status_t error = init_attribute_dir_base_dir();
|
status_t error = init_attribute_dir_base_dir();
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
string attrDirPath;
|
||||||
|
error = get_attribute_dir_path_needed(ref, path, fd, attrDirPath);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
// stat the dir
|
// stat the dir
|
||||||
string attrDirPath(get_attribute_dir_path(ref));
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(attrDirPath.c_str(), &st) == 0) {
|
if (lstat(attrDirPath.c_str(), &st) == 0) {
|
||||||
if (!S_ISDIR(st.st_mode)) {
|
if (!S_ISDIR(st.st_mode)) {
|
||||||
@ -161,7 +324,7 @@ open_attr_dir(NodeRef ref, const char *path, int fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// open it
|
// open it
|
||||||
string dirPath(get_attribute_dir_path(ref));
|
string dirPath(get_attribute_dir_path(ref, path, fd));
|
||||||
return opendir(dirPath.c_str());
|
return opendir(dirPath.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +344,7 @@ get_attribute_path(NodeRef ref, const char *path, int fd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// construct the attribute path
|
// construct the attribute path
|
||||||
attrPath = get_attribute_dir_path(ref) + '/';
|
attrPath = get_attribute_dir_path(ref, path, fd) + '/';
|
||||||
string attrName(escape_attr_name(attribute));
|
string attrName(escape_attr_name(attribute));
|
||||||
typePath = attrPath + "t" + attrName;
|
typePath = attrPath + "t" + attrName;
|
||||||
attrPath += attrName;
|
attrPath += attrName;
|
||||||
@ -407,8 +570,17 @@ fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos,
|
|||||||
// open the attribute
|
// open the attribute
|
||||||
int attrFD = fs_fopen_attr(fd, attribute, type,
|
int attrFD = fs_fopen_attr(fd, attribute, type,
|
||||||
O_WRONLY | O_CREAT | O_TRUNC);
|
O_WRONLY | O_CREAT | O_TRUNC);
|
||||||
if (attrFD < 0)
|
if (attrFD < 0) {
|
||||||
|
// Setting user attributes on symlinks is not allowed (xattr). So, if
|
||||||
|
// this is a symlink and we're only supposed to write a "BEOS:TYPE"
|
||||||
|
// attribute we silently pretend to have succeeded.
|
||||||
|
struct stat st;
|
||||||
|
if (strcmp(attribute, "BEOS:TYPE") == 0 && fstat(fd, &st) == 0
|
||||||
|
&& S_ISLNK(st.st_mode)) {
|
||||||
|
return readBytes;
|
||||||
|
}
|
||||||
return attrFD;
|
return attrFD;
|
||||||
|
}
|
||||||
|
|
||||||
// read
|
// read
|
||||||
ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes);
|
ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes);
|
||||||
@ -601,12 +773,13 @@ _kern_remove_attr(int fd, const char *name)
|
|||||||
|
|
||||||
|
|
||||||
// __get_attribute_dir_path
|
// __get_attribute_dir_path
|
||||||
extern "C" bool __get_attribute_dir_path(const struct stat* st, char* buffer);
|
extern "C" bool __get_attribute_dir_path(const struct stat* st,
|
||||||
|
const char* path, char* buffer);
|
||||||
bool
|
bool
|
||||||
__get_attribute_dir_path(const struct stat* st, char* buffer)
|
__get_attribute_dir_path(const struct stat* st, const char* path, char* buffer)
|
||||||
{
|
{
|
||||||
NodeRef ref(*st);
|
NodeRef ref(*st);
|
||||||
string path = get_attribute_dir_path(ref);
|
string dirPath = get_attribute_dir_path(ref, path, -1);
|
||||||
strcpy(buffer, path.c_str());
|
strcpy(buffer, dirPath.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
|
|
||||||
|
|
||||||
// exported by the generic attribute support in libroot_build.so
|
// exported by the generic attribute support in libroot_build.so
|
||||||
extern "C" bool __get_attribute_dir_path(const struct stat* st, char* buffer);
|
extern "C" bool __get_attribute_dir_path(const struct stat* st,
|
||||||
|
const char* path, char* buffer);
|
||||||
|
|
||||||
|
|
||||||
class Path {
|
class Path {
|
||||||
@ -143,7 +144,8 @@ remove_entry(Path& path, bool recursive, bool force, bool removeAttributes)
|
|||||||
// remove the file's attributes
|
// remove the file's attributes
|
||||||
if (removeAttributes) {
|
if (removeAttributes) {
|
||||||
Path attrDirPath;
|
Path attrDirPath;
|
||||||
if (__get_attribute_dir_path(&st, attrDirPath.Buffer())) {
|
if (__get_attribute_dir_path(&st, path.GetPath(),
|
||||||
|
attrDirPath.Buffer())) {
|
||||||
attrDirPath.BufferChanged();
|
attrDirPath.BufferChanged();
|
||||||
remove_entry(attrDirPath, true, true, false);
|
remove_entry(attrDirPath, true, true, false);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user