Merge branch 'ctdb-merge' into v3-2-test
authorAlexander Bokovoy <ab@samba.org>
Wed, 16 Jan 2008 15:53:16 +0000 (18:53 +0300)
committerAlexander Bokovoy <ab@samba.org>
Wed, 16 Jan 2008 15:53:16 +0000 (18:53 +0300)
(This used to be commit 175662b5bbb5c8ecd81a60539f5846a938c26a3a)

33 files changed:
source3/Makefile.in
source3/configure.in
source3/include/ctdbd_conn.h
source3/include/dbwrap.h
source3/include/vfs.h
source3/include/vfs_macros.h
source3/lib/conn_tdb.c
source3/lib/ctdbd_conn.c
source3/lib/dbwrap.c
source3/lib/dbwrap_ctdb.c
source3/lib/dbwrap_file.c
source3/lib/dbwrap_tdb.c
source3/lib/dbwrap_util.c [new file with mode: 0644]
source3/lib/messages_ctdbd.c
source3/lib/util.c
source3/locking/brlock.c
source3/modules/gpfs.c
source3/modules/nfs4_acls.c
source3/modules/vfs_default.c
source3/modules/vfs_gpfs.c
source3/modules/vfs_gpfs.h [new file with mode: 0644]
source3/modules/vfs_prealloc.c
source3/modules/vfs_shadow_copy2.c [new file with mode: 0644]
source3/modules/vfs_tsmsm.c [new file with mode: 0644]
source3/smbd/connection.c
source3/smbd/dmapi.c
source3/smbd/dosmode.c
source3/smbd/oplock_linux.c
source3/smbd/posix_acls.c
source3/smbd/reply.c
source3/smbd/server.c
source3/smbd/trans2.c
source3/winbindd/idmap_tdb2.c [new file with mode: 0644]

index 46f733c0bcd641f32d9afc394f92f738e5666d72..265d14547cb904d77d0bf691d815d1949f72e071 100644 (file)
@@ -308,7 +308,7 @@ LIB_WITH_PROTO_OBJ = $(VERSION_OBJ) lib/charcnv.o lib/debug.o lib/fault.o \
          lib/util_str.o lib/clobber.o lib/util_sid.o lib/util_uuid.o \
          lib/util_unistr.o lib/util_file.o lib/data_blob.o \
          lib/util.o lib/util_sock.o lib/sock_exec.o lib/util_sec.o \
-         lib/substitute.o lib/fsusage.o \
+         lib/substitute.o lib/fsusage.o lib/dbwrap_util.o \
          lib/ms_fnmatch.o lib/select.o lib/errmap_unix.o \
          lib/tallocmsg.o lib/dmallocmsg.o libsmb/smb_signing.o \
          lib/md5.o lib/hmacmd5.o lib/arc4.o lib/iconv.o \
@@ -503,6 +503,7 @@ VFS_READONLY_OBJ = modules/vfs_readonly.o modules/getdate.o
 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_SHADOW_COPY2_OBJ = modules/vfs_shadow_copy2.o
 VFS_AFSACL_OBJ = modules/vfs_afsacl.o
 VFS_XATTR_TDB_OBJ = modules/vfs_xattr_tdb.o librpc/gen_ndr/ndr_xattr.o
 VFS_POSIXACL_OBJ = modules/vfs_posixacl.o
@@ -520,6 +521,7 @@ VFS_COMMIT_OBJ = modules/vfs_commit.o
 VFS_GPFS_OBJ = modules/vfs_gpfs.o modules/gpfs.o modules/nfs4_acls.o
 VFS_NOTIFY_FAM_OBJ = modules/vfs_notify_fam.o
 VFS_READAHEAD_OBJ = modules/vfs_readahead.o
+VFS_TSMSM_OBJ = modules/vfs_tsmsm.o
 VFS_FILEID_OBJ = modules/vfs_fileid.o
 VFS_SYNCOPS_OBJ = modules/vfs_syncops.o
 
@@ -1611,6 +1613,10 @@ bin/ad.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_ad.o
        @echo "Building plugin $@"
        @$(SHLD_MODULE) winbindd/idmap_ad.o
 
+bin/tdb2.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_tdb2.o
+       @echo "Building plugin $@"
+       @$(SHLD_MODULE) winbindd/idmap_tdb2.o
+
 bin/ldap.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_ldap.o
        @echo "Building plugin $@"
        @$(SHLD_MODULE) winbindd/idmap_ldap.o
@@ -1670,6 +1676,10 @@ bin/shadow_copy.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_SHADOW_COPY_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_SHADOW_COPY_OBJ)
 
+bin/shadow_copy2.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_SHADOW_COPY2_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD_MODULE) $(VFS_SHADOW_COPY2_OBJ)
+
 bin/syncops.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_SYNCOPS_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_SYNCOPS_OBJ)
@@ -1750,6 +1760,10 @@ bin/readahead.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_READAHEAD_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_READAHEAD_OBJ)
 
+bin/tsmsm.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_TSMSM_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD_MODULE) $(VFS_TSMSM_OBJ)
+
 bin/fileid.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_FILEID_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_FILEID_OBJ)
index cd04b598c024f44793386f83e1bb24b6560407cc..f65eb3c204bbd4a59016f894facb832e436b849f 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 vfs_xattr_tdb"
+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 vfs_shadow_copy2 charset_CP850 charset_CP437 auth_script vfs_readahead vfs_syncops vfs_xattr_tdb"
 
 if test "x$developer" = xyes; then
    default_static_modules="$default_static_modules rpc_rpcecho"
@@ -2751,6 +2751,12 @@ AC_SUBST(SMB_FAM_LIBS)
 
 SMB_CHECK_DMAPI([], AC_MSG_NOTICE(DMAPI support not present) )
 
