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:
Ingo Weinhold 2013-06-07 02:27:48 +02:00
parent 01176bd944
commit bc96e8f30c
4 changed files with 206 additions and 18 deletions

View File

@ -914,6 +914,11 @@ if $(HOST_PLATFORM_BEOS_COMPATIBLE) {
tools rm_attrs ] -f ;
# assumes that rm_attrs is built with debugging disabled
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
View File

@ -66,10 +66,15 @@ options:
--use-gcc-graphite Build with GCC Graphite engine for loop
optimizations. Only for gcc 4.
--use-32bit Use -m32 flag on 64bit host gcc compiler.
--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).
--use-xattr Use Linux xattr respectively *BSD extattr 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).
--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:
HAIKU_AR The static library archiver. Defaults to "ar".
@ -343,6 +348,7 @@ HAIKU_USE_GCC_PIPE=0
HAIKU_USE_GCC_GRAPHITE=0
HAIKU_HOST_USE_32BIT=0
HAIKU_HOST_USE_XATTR=0
HAIKU_HOST_USE_XATTR_REF=0
HOST_GCC_LD=`gcc -print-prog-name=ld`
HOST_GCC_OBJCOPY=`gcc -print-prog-name=objcopy`
SFDISK_BINARY=sfdisk
@ -448,6 +454,7 @@ while [ $# -gt 0 ] ; do
--use-gcc-graphite) HAIKU_USE_GCC_GRAPHITE=1; shift 1;;
--use-32bit) HAIKU_HOST_USE_32BIT=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;;
esac
done
@ -565,6 +572,7 @@ HAIKU_USE_GCC_PIPE ?= "${HAIKU_USE_GCC_PIPE}" ;
HAIKU_USE_GCC_GRAPHITE ?= "${HAIKU_USE_GCC_GRAPHITE}" ;
HAIKU_HOST_USE_32BIT ?= "${HAIKU_HOST_USE_32BIT}" ;
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_MACHINE ?= ${HAIKU_GCC_MACHINE} ;

View File

@ -18,11 +18,30 @@
#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 BPrivate;
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
static status_t
@ -107,28 +126,172 @@ deescape_attr_name(const char *name)
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
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);
char buffer[32];
sprintf(buffer, "/%lld", (int64)ref.node);
sprintf(buffer, "/%" B_PRIdINO, ref.node);
attrDirPath += buffer;
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
static status_t
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();
if (error != B_OK)
return error;
string attrDirPath;
error = get_attribute_dir_path_needed(ref, path, fd, attrDirPath);
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)) {
@ -161,7 +324,7 @@ open_attr_dir(NodeRef ref, const char *path, int fd)
}
// open it
string dirPath(get_attribute_dir_path(ref));
string dirPath(get_attribute_dir_path(ref, path, fd));
return opendir(dirPath.c_str());
}
@ -181,7 +344,7 @@ get_attribute_path(NodeRef ref, const char *path, int fd,
}
// construct the attribute path
attrPath = get_attribute_dir_path(ref) + '/';
attrPath = get_attribute_dir_path(ref, path, fd) + '/';
string attrName(escape_attr_name(attribute));
typePath = attrPath + "t" + attrName;
attrPath += attrName;
@ -407,8 +570,17 @@ fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos,
// open the attribute
int attrFD = fs_fopen_attr(fd, attribute, type,
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;
}
// read
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
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
__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);
string path = get_attribute_dir_path(ref);
strcpy(buffer, path.c_str());
string dirPath = get_attribute_dir_path(ref, path, -1);
strcpy(buffer, dirPath.c_str());
return true;
}

View File

@ -14,7 +14,8 @@
// 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 {
@ -143,7 +144,8 @@ remove_entry(Path& path, bool recursive, bool force, bool removeAttributes)
// remove the file's attributes
if (removeAttributes) {
Path attrDirPath;
if (__get_attribute_dir_path(&st, attrDirPath.Buffer())) {
if (__get_attribute_dir_path(&st, path.GetPath(),
attrDirPath.Buffer())) {
attrDirPath.BufferChanged();
remove_entry(attrDirPath, true, true, false);
}