From 50005129ab0a5c5f2422460e6d7c19616e5e1124 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 17 Dec 2004 03:39:29 +0000 Subject: r4242: added support for storing xattrs in a tdb. This allows all advanced NT attributes (streams, EAs, NT ACLs, timestamps etc) to be used on filesystems that don't support xattrs. It also allows for large streams, although they are very inefficient. I won't enable this by default, as I really wrote it as a way of testing large stream support while still using ext3, but perhaps with a bit more work this could be generally usable. To enable this use: posix:eadb = /home/test/myeas.tdb (This used to be commit 0c927d912cb65754351189d3a0442004a14aa5c6) --- source4/ntvfs/posix/config.mk | 4 +- source4/ntvfs/posix/pvfs_open.c | 6 + source4/ntvfs/posix/pvfs_unlink.c | 5 + source4/ntvfs/posix/pvfs_xattr.c | 93 +++++---------- source4/ntvfs/posix/vfs_posix.c | 16 +++ source4/ntvfs/posix/vfs_posix.h | 3 + source4/ntvfs/posix/xattr_system.c | 134 +++++++++++++++++++++ source4/ntvfs/posix/xattr_tdb.c | 232 +++++++++++++++++++++++++++++++++++++ 8 files changed, 425 insertions(+), 68 deletions(-) create mode 100644 source4/ntvfs/posix/xattr_system.c create mode 100644 source4/ntvfs/posix/xattr_tdb.c diff --git a/source4/ntvfs/posix/config.mk b/source4/ntvfs/posix/config.mk index 44a1625c5f..f2f7207773 100644 --- a/source4/ntvfs/posix/config.mk +++ b/source4/ntvfs/posix/config.mk @@ -28,7 +28,9 @@ ADD_OBJ_FILES = \ ntvfs/posix/pvfs_ioctl.o \ ntvfs/posix/pvfs_xattr.o \ ntvfs/posix/pvfs_streams.o \ - ntvfs/posix/pvfs_acl.o + ntvfs/posix/pvfs_acl.o \ + ntvfs/posix/xattr_system.o \ + ntvfs/posix/xattr_tdb.o REQUIRED_SUBSYSTEMS = NDR_XATTR ntvfs_common # End MODULE ntvfs_posix ################################################ diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index af0858f639..e31d79b9e0 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -267,6 +267,12 @@ static int pvfs_handle_destructor(void *p) if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) && h->name->stream_name == NULL) { + NTSTATUS status; + status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n", + h->name->full_name, nt_errstr(status))); + } if (unlink(h->name->full_name) != 0) { DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n", h->name->full_name, strerror(errno))); diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c index 434577a862..f29a70600f 100644 --- a/source4/ntvfs/posix/pvfs_unlink.c +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -86,6 +86,11 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, return NT_STATUS_FILE_IS_A_DIRECTORY; } + status = pvfs_xattr_unlink_hook(pvfs, name->full_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + /* finally try the actual unlink */ if (unlink(name->full_name) == -1) { status = pvfs_map_errno(pvfs, errno); diff --git a/source4/ntvfs/posix/pvfs_xattr.c b/source4/ntvfs/posix/pvfs_xattr.c index 47549499e6..4487663e8d 100644 --- a/source4/ntvfs/posix/pvfs_xattr.c +++ b/source4/ntvfs/posix/pvfs_xattr.c @@ -21,12 +21,11 @@ */ #include "includes.h" -#include "system/filesys.h" #include "vfs_posix.h" #include "librpc/gen_ndr/ndr_xattr.h" /* - pull a xattr as a blob, from either a file or a file descriptor + pull a xattr as a blob */ static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, @@ -36,45 +35,16 @@ static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs, size_t estimated_size, DATA_BLOB *blob) { -#if HAVE_XATTR_SUPPORT - int ret; - - *blob = data_blob_talloc(mem_ctx, NULL, estimated_size); - if (blob->data == NULL) { - return NT_STATUS_NO_MEMORY; - } - -again: - if (fd != -1) { - ret = fgetxattr(fd, attr_name, blob->data, estimated_size); - } else { - ret = getxattr(fname, attr_name, blob->data, estimated_size); - } - if (ret == -1 && errno == ERANGE) { - estimated_size *= 2; - blob->data = talloc_realloc(mem_ctx, blob->data, estimated_size); - if (blob->data == NULL) { - return NT_STATUS_NO_MEMORY; - } - blob->length = estimated_size; - goto again; + if (pvfs->ea_db) { + return pull_xattr_blob_tdb(pvfs, mem_ctx, attr_name, fname, + fd, estimated_size, blob); } - - if (ret == -1) { - data_blob_free(blob); - return pvfs_map_errno(pvfs, errno); - } - - blob->length = ret; - - return NT_STATUS_OK; -#else - return NT_STATUS_NOT_SUPPORTED; -#endif + return pull_xattr_blob_system(pvfs, mem_ctx, attr_name, fname, + fd, estimated_size, blob); } /* - push a xattr as a blob, from either a file or a file descriptor + push a xattr as a blob */ static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs, const char *attr_name, @@ -82,22 +52,10 @@ static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs, int fd, const DATA_BLOB *blob) { -#if HAVE_XATTR_SUPPORT - int ret; - - if (fd != -1) { - ret = fsetxattr(fd, attr_name, blob->data, blob->length, 0); - } else { - ret = setxattr(fname, attr_name, blob->data, blob->length, 0); + if (pvfs->ea_db) { + return push_xattr_blob_tdb(pvfs, attr_name, fname, fd, blob); } - if (ret == -1) { - return pvfs_map_errno(pvfs, errno); - } - - return NT_STATUS_OK; -#else - return NT_STATUS_NOT_SUPPORTED; -#endif + return push_xattr_blob_system(pvfs, attr_name, fname, fd, blob); } @@ -107,24 +65,24 @@ static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs, static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name, const char *fname, int fd) { -#if HAVE_XATTR_SUPPORT - int ret; - - if (fd != -1) { - ret = fremovexattr(fd, attr_name); - } else { - ret = removexattr(fname, attr_name); - } - if (ret == -1) { - return pvfs_map_errno(pvfs, errno); + if (pvfs->ea_db) { + return delete_xattr_tdb(pvfs, attr_name, fname, fd); } + return delete_xattr_system(pvfs, attr_name, fname, fd); +} - return NT_STATUS_OK; -#else - return NT_STATUS_NOT_SUPPORTED; -#endif +/* + a hook called on unlink - allows the tdb xattr backend to cleanup +*/ +NTSTATUS pvfs_xattr_unlink_hook(struct pvfs_state *pvfs, const char *fname) +{ + if (pvfs->ea_db) { + return unlink_xattr_tdb(pvfs, fname); + } + return unlink_xattr_system(pvfs, fname); } + /* load a NDR structure from a xattr */ @@ -211,7 +169,7 @@ NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name /* not having a DosAttrib is not an error */ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { talloc_free(mem_ctx); - return NT_STATUS_OK; + return pvfs_stream_info(pvfs, name, fd); } if (!NT_STATUS_IS_OK(status)) { @@ -493,3 +451,4 @@ NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs, talloc_free(aname); return status; } + diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c index ff3d3448f2..6f4de1e038 100644 --- a/source4/ntvfs/posix/vfs_posix.c +++ b/source4/ntvfs/posix/vfs_posix.c @@ -35,6 +35,7 @@ static void pvfs_setup_options(struct pvfs_state *pvfs) { int snum = pvfs->tcon->service; int delay; + const char *eadb; if (lp_map_hidden(snum)) pvfs->flags |= PVFS_FLAG_MAP_HIDDEN; if (lp_map_archive(snum)) pvfs->flags |= PVFS_FLAG_MAP_ARCHIVE; @@ -66,6 +67,21 @@ static void pvfs_setup_options(struct pvfs_state *pvfs) FS_ATTR_UNICODE_ON_DISK | FS_ATTR_SPARSE_FILES; + /* allow xattrs to be stored in a external tdb */ + eadb = lp_parm_string(snum, "posix", "eadb"); + if (eadb != NULL) { + pvfs->ea_db = tdb_wrap_open(pvfs, eadb, 50000, + TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (pvfs->ea_db != NULL) { + pvfs->flags |= PVFS_FLAG_XATTR_ENABLE; + } else { + DEBUG(0,("Failed to open eadb '%s' - %s\n", + eadb, strerror(errno))); + pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE; + } + } + + if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) { pvfs->fs_attribs |= FS_ATTR_NAMED_STREAMS; } diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index 16c6f807e9..e80790f6fa 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -57,6 +57,9 @@ struct pvfs_state { /* filesystem attributes (see FS_ATTR_*) */ uint32_t fs_attribs; + + /* if posix:eadb is set, then this gets setup */ + struct tdb_wrap *ea_db; }; /* this is the basic information needed about a file from the filesystem */ diff --git a/source4/ntvfs/posix/xattr_system.c b/source4/ntvfs/posix/xattr_system.c new file mode 100644 index 0000000000..c86ee0bd87 --- /dev/null +++ b/source4/ntvfs/posix/xattr_system.c @@ -0,0 +1,134 @@ +/* + Unix SMB/CIFS implementation. + + POSIX NTVFS backend - xattr support using filesystem xattrs + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "vfs_posix.h" + +/* + pull a xattr as a blob, from either a file or a file descriptor +*/ +NTSTATUS pull_xattr_blob_system(struct pvfs_state *pvfs, + TALLOC_CTX *mem_ctx, + const char *attr_name, + const char *fname, + int fd, + size_t estimated_size, + DATA_BLOB *blob) +{ +#if HAVE_XATTR_SUPPORT + int ret; + + *blob = data_blob_talloc(mem_ctx, NULL, estimated_size); + if (blob->data == NULL) { + return NT_STATUS_NO_MEMORY; + } + +again: + if (fd != -1) { + ret = fgetxattr(fd, attr_name, blob->data, estimated_size); + } else { + ret = getxattr(fname, attr_name, blob->data, estimated_size); + } + if (ret == -1 && errno == ERANGE) { + estimated_size *= 2; + blob->data = talloc_realloc(mem_ctx, blob->data, estimated_size); + if (blob->data == NULL) { + return NT_STATUS_NO_MEMORY; + } + blob->length = estimated_size; + goto again; + } + + if (ret == -1) { + data_blob_free(blob); + return pvfs_map_errno(pvfs, errno); + } + + blob->length = ret; + + return NT_STATUS_OK; +#else + return NT_STATUS_NOT_SUPPORTED; +#endif +} + +/* + push a xattr as a blob, from either a file or a file descriptor +*/ +NTSTATUS push_xattr_blob_system(struct pvfs_state *pvfs, + const char *attr_name, + const char *fname, + int fd, + const DATA_BLOB *blob) +{ +#if HAVE_XATTR_SUPPORT + int ret; + + if (fd != -1) { + ret = fsetxattr(fd, attr_name, blob->data, blob->length, 0); + } else { + ret = setxattr(fname, attr_name, blob->data, blob->length, 0); + } + if (ret == -1) { + return pvfs_map_errno(pvfs, errno); + } + + return NT_STATUS_OK; +#else + return NT_STATUS_NOT_SUPPORTED; +#endif +} + + +/* + delete a xattr +*/ +NTSTATUS delete_xattr_system(struct pvfs_state *pvfs, const char *attr_name, + const char *fname, int fd) +{ +#if HAVE_XATTR_SUPPORT + int ret; + + if (fd != -1) { + ret = fremovexattr(fd, attr_name); + } else { + ret = removexattr(fname, attr_name); + } + if (ret == -1) { + return pvfs_map_errno(pvfs, errno); + } + + return NT_STATUS_OK; +#else + return NT_STATUS_NOT_SUPPORTED; +#endif +} + +/* + unlink a file - cleanup any xattrs +*/ +NTSTATUS unlink_xattr_system(struct pvfs_state *pvfs, const char *fname) +{ + /* nothing needs to be done for filesystem based xattrs */ + return NT_STATUS_OK; +} diff --git a/source4/ntvfs/posix/xattr_tdb.c b/source4/ntvfs/posix/xattr_tdb.c new file mode 100644 index 0000000000..08a2ea4c18 --- /dev/null +++ b/source4/ntvfs/posix/xattr_tdb.c @@ -0,0 +1,232 @@ +/* + Unix SMB/CIFS implementation. + + POSIX NTVFS backend - xattr support using a tdb + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "vfs_posix.h" + +#define XATTR_LIST_ATTR ".xattr_list" + +/* + we need to maintain a list of attributes on each file, so that unlink + can automatically clean them up +*/ +static NTSTATUS xattr_tdb_add_list(struct pvfs_state *pvfs, const char *attr_name, + const char *fname, int fd) +{ + DATA_BLOB blob; + TALLOC_CTX *mem_ctx; + const char *s; + NTSTATUS status; + size_t len; + + if (strcmp(attr_name, XATTR_LIST_ATTR) == 0) { + return NT_STATUS_OK; + } + + mem_ctx = talloc(pvfs, 0); + + status = pull_xattr_blob_tdb(pvfs, mem_ctx, XATTR_LIST_ATTR, + fname, fd, 100, &blob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return NT_STATUS_OK; + } + + for (s=blob.data; s < (char *)(blob.data+blob.length); s += strlen(s) + 1) { + if (strcmp(attr_name, s) == 0) { + talloc_free(mem_ctx); + return NT_STATUS_OK; + } + } + + len = strlen(attr_name) + 1; + + blob.data = talloc_realloc_p(mem_ctx, blob.data, uint8_t, blob.length + len); + if (blob.data == NULL) { + talloc_free(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + memcpy(blob.data + blob.length, attr_name, len); + blob.length += len; + + status = push_xattr_blob_tdb(pvfs, XATTR_LIST_ATTR, fname, fd, &blob); + talloc_free(mem_ctx); + + return status; +} + +/* + form a key for using in the ea_db +*/ +static NTSTATUS get_ea_db_key(TALLOC_CTX *mem_ctx, + const char *attr_name, + const char *fname, int fd, + TDB_DATA *key) +{ + struct stat st; + size_t len = strlen(attr_name); + + if (fd == -1) { + if (stat(fname, &st) == -1) { + return NT_STATUS_NOT_FOUND; + } + } else { + if (fstat(fd, &st) == -1) { + return NT_STATUS_NOT_FOUND; + } + } + + key->dptr = talloc_array_p(mem_ctx, char, 16 + len); + if (key->dptr == NULL) { + return NT_STATUS_NO_MEMORY; + } + key->dsize = 16 + len; + + SBVAL(key->dptr, 0, st.st_dev); + SBVAL(key->dptr, 8, st.st_ino); + memcpy(key->dptr+16, attr_name, len); + + return NT_STATUS_OK; +} + +/* + pull a xattr as a blob, using the ea_db tdb +*/ +NTSTATUS pull_xattr_blob_tdb(struct pvfs_state *pvfs, + TALLOC_CTX *mem_ctx, + const char *attr_name, + const char *fname, + int fd, + size_t estimated_size, + DATA_BLOB *blob) +{ + TDB_DATA tkey, tdata; + NTSTATUS status; + + status = get_ea_db_key(mem_ctx, attr_name, fname, fd, &tkey); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + tdata = tdb_fetch(pvfs->ea_db->tdb, tkey); + if (tdata.dptr == NULL) { + return NT_STATUS_NOT_FOUND; + } + + *blob = data_blob_talloc(mem_ctx, tdata.dptr, tdata.dsize); + free(tdata.dptr); + if (blob->data == NULL) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +/* + push a xattr as a blob, using ea_db +*/ +NTSTATUS push_xattr_blob_tdb(struct pvfs_state *pvfs, + const char *attr_name, + const char *fname, + int fd, + const DATA_BLOB *blob) +{ + TDB_DATA tkey, tdata; + NTSTATUS status; + + status = get_ea_db_key(pvfs, attr_name, fname, fd, &tkey); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + tdata.dptr = blob->data; + tdata.dsize = blob->length; + + if (tdb_chainlock(pvfs->ea_db->tdb, tkey) != 0) { + talloc_free(tkey.dptr); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + status = xattr_tdb_add_list(pvfs, attr_name, fname, fd); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (tdb_store(pvfs->ea_db->tdb, tkey, tdata, TDB_REPLACE) == -1) { + status = NT_STATUS_INTERNAL_DB_CORRUPTION; + } + +done: + tdb_chainunlock(pvfs->ea_db->tdb, tkey); + talloc_free(tkey.dptr); + return status; +} + + +/* + delete a xattr +*/ +NTSTATUS delete_xattr_tdb(struct pvfs_state *pvfs, const char *attr_name, + const char *fname, int fd) +{ + TDB_DATA tkey; + NTSTATUS status; + + status = get_ea_db_key(NULL, attr_name, fname, fd, &tkey); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (tdb_delete(pvfs->ea_db->tdb, tkey) == -1) { + talloc_free(tkey.dptr); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + talloc_free(tkey.dptr); + return NT_STATUS_OK; +} + + + +/* + delete all xattrs for a file +*/ +NTSTATUS unlink_xattr_tdb(struct pvfs_state *pvfs, const char *fname) +{ + TALLOC_CTX *mem_ctx = talloc(pvfs, 0); + DATA_BLOB blob; + const char *s; + NTSTATUS status; + + status = pull_xattr_blob_tdb(pvfs, mem_ctx, XATTR_LIST_ATTR, + fname, -1, 100, &blob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return NT_STATUS_OK; + } + + for (s=blob.data; s < (char *)(blob.data+blob.length); s += strlen(s) + 1) { + delete_xattr_tdb(pvfs, s, fname, -1); + } + + return delete_xattr_tdb(pvfs, XATTR_LIST_ATTR, fname, -1); +} -- cgit