r4242: added support for storing xattrs in a tdb. This allows all advanced NT
authorAndrew Tridgell <tridge@samba.org>
Fri, 17 Dec 2004 03:39:29 +0000 (03:39 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:07:28 +0000 (13:07 -0500)
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
source4/ntvfs/posix/pvfs_open.c
source4/ntvfs/posix/pvfs_unlink.c
source4/ntvfs/posix/pvfs_xattr.c
source4/ntvfs/posix/vfs_posix.c
source4/ntvfs/posix/vfs_posix.h
source4/ntvfs/posix/xattr_system.c [new file with mode: 0644]
source4/ntvfs/posix/xattr_tdb.c [new file with mode: 0644]

index 44a1625c5f2fc0f597b2c03a43ebefbc80b4c2b6..f2f72077736806390130eee0ebc11a0748760348 100644 (file)
@@ -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
 ################################################
index af0858f6399326332a522f28aba9b5111c939bed..e31d79b9e063b112fa57513f8332d49b491f5413 100644 (file)
@@ -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)));
index 434577a8623921dec0a4f04ef81a2ff7ad6f5e01..f29a70600fc324247544798b56d20fcef6dc008b 100644 (file)
@@ -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);
index 47549499e6af58e39db0b16bbf685878671a25d4..4487663e8d39f04a3a89b6c1355b19dff0e4df68 100644 (file)
 */
 
 #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;
 }
+
index ff3d3448f296464c20253e904393ccaad363284e..6f4de1e0388b4439ec199bb5ef0213d8531cf86c 100644 (file)
@@ -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;
        }
index 16c6f807e9621abab45c0670b96164f315727288..e80790f6fa179fecf0012ecb9ebd603a2b30a687 100644 (file)
@@ -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 (file)
index 0000000..c86ee0b
--- /dev/null
@@ -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 (file)
index 0000000..08a2ea4
--- /dev/null
@@ -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);
+}