+# Add TSM SM VFS module only if there are both GPFS and DMAPI support
+# Theoretically it should work with AIX JFS2 too but this needs testing
+if test x"$samba_cv_HAVE_GPFS" = x"yes" && test x"$samba_dmapi_libs" != x"" ; then
+    default_shared_modules="$default_shared_modules vfs_tsmsm"
+fi
+
 AC_CACHE_CHECK([for kernel share modes],samba_cv_HAVE_KERNEL_SHARE_MODES,[
 AC_TRY_RUN([
 #include <sys/types.h>
@@ -6452,6 +6458,7 @@ SMB_SUBSYSTEM(RPC,smbd/server.o)
 
 SMB_MODULE(idmap_ldap, winbindd/idmap_ldap.o, "bin/ldap.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_tdb, winbindd/idmap_tdb.o, "bin/tdb.$SHLIBEXT", IDMAP)
+SMB_MODULE(idmap_tdb2, winbindd/idmap_tdb2.o, "bin/tdb2.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_passdb, winbindd/idmap_passdb.o, "bin/passdb.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_nss, winbindd/idmap_nss.o, "bin/nss.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_rid, winbindd/idmap_rid.o, "bin/rid.$SHLIBEXT", IDMAP)
@@ -6488,6 +6495,7 @@ SMB_MODULE(vfs_readonly, \$(VFS_READONLY_OBJ), "bin/readonly.$SHLIBEXT", VFS)
 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_shadow_copy2, \$(VFS_SHADOW_COPY2_OBJ), "bin/shadow_copy2.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_afsacl, \$(VFS_AFSACL_OBJ), "bin/afsacl.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_xattr_tdb, \$(VFS_XATTR_TDB_OBJ), "bin/xattr_tdb.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_posixacl, \$(VFS_POSIXACL_OBJ), "bin/posixacl.$SHLIBEXT", VFS)
@@ -6503,6 +6511,7 @@ SMB_MODULE(vfs_prealloc, \$(VFS_PREALLOC_OBJ), "bin/prealloc.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_commit, \$(VFS_COMMIT_OBJ), "bin/commit.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_gpfs, \$(VFS_GPFS_OBJ), "bin/gpfs.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_readahead, \$(VFS_READAHEAD_OBJ), "bin/readahead.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_tsmsm, \$(VFS_TSMSM_OBJ), "bin/tsmsm.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_fileid, \$(VFS_FILEID_OBJ), "bin/fileid.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_syncops, \$(VFS_SYNCOPS_OBJ), "bin/syncops.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_zfsacl, \$(VFS_ZFSACL_OBJ), "bin/zfsacl.$SHLIBEXT", VFS)
index 425cc65a007b5d585e579e1341013131bbc4d655..6e1b2f737abe187ac4cd6c46bcae1db2d7b6b16f 100644 (file)
@@ -17,6 +17,9 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#ifndef _CTDBD_CONN_H
+#define _CTDBD_CONN_H
+
 struct ctdbd_connection;
 
 NTSTATUS ctdbd_init_connection(TALLOC_CTX *mem_ctx,
@@ -62,3 +65,6 @@ NTSTATUS ctdbd_register_ips(struct ctdbd_connection *conn,
 
 NTSTATUS ctdbd_register_reconfigure(struct ctdbd_connection *conn);
 
+NTSTATUS ctdbd_persistent_store(struct ctdbd_connection *conn, uint32_t db_id, TDB_DATA key, TDB_DATA data);
+
+#endif /* _CTDBD_CONN_H */
index 3bb378c8413516156e50ec608c957a099351c624..4eb174fef1c15e58e2f1690c2513567e1a640758 100644 (file)
@@ -43,6 +43,7 @@ struct db_context {
                             void *private_data);
        int (*get_seqnum)(struct db_context *db);
        void *private_data;
+       bool persistent;
 };
 
 struct db_context *db_open(TALLOC_CTX *mem_ctx,
index 0be3886227895401d44e166ea7b8f1bd119e4508..be3cd91520aa16c75c8324b7ee08a05a42bf7a8f 100644 (file)
@@ -70,6 +70,7 @@
  * timestamp resolition. JRA. */
 /* Changed to version21 to add chflags operation -- jpeach */
 /* Changed to version22 to add lchown operation -- jra */
+/* Additional change: add operations for offline files and remote storage volume abstraction -- ab*/
 /* Leave at 22 - not yet released. But change set_nt_acl to return an NTSTATUS. jra. */
 /* Leave at 22 - not yet released. Add file_id_create operation. --metze */
 /* Leave at 22 - not yet released. Change all BOOL parameters (int) to bool. jra. */
@@ -257,6 +258,12 @@ typedef enum _vfs_op_type {
        SMB_VFS_OP_AIO_ERROR,
        SMB_VFS_OP_AIO_FSYNC,
        SMB_VFS_OP_AIO_SUSPEND,
+        SMB_VFS_OP_AIO_FORCE,
+
+       /* offline operations */
+       SMB_VFS_OP_IS_OFFLINE,
+       SMB_VFS_OP_SET_OFFLINE,
+       SMB_VFS_OP_IS_REMOTESTORAGE,
 
        /* This should always be last enum value */
        
@@ -405,6 +412,12 @@ struct vfs_ops {
                int (*aio_error_fn)(struct vfs_handle_struct *handle, struct files_struct *fsp, SMB_STRUCT_AIOCB *aiocb);
                int (*aio_fsync)(struct vfs_handle_struct *handle, struct files_struct *fsp, int op, SMB_STRUCT_AIOCB *aiocb);
                int (*aio_suspend)(struct vfs_handle_struct *handle, struct files_struct *fsp, const SMB_STRUCT_AIOCB * const aiocb[], int n, const struct timespec *timeout);
+               bool (*aio_force)(struct vfs_handle_struct *handle, struct files_struct *fsp);
+               
+               /* offline operations */
+               int (*is_offline)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, bool *offline);
+               int (*set_offline)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+               bool (*is_remotestorage)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
 
        } ops;
 
@@ -526,6 +539,12 @@ struct vfs_ops {
                struct vfs_handle_struct *aio_error;
                struct vfs_handle_struct *aio_fsync;
                struct vfs_handle_struct *aio_suspend;
+               struct vfs_handle_struct *aio_force;
+               
+               /* offline operations */
+               struct vfs_handle_struct *is_offline;
+               struct vfs_handle_struct *set_offline;
+               struct vfs_handle_struct *is_remotestorage;
        } handles;
 };
 
index 9232e94a421f98008d8bd573df26ea88fb2a1486..dbba7813771c55f2aaebd6d9152cb062c16f7395 100644 (file)
 #define SMB_VFS_AIO_ERROR(fsp,aiocb) ((fsp)->conn->vfs.ops.aio_error_fn((fsp)->conn->vfs.handles.aio_error,(fsp),(aiocb)))
 #define SMB_VFS_AIO_FSYNC(fsp,op,aiocb) ((fsp)->conn->vfs.ops.aio_fsync((fsp)->conn->vfs.handles.aio_fsync,(fsp),(op),(aiocb)))
 #define SMB_VFS_AIO_SUSPEND(fsp,aiocb,n,ts) ((fsp)->conn->vfs.ops.aio_suspend((fsp)->conn->vfs.handles.aio_suspend,(fsp),(aiocb),(n),(ts)))
+#define SMB_VFS_AIO_FORCE(fsp) ((fsp)->conn->vfs.ops.aio_force((fsp)->conn->vfs.handles.aio_force,(fsp)))
+
+/* Offline operations */
+#define SMB_VFS_IS_OFFLINE(conn,path,sbuf,offline) ((conn)->vfs.ops.is_offline((conn)->vfs.handles.is_offline,(conn),(path),(sbuf),(offline)))
+#define SMB_VFS_SET_OFFLINE(conn,path) ((conn)->vfs.ops.set_offline((conn)->vfs.handles.set_offline,(conn),(path)))
+#define SMB_VFS_IS_REMOTESTORAGE(conn,path) ((conn)->vfs.ops.is_remotestorage((conn)->vfs.handles.is_remotestorage,(conn),(path)))
 
 /*******************************************************************
  Don't access conn->vfs_opaque.ops directly!!!
 #define SMB_VFS_OPAQUE_AIO_ERROR(fsp,aiocb) ((fsp)->conn->vfs_opaque.ops.aio_error_fn((fsp)->conn->vfs_opaque.handles.aio_error,(fsp),(aiocb)))
 #define SMB_VFS_OPAQUE_AIO_FSYNC(fsp,op,aiocb) ((fsp)->conn->vfs_opaque.ops.aio_fsync((fsp)->conn->vfs_opaque.handles.aio_fsync,(fsp),(op),(aiocb)))
 #define SMB_VFS_OPAQUE_AIO_SUSPEND(fsp,aiocb,n,ts) ((fsp)->conn->vfs_opaque.ops.aio_suspend((fsp)->conn->vfs_opaque.handles.aio_suspend,(fsp),(aiocb),(n),(ts)))
+#define SMB_VFS_OPAQUE_AIO_FORCE(fsp) ((fsp)->conn->vfs_opaque.ops.aio_force((fsp)->conn->vfs_opaque.handles.aio_force,(fsp)))
+
+/* Offline operations */
+#define SMB_VFS_OPAQUE_IS_OFFLINE(conn,path,sbuf,offline) ((conn)->vfs_opaque.ops.is_offline((conn)->vfs_opaque.handles.is_offline,(conn),(path),(sbuf),(offline)))
+#define SMB_VFS_OPAQUE_SET_OFFLINE(conn,path) ((conn)->vfs_opaque.ops.set_offline((conn)->vfs_opaque.handles.set_offline,(conn),(path)))
+#define SMB_VFS_OPAQUE_IS_REMOTESTORAGE(conn,path) ((conn)->vfs_opaque.ops.is_remotestorage((conn)->vfs_opaque.handles.is_remotestorage,(conn),(path)))
 
 /*******************************************************************
  Don't access handle->vfs_next.ops.* directly!!!
 #define SMB_VFS_NEXT_AIO_ERROR(handle,fsp,aiocb) ((handle)->vfs_next.ops.aio_error_fn((handle)->vfs_next.handles.aio_error,(fsp),(aiocb)))
 #define SMB_VFS_NEXT_AIO_FSYNC(handle,fsp,op,aiocb) ((handle)->vfs_next.ops.aio_fsync((handle)->vfs_next.handles.aio_fsync,(fsp),(op),(aiocb)))
 #define SMB_VFS_NEXT_AIO_SUSPEND(handle,fsp,aiocb,n,ts) ((handle)->vfs_next.ops.aio_suspend((handle)->vfs_next.handles.aio_suspend,(fsp),(aiocb),(n),(ts)))
+#define SMB_VFS_NEXT_AIO_FORCE(handle,fsp) ((handle)->vfs_next.ops.aio_force((handle)->vfs_next.handles.aio_force,(fsp)))
+
+/* Offline operations */
+#define SMB_VFS_NEXT_IS_OFFLINE(conn,path,sbuf,offline) ((conn)->vfs_next.ops.is_offline((conn)->vfs_next.handles.is_offline,(conn),(path),(sbuf),(offline)))
+#define SMB_VFS_NEXT_SET_OFFLINE(conn,path) ((conn)->vfs_next.ops.set_offline((conn)->vfs_next.handles.set_offline,(conn),(path)))
+#define SMB_VFS_NEXT_IS_REMOTESTORAGE(conn,path) ((conn)->vfs_next.ops.is_remotestorage((conn)->vfs_next.handles.is_remotestorage,(conn),(path)))
 
 #endif /* _VFS_MACROS_H */
index dd0a354a85169564b01b5290bc23c55a4d84d08b..22d85c873d3399c1791c5af15df9a216ea97eb16 100644 (file)
@@ -34,7 +34,7 @@ static struct db_context *connections_db_ctx(bool rw)
        }
        else {
                db_ctx = db_open(NULL, lock_path("connections.tdb"), 0,
-                                TDB_DEFAULT, O_RDONLY, 0);
+                                TDB_CLEAR_IF_FIRST|TDB_DEFAULT, O_RDONLY, 0);
        }
 
        return db_ctx;
index 899bbcfcce6147f5db00ccd4b1f1a6c09787c270..18e9879601735466cbf575583477ee6adaf774bb 100644 (file)
@@ -1203,6 +1203,42 @@ NTSTATUS ctdbd_register_reconfigure(struct ctdbd_connection *conn)
        return register_with_ctdbd(conn, CTDB_SRVID_RECONFIGURE);
 }
 
+/*
+  persstent store. Used when we update a record in a persistent database
+ */
+NTSTATUS ctdbd_persistent_store(struct ctdbd_connection *conn, uint32_t db_id, TDB_DATA key, TDB_DATA data)
+{
+       int cstatus=0;
+       struct ctdb_rec_data *rec;
+       TDB_DATA recdata;
+       size_t length;
+       NTSTATUS status;
+
+       length = offsetof(struct ctdb_rec_data, data) + key.dsize + data.dsize;
+
+       rec = (struct ctdb_rec_data *)talloc_size(conn, length);
+       NT_STATUS_HAVE_NO_MEMORY(rec);
+
+       rec->length = length;
+       rec->reqid  = db_id;
+       rec->keylen = key.dsize;
+       rec->datalen= data.dsize;
+       memcpy(&rec->data[0], key.dptr, key.dsize);
+       memcpy(&rec->data[key.dsize], data.dptr, data.dsize);
+
+       recdata.dptr  = (uint8_t *)rec;
+       recdata.dsize = length;
+
+       status = ctdbd_control(conn, CTDB_CURRENT_NODE, 
+                              CTDB_CONTROL_PERSISTENT_STORE, 
+                              0, recdata, NULL, NULL, &cstatus);
+       if (cstatus != 0) {
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+       return status;
+}
+
+
 #else
 
 NTSTATUS ctdbd_init_connection(TALLOC_CTX *mem_ctx,
index 4e16d1868235c8a594816799733808c9b21056eb..001424a6c0ffe598d5e7ca016d7a03fae79ba822 100644 (file)
@@ -20,7 +20,9 @@
 */
 
 #include "includes.h"
-
+#ifdef CLUSTER_SUPPORT
+#include "ctdb_private.h"
+#endif
 /*
  * Fall back using fetch_locked if no genuine fetch operation is provided
  */
@@ -46,10 +48,16 @@ struct db_context *db_open(TALLOC_CTX *mem_ctx,
                           int open_flags, mode_t mode)
 {
        struct db_context *result = NULL;
+#ifdef CLUSTER_SUPPORT
+       const char *sockname = lp_ctdbd_socket();
+#endif
 
 #ifdef CLUSTER_SUPPORT
+       if(!sockname || !*sockname) {
+               sockname = CTDB_PATH;
+       }
 
-       if (lp_clustering()) {
+       if (lp_clustering() && socket_exist(sockname)) {
                const char *partname;
                /* ctdb only wants the file part of the name */
                partname = strrchr(name, '/');
index 90d0b260c5409ea675250b0a1aa0736445bfef61..f497f871d2030e3483a0ce5be9b8945c2936808a 100644 (file)
 */
 
 #include "includes.h"
-
 #ifdef CLUSTER_SUPPORT
-
 #include "ctdb.h"
 #include "ctdb_private.h"
+#include "ctdbd_conn.h"
 
 struct db_ctdb_ctx {
        struct tdb_wrap *wtdb;
        uint32 db_id;
-       struct ctdbd_connection *conn;
 };
 
 struct db_ctdb_rec {
@@ -35,8 +33,6 @@ struct db_ctdb_rec {
        struct ctdb_ltdb_header header;
 };
 
-static struct ctdbd_connection *db_ctdbd_conn(struct db_ctdb_ctx *ctx);
-
 static NTSTATUS db_ctdb_store(struct db_record *rec, TDB_DATA data, int flag)
 {
        struct db_ctdb_rec *crec = talloc_get_type_abort(
@@ -60,6 +56,42 @@ static NTSTATUS db_ctdb_store(struct db_record *rec, TDB_DATA data, int flag)
        return (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION;
 }
 
+
+/* for persistent databases the store is a bit different. We have to
+   ask the ctdb daemon to push the record to all nodes after the
+   store */
+static NTSTATUS db_ctdb_store_persistent(struct db_record *rec, TDB_DATA data, int flag)
+{
+       struct db_ctdb_rec *crec = talloc_get_type_abort(
+               rec->private_data, struct db_ctdb_rec);
+       TDB_DATA cdata;
+       int ret;
+       NTSTATUS status;
+
+       cdata.dsize = sizeof(crec->header) + data.dsize;
+
+       if (!(cdata.dptr = SMB_MALLOC_ARRAY(uint8, cdata.dsize))) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       crec->header.rsn++;
+
+       memcpy(cdata.dptr, &crec->header, sizeof(crec->header));
+       memcpy(cdata.dptr + sizeof(crec->header), data.dptr, data.dsize);
+
+       ret = tdb_store(crec->ctdb_ctx->wtdb->tdb, rec->key, cdata, TDB_REPLACE);
+       status = (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION;
+       
+       /* now tell ctdbd to update this record on all other nodes */
+       if (NT_STATUS_IS_OK(status)) {
+               status = ctdbd_persistent_store(messaging_ctdbd_connection(), crec->ctdb_ctx->db_id, rec->key, cdata);
+       }
+
+       SAFE_FREE(cdata.dptr);
+
+       return status;
+}
+
 static NTSTATUS db_ctdb_delete(struct db_record *rec)
 {
        struct db_ctdb_rec *crec = talloc_get_type_abort(
@@ -110,6 +142,7 @@ static struct db_record *db_ctdb_fetch_locked(struct db_context *db,
        struct db_ctdb_rec *crec;
        NTSTATUS status;
        TDB_DATA ctdb_data;
+       int migrate_attempts = 0;
 
        if (!(result = talloc(mem_ctx, struct db_record))) {
                DEBUG(0, ("talloc failed\n"));
@@ -153,7 +186,11 @@ again:
                return NULL;
        }
 
-       result->store = db_ctdb_store;
+       if (db->persistent) {
+               result->store = db_ctdb_store_persistent;
+       } else {
+               result->store = db_ctdb_store;
+       }
        result->delete_rec = db_ctdb_delete;
        talloc_set_destructor(result, db_ctdb_record_destr);
 
@@ -175,12 +212,14 @@ again:
                tdb_chainunlock(ctx->wtdb->tdb, key);
                talloc_set_destructor(result, NULL);
 
+               migrate_attempts += 1;
+
                DEBUG(10, ("ctdb_data.dptr = %p, dmaster = %u (%u)\n",
                           ctdb_data.dptr, ctdb_data.dptr ?
                           ((struct ctdb_ltdb_header *)ctdb_data.dptr)->dmaster : -1,
                           get_my_vnn()));
 
-               status = ctdbd_migrate(db_ctdbd_conn(ctx), ctx->db_id, key);
+               status = ctdbd_migrate(messaging_ctdbd_connection(),ctx->db_id, key);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(5, ("ctdb_migrate failed: %s\n",
                                  nt_errstr(status)));
@@ -191,6 +230,11 @@ again:
                goto again;
        }
 
+       if (migrate_attempts > 10) {
+               DEBUG(0, ("db_ctdb_fetch_locked needed %d attempts\n",
+                         migrate_attempts));
+       }
+
        memcpy(&crec->header, ctdb_data.dptr, sizeof(crec->header));
 
        result->value.dsize = ctdb_data.dsize - sizeof(crec->header);
@@ -226,10 +270,12 @@ static int db_ctdb_fetch(struct db_context *db, TALLOC_CTX *mem_ctx,
        /*
         * See if we have a valid record and we are the dmaster. If so, we can
         * take the shortcut and just return it.
+        * we bypass the dmaster check for persistent databases
         */
        if ((ctdb_data.dptr != NULL) &&
            (ctdb_data.dsize >= sizeof(struct ctdb_ltdb_header)) &&
-           ((struct ctdb_ltdb_header *)ctdb_data.dptr)->dmaster == get_my_vnn()) {
+           (db->persistent ||
+            ((struct ctdb_ltdb_header *)ctdb_data.dptr)->dmaster == get_my_vnn())) {
                /* we are the dmaster - avoid the ctdb protocol op */
 
                data->dsize = ctdb_data.dsize - sizeof(struct ctdb_ltdb_header);
@@ -254,8 +300,7 @@ static int db_ctdb_fetch(struct db_context *db, TALLOC_CTX *mem_ctx,
        SAFE_FREE(ctdb_data.dptr);
 
        /* we weren't able to get it locally - ask ctdb to fetch it for us */
-       status = ctdbd_fetch(db_ctdbd_conn(ctx), ctx->db_id, key, mem_ctx,
-                            data);
+       status = ctdbd_fetch(messaging_ctdbd_connection(),ctx->db_id, key, mem_ctx, data);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("ctdbd_fetch failed: %s\n", nt_errstr(status)));
                return -1;
@@ -283,6 +328,22 @@ static void traverse_callback(TDB_DATA key, TDB_DATA data, void *private_data)
        talloc_free(tmp_ctx);
 }
 
+static int traverse_persistent_callback(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+                                       void *private_data)
+{
+       struct traverse_state *state = (struct traverse_state *)private_data;
+       struct db_record *rec;
+       TALLOC_CTX *tmp_ctx = talloc_new(state->db);
+       int ret = 0;
+       /* we have to give them a locked record to prevent races */
+       rec = db_ctdb_fetch_locked(state->db, tmp_ctx, kbuf);
+       if (rec && rec->value.dsize > 0) {
+               ret = state->fn(rec, state->private_data);
+       }
+       talloc_free(tmp_ctx);
+       return ret;
+}
+
 static int db_ctdb_traverse(struct db_context *db,
                            int (*fn)(struct db_record *rec,
                                      void *private_data),
@@ -296,6 +357,13 @@ static int db_ctdb_traverse(struct db_context *db,
        state.fn = fn;
        state.private_data = private_data;
 
+       if (db->persistent) {
+               /* for persistent databases we don't need to do a ctdb traverse,
+                  we can do a faster local traverse */
+               return tdb_traverse(ctx->wtdb->tdb, traverse_persistent_callback, &state);
+       }
+
+
        ctdbd_traverse(ctx->db_id, traverse_callback, &state);
        return 0;
 }
@@ -322,6 +390,27 @@ static void traverse_read_callback(TDB_DATA key, TDB_DATA data, void *private_da
        state->fn(&rec, state->private_data);
 }
 
+static int traverse_persistent_callback_read(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+                                       void *private_data)
+{
+       struct traverse_state *state = (struct traverse_state *)private_data;
+       struct db_record rec;
+       rec.key = kbuf;
+       rec.value = dbuf;
+       rec.store = db_ctdb_store_deny;
+       rec.delete_rec = db_ctdb_delete_deny;
+       rec.private_data = state->db;
+
+       if (rec.value.dsize <= sizeof(struct ctdb_ltdb_header)) {
+               /* a deleted record */
+               return 0;
+       }
+       rec.value.dsize -= sizeof(struct ctdb_ltdb_header);
+       rec.value.dptr += sizeof(struct ctdb_ltdb_header);
+
+       return state->fn(&rec, state->private_data);
+}
+
 static int db_ctdb_traverse_read(struct db_context *db,
                                 int (*fn)(struct db_record *rec,
                                           void *private_data),
@@ -335,6 +424,12 @@ static int db_ctdb_traverse_read(struct db_context *db,
        state.fn = fn;
        state.private_data = private_data;
 
+       if (db->persistent) {
+               /* for persistent databases we don't need to do a ctdb traverse,
+                  we can do a faster local traverse */
+               return tdb_traverse_read(ctx->wtdb->tdb, traverse_persistent_callback_read, &state);
+       }
+
        ctdbd_traverse(ctx->db_id, traverse_read_callback, &state);
        return 0;
 }
@@ -346,41 +441,6 @@ static int db_ctdb_get_seqnum(struct db_context *db)
        return tdb_get_seqnum(ctx->wtdb->tdb);
 }
 
-/*
- * Get the ctdbd connection for a database. If possible, re-use the messaging
- * ctdbd connection
- */
-static struct ctdbd_connection *db_ctdbd_conn(struct db_ctdb_ctx *ctx)
-{
-       struct ctdbd_connection *result;
-
-       result = messaging_ctdbd_connection();
-
-       if (result != NULL) {
-
-               if (ctx->conn == NULL) {
-                       /*
-                        * Someone has initialized messaging since we
-                        * initialized our own connection, we don't need it
-                        * anymore.
-                        */
-                       TALLOC_FREE(ctx->conn);
-               }
-
-               return result;
-       }
-
-       if (ctx->conn == NULL) {
-               NTSTATUS status;
-               status = ctdbd_init_connection(ctx, &ctx->conn);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return NULL;
-               }
-               set_my_vnn(ctdbd_vnn(ctx->conn));
-       }
-
-       return ctx->conn;
-}
 
 struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx,
                                const char *name,
@@ -390,7 +450,6 @@ struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx,
        struct db_context *result;
        struct db_ctdb_ctx *db_ctdb;
        char *db_path;
-       NTSTATUS status;
 
        if (!lp_clustering()) {
                DEBUG(10, ("Clustering disabled -- no ctdb\n"));
@@ -409,20 +468,15 @@ struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       db_ctdb->conn = NULL;
-
-       status = ctdbd_db_attach(db_ctdbd_conn(db_ctdb), name,
-                                &db_ctdb->db_id, tdb_flags);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("ctdbd_db_attach failed for %s: %s\n", name,
-                         nt_errstr(status)));
+       if (!NT_STATUS_IS_OK(ctdbd_db_attach(messaging_ctdbd_connection(),name, &db_ctdb->db_id, tdb_flags))) {
+               DEBUG(0, ("ctdbd_db_attach failed for %s\n", name));
                TALLOC_FREE(result);
                return NULL;
        }
 
-       db_path = ctdbd_dbpath(db_ctdbd_conn(db_ctdb), db_ctdb,
-                              db_ctdb->db_id);
+       db_path = ctdbd_dbpath(messaging_ctdbd_connection(), db_ctdb, db_ctdb->db_id);
+
+       result->persistent = ((tdb_flags & TDB_CLEAR_IF_FIRST) == 0);
 
        /* only pass through specific flags */
        tdb_flags &= TDB_SEQNUM;
@@ -447,16 +501,4 @@ struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx,
 
        return result;
 }
-
-#else
-
-struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx,
-                               const char *name,
-                               int hash_size, int tdb_flags,
-                               int open_flags, mode_t mode)
-{
-       DEBUG(0, ("no clustering compiled in\n"));
-       return NULL;
-}
-
 #endif
index 5b41f5941b05f95ad60c0dae7df54e175814c4cf..e3779de1e47effda998f93ba7b996cc0243a5f44 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-/*
- * Be aware that this is just sample code that has not seen too much testing
- */
-
 #include "includes.h"
 
 struct db_file_ctx {
@@ -367,6 +363,7 @@ struct db_context *db_open_file(TALLOC_CTX *mem_ctx,
        result->fetch_locked = db_file_fetch_locked;
        result->traverse = db_file_traverse;
        result->traverse_read = db_file_traverse;
+       result->persistent = ((tdb_flags & TDB_CLEAR_IF_FIRST) == 0);
 
        ctx->locked_record = NULL;
        if (!(ctx->dirname = talloc_strdup(ctx, name))) {
index 710e45de6be924fbb7bbadc63f854a92e15494be..e87ceb428ffba5954ba58b75c487159832fa2527 100644 (file)
@@ -31,6 +31,11 @@ static int db_tdb_record_destr(struct db_record* data)
        struct db_tdb_ctx *ctx =
                talloc_get_type_abort(data->private_data, struct db_tdb_ctx);
 
+       /* This hex_encode() call allocates memory on data context. By way how current 
+          __talloc_free() code works, it is OK to allocate in the destructor as 
+          the children of data will be freed after call to the destructor and this 
+          new 'child' will be caught and freed correctly.
+        */
        DEBUG(10, (DEBUGLEVEL > 10
                   ? "Unlocking key %s\n" : "Unlocking key %.20s\n",
                   hex_encode(data, (unsigned char *)data->key.dptr,
@@ -88,8 +93,9 @@ static struct db_record *db_tdb_fetch_locked(struct db_context *db,
        struct tdb_fetch_locked_state state;
        int res;
 
-       if (DEBUGLEVEL >= 10) {
-               char *keystr = hex_encode(NULL, key.dptr, key.dsize);
+       /* Do not accidently allocate/deallocate w/o need when debug level is lower than needed */
+       if(DEBUGLEVEL >= 10) {
+               char *keystr = hex_encode(NULL, (unsigned char*)key.dptr, key.dsize);
                DEBUG(10, (DEBUGLEVEL > 10
                           ? "Locking key %s\n" : "Locking key %.20s\n",
                           keystr));
@@ -191,15 +197,9 @@ static NTSTATUS db_tdb_delete(struct db_record *rec)
 {
        struct db_tdb_ctx *ctx = talloc_get_type_abort(rec->private_data,
                                                       struct db_tdb_ctx);
-       int res;
-       
-       res = tdb_delete(ctx->wtdb->tdb, rec->key);
 
-       if (res == 0) {
-               return NT_STATUS_OK;
-       }
-
-       return map_nt_error_from_tdb(tdb_error(ctx->wtdb->tdb));
+       return (tdb_delete(ctx->wtdb->tdb, rec->key) == 0) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
 }
 
 struct db_tdb_traverse_ctx {
@@ -318,6 +318,7 @@ struct db_context *db_open_tdb(TALLOC_CTX *mem_ctx,
        result->traverse = db_tdb_traverse;
        result->traverse_read = db_tdb_traverse_read;
        result->get_seqnum = db_tdb_get_seqnum;
+       result->persistent = ((tdb_flags & TDB_CLEAR_IF_FIRST) == 0);
        return result;
 
  fail:
diff --git a/source3/lib/dbwrap_util.c b/source3/lib/dbwrap_util.c
new file mode 100644 (file)
index 0000000..002d572
--- /dev/null
@@ -0,0 +1,90 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Utility functions for the dbwrap API
+   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 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"
+
+int32_t dbwrap_fetch_int32(struct db_context *db, const char *keystr)
+{
+       TDB_DATA dbuf;
+       int32 ret;
+
+       if (db->fetch(db, NULL, string_term_tdb_data(keystr), &dbuf) != 0) {
+               return -1;
+       }
+
+       if ((dbuf.dptr == NULL) || (dbuf.dsize != sizeof(int32_t))) {
+               TALLOC_FREE(dbuf.dptr);
+               return -1;
+       }
+
+       ret = IVAL(dbuf.dptr, 0);
+       TALLOC_FREE(dbuf.dptr);
+       return ret;
+}
+
+int dbwrap_store_int32(struct db_context *db, const char *keystr, int32_t v)
+{
+       struct db_record *rec;
+       int32 v_store;
+       NTSTATUS status;
+
+       rec = db->fetch_locked(db, NULL, string_term_tdb_data(keystr));
+       if (rec == NULL) {
+               return -1;
+       }
+
+       SIVAL(&v_store, 0, v);
+
+       status = rec->store(rec, make_tdb_data((const uint8 *)&v_store,
+                                              sizeof(v_store)),
+                           TDB_REPLACE);
+       TALLOC_FREE(rec);
+       return NT_STATUS_IS_OK(status) ? 0 : -1;
+}
+
+uint32_t dbwrap_change_uint32_atomic(struct db_context *db, const char *keystr,
+                                    uint32_t *oldval, uint32_t change_val)
+{
+       struct db_record *rec;
+       uint32 val = -1;
+       TDB_DATA data;
+
+       if (!(rec = db->fetch_locked(db, NULL,
+                                    string_term_tdb_data(keystr)))) {
+               return -1;
+       }
+
+       if ((rec->value.dptr != NULL)
+           && (rec->value.dsize == sizeof(val))) {
+               val = IVAL(rec->value.dptr, 0);
+       }
+
+       val += change_val;
+
+       data.dsize = sizeof(val);
+       data.dptr = (uint8 *)&val;
+
+       rec->store(rec, data, TDB_REPLACE);
+
+       TALLOC_FREE(rec);
+
+       return 0;
+}
+
index 6e9b934a7576a9d922bfa1346492223a76b803e1..f1a02e6af9fdb3d6cc8ff86c2c888b17b531b7b5 100644 (file)
 #ifdef CLUSTER_SUPPORT
 
 #include "librpc/gen_ndr/messaging.h"
+#include "ctdb.h"
+#include "ctdb_private.h"
+#include "ctdbd_conn.h"
+
 
 struct messaging_ctdbd_context {
        struct ctdbd_connection *conn;
index 0653fc9d3f30e35c1646244dfce6f27e884eda7a..bc3eaa8d5e95b8cd600bc7ea62e15534a4dc3a5b 100644 (file)
@@ -505,6 +505,19 @@ bool file_exist(const char *fname,SMB_STRUCT_STAT *sbuf)
        return((S_ISREG(sbuf->st_mode)) || (S_ISFIFO(sbuf->st_mode)));
 }
 
+/*******************************************************************
+ Check if a unix domain socket exists - call vfs_file_exist for samba files.
+********************************************************************/
+
+bool socket_exist(const char *fname)
+{
+       SMB_STRUCT_STAT st;
+       if (sys_stat(fname,&st) != 0) 
+               return(False);
+
+       return S_ISSOCK(st.st_mode);
+}
+
 /*******************************************************************
  Check a files mod time.
 ********************************************************************/
index eb42d081fed5d42791aa503b35f6f435120d1681..4191871bb1a200bedf84b35075d663cb1eab1f01 100644 (file)
@@ -41,11 +41,11 @@ static struct db_context *brlock_db;
 
 static void print_lock_struct(unsigned int i, struct lock_struct *pls)
 {
-       DEBUG(10,("[%u]: smbpid = %u, tid = %u, pid = %s, ",
+       DEBUG(10,("[%u]: smbpid = %u, tid = %u, pid = %u, ",
                        i,
                        (unsigned int)pls->context.smbpid,
                        (unsigned int)pls->context.tid,
-                       procid_str_static(&pls->context.pid) ));
+                       (unsigned int)procid_to_pid(&pls->context.pid) ));
        
        DEBUG(10,("start = %.0f, size = %.0f, fnum = %d, %s %s\n",
                (double)pls->start,
@@ -263,10 +263,9 @@ void brl_init(bool read_only)
        if (brlock_db) {
                return;
        }
-       brlock_db = db_open(NULL, lock_path("brlock.tdb"), 0,
-                           TDB_DEFAULT
-                           |TDB_VOLATILE
-                           |(read_only?0x0:TDB_CLEAR_IF_FIRST),
+       brlock_db = db_open(NULL, lock_path("brlock.tdb"),
+                           lp_open_files_db_hash_size(),
+                           TDB_DEFAULT | TDB_CLEAR_IF_FIRST,
                            read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644 );
        if (!brlock_db) {
                DEBUG(0,("Failed to open byte range locking database %s\n",
@@ -1495,14 +1494,16 @@ static int traverse_fn(struct db_record *rec, void *state)
                }
        }
 
-       for ( i=0; i<num_locks; i++) {
-               cb->fn(*key,
-                      locks[i].context.pid,
-                      locks[i].lock_type,
-                      locks[i].lock_flav,
-                      locks[i].start,
-                      locks[i].size,
-                      cb->private_data);
+       if (cb->fn) {
+               for ( i=0; i<num_locks; i++) {
+                       cb->fn(*key,
+                               locks[i].context.pid,
+                               locks[i].lock_type,
+                               locks[i].lock_flav,
+                               locks[i].start,
+                               locks[i].size,
+                               cb->private_data);
+               }
        }
 
        SAFE_FREE(locks);
index 300e90fa697c7daa0ccea27a12135c307cd48d3b..590dbac26fe41c42108e4ef6717d7cd45519b116 100644 (file)
 #ifdef HAVE_GPFS
 
 #include "gpfs_gpl.h"
+#include "vfs_gpfs.h"
 
 static void *libgpfs_handle = NULL;
 static bool gpfs_share_modes;
+static bool gpfs_leases;
 
 static int (*gpfs_set_share_fn)(int fd, unsigned int allow, unsigned int deny);
 static int (*gpfs_set_lease_fn)(int fd, unsigned int leaseType);
@@ -42,7 +44,7 @@ bool set_gpfs_sharemode(files_struct *fsp, uint32 access_mask,
        if (!gpfs_share_modes) {
                return True;
        }
-
+       
        if (gpfs_set_share_fn == NULL) {
                return False;
        }
@@ -88,7 +90,7 @@ int set_gpfs_lease(int fd, int leasetype)
 {
        int gpfs_type = GPFS_LEASE_NONE;
 
-       if (!gpfs_share_modes) {
+       if (!gpfs_leases) {
                return True;
        }
 
@@ -103,6 +105,13 @@ int set_gpfs_lease(int fd, int leasetype)
        if (leasetype == F_WRLCK) {
                gpfs_type = GPFS_LEASE_WRITE;
        }
+       
+       /* we unconditionally set CAP_LEASE, rather than looking for
+          -1/EACCES as there is a bug in some versions of
+          libgpfs_gpl.so which results in a leaked fd on /dev/ss0
+          each time we try this with the wrong capabilities set
+       */
+       linux_set_lease_capability();
        return gpfs_set_lease_fn(fd, gpfs_type);
 }
 
@@ -172,11 +181,8 @@ void init_gpfs(void)
                goto failed;
        }
 
-       if (lp_parm_bool(-1, "gpfs", "sharemodes", True)) {
-               gpfs_share_modes = True;
-       } else {
-               gpfs_share_modes = False;
-       }
+       gpfs_share_modes = lp_parm_bool(-1, "gpfs", "sharemodes", True);
+       gpfs_leases      = lp_parm_bool(-1, "gpfs", "leases", True);
 
        return;
 
index 52d3983fff7ee43239b5742b16fe98dfe67ede78..0c3d010dcde64553487648b50ef23dc6e46d619f 100644 (file)
@@ -20,6 +20,9 @@
 #include "includes.h"
 #include "nfs4_acls.h"
 
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_ACLS
+
 #define SMBACL4_PARAM_TYPE_NAME "nfs4"
 
 #define SMB_ACE4_INT_MAGIC 0x76F8A967
@@ -352,6 +355,7 @@ typedef struct _smbacl4_vfs_params {
        enum smbacl4_mode_enum mode;
        bool do_chown;
        enum smbacl4_acedup_enum acedup;
+       struct db_context *sid_mapping_table;
 } smbacl4_vfs_params;
 
 /*
@@ -451,8 +455,65 @@ static SMB_ACE4PROP_T *smbacl4_find_equal_special(
        return NULL;
 }
 
-static int smbacl4_fill_ace4(
+static bool nfs4_map_sid(smbacl4_vfs_params *params, const DOM_SID *src,
+                        DOM_SID *dst)
+{
+       static struct db_context *mapping_db = NULL;
+       TDB_DATA data;
+       
+       if (mapping_db == NULL) {
+               const char *dbname = lp_parm_const_string(
+                       -1, SMBACL4_PARAM_TYPE_NAME, "sidmap", NULL);
+               
+               if (dbname == NULL) {
+                       DEBUG(10, ("%s:sidmap not defined\n",
+                                  SMBACL4_PARAM_TYPE_NAME));
+                       return False;
+               }
+               
+               become_root();
+               mapping_db = db_open(NULL, dbname, 0, TDB_DEFAULT,
+                                    O_RDONLY, 0600);
+               unbecome_root();
+               
+               if (mapping_db == NULL) {
+                       DEBUG(1, ("could not open sidmap: %s\n",
+                                 strerror(errno)));
+                       return False;
+               }
+       }
+       
+       if (mapping_db->fetch(mapping_db, NULL,
+                             string_term_tdb_data(sid_string_tos(src)),
+                             &data) == -1) {
+               DEBUG(10, ("could not find mapping for SID %s\n",
+                          sid_string_dbg(src)));
+               return False;
+       }
+       
+       if ((data.dptr == NULL) || (data.dsize <= 0)
+           || (data.dptr[data.dsize-1] != '\0')) {
+               DEBUG(5, ("invalid mapping for SID %s\n",
+                         sid_string_dbg(src)));
+               TALLOC_FREE(data.dptr);
+               return False;
+       }
+       
+       if (!string_to_sid(dst, (char *)data.dptr)) {
+               DEBUG(1, ("invalid mapping %s for SID %s\n",
+                         (char *)data.dptr, sid_string_dbg(src)));
+               TALLOC_FREE(data.dptr);
+               return False;
+       }
+
+       TALLOC_FREE(data.dptr);
+       
+       return True;
+}
+
+static bool smbacl4_fill_ace4(
        TALLOC_CTX *mem_ctx,
+       const char *filename,
        smbacl4_vfs_params *params,
        uid_t ownerUID,
        gid_t ownerGID,
@@ -460,11 +521,6 @@ static int smbacl4_fill_ace4(
        SMB_ACE4PROP_T *ace_v4 /* output */
 )
 {
-       const char *dom, *name;
-       enum lsa_SidType type;
-       uid_t uid;
-       gid_t gid;
-
        DEBUG(10, ("got ace for %s\n", sid_string_dbg(&ace_nt->trustee)));
 
        memset(ace_v4, 0, sizeof(SMB_ACE4PROP_T));
@@ -485,18 +541,46 @@ static int smbacl4_fill_ace4(
                ace_v4->who.special_id = SMB_ACE4_WHO_EVERYONE;
                ace_v4->flags |= SMB_ACE4_ID_SPECIAL;
        } else {
-               if (!lookup_sid(mem_ctx, &ace_nt->trustee, &dom, &name, &type)) {
-                       DEBUG(8, ("Could not find %s' type\n",
-                                 sid_string_dbg(&ace_nt->trustee)));
-                       errno = EINVAL;
-                       return -1;
+               const char *dom, *name;
+               enum lsa_SidType type;
+               uid_t uid;
+               gid_t gid;
+               DOM_SID sid;
+               
+               sid_copy(&sid, &ace_nt->trustee);
+               
+               if (!lookup_sid(mem_ctx, &sid, &dom, &name, &type)) {
+                       
+                       DOM_SID mapped;
+                       
+                       if (!nfs4_map_sid(params, &sid, &mapped)) {
+                               DEBUG(1, ("nfs4_acls.c: file [%s]: SID %s "
+                                         "unknown\n", filename, sid_string_dbg(&sid)));
+                               errno = EINVAL;
+                               return False;
+                       }
+                       
+                       DEBUG(2, ("nfs4_acls.c: file [%s]: mapped SID %s "
+                                 "to %s\n", filename, sid_string_dbg(&sid), sid_string_dbg(&mapped)));
+                       
+                       if (!lookup_sid(mem_ctx, &mapped, &dom,
+                                       &name, &type)) {
+                               DEBUG(1, ("nfs4_acls.c: file [%s]: SID %s "
+                                         "mapped from %s is unknown\n",
+                                         filename, sid_string_dbg(&mapped), sid_string_dbg(&sid)));
+                               errno = EINVAL;
+                               return False;
+                       }
+                       
+                       sid_copy(&sid, &mapped);
                }
-
+               
                if (type == SID_NAME_USER) {
-                       if (!sid_to_uid(&ace_nt->trustee, &uid)) {
-                               DEBUG(2, ("Could not convert %s to uid\n",
-                                         sid_string_dbg(&ace_nt->trustee)));
-                               return -1;
+                       if (!sid_to_uid(&sid, &uid)) {
+                               DEBUG(1, ("nfs4_acls.c: file [%s]: could not "
+                                         "convert %s to uid\n", filename,
+                                         sid_string_dbg(&sid)));
+                               return False;
                        }
 
                        if (params->mode==e_special && uid==ownerUID) {
@@ -506,11 +590,13 @@ static int smbacl4_fill_ace4(
                                ace_v4->who.uid = uid;
                        }
                } else { /* else group? - TODO check it... */
-                       if (!sid_to_gid(&ace_nt->trustee, &gid)) {
-                               DEBUG(2, ("Could not convert %s to gid\n",
-                                         sid_string_dbg(&ace_nt->trustee)));
-                               return -1;
+                       if (!sid_to_gid(&sid, &gid)) {
+                               DEBUG(1, ("nfs4_acls.c: file [%s]: could not "
+                                         "convert %s to gid\n", filename,
+                                         sid_string_dbg(&sid)));
+                               return False;
                        }
+                               
                        ace_v4->aceFlags |= SMB_ACE4_IDENTIFIER_GROUP;
 
                        if (params->mode==e_special && gid==ownerGID) {
@@ -522,7 +608,7 @@ static int smbacl4_fill_ace4(
                }
        }
 
-       return 0; /* OK */
+       return True; /* OK */
 }
 
 static int smbacl4_MergeIgnoreReject(
@@ -560,6 +646,7 @@ static int smbacl4_MergeIgnoreReject(
 }
 
 static SMB4ACL_T *smbacl4_win2nfs4(
+       const char *filename,
        SEC_ACL *dacl,
        smbacl4_vfs_params *pparams,
        uid_t ownerUID,
@@ -580,9 +667,14 @@ static SMB4ACL_T *smbacl4_win2nfs4(
                SMB_ACE4PROP_T  ace_v4;
                bool    addNewACE = True;
 
-               if (smbacl4_fill_ace4(mem_ctx, pparams, ownerUID, ownerGID,
-                       dacl->aces + i, &ace_v4))
-                       return NULL;
+               if (!smbacl4_fill_ace4(mem_ctx, filename, pparams,
+                                      ownerUID, ownerGID,
+                                      dacl->aces + i, &ace_v4)) {
+                       DEBUG(3, ("Could not fill ace for file %s, SID %s\n",
+                                 filename,
+                                 sid_string_dbg(&((dacl->aces+i)->trustee))));
+                       continue;
+               }
 
                if (pparams->acedup!=e_dontcare) {
                        if (smbacl4_MergeIgnoreReject(pparams->acedup, acl,
@@ -607,6 +699,7 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp,
        bool    result;
 
        SMB_STRUCT_STAT sbuf;
+       bool need_chown = False;
        uid_t newUID = (uid_t)-1;
        gid_t newGID = (gid_t)-1;
 
@@ -635,25 +728,33 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp,
                        return status;
                }
                if (((newUID != (uid_t)-1) && (sbuf.st_uid != newUID)) ||
-                               ((newGID != (gid_t)-1) && (sbuf.st_gid != newGID))) {
-                       if(try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) {
-                               DEBUG(3,("chown %s, %u, %u failed. Error = %s.\n",
-                                       fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID, strerror(errno) ));
-                               if (errno == EPERM) {
-                                       return NT_STATUS_INVALID_OWNER;
+                   ((newGID != (gid_t)-1) && (sbuf.st_gid != newGID))) {
+                       need_chown = True;
+               }
+               if (need_chown) {
+                       if ((newUID == (uid_t)-1 || newUID == current_user.ut.uid)) {
+                               if(try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) {
+                                       DEBUG(3,("chown %s, %u, %u failed. Error = %s.\n",
+                                                fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID, 
+                                                strerror(errno)));
+                                       return map_nt_error_from_unix(errno);
                                }
-                               return map_nt_error_from_unix(errno);
+
+                               DEBUG(10,("chown %s, %u, %u succeeded.\n",
+                                         fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID));
+                               if (smbacl4_GetFileOwner(fsp->conn, fsp->fsp_name, &sbuf))
+                                       return map_nt_error_from_unix(errno);
+                               need_chown = False;
+                       } else { /* chown is needed, but _after_ changing acl */
+                               sbuf.st_uid = newUID; /* OWNER@ in case of e_special */
+                               sbuf.st_gid = newGID; /* GROUP@ in case of e_special */
                        }
-                       DEBUG(10,("chown %s, %u, %u succeeded.\n",
-                               fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID));
-                       if (smbacl4_fGetFileOwner(fsp, &sbuf))
-                               return map_nt_error_from_unix(errno);
                }
        }
 
        if ((security_info_sent & DACL_SECURITY_INFORMATION)!=0 && psd->dacl!=NULL)
        {
-               acl = smbacl4_win2nfs4(psd->dacl, &params, sbuf.st_uid, sbuf.st_gid);
+               acl = smbacl4_win2nfs4(fsp->fsp_name, psd->dacl, &params, sbuf.st_uid, sbuf.st_gid);
                if (!acl)
                        return map_nt_error_from_unix(errno);
 
@@ -668,6 +769,20 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp,
        } else
                DEBUG(10, ("no dacl found; security_info_sent = 0x%x\n", security_info_sent));
 
+       /* Any chown pending? */
+       if (need_chown) {
+               DEBUG(3,("chown#2 %s. uid = %u, gid = %u.\n",
+                        fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID));
+               if (try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) {
+                       DEBUG(2,("chown#2 %s, %u, %u failed. Error = %s.\n",
+                                fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID,
+                                strerror(errno)));
+                       return map_nt_error_from_unix(errno);
+               }
+               DEBUG(10,("chown#2 %s, %u, %u succeeded.\n",
+                         fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID));
+       }
+
        DEBUG(10, ("smb_set_nt_acl_nfs4 succeeded\n"));
        return NT_STATUS_OK;
 }
index e21136ccee9e0a10ef5464f0b54a87deb0a9dd40..3e78c69a5e6412ce3bf75372e5e4c134aba2f10e 100644 (file)
@@ -1225,6 +1225,38 @@ static int vfswrap_aio_suspend(struct vfs_handle_struct *handle, struct files_st
        return sys_aio_suspend(aiocb, n, timeout);
 }
 
+static int vfswrap_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
+{
+       return False;
+}
+
+static int vfswrap_is_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, bool *offline)
+{
+       if (ISDOT(path) || ISDOTDOT(path)) {
+               return 1;
+       }
+       
+       if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
+               return -1;
+       }
+       
+       *offline = (dmapi_file_flags(path) & FILE_ATTRIBUTE_OFFLINE) != 0;
+       return 0;
+}
+
+static int vfswrap_set_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
+{
+       /* We don't know how to set offline bit by default, needs to be overriden in the vfs modules */
+       return -1;
+}
+
+static bool vfswrap_is_remotestorage(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
+{
+       /* We don't know how to detect that volume is remote storage. VFS modules should redefine it. */
+       return False;
+}
+
+
 static vfs_op_tuple vfs_default_ops[] = {
 
        /* Disk operations */
@@ -1442,6 +1474,16 @@ static vfs_op_tuple vfs_default_ops[] = {
        {SMB_VFS_OP(vfswrap_aio_suspend),SMB_VFS_OP_AIO_SUSPEND,
         SMB_VFS_LAYER_OPAQUE},
 
+       {SMB_VFS_OP(vfswrap_aio_force), SMB_VFS_OP_AIO_FORCE,
+        SMB_VFS_LAYER_OPAQUE},
+       
+       {SMB_VFS_OP(vfswrap_is_offline),SMB_VFS_OP_IS_OFFLINE,
+        SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(vfswrap_set_offline),SMB_VFS_OP_SET_OFFLINE,
+        SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(vfswrap_is_remotestorage),SMB_VFS_OP_IS_REMOTESTORAGE,
+        SMB_VFS_LAYER_OPAQUE},
+       
        /* Finish VFS operations definition */
 
        {SMB_VFS_OP(NULL),              SMB_VFS_OP_NOOP,
index bcf61f3bc75f6a49ef91e7ddb853a5c68998458a..d10906dfb1e07da32a85d06f92dab0d2f322ff05 100644 (file)
@@ -30,7 +30,7 @@
 
 #include <gpfs_gpl.h>
 #include "nfs4_acls.h"
-
+#include "vfs_gpfs.h"
 
 static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp, 
                                 uint32 share_mode)
@@ -153,7 +153,7 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
        DEBUG(10, ("gpfs_get_nfs4_acl invoked for %s\n", fname));
 
        /* First get the real acl length */
-       gacl = gpfs_getacl_alloc(fname, GPFS_ACL_TYPE_NFS4);
+       gacl = gpfs_getacl_alloc(fname, 0);
        if (gacl == NULL) {
                DEBUG(9, ("gpfs_getacl failed for %s with %s\n",
                           fname, strerror(errno)));
@@ -208,10 +208,10 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
                if (i > 0 && gace->aceType == SMB_ACE4_ACCESS_DENIED_ACE_TYPE) {
                        struct gpfs_ace_v4 *prev = &gacl->ace_v4[i-1];
                        if (prev->aceType == SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE &&
-                                       prev->aceFlags == gace->aceFlags &&
-                                       prev->aceIFlags == gace->aceIFlags &&
-                                       (gace->aceMask & prev->aceMask) == 0 &&
-                                       gace->aceWho == prev->aceWho) {
+                           prev->aceFlags == gace->aceFlags &&
+                           prev->aceIFlags == gace->aceIFlags &&
+                           (gace->aceMask & prev->aceMask) == 0 &&
+                           gace->aceWho == prev->aceWho) {
                                /* its redundent - skip it */
                                continue;
                        }                                                
@@ -256,7 +256,7 @@ static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle,
        int     result;
 
        *ppdesc = NULL;
-       result = gpfs_get_nfs4_acl(fsp->fsp_name, &pacl);
+       result = gpfs_get_nfs4_acl(name, &pacl);
 
        if (result == 0)
                return smb_get_nt_acl_nfs4(handle->conn, name, security_info, ppdesc, pacl);
@@ -301,8 +301,31 @@ static bool gpfsacl_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
                gace->aceType = aceprop->aceType;
                gace->aceFlags = aceprop->aceFlags;
                gace->aceMask = aceprop->aceMask;
+               
+               /*
+                * GPFS can't distinguish between WRITE and APPEND on
+                * files, so one being set without the other is an
+                * error. Sorry for the many ()'s :-)
+                */
+               
+               if (!fsp->is_directory
+                   &&
+                   ((((gace->aceMask & ACE4_MASK_WRITE) == 0)
+                     && ((gace->aceMask & ACE4_MASK_APPEND) != 0))
+                    ||
+                    (((gace->aceMask & ACE4_MASK_WRITE) != 0)
+                     && ((gace->aceMask & ACE4_MASK_APPEND) == 0)))
+                   &&
+                   lp_parm_bool(fsp->conn->params->service, "gpfs",
+                                "merge_writeappend", True)) {
+                       DEBUG(2, ("vfs_gpfs.c: file [%s]: ACE contains "
+                                 "WRITE^APPEND, setting WRITE|APPEND\n",
+                                 fsp->fsp_name));
+                       gace->aceMask |= ACE4_MASK_WRITE|ACE4_MASK_APPEND;
+               }
+               
                gace->aceIFlags = (aceprop->flags&SMB_ACE4_ID_SPECIAL) ? ACE4_IFLAG_SPECIAL_ID : 0;
-
+               
                if (aceprop->flags&SMB_ACE4_ID_SPECIAL)
                {
                        switch(aceprop->who.special_id)
@@ -347,7 +370,7 @@ static NTSTATUS gpfsacl_set_nt_acl_internal(files_struct *fsp, uint32 security_i
        struct gpfs_acl *acl;
        NTSTATUS result = NT_STATUS_ACCESS_DENIED;
 
-       acl = gpfs_getacl_alloc(fsp->fsp_name, GPFS_ACL_TYPE_ACCESS);
+       acl = gpfs_getacl_alloc(fsp->fsp_name, 0);
        if (acl == NULL)
                return result;
 
@@ -628,75 +651,225 @@ int gpfsacl_sys_acl_delete_def_file(vfs_handle_struct *handle,
        return -1;
 }
 
+/*
+ * Assumed: mode bits are shiftable and standard
+ * Output: the new aceMask field for an smb nfs4 ace
+ */
+static uint32 gpfsacl_mask_filter(uint32 aceType, uint32 aceMask, uint32 rwx)
+{
+       const uint32 posix_nfs4map[3] = {
+                SMB_ACE4_EXECUTE, /* execute */
+               SMB_ACE4_WRITE_DATA | SMB_ACE4_APPEND_DATA, /* write; GPFS specific */
+                SMB_ACE4_READ_DATA /* read */
+       };
+       int     i;
+       uint32_t        posix_mask = 0x01;
+       uint32_t        posix_bit;
+       uint32_t        nfs4_bits;
+       
+       for(i=0; i<3; i++) {
+               nfs4_bits = posix_nfs4map[i];
+               posix_bit = rwx & posix_mask;
+               
+               if (aceType==SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) {
+                       if (posix_bit)
+                               aceMask |= nfs4_bits;
+                       else
+                               aceMask &= ~nfs4_bits;
+               } else {
+                       /* add deny bits when suitable */
+                       if (!posix_bit)
+                               aceMask |= nfs4_bits;
+                       else
+                               aceMask &= ~nfs4_bits;
+               } /* other ace types are unexpected */
+               
+               posix_mask <<= 1;
+       }
+       
+       return aceMask;
+}
+
+static int gpfsacl_emu_chmod(const char *path, mode_t mode)
+{
+       SMB4ACL_T *pacl = NULL;
+       int     result;
+       bool    haveAllowEntry[SMB_ACE4_WHO_EVERYONE + 1] = {False, False, False, False};
+       int     i;
+       files_struct    fake_fsp; /* TODO: rationalize parametrization */
+       SMB4ACE_T       *smbace;
+       
+       DEBUG(10, ("gpfsacl_emu_chmod invoked for %s mode %o\n", path, mode));
+       
+       result = gpfs_get_nfs4_acl(path, &pacl);
+       if (result)
+               return result;
+       
+       if (mode & ~(S_IRWXU | S_IRWXG | S_IRWXO)) {
+               DEBUG(2, ("WARNING: cutting extra mode bits %o on %s\n", mode, path));
+       }
+       
+       for (smbace=smb_first_ace4(pacl); smbace!=NULL; smbace = smb_next_ace4(smbace)) {
+               SMB_ACE4PROP_T  *ace = smb_get_ace4(smbace);
+               uint32_t        specid = ace->who.special_id;
+               
+               if (ace->flags&SMB_ACE4_ID_SPECIAL &&
+                   ace->aceType<=SMB_ACE4_ACCESS_DENIED_ACE_TYPE &&
+                   specid <= SMB_ACE4_WHO_EVERYONE) {
+                       
+                       uint32_t newMask;
+                       
+                       if (ace->aceType==SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE)
+                               haveAllowEntry[specid] = True;
+                       
+                       /* mode >> 6 for @owner, mode >> 3 for @group,
+                        * mode >> 0 for @everyone */
+                       newMask = gpfsacl_mask_filter(ace->aceType, ace->aceMask,
+                                                     mode >> ((SMB_ACE4_WHO_EVERYONE - specid) * 3));
+                       if (ace->aceMask!=newMask) {
+                               DEBUG(10, ("ace changed for %s (%o -> %o) id=%d\n",
+                                          path, ace->aceMask, newMask, specid));
+                       }
+                       ace->aceMask = newMask;
+               }
+       }
+
+       /* make sure we have at least ALLOW entries
+        * for all the 3 special ids (@EVERYONE, @OWNER, @GROUP)
+        * - if necessary
+        */
+       for(i = SMB_ACE4_WHO_OWNER; i<=SMB_ACE4_WHO_EVERYONE; i++) {
+               SMB_ACE4PROP_T  ace;
+               
+               if (haveAllowEntry[i]==True)
+                       continue;
+               
+               memset(&ace, 0, sizeof(SMB_ACE4PROP_T));
+               ace.aceType = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE;
+               ace.flags |= SMB_ACE4_ID_SPECIAL;
+               ace.who.special_id = i;
+               
+               if (i==SMB_ACE4_WHO_GROUP) /* not sure it's necessary... */
+                       ace.aceFlags |= SMB_ACE4_IDENTIFIER_GROUP;
+               
+               ace.aceMask = gpfsacl_mask_filter(ace.aceType, ace.aceMask,
+                                                 mode >> ((SMB_ACE4_WHO_EVERYONE - i) * 3));
+               
+               /* don't add unnecessary aces */
+               if (!ace.aceMask)
+                       continue;
+               
+               /* we add it to the END - as windows expects allow aces */
+               smb_add_ace4(pacl, &ace);
+               DEBUG(10, ("Added ALLOW ace for %s, mode=%o, id=%d, aceMask=%x\n",
+                          path, mode, i, ace.aceMask));
+       }
+       
+       /* don't add complementary DENY ACEs here */
+       memset(&fake_fsp, 0, sizeof(struct files_struct));
+       fake_fsp.fsp_name = (char *)path; /* no file_new is needed here */
+       
+       /* put the acl */
+       if (gpfsacl_process_smbacl(&fake_fsp, pacl) == False)
+               return -1;
+       return 0; /* ok for [f]chmod */
+}
+
 static int vfs_gpfs_chmod(vfs_handle_struct *handle, const char *path, mode_t mode)
 {
                 SMB_STRUCT_STAT st;
+                int rc;
+                
                 if (SMB_VFS_NEXT_STAT(handle, path, &st) != 0) {
-                                return -1;
+                        return -1;
                 }
+                
                 /* avoid chmod() if possible, to preserve acls */
                 if ((st.st_mode & ~S_IFMT) == mode) {
-                                return 0;
+                        return 0;
                 }
-                return SMB_VFS_NEXT_CHMOD(handle, path, mode);
+
+                rc = gpfsacl_emu_chmod(path, mode);
+                if (rc == 1)
+                        return SMB_VFS_NEXT_CHMOD(handle, path, mode);
+                return rc;
 }
 
 static int vfs_gpfs_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
 {
                 SMB_STRUCT_STAT st;
+                int rc;
+                
                 if (SMB_VFS_NEXT_FSTAT(handle, fsp, &st) != 0) {
-                                return -1;
+                        return -1;
                 }
+
                 /* avoid chmod() if possible, to preserve acls */
                 if ((st.st_mode & ~S_IFMT) == mode) {
-                                return 0;
+                        return 0;
                 }
-                return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
+
+                rc = gpfsacl_emu_chmod(fsp->fsp_name, mode);
+                if (rc == 1)
+                        return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
+                return rc;
 }
 
 /* VFS operations structure */
 
 static vfs_op_tuple gpfs_op_tuples[] = {
-
-               { SMB_VFS_OP(vfs_gpfs_kernel_flock), SMB_VFS_OP_KERNEL_FLOCK,
-                               SMB_VFS_LAYER_OPAQUE },
-
-        { SMB_VFS_OP(vfs_gpfs_setlease), SMB_VFS_OP_LINUX_SETLEASE,
-                       SMB_VFS_LAYER_OPAQUE },
-
-        { SMB_VFS_OP(gpfsacl_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL,
-                       SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(gpfsacl_get_nt_acl), SMB_VFS_OP_GET_NT_ACL,
-                       SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(gpfsacl_fset_nt_acl), SMB_VFS_OP_FSET_NT_ACL,
-                       SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(gpfsacl_set_nt_acl), SMB_VFS_OP_SET_NT_ACL,
-                       SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(gpfsacl_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE,
-                       SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(gpfsacl_sys_acl_get_fd), SMB_VFS_OP_SYS_ACL_GET_FD,
-                       SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(gpfsacl_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE,
-                       SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(gpfsacl_sys_acl_set_fd), SMB_VFS_OP_SYS_ACL_SET_FD,
-                       SMB_VFS_LAYER_TRANSPARENT },
-
+       
+       { SMB_VFS_OP(vfs_gpfs_kernel_flock), 
+         SMB_VFS_OP_KERNEL_FLOCK,
+         SMB_VFS_LAYER_OPAQUE },
+       
+        { SMB_VFS_OP(vfs_gpfs_setlease), 
+         SMB_VFS_OP_LINUX_SETLEASE,
+         SMB_VFS_LAYER_OPAQUE },
+       
+        { SMB_VFS_OP(gpfsacl_fget_nt_acl), 
+         SMB_VFS_OP_FGET_NT_ACL,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(gpfsacl_get_nt_acl), 
+         SMB_VFS_OP_GET_NT_ACL,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(gpfsacl_fset_nt_acl), 
+         SMB_VFS_OP_FSET_NT_ACL,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(gpfsacl_set_nt_acl), 
+         SMB_VFS_OP_SET_NT_ACL,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(gpfsacl_sys_acl_get_file), 
+         SMB_VFS_OP_SYS_ACL_GET_FILE,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(gpfsacl_sys_acl_get_fd), 
+         SMB_VFS_OP_SYS_ACL_GET_FD,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(gpfsacl_sys_acl_set_file), 
+         SMB_VFS_OP_SYS_ACL_SET_FILE,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(gpfsacl_sys_acl_set_fd), 
+         SMB_VFS_OP_SYS_ACL_SET_FD,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
         { SMB_VFS_OP(gpfsacl_sys_acl_delete_def_file),
-                SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,
-                SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(vfs_gpfs_chmod), SMB_VFS_OP_CHMOD,
-                SMB_VFS_LAYER_TRANSPARENT },
-
-        { SMB_VFS_OP(vfs_gpfs_fchmod), SMB_VFS_OP_FCHMOD,
-                SMB_VFS_LAYER_TRANSPARENT },
+         SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(vfs_gpfs_chmod), 
+         SMB_VFS_OP_CHMOD,
+         SMB_VFS_LAYER_TRANSPARENT },
+       
+        { SMB_VFS_OP(vfs_gpfs_fchmod), 
+         SMB_VFS_OP_FCHMOD,
+         SMB_VFS_LAYER_TRANSPARENT },
 
         { SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP }
 
diff --git a/source3/modules/vfs_gpfs.h b/source3/modules/vfs_gpfs.h
new file mode 100644 (file)
index 0000000..3c499b0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+   Unix SMB/CIFS implementation.
+   Wrap gpfs calls in vfs functions.
+   Copyright (C) Christian Ambach <cambach1@de.ibm.com> 2006
+   
+   Major code contributions by Chetan Shringarpure <chetan.sh@in.ibm.com>
+                            and Gomati Mohanan <gomati.mohanan@in.ibm.com>
+   
+   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.
+  
+
+*/
+
+bool set_gpfs_sharemode(files_struct *fsp, uint32 access_mask,
+                       uint32 share_access);
+int set_gpfs_lease(int fd, int leasetype);
+int smbd_gpfs_getacl(char *pathname, int flags, void *acl);
+int smbd_gpfs_putacl(char *pathname, int flags, void *acl);
+void init_gpfs(void);
index 2d64bc0184ded75357642f30c1881eb52e983fe4..cb3508dc30f7dd291759c34854fb932cb764122a 100644 (file)
 #define lock_type struct flock64
 #endif
 
+#ifdef HAVE_GPFS
+#include "gpfs_gpl.h"
+#endif
+
 #define MODULE "prealloc"
 static int module_debug;
 
 static int preallocate_space(int fd, SMB_OFF_T size)
 {
+#ifndef HAVE_GPFS
        lock_type fl = {0};
        int err;
 
@@ -78,6 +83,9 @@ static int preallocate_space(int fd, SMB_OFF_T size)
        err = -1;
        errno = ENOSYS;
 #endif
+#else /* GPFS uses completely different interface */
+       err = gpfs_prealloc(fd, (gpfs_off64_t)0, (gpfs_off64_t)size);
+#endif
 
        if (err) {
                DEBUG(module_debug,
diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c
new file mode 100644 (file)
index 0000000..54fc672
--- /dev/null
@@ -0,0 +1,637 @@
+/* 
+ * implementation of an Shadow Copy module - version 2
+ *
+ * Copyright (C) Andrew Tridgell     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 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"
+
+/*
+
+  This is a 2nd implemetation of a shadow copy module for exposing
+  snapshots to windows clients as shadow copies. This version has the
+  following features:
+
+     1) you don't need to populate your shares with symlinks to the
+     snapshots. This can be very important when you have thousands of
+     shares, or use [homes]
+
+     2) the inode number of the files is altered so it is different
+     from the original. This allows the 'restore' button to work
+     without a sharing violation
+
+  Module options:
+
+      shadow:snapdir = <directory where snapshots are kept>
+
+      This is the directory containing the @GMT-* snapshot directories. If it is an absolute
+      path it is used as-is. If it is a relative path, then it is taken relative to the mount
+      point of the filesystem that the root of this share is on
+
+      shadow:basedir = <base directory that snapshots are from>
+
+      This is an optional parameter that specifies the directory that
+      the snapshots are relative to. It defaults to the filesystem
+      mount point
+
+      shadow:fixinodes = yes/no
+
+      If you enable shadow:fixinodes then this module will modify the
+      apparent inode number of files in the snapshot directories using
+      a hash of the files path. This is needed for snapshot systems
+      where the snapshots have the same device:inode number as the
+      original files (such as happens with GPFS snapshots). If you
+      don't set this option then the 'restore' button in the shadow
+      copy UI will fail with a sharing violation.
+
+  Note that the directory names in the snapshot directory must take the form
+  @GMT-YYYY.MM.DD-HH.MM.SS
+  
+  The following command would generate a correctly formatted directory name:
+     date -u +@GMT-%Y.%m.%d-%H.%M.%S
+  
+ */
+
+static int vfs_shadow_copy2_debug_level = DBGC_VFS;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS vfs_shadow_copy2_debug_level
+
+#define GMT_NAME_LEN 24 /* length of a @GMT- name */
+
+/*
+  make very sure it is one of our special names 
+ */
+static inline bool shadow_copy2_match_name(const char *name)
+{
+       unsigned year, month, day, hr, min, sec;
+       if (name[0] != '@') return False;
+       if (strncmp(name, "@GMT-", 5) != 0) return False;
+       if (sscanf(name, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
+                  &day, &hr, &min, &sec) != 6) {
+               return False;
+       }
+       if (name[24] != 0 && name[24] != '/') {
+               return False;
+       }
+       return True;
+}
+
+/*
+  convert a name to the shadow directory
+ */
+
+#define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
+       const char *name = fname; \
+       if (shadow_copy2_match_name(fname)) { \
+               char *name2; \
+               rtype ret; \
+               name2 = convert_shadow2_name(handle, fname); \
+               if (name2 == NULL) { \
+                       errno = EINVAL; \
+                       return eret; \
+               } \
+               name = name2; \
+               ret = SMB_VFS_NEXT_ ## op args; \
+               talloc_free(name2); \
+               if (ret != eret) extra; \
+               return ret; \
+       } else { \
+               return SMB_VFS_NEXT_ ## op args; \
+       } \
+} while (0)
+
+/*
+  convert a name to the shadow directory: NTSTATUS-specific handling
+ */
+
+#define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
+        const char *name = fname; \
+        if (shadow_copy2_match_name(fname)) { \
+                char *name2; \
+                NTSTATUS ret; \
+                name2 = convert_shadow2_name(handle, fname); \
+                if (name2 == NULL) { \
+                        errno = EINVAL; \
+                        return eret; \
+                } \
+                name = name2; \
+                ret = SMB_VFS_NEXT_ ## op args; \
+                talloc_free(name2); \
+                if (!NT_STATUS_EQUAL(ret, eret)) extra; \
+                return ret; \
+        } else { \
+                return SMB_VFS_NEXT_ ## op args; \
+        } \
+} while (0)
+
+#define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
+
+#define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
+
+#define SHADOW2_NEXT2(op, args) do { \
+       if (shadow_copy2_match_name(oldname) || shadow_copy2_match_name(newname)) { \
+               errno = EROFS; \
+               return -1; \
+       } else { \
+               return SMB_VFS_NEXT_ ## op args; \
+       } \
+} while (0)
+
+
+/*
+  find the mount point of a filesystem
+ */
+static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
+{
+       char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
+       dev_t dev;
+       struct stat st;
+       char *p;
+
+       if (stat(path, &st) != 0) {
+               talloc_free(path);
+               return NULL;
+       }
+
+       dev = st.st_dev;
+
+       while ((p = strrchr(path, '/')) && p > path) {
+               *p = 0;
+               if (stat(path, &st) != 0) {
+                       talloc_free(path);
+                       return NULL;
+               }
+               if (st.st_dev != dev) {
+                       *p = '/';
+                       break;
+               }
+       }
+
+       return path;    
+}
+
+/*
+  work out the location of the snapshot for this share
+ */
+static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
+{
+       const char *snapdir;
+       char *mount_point;
+       const char *ret;
+
+       snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
+       if (snapdir == NULL) {
+               return NULL;
+       }
+       /* if its an absolute path, we're done */
+       if (*snapdir == '/') {
+               return snapdir;
+       }
+
+       /* other its relative to the filesystem mount point */
+       mount_point = find_mount_point(mem_ctx, handle);
+       if (mount_point == NULL) {
+               return NULL;
+       }
+
+       ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
+       talloc_free(mount_point);
+       return ret;
+}
+
+/*
+  work out the location of the base directory for snapshots of this share
+ */
+static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
+{
+       const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
+
+       /* other its the filesystem mount point */
+       if (basedir == NULL) {
+               basedir = find_mount_point(mem_ctx, handle);
+       }
+
+       return basedir;
+}
+
+/*
+  convert a filename from a share relative path, to a path in the
+  snapshot directory
+ */
+static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
+       const char *snapdir, *relpath, *baseoffset, *basedir;
+       size_t baselen;
+       char *ret;
+
+       snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
+       if (snapdir == NULL) {
+               DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
+               talloc_free(tmp_ctx);
+               return NULL;
+       }
+
+       basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
+       if (basedir == NULL) {
+               DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
+               talloc_free(tmp_ctx);
+               return NULL;
+       }
+
+       relpath = fname + GMT_NAME_LEN;
+       baselen = strlen(basedir);
+       baseoffset = handle->conn->connectpath + baselen;
+
+       /* some sanity checks */
+       if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
+           (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
+               DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
+                        basedir, handle->conn->connectpath));
+               talloc_free(tmp_ctx);
+               return NULL;
+       }
+
+       if (*relpath == '/') relpath++;
+       if (*baseoffset == '/') baseoffset++;
+
+       ret = talloc_asprintf(handle->data, "%s/%.*s/%s/%s", 
+                             snapdir, 
+                             GMT_NAME_LEN, fname, 
+                             baseoffset, 
+                             relpath);
+       DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
+       talloc_free(tmp_ctx);
+       return ret;
+}
+
+
+/*
+  simple string hash
+ */
+static uint32 string_hash(const char *s)
+{
+        uint32 n = 0;
+       while (*s) {
+                n = ((n << 5) + n) ^ (uint32)(*s++);
+        }
+        return n;
+}
+
+/*
+  modify a sbuf return to ensure that inodes in the shadow directory
+  are different from those in the main directory
+ */
+static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+       if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {           
+               /* some snapshot systems, like GPFS, return the name
+                  device:inode for the snapshot files as the current
+                  files. That breaks the 'restore' button in the shadow copy
+                  GUI, as the client gets a sharing violation.
+
+                  This is a crude way of allowing both files to be
+                  open at once. It has a slight chance of inode
+                  number collision, but I can't see a better approach
+                  without significant VFS changes
+               */
+               uint32_t shash = string_hash(fname) & 0xFF000000;
+               if (shash == 0) {
+                       shash = 1;
+               }
+               sbuf->st_ino ^= shash;
+       }
+}
+
+static int shadow_copy2_rename(vfs_handle_struct *handle,
+                       const char *oldname, const char *newname)
+{
+       SHADOW2_NEXT2(RENAME, (handle, oldname, newname));
+}
+
+static int shadow_copy2_symlink(vfs_handle_struct *handle,
+                               const char *oldname, const char *newname)
+{
+       SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
+}
+
+static int shadow_copy2_link(vfs_handle_struct *handle,
+                         const char *oldname, const char *newname)
+{
+       SHADOW2_NEXT2(LINK, (handle, oldname, newname));
+}
+
+static int shadow_copy2_open(vfs_handle_struct *handle,
+                            const char *fname, files_struct *fsp, int flags, mode_t mode)
+{
+       SHADOW2_NEXT(OPEN, (handle, name, fsp, flags, mode), int, -1);
+}
+
+static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
+                         const char *fname, const char *mask, uint32 attr)
+{
+        SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), void*, NULL);
+}
+
+static int shadow_copy2_stat(vfs_handle_struct *handle,
+                     const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+        _SHADOW2_NEXT(STAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf));
+}
+
+static int shadow_copy2_lstat(vfs_handle_struct *handle,
+                      const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+        _SHADOW2_NEXT(LSTAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf));
+}
+
+static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
+{
+       int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+       if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name)) {
+               convert_sbuf(handle, fsp->fsp_name, sbuf);
+       }
+       return ret;
+}
+
+static int shadow_copy2_unlink(vfs_handle_struct *handle, const char *fname)
+{
+        SHADOW2_NEXT(UNLINK, (handle, name), int, -1);
+}
+
+static int shadow_copy2_chmod(vfs_handle_struct *handle,
+                      const char *fname, mode_t mode)
+{
+        SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
+}
+
+static int shadow_copy2_chown(vfs_handle_struct *handle,
+                      const char *fname, uid_t uid, gid_t gid)
+{
+        SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
+}
+
+static int shadow_copy2_chdir(vfs_handle_struct *handle,
+                      const char *fname)
+{
+       SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
+}
+
+static int shadow_copy2_ntimes(vfs_handle_struct *handle,
+                      const char *fname, const struct timespec ts[2])
+{
+        SHADOW2_NEXT(NTIMES, (handle, name, ts), int, -1);
+}
+
+static int shadow_copy2_readlink(vfs_handle_struct *handle,
+                                const char *fname, char *buf, size_t bufsiz)
+{
+        SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
+}
+
+static int shadow_copy2_mknod(vfs_handle_struct *handle,
+                      const char *fname, mode_t mode, SMB_DEV_T dev)
+{
+        SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
+}
+
+static char *shadow_copy2_realpath(vfs_handle_struct *handle,
+                           const char *fname, char *resolved_path)
+{
+        SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), void*, NULL);
+}
+
+static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
+                              const char *fname, uint32 security_info,
+                              struct security_descriptor **ppdesc)
+{
+        SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
+}
+
+static NTSTATUS shadow_copy2_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
+                            const char *fname, uint32 security_info_sent,
+                            struct security_descriptor *psd)
+{
+        SHADOW2_NTSTATUS_NEXT(SET_NT_ACL, (handle, fsp, name, security_info_sent, psd), NT_STATUS_ACCESS_DENIED);
+}
+
+static int shadow_copy2_mkdir(vfs_handle_struct *handle,  const char *fname, mode_t mode)
+{
+        SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
+}
+
+static int shadow_copy2_rmdir(vfs_handle_struct *handle,  const char *fname)
+{
+        SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
+}
+
+static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, int flags)
+{
+        SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
+}
+
+static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
+                                 const char *fname, const char *aname, void *value, size_t size)
+{
+        SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
+}
+
+static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
+                                     const char *fname, const char *aname, void *value, size_t size)
+{
+        SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
+}
+
+static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, 
+                                     char *list, size_t size)
+{
+       SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
+}
+
+static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, 
+                                   const char *aname)
+{
+       SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
+}
+
+static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, 
+                                    const char *aname)
+{
+       SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
+}
+
+static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, 
+                                const char *aname, const void *value, size_t size, int flags)
+{
+       SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
+}
+
+static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, 
+                                 const char *aname, const void *value, size_t size, int flags)
+{
+       SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
+}
+
+static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
+                          const char *fname, mode_t mode)
+{
+        /* If the underlying VFS doesn't have ACL support... */
+        if (!handle->vfs_next.ops.chmod_acl) {
+                errno = ENOSYS;
+                return -1;
+        }
+        SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
+}
+
+static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, 
+                                             files_struct *fsp, 
+                                             SHADOW_COPY_DATA *shadow_copy2_data, 
+                                             bool labels)
+{
+       SMB_STRUCT_DIR *p;
+       const char *snapdir;
+       SMB_STRUCT_DIRENT *d;
+       TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
+
+       snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
+       if (snapdir == NULL) {
+               DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
+                        handle->conn->connectpath));
+               errno = EINVAL;
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
+
+       if (!p) {
+               DEBUG(0,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s' - %s\n", 
+                        snapdir, strerror(errno)));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       talloc_free(tmp_ctx);
+
+       shadow_copy2_data->num_volumes = 0;
+       shadow_copy2_data->labels      = NULL;
+
+       while ((d = SMB_VFS_NEXT_READDIR(handle, p))) {
+               SHADOW_COPY_LABEL *tlabels;
+
+               /* ignore names not of the right form in the snapshot directory */
+               if (!shadow_copy2_match_name(d->d_name)) {
+                       continue;
+               }
+
+               if (!labels) {
+                       /* the caller doesn't want the labels */
+                       shadow_copy2_data->num_volumes++;
+                       continue;
+               }
+
+               tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
+                                        shadow_copy2_data->labels,
+                                        SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
+               if (tlabels == NULL) {
+                       DEBUG(0,("shadow_copy2: out of memory\n"));
+                       SMB_VFS_NEXT_CLOSEDIR(handle, p);
+                       return -1;
+               }
+
+               strlcpy(tlabels[shadow_copy2_data->num_volumes], d->d_name, sizeof(*tlabels));
+               shadow_copy2_data->num_volumes++;
+               shadow_copy2_data->labels = tlabels;
+       }
+
+       SMB_VFS_NEXT_CLOSEDIR(handle,p);
+       return 0;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple shadow_copy2_ops[] = {
+        {SMB_VFS_OP(shadow_copy2_opendir),  SMB_VFS_OP_OPENDIR,  SMB_VFS_LAYER_TRANSPARENT},
+
+       /* directory operations */
+        {SMB_VFS_OP(shadow_copy2_mkdir),       SMB_VFS_OP_MKDIR,       SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_rmdir),       SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
+
+       /* xattr and flags operations */
+        {SMB_VFS_OP(shadow_copy2_chflags),     SMB_VFS_OP_CHFLAGS,     SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_getxattr),    SMB_VFS_OP_GETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_lgetxattr),   SMB_VFS_OP_LGETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_listxattr),   SMB_VFS_OP_LISTXATTR,   SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_lremovexattr),SMB_VFS_OP_LREMOVEXATTR,SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_setxattr),    SMB_VFS_OP_SETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_lsetxattr),   SMB_VFS_OP_LSETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
+
+        /* File operations */
+        {SMB_VFS_OP(shadow_copy2_open),       SMB_VFS_OP_OPEN,     SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_rename),     SMB_VFS_OP_RENAME,   SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_stat),       SMB_VFS_OP_STAT,     SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_lstat),      SMB_VFS_OP_LSTAT,    SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_fstat),      SMB_VFS_OP_FSTAT,    SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_unlink),     SMB_VFS_OP_UNLINK,   SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_chmod),      SMB_VFS_OP_CHMOD,    SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_chown),      SMB_VFS_OP_CHOWN,    SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_chdir),      SMB_VFS_OP_CHDIR,    SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_ntimes),     SMB_VFS_OP_NTIMES,   SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_symlink),    SMB_VFS_OP_SYMLINK,  SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_readlink),   SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_link),       SMB_VFS_OP_LINK,     SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_mknod),      SMB_VFS_OP_MKNOD,    SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_realpath),   SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
+
+        /* NT File ACL operations */
+        {SMB_VFS_OP(shadow_copy2_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(shadow_copy2_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+        /* POSIX ACL operations */
+        {SMB_VFS_OP(shadow_copy2_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+       /* special shadown copy op */
+       {SMB_VFS_OP(shadow_copy2_get_shadow_copy2_data), 
+        SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_shadow_copy2_init(void);
+NTSTATUS vfs_shadow_copy2_init(void)
+{
+       NTSTATUS ret;
+
+       ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2", shadow_copy2_ops);
+
+       if (!NT_STATUS_IS_OK(ret))
+               return ret;
+
+       vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
+       if (vfs_shadow_copy2_debug_level == -1) {
+               vfs_shadow_copy2_debug_level = DBGC_VFS;
+               DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
+                       "vfs_shadow_copy2_init"));
+       } else {
+               DEBUG(10, ("%s: Debug class number of '%s': %d\n", 
+                       "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
+       }
+
+       return ret;
+}
diff --git a/source3/modules/vfs_tsmsm.c b/source3/modules/vfs_tsmsm.c
new file mode 100644 (file)
index 0000000..fe791b2
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+  Unix SMB/CIFS implementation.
+  Samba VFS module for handling offline files
+  with Tivoli Storage Manager Space Management
+
+  (c) Alexander Bokovoy, 2007
+  (c) Andrew Tridgell, 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/>.
+ */
+/*
+  This VFS module accepts following options:
+  tsmsm: hsm script = <path to hsm script> (/bin/true by default, i.e. does nothing)
+         hsm script should point to a shell script which accepts two arguments:
+        <operation> <filepath>
+        where <operation> is currently 'offline' to set offline status of the <filepath>
+
+  tsmsm: online ratio = ratio to check reported size against actual file size (0.5 by default)
+
+  The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics
+  based on the fact that number of blocks reported of a file multiplied by 512 will be
+  bigger than 'online ratio' of actual size for online (non-migrated) files.
+
+  If checks fail, we call DMAPI and ask for specific IBM attribute which present for
+  offline (migrated) files. If this attribute presents, we consider file offline.
+ */
+
+#include "includes.h"
+
+#ifndef USE_DMAPI
+#error "This module requires DMAPI support!"
+#endif
+
+#ifdef HAVE_XFS_DMAPI_H
+#include <xfs/dmapi.h>
+#elif defined(HAVE_SYS_DMI_H)
+#include <sys/dmi.h>
+#elif defined(HAVE_SYS_JFSDMAPI_H)
+#include <sys/jfsdmapi.h>
+#elif defined(HAVE_SYS_DMAPI_H)
+#include <sys/dmapi.h>
+#elif defined(HAVE_DMAPI_H)
+#include <dmapi.h>
+#endif
+
+#ifndef _ISOC99_SOURCE
+#define _ISOC99_SOURCE 
+#endif
+
+#include <math.h> 
+
+/* optimisation tunables - used to avoid the DMAPI slow path */
+#define FILE_IS_ONLINE_RATIO      0.5
+#define DM_ATTRIB_OBJECT "IBMObj"
+#define DM_ATTRIB_MIGRATED "IBMMig"
+
+struct tsmsm_struct {
+       dm_sessid_t sid;
+       float online_ratio;
+       char *hsmscript;
+};
+
+#define TSM_STRINGIFY(a) #a
+#define TSM_TOSTRING(a) TSM_STRINGIFY(a)
+
+static void tsmsm_free_data(void **pptr) {
+       struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr;
+       if(!tsmd) return;
+       TALLOC_FREE(*tsmd);
+}
+
+static int tsmsm_connect(struct vfs_handle_struct *handle,
+                        const char *service,
+                        const char *user) {
+       struct tsmsm_struct *tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct);
+       const char *hsmscript, *tsmname;
+       const char *fres;
+       
+       if (!tsmd) {
+               DEBUG(0,("tsmsm_connect: out of memory!\n"));
+               return -1;
+       }
+
+       tsmd->sid = *(dm_sessid_t*) dmapi_get_current_session();
+
+       if (tsmd->sid == DM_NO_SESSION) {
+               DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
+               TALLOC_FREE(tsmd);
+               return -1;
+       }
+
+       tsmname = (handle->param ? handle->param : "tsmsm");
+       hsmscript = lp_parm_const_string(SNUM(handle->conn), tsmname,
+                                        "hsm script", NULL);
+       if (hsmscript) {
+               tsmd->hsmscript = talloc_strdup(tsmd, hsmscript);
+               if(!tsmd->hsmscript) {
+                       DEBUG(1, ("tsmsm_connect: can't allocate memory for hsm script path"));
+                       TALLOC_FREE(tsmd);
+                       return -1;
+               }
+       } else {
+               DEBUG(1, ("tsmsm_connect: can't call hsm script because it "
+                         "is not set to anything in the smb.conf\n"
+                         "Use %s: 'hsm script = path' to set it\n",
+                         tsmname));
+               TALLOC_FREE(tsmd);
+               return -1;
+       }
+
+       fres = lp_parm_const_string(SNUM(handle->conn), tsmname, 
+                                   "online ratio", TSM_TOSTRING(FILE_IS_ONLINE_RATIO));
+       tsmd->online_ratio = strtof(fres, NULL);
+       if((tsmd->online_ratio == (float)0) || ((errno == ERANGE) &&
+                                               ((tsmd->online_ratio == HUGE_VALF) ||
+                                                (tsmd->online_ratio == HUGE_VALL)))) {
+               DEBUG(1, ("tsmsm_connect: error while getting online ratio from smb.conf."
+                         "Default to %s.\n", TSM_TOSTRING(FILE_IS_ONLINE_RATIO)));
+               tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
+       }
+
+        /* Store the private data. */
+        SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
+                                struct tsmsm_struct, return -1);
+        return SMB_VFS_NEXT_CONNECT(handle, service, user); 
+}
+
+static int tsmsm_is_offline(struct vfs_handle_struct *handle, 
+                           struct connection_struct *conn,
+                           const char *path,
+                           SMB_STRUCT_STAT *stbuf,
+                           bool *offline) {
+       struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
+       void *dmhandle = NULL;
+       size_t dmhandle_len = 0;
+       size_t rlen;
+       dm_attrname_t dmname;
+       int ret;
+
+        /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
+          then assume it is not offline (it may not be 100%, as it could be sparse) */
+       if (512 * (off_t)stbuf->st_blocks >= stbuf->st_size * tsmd->online_ratio) {
+               *offline = false;
+               DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
+                         path, stbuf->st_blocks, stbuf->st_size, tsmd->online_ratio));
+               return 0;
+       }
+       
+        /* using POSIX capabilities does not work here. It's a slow path, so 
+        * become_root() is just as good anyway (tridge) 
+        */
+
+       /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
+        * we need to be root to do DMAPI manipulations.
+        */
+       become_root();
+
+       /* go the slow DMAPI route */
+       if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
+               ret = -1;
+               DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n", 
+                        path, strerror(errno)));
+               *offline = True;
+               goto done;
+       }
+
+       memset(&dmname, 0, sizeof(dmname));
+       strlcpy((char *)&dmname.an_chars[0], DM_ATTRIB_OBJECT, sizeof(dmname.an_chars));
+
+       ret = dm_get_dmattr(tsmd->sid, dmhandle, dmhandle_len, 
+                           DM_NO_TOKEN, &dmname, 0, NULL, &rlen);
+
+       /* its offline if the IBMObj attribute exists */
+       *offline = (ret == 0 || (ret == -1 && errno == E2BIG));
+
+       DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
+
+       ret = 0;
+
+       dm_handle_free(dmhandle, dmhandle_len); 
+
+done:
+       unbecome_root();
+       return ret;
+}
+
+
+static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
+       /* see if the file might be offline. This is called before each IO
+          to ensure we use AIO if the file is offline. We don't do the full dmapi
+          call as that would be too slow, instead we err on the side of using AIO
+          if the file might be offline
+       */
+       if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
+               DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
+                         sbuf.st_blocks, sbuf.st_size, tsmd->online_ratio));
+               return !(512 * (off_t)sbuf.st_blocks >= sbuf.st_size * tsmd->online_ratio);
+       }
+       return False;
+}
+
+static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp, 
+                               SMB_STRUCT_AIOCB *aiocb)
+{
+       ssize_t result;
+
+       result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
+       if(result >= 0) {
+               notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name);
+       }
+
+       return result;
+}
+
+static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
+                             SMB_OFF_T offset, size_t n)
+{
+       bool file_online = tsmsm_aio_force(handle, fsp);
+
+       if(!file_online) 
+           return ENOSYS;
+           
+       return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
+}
+
+/* We do overload pread to allow notification when file becomes online after offline status */
+/* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
+static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
+                          void *data, size_t n, SMB_OFF_T offset) {
+       ssize_t result;
+       bool notify_online = tsmsm_aio_force(handle, fsp);
+
+       result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+       if((result != -1) && notify_online) {
+           /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
+              what we can do is to send notification that file became online
+           */
+               notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name);
+       }
+
+       return result;
+}
+
+static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
+                          void *data, size_t n, SMB_OFF_T offset) {
+       ssize_t result;
+       bool notify_online = tsmsm_aio_force(handle, fsp);
+
+       result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+       if((result != -1) && notify_online) {
+           /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
+              what we can do is to send notification that file became online
+           */
+               notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name);
+       }
+
+       return result;
+}
+
+static int tsmsm_set_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, 
+                            const char *path) {
+       struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
+       int result = 0;
+       char *command;
+
+       /* Now, call the script */
+       command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
+       if(!command) {
+               DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
+               return -1;
+       }
+       DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
+       if((result = smbrun(command, NULL)) != 0) {
+               DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
+       }
+       TALLOC_FREE(command);
+       return result;
+}
+
+static bool tsmsm_is_remotestorage(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path) {
+       return True;
+}
+
+static vfs_op_tuple vfs_tsmsm_ops[] = {
+
+       /* Disk operations */
+
+       {SMB_VFS_OP(tsmsm_connect),     SMB_VFS_OP_CONNECT,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(tsmsm_aio_force),   SMB_VFS_OP_AIO_FORCE,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(tsmsm_aio_return),  SMB_VFS_OP_AIO_RETURN,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(tsmsm_pread),       SMB_VFS_OP_PREAD,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(tsmsm_pwrite),      SMB_VFS_OP_PWRITE,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(tsmsm_sendfile),    SMB_VFS_OP_SENDFILE,
+        SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(tsmsm_is_offline),SMB_VFS_OP_IS_OFFLINE,
+        SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(tsmsm_set_offline),SMB_VFS_OP_SET_OFFLINE,
+        SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(tsmsm_is_remotestorage),SMB_VFS_OP_IS_REMOTESTORAGE,
+        SMB_VFS_LAYER_OPAQUE},
+
+       /* Finish VFS operations definition */
+
+       {SMB_VFS_OP(NULL),              SMB_VFS_OP_NOOP,
+        SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_tsmsm_init(void);
+NTSTATUS vfs_tsmsm_init(void)
+{
+       return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
+                               "tsmsm", vfs_tsmsm_ops);
+}
index 016c8adb1bd59d2e39ab93f51900c1cd189b7c32..d7063c99894bcfd948d5c48d3162feea5b0c35a5 100644 (file)
@@ -117,6 +117,15 @@ int count_current_connections( const char *sharename, bool clear  )
        return cs.curr_connections;
 }
 
