From 40d0707827ee154bcb03013abe6f72f1026a70c9 Mon Sep 17 00:00:00 2001 From: James Peach Date: Wed, 22 Mar 2006 23:49:09 +0000 Subject: r14668: Set the FILE_STATUS_OFFLINE bit by observing the events a DMAPI-based HSM is interested in. Tested on both IRIX and SLES9. (This used to be commit 514a767c57f8194547e5b708ad2573ab9a0719c6) --- source3/Makefile.in | 2 +- source3/configure.in | 17 +- source3/include/debug.h | 1 + source3/include/smb.h | 3 +- source3/include/smb_macros.h | 4 + source3/lib/debug.c | 1 + source3/lib/smbrun.c | 6 +- source3/lib/system.c | 30 +++ source3/param/loadparm.c | 5 + source3/smbd/chgpasswd.c | 3 +- source3/smbd/dmapi.c | 576 +++++++++++++++++++++++++++++++++++++++++++ source3/smbd/dosmode.c | 30 +++ source3/smbd/server.c | 6 + 13 files changed, 677 insertions(+), 7 deletions(-) create mode 100644 source3/smbd/dmapi.c diff --git a/source3/Makefile.in b/source3/Makefile.in index ac67388d6a..d399cb66a5 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -416,7 +416,7 @@ SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \ smbd/change_trust_pw.o smbd/fake_file.o \ smbd/quotas.o smbd/ntquotas.o $(AFS_OBJ) smbd/msdfs.o \ $(AFS_SETTOKEN_OBJ) smbd/aio.o smbd/statvfs.o \ - $(MANGLE_OBJ) @VFS_STATIC@ + smbd/dmapi.o $(MANGLE_OBJ) @VFS_STATIC@ SMBD_OBJ_BASE = $(PARAM_OBJ) $(SMBD_OBJ_SRV) $(LIBSMB_OBJ) \ $(RPC_SERVER_OBJ) $(RPC_PARSE_OBJ) $(SECRETS_OBJ) \ diff --git a/source3/configure.in b/source3/configure.in index 6063ecf0f3..f3fa91e3b4 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -829,6 +829,7 @@ AC_CHECK_HEADERS(sys/termio.h sys/statfs.h sys/dustat.h sys/statvfs.h stdarg.h s AC_CHECK_HEADERS(sys/sysmacros.h security/_pam_macros.h dlfcn.h) AC_CHECK_HEADERS(sys/syslog.h syslog.h execinfo.h) AC_CHECK_HEADERS(langinfo.h locale.h) +AC_CHECK_HEADERS(sys/dmi.h xfs/dmapi.h) AC_CHECK_HEADERS(rpcsvc/yp_prot.h,,,[[ #if HAVE_RPC_RPC_H @@ -2337,6 +2338,17 @@ if test x"$samba_cv_HAVE_LIBFAM" = x"yes" ; then []) fi +################################################# +# Check for DMAPI interfaces in libdm. + +AC_CHECK_LIB(dm, dm_get_eventlist, + [samba_cv_HAVE_LIBDM=yes; samba_dmapi_libs="-ldm"], + [samba_cv_HAVE_LIBDM=no]) + +if test x"$samba_cv_HAVE_LIBDM" = x"yes" ; then + AC_DEFINE(HAVE_LIBDM, 1, [Whether libdm is available]) +fi + AC_CACHE_CHECK([for kernel share modes],samba_cv_HAVE_KERNEL_SHARE_MODES,[ AC_TRY_RUN([ #include @@ -5423,8 +5435,9 @@ AC_TRY_RUN([#include "${srcdir-.}/tests/summary.c"], builddir=`pwd` AC_SUBST(builddir) -# Stuff the FAM libraries at the end of the smbd link path (if we have them). -SMBD_LIBS="$samba_fam_libs" +# Stuff the smbd-only libraries at the end of the smbd link +# path (if we have them). +SMBD_LIBS="$samba_fam_libs $samba_dmapi_libs" AC_SUBST(SMBD_LIBS) dnl Remove -L/usr/lib/? from LDFLAGS and LIBS diff --git a/source3/include/debug.h b/source3/include/debug.h index b6fb50a9ac..2cf1ceaead 100644 --- a/source3/include/debug.h +++ b/source3/include/debug.h @@ -102,6 +102,7 @@ extern int DEBUGLEVEL; #define DBGC_ACLS 15 #define DBGC_LOCKING 16 #define DBGC_MSDFS 17 +#define DBGC_DMAPI 18 /* So you can define DBGC_CLASS before including debug.h */ #ifndef DBGC_CLASS diff --git a/source3/include/smb.h b/source3/include/smb.h index 26b4b69266..8faf3877ce 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -1567,7 +1567,8 @@ minimum length == 18. */ enum smbd_capability { - KERNEL_OPLOCK_CAPABILITY + KERNEL_OPLOCK_CAPABILITY, + DMAPI_ACCESS_CAPABILITY }; /* if a kernel does support oplocks then a structure of the following diff --git a/source3/include/smb_macros.h b/source3/include/smb_macros.h index 3ae8814cfd..554dbbc087 100644 --- a/source3/include/smb_macros.h +++ b/source3/include/smb_macros.h @@ -76,6 +76,10 @@ (DEBUG(0,("PANIC: assert failed at %s(%d)\n", __FILE__, __LINE__)))) #endif +#define SMB_WARN(condition, message) \ + ((condition) ? (void)0 : \ + DEBUG(0, ("WARNING: %s: %s\n", #condition, message))) + #define SMB_ASSERT_ARRAY(a,n) SMB_ASSERT((sizeof(a)/sizeof((a)[0])) >= (n)) /* these are useful macros for checking validity of handles */ diff --git a/source3/lib/debug.c b/source3/lib/debug.c index 29d879adbc..97a147f17d 100644 --- a/source3/lib/debug.c +++ b/source3/lib/debug.c @@ -166,6 +166,7 @@ static const char *default_classname_table[] = { "acls", /* DBGC_ACLS */ "locking", /* DBGC_LOCKING */ "msdfs", /* DBGC_MSDFS */ + "dmapi", /* DBGC_DMAPI */ NULL }; diff --git a/source3/lib/smbrun.c b/source3/lib/smbrun.c index 521b1bf761..4400aeb443 100644 --- a/source3/lib/smbrun.c +++ b/source3/lib/smbrun.c @@ -62,9 +62,10 @@ int smbrun(const char *cmd, int *outfd) gid_t gid = current_user.ut.gid; /* - * Lose any kernel oplock capabilities we may have. + * Lose any elevated privileges. */ drop_effective_capability(KERNEL_OPLOCK_CAPABILITY); + drop_effective_capability(DMAPI_ACCESS_CAPABILITY); /* point our stdout at the file we want output to go into */ @@ -194,9 +195,10 @@ int smbrunsecret(const char *cmd, const char *secret) int ifd[2]; /* - * Lose any kernel oplock capabilities we may have. + * Lose any elevated privileges. */ drop_effective_capability(KERNEL_OPLOCK_CAPABILITY); + drop_effective_capability(DMAPI_ACCESS_CAPABILITY); /* build up an input pipe */ if(pipe(ifd)) { diff --git a/source3/lib/system.c b/source3/lib/system.c index ffb7031715..2e5f42307b 100644 --- a/source3/lib/system.c +++ b/source3/lib/system.c @@ -22,6 +22,10 @@ #include "includes.h" +#ifdef HAVE_SYS_PRCTL_H +#include +#endif + /* The idea is that this file will eventually have wrappers around all important system calls in samba. The aims are: @@ -661,6 +665,19 @@ static BOOL set_process_capability(enum smbd_capability capability, cap_t cap; +#if defined(HAVE_PRCTL) && defined(PR_GET_KEEPCAPS) && defined(PR_SET_KEEPCAPS) + /* On Linux, make sure that any capabilities we grab are sticky + * across UID changes. We expect that this would allow us to keep both + * the effective and permitted capability sets, but as of circa 2.6.16, + * only the permitted set is kept. It is a bug (which we work around) + * that the effective set is lost, but we still require the effective + * set to be kept. + */ + if (!prctl(PR_GET_KEEPCAPS)) { + prctl(PR_SET_KEEPCAPS, 1); + } +#endif + cap = cap_get_proc(); if (cap == NULL) { DEBUG(0,("set_process_capability: cap_get_proc failed: %s\n", @@ -673,6 +690,15 @@ static BOOL set_process_capability(enum smbd_capability capability, #ifdef CAP_NETWORK_MGT /* IRIX has CAP_NETWORK_MGT for oplocks. */ cap_vals[num_cap_vals++] = CAP_NETWORK_MGT; +#endif + break; + case DMAPI_ACCESS_CAPABILITY: +#ifdef CAP_DEVICE_MGT + /* IRIX has CAP_DEVICE_MGT for DMAPI access. */ + cap_vals[num_cap_vals++] = CAP_DEVICE_MGT; +#elif CAP_MKNOD + /* Linux has CAP_MKNOD for DMAPI access. */ + cap_vals[num_cap_vals++] = CAP_MKNOD; #endif break; } @@ -686,6 +712,10 @@ static BOOL set_process_capability(enum smbd_capability capability, cap_set_flag(cap, CAP_EFFECTIVE, num_cap_vals, cap_vals, enable ? CAP_SET : CAP_CLEAR); + + /* We never want to pass capabilities down to our children, so make + * sure they are not inherited. + */ cap_set_flag(cap, CAP_INHERITABLE, num_cap_vals, cap_vals, CAP_CLEAR); if (cap_set_proc(cap) == -1) { diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 0bde5805b0..13f585d8d0 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -410,6 +410,7 @@ typedef struct { BOOL bMap_hidden; BOOL bMap_archive; BOOL bStoreDosAttributes; + BOOL bDmapiSupport; BOOL bLocking; int iStrictLocking; BOOL bPosixLocking; @@ -547,6 +548,7 @@ static service sDefault = { False, /* bMap_hidden */ True, /* bMap_archive */ False, /* bStoreDosAttributes */ + False, /* bDmapiSupport */ True, /* bLocking */ True, /* iStrictLocking */ True, /* bPosixLocking */ @@ -1097,6 +1099,8 @@ static struct parm_struct parm_table[] = { {"max stat cache size", P_INTEGER, P_GLOBAL, &Globals.iMaxStatCacheSize, NULL, NULL, FLAG_ADVANCED}, {"stat cache", P_BOOL, P_GLOBAL, &Globals.bStatCache, NULL, NULL, FLAG_ADVANCED}, {"store dos attributes", P_BOOL, P_LOCAL, &sDefault.bStoreDosAttributes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL}, + {"dmapi support", P_BOOL, P_LOCAL, &sDefault.bDmapiSupport, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL}, + {N_("Domain Options"), P_SEP, P_SEPARATOR}, @@ -2024,6 +2028,7 @@ FN_LOCAL_BOOL(lp_print_ok, bPrint_ok) FN_LOCAL_BOOL(lp_map_hidden, bMap_hidden) FN_LOCAL_BOOL(lp_map_archive, bMap_archive) FN_LOCAL_BOOL(lp_store_dos_attributes, bStoreDosAttributes) +FN_LOCAL_BOOL(lp_dmapi_support, bDmapiSupport) FN_LOCAL_BOOL(lp_locking, bLocking) FN_LOCAL_INTEGER(lp_strict_locking, iStrictLocking) FN_LOCAL_BOOL(lp_posix_locking, bPosixLocking) diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index aef487f4a7..16b44a54bf 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -415,9 +415,10 @@ while we were waiting\n", WTERMSIG(wstat))); /* CHILD */ /* - * Lose any oplock capabilities. + * Lose any elevated privileges. */ drop_effective_capability(KERNEL_OPLOCK_CAPABILITY); + drop_effective_capability(DMAPI_ACCESS_CAPABILITY); /* make sure it doesn't freeze */ alarm(20); diff --git a/source3/smbd/dmapi.c b/source3/smbd/dmapi.c new file mode 100644 index 0000000000..0fa3a16760 --- /dev/null +++ b/source3/smbd/dmapi.c @@ -0,0 +1,576 @@ +/* + Unix SMB/CIFS implementation. + DMAPI Support routines + + Copyright (C) James Peach 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_DMAPI + +#if defined(HAVE_LIBDM) +#if (defined(HAVE_XFS_DMAPI_H) || defined(HAVE_SYS_DMI_H)) +#define USE_DMAPI 1 +#endif +#endif + +#ifndef USE_DMAPI + +int dmapi_init_session(void) { return -1; } +uint32 dmapi_file_flags(const char * const path) { return 0; } +BOOL dmapi_have_session(void) { return False; } + +#else /* USE_DMAPI */ + +#ifdef HAVE_XFS_DMAPI_H +#include +#endif + +#ifdef HAVE_SYS_DMI_H +#include +#endif + +#define DMAPI_SESSION_NAME "samba" +#define DMAPI_TRACE 10 + +static dm_sessid_t 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 + * implementations. + */ +static int init_dmapi_service(void) +{ + static pid_t lastpid; + + pid_t mypid; + + mypid = sys_getpid(); + if (mypid != lastpid) { + char *version; + + lastpid = mypid; + if (dm_init_service(&version) < 0) { + return -1; + } + + DEBUG(0, ("Initializing DMAPI: %s\n", version)); + } + + return 0; +} + +BOOL dmapi_have_session(void) +{ + return dmapi_session != DM_NO_SESSION; +} + +static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count) +{ + dm_sessid_t *nsessions; + + nsessions = TALLOC_REALLOC_ARRAY(NULL, sessions, dm_sessid_t, count); + if (nsessions == NULL) { + TALLOC_FREE(sessions); + return NULL; + } + + return nsessions; +} + +/* Initialise DMAPI session. The session is persistant kernel state, so it + * might already exist, in which case we merely want to reconnect to it. This + * function should be called as root. + */ +int dmapi_init_session(void) +{ + char buf[DM_SESSION_INFO_LEN]; + size_t buflen; + + uint nsessions = 10; + dm_sessid_t *sessions = NULL; + + int i, err; + + /* If we aren't root, something in the following will fail due to lack + * of privileges. Aborting seems a little extreme. + */ + SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root"); + + dmapi_session = DM_NO_SESSION; + if (init_dmapi_service() < 0) { + return -1; + } + +retry: + + if ((sessions = realloc_session_list(sessions, nsessions)) == NULL) { + return -1; + } + + err = dm_getall_sessions(nsessions, sessions, &nsessions); + if (err < 0) { + if (errno == E2BIG) { + nsessions *= 2; + goto retry; + } + + DEBUGADD(DMAPI_TRACE, + ("failed to retrieve DMAPI sessions: %s\n", + strerror(errno))); + TALLOC_FREE(sessions); + return -1; + } + + for (i = 0; i < nsessions; ++i) { + 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]; + DEBUGADD(DMAPI_TRACE, + ("attached to existing DMAPI session " + "named '%s'\n", buf)); + break; + } + } + + TALLOC_FREE(sessions); + + /* No session already defined. */ + if (dmapi_session == DM_NO_SESSION) { + err = dm_create_session(DM_NO_SESSION, DMAPI_SESSION_NAME, + &dmapi_session); + if (err < 0) { + DEBUGADD(DMAPI_TRACE, + ("failed to create new DMAPI session: %s\n", + strerror(errno))); + dmapi_session = DM_NO_SESSION; + return -1; + } + + DEBUGADD(DMAPI_TRACE, + ("created new DMAPI session named '%s'\n", + DMAPI_SESSION_NAME)); + } + + /* Note that we never end the DMAPI session. This enables child + * processes to continue to use the session after we exit. It also lets + * you run a second Samba server on different ports without any + * conflict. + */ + + return 0; +} + +/* Reattach to an existing dmapi session. Called from service processes that + * might not be running as root. + */ +static int reattach_dmapi_session(void) +{ + char buf[DM_SESSION_INFO_LEN]; + size_t buflen; + + if (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; + unbecome_root(); + return -1; + } + + if (dm_query_session(dmapi_session, sizeof(buf), + buf, &buflen) < 0) { + /* Session is stale. Disable DMAPI. */ + dmapi_session = DM_NO_SESSION; + unbecome_root(); + return -1; + } + + set_effective_capability(DMAPI_ACCESS_CAPABILITY); + + DEBUG(DMAPI_TRACE, ("reattached DMAPI session\n")); + unbecome_root(); + } + + return 0; +} + +uint32 dmapi_file_flags(const char * const path) +{ + static int attached = 0; + + int err; + dm_eventset_t events = {0}; + uint nevents; + + void *dm_handle; + size_t dm_handle_len; + + 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; + } + } + + err = dm_path_to_handle(CONST_DISCARD(char *, path), + &dm_handle, &dm_handle_len); + if (err < 0) { + DEBUG(DMAPI_TRACE, ("dm_path_to_handle(%s): %s\n", + path, strerror(errno))); + + if (errno != EPERM) { + return 0; + } + + /* Linux capabilities are broken in that changing our + * user ID will clobber out effective capabilities irrespective + * of whether we have set PR_SET_KEEPCAPS. Fortunately, the + * capabilities are not removed from our permitted set, so we + * can re-acquire them if necessary. + */ + + set_effective_capability(DMAPI_ACCESS_CAPABILITY); + + err = dm_path_to_handle(CONST_DISCARD(char *, path), + &dm_handle, &dm_handle_len); + if (err < 0) { + DEBUG(DMAPI_TRACE, + ("retrying dm_path_to_handle(%s): %s\n", + path, strerror(errno))); + return 0; + } + } + + err = dm_get_eventlist(dmapi_session, dm_handle, dm_handle_len, + DM_NO_TOKEN, DM_EVENT_MAX, &events, &nevents); + if (err < 0) { + DEBUG(DMAPI_TRACE, ("dm_get_eventlist(%s): %s\n", + path, strerror(errno))); + dm_handle_free(dm_handle, dm_handle_len); + return 0; + } + + /* We figure that the only reason a DMAPI application would be + * interested in trapping read events is that part of the file is + * offline. + */ + DEBUG(DMAPI_TRACE, ("DMAPI event list for %s is %#llx\n", + path, events)); + if (DMEV_ISSET(DM_EVENT_READ, events)) { + flags = FILE_ATTRIBUTE_OFFLINE; + } + + dm_handle_free(dm_handle, dm_handle_len); + + if (flags & FILE_ATTRIBUTE_OFFLINE) { + DEBUG(DMAPI_TRACE, ("%s is OFFLINE\n", path)); + } + + return flags; +} + +#endif /* USE_DMAPI */ +/* + Unix SMB/CIFS implementation. + DMAPI Support routines + + Copyright (C) Silicon Graphics, Inc. 2006. All rights reserved. + James Peach + + 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" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_DMAPI + +#if defined(HAVE_LIBDM) +#if (defined(HAVE_XFS_DMAPI_H) || defined(HAVE_SYS_DMI_H)) +#define USE_DMAPI 1 +#endif +#endif + +#ifndef USE_DMAPI + +int dmapi_init_session(void) { return -1; } +uint32 dmapi_file_flags(const char * const path) { return 0; } +BOOL dmapi_have_session(void) { return False; } + +#else /* USE_DMAPI */ + +#ifdef HAVE_XFS_DMAPI_H +#include +#endif + +#ifdef HAVE_SYS_DMI_H +#include +#endif + +#define DMAPI_SESSION_NAME "samba" +#define DMAPI_TRACE 10 + +static dm_sessid_t 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 + * implementations. + */ +static int init_dmapi_service(void) +{ + static pid_t lastpid; + + pid_t mypid; + + mypid = sys_getpid(); + if (mypid != lastpid) { + char *version; + + lastpid = mypid; + if (dm_init_service(&version) < 0) { + return -1; + } + + DEBUG(0, ("Initializing DMAPI: %s\n", version)); + } + + return 0; +} + +BOOL dmapi_have_session(void) +{ + return dmapi_session != DM_NO_SESSION; +} + +static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count) +{ + dm_sessid_t *nsessions; + + nsessions = TALLOC_REALLOC_ARRAY(NULL, sessions, dm_sessid_t, count); + if (nsessions == NULL) { + TALLOC_FREE(sessions); + return NULL; + } + + return nsessions; +} + +/* Initialise DMAPI session. The session is persistant kernel state, so it + * might already exist, in which case we merely want to reconnect to it. This + * function should be called as root. + */ +int dmapi_init_session(void) +{ + char buf[DM_SESSION_INFO_LEN]; + size_t buflen; + + uint nsessions = 10; + dm_sessid_t *sessions = NULL; + + int i, err; + + /* If we aren't root, something in the following will fail due to lack + * of privileges. Aborting seems a little extreme. + */ + SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root"); + + dmapi_session = DM_NO_SESSION; + if (init_dmapi_service() < 0) { + return -1; + } + +retry: + + if ((sessions = realloc_session_list(sessions, nsessions)) == NULL) { + return -1; + } + + err = dm_getall_sessions(nsessions, sessions, &nsessions); + if (err < 0) { + if (errno == E2BIG) { + nsessions *= 2; + goto retry; + } + + DEBUGADD(DMAPI_TRACE, + ("failed to retrieve DMAPI sessions: %s\n", + strerror(errno))); + TALLOC_FREE(sessions); + return -1; + } + + for (i = 0; i < nsessions; ++i) { + 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]; + DEBUGADD(DMAPI_TRACE, + ("attached to existing DMAPI session " + "named '%s'\n", buf)); + break; + } + } + + TALLOC_FREE(sessions); + + /* No session already defined. */ + if (dmapi_session == DM_NO_SESSION) { + err = dm_create_session(DM_NO_SESSION, DMAPI_SESSION_NAME, + &dmapi_session); + if (err < 0) { + DEBUGADD(DMAPI_TRACE, + ("failed to create new DMAPI session: %s\n", + strerror(errno))); + return -1; + } + + DEBUGADD(DMAPI_TRACE, + ("created new DMAPI session named '%s'\n", + DMAPI_SESSION_NAME)); + } + + /* Note that we never end the DMAPI session. This enables child + * processes to continue to use the session after we exit. It also lets + * you run a second Samba server on different ports without any + * conflict. + */ + + return 0; +} + +/* Reattach to an existing dmapi session. Called from service processes that + * might not be running as root. + */ +static int reattach_dmapi_session(void) +{ + char buf[DM_SESSION_INFO_LEN]; + size_t buflen; + + if (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; + unbecome_root(); + return -1; + } + + if (dm_query_session(dmapi_session, sizeof(buf), + buf, &buflen) < 0) { + /* Session is stale. Disable DMAPI. */ + dmapi_session = DM_NO_SESSION; + unbecome_root(); + return -1; + } + + set_effective_capability(DMAPI_ACCESS_CAPABILITY); + + DEBUG(DMAPI_TRACE, ("reattached DMAPI session\n")); + unbecome_root(); + return 0; + } + + return 0; +} + +uint32 dmapi_file_flags(const char * const path) +{ + int err; + dm_eventset_t events = {0}; + uint nevents; + + void *dm_handle; + size_t dm_handle_len; + + uint32 flags = 0; + + if (dmapi_have_session()) { + if (reattach_dmapi_session() < 0) { + return 0; + } + } + + err = dm_path_to_handle(CONST_DISCARD(char *, path), + &dm_handle, &dm_handle_len); + if (err < 0) { + DEBUG(DMAPI_TRACE, ("dm_path_to_handle(%s): %s\n", + path, strerror(errno))); + return 0; + } + + err = dm_get_eventlist(dmapi_session, dm_handle, dm_handle_len, + DM_NO_TOKEN, DM_EVENT_MAX, &events, &nevents); + if (err < 0) { + DEBUG(DMAPI_TRACE, ("dm_get_eventlist: %s\n", + strerror(errno))); + dm_handle_free(dm_handle, dm_handle_len); + return 0; + } + + /* We figure that the only reason a DMAPI application would be + * interested in trapping read events is that part of the file is + * offline. + */ + DEBUG(DMAPI_TRACE, ("DMAPI event list for %s is %#llx\n", + path, events)); + if (DMEV_ISSET(DM_EVENT_READ, events)) { + flags = FILE_ATTRIBUTE_OFFLINE; + } + + dm_handle_free(dm_handle, dm_handle_len); + + if (flags & FILE_ATTRIBUTE_OFFLINE) { + DEBUG(DMAPI_TRACE, ("%s is OFFLINE\n", path)); + } + + return flags; +} + +#endif /* USE_DMAPI */ diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c index 7f3bda582d..f376de1343 100644 --- a/source3/smbd/dosmode.c +++ b/source3/smbd/dosmode.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. dos mode handling functions Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) James Peach 2006 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,6 +31,31 @@ static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf) return 0; } +/**************************************************************************** + Work out whether this file is offline +****************************************************************************/ + +#ifndef ISDOT +#define ISDOT(p) (*(p) == '.' && *((p) + 1) == '\0') +#endif /* ISDOT */ + +#ifndef ISDOTDOT +#define ISDOTDOT(p) (*(p) == '.' && *((p) + 1) == '.' && *((p) + 2) == '\0') +#endif /* ISDOTDOT */ + +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: @@ -325,6 +351,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); + } + /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) { diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 46ab191530..5da73046a1 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -930,6 +930,12 @@ void build_options(BOOL screen); if ( is_daemon && !interactive ) start_background_queue(); + /* Always attempt to initialize DMAPI. We will only use it later if + * lp_dmapi_support is set on the share, but we need a single global + * session to work with. + */ + dmapi_init_session(); + if (!open_sockets_smbd(is_daemon, interactive, ports)) exit(1); -- cgit