mirror of
https://github.com/samba-team/samba.git
synced 2025-01-15 23:24:37 +03:00
Merge branch 'ctdb-merge' into v3-2-test
(This used to be commit 175662b5bbb5c8ecd81a60539f5846a938c26a3a)
This commit is contained in:
commit
02680bd75d
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -138,6 +138,12 @@
|
||||
#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!!!
|
||||
@ -257,6 +263,12 @@
|
||||
#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!!!
|
||||
@ -377,5 +389,11 @@
|
||||
#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 */
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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, '/');
|
||||
|
@ -18,16 +18,14 @@
|
||||
*/
|
||||
|
||||
#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
|
||||
|
@ -17,10 +17,6 @@
|
||||
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))) {
|
||||
|
@ -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:
|
||||
|
90
source3/lib/dbwrap_util.c
Normal file
90
source3/lib/dbwrap_util.c
Normal file
@ -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;
|
||||
}
|
||||
|
@ -22,6 +22,10 @@
|
||||
#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;
|
||||
|
@ -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.
|
||||
********************************************************************/
|
||||
|
@ -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);
|
||||
|
@ -22,9 +22,11 @@
|
||||
#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;
|
||||
|
||||
|
@ -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, ¶ms, sbuf.st_uid, sbuf.st_gid);
|
||||
acl = smbacl4_win2nfs4(fsp->fsp_name, psd->dacl, ¶ms, 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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 }
|
||||
|
||||
|
32
source3/modules/vfs_gpfs.h
Normal file
32
source3/modules/vfs_gpfs.h
Normal file
@ -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);
|
@ -47,11 +47,16 @@
|
||||
#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,
|
||||
|
637
source3/modules/vfs_shadow_copy2.c
Normal file
637
source3/modules/vfs_shadow_copy2.c
Normal file
@ -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;
|
||||
}
|
338
source3/modules/vfs_tsmsm.c
Normal file
338
source3/modules/vfs_tsmsm.c
Normal file
@ -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);
|
||||
}
|
@ -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.
|
||||
****************************************************************************/
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
1017
source3/winbindd/idmap_tdb2.c
Normal file
1017
source3/winbindd/idmap_tdb2.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user