+/****************************************************************************
+ Count the number of connections open across all shares.
+****************************************************************************/
+
+int count_all_current_connections(void)
+{
+        return count_current_connections(NULL, True /* clear stale entries */);
+}
+
 /****************************************************************************
  Claim an entry in the connections database.
 ****************************************************************************/
index 05e9138ea97920bad5e7b51e6debf57fcf4d3e84..620baf199e42cf8634af6a8e4f9b50b9b8f640ef 100644 (file)
@@ -46,7 +46,7 @@ bool dmapi_have_session(void) { return False; }
 #define DMAPI_SESSION_NAME "samba"
 #define DMAPI_TRACE 10
 
-static dm_sessid_t dmapi_session = DM_NO_SESSION;
+static dm_sessid_t samba_dmapi_session = DM_NO_SESSION;
 
 /* Initialise the DMAPI interface. Make sure that we only end up initialising
  * once per process to avoid resource leaks across different DMAPI
@@ -75,7 +75,7 @@ static int init_dmapi_service(void)
 
 bool dmapi_have_session(void)
 {
-       return dmapi_session != DM_NO_SESSION;
+       return samba_dmapi_session != DM_NO_SESSION;
 }
 
 static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count)
@@ -110,7 +110,7 @@ int dmapi_init_session(void)
         */
        SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root");
 
