r14668: Set the FILE_STATUS_OFFLINE bit by observing the events a DMAPI-based
authorJames Peach <jpeach@samba.org>
Wed, 22 Mar 2006 23:49:09 +0000 (23:49 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 16:15:42 +0000 (11:15 -0500)
HSM is interested in. Tested on both IRIX and SLES9.
(This used to be commit 514a767c57f8194547e5b708ad2573ab9a0719c6)

13 files changed:
source3/Makefile.in
source3/configure.in
source3/include/debug.h
source3/include/smb.h
source3/include/smb_macros.h
source3/lib/debug.c
source3/lib/smbrun.c
source3/lib/system.c
source3/param/loadparm.c
source3/smbd/chgpasswd.c
source3/smbd/dmapi.c [new file with mode: 0644]
source3/smbd/dosmode.c
source3/smbd/server.c

index ac67388d6a759cf78f60854eafd5f4d481a4d635..d399cb66a582e12b40c12715db92a8c15afed769 100644 (file)
@@ -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) \
index 6063ecf0f30b916e8bd06b5002bf5133d9020297..f3fa91e3b48710be0ead3daa48e1b534f1359e17 100644 (file)
@@ -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 <sys/types.h>
@@ -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
index b6fb50a9acbcaa976dce077e17266b45c02ebad4..2cf1ceaead09e51ec6e29566a68ad64aa38fa56d 100644 (file)
@@ -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
index 26b4b69266fb1bef6c7b9e85b28126b39d1f793b..8faf3877ce990e30fb8159bb9a96e72a93fe3ab7 100644 (file)
@@ -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
index 3ae8814cfd32cde554609f5c6379093c540def59..554dbbc0878dee0ebfad99452a93b3ec01b78cf5 100644 (file)
         (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 */
index 29d879adbc5038969ac1ab67e65b606e0cc0f8be..97a147f17d429a4a324c61c29268cae3925b53fd 100644 (file)
@@ -166,6 +166,7 @@ static const char *default_classname_table[] = {
        "acls",              /* DBGC_ACLS         */
        "locking",           /* DBGC_LOCKING      */
        "msdfs",             /* DBGC_MSDFS        */
+       "dmapi",             /* DBGC_DMAPI        */
        NULL
 };
 
index 521b1bf761e211d1aeba86af813ccd857f82f2ff..4400aeb4433e20219f96eeb1173a7d25ed2e6379 100644 (file)
@@ -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)) {
index ffb7031715049ec540ad472e6f337dce7e5363e3..2e5f42307bdf3e09114ef12999ef3e729935d673 100644 (file)
 
 #include "includes.h"
 
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#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) {
index 0bde5805b07cbbbb26868f9089cc2acd8b448dc5..13f585d8d0a6784dbe4f5f61ce2bc850e992750c 100644 (file)
@@ -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)
index aef487f4a7628c94ea1ffa4951737984e6e073c6..16b44a54bf9a2082ddf3869389016bd6e17843b1 100644 (file)
@@ -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 (file)
index 0000000..0fa3a16
--- /dev/null
@@ -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 <xfs/dmapi.h>
+#endif
+
+#ifdef HAVE_SYS_DMI_H
+#include <sys/dmi.h>
+#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 <jpeach@sgi.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.
+*/
+
+#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 <xfs/dmapi.h>
+#endif
+
+#ifdef HAVE_SYS_DMI_H
+#include <sys/dmi.h>
+#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 */
index 7f3bda582df76b0162b0811714ef5e535f72f5fe..f376de1343d5abedfde36e6ff5a8f6e3dd719555 100644 (file)
@@ -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)) {
index 46ab191530dcfac36084b505eb2166e54afc1a0c..5da73046a123307ca669dd86b76c2765d59a9fa8 100644 (file)
@@ -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);