summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorJames Peach <jpeach@samba.org>2006-03-22 23:49:09 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 11:15:42 -0500
commit40d0707827ee154bcb03013abe6f72f1026a70c9 (patch)
treeb465d32e79fc65a682aa55dd96e4a665109bfc15 /source3/smbd
parente836508704dd964e22e8bfc0f8e9ec520a2c94f2 (diff)
downloadsamba-40d0707827ee154bcb03013abe6f72f1026a70c9.tar.gz
samba-40d0707827ee154bcb03013abe6f72f1026a70c9.tar.bz2
samba-40d0707827ee154bcb03013abe6f72f1026a70c9.zip
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)
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/chgpasswd.c3
-rw-r--r--source3/smbd/dmapi.c576
-rw-r--r--source3/smbd/dosmode.c30
-rw-r--r--source3/smbd/server.c6
4 files changed, 614 insertions, 1 deletions
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 <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 */
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
@@ -31,6 +32,31 @@ static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
}
/****************************************************************************
+ 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:
if creating file and inheriting
@@ -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);