-       dmapi_session = DM_NO_SESSION;
+       samba_dmapi_session = DM_NO_SESSION;
        if (init_dmapi_service() < 0) {
                return -1;
        }
@@ -139,7 +139,7 @@ retry:
                err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen);
                buf[sizeof(buf) - 1] = '\0';
                if (err == 0 && strcmp(DMAPI_SESSION_NAME, buf) == 0) {
-                       dmapi_session = sessions[i];
+                       samba_dmapi_session = sessions[i];
                        DEBUGADD(DMAPI_TRACE,
                                ("attached to existing DMAPI session "
                                 "named '%s'\n", buf));
@@ -150,16 +150,15 @@ retry:
        TALLOC_FREE(sessions);
 
        /* No session already defined. */
-       if (dmapi_session == DM_NO_SESSION) {
-               err = dm_create_session(DM_NO_SESSION,
-                                       CONST_DISCARD(char *,
-                                                     DMAPI_SESSION_NAME),
-                                       &dmapi_session);
+       if (samba_dmapi_session == DM_NO_SESSION) {
+               err = dm_create_session(DM_NO_SESSION, 
+                                       CONST_DISCARD(char *, DMAPI_SESSION_NAME),
+                                       &samba_dmapi_session);
                if (err < 0) {
                        DEBUGADD(DMAPI_TRACE,
                                ("failed to create new DMAPI session: %s\n",
                                strerror(errno)));
-                       dmapi_session = DM_NO_SESSION;
+                       samba_dmapi_session = DM_NO_SESSION;
                        return -1;
                }
 
@@ -185,22 +184,22 @@ static int reattach_dmapi_session(void)
        char    buf[DM_SESSION_INFO_LEN];
        size_t  buflen;
 
-       if (dmapi_session != DM_NO_SESSION ) {
+       if (samba_dmapi_session != DM_NO_SESSION ) {
                become_root();
 
                /* NOTE: On Linux, this call opens /dev/dmapi, costing us a
                 * file descriptor. Ideally, we would close this when we fork.
                 */
                if (init_dmapi_service() < 0) {
-                       dmapi_session = DM_NO_SESSION;
+                       samba_dmapi_session = DM_NO_SESSION;
                        unbecome_root();
                        return -1;
                }
 
-               if (dm_query_session(dmapi_session, sizeof(buf),
+               if (dm_query_session(samba_dmapi_session, sizeof(buf),
                            buf, &buflen) < 0) {
                        /* Session is stale. Disable DMAPI. */
-                       dmapi_session = DM_NO_SESSION;
+                       samba_dmapi_session = DM_NO_SESSION;
                        unbecome_root();
                        return -1;
                }
@@ -214,33 +213,42 @@ static int reattach_dmapi_session(void)
        return 0;
 }
 
-uint32 dmapi_file_flags(const char * const path)
+/* If a DMAPI session has been initialised, then we need to make sure
+ * we are attached to it and have the correct privileges. This is
+ * necessary to be able to do DMAPI operations across a fork(2). If
+ * it fails, there is no likelihood of that failure being transient.
+ *
+ * Note that this use of the static attached flag relies on the fact
+ * that dmapi_file_flags() is never called prior to forking the
+ * per-client server process.
+ */
+const void * dmapi_get_current_session(void) 
 {
        static int attached = 0;
+       if (dmapi_have_session() && !attached) {
+               attached++;
+               if (reattach_dmapi_session() < 0) {
+                       return DM_NO_SESSION;
+               }
+       }
+       return &samba_dmapi_session;
+}
 
+uint32 dmapi_file_flags(const char * const path)
+{
+       dm_sessid_t dmapi_session;
        int             err;
        dm_eventset_t   events = {0};
        uint            nevents;
 
-       void    *dm_handle;
-       size_t  dm_handle_len;
+       void    *dm_handle = NULL;
+       size_t  dm_handle_len = 0;
 
        uint32  flags = 0;
 
-       /* If a DMAPI session has been initialised, then we need to make sure
-        * we are attached to it and have the correct privileges. This is
-        * necessary to be able to do DMAPI operations across a fork(2). If
-        * it fails, there is no liklihood of that failure being transient.
-        *
-        * Note that this use of the static attached flag relies on the fact
-        * that dmapi_file_flags() is never called prior to forking the
-        * per-client server process.
-        */
-       if (dmapi_have_session() && !attached) {
-               attached++;
-               if (reattach_dmapi_session() < 0) {
-                       return 0;
-               }
+       dmapi_session = *(dm_sessid_t*) dmapi_get_current_session();
+       if (dmapi_session == DM_NO_SESSION) {
+               return 0;
        }
 
        /* AIX has DMAPI but no POSIX capablities support. In this case,
@@ -313,4 +321,5 @@ done:
        return flags;
 }
 
+
 #endif /* USE_DMAPI */
index d3813f9b41b0af24c85733dd7c2f1713f666b114..2021621dfa0c94e54b74410af85c8d61184c08e1 100644 (file)
@@ -30,23 +30,6 @@ static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
        return 0;
 }
 
-/****************************************************************************
- Work out whether this file is offline
-****************************************************************************/
-
-static uint32 set_offline_flag(connection_struct *conn, const char *const path)
-{
-       if (ISDOT(path) || ISDOTDOT(path)) {
-               return 0;
-       }
-
-       if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
-               return 0;
-       }
-
-       return dmapi_file_flags(path);
-}
-
 /****************************************************************************
  Change a dos mode to a unix mode.
     Base permission for files:
@@ -366,6 +349,8 @@ uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT
 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
 {
        uint32 result = 0;
+       bool offline;
+       int ret;
 
        DEBUG(8,("dos_mode: %s\n", path));
 
@@ -395,8 +380,10 @@ uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
                result |= dos_mode_from_sbuf(conn, path, sbuf);
        }
 
-       if (S_ISREG(sbuf->st_mode)) {
-               result |= set_offline_flag(conn, path);
+       
+       ret = SMB_VFS_IS_OFFLINE(conn, path, sbuf, &offline);
+       if (S_ISREG(sbuf->st_mode) && (ret == 0) && offline) {
+               result |= FILE_ATTRIBUTE_OFFLINE;
        }
 
        /* Optimization : Only call is_hidden_path if it's not already
@@ -432,7 +419,7 @@ int file_set_dosmode(connection_struct *conn, const char *fname,
        int mask=0;
        mode_t tmp;
        mode_t unixmode;
-       int ret = -1;
+       int ret = -1, lret = -1;
 
        /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
        dosmode &= SAMBA_ATTRIBUTES_MASK;
@@ -505,10 +492,21 @@ int file_set_dosmode(connection_struct *conn, const char *fname,
                unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
        }
 
-       if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) {
-               if (!newfile) {
+       if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
+               lret = SMB_VFS_SET_OFFLINE(conn, fname);
+               if (lret == -1) {
+                       DEBUG(0, ("set_dos_mode: client has asked to set "
+                                 "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
+                                 "an error while setting it or it is not supported.\n", 
+                                 parent_dir, fname));
+               }
+       }
+
+       ret = SMB_VFS_CHMOD(conn, fname, unixmode);
+       if (ret == 0) {
+               if(!newfile || (lret != -1)) {
                        notify_fname(conn, NOTIFY_ACTION_MODIFIED,
-                               FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
+                                    FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
                }
                st->st_mode = unixmode;
                return 0;
index 05021b6c74e30f039a19bf33c80055271050a64a..fa7cb42bc6d9ca4bcb17e617ad0a35ef01fa755e 100644 (file)
@@ -93,17 +93,27 @@ static void set_capability(unsigned capability)
                return;
        }
 
-       data.effective |= (1<<capability);
+       if (0 == (data.effective & (1<<capability))) {
+               data.effective |= (1<<capability);
 
-       if (capset(&header, &data) == -1) {
-               DEBUG(3,("Unable to set %d capability (%s)\n", 
-                        capability, strerror(errno)));
+               if (capset(&header, &data) == -1) {
+                       DEBUG(3,("Unable to set %d capability (%s)\n", 
+                                capability, strerror(errno)));
+               }
        }
 }
 
 /*
- Call to set the kernel lease signal handler
-*/
+ * public function to get linux lease capability. Needed by some VFS modules (eg. gpfs.c)
+ */
+void linux_set_lease_capability(void)
+{
+       set_capability(CAP_LEASE);
+}
+
+/* 
+ * Call to set the kernel lease signal handler
+ */
 int linux_set_lease_sighandler(int fd)
 {
         if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
index 5f18615f6651e731219efff2cd65e0c2d80f6d22..6cec39f9c0103b96d62c87da42035664af47cc83 100644 (file)
@@ -3413,6 +3413,9 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
        bool acl_perms = False;
        mode_t orig_mode = (mode_t)0;
        NTSTATUS status;
+       uid_t orig_uid;
+       gid_t orig_gid;
+       bool need_chown = False;
 
        DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
 
@@ -3435,6 +3438,8 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
 
        /* Save the original elements we check against. */
        orig_mode = sbuf.st_mode;
+       orig_uid = sbuf.st_uid;
+       orig_gid = sbuf.st_gid;
 
        /*
         * Unpack the user/group/world id's.
@@ -3449,7 +3454,11 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
         * Do we need to chown ?
         */
 
-       if (((user != (uid_t)-1) && (sbuf.st_uid != user)) || (( grp != (gid_t)-1) && (sbuf.st_gid != grp))) {
+       if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp))) {
+               need_chown = True;
+       }
+
+       if (need_chown && (user == (uid_t)-1 || user == current_user.ut.uid)) {
 
                DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
                                fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
@@ -3487,6 +3496,11 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
 
                /* Save the original elements we check against. */
                orig_mode = sbuf.st_mode;
+               orig_uid = sbuf.st_uid;
+               orig_gid = sbuf.st_gid;
+
+               /* We did chown already, drop the flag */
+               need_chown = False;
        }
 
        create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
@@ -3630,6 +3644,21 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                free_canon_ace_list(dir_ace_list); 
        }
 
+       /* Any chown pending? */
+       if (need_chown) {
+               DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
+                        fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
+               
+               if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
+                       DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
+                                fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
+                       if (errno == EPERM) {
+                               return NT_STATUS_INVALID_OWNER;
+                       }
+                       return map_nt_error_from_unix(errno);
+               }
+       }
+       
        return NT_STATUS_OK;
 }
 
index e2316ef12008fce523c00dcadfdbc55aad4ef193..381ddfe1517b4f83d8030af1715127a86cd6394e 100644 (file)
@@ -3329,8 +3329,12 @@ void reply_read_and_X(struct smb_request *req)
                return;
        }
 
-       if (!big_readX
-           && schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) {
+       /* It is possible for VFS modules to selectively decide whether Async I/O should be used
+          for the file or not.
+        */
+       if ((SMB_VFS_AIO_FORCE(fsp)) &&
+           !big_readX &&
+           schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) {
                END_PROFILE(SMBreadX);
                return;
        }
@@ -4001,13 +4005,16 @@ void reply_write_and_X(struct smb_request *req)
                nwritten = 0;
        } else {
 
-               if (req->unread_bytes == 0 &&
-                               schedule_aio_write_and_X(conn, req, fsp, data,
-                                                       startpos, numtowrite)) {
+               /* It is possible for VFS modules to selectively decide whether Async I/O
+                  should be used for the file or not.
+               */
+               if ((SMB_VFS_AIO_FORCE(fsp)) && (req->unread_bytes == 0) &&
+                   schedule_aio_write_and_X(conn, req, fsp, data, startpos,
+                                            numtowrite)) {
                        END_PROFILE(SMBwriteX);
                        return;
                }
-
+               
                nwritten = write_file(req,fsp,data,startpos,numtowrite);
        }
 
index 8371d17f104fa0bb55557f6024ab04cd03829a66..db241103ed7a0923acd1df3f500e12319c2e0b39 100644 (file)
@@ -268,10 +268,20 @@ static void add_child_pid(pid_t pid)
        num_children += 1;
 }
 
-static void remove_child_pid(pid_t pid)
+static void remove_child_pid(pid_t pid, bool unclean_shutdown)
 {
        struct child_pid *child;
 
+       if (unclean_shutdown) {
+               /* a child terminated uncleanly so tickle all processes to see 
+                  if they can grab any of the pending locks
+               */
+               messaging_send_buf(smbd_messaging_context(), procid_self(), 
+                                  MSG_SMB_BRL_VALIDATE, NULL, 0);
+               message_send_all(smbd_messaging_context(), 
+                                MSG_SMB_UNLOCK, NULL, 0, NULL);
+       }
+
        if (lp_max_smbd_processes() == 0) {
                /* Don't bother with the child list if we don't care anyway */
                return;
@@ -560,10 +570,27 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
 
                if (got_sig_cld) {
                        pid_t pid;
+                       int status;
+
                        got_sig_cld = False;
 
-                       while ((pid = sys_waitpid(-1, NULL, WNOHANG)) > 0) {
-                               remove_child_pid(pid);
+                       while ((pid = sys_waitpid(-1, &status, WNOHANG)) > 0) {
+                               bool unclean_shutdown = False;
+                               
+                               /* If the child terminated normally, assume
+                                  it was an unclean shutdown unless the
+                                  status is 0 
+                               */
+                               if (WIFEXITED(status)) {
+                                       unclean_shutdown = WEXITSTATUS(status);
+                               }
+                               /* If the child terminated due to a signal
+                                  we always assume it was unclean.
+                               */
+                               if (WIFSIGNALED(status)) {
+                                       unclean_shutdown = True;
+                               }
+                               remove_child_pid(pid, unclean_shutdown);
                        }
                }
 
@@ -603,6 +630,15 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_
 
                        continue;
                }
+               
+
+               /* If the idle timeout fired and we don't have any connected
+                * users, exit gracefully. We should be running under a process
+                * controller that will restart us if necessry.
+                */
+               if (num == 0 && count_all_current_connections() == 0) {
+                       exit_server_cleanly("idle timeout");
+               }
 
                /* process pending nDNS responses */
                if (dns_register_smbd_reply(dns_reg, &r_fds, &idle_timeout)) {
@@ -906,6 +942,29 @@ void exit_server_fault(void)
        exit_server("critical server fault");
 }
 
+
+/****************************************************************************
+received when we should release a specific IP
+****************************************************************************/
+static void msg_release_ip(struct messaging_context *msg_ctx, void *private_data, 
+                          uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
+{
+       const char *ip = (const char *)data->data;
+       char addr[INET6_ADDRSTRLEN];
+
+       if (strcmp(client_socket_addr(get_client_fd(),addr,sizeof(addr)), ip) == 0) {
+               /* we can't afford to do a clean exit - that involves
+                  database writes, which would potentially mean we
+                  are still running after the failover has finished -
+                  we have to get rid of this process ID straight
+                  away */
+               DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
+                       ip));
+               _exit(0);
+       }
+}
+
+
 /****************************************************************************
  Initialise connect, service and file structs.
 ****************************************************************************/
@@ -1305,6 +1364,8 @@ extern void build_options(bool screen);
        /* register our message handlers */
        messaging_register(smbd_messaging_context(), NULL,
                           MSG_SMB_FORCE_TDIS, msg_force_tdis);
+       messaging_register(smbd_messaging_context(), NULL,
+                          MSG_SMB_RELEASE_IP, msg_release_ip);
 
        if ((lp_keepalive() != 0)
            && !(event_add_idle(smbd_event_context(), NULL,
index bf6802f2a6c7e1a217daec4d07aa86efd4a2a01f..5729ab534997850fd69a3d7c7cdd3b660a6d35f3 100644 (file)
@@ -2373,8 +2373,8 @@ static void call_trans2qfsinfo(connection_struct *conn,
        const char *vname = volume_label(SNUM(conn));
        int snum = SNUM(conn);
        char *fstype = lp_fstype(SNUM(conn));
-       int quota_flag = 0;
-
+       uint32 additional_flags = 0;
+       
        if (total_params < 2) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
                return;
@@ -2487,16 +2487,23 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_dev, (unsi
                case SMB_QUERY_FS_ATTRIBUTE_INFO:
                case SMB_FS_ATTRIBUTE_INFORMATION:
 
-
+                       additional_flags = 0;
 #if defined(HAVE_SYS_QUOTAS)
-                       quota_flag = FILE_VOLUME_QUOTAS;
+                       additional_flags |= FILE_VOLUME_QUOTAS;
 #endif
 
+                       if(lp_nt_acl_support(SNUM(conn))) {
+                               additional_flags |= FILE_PERSISTENT_ACLS;
+                       }
+                       
+                       if(SMB_VFS_IS_REMOTESTORAGE(conn, lp_pathname(SNUM(conn)))) {
+                               additional_flags |= FILE_SUPPORTS_REMOTE_STORAGE;
+                               additional_flags |= FILE_SUPPORTS_REPARSE_POINTS;
+                       }
+                       
                        SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH|
-                               (lp_nt_acl_support(SNUM(conn)) ? FILE_PERSISTENT_ACLS : 0)|
-                               FILE_SUPPORTS_OBJECT_IDS|
-                               FILE_UNICODE_ON_DISK|
-                               quota_flag); /* FS ATTRIBUTES */
+                               FILE_SUPPORTS_OBJECT_IDS|FILE_UNICODE_ON_DISK|
+                               additional_flags); /* FS ATTRIBUTES */
 
                        SIVAL(pdata,4,255); /* Max filename component length */
                        /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it
diff --git a/source3/winbindd/idmap_tdb2.c b/source3/winbindd/idmap_tdb2.c
new file mode 100644 (file)
index 0000000..ab89e61
--- /dev/null
@@ -0,0 +1,1017 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   idmap TDB2 backend, used for clustered Samba setups.
+
+   This uses 2 tdb files. One is permanent, and is in shared storage
+   on the cluster (using "tdb:idmap2.tdb =" in smb.conf). The other is a
+   temporary cache tdb on local storage.
+
+   Copyright (C) Andrew Tridgell 2007
+
+   This is heavily based upon idmap_tdb.c, which is:
+
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+   Copyright (C) Jeremy Allison 2006
+   Copyright (C) Simo Sorce 2003-2006
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/* High water mark keys */
+#define HWM_GROUP  "GROUP HWM"
+#define HWM_USER   "USER HWM"
+
+static struct idmap_tdb2_state {
+       /* User and group id pool */
+       uid_t low_uid, high_uid;               /* Range of uids to allocate */
+       gid_t low_gid, high_gid;               /* Range of gids to allocate */
+       const char *idmap_script;
+} idmap_tdb2_state;
+
+
+
+/* tdb context for the local cache tdb */
+static TDB_CONTEXT *idmap_tdb2_tmp;
+
+/* handle to the permanent tdb */
+static struct db_context *idmap_tdb2_perm;
+
+/*
+  open the cache tdb
+ */
+static NTSTATUS idmap_tdb2_open_cache_db(void)
+{
+       const char *db_path;
+
+       if (idmap_tdb2_tmp) {
+               /* its already open */
+               return NT_STATUS_OK;
+       }
+
+       db_path = lock_path("idmap2_cache.tdb");
+
+       /* Open idmap repository */
+       if (!(idmap_tdb2_tmp = tdb_open_log(db_path, 0, TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0644))) {
+               DEBUG(0, ("Unable to open cache idmap database '%s'\n", db_path));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS idmap_tdb2_alloc_load(void);
+
+/*
+  open the permanent tdb
+ */
+static NTSTATUS idmap_tdb2_open_perm_db(void)
+{
+       char *db_path;
+       
+       if (idmap_tdb2_perm) {
+               /* its already open */
+               return NT_STATUS_OK;
+       }
+
+       db_path = lp_parm_talloc_string(-1, "tdb", "idmap2.tdb", NULL);
+       if (db_path == NULL) {
+               /* fall back to the private directory, which, despite
+                  its name, is usually on shared storage */
+               db_path = talloc_asprintf(NULL, "%s/idmap2.tdb", lp_private_dir());
+       }
+       NT_STATUS_HAVE_NO_MEMORY(db_path);
+
+       /* Open idmap repository */
+       idmap_tdb2_perm = db_open(NULL, db_path, 0, TDB_DEFAULT,
+                                 O_RDWR|O_CREAT, 0644);
+       TALLOC_FREE(db_path);
+
+       if (idmap_tdb2_perm == NULL) {
+               DEBUG(0, ("Unable to open permanent idmap database '%s'\n",
+                         db_path));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* load the ranges and high/low water marks */
+       return idmap_tdb2_alloc_load();
+}
+
+
+/*
+  load the idmap allocation ranges and high/low water marks
+*/
+static NTSTATUS idmap_tdb2_alloc_load(void)
+{
+       const char *range;
+       uid_t low_uid = 0;
+       uid_t high_uid = 0;
+       gid_t low_gid = 0;
+       gid_t high_gid = 0;
+
+       /* load ranges */
+       idmap_tdb2_state.low_uid = 0;
+       idmap_tdb2_state.high_uid = 0;
+       idmap_tdb2_state.low_gid = 0;
+       idmap_tdb2_state.high_gid = 0;
+
+       /* see if a idmap script is configured */
+       idmap_tdb2_state.idmap_script = lp_parm_const_string(-1, "idmap", "script", NULL);
+
+       if (idmap_tdb2_state.idmap_script) {
+               DEBUG(1, ("using idmap script '%s'\n", idmap_tdb2_state.idmap_script));
+       }
+
+       range = lp_parm_const_string(-1, "idmap alloc config", "range", NULL);
+       if (range && range[0]) {
+               unsigned low_id, high_id;
+               if (sscanf(range, "%u - %u", &low_id, &high_id) == 2) {
+                       if (low_id < high_id) {
+                               idmap_tdb2_state.low_gid = idmap_tdb2_state.low_uid = low_id;
+                               idmap_tdb2_state.high_gid = idmap_tdb2_state.high_uid = high_id;
+                       } else {
+                               DEBUG(1, ("ERROR: invalid idmap alloc range [%s]", range));
+                       }
+               } else {
+                       DEBUG(1, ("ERROR: invalid syntax for idmap alloc config:range [%s]", range));
+               }
+       }
+
+       /* Create high water marks for group and user id */
+       if (lp_idmap_uid(&low_uid, &high_uid)) {
+               idmap_tdb2_state.low_uid = low_uid;
+               idmap_tdb2_state.high_uid = high_uid;
+       }
+
+       if (lp_idmap_gid(&low_gid, &high_gid)) {
+               idmap_tdb2_state.low_gid = low_gid;
+               idmap_tdb2_state.high_gid = high_gid;
+       }
+
+       if (idmap_tdb2_state.high_uid <= idmap_tdb2_state.low_uid) {
+               DEBUG(1, ("idmap uid range missing or invalid\n"));
+               DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       } else {
+               uint32 low_id;
+
+               if (((low_id = dbwrap_fetch_int32(idmap_tdb2_perm,
+                                                 HWM_USER)) == -1) ||
+                   (low_id < idmap_tdb2_state.low_uid)) {
+                       if (dbwrap_store_int32(
+                                   idmap_tdb2_perm, HWM_USER,
+                                   idmap_tdb2_state.low_uid) == -1) {
+                               DEBUG(0, ("Unable to initialise user hwm in idmap database\n"));
+                               return NT_STATUS_INTERNAL_DB_ERROR;
+                       }
+               }
+       }
+
+       if (idmap_tdb2_state.high_gid <= idmap_tdb2_state.low_gid) {
+               DEBUG(1, ("idmap gid range missing or invalid\n"));
+               DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       } else {
+               uint32 low_id;
+
+               if (((low_id = dbwrap_fetch_int32(idmap_tdb2_perm,
+                                                 HWM_GROUP)) == -1) ||
+                   (low_id < idmap_tdb2_state.low_gid)) {
+                       if (dbwrap_store_int32(
+                                   idmap_tdb2_perm, HWM_GROUP,
+                                   idmap_tdb2_state.low_gid) == -1) {
+                               DEBUG(0, ("Unable to initialise group hwm in idmap database\n"));
+                               return NT_STATUS_INTERNAL_DB_ERROR;
+                       }
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  Initialise idmap alloc database. 
+*/
+static NTSTATUS idmap_tdb2_alloc_init(const char *params)
+{
+       /* nothing to do - we want to avoid opening the permanent
+          database if possible. Instead we load the params when we
+          first need it. */
+       return NT_STATUS_OK;
+}
+
+
+/*
+  Allocate a new id. 
+*/
+static NTSTATUS idmap_tdb2_allocate_id(struct unixid *xid)
+{
+       bool ret;
+       const char *hwmkey;
+       const char *hwmtype;
+       uint32_t high_hwm;
+       uint32_t hwm;
+       NTSTATUS status;
+
+       status = idmap_tdb2_open_perm_db();
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       /* Get current high water mark */
+       switch (xid->type) {
+
+       case ID_TYPE_UID:
+               hwmkey = HWM_USER;
+               hwmtype = "UID";
+               high_hwm = idmap_tdb2_state.high_uid;
+               break;
+
+       case ID_TYPE_GID:
+               hwmkey = HWM_GROUP;
+               hwmtype = "GID";
+               high_hwm = idmap_tdb2_state.high_gid;
+               break;
+
+       default:
+               DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if ((hwm = dbwrap_fetch_int32(idmap_tdb2_perm, hwmkey)) == -1) {
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       /* check it is in the range */
+       if (hwm > high_hwm) {
+               DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", 
+                         hwmtype, (unsigned long)high_hwm));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* fetch a new id and increment it */
+       ret = dbwrap_change_uint32_atomic(idmap_tdb2_perm, hwmkey, &hwm, 1);
+       if (ret == -1) {
+               DEBUG(1, ("Fatal error while fetching a new %s value\n!", hwmtype));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* recheck it is in the range */
+       if (hwm > high_hwm) {
+               DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", 
+                         hwmtype, (unsigned long)high_hwm));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       xid->id = hwm;
+       DEBUG(10,("New %s = %d\n", hwmtype, hwm));
+
+       return NT_STATUS_OK;
+}
+
+/*
+  Get current highest id. 
+*/
+static NTSTATUS idmap_tdb2_get_hwm(struct unixid *xid)
+{
+       const char *hwmkey;
+       const char *hwmtype;
+       uint32_t hwm;
+       uint32_t high_hwm;
+
+       /* Get current high water mark */
+       switch (xid->type) {
+
+       case ID_TYPE_UID:
+               hwmkey = HWM_USER;
+               hwmtype = "UID";
+               high_hwm = idmap_tdb2_state.high_uid;
+               break;
+
+       case ID_TYPE_GID:
+               hwmkey = HWM_GROUP;
+               hwmtype = "GID";
+               high_hwm = idmap_tdb2_state.high_gid;
+               break;
+
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if ((hwm = dbwrap_fetch_int32(idmap_tdb2_perm, hwmkey)) == -1) {
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       xid->id = hwm;
+
+       /* Warn if it is out of range */
+       if (hwm >= high_hwm) {
+               DEBUG(0, ("Warning: %s range full!! (max: %lu)\n", 
+                         hwmtype, (unsigned long)high_hwm));
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  Set high id. 
+*/
+static NTSTATUS idmap_tdb2_set_hwm(struct unixid *xid)
+{
+       /* not supported, or we would invalidate the cache tdb on
+          other nodes */
+       DEBUG(0,("idmap_tdb2_set_hwm not supported\n"));
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  Close the alloc tdb 
+*/
+static NTSTATUS idmap_tdb2_alloc_close(void)
+{
+       /* don't actually close it */
+       return NT_STATUS_OK;
+}
+
+/*
+  IDMAP MAPPING TDB BACKEND
+*/
+struct idmap_tdb2_context {
+       uint32_t filter_low_id;
+       uint32_t filter_high_id;
+};
+
+/*
+  try fetching from the cache tdb, and if that fails then
+  fetch from the permanent tdb
+ */
+static TDB_DATA tdb2_fetch_bystring(TALLOC_CTX *mem_ctx, const char *keystr)
+{
+       TDB_DATA ret;
+       NTSTATUS status;
+
+       ret = tdb_fetch_bystring(idmap_tdb2_tmp, keystr);
+       if (ret.dptr != NULL) {
+               /* got it from cache */
+               unsigned char *tmp;
+
+               tmp = (unsigned char *)talloc_memdup(mem_ctx, ret.dptr,
+                                                    ret.dsize);
+               SAFE_FREE(ret.dptr);
+               ret.dptr = tmp;
+
+               if (ret.dptr == NULL) {
+                       return make_tdb_data(NULL, 0);
+               }
+               return ret;
+       }
+       
+       status = idmap_tdb2_open_perm_db();
+       if (!NT_STATUS_IS_OK(status)) {
+               return ret;
+       }
+
+       /* fetch from the permanent tdb */
+       return dbwrap_fetch_bystring(idmap_tdb2_perm, mem_ctx, keystr);
+}
+
+/*
+  store into both databases
+ */
+static NTSTATUS tdb2_store_bystring(const char *keystr, TDB_DATA data, int flags)
+{
+       NTSTATUS ret;
+       NTSTATUS status = idmap_tdb2_open_perm_db();
+       if (!NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       ret = dbwrap_store_bystring(idmap_tdb2_perm, keystr, data, flags);
+       if (!NT_STATUS_IS_OK(ret)) {
+               ret = tdb_store_bystring(idmap_tdb2_tmp, keystr, data, flags) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+       }
+       return ret;
+}
+
+/*
+  delete from both databases
+ */
+static NTSTATUS tdb2_delete_bystring(const char *keystr)
+{
+       NTSTATUS ret;
+       NTSTATUS status = idmap_tdb2_open_perm_db();
+       if (!NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       ret = dbwrap_delete_bystring(idmap_tdb2_perm, keystr);
+       if (!NT_STATUS_IS_OK(ret)) {
+               ret = tdb_delete_bystring(idmap_tdb2_tmp, keystr)  ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+       }
+       return ret;
+}
+
+/*
+  Initialise idmap database. 
+*/
+static NTSTATUS idmap_tdb2_db_init(struct idmap_domain *dom)
+{
+       NTSTATUS ret;
+       struct idmap_tdb2_context *ctx;
+       char *config_option = NULL;
+       const char *range;
+       NTSTATUS status;
+
+       status = idmap_tdb2_open_cache_db();
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       ctx = talloc(dom, struct idmap_tdb2_context);
+       if ( ! ctx) {
+               DEBUG(0, ("Out of memory!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
+       if ( ! config_option) {
+               DEBUG(0, ("Out of memory!\n"));
+               ret = NT_STATUS_NO_MEMORY;
+               goto failed;
+       }
+
+       range = lp_parm_const_string(-1, config_option, "range", NULL);
+       if (( ! range) ||
+           (sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) ||
+           (ctx->filter_low_id > ctx->filter_high_id)) {
+               ctx->filter_low_id = 0;
+               ctx->filter_high_id = 0;
+       }
+
+       dom->private_data = ctx;
+       dom->initialized = True;
+
+       talloc_free(config_option);
+       return NT_STATUS_OK;
+
+failed:
+       talloc_free(ctx);
+       return ret;
+}
+
+
+/*
+  run a script to perform a mapping
+
+  The script should the following command lines:
+
+      SIDTOID S-1-xxxx
+      IDTOSID UID xxxx
+      IDTOSID GID xxxx
+
+  and should return one of the following as a single line of text
+     UID:xxxx
+     GID:xxxx
+     SID:xxxx
+     ERR:xxxx
+ */
+static NTSTATUS idmap_tdb2_script(struct idmap_tdb2_context *ctx, struct id_map *map,
+                                 const char *fmt, ...)
+{
+       va_list ap;
+       char *cmd;
+       FILE *p;
+       char line[64];
+       unsigned long v;
+
+       cmd = talloc_asprintf(ctx, "%s ", idmap_tdb2_state.idmap_script);
+       NT_STATUS_HAVE_NO_MEMORY(cmd);  
+
+       va_start(ap, fmt);
+       cmd = talloc_vasprintf_append(cmd, fmt, ap);
+       va_end(ap);
+       NT_STATUS_HAVE_NO_MEMORY(cmd);
+
+       p = popen(cmd, "r");
+       talloc_free(cmd);
+       if (p == NULL) {
+               return NT_STATUS_NONE_MAPPED;
+       }
+
+       if (fgets(line, sizeof(line)-1, p) == NULL) {
+               pclose(p);
+               return NT_STATUS_NONE_MAPPED;
+       }
+       pclose(p);
+
+       DEBUG(10,("idmap script gave: %s\n", line));
+
+       if (sscanf(line, "UID:%lu", &v) == 1) {
+               map->xid.id   = v;
+               map->xid.type = ID_TYPE_UID;
+       } else if (sscanf(line, "GID:%lu", &v) == 1) {
+               map->xid.id   = v;
+               map->xid.type = ID_TYPE_GID;            
+       } else if (strncmp(line, "SID:S-", 6) == 0) {
+               if (!string_to_sid(map->sid, &line[4])) {
+                       DEBUG(0,("Bad SID in '%s' from idmap script %s\n",
+                                line, idmap_tdb2_state.idmap_script));
+                       return NT_STATUS_NONE_MAPPED;                   
+               }
+       } else {
+               DEBUG(0,("Bad reply '%s' from idmap script %s\n",
+                        line, idmap_tdb2_state.idmap_script));
+               return NT_STATUS_NONE_MAPPED;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+
+/*
+  Single id to sid lookup function. 
+*/
+static NTSTATUS idmap_tdb2_id_to_sid(struct idmap_tdb2_context *ctx, struct id_map *map)
+{
+       NTSTATUS ret;
+       TDB_DATA data;
+       char *keystr;
+
+       if (!ctx || !map) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* apply filters before checking */
+       if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) ||
+           (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) {
+               DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
+                               map->xid.id, ctx->filter_low_id, ctx->filter_high_id));
+               return NT_STATUS_NONE_MAPPED;
+       }
+
+       switch (map->xid.type) {
+
+       case ID_TYPE_UID:
+               keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
+               break;
+               
+       case ID_TYPE_GID:
+               keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
+               break;
+
+       default:
+               DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* final SAFE_FREE safe */
+       data.dptr = NULL;
+
+       if (keystr == NULL) {
+               DEBUG(0, ("Out of memory!\n"));
+               ret = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       DEBUG(10,("Fetching record %s\n", keystr));
+
+       /* Check if the mapping exists */
+       data = tdb2_fetch_bystring(keystr, keystr);
+
+       if (!data.dptr) {
+               fstring sidstr;
+
+               DEBUG(10,("Record %s not found\n", keystr));
+               if (idmap_tdb2_state.idmap_script == NULL) {
+                       ret = NT_STATUS_NONE_MAPPED;
+                       goto done;
+               }
+
+               ret = idmap_tdb2_script(ctx, map, "IDTOSID %s", keystr);
+
+               /* store it on shared storage */
+               if (!NT_STATUS_IS_OK(ret)) {
+                       goto done;
+               }
+
+               if (sid_to_fstring(sidstr, map->sid)) {
+                       /* both forward and reverse mappings */
+                       tdb2_store_bystring(keystr,
+                                           string_term_tdb_data(sidstr), 
+                                           TDB_REPLACE);
+                       tdb2_store_bystring(sidstr,
+                                           string_term_tdb_data(keystr), 
+                                           TDB_REPLACE);
+               }
+               goto done;
+       }
+               
+       if (!string_to_sid(map->sid, (const char *)data.dptr)) {
+               DEBUG(10,("INVALID SID (%s) in record %s\n",
+                       (const char *)data.dptr, keystr));
+               ret = NT_STATUS_INTERNAL_DB_ERROR;
+               goto done;
+       }
+
+       DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr));
+       ret = NT_STATUS_OK;
+
+done:
+       talloc_free(keystr);
+       return ret;
+}
+
+
+/*
+ Single sid to id lookup function. 
+*/
+static NTSTATUS idmap_tdb2_sid_to_id(struct idmap_tdb2_context *ctx, struct id_map *map)
+{
+       NTSTATUS ret;
+       TDB_DATA data;
+       char *keystr;
+       unsigned long rec_id = 0;
+
+       if ((keystr = sid_string_talloc(ctx, map->sid)) == NULL) {
+               DEBUG(0, ("Out of memory!\n"));
+               ret = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       DEBUG(10,("Fetching record %s\n", keystr));
+
+       /* Check if sid is present in database */
+       data = tdb2_fetch_bystring(keystr, keystr);
+       if (!data.dptr) {
+               fstring idstr;
+
+               DEBUG(10,(__location__ " Record %s not found\n", keystr));
+
+               if (idmap_tdb2_state.idmap_script == NULL) {
+                       ret = NT_STATUS_NONE_MAPPED;
+                       goto done;
+               }
+                       
+               ret = idmap_tdb2_script(ctx, map, "SIDTOID %s", keystr);
+               /* store it on shared storage */
+               if (!NT_STATUS_IS_OK(ret)) {
+                       goto done;
+               }
+
+               snprintf(idstr, sizeof(idstr), "%cID %lu", 
+                        map->xid.type == ID_TYPE_UID?'U':'G',
+                        (unsigned long)map->xid.id);
+               /* store both forward and reverse mappings */
+               tdb2_store_bystring(keystr, string_term_tdb_data(idstr),
+                                   TDB_REPLACE);
+               tdb2_store_bystring(idstr, string_term_tdb_data(keystr),
+                                   TDB_REPLACE);
+               goto done;
+       }
+
+       /* What type of record is this ? */
+       if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
+               map->xid.id = rec_id;
+               map->xid.type = ID_TYPE_UID;
+               DEBUG(10,("Found uid record %s -> %s \n", keystr, (const char *)data.dptr ));
+               ret = NT_STATUS_OK;
+
+       } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
+               map->xid.id = rec_id;
+               map->xid.type = ID_TYPE_GID;
+               DEBUG(10,("Found gid record %s -> %s \n", keystr, (const char *)data.dptr ));
+               ret = NT_STATUS_OK;
+
+       } else { /* Unknown record type ! */
+               DEBUG(2, ("Found INVALID record %s -> %s\n", keystr, (const char *)data.dptr));
+               ret = NT_STATUS_INTERNAL_DB_ERROR;
+       }
+       
+       /* apply filters before returning result */
+       if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) ||
+           (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) {
+               DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
+                               map->xid.id, ctx->filter_low_id, ctx->filter_high_id));
+               ret = NT_STATUS_NONE_MAPPED;
+       }
+
+done:
+       talloc_free(keystr);
+       return ret;
+}
+
+/*
+  lookup a set of unix ids. 
+*/
+static NTSTATUS idmap_tdb2_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
+{
+       struct idmap_tdb2_context *ctx;
+       NTSTATUS ret;
+       int i;
+
+       /* make sure we initialized */
+       if ( ! dom->initialized) {
+               ret = idmap_tdb2_db_init(dom);
+               if ( ! NT_STATUS_IS_OK(ret)) {
+                       return ret;
+               }
+       }
+
+       ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
+
+       for (i = 0; ids[i]; i++) {
+               ret = idmap_tdb2_id_to_sid(ctx, ids[i]);
+               if ( ! NT_STATUS_IS_OK(ret)) {
+
+                       /* if it is just a failed mapping continue */
+                       if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
+
+                               /* make sure it is marked as unmapped */
+                               ids[i]->status = ID_UNMAPPED;
+                               continue;
+                       }
+                       
+                       /* some fatal error occurred, return immediately */
+                       goto done;
+               }
+
+               /* all ok, id is mapped */
+               ids[i]->status = ID_MAPPED;
+       }
+
+       ret = NT_STATUS_OK;
+
+done:
+       return ret;
+}
+
+/*
+  lookup a set of sids. 
+*/
+static NTSTATUS idmap_tdb2_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
+{
+       struct idmap_tdb2_context *ctx;
+       NTSTATUS ret;
+       int i;
+
+       /* make sure we initialized */
+       if ( ! dom->initialized) {
+               ret = idmap_tdb2_db_init(dom);
+               if ( ! NT_STATUS_IS_OK(ret)) {
+                       return ret;
+               }
+       }
+
+       ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
+
+       for (i = 0; ids[i]; i++) {
+               ret = idmap_tdb2_sid_to_id(ctx, ids[i]);
+               if ( ! NT_STATUS_IS_OK(ret)) {
+
+                       /* if it is just a failed mapping continue */
+                       if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
+
+                               /* make sure it is marked as unmapped */
+                               ids[i]->status = ID_UNMAPPED;
+                               continue;
+                       }
+                       
+                       /* some fatal error occurred, return immediately */
+                       goto done;
+               }
+
+               /* all ok, id is mapped */
+               ids[i]->status = ID_MAPPED;
+       }
+
+       ret = NT_STATUS_OK;
+
+done:
+       return ret;
+}
+
+
+/*
+  set a mapping. 
+*/
+static NTSTATUS idmap_tdb2_set_mapping(struct idmap_domain *dom, const struct id_map *map)
+{
+       struct idmap_tdb2_context *ctx;
+       NTSTATUS ret;
+       TDB_DATA data;
+       char *ksidstr, *kidstr;
+       struct db_record *update_lock = NULL;
+       struct db_record *rec = NULL;
+
+       /* make sure we initialized */
+       if ( ! dom->initialized) {
+               ret = idmap_tdb2_db_init(dom);
+               if ( ! NT_STATUS_IS_OK(ret)) {
+                       return ret;
+               }
+       }
+
+       if (!map || !map->sid) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       ksidstr = kidstr = NULL;
+       data.dptr = NULL;
+
+       /* TODO: should we filter a set_mapping using low/high filters ? */
+       
+       ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
+
+       switch (map->xid.type) {
+
+       case ID_TYPE_UID:
+               kidstr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
+               break;
+               
+       case ID_TYPE_GID:
+               kidstr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
+               break;
+
+       default:
+               DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (kidstr == NULL) {
+               DEBUG(0, ("ERROR: Out of memory!\n"));
+               ret = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       if (!(ksidstr = sid_string_talloc(ctx, map->sid))) {
+               DEBUG(0, ("Out of memory!\n"));
+               ret = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       DEBUG(10, ("Storing %s <-> %s map\n", ksidstr, kidstr));
+
+       /*
+        * Get us the update lock. This is necessary to get the lock orders
+        * right, we need to deal with two records under a lock.
+        */
+
+       if (!(update_lock = idmap_tdb2_perm->fetch_locked(
+                     idmap_tdb2_perm, ctx,
+                     string_term_tdb_data("UPDATELOCK")))) {
+               DEBUG(10,("Failed to lock record %s\n", ksidstr));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /*
+        * *DELETE* previous mappings if any. *
+        */
+
+       /* First delete indexed on SID */
+
+       if (((rec = idmap_tdb2_perm->fetch_locked(
+                    idmap_tdb2_perm, update_lock,
+                    string_term_tdb_data(ksidstr))) != NULL)
+           && (rec->value.dsize != 0)) {
+               struct db_record *rec2;
+
+               if ((rec2 = idmap_tdb2_perm->fetch_locked(
+                            idmap_tdb2_perm, update_lock, rec->value))
+                   != NULL) {
+                       rec2->delete_rec(rec2);
+                       TALLOC_FREE(rec2);
+               }
+
+               rec->delete_rec(rec);
+
+               tdb_delete(idmap_tdb2_tmp, rec->key);
+               tdb_delete(idmap_tdb2_tmp, rec->value);
+       }
+       TALLOC_FREE(rec);
+
+       /* Now delete indexed on unix ID */
+
+       if (((rec = idmap_tdb2_perm->fetch_locked(
+                    idmap_tdb2_perm, update_lock,
+                    string_term_tdb_data(kidstr))) != NULL)
+           && (rec->value.dsize != 0)) {
+               struct db_record *rec2;
+
+               if ((rec2 = idmap_tdb2_perm->fetch_locked(
+                            idmap_tdb2_perm, update_lock, rec->value))
+                   != NULL) {
+                       rec2->delete_rec(rec2);
+                       TALLOC_FREE(rec2);
+               }
+
+               rec->delete_rec(rec);
+
+               tdb_delete(idmap_tdb2_tmp, rec->key);
+               tdb_delete(idmap_tdb2_tmp, rec->value);
+       }
+       TALLOC_FREE(rec);
+
+       if (!NT_STATUS_IS_OK(tdb2_store_bystring(ksidstr, string_term_tdb_data(kidstr),
+                               TDB_INSERT))) {
+               DEBUG(0, ("Error storing SID -> ID\n"));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+       if (!NT_STATUS_IS_OK(tdb2_store_bystring(kidstr, string_term_tdb_data(ksidstr),
+                               TDB_INSERT))) {
+               DEBUG(0, ("Error storing ID -> SID\n"));
+               /* try to remove the previous stored SID -> ID map */
+               tdb2_delete_bystring(ksidstr);
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       DEBUG(10,("Stored %s <-> %s\n", ksidstr, kidstr));
+       ret = NT_STATUS_OK;
+
+done:
+       talloc_free(ksidstr);
+       talloc_free(kidstr);
+       SAFE_FREE(data.dptr);
+       TALLOC_FREE(update_lock);
+       return ret;
+}
+
+/*
+  remove a mapping. 
+*/
+static NTSTATUS idmap_tdb2_remove_mapping(struct idmap_domain *dom, const struct id_map *map)
+{
+       /* not supported as it would invalidate the cache tdb on other
+          nodes */
+       DEBUG(0,("idmap_tdb2_remove_mapping not supported\n"));
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  Close the idmap tdb instance
+*/
+static NTSTATUS idmap_tdb2_close(struct idmap_domain *dom)
+{
+       /* don't do anything */
+       return NT_STATUS_OK;
+}
+
+
+/*
+  Dump all mappings out
+*/
+static NTSTATUS idmap_tdb2_dump_data(struct idmap_domain *dom, struct id_map **maps, int *num_maps)
+{
+       DEBUG(0,("idmap_tdb2_dump_data not supported\n"));
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+static struct idmap_methods db_methods = {
+       .init            = idmap_tdb2_db_init,
+       .unixids_to_sids = idmap_tdb2_unixids_to_sids,
+       .sids_to_unixids = idmap_tdb2_sids_to_unixids,
+       .set_mapping     = idmap_tdb2_set_mapping,
+       .remove_mapping  = idmap_tdb2_remove_mapping,
+       .dump_data       = idmap_tdb2_dump_data,
+       .close_fn        = idmap_tdb2_close
+};
+
+static struct idmap_alloc_methods db_alloc_methods = {
+       .init        = idmap_tdb2_alloc_init,
+       .allocate_id = idmap_tdb2_allocate_id,
+       .get_id_hwm  = idmap_tdb2_get_hwm,
+       .set_id_hwm  = idmap_tdb2_set_hwm,
+       .close_fn    = idmap_tdb2_alloc_close
+};
+
+NTSTATUS idmap_tdb2_init(void)
+{
+       NTSTATUS ret;
+
+       /* register both backends */
+       ret = smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, "tdb2", &db_alloc_methods);
+       if (! NT_STATUS_IS_OK(ret)) {
+               DEBUG(0, ("Unable to register idmap alloc tdb2 module: %s\n", get_friendly_nt_error_msg(ret)));
+               return ret;
+       }
+
+       return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb2", &db_methods);
+}