Implement vfs_ea_tdb
authorVolker Lendecke <vl@sernet.de>
Mon, 22 Oct 2007 16:14:57 +0000 (18:14 +0200)
committerVolker Lendecke <vl@samba.org>
Tue, 15 Jan 2008 10:01:18 +0000 (11:01 +0100)
This is an option for file systems that do not implement xattrs: in
lockdir/eas.tdb an array of xatts per inode is stored.

It can not solve the problem that xattrs might reappear if a posix-level
process deletes a file and happens to re-create it under the same name. On file
systems with birthtime we might have a chance to detect this, but not with
standard posix. A future version might put relief on file systems that do have
xattrs but where these are severely limited in size/speed/whatever: We can put
a simple marker as a native xattr, but the xattrs proper are stored in the tdb.

Volker
(This used to be commit 2036b4c5ad677b8a477b34b0f076febab0abff5e)

source3/Makefile.in
source3/configure.in
source3/librpc/gen_ndr/ndr_xattr.c [new file with mode: 0644]
source3/librpc/gen_ndr/ndr_xattr.h [new file with mode: 0644]
source3/librpc/gen_ndr/xattr.h [new file with mode: 0644]
source3/librpc/idl/xattr.idl [new file with mode: 0644]
source3/modules/vfs_ea_tdb.c [new file with mode: 0644]
source3/script/tests/selftest.sh
source3/script/tests/test_posix_s3.sh

index 3918d29a3ed54db20e8104dcadf1e5746d994185..1d480552eb8ce943112b7f03c3ea2edbb7af1d7b 100644 (file)
@@ -503,6 +503,7 @@ VFS_CAP_OBJ = modules/vfs_cap.o
 VFS_EXPAND_MSDFS_OBJ = modules/vfs_expand_msdfs.o
 VFS_SHADOW_COPY_OBJ = modules/vfs_shadow_copy.o
 VFS_AFSACL_OBJ = modules/vfs_afsacl.o
+VFS_EA_TDB_OBJ = modules/vfs_ea_tdb.o librpc/gen_ndr/ndr_xattr.o
 VFS_POSIXACL_OBJ = modules/vfs_posixacl.o
 VFS_AIXACL_OBJ = modules/vfs_aixacl.o modules/vfs_aixacl_util.o
 VFS_AIXACL2_OBJ = modules/vfs_aixacl2.o modules/vfs_aixacl_util.o modules/nfs4_acls.o
@@ -1060,7 +1061,7 @@ modules: SHOWFLAGS $(MODULES)
 ## Perl IDL Compiler
 IDL_FILES = unixinfo.idl lsa.idl dfs.idl echo.idl winreg.idl initshutdown.idl \
        srvsvc.idl svcctl.idl eventlog.idl wkssvc.idl netlogon.idl notify.idl \
-       epmapper.idl messaging.idl
+       epmapper.idl messaging.idl xattr.idl
 
 idl:
        @IDL_FILES="$(IDL_FILES)" CPP="$(CPP)" PERL="$(PERL)" \
@@ -1684,6 +1685,10 @@ bin/afsacl.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_AFSACL_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_AFSACL_OBJ)
 
+bin/ea_tdb.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_EA_TDB_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD_MODULE) $(VFS_EA_TDB_OBJ)
+
 bin/posixacl.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_POSIXACL_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_POSIXACL_OBJ)
index 98f5c450a87945adb0e2982c22d4cb9871af2635..4ddc64407c8502d7c7d91e7b34760eba6b1fc17f 100644 (file)
@@ -707,7 +707,7 @@ dnl These have to be built static:
 default_static_modules="pdb_smbpasswd pdb_tdbsam rpc_lsa rpc_samr rpc_winreg rpc_initshutdown rpc_lsa_ds rpc_wkssvc rpc_svcctl2 rpc_ntsvcs rpc_net rpc_netdfs rpc_srvsvc2 rpc_spoolss rpc_eventlog2 auth_sam auth_unix auth_winbind auth_server auth_domain auth_builtin vfs_default nss_info_template"
 
 dnl These are preferably build shared, and static if dlopen() is not available
-default_shared_modules="vfs_recycle vfs_audit vfs_extd_audit vfs_full_audit vfs_netatalk vfs_fake_perms vfs_default_quota vfs_readonly vfs_cap vfs_expand_msdfs vfs_shadow_copy charset_CP850 charset_CP437 auth_script vfs_readahead vfs_syncops"
+default_shared_modules="vfs_recycle vfs_audit vfs_extd_audit vfs_full_audit vfs_netatalk vfs_fake_perms vfs_default_quota vfs_readonly vfs_cap vfs_expand_msdfs vfs_shadow_copy charset_CP850 charset_CP437 auth_script vfs_readahead vfs_syncops vfs_ea_tdb"
 
 if test "x$developer" = xyes; then
    default_static_modules="$default_static_modules rpc_rpcecho"
@@ -6489,6 +6489,7 @@ SMB_MODULE(vfs_cap, \$(VFS_CAP_OBJ), "bin/cap.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_expand_msdfs, \$(VFS_EXPAND_MSDFS_OBJ), "bin/expand_msdfs.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_shadow_copy, \$(VFS_SHADOW_COPY_OBJ), "bin/shadow_copy.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_afsacl, \$(VFS_AFSACL_OBJ), "bin/afsacl.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_ea_tdb, \$(VFS_EA_TDB_OBJ), "bin/ea_tdb.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_posixacl, \$(VFS_POSIXACL_OBJ), "bin/posixacl.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_aixacl, \$(VFS_AIXACL_OBJ), "bin/aixacl.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_aixacl2, \$(VFS_AIXACL2_OBJ), "bin/aixacl2.$SHLIBEXT", VFS)
diff --git a/source3/librpc/gen_ndr/ndr_xattr.c b/source3/librpc/gen_ndr/ndr_xattr.c
new file mode 100644 (file)
index 0000000..29a31a1
--- /dev/null
@@ -0,0 +1,102 @@
+/* parser auto-generated by pidl */
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_xattr.h"
+
+_PUBLIC_ enum ndr_err_code ndr_push_tdb_xattr(struct ndr_push *ndr, int ndr_flags, const struct tdb_xattr *r)
+{
+       if (ndr_flags & NDR_SCALARS) {
+               NDR_CHECK(ndr_push_align(ndr, 4));
+               {
+                       uint32_t _flags_save_string = ndr->flags;
+                       ndr_set_flags(&ndr->flags, LIBNDR_FLAG_STR_UTF8|LIBNDR_FLAG_STR_NULLTERM);
+                       NDR_CHECK(ndr_push_string(ndr, NDR_SCALARS, r->name));
+                       ndr->flags = _flags_save_string;
+               }
+               NDR_CHECK(ndr_push_DATA_BLOB(ndr, NDR_SCALARS, r->value));
+       }
+       if (ndr_flags & NDR_BUFFERS) {
+       }
+       return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_pull_tdb_xattr(struct ndr_pull *ndr, int ndr_flags, struct tdb_xattr *r)
+{
+       if (ndr_flags & NDR_SCALARS) {
+               NDR_CHECK(ndr_pull_align(ndr, 4));
+               {
+                       uint32_t _flags_save_string = ndr->flags;
+                       ndr_set_flags(&ndr->flags, LIBNDR_FLAG_STR_UTF8|LIBNDR_FLAG_STR_NULLTERM);
+                       NDR_CHECK(ndr_pull_string(ndr, NDR_SCALARS, &r->name));
+                       ndr->flags = _flags_save_string;
+               }
+               NDR_CHECK(ndr_pull_DATA_BLOB(ndr, NDR_SCALARS, &r->value));
+       }
+       if (ndr_flags & NDR_BUFFERS) {
+       }
+       return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ void ndr_print_tdb_xattr(struct ndr_print *ndr, const char *name, const struct tdb_xattr *r)
+{
+       ndr_print_struct(ndr, name, "tdb_xattr");
+       ndr->depth++;
+       ndr_print_string(ndr, "name", r->name);
+       ndr_print_DATA_BLOB(ndr, "value", r->value);
+       ndr->depth--;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_push_tdb_xattrs(struct ndr_push *ndr, int ndr_flags, const struct tdb_xattrs *r)
+{
+       uint32_t cntr_xattrs_0;
+       if (ndr_flags & NDR_SCALARS) {
+               NDR_CHECK(ndr_push_align(ndr, 4));
+               NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->num_xattrs));
+               for (cntr_xattrs_0 = 0; cntr_xattrs_0 < r->num_xattrs; cntr_xattrs_0++) {
+                       NDR_CHECK(ndr_push_tdb_xattr(ndr, NDR_SCALARS, &r->xattrs[cntr_xattrs_0]));
+               }
+       }
+       if (ndr_flags & NDR_BUFFERS) {
+       }
+       return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_pull_tdb_xattrs(struct ndr_pull *ndr, int ndr_flags, struct tdb_xattrs *r)
+{
+       uint32_t cntr_xattrs_0;
+       TALLOC_CTX *_mem_save_xattrs_0;
+       if (ndr_flags & NDR_SCALARS) {
+               NDR_CHECK(ndr_pull_align(ndr, 4));
+               NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->num_xattrs));
+               NDR_PULL_ALLOC_N(ndr, r->xattrs, r->num_xattrs);
+               _mem_save_xattrs_0 = NDR_PULL_GET_MEM_CTX(ndr);
+               NDR_PULL_SET_MEM_CTX(ndr, r->xattrs, 0);
+               for (cntr_xattrs_0 = 0; cntr_xattrs_0 < r->num_xattrs; cntr_xattrs_0++) {
+                       NDR_CHECK(ndr_pull_tdb_xattr(ndr, NDR_SCALARS, &r->xattrs[cntr_xattrs_0]));
+               }
+               NDR_PULL_SET_MEM_CTX(ndr, _mem_save_xattrs_0, 0);
+       }
+       if (ndr_flags & NDR_BUFFERS) {
+       }
+       return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ void ndr_print_tdb_xattrs(struct ndr_print *ndr, const char *name, const struct tdb_xattrs *r)
+{
+       uint32_t cntr_xattrs_0;
+       ndr_print_struct(ndr, name, "tdb_xattrs");
+       ndr->depth++;
+       ndr_print_uint32(ndr, "num_xattrs", r->num_xattrs);
+       ndr->print(ndr, "%s: ARRAY(%d)", "xattrs", r->num_xattrs);
+       ndr->depth++;
+       for (cntr_xattrs_0=0;cntr_xattrs_0<r->num_xattrs;cntr_xattrs_0++) {
+               char *idx_0=NULL;
+               asprintf(&idx_0, "[%d]", cntr_xattrs_0);
+               if (idx_0) {
+                       ndr_print_tdb_xattr(ndr, "xattrs", &r->xattrs[cntr_xattrs_0]);
+                       free(idx_0);
+               }
+       }
+       ndr->depth--;
+       ndr->depth--;
+}
diff --git a/source3/librpc/gen_ndr/ndr_xattr.h b/source3/librpc/gen_ndr/ndr_xattr.h
new file mode 100644 (file)
index 0000000..a18477f
--- /dev/null
@@ -0,0 +1,16 @@
+/* header auto-generated by pidl */
+
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/xattr.h"
+
+#ifndef _HEADER_NDR_xattr
+#define _HEADER_NDR_xattr
+
+#define NDR_XATTR_CALL_COUNT (0)
+enum ndr_err_code ndr_push_tdb_xattr(struct ndr_push *ndr, int ndr_flags, const struct tdb_xattr *r);
+enum ndr_err_code ndr_pull_tdb_xattr(struct ndr_pull *ndr, int ndr_flags, struct tdb_xattr *r);
+void ndr_print_tdb_xattr(struct ndr_print *ndr, const char *name, const struct tdb_xattr *r);
+enum ndr_err_code ndr_push_tdb_xattrs(struct ndr_push *ndr, int ndr_flags, const struct tdb_xattrs *r);
+enum ndr_err_code ndr_pull_tdb_xattrs(struct ndr_pull *ndr, int ndr_flags, struct tdb_xattrs *r);
+void ndr_print_tdb_xattrs(struct ndr_print *ndr, const char *name, const struct tdb_xattrs *r);
+#endif /* _HEADER_NDR_xattr */
diff --git a/source3/librpc/gen_ndr/xattr.h b/source3/librpc/gen_ndr/xattr.h
new file mode 100644 (file)
index 0000000..ee30376
--- /dev/null
@@ -0,0 +1,18 @@
+/* header auto-generated by pidl */
+
+#include <stdint.h>
+
+#ifndef _HEADER_xattr
+#define _HEADER_xattr
+
+struct tdb_xattr {
+       const char * name;/* [flag(LIBNDR_FLAG_STR_UTF8|LIBNDR_FLAG_STR_NULLTERM)] */
+       DATA_BLOB value;
+}/* [public] */;
+
+struct tdb_xattrs {
+       uint32_t num_xattrs;
+       struct tdb_xattr *xattrs;
+}/* [public] */;
+
+#endif /* _HEADER_xattr */
diff --git a/source3/librpc/idl/xattr.idl b/source3/librpc/idl/xattr.idl
new file mode 100644 (file)
index 0000000..ec230a4
--- /dev/null
@@ -0,0 +1,23 @@
+#include "idl_types.h"
+
+/*
+   IDL structures for xattrs
+*/
+
+[
+  pointer_default(unique)
+]
+interface xattr
+{
+       /* xattrs for file systems that don't have any */
+
+       typedef [public] struct {
+               utf8string name;
+               DATA_BLOB value;
+       } tdb_xattr;
+
+       typedef [public] struct {
+               uint32 num_xattrs;
+               tdb_xattr xattrs[num_xattrs];
+       } tdb_xattrs;
+}
diff --git a/source3/modules/vfs_ea_tdb.c b/source3/modules/vfs_ea_tdb.c
new file mode 100644 (file)
index 0000000..8cf04d7
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ * Store posix-level xattrs in a tdb
+ *
+ * Copyright (C) Volker Lendecke, 2007
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "librpc/gen_ndr/xattr.h"
+#include "librpc/gen_ndr/ndr_xattr.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+/*
+ * unmarshall tdb_xattrs
+ */
+
+static NTSTATUS ea_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
+                                 const TDB_DATA *data,
+                                 struct tdb_xattrs **presult)
+{
+       DATA_BLOB blob;
+       enum ndr_err_code ndr_err;
+       struct tdb_xattrs *result;
+
+       if (!(result = TALLOC_ZERO_P(mem_ctx, struct tdb_xattrs))) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (data->dsize == 0) {
+               *presult = result;
+               return NT_STATUS_OK;
+       }
+
+       blob = data_blob_const(data->dptr, data->dsize);
+
+       ndr_err = ndr_pull_struct_blob(
+               &blob, result, result,
+               (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n",
+                         ndr_errstr(ndr_err)));
+               TALLOC_FREE(result);
+               return ndr_map_error2ntstatus(ndr_err);;
+       }
+
+       *presult = result;
+       return NT_STATUS_OK;
+}
+
+/*
+ * marshall tdb_xattrs
+ */
+
+static NTSTATUS ea_tdb_push_attrs(TALLOC_CTX *mem_ctx,
+                                 const struct tdb_xattrs *attribs,
+                                 TDB_DATA *data)
+{
+       DATA_BLOB blob;
+       enum ndr_err_code ndr_err;
+
+       ndr_err = ndr_push_struct_blob(
+               &blob, mem_ctx, attribs,
+               (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
+                         ndr_errstr(ndr_err)));
+               return ndr_map_error2ntstatus(ndr_err);;
+       }
+
+       *data = make_tdb_data(blob.data, blob.length);
+       return NT_STATUS_OK;
+}
+
+/*
+ * Load tdb_xattrs for a file from the tdb
+ */
+
+static NTSTATUS ea_tdb_load_attrs(TALLOC_CTX *mem_ctx,
+                                 struct db_context *db_ctx,
+                                 const struct file_id *id,
+                                 struct tdb_xattrs **presult)
+{
+       uint8 id_buf[16];
+       NTSTATUS status;
+       TDB_DATA data;
+
+       push_file_id_16((char *)id_buf, id);
+
+       if (db_ctx->fetch(db_ctx, mem_ctx,
+                         make_tdb_data(id_buf, sizeof(id_buf)),
+                         &data) == -1) {
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+       status = ea_tdb_pull_attrs(mem_ctx, &data, presult);
+       TALLOC_FREE(data.dptr);
+       return NT_STATUS_OK;
+}
+
+/*
+ * fetch_lock the tdb_ea record for a file
+ */
+
+static struct db_record *ea_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
+                                          struct db_context *db_ctx,
+                                          const struct file_id *id)
+{
+       uint8 id_buf[16];
+       push_file_id_16((char *)id_buf, id);
+       return db_ctx->fetch_locked(db_ctx, mem_ctx,
+                                   make_tdb_data(id_buf, sizeof(id_buf)));
+}
+
+/*
+ * Save tdb_xattrs to a previously fetch_locked record
+ */
+
+static NTSTATUS ea_tdb_save_attrs(struct db_record *rec,
+                                 const struct tdb_xattrs *attribs)
+{
+       TDB_DATA data;
+       NTSTATUS status;
+
+       status = ea_tdb_push_attrs(talloc_tos(), attribs, &data);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("ea_tdb_push_attrs failed: %s\n",
+                         nt_errstr(status)));
+               return status;
+       }
+
+       status = rec->store(rec, data, 0);
+
+       TALLOC_FREE(data.dptr);
+
+       return status;
+}
+
+/*
+ * Worker routine for getxattr and fgetxattr
+ */
+
+static ssize_t ea_tdb_getattr(struct db_context *db_ctx,
+                             const struct file_id *id,
+                             const char *name, void *value, size_t size)
+{
+       struct tdb_xattrs *attribs;
+       uint32_t i;
+       ssize_t result = -1;
+       NTSTATUS status;
+
+       status = ea_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
+                          nt_errstr(status)));
+               errno = EINVAL;
+               return -1;
+       }
+
+       for (i=0; i<attribs->num_xattrs; i++) {
+               if (strcmp(attribs->xattrs[i].name, name) == 0) {
+                       break;
+               }
+       }
+
+       if (i == attribs->num_xattrs) {
+               errno = ENOATTR;
+               goto fail;
+       }
+
+       if (attribs->xattrs[i].value.length > size) {
+               errno = ERANGE;
+               goto fail;
+       }
+
+       memcpy(value, attribs->xattrs[i].value.data,
+              attribs->xattrs[i].value.length);
+       result = attribs->xattrs[i].value.length;
+
+ fail:
+       TALLOC_FREE(attribs);
+       return result;
+}
+
+static ssize_t ea_tdb_getxattr(struct vfs_handle_struct *handle,
+                              const char *path, const char *name,
+                              void *value, size_t size)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       return ea_tdb_getattr(db, &id, name, value, size);
+}
+
+static ssize_t ea_tdb_fgetxattr(struct vfs_handle_struct *handle,
+                               struct files_struct *fsp,
+                               const char *name, void *value, size_t size)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       return ea_tdb_getattr(db, &id, name, value, size);
+}
+
+/*
+ * Worker routine for setxattr and fsetxattr
+ */
+
+static int ea_tdb_setattr(struct db_context *db_ctx,
+                         const struct file_id *id, const char *name,
+                         const void *value, size_t size, int flags)
+{
+       NTSTATUS status;
+       struct db_record *rec;
+       struct tdb_xattrs *attribs;
+       uint32_t i;
+
+       rec = ea_tdb_lock_attrs(talloc_tos(), db_ctx, id);
+
+       if (rec == NULL) {
+               DEBUG(0, ("ea_tdb_lock_attrs failed\n"));
+               errno = EINVAL;
+               return -1;
+       }
+
+       status = ea_tdb_pull_attrs(rec, &rec->value, &attribs);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
+                          nt_errstr(status)));
+               TALLOC_FREE(rec);
+               return -1;
+       }
+
+       for (i=0; i<attribs->num_xattrs; i++) {
+               if (strcmp(attribs->xattrs[i].name, name) == 0) {
+                       break;
+               }
+       }
+
+       if (i == attribs->num_xattrs) {
+               struct tdb_xattr *tmp;
+
+               tmp = TALLOC_REALLOC_ARRAY(
+                       attribs, attribs->xattrs, struct tdb_xattr,
+                       attribs->num_xattrs + 1);
+
+               if (tmp == NULL) {
+                       DEBUG(0, ("TALLOC_REALLOC_ARRAY failed\n"));
+                       TALLOC_FREE(rec);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               attribs->xattrs = tmp;
+               attribs->num_xattrs += 1;
+       }
+
+       attribs->xattrs[i].name = name;
+       attribs->xattrs[i].value.data = CONST_DISCARD(uint8 *, value);
+       attribs->xattrs[i].value.length = size;
+
+       status = ea_tdb_save_attrs(rec, attribs);
+
+       TALLOC_FREE(rec);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ea_tdb_setxattr(struct vfs_handle_struct *handle,
+                          const char *path, const char *name,
+                          const void *value, size_t size, int flags)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       return ea_tdb_setattr(db, &id, name, value, size, flags);
+}
+
+static int ea_tdb_fsetxattr(struct vfs_handle_struct *handle,
+                           struct files_struct *fsp,
+                           const char *name, const void *value,
+                           size_t size, int flags)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       return ea_tdb_setattr(db, &id, name, value, size, flags);
+}
+
+/*
+ * Worker routine for listxattr and flistxattr
+ */
+
+static ssize_t ea_tdb_listattr(struct db_context *db_ctx,
+                              const struct file_id *id, char *list,
+                              size_t size)
+{
+       NTSTATUS status;
+       struct tdb_xattrs *attribs;
+       uint32_t i;
+       size_t len = 0;
+
+       status = ea_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
+                          nt_errstr(status)));
+               errno = EINVAL;
+               return -1;
+       }
+
+       DEBUG(10, ("ea_tdb_listattr: Found %d xattrs\n", attribs->num_xattrs));
+
+       for (i=0; i<attribs->num_xattrs; i++) {
+               size_t tmp;
+
+               DEBUG(10, ("ea_tdb_listattr: xattrs[i].name: %s\n",
+                          attribs->xattrs[i].name));
+
+               tmp = strlen(attribs->xattrs[i].name);
+
+               /*
+                * Try to protect against overflow
+                */
+
+               if (len + (tmp+1) < len) {
+                       TALLOC_FREE(attribs);
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               /*
+                * Take care of the terminating NULL
+                */
+               len += (tmp + 1);
+       }
+
+       if (len > size) {
+               TALLOC_FREE(attribs);
+               errno = ERANGE;
+               return -1;
+       }
+
+       len = 0;
+
+       for (i=0; i<attribs->num_xattrs; i++) {
+               strlcpy(list+len, attribs->xattrs[i].name,
+                       size-len);
+               len += (strlen(attribs->xattrs[i].name) + 1);
+       }
+
+       TALLOC_FREE(attribs);
+       return len;
+}
+
+static ssize_t ea_tdb_listxattr(struct vfs_handle_struct *handle,
+                               const char *path, char *list, size_t size)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       return ea_tdb_listattr(db, &id, list, size);
+}
+
+static ssize_t ea_tdb_flistxattr(struct vfs_handle_struct *handle,
+                                struct files_struct *fsp, char *list,
+                                size_t size)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       return ea_tdb_listattr(db, &id, list, size);
+}
+
+/*
+ * Worker routine for removexattr and fremovexattr
+ */
+
+static int ea_tdb_removeattr(struct db_context *db_ctx,
+                            const struct file_id *id, const char *name)
+{
+       NTSTATUS status;
+       struct db_record *rec;
+       struct tdb_xattrs *attribs;
+       uint32_t i;
+
+       rec = ea_tdb_lock_attrs(talloc_tos(), db_ctx, id);
+
+       if (rec == NULL) {
+               DEBUG(0, ("ea_tdb_lock_attrs failed\n"));
+               errno = EINVAL;
+               return -1;
+       }
+
+       status = ea_tdb_pull_attrs(rec, &rec->value, &attribs);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
+                          nt_errstr(status)));
+               TALLOC_FREE(rec);
+               return -1;
+       }
+
+       for (i=0; i<attribs->num_xattrs; i++) {
+               if (strcmp(attribs->xattrs[i].name, name) == 0) {
+                       break;
+               }
+       }
+
+       if (i == attribs->num_xattrs) {
+               TALLOC_FREE(rec);
+               errno = ENOATTR;
+               return -1;
+       }
+
+       attribs->xattrs[i] =
+               attribs->xattrs[attribs->num_xattrs-1];
+       attribs->num_xattrs -= 1;
+
+       if (attribs->num_xattrs == 0) {
+               rec->delete_rec(rec);
+               TALLOC_FREE(rec);
+               return 0;
+       }
+
+       status = ea_tdb_save_attrs(rec, attribs);
+
+       TALLOC_FREE(rec);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ea_tdb_removexattr(struct vfs_handle_struct *handle,
+                             const char *path, const char *name)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       return ea_tdb_removeattr(db, &id, name);
+}
+
+static int ea_tdb_fremovexattr(struct vfs_handle_struct *handle,
+                              struct files_struct *fsp, const char *name)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       return ea_tdb_removeattr(db, &id, name);
+}
+
+/*
+ * Open the tdb file upon VFS_CONNECT
+ */
+
+static bool ea_tdb_init(int snum, struct db_context **p_db)
+{
+       struct db_context *db;
+       const char *dbname;
+
+       dbname = lp_parm_const_string(snum, "ea", "tdb", lock_path("eas.tdb"));
+
+       if (dbname == NULL) {
+               errno = ENOTSUP;
+               return false;
+       }
+
+       become_root();
+       db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+       unbecome_root();
+
+       if (db == NULL) {
+               errno = ENOTSUP;
+               return false;
+       }
+
+       *p_db = db;
+       return true;
+}
+
+/*
+ * On unlink we need to delete the tdb record
+ */
+static int ea_tdb_unlink(vfs_handle_struct *handle, const char *path)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+       struct db_record *rec;
+       int ret;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+               return -1;
+       }
+
+       ret = SMB_VFS_NEXT_UNLINK(handle, path);
+
+       if (ret == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       rec = ea_tdb_lock_attrs(talloc_tos(), db, &id);
+
+       /*
+        * If rec == NULL there's not much we can do about it
+        */
+
+       if (rec != NULL) {
+               rec->delete_rec(rec);
+               TALLOC_FREE(rec);
+       }
+
+       return 0;
+}
+
+/*
+ * On rmdir we need to delete the tdb record
+ */
+static int ea_tdb_rmdir(vfs_handle_struct *handle, const char *path)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct file_id id;
+       struct db_context *db;
+       struct db_record *rec;
+       int ret;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
+
+       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+               return -1;
+       }
+
+       ret = SMB_VFS_NEXT_RMDIR(handle, path);
+
+       if (ret == -1) {
+               return -1;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+
+       rec = ea_tdb_lock_attrs(talloc_tos(), db, &id);
+
+       /*
+        * If rec == NULL there's not much we can do about it
+        */
+
+       if (rec != NULL) {
+               rec->delete_rec(rec);
+               TALLOC_FREE(rec);
+       }
+
+       return 0;
+}
+
+/*
+ * Destructor for the VFS private data
+ */
+
+static void close_ea_db(void **data)
+{
+       struct db_context **p_db = (struct db_context **)data;
+       TALLOC_FREE(*p_db);
+}
+
+static int ea_tdb_connect(vfs_handle_struct *handle, const char *service,
+                         const char *user)
+{
+       fstring sname;
+       int res, snum;
+       struct db_context *db;
+
+       res = SMB_VFS_NEXT_CONNECT(handle, service, user);
+       if (res < 0) {
+               return res;
+       }
+
+       fstrcpy(sname, service);
+       snum = find_service(sname);
+       if (snum == -1) {
+               /*
+                * Should not happen, but we should not fail just *here*.
+                */
+               return 0;
+       }
+
+       if (!ea_tdb_init(snum, &db)) {
+               DEBUG(5, ("Could not init ea tdb\n"));
+               lp_do_parameter(snum, "ea support", "False");
+               return 0;
+       }
+
+       lp_do_parameter(snum, "ea support", "True");
+
+       SMB_VFS_HANDLE_SET_DATA(handle, db, close_ea_db,
+                               struct db_context, return -1);
+
+       return 0;
+}
+
+/* VFS operations structure */
+
+static const vfs_op_tuple ea_tdb_ops[] = {
+       {SMB_VFS_OP(ea_tdb_getxattr), SMB_VFS_OP_GETXATTR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_fgetxattr), SMB_VFS_OP_FGETXATTR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_setxattr), SMB_VFS_OP_SETXATTR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_fsetxattr), SMB_VFS_OP_FSETXATTR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_listxattr), SMB_VFS_OP_LISTXATTR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_flistxattr), SMB_VFS_OP_FLISTXATTR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_removexattr), SMB_VFS_OP_REMOVEXATTR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_fremovexattr), SMB_VFS_OP_FREMOVEXATTR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_unlink), SMB_VFS_OP_UNLINK,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_rmdir), SMB_VFS_OP_RMDIR,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(ea_tdb_connect), SMB_VFS_OP_CONNECT,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_ea_tdb_init(void);
+NTSTATUS vfs_ea_tdb_init(void)
+{
+       return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ea_tdb",
+                               ea_tdb_ops);
+}
index 86abb15ed1dcc3f9559e04d2cffc44827d379767..08bfdf601c83999db3fb7f383207e367e761c002 100755 (executable)
@@ -186,6 +186,7 @@ cat >$SERVERCONFFILE<<EOF
        map hidden = yes
        map system = yes
        create mask = 755
+       vfs objects = $BINDIR/ea_tdb.so
 [hideunread]
        copy = tmp
        hide unreadable = yes
index 9f672897acbab9d7cfd29f733ff9f788c1e93cee..0b4a52d4b50cbc7191bcb7ed6ad655435dfb6b4e 100755 (executable)
@@ -47,7 +47,7 @@ unix="UNIX-INFO2"
 tests="$base $raw $rpc $unix"
 
 skipped="BASE-CHARSET BASE-DELAYWRITE BASE-TCONDEV"
-skipped="$skipped RAW-ACLS RAW-COMPOSITE RAW-CONTEXT RAW-EAS"
+skipped="$skipped RAW-ACLS RAW-COMPOSITE RAW-CONTEXT"
 skipped="$skipped RAW-IOCTL"
 skipped="$skipped RAW-QFILEINFO RAW-QFSINFO RAW-SEARCH"
 skipped="$skipped RAW-SFILEINFO RAW-STREAMS"