summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorKarolin Seeger <kseeger@samba.org>2008-02-29 10:44:38 +0100
committerKarolin Seeger <kseeger@samba.org>2008-02-29 10:44:38 +0100
commitc4fbe2846231a6b322c1094c6a1dbf93b7305768 (patch)
tree09eb77a294f4acda131b41fe4c9bec4ed175eb16 /source3/smbd
parent1a6415fc77c708b87c8e2ce6e7828f486ffc87ac (diff)
parent695b6662abe64a40061bfa05ede12173fc4b1945 (diff)
downloadsamba-c4fbe2846231a6b322c1094c6a1dbf93b7305768.tar.gz
samba-c4fbe2846231a6b322c1094c6a1dbf93b7305768.tar.bz2
samba-c4fbe2846231a6b322c1094c6a1dbf93b7305768.zip
Merge commit 'origin/v3-2-test' into v3-2-stable
Conflicts: WHATSNEW.txt (This used to be commit a390bcf9403df4cf4d5eef42b35ebccbe253882e)
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/aio.c51
-rw-r--r--source3/smbd/chgpasswd.c31
-rw-r--r--source3/smbd/close.c115
-rw-r--r--source3/smbd/connection.c9
-rw-r--r--source3/smbd/dmapi.c266
-rw-r--r--source3/smbd/dosmode.c55
-rw-r--r--source3/smbd/filename.c123
-rw-r--r--source3/smbd/lanman.c2
-rw-r--r--source3/smbd/map_username.c6
-rw-r--r--source3/smbd/msdfs.c5
-rw-r--r--source3/smbd/notify_inotify.c8
-rw-r--r--source3/smbd/nttrans.c13
-rw-r--r--source3/smbd/open.c227
-rw-r--r--source3/smbd/oplock_irix.c2
-rw-r--r--source3/smbd/oplock_linux.c22
-rw-r--r--source3/smbd/password.c31
-rw-r--r--source3/smbd/posix_acls.c223
-rw-r--r--source3/smbd/process.c317
-rw-r--r--source3/smbd/reply.c98
-rw-r--r--source3/smbd/seal.c20
-rw-r--r--source3/smbd/server.c76
-rw-r--r--source3/smbd/service.c94
-rw-r--r--source3/smbd/sesssetup.c144
-rw-r--r--source3/smbd/trans2.c427
-rw-r--r--source3/smbd/utmp.c2
25 files changed, 1591 insertions, 776 deletions
diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c
index 9c25f69c97..2889e3c13f 100644
--- a/source3/smbd/aio.c
+++ b/source3/smbd/aio.c
@@ -141,21 +141,27 @@ static struct aio_extra *find_aio_ex(uint16 mid)
We can have these many aio buffers in flight.
*****************************************************************************/
-#define AIO_PENDING_SIZE 10
+static int aio_pending_size;
static sig_atomic_t signals_received;
static int outstanding_aio_calls;
-static uint16 aio_pending_array[AIO_PENDING_SIZE];
+static uint16 *aio_pending_array;
/****************************************************************************
Signal handler when an aio request completes.
*****************************************************************************/
-static void signal_handler(int sig, siginfo_t *info, void *unused)
+void aio_request_done(uint16_t mid)
{
- if (signals_received < AIO_PENDING_SIZE) {
- aio_pending_array[signals_received] = info->si_value.sival_int;
+ if (signals_received < aio_pending_size) {
+ aio_pending_array[signals_received] = mid;
signals_received++;
- } /* Else signal is lost. */
+ }
+ /* Else signal is lost. */
+}
+
+static void signal_handler(int sig, siginfo_t *info, void *unused)
+{
+ aio_request_done(info->si_value.sival_int);
sys_select_signal(RT_SIGNAL_AIO);
}
@@ -176,6 +182,10 @@ void initialize_async_io_handler(void)
{
struct sigaction act;
+ aio_pending_size = lp_maxmux();
+ aio_pending_array = SMB_MALLOC_ARRAY(uint16, aio_pending_size);
+ SMB_ASSERT(aio_pending_array != NULL);
+
ZERO_STRUCT(act);
act.sa_sigaction = signal_handler;
act.sa_flags = SA_SIGINFO;
@@ -202,7 +212,14 @@ bool schedule_aio_read_and_X(connection_struct *conn,
size_t bufsize;
size_t min_aio_read_size = lp_aio_read_size(SNUM(conn));
- if (!min_aio_read_size || (smb_maxcnt < min_aio_read_size)) {
+ if (fsp->base_fsp != NULL) {
+ /* No AIO on streams yet */
+ DEBUG(10, ("AIO on streams not yet supported\n"));
+ return false;
+ }
+
+ if ((!min_aio_read_size || (smb_maxcnt < min_aio_read_size))
+ && !SMB_VFS_AIO_FORCE(fsp)) {
/* Too small a read for aio request. */
DEBUG(10,("schedule_aio_read_and_X: read size (%u) too small "
"for minimum aio_read of %u\n",
@@ -218,7 +235,7 @@ bool schedule_aio_read_and_X(connection_struct *conn,
return False;
}
- if (outstanding_aio_calls >= AIO_PENDING_SIZE) {
+ if (outstanding_aio_calls >= aio_pending_size) {
DEBUG(10,("schedule_aio_read_and_X: Already have %d aio "
"activities outstanding.\n",
outstanding_aio_calls ));
@@ -284,7 +301,14 @@ bool schedule_aio_write_and_X(connection_struct *conn,
bool write_through = BITSETW(req->inbuf+smb_vwv7,0);
size_t min_aio_write_size = lp_aio_write_size(SNUM(conn));
- if (!min_aio_write_size || (numtowrite < min_aio_write_size)) {
+ if (fsp->base_fsp != NULL) {
+ /* No AIO on streams yet */
+ DEBUG(10, ("AIO on streams not yet supported\n"));
+ return false;
+ }
+
+ if ((!min_aio_write_size || (numtowrite < min_aio_write_size))
+ && !SMB_VFS_AIO_FORCE(fsp)) {
/* Too small a write for aio request. */
DEBUG(10,("schedule_aio_write_and_X: write size (%u) too "
"small for minimum aio_write of %u\n",
@@ -300,7 +324,7 @@ bool schedule_aio_write_and_X(connection_struct *conn,
return False;
}
- if (outstanding_aio_calls >= AIO_PENDING_SIZE) {
+ if (outstanding_aio_calls >= aio_pending_size) {
DEBUG(3,("schedule_aio_write_and_X: Already have %d aio "
"activities outstanding.\n",
outstanding_aio_calls ));
@@ -349,6 +373,8 @@ bool schedule_aio_write_and_X(connection_struct *conn,
return False;
}
+ release_level_2_oplocks_on_change(fsp);
+
if (!write_through && !lp_syncalways(SNUM(fsp->conn))
&& fsp->aio_write_behind) {
/* Lie to the client and immediately claim we finished the
@@ -419,6 +445,9 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex)
SSVAL(outbuf,smb_vwv7,((nread >> 16) & 1));
SSVAL(smb_buf(outbuf),-2,nread);
+ aio_ex->fsp->fh->pos = aio_ex->acb.aio_offset + nread;
+ aio_ex->fsp->fh->position_information = aio_ex->fsp->fh->pos;
+
DEBUG( 3, ( "handle_aio_read_complete file %s max=%d "
"nread=%d\n",
aio_ex->fsp->fsp_name,
@@ -520,6 +549,8 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
DEBUG(5,("handle_aio_write: sync_file for %s returned %s\n",
fsp->fsp_name, nt_errstr(status) ));
}
+
+ aio_ex->fsp->fh->pos = aio_ex->acb.aio_offset + nwritten;
}
show_msg(outbuf);
diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c
index fb228f9e2a..e7ab60d22f 100644
--- a/source3/smbd/chgpasswd.c
+++ b/source3/smbd/chgpasswd.c
@@ -239,7 +239,8 @@ static int dochild(int master, const char *slavedev, const struct passwd *pass,
static int expect(int master, char *issue, char *expected)
{
char buffer[1024];
- int attempts, timeout, nread, len;
+ int attempts, timeout, nread;
+ size_t len;
bool match = False;
for (attempts = 0; attempts < 2; attempts++) {
@@ -248,7 +249,8 @@ static int expect(int master, char *issue, char *expected)
DEBUG(100, ("expect: sending [%s]\n", issue));
if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
- DEBUG(2,("expect: (short) write returned %d\n", len ));
+ DEBUG(2,("expect: (short) write returned %d\n",
+ (int)len ));
return False;
}
}
@@ -261,9 +263,16 @@ static int expect(int master, char *issue, char *expected)
nread = 0;
buffer[nread] = 0;
- while ((len = read_socket_with_timeout(master, buffer + nread, 1,
- sizeof(buffer) - nread - 1,
- timeout, NULL)) > 0) {
+ while (True) {
+ NTSTATUS status;
+ status = read_socket_with_timeout(
+ master, buffer + nread, 1,
+ sizeof(buffer) - nread - 1,
+ timeout, &len);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
nread += len;
buffer[nread] = 0;
@@ -1092,7 +1101,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw
if (!pdb_get_pass_can_change(hnd)) {
DEBUG(1, ("user %s does not have permissions to change password\n", username));
if (samr_reject_reason) {
- *samr_reject_reason = REJECT_REASON_OTHER;
+ *samr_reject_reason = SAMR_REJECT_OTHER;
}
return NT_STATUS_ACCOUNT_RESTRICTION;
}
@@ -1106,7 +1115,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw
"denied by Refuse Machine Password Change policy\n",
username));
if (samr_reject_reason) {
- *samr_reject_reason = REJECT_REASON_OTHER;
+ *samr_reject_reason = SAMR_REJECT_OTHER;
}
return NT_STATUS_ACCOUNT_RESTRICTION;
}
@@ -1119,7 +1128,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw
"wait until %s\n", username,
http_timestring(can_change_time)));
if (samr_reject_reason) {
- *samr_reject_reason = REJECT_REASON_OTHER;
+ *samr_reject_reason = SAMR_REJECT_OTHER;
}
return NT_STATUS_ACCOUNT_RESTRICTION;
}
@@ -1129,7 +1138,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw
username));
DEBUGADD(1, (" account policy min password len = %d\n", min_len));
if (samr_reject_reason) {
- *samr_reject_reason = REJECT_REASON_TOO_SHORT;
+ *samr_reject_reason = SAMR_REJECT_TOO_SHORT;
}
return NT_STATUS_PASSWORD_RESTRICTION;
/* return NT_STATUS_PWD_TOO_SHORT; */
@@ -1137,7 +1146,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw
if (check_passwd_history(hnd,new_passwd)) {
if (samr_reject_reason) {
- *samr_reject_reason = REJECT_REASON_IN_HISTORY;
+ *samr_reject_reason = SAMR_REJECT_IN_HISTORY;
}
return NT_STATUS_PASSWORD_RESTRICTION;
}
@@ -1158,7 +1167,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw
if (check_ret != 0) {
DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
if (samr_reject_reason) {
- *samr_reject_reason = REJECT_REASON_NOT_COMPLEX;
+ *samr_reject_reason = SAMR_REJECT_COMPLEXITY;
}
TALLOC_FREE(pass);
return NT_STATUS_PASSWORD_RESTRICTION;
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
index 4c385d7611..4bd23a35fc 100644
--- a/source3/smbd/close.c
+++ b/source3/smbd/close.c
@@ -156,6 +156,75 @@ static void notify_deferred_opens(struct share_mode_lock *lck)
}
/****************************************************************************
+ Delete all streams
+****************************************************************************/
+
+static NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
+{
+ struct stream_struct *stream_info;
+ int i;
+ unsigned int num_streams;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(),
+ &num_streams, &stream_info);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ DEBUG(10, ("no streams around\n"));
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n",
+ nt_errstr(status)));
+ goto fail;
+ }
+
+ DEBUG(10, ("delete_all_streams found %d streams\n",
+ num_streams));
+
+ if (num_streams == 0) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ for (i=0; i<num_streams; i++) {
+ int res;
+ char *streamname;
+
+ if (strequal(stream_info[i].name, "::$DATA")) {
+ continue;
+ }
+
+ streamname = talloc_asprintf(talloc_tos(), "%s%s", fname,
+ stream_info[i].name);
+
+ if (streamname == NULL) {
+ DEBUG(0, ("talloc_aprintf failed\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ res = SMB_VFS_UNLINK(conn, streamname);
+
+ TALLOC_FREE(streamname);
+
+ if (res == -1) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(10, ("Could not delete stream %s: %s\n",
+ streamname, strerror(errno)));
+ break;
+ }
+ }
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
Deal with removing a share mode on last close.
****************************************************************************/
@@ -305,6 +374,19 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
goto done;
}
+ if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
+ && !is_ntfs_stream_name(fsp->fsp_name)) {
+
+ status = delete_all_streams(conn, fsp->fsp_name);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("delete_all_streams failed: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ }
+
+
if (SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) {
/*
* This call can potentially fail as another smbd may
@@ -570,12 +652,37 @@ static NTSTATUS close_stat(files_struct *fsp)
NTSTATUS close_file(files_struct *fsp, enum file_close_type close_type)
{
+ NTSTATUS status;
+ struct files_struct *base_fsp = fsp->base_fsp;
+
if(fsp->is_directory) {
- return close_directory(fsp, close_type);
+ status = close_directory(fsp, close_type);
} else if (fsp->is_stat) {
- return close_stat(fsp);
+ status = close_stat(fsp);
} else if (fsp->fake_file_handle != NULL) {
- return close_fake_file(fsp);
+ status = close_fake_file(fsp);
+ } else {
+ status = close_normal_file(fsp, close_type);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- return close_normal_file(fsp, close_type);
+
+ if ((base_fsp != NULL) && (close_type != SHUTDOWN_CLOSE)) {
+
+ /*
+ * fsp was a stream, the base fsp can't be a stream as well
+ *
+ * For SHUTDOWN_CLOSE this is not possible here, because
+ * SHUTDOWN_CLOSE only happens from files.c which walks the
+ * complete list of files. If we mess with more than one fsp
+ * those loops will become confused.
+ */
+
+ SMB_ASSERT(base_fsp->base_fsp == NULL);
+ close_file(base_fsp, close_type);
+ }
+
+ return status;
}
diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c
index 016c8adb1b..d7063c9989 100644
--- a/source3/smbd/connection.c
+++ b/source3/smbd/connection.c
@@ -118,6 +118,15 @@ int count_current_connections( const char *sharename, bool clear )
}
/****************************************************************************
+ 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.
****************************************************************************/
diff --git a/source3/smbd/dmapi.c b/source3/smbd/dmapi.c
index 05e9138ea9..fab0d5f9ef 100644
--- a/source3/smbd/dmapi.c
+++ b/source3/smbd/dmapi.c
@@ -25,9 +25,9 @@
#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; }
+const void * dmapi_get_current_session(void) { return NULL; }
#else /* USE_DMAPI */
@@ -46,100 +46,77 @@ 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;
+static unsigned session_num;
-/* 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)
+/*
+ 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.
+*/
+static int dmapi_init_session(void)
{
char buf[DM_SESSION_INFO_LEN];
size_t buflen;
-
- uint nsessions = 10;
+ uint nsessions = 5;
dm_sessid_t *sessions = NULL;
+ char *version;
+ char *session_name;
+ TALLOC_CTX *tmp_ctx = talloc_new(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");
+ if (session_num == 0) {
+ session_name = DMAPI_SESSION_NAME;
+ } else {
+ session_name = talloc_asprintf(tmp_ctx, "%s%u", DMAPI_SESSION_NAME,
+ session_num);
+ }
- dmapi_session = DM_NO_SESSION;
- if (init_dmapi_service() < 0) {
+ if (session_name == NULL) {
+ DEBUG(0,("Out of memory in dmapi_init_session\n"));
+ talloc_free(tmp_ctx);
return -1;
}
+
-retry:
-
- if ((sessions = realloc_session_list(sessions, nsessions)) == NULL) {
+ if (dm_init_service(&version) < 0) {
+ DEBUG(0, ("dm_init_service failed - disabling DMAPI\n"));
+ talloc_free(tmp_ctx);
return -1;
}
- err = dm_getall_sessions(nsessions, sessions, &nsessions);
- if (err < 0) {
- if (errno == E2BIG) {
- nsessions *= 2;
- goto retry;
+ ZERO_STRUCT(buf);
+
+ /* Fetch kernel DMAPI sessions until we get any of them */
+ do {
+ dm_sessid_t *new_sessions;
+ nsessions *= 2;
+ new_sessions = TALLOC_REALLOC_ARRAY(tmp_ctx, sessions,
+ dm_sessid_t, nsessions);
+ if (new_sessions == NULL) {
+ talloc_free(tmp_ctx);
+ return -1;
}
+ sessions = new_sessions;
+ err = dm_getall_sessions(nsessions, sessions, &nsessions);
+ } while (err == -1 && errno == E2BIG);
+
+ if (err == -1) {
DEBUGADD(DMAPI_TRACE,
("failed to retrieve DMAPI sessions: %s\n",
strerror(errno)));
- TALLOC_FREE(sessions);
+ talloc_free(tmp_ctx);
return -1;
}
+ /* Look through existing kernel DMAPI sessions to find out ours */
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];
+ if (err == 0 && strcmp(session_name, buf) == 0) {
+ samba_dmapi_session = sessions[i];
DEBUGADD(DMAPI_TRACE,
("attached to existing DMAPI session "
"named '%s'\n", buf));
@@ -147,100 +124,123 @@ 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,
+ 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;
+ talloc_free(tmp_ctx);
return -1;
}
- DEBUGADD(DMAPI_TRACE,
- ("created new DMAPI session named '%s'\n",
- DMAPI_SESSION_NAME));
+ DEBUG(0, ("created new DMAPI session named '%s' for %s\n",
+ session_name, version));
+ }
+
+ if (samba_dmapi_session != DM_NO_SESSION) {
+ set_effective_capability(DMAPI_ACCESS_CAPABILITY);
}
- /* 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.
+ /*
+ Note that we never end the DMAPI session. It gets re-used if possiblie.
+ DMAPI session is a kernel resource that is usually lives until server reboot
+ and doesn't get destroed when an application finishes.
+
+ However, we free list of references to DMAPI sessions we've got from the kernel
+ as it is not needed anymore once we have found (or created) our session.
*/
+ talloc_free(tmp_ctx);
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)
+/*
+ Return a pointer to our DMAPI session, if available.
+ This assumes that you have called dmapi_have_session() first.
+*/
+const void *dmapi_get_current_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 (samba_dmapi_session == DM_NO_SESSION) {
+ return NULL;
+ }
- 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;
- }
+ return (void *)&samba_dmapi_session;
+}
+
+/*
+ dmapi_have_session() must be the first DMAPI call you make in Samba. It will
+ initialize DMAPI, if available, and tell you if you can get a DMAPI session.
+ This should be called in the client-specific child process.
+*/
- set_effective_capability(DMAPI_ACCESS_CAPABILITY);
+bool dmapi_have_session(void)
+{
+ static bool initialized;
+ if (!initialized) {
+ initialized = true;
- DEBUG(DMAPI_TRACE, ("reattached DMAPI session\n"));
+ become_root();
+ dmapi_init_session();
unbecome_root();
+
}
- return 0;
+ return samba_dmapi_session != DM_NO_SESSION;
}
-uint32 dmapi_file_flags(const char * const path)
+/*
+ only call this when you get back an EINVAL error indicating that the
+ session you are using is invalid. This destroys the existing session
+ and creates a new one.
+ */
+bool dmapi_new_session(void)
{
- static int attached = 0;
+ if (dmapi_have_session()) {
+ /* try to destroy the old one - this may not succeed */
+ dm_destroy_session(samba_dmapi_session);
+ }
+ samba_dmapi_session = DM_NO_SESSION;
+ become_root();
+ session_num++;
+ dmapi_init_session();
+ unbecome_root();
+ return samba_dmapi_session != DM_NO_SESSION;
+}
+/*
+ This is default implementation of dmapi_file_flags() that is
+ called from VFS is_offline() call to know whether file is offline.
+ For GPFS-specific version see modules/vfs_tsmsm.c. It might be
+ that approach on quering existence of a specific attribute that
+ is used in vfs_tsmsm.c will work with other DMAPI-based HSM
+ implementations as well.
+*/
+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;
+ dm_sessid_t dmapi_session;
+ const void *dmapi_session_ptr;
+ void *dm_handle = NULL;
+ size_t dm_handle_len = 0;
- uint32 flags = 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_ptr = dmapi_get_current_session();
+ if (dmapi_session_ptr == NULL) {
+ return 0;
+ }
+
+ dmapi_session = *(dm_sessid_t *)dmapi_session_ptr;
+ if (dmapi_session == DM_NO_SESSION) {
+ return 0;
}
/* AIX has DMAPI but no POSIX capablities support. In this case,
@@ -292,8 +292,7 @@ uint32 dmapi_file_flags(const char * const path)
* 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));
+ DEBUG(DMAPI_TRACE, ("DMAPI event list for %s\n", path));
if (DMEV_ISSET(DM_EVENT_READ, events)) {
flags = FILE_ATTRIBUTE_OFFLINE;
}
@@ -313,4 +312,5 @@ done:
return flags;
}
+
#endif /* USE_DMAPI */
diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c
index d3813f9b41..a2e617c117 100644
--- a/source3/smbd/dosmode.c
+++ b/source3/smbd/dosmode.c
@@ -31,23 +31,6 @@ static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
}
/****************************************************************************
- 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:
if creating file and inheriting (i.e. parent_dir != NULL)
@@ -366,6 +349,7 @@ 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;
DEBUG(8,("dos_mode: %s\n", path));
@@ -395,8 +379,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);
+
+ offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
+ if (S_ISREG(sbuf->st_mode) && offline) {
+ result |= FILE_ATTRIBUTE_OFFLINE;
}
/* Optimization : Only call is_hidden_path if it's not already
@@ -432,10 +418,11 @@ 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;
+ uint32_t old_mode;
/* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
- dosmode &= SAMBA_ATTRIBUTES_MASK;
+ dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
@@ -458,7 +445,24 @@ int file_set_dosmode(connection_struct *conn, const char *fname,
else
dosmode &= ~aDIR;
- if (dos_mode(conn,fname,st) == dosmode) {
+ old_mode = dos_mode(conn,fname,st);
+
+ if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
+ if (!(old_mode & 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));
+ }
+ }
+ }
+
+ dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
+ old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
+
+ if (old_mode == dosmode) {
st->st_mode = unixmode;
return(0);
}
@@ -505,10 +509,11 @@ 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) {
+ 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;
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
index be4960cab8..10e9583049 100644
--- a/source3/smbd/filename.c
+++ b/source3/smbd/filename.c
@@ -28,6 +28,13 @@
static bool scan_directory(connection_struct *conn, const char *path,
char *name, char **found_name);
+static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,
+ connection_struct *conn,
+ const char *orig_path,
+ const char *basepath,
+ const char *streamname,
+ SMB_STRUCT_STAT *pst,
+ char **path);
/****************************************************************************
Mangle the 2nd name and check if it is then equal to the first name.
@@ -119,6 +126,7 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
char *start, *end;
char *dirpath = NULL;
char *name = NULL;
+ char *stream = NULL;
bool component_was_mangled = False;
bool name_has_wildcard = False;
NTSTATUS result;
@@ -206,6 +214,20 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
return NT_STATUS_NO_MEMORY;
}
+ if (!lp_posix_pathnames()) {
+ stream = strchr_m(name, ':');
+
+ if (stream != NULL) {
+ char *tmp = talloc_strdup(ctx, stream);
+ if (tmp == NULL) {
+ TALLOC_FREE(name);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *stream = '\0';
+ stream = tmp;
+ }
+ }
+
/*
* Large directory fix normalization. If we're case sensitive, and
* the case preserving parameters are set to "no", normalize the case of
@@ -653,6 +675,20 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
done:
+ if (stream != NULL) {
+ char *tmp = NULL;
+
+ result = build_stream_path(ctx, conn, orig_path, name, stream,
+ pst, &tmp);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto fail;
+ }
+
+ DEBUG(10, ("build_stream_path returned %s\n", tmp));
+
+ TALLOC_FREE(name);
+ name = tmp;
+ }
*pp_conv_path = name;
TALLOC_FREE(dirpath);
return NT_STATUS_OK;
@@ -823,3 +859,90 @@ static bool scan_directory(connection_struct *conn, const char *path,
errno = ENOENT;
return False;
}
+
+static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,
+ connection_struct *conn,
+ const char *orig_path,
+ const char *basepath,
+ const char *streamname,
+ SMB_STRUCT_STAT *pst,
+ char **path)
+{
+ SMB_STRUCT_STAT st;
+ char *result = NULL;
+ NTSTATUS status;
+ unsigned int i, num_streams;
+ struct stream_struct *streams = NULL;
+
+ result = talloc_asprintf(mem_ctx, "%s%s", basepath, streamname);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (SMB_VFS_STAT(conn, result, &st) == 0) {
+ *pst = st;
+ *path = result;
+ return NT_STATUS_OK;
+ }
+
+ if (errno != ENOENT) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(10, ("vfs_stat failed: %s\n", nt_errstr(status)));
+ goto fail;
+ }
+
+ status = SMB_VFS_STREAMINFO(conn, NULL, basepath, mem_ctx,
+ &num_streams, &streams);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ SET_STAT_INVALID(*pst);
+ *path = result;
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("vfs_streaminfo failed: %s\n", nt_errstr(status)));
+ goto fail;
+ }
+
+ for (i=0; i<num_streams; i++) {
+ DEBUG(10, ("comparing [%s] and [%s]: ",
+ streamname, streams[i].name));
+ if (fname_equal(streamname, streams[i].name,
+ conn->case_sensitive)) {
+ DEBUGADD(10, ("equal\n"));
+ break;
+ }
+ DEBUGADD(10, ("not equal\n"));
+ }
+
+ if (i == num_streams) {
+ SET_STAT_INVALID(*pst);
+ *path = result;
+ TALLOC_FREE(streams);
+ return NT_STATUS_OK;
+ }
+
+ TALLOC_FREE(result);
+
+ result = talloc_asprintf(mem_ctx, "%s%s", basepath, streams[i].name);
+ if (result == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ SET_STAT_INVALID(*pst);
+
+ if (SMB_VFS_STAT(conn, result, pst) == 0) {
+ stat_cache_add(orig_path, result, conn->case_sensitive);
+ }
+
+ *path = result;
+ TALLOC_FREE(streams);
+ return NT_STATUS_OK;
+
+ fail:
+ TALLOC_FREE(result);
+ TALLOC_FREE(streams);
+ return status;
+}
diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c
index 5a6df1f139..18e6bf9f7b 100644
--- a/source3/smbd/lanman.c
+++ b/source3/smbd/lanman.c
@@ -3175,7 +3175,7 @@ static bool api_NetWkstaGetInfo(connection_struct *conn,uint16 vuid,
where:
- usri11_name specifies the user name for which information is retireved
+ usri11_name specifies the user name for which information is retrieved
usri11_pad aligns the next data structure element to a word boundary
diff --git a/source3/smbd/map_username.c b/source3/smbd/map_username.c
index 7290f70547..7536758bcb 100644
--- a/source3/smbd/map_username.c
+++ b/source3/smbd/map_username.c
@@ -178,7 +178,7 @@ bool map_username(fstring user)
/* skip lines like 'user = ' */
- dosuserlist = str_list_make(dosname, NULL);
+ dosuserlist = str_list_make(talloc_tos(), dosname, NULL);
if (!dosuserlist) {
DEBUG(0,("Bad username map entry. Unable to build user list. Ignoring.\n"));
continue;
@@ -193,13 +193,13 @@ bool map_username(fstring user)
fstrcpy( user, unixname );
if ( return_if_mapped ) {
- str_list_free (&dosuserlist);
+ TALLOC_FREE(dosuserlist);
x_fclose(f);
return True;
}
}
- str_list_free (&dosuserlist);
+ TALLOC_FREE(dosuserlist);
}
x_fclose(f);
diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c
index 4ae735633b..8ffa0f7751 100644
--- a/source3/smbd/msdfs.c
+++ b/source3/smbd/msdfs.c
@@ -288,12 +288,13 @@ static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
char **alt_path = NULL;
int count = 0, i;
struct referral *reflist;
+ char *saveptr;
temp = talloc_strdup(ctx, target);
if (!temp) {
return False;
}
- prot = strtok(temp,":");
+ prot = strtok_r(temp, ":", &saveptr);
if (!prot) {
DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
return False;
@@ -306,7 +307,7 @@ static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
/* parse out the alternate paths */
while((count<MAX_REFERRAL_COUNT) &&
- ((alt_path[count] = strtok(NULL,",")) != NULL)) {
+ ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
count++;
}
diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index 73cee440d4..fa0f0ed51d 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -29,10 +29,12 @@
#include <asm/types.h>
#endif
+#ifndef HAVE_INOTIFY_INIT
+
#include <linux/inotify.h>
#include <asm/unistd.h>
-#ifndef HAVE_INOTIFY_INIT
+
/*
glibc doesn't define these functions yet (as of March 2006)
*/
@@ -50,6 +52,10 @@ static int inotify_rm_watch(int fd, int wd)
{
return syscall(__NR_inotify_rm_watch, fd, wd);
}
+#else
+
+#include <sys/inotify.h>
+
#endif
diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index e8df732ea2..5293ca5347 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -271,6 +271,9 @@ void send_nt_replies(connection_struct *conn,
/****************************************************************************
Is it an NTFS stream name ?
+ An NTFS file name is <path>.<extention>:<stream name>:<stream type>
+ $DATA can be used as both a stream name and a stream type. A missing stream
+ name or type implies $DATA.
****************************************************************************/
bool is_ntfs_stream_name(const char *fname)
@@ -555,7 +558,7 @@ void reply_ntcreate_and_X(struct smb_request *req)
}
file_len = sbuf.st_size;
- fattr = dos_mode(conn,fname,&sbuf);
+ fattr = dos_mode(conn,fsp->fsp_name,&sbuf);
if (fattr == 0) {
fattr = FILE_ATTRIBUTE_NORMAL;
}
@@ -623,7 +626,7 @@ void reply_ntcreate_and_X(struct smb_request *req)
uint32 perms = 0;
p += 25;
if (fsp->is_directory
- || can_write_to_file(conn, fname, &sbuf)) {
+ || can_write_to_file(conn, fsp->fsp_name, &sbuf)) {
perms = FILE_GENERIC_ALL;
} else {
perms = FILE_GENERIC_READ|FILE_EXECUTE;
@@ -1004,7 +1007,7 @@ static void call_nt_transact_create(connection_struct *conn,
}
file_len = sbuf.st_size;
- fattr = dos_mode(conn,fname,&sbuf);
+ fattr = dos_mode(conn,fsp->fsp_name,&sbuf);
if (fattr == 0) {
fattr = FILE_ATTRIBUTE_NORMAL;
}
@@ -1072,7 +1075,7 @@ static void call_nt_transact_create(connection_struct *conn,
uint32 perms = 0;
p += 25;
if (fsp->is_directory
- || can_write_to_file(conn, fname, &sbuf)) {
+ || can_write_to_file(conn, fsp->fsp_name, &sbuf)) {
perms = FILE_GENERIC_ALL;
} else {
perms = FILE_GENERIC_READ|FILE_EXECUTE;
@@ -1080,7 +1083,7 @@ static void call_nt_transact_create(connection_struct *conn,
SIVAL(p,0,perms);
}
- DEBUG(5,("call_nt_transact_create: open name = %s\n", fname));
+ DEBUG(5,("call_nt_transact_create: open name = %s\n", fsp->fsp_name));
/* Send the required number of replies */
send_nt_replies(conn, req, NT_STATUS_OK, params, param_len, *ppdata, 0);
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 9d48bcc98b..cc78503379 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1842,7 +1842,9 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
set_share_mode(lck, fsp, current_user.ut.uid, 0, fsp->oplock_type, new_file_created);
/* Handle strange delete on close create semantics. */
- if ((create_options & FILE_DELETE_ON_CLOSE) && can_set_initial_delete_on_close(lck)) {
+ if ((create_options & FILE_DELETE_ON_CLOSE)
+ && (is_ntfs_stream_name(fname)
+ || can_set_initial_delete_on_close(lck))) {
status = can_set_delete_on_close(fsp, True, new_dos_attributes);
if (!NT_STATUS_IS_OK(status)) {
@@ -2104,7 +2106,7 @@ NTSTATUS open_directory(connection_struct *conn,
(unsigned int)create_disposition,
(unsigned int)file_attributes));
- if (is_ntfs_stream_name(fname)) {
+ if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) && is_ntfs_stream_name(fname)) {
DEBUG(2, ("open_directory: %s is a stream name!\n", fname));
return NT_STATUS_NOT_A_DIRECTORY;
}
@@ -2444,6 +2446,116 @@ static struct case_semantics_state *set_posix_case_semantics(TALLOC_CTX *mem_ctx
}
/*
+ * If a main file is opened for delete, all streams need to be checked for
+ * !FILE_SHARE_DELETE. Do this by opening with DELETE_ACCESS.
+ * If that works, delete them all by setting the delete on close and close.
+ */
+
+static NTSTATUS open_streams_for_delete(connection_struct *conn,
+ const char *fname)
+{
+ struct stream_struct *stream_info;
+ files_struct **streams;
+ int i;
+ unsigned int num_streams;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(),
+ &num_streams, &stream_info);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)
+ || NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ DEBUG(10, ("no streams around\n"));
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n",
+ nt_errstr(status)));
+ goto fail;
+ }
+
+ DEBUG(10, ("open_streams_for_delete found %d streams\n",
+ num_streams));
+
+ if (num_streams == 0) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ streams = TALLOC_ARRAY(talloc_tos(), files_struct *, num_streams);
+ if (streams == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ for (i=0; i<num_streams; i++) {
+ char *streamname;
+
+ if (strequal(stream_info[i].name, "::$DATA")) {
+ streams[i] = NULL;
+ continue;
+ }
+
+ streamname = talloc_asprintf(talloc_tos(), "%s%s", fname,
+ stream_info[i].name);
+
+ if (streamname == NULL) {
+ DEBUG(0, ("talloc_aprintf failed\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = create_file_unixpath
+ (conn, /* conn */
+ NULL, /* req */
+ streamname, /* fname */
+ DELETE_ACCESS, /* access_mask */
+ FILE_SHARE_READ | FILE_SHARE_WRITE
+ | FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition*/
+ NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE, /* create_options */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes */
+ 0, /* oplock_request */
+ 0, /* allocation_size */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &streams[i], /* result */
+ NULL, /* pinfo */
+ NULL); /* psbuf */
+
+ TALLOC_FREE(streamname);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Could not open stream %s: %s\n",
+ streamname, nt_errstr(status)));
+ break;
+ }
+ }
+
+ /*
+ * don't touch the variable "status" beyond this point :-)
+ */
+
+ for (i -= 1 ; i >= 0; i--) {
+ if (streams[i] == NULL) {
+ continue;
+ }
+
+ DEBUG(10, ("Closing stream # %d, %s\n", i,
+ streams[i]->fsp_name));
+ close_file(streams[i], NORMAL_CLOSE);
+ }
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/*
* Wrapper around open_file_ntcreate and open_directory
*/
@@ -2466,6 +2578,7 @@ NTSTATUS create_file_unixpath(connection_struct *conn,
{
SMB_STRUCT_STAT sbuf;
int info = FILE_WAS_OPENED;
+ files_struct *base_fsp = NULL;
files_struct *fsp = NULL;
NTSTATUS status;
@@ -2495,7 +2608,23 @@ NTSTATUS create_file_unixpath(connection_struct *conn,
sbuf = *psbuf;
}
else {
- SET_STAT_INVALID(sbuf);
+ if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) {
+ SET_STAT_INVALID(sbuf);
+ }
+ }
+
+ if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
+ && (access_mask & DELETE_ACCESS)
+ && !is_ntfs_stream_name(fname)) {
+ /*
+ * We can't open a file with DELETE access if any of the
+ * streams is open without FILE_SHARE_DELETE
+ */
+ status = open_streams_for_delete(conn, fname);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
}
/* This is the correct thing to do (check every time) but can_delete
@@ -2528,12 +2657,62 @@ NTSTATUS create_file_unixpath(connection_struct *conn,
}
#endif
+ if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
+ && is_ntfs_stream_name(fname)
+ && (!(create_options & NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE))) {
+ char *base;
+ uint32 base_create_disposition;
+
+ if (create_options & FILE_DIRECTORY_FILE) {
+ status = NT_STATUS_NOT_A_DIRECTORY;
+ goto fail;
+ }
+
+ status = split_ntfs_stream_name(talloc_tos(), fname,
+ &base, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("create_file_unixpath: "
+ "split_ntfs_stream_name failed: %s\n",
+ nt_errstr(status)));
+ goto fail;
+ }
+
+ SMB_ASSERT(!is_ntfs_stream_name(base)); /* paranoia.. */
+
+ switch (create_disposition) {
+ case FILE_OPEN:
+ base_create_disposition = FILE_OPEN;
+ break;
+ default:
+ base_create_disposition = FILE_OPEN_IF;
+ break;
+ }
+
+ status = create_file_unixpath(conn, NULL, base, 0,
+ FILE_SHARE_READ
+ | FILE_SHARE_WRITE
+ | FILE_SHARE_DELETE,
+ base_create_disposition,
+ 0, 0, 0, 0, NULL, NULL,
+ &base_fsp, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("create_file_unixpath for base %s failed: "
+ "%s\n", base, nt_errstr(status)));
+ goto fail;
+ }
+ }
+
/*
* If it's a request for a directory open, deal with it separately.
*/
if (create_options & FILE_DIRECTORY_FILE) {
+ if (create_options & FILE_NON_DIRECTORY_FILE) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
/* Can't open a temp directory. IFS kit test. */
if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) {
status = NT_STATUS_INVALID_PARAMETER;
@@ -2662,7 +2841,17 @@ NTSTATUS create_file_unixpath(connection_struct *conn,
}
}
- DEBUG(10, ("create_file: info=%d\n", info));
+ DEBUG(10, ("create_file_unixpath: info=%d\n", info));
+
+ /*
+ * Set fsp->base_fsp late enough that we can't "goto fail" anymore. In
+ * the fail: branch we call close_file(fsp, ERROR_CLOSE) which would
+ * also close fsp->base_fsp which we have to also do explicitly in
+ * this routine here, as not in all "goto fail:" we have the fsp set
+ * up already to be initialized with the base_fsp.
+ */
+
+ fsp->base_fsp = base_fsp;
*result = fsp;
if (pinfo != NULL) {
@@ -2679,12 +2868,16 @@ NTSTATUS create_file_unixpath(connection_struct *conn,
return NT_STATUS_OK;
fail:
- DEBUG(10, ("create_file: %s\n", nt_errstr(status)));
+ DEBUG(10, ("create_file_unixpath: %s\n", nt_errstr(status)));
if (fsp != NULL) {
close_file(fsp, ERROR_CLOSE);
fsp = NULL;
}
+ if (base_fsp != NULL) {
+ close_file(base_fsp, ERROR_CLOSE);
+ base_fsp = NULL;
+ }
return status;
}
@@ -2812,19 +3005,18 @@ NTSTATUS create_file(connection_struct *conn,
status = NT_STATUS_NO_MEMORY;
goto fail;
}
- } else {
- /*
- * Check to see if this is a mac fork of some kind.
- */
+ }
- if (is_ntfs_stream_name(fname)) {
- enum FAKE_FILE_TYPE fake_file_type;
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
- fake_file_type = is_fake_file(fname);
+ if (is_ntfs_stream_name(fname)) {
+ enum FAKE_FILE_TYPE fake_file_type;
- if (fake_file_type == FAKE_FILE_TYPE_NONE) {
- return NT_STATUS_OBJECT_PATH_NOT_FOUND;
- }
+ fake_file_type = is_fake_file(fname);
+
+ if (fake_file_type != FAKE_FILE_TYPE_NONE) {
/*
* Here we go! support for changing the disk quotas
@@ -2843,8 +3035,7 @@ NTSTATUS create_file(connection_struct *conn,
goto fail;
}
- SET_STAT_INVALID(sbuf);
-
+ ZERO_STRUCT(sbuf);
goto done;
}
}
@@ -2889,6 +3080,8 @@ NTSTATUS create_file(connection_struct *conn,
fname = converted_fname;
}
+ TALLOC_FREE(case_state);
+
/* All file access must go through check_name() */
status = check_name(conn, fname);
diff --git a/source3/smbd/oplock_irix.c b/source3/smbd/oplock_irix.c
index a4ea63bc0a..788cd04c17 100644
--- a/source3/smbd/oplock_irix.c
+++ b/source3/smbd/oplock_irix.c
@@ -121,7 +121,6 @@ static files_struct *irix_oplock_receive_message(fd_set *fds)
DEBUG(0,("irix_oplock_receive_message: read of kernel "
"notification failed. Error was %s.\n",
strerror(errno) ));
- set_smb_read_error(get_srv_read_error(), SMB_READ_ERROR);
return NULL;
}
@@ -141,7 +140,6 @@ static files_struct *irix_oplock_receive_message(fd_set *fds)
*/
return NULL;
}
- set_smb_read_error(get_srv_read_error(), SMB_READ_ERROR);
return NULL;
}
diff --git a/source3/smbd/oplock_linux.c b/source3/smbd/oplock_linux.c
index 05021b6c74..fa7cb42bc6 100644
--- a/source3/smbd/oplock_linux.c
+++ b/source3/smbd/oplock_linux.c
@@ -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) {
diff --git a/source3/smbd/password.c b/source3/smbd/password.c
index 6b517c3d86..80eba562c5 100644
--- a/source3/smbd/password.c
+++ b/source3/smbd/password.c
@@ -121,6 +121,10 @@ void invalidate_vuid(uint16 vuid)
data_blob_free(&vuser->session_key);
+ if (vuser->auth_ntlmssp_state) {
+ auth_ntlmssp_end(&vuser->auth_ntlmssp_state);
+ }
+
DLIST_REMOVE(validated_users, vuser);
/* clear the vuid from the 'cache' on each connection, and
@@ -582,7 +586,7 @@ static bool user_ok(const char *user, int snum)
ret = True;
if (lp_invalid_users(snum)) {
- str_list_copy(&invalid, lp_invalid_users(snum));
+ str_list_copy(talloc_tos(), &invalid, lp_invalid_users(snum));
if (invalid &&
str_list_substitute(invalid, "%S", lp_servicename(snum))) {
@@ -595,11 +599,10 @@ static bool user_ok(const char *user, int snum)
}
}
}
- if (invalid)
- str_list_free (&invalid);
+ TALLOC_FREE(invalid);
if (ret && lp_valid_users(snum)) {
- str_list_copy(&valid, lp_valid_users(snum));
+ str_list_copy(talloc_tos(), &valid, lp_valid_users(snum));
if ( valid &&
str_list_substitute(valid, "%S", lp_servicename(snum)) ) {
@@ -611,17 +614,17 @@ static bool user_ok(const char *user, int snum)
}
}
}
- if (valid)
- str_list_free (&valid);
+ TALLOC_FREE(valid);
if (ret && lp_onlyuser(snum)) {
- char **user_list = str_list_make (lp_username(snum), NULL);
+ char **user_list = str_list_make(
+ talloc_tos(), lp_username(snum), NULL);
if (user_list &&
str_list_substitute(user_list, "%S",
lp_servicename(snum))) {
ret = user_in_list(user, (const char **)user_list);
}
- if (user_list) str_list_free (&user_list);
+ TALLOC_FREE(user_list);
}
return(ret);
@@ -759,6 +762,7 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password,
if (!ok) {
char *auser;
char *user_list = NULL;
+ char *saveptr;
if ( session_userlist )
user_list = SMB_STRDUP(session_userlist);
@@ -768,8 +772,9 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password,
if (!user_list)
return(False);
- for (auser=strtok(user_list,LIST_SEP); !ok && auser;
- auser = strtok(NULL,LIST_SEP)) {
+ for (auser = strtok_r(user_list, LIST_SEP, &saveptr);
+ !ok && auser;
+ auser = strtok_r(NULL, LIST_SEP, &saveptr)) {
fstring user2;
fstrcpy(user2,auser);
if (!user_ok(user2,snum))
@@ -792,6 +797,7 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password,
TALLOC_CTX *ctx = talloc_tos();
char *auser;
char *user_list = talloc_strdup(ctx, lp_username(snum));
+ char *saveptr;
if (!user_list) {
goto check_guest;
@@ -806,8 +812,9 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password,
goto check_guest;
}
- for (auser=strtok(user_list,LIST_SEP); auser && !ok;
- auser = strtok(NULL,LIST_SEP)) {
+ for (auser = strtok_r(user_list, LIST_SEP, &saveptr);
+ auser && !ok;
+ auser = strtok_r(NULL, LIST_SEP, &saveptr)) {
if (*auser == '@') {
auser = validate_group(auser+1,password,snum);
if (auser) {
diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c
index 5f18615f66..f60329a039 100644
--- a/source3/smbd/posix_acls.c
+++ b/source3/smbd/posix_acls.c
@@ -1408,12 +1408,12 @@ static bool create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
-
+
} else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
-
+
}
}
}
@@ -1474,10 +1474,22 @@ static bool create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
} else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid)) {
current_ace->owner_type = UID_ACE;
- current_ace->type = SMB_ACL_USER;
+ /* If it's the owning user, this is a user_obj, not
+ * a user. */
+ if (current_ace->unix_ug.uid == pst->st_uid) {
+ current_ace->type = SMB_ACL_USER_OBJ;
+ } else {
+ current_ace->type = SMB_ACL_USER;
+ }
} else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid)) {
current_ace->owner_type = GID_ACE;
- current_ace->type = SMB_ACL_GROUP;
+ /* If it's the primary group, this is a group_obj, not
+ * a group. */
+ if (current_ace->unix_ug.gid == pst->st_gid) {
+ current_ace->type = SMB_ACL_GROUP_OBJ;
+ } else {
+ current_ace->type = SMB_ACL_GROUP;
+ }
} else {
/*
* Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
@@ -3202,57 +3214,8 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
return ret;
}
-static NTSTATUS append_ugw_ace(files_struct *fsp,
- SMB_STRUCT_STAT *psbuf,
- mode_t unx_mode,
- int ugw,
- SEC_ACE *se)
-{
- mode_t perms;
- SEC_ACCESS acc;
- enum security_ace_type nt_acl_type;
- DOM_SID trustee;
-
- switch (ugw) {
- case S_IRUSR:
- perms = unix_perms_to_acl_perms(unx_mode,
- S_IRUSR,
- S_IWUSR,
- S_IXUSR);
- uid_to_sid(&trustee, psbuf->st_uid );
- break;
- case S_IRGRP:
- perms = unix_perms_to_acl_perms(unx_mode,
- S_IRGRP,
- S_IWGRP,
- S_IXGRP);
- gid_to_sid(&trustee, psbuf->st_gid );
- break;
- case S_IROTH:
- perms = unix_perms_to_acl_perms(unx_mode,
- S_IROTH,
- S_IWOTH,
- S_IXOTH);
- sid_copy(&trustee, &global_sid_World);
- break;
- default:
- return NT_STATUS_INVALID_PARAMETER;
- }
- acc = map_canon_ace_perms(SNUM(fsp->conn),
- &nt_acl_type,
- perms,
- fsp->is_directory);
-
- init_sec_ace(se,
- &trustee,
- nt_acl_type,
- acc,
- 0);
- return NT_STATUS_OK;
-}
-
/****************************************************************************
- If this is an
+ Take care of parent ACL inheritance.
****************************************************************************/
static NTSTATUS append_parent_acl(files_struct *fsp,
@@ -3270,7 +3233,7 @@ static NTSTATUS append_parent_acl(files_struct *fsp,
NTSTATUS status;
int info;
unsigned int i, j;
- mode_t unx_mode;
+ bool is_dacl_protected = (psd->type & SE_DESC_DACL_PROTECTED);
ZERO_STRUCT(sbuf);
@@ -3285,12 +3248,6 @@ static NTSTATUS append_parent_acl(files_struct *fsp,
return NT_STATUS_NO_MEMORY;
}
- /* Create a default mode for u/g/w. */
- unx_mode = unix_mode(fsp->conn,
- aARCH | (fsp->is_directory ? aDIR : 0),
- fsp->fsp_name,
- parent_name);
-
status = open_directory(fsp->conn,
NULL,
parent_name,
@@ -3318,20 +3275,23 @@ static NTSTATUS append_parent_acl(files_struct *fsp,
/*
* Make room for potentially all the ACLs from
- * the parent, plus the user/group/other triple.
+ * the parent. We used to add the ugw triple here,
+ * as we knew we were dealing with POSIX ACLs.
+ * We no longer need to do so as we can guarentee
+ * that a default ACL from the parent directory will
+ * be well formed for POSIX ACLs if it came from a
+ * POSIX ACL source, and if we're not writing to a
+ * POSIX ACL sink then we don't care if it's not well
+ * formed. JRA.
*/
- num_aces += parent_sd->dacl->num_aces + 3;
+ num_aces += parent_sd->dacl->num_aces;
if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE,
num_aces)) == NULL) {
return NT_STATUS_NO_MEMORY;
}
- DEBUG(10,("append_parent_acl: parent ACL has %u entries. New "
- "ACL has %u entries\n",
- parent_sd->dacl->num_aces, num_aces ));
-
/* Start by copying in all the given ACE entries. */
for (i = 0; i < psd->dacl->num_aces; i++) {
sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]);
@@ -3342,49 +3302,95 @@ static NTSTATUS append_parent_acl(files_struct *fsp,
* as that really only applies to newly created files. JRA.
*/
- /*
- * Append u/g/w.
- */
-
- status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRUSR, &new_ace[i++]);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRGRP, &new_ace[i++]);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- status = append_ugw_ace(fsp, psbuf, unx_mode, S_IROTH, &new_ace[i++]);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
/* Finally append any inherited ACEs. */
for (j = 0; j < parent_sd->dacl->num_aces; j++) {
SEC_ACE *se = &parent_sd->dacl->aces[j];
- uint32 i_flags = se->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
- SEC_ACE_FLAG_CONTAINER_INHERIT|
- SEC_ACE_FLAG_INHERIT_ONLY);
if (fsp->is_directory) {
- if (i_flags == SEC_ACE_FLAG_OBJECT_INHERIT) {
- /* Should only apply to a file - ignore. */
+ if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ /* Doesn't apply to a directory - ignore. */
+ DEBUG(10,("append_parent_acl: directory %s "
+ "ignoring non container "
+ "inherit flags %u on ACE with sid %s "
+ "from parent %s\n",
+ fsp->fsp_name,
+ (unsigned int)se->flags,
+ sid_string_dbg(&se->trustee),
+ parent_name));
continue;
}
} else {
- if ((i_flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
- SEC_ACE_FLAG_INHERIT_ONLY)) !=
- SEC_ACE_FLAG_OBJECT_INHERIT) {
- /* Should not apply to a file - ignore. */
+ if (!(se->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
+ /* Doesn't apply to a file - ignore. */
+ DEBUG(10,("append_parent_acl: file %s "
+ "ignoring non object "
+ "inherit flags %u on ACE with sid %s "
+ "from parent %s\n",
+ fsp->fsp_name,
+ (unsigned int)se->flags,
+ sid_string_dbg(&se->trustee),
+ parent_name));
+ continue;
+ }
+ }
+
+ if (is_dacl_protected) {
+ /* If the DACL is protected it means we must
+ * not overwrite an existing ACE entry with the
+ * same SID. This is order N^2. Ouch :-(. JRA. */
+ unsigned int k;
+ for (k = 0; k < psd->dacl->num_aces; k++) {
+ if (sid_equal(&psd->dacl->aces[k].trustee,
+ &se->trustee)) {
+ break;
+ }
+ }
+ if (k < psd->dacl->num_aces) {
+ /* SID matched. Ignore. */
+ DEBUG(10,("append_parent_acl: path %s "
+ "ignoring ACE with protected sid %s "
+ "from parent %s\n",
+ fsp->fsp_name,
+ sid_string_dbg(&se->trustee),
+ parent_name));
continue;
}
}
+
sec_ace_copy(&new_ace[i], se);
if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT);
}
new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE;
+
+ if (fsp->is_directory) {
+ /*
+ * Strip off any inherit only. It's applied.
+ */
+ new_ace[i].flags &= ~(SEC_ACE_FLAG_INHERIT_ONLY);
+ if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ /* No further inheritance. */
+ new_ace[i].flags &=
+ ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_OBJECT_INHERIT);
+ }
+ } else {
+ /*
+ * Strip off any container or inherit
+ * flags, they can't apply to objects.
+ */
+ new_ace[i].flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY|
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
+ }
i++;
+
+ DEBUG(10,("append_parent_acl: path %s "
+ "inheriting ACE with sid %s "
+ "from parent %s\n",
+ fsp->fsp_name,
+ sid_string_dbg(&se->trustee),
+ parent_name));
}
parent_sd->dacl->aces = new_ace;
@@ -3413,6 +3419,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 +3444,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 +3460,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 +3502,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 +3650,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;
}
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index a5bdb96650..68bec7830a 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -20,12 +20,8 @@
#include "includes.h"
-extern struct auth_context *negprot_global_auth_context;
extern int smb_echo_count;
-const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
-static enum smb_read_errors smb_read_error = SMB_READ_OK;
-
/*
* Size of data we can send to client. Set
* by the client for all protocols above CORE.
@@ -45,11 +41,6 @@ extern int max_send;
/* Accessor function for smb_read_error for smbd functions. */
-enum smb_read_errors *get_srv_read_error(void)
-{
- return &smb_read_error;
-}
-
/****************************************************************************
Send an smb to a fd.
****************************************************************************/
@@ -130,50 +121,20 @@ static bool valid_packet_size(size_t len)
DEBUG(0,("Invalid packet length! (%lu bytes).\n",
(unsigned long)len));
if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) {
-
- /*
- * Correct fix. smb_read_error may have already been
- * set. Only set it here if not already set. Global
- * variables still suck :-). JRA.
- */
-
- cond_set_smb_read_error(get_srv_read_error(),
- SMB_READ_ERROR);
return false;
}
}
return true;
}
-static ssize_t read_packet_remainder(int fd,
- char *buffer,
- unsigned int timeout,
- ssize_t len)
+static NTSTATUS read_packet_remainder(int fd, char *buffer,
+ unsigned int timeout, ssize_t len)
{
- ssize_t ret;
-
- if(len <= 0) {
- return len;
- }
-
- if (timeout > 0) {
- ret = read_socket_with_timeout(fd,
- buffer,
- len,
- len,
- timeout,
- get_srv_read_error());
- } else {
- ret = read_data(fd, buffer, len, get_srv_read_error());
- }
-
- if (ret != len) {
- cond_set_smb_read_error(get_srv_read_error(),
- SMB_READ_ERROR);
- return -1;
+ if (len <= 0) {
+ return NT_STATUS_OK;
}
- return len;
+ return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL);
}
/****************************************************************************
@@ -192,39 +153,29 @@ static ssize_t read_packet_remainder(int fd,
(2*14) + /* word count (including bcc) */ \
1 /* pad byte */)
-static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
- const char lenbuf[4],
- int fd,
- char **buffer,
- unsigned int timeout,
- size_t *p_unread)
+static NTSTATUS receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
+ const char lenbuf[4],
+ int fd, char **buffer,
+ unsigned int timeout,
+ size_t *p_unread,
+ size_t *len_ret)
{
/* Size of a WRITEX call (+4 byte len). */
char writeX_header[4 + STANDARD_WRITE_AND_X_HEADER_SIZE];
ssize_t len = smb_len_large(lenbuf); /* Could be a UNIX large writeX. */
ssize_t toread;
- ssize_t ret;
+ NTSTATUS status;
memcpy(writeX_header, lenbuf, sizeof(lenbuf));
- if (timeout > 0) {
- ret = read_socket_with_timeout(fd,
- writeX_header + 4,
- STANDARD_WRITE_AND_X_HEADER_SIZE,
- STANDARD_WRITE_AND_X_HEADER_SIZE,
- timeout,
- get_srv_read_error());
- } else {
- ret = read_data(fd,
- writeX_header+4,
- STANDARD_WRITE_AND_X_HEADER_SIZE,
- get_srv_read_error());
- }
+ status = read_socket_with_timeout(
+ fd, writeX_header + 4,
+ STANDARD_WRITE_AND_X_HEADER_SIZE,
+ STANDARD_WRITE_AND_X_HEADER_SIZE,
+ timeout, NULL);
- if (ret != STANDARD_WRITE_AND_X_HEADER_SIZE) {
- cond_set_smb_read_error(get_srv_read_error(),
- SMB_READ_ERROR);
- return -1;
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
/*
@@ -263,19 +214,17 @@ static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
if (*buffer == NULL) {
DEBUG(0, ("Could not allocate inbuf of length %d\n",
(int)sizeof(writeX_header)));
- cond_set_smb_read_error(get_srv_read_error(),
- SMB_READ_ERROR);
- return -1;
+ return NT_STATUS_NO_MEMORY;
}
/* Work out the remaining bytes. */
*p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
-
- return newlen + 4;
+ *len_ret = newlen + 4;
+ return NT_STATUS_OK;
}
if (!valid_packet_size(len)) {
- return -1;
+ return NT_STATUS_INVALID_PARAMETER;
}
/*
@@ -288,9 +237,7 @@ static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
if (*buffer == NULL) {
DEBUG(0, ("Could not allocate inbuf of length %d\n",
(int)len+4));
- cond_set_smb_read_error(get_srv_read_error(),
- SMB_READ_ERROR);
- return -1;
+ return NT_STATUS_NO_MEMORY;
}
/* Copy in what we already read. */
@@ -300,44 +247,34 @@ static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
if(toread > 0) {
- ret = read_packet_remainder(fd,
- (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE,
- timeout,
- toread);
- if (ret != toread) {
- return -1;
+ status = read_packet_remainder(
+ fd, (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE,
+ timeout, toread);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
}
- return len + 4;
+ *len_ret = len + 4;
+ return NT_STATUS_OK;
}
-static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx,
- int fd,
- char **buffer,
- unsigned int timeout,
- size_t *p_unread)
+static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
+ char **buffer, unsigned int timeout,
+ size_t *p_unread, size_t *plen)
{
char lenbuf[4];
- ssize_t len,ret;
+ size_t len;
int min_recv_size = lp_min_receive_file_size();
+ NTSTATUS status;
- set_smb_read_error(get_srv_read_error(),SMB_READ_OK);
*p_unread = 0;
- len = read_smb_length_return_keepalive(fd, lenbuf,
- timeout, get_srv_read_error());
- if (len < 0) {
- DEBUG(10,("receive_smb_raw: length < 0!\n"));
-
- /*
- * Correct fix. smb_read_error may have already been
- * set. Only set it here if not already set. Global
- * variables still suck :-). JRA.
- */
-
- cond_set_smb_read_error(get_srv_read_error(),SMB_READ_ERROR);
- return -1;
+ status = read_smb_length_return_keepalive(fd, lenbuf, timeout, &len);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("receive_smb_raw: %s\n", nt_errstr(status)));
+ return status;
}
if (CVAL(lenbuf,0) == 0 &&
@@ -345,16 +282,18 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx,
smb_len_large(lenbuf) > min_recv_size && /* Could be a UNIX large writeX. */
!srv_is_signing_active()) {
- return receive_smb_raw_talloc_partial_read(mem_ctx,
- lenbuf,
- fd,
- buffer,
- timeout,
- p_unread);
+ status = receive_smb_raw_talloc_partial_read(
+ mem_ctx, lenbuf, fd, buffer, timeout, p_unread, &len);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("receive_smb_raw: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
}
if (!valid_packet_size(len)) {
- return -1;
+ return NT_STATUS_INVALID_PARAMETER;
}
/*
@@ -366,46 +305,43 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx,
if (*buffer == NULL) {
DEBUG(0, ("Could not allocate inbuf of length %d\n",
(int)len+4));
- cond_set_smb_read_error(get_srv_read_error(),SMB_READ_ERROR);
- return -1;
+ return NT_STATUS_NO_MEMORY;
}
memcpy(*buffer, lenbuf, sizeof(lenbuf));
- ret = read_packet_remainder(fd, (*buffer)+4, timeout, len);
- if (ret != len) {
- return -1;
+ status = read_packet_remainder(fd, (*buffer)+4, timeout, len);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- return len + 4;
+ *plen = len + 4;
+ return NT_STATUS_OK;
}
-static ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx,
- int fd,
- char **buffer,
- unsigned int timeout,
- size_t *p_unread,
- bool *p_encrypted)
+static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd,
+ char **buffer, unsigned int timeout,
+ size_t *p_unread, bool *p_encrypted,
+ size_t *p_len)
{
- ssize_t len;
+ size_t len = 0;
+ NTSTATUS status;
*p_encrypted = false;
- len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout, p_unread);
-
- if (len < 0) {
- return -1;
+ status = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout,
+ p_unread, &len);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
if (is_encrypted_packet((uint8_t *)*buffer)) {
- NTSTATUS status = srv_decrypt_buffer(*buffer);
+ status = srv_decrypt_buffer(*buffer);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("receive_smb_talloc: SMB decryption failed on "
"incoming packet! Error %s\n",
nt_errstr(status) ));
- cond_set_smb_read_error(get_srv_read_error(),
- SMB_READ_BAD_DECRYPT);
- return -1;
+ return status;
}
*p_encrypted = true;
}
@@ -414,11 +350,11 @@ static ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx,
if (!srv_check_sign_mac(*buffer, true)) {
DEBUG(0, ("receive_smb: SMB Signature verification failed on "
"incoming packet!\n"));
- cond_set_smb_read_error(get_srv_read_error(),SMB_READ_BAD_SIG);
- return -1;
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
- return len;
+ *p_len = len;
+ return NT_STATUS_OK;
}
/*
@@ -775,21 +711,18 @@ static int select_on_fd(int fd, int maxfd, fd_set *fds)
The timeout is in milliseconds
****************************************************************************/
-static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
- char **buffer,
- size_t *buffer_len,
- int timeout,
- size_t *p_unread,
- bool *p_encrypted)
+static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
+ size_t *buffer_len, int timeout,
+ size_t *p_unread, bool *p_encrypted)
{
fd_set r_fds, w_fds;
int selrtn;
struct timeval to;
int maxfd = 0;
- ssize_t len;
+ size_t len = 0;
+ NTSTATUS status;
*p_unread = 0;
- set_smb_read_error(get_srv_read_error(),SMB_READ_OK);
again:
@@ -843,8 +776,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
msg->buf.length);
if (*buffer == NULL) {
DEBUG(0, ("talloc failed\n"));
- set_smb_read_error(get_srv_read_error(),SMB_READ_ERROR);
- return False;
+ return NT_STATUS_NO_MEMORY;
}
*buffer_len = msg->buf.length;
*p_encrypted = msg->encrypted;
@@ -852,7 +784,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
/* We leave this message on the queue so the open code can
know this is a retry. */
DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n"));
- return True;
+ return NT_STATUS_OK;
}
}
@@ -938,14 +870,12 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
/* Check if error */
if (selrtn == -1) {
/* something is wrong. Maybe the socket is dead? */
- set_smb_read_error(get_srv_read_error(),SMB_READ_ERROR);
- return False;
+ return map_nt_error_from_unix(errno);
}
/* Did we timeout ? */
if (selrtn == 0) {
- set_smb_read_error(get_srv_read_error(),SMB_READ_TIMEOUT);
- return False;
+ return NT_STATUS_IO_TIMEOUT;
}
/*
@@ -964,16 +894,16 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
goto again;
}
- len = receive_smb_talloc(mem_ctx, smbd_server_fd(),
- buffer, 0, p_unread, p_encrypted);
+ status = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0,
+ p_unread, p_encrypted, &len);
- if (len == -1) {
- return False;
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- *buffer_len = (size_t)len;
+ *buffer_len = len;
- return True;
+ return NT_STATUS_OK;
}
/*
@@ -1951,27 +1881,11 @@ void check_reload(time_t t)
Process any timeout housekeeping. Return False if the caller should exit.
****************************************************************************/
-static bool timeout_processing(int *select_timeout,
+static void timeout_processing(int *select_timeout,
time_t *last_timeout_processing_time)
{
time_t t;
- if (*get_srv_read_error() == SMB_READ_EOF) {
- DEBUG(3,("timeout_processing: End of file from client (client has disconnected).\n"));
- return false;
- }
-
- if (*get_srv_read_error() == SMB_READ_ERROR) {
- DEBUG(3,("timeout_processing: receive_smb error (%s) Exiting\n",
- strerror(errno)));
- return false;
- }
-
- if (*get_srv_read_error() == SMB_READ_BAD_SIG) {
- DEBUG(3,("timeout_processing: receive_smb error bad smb signature. Exiting\n"));
- return false;
- }
-
*last_timeout_processing_time = t = time(NULL);
/* become root again if waiting */
@@ -2001,14 +1915,14 @@ static bool timeout_processing(int *select_timeout,
if (secrets_lock_trust_account_password(lp_workgroup(), True) == False) {
DEBUG(0,("process: unable to lock the machine account password for \
machine %s in domain %s.\n", global_myname(), lp_workgroup() ));
- return True;
+ return;
}
if(!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd_hash, &lct, NULL)) {
DEBUG(0,("process: unable to read the machine account password for \
machine %s in domain %s.\n", global_myname(), lp_workgroup()));
secrets_lock_trust_account_password(lp_workgroup(), False);
- return True;
+ return;
}
/*
@@ -2018,7 +1932,7 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup()));
if(t < lct + lp_machine_password_timeout()) {
global_machine_password_needs_changing = False;
secrets_lock_trust_account_password(lp_workgroup(), False);
- return True;
+ return;
}
/* always just contact the PDC here */
@@ -2050,7 +1964,7 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup()));
*select_timeout = setup_select_timeout();
- return True;
+ return;
}
/****************************************************************************
@@ -2068,8 +1982,8 @@ void smbd_process(void)
while (True) {
int select_timeout = setup_select_timeout();
int num_echos;
- char *inbuf;
- size_t inbuf_len;
+ char *inbuf = NULL;
+ size_t inbuf_len = 0;
bool encrypted = false;
TALLOC_CTX *frame = talloc_stackframe_pool(8192);
@@ -2077,21 +1991,35 @@ void smbd_process(void)
/* Did someone ask for immediate checks on things like blocking locks ? */
if (select_timeout == 0) {
- if(!timeout_processing(&select_timeout,
- &last_timeout_processing_time))
- return;
+ timeout_processing(&select_timeout,
+ &last_timeout_processing_time);
num_smbs = 0; /* Reset smb counter. */
}
run_events(smbd_event_context(), 0, NULL, NULL);
- while (!receive_message_or_smb(talloc_tos(), &inbuf, &inbuf_len,
- select_timeout,
- &unread_bytes,
- &encrypted)) {
- if(!timeout_processing(&select_timeout,
- &last_timeout_processing_time))
- return;
+ while (True) {
+ NTSTATUS status;
+
+ status = receive_message_or_smb(
+ talloc_tos(), &inbuf, &inbuf_len,
+ select_timeout, &unread_bytes, &encrypted);
+
+ if (NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ timeout_processing(
+ &select_timeout,
+ &last_timeout_processing_time);
+ continue;
+ }
+
+ DEBUG(3, ("receive_message_or_smb failed: %s, "
+ "exiting\n", nt_errstr(status)));
+ return;
+
num_smbs = 0; /* Reset smb counter. */
}
@@ -2112,8 +2040,8 @@ void smbd_process(void)
TALLOC_FREE(inbuf);
if (smb_echo_count != num_echos) {
- if(!timeout_processing( &select_timeout, &last_timeout_processing_time))
- return;
+ timeout_processing(&select_timeout,
+ &last_timeout_processing_time);
num_smbs = 0; /* Reset smb counter. */
}
@@ -2129,10 +2057,9 @@ void smbd_process(void)
if ((num_smbs % 200) == 0) {
time_t new_check_time = time(NULL);
if(new_check_time - last_timeout_processing_time >= (select_timeout/1000)) {
- if(!timeout_processing(
- &select_timeout,
- &last_timeout_processing_time))
- return;
+ timeout_processing(
+ &select_timeout,
+ &last_timeout_processing_time);
num_smbs = 0; /* Reset smb counter. */
last_timeout_processing_time = new_check_time; /* Reset time. */
}
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index e2316ef120..818ff319e4 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -167,6 +167,7 @@ static NTSTATUS check_path_syntax_internal(char *path,
}
*d = '\0';
+
return ret;
}
@@ -1109,6 +1110,12 @@ void reply_setatr(struct smb_request *req)
mode = SVAL(req->inbuf,smb_vwv0);
mtime = srv_make_unix_date3(req->inbuf+smb_vwv1);
+ if (!set_filetime(conn,fname,convert_time_t_to_timespec(mtime))) {
+ reply_unixerror(req, ERRDOS, ERRnoaccess);
+ END_PROFILE(SMBsetatr);
+ return;
+ }
+
if (mode != FILE_ATTRIBUTE_NORMAL) {
if (VALID_STAT_OF_DIR(sbuf))
mode |= aDIR;
@@ -1122,12 +1129,6 @@ void reply_setatr(struct smb_request *req)
}
}
- if (!set_filetime(conn,fname,convert_time_t_to_timespec(mtime))) {
- reply_unixerror(req, ERRDOS, ERRnoaccess);
- END_PROFILE(SMBsetatr);
- return;
- }
-
reply_outbuf(req, 0, 0);
DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) );
@@ -1638,11 +1639,11 @@ void reply_open(struct smb_request *req)
}
size = sbuf.st_size;
- fattr = dos_mode(conn,fname,&sbuf);
+ fattr = dos_mode(conn,fsp->fsp_name,&sbuf);
mtime = sbuf.st_mtime;
if (fattr & aDIR) {
- DEBUG(3,("attempt to open a directory %s\n",fname));
+ DEBUG(3,("attempt to open a directory %s\n",fsp->fsp_name));
close_file(fsp,ERROR_CLOSE);
reply_doserror(req, ERRDOS,ERRnoaccess);
END_PROFILE(SMBopen);
@@ -1801,7 +1802,7 @@ void reply_open_and_X(struct smb_request *req)
sbuf.st_size = get_allocation_size(conn,fsp,&sbuf);
}
- fattr = dos_mode(conn,fname,&sbuf);
+ fattr = dos_mode(conn,fsp->fsp_name,&sbuf);
mtime = sbuf.st_mtime;
if (fattr & aDIR) {
close_file(fsp,ERROR_CLOSE);
@@ -1984,7 +1985,7 @@ void reply_mknew(struct smb_request *req)
}
ts[0] = get_atimespec(&sbuf); /* atime. */
- file_ntimes(conn, fname, ts);
+ file_ntimes(conn, fsp->fsp_name, ts);
reply_outbuf(req, 1, 0);
SSVAL(req->outbuf,smb_vwv0,fsp->fnum);
@@ -1999,9 +2000,9 @@ void reply_mknew(struct smb_request *req)
CVAL(req->outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
}
- DEBUG( 2, ( "reply_mknew: file %s\n", fname ) );
+ DEBUG( 2, ( "reply_mknew: file %s\n", fsp->fsp_name ) );
DEBUG( 3, ( "reply_mknew %s fd=%d dmode=0x%x\n",
- fname, fsp->fh->fd, (unsigned int)fattr ) );
+ fsp->fsp_name, fsp->fh->fd, (unsigned int)fattr ) );
END_PROFILE(SMBcreate);
return;
@@ -2124,9 +2125,9 @@ void reply_ctemp(struct smb_request *req)
SSVAL(req->outbuf,smb_vwv0,fsp->fnum);
/* the returned filename is relative to the directory */
- s = strrchr_m(fname, '/');
+ s = strrchr_m(fsp->fsp_name, '/');
if (!s) {
- s = fname;
+ s = fsp->fsp_name;
} else {
s++;
}
@@ -2153,9 +2154,9 @@ void reply_ctemp(struct smb_request *req)
CVAL(req->outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
}
- DEBUG( 2, ( "reply_ctemp: created temp file %s\n", fname ) );
- DEBUG( 3, ( "reply_ctemp %s fd=%d umode=0%o\n", fname, fsp->fh->fd,
- (unsigned int)sbuf.st_mode ) );
+ DEBUG( 2, ( "reply_ctemp: created temp file %s\n", fsp->fsp_name ) );
+ DEBUG( 3, ( "reply_ctemp %s fd=%d umode=0%o\n", fsp->fsp_name,
+ fsp->fh->fd, (unsigned int)sbuf.st_mode ) );
END_PROFILE(SMBctemp);
return;
@@ -2289,14 +2290,22 @@ static NTSTATUS do_unlink(connection_struct *conn,
/* On open checks the open itself will check the share mode, so
don't do it here as we'll get it wrong. */
- status = open_file_ntcreate(conn, req, fname, &sbuf,
- DELETE_ACCESS,
- FILE_SHARE_NONE,
- FILE_OPEN,
- 0,
- FILE_ATTRIBUTE_NORMAL,
- req != NULL ? 0 : INTERNAL_OPEN_ONLY,
- NULL, &fsp);
+ status = create_file_unixpath
+ (conn, /* conn */
+ req, /* req */
+ fname, /* fname */
+ DELETE_ACCESS, /* access_mask */
+ FILE_SHARE_NONE, /* share_access */
+ FILE_OPEN, /* create_disposition*/
+ FILE_NON_DIRECTORY_FILE, /* create_options */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes */
+ 0, /* oplock_request */
+ 0, /* allocation_size */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ &sbuf); /* psbuf */
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("open_file_ntcreate failed: %s\n",
@@ -2460,7 +2469,7 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req,
}
count++;
- DEBUG(3,("unlink_internals: succesful unlink [%s]\n",
+ DEBUG(3,("unlink_internals: successful unlink [%s]\n",
fname));
TALLOC_FREE(fname);
@@ -3329,8 +3338,8 @@ void reply_read_and_X(struct smb_request *req)
return;
}
- if (!big_readX
- && schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) {
+ if (!big_readX &&
+ schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) {
END_PROFILE(SMBreadX);
return;
}
@@ -3362,7 +3371,6 @@ void error_to_writebrawerr(struct smb_request *req)
void reply_writebraw(struct smb_request *req)
{
connection_struct *conn = req->conn;
- int outsize = 0;
char *buf = NULL;
ssize_t nwritten=0;
ssize_t total_written=0;
@@ -3472,8 +3480,7 @@ void reply_writebraw(struct smb_request *req)
* it to send more bytes */
memcpy(buf, req->inbuf, smb_size);
- outsize = srv_set_message(buf,
- Protocol>PROTOCOL_COREPLUS?1:0,0,True);
+ srv_set_message(buf,Protocol>PROTOCOL_COREPLUS?1:0,0,True);
SCVAL(buf,smb_com,SMBwritebraw);
SSVALS(buf,smb_vwv0,0xFFFF);
show_msg(buf);
@@ -3485,18 +3492,12 @@ void reply_writebraw(struct smb_request *req)
}
/* Now read the raw data into the buffer and write it */
- if (read_smb_length(smbd_server_fd(),buf,
- SMB_SECONDARY_WAIT, get_srv_read_error()) == -1) {
+ status = read_smb_length(smbd_server_fd(), buf, SMB_SECONDARY_WAIT,
+ &numtowrite);
+ if (!NT_STATUS_IS_OK(status)) {
exit_server_cleanly("secondary writebraw failed");
}
- /*
- * Even though this is not an smb message,
- * smb_len returns the generic length of a packet.
- */
-
- numtowrite = smb_len(buf);
-
/* Set up outbuf to return the correct size */
reply_outbuf(req, 1, 0);
@@ -3515,11 +3516,12 @@ void reply_writebraw(struct smb_request *req)
(int)tcount,(int)nwritten,(int)numtowrite));
}
- if (read_data(smbd_server_fd(), buf+4, numtowrite,get_srv_read_error())
- != numtowrite ) {
+ status = read_data(smbd_server_fd(), buf+4, numtowrite);
+
+ if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("reply_writebraw: Oversize secondary write "
- "raw read failed (%s). Terminating\n",
- strerror(errno) ));
+ "raw read failed (%s). Terminating\n",
+ nt_errstr(status)));
exit_server_cleanly("secondary writebraw failed");
}
@@ -4001,13 +4003,13 @@ 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)) {
+ if ((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);
}
diff --git a/source3/smbd/seal.c b/source3/smbd/seal.c
index ea017e08d8..d4394e5bca 100644
--- a/source3/smbd/seal.c
+++ b/source3/smbd/seal.c
@@ -496,12 +496,15 @@ static NTSTATUS srv_enc_spnego_negotiate(connection_struct *conn,
srv_free_encryption_context(&partial_srv_trans_enc_ctx);
+ if (got_kerberos_mechanism) {
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
- if (got_kerberos_mechanism && lp_use_kerberos_keytab() ) {
status = srv_enc_spnego_gss_negotiate(ppdata, p_data_size, secblob);
- } else
+#else
+ /* Currently we don't SPNEGO negotiate
+ * back to NTLMSSP as we do in sessionsetupX. We should... */
+ return NT_STATUS_LOGON_FAILURE;
#endif
- {
+ } else {
status = srv_enc_ntlm_negotiate(ppdata, p_data_size, secblob, true);
}
@@ -558,7 +561,16 @@ static NTSTATUS srv_enc_spnego_ntlm_auth(connection_struct *conn,
status = auth_ntlmssp_update(ec->auth_ntlmssp_state, auth, &auth_reply);
data_blob_free(&auth);
- response = spnego_gen_auth_response(&auth_reply, status, OID_NTLMSSP);
+ /* From RFC4178.
+ *
+ * supportedMech
+ *
+ * This field SHALL only be present in the first reply from the
+ * target.
+ * So set mechOID to NULL here.
+ */
+
+ response = spnego_gen_auth_response(&auth_reply, status, NULL);
data_blob_free(&auth_reply);
if (NT_STATUS_IS_OK(status)) {
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 8371d17f10..7bf11abfbd 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -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.
****************************************************************************/
@@ -929,7 +988,8 @@ static bool init_structs(void )
init_dptrs();
- secrets_init();
+ if (!secrets_init())
+ return False;
return True;
}
@@ -1254,12 +1314,6 @@ extern 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);
@@ -1305,6 +1359,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,
diff --git a/source3/smbd/service.c b/source3/smbd/service.c
index 2588a66b8b..a8aa25405a 100644
--- a/source3/smbd/service.c
+++ b/source3/smbd/service.c
@@ -219,44 +219,6 @@ bool set_current_service(connection_struct *conn, uint16 flags, bool do_chdir)
return(True);
}
-/****************************************************************************
- Add a home service. Returns the new service number or -1 if fail.
-****************************************************************************/
-
-int add_home_service(const char *service, const char *username, const char *homedir)
-{
- int iHomeService;
-
- if (!service || !homedir)
- return -1;
-
- if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
- return -1;
-
- /*
- * If this is a winbindd provided username, remove
- * the domain component before adding the service.
- * Log a warning if the "path=" parameter does not
- * include any macros.
- */
-
- {
- const char *p = strchr(service,*lp_winbind_separator());
-
- /* We only want the 'user' part of the string */
- if (p) {
- service = p + 1;
- }
- }
-
- if (!lp_add_home(service, iHomeService, username, homedir)) {
- return -1;
- }
-
- return lp_servicenumber(service);
-
-}
-
static int load_registry_service(const char *servicename)
{
struct registry_key *key;
@@ -348,6 +310,47 @@ void load_registry_shares(void)
return;
}
+/****************************************************************************
+ Add a home service. Returns the new service number or -1 if fail.
+****************************************************************************/
+
+int add_home_service(const char *service, const char *username, const char *homedir)
+{
+ int iHomeService;
+
+ if (!service || !homedir)
+ return -1;
+
+ if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0) {
+ if ((iHomeService = load_registry_service(HOMES_NAME)) < 0) {
+ return -1;
+ }
+ }
+
+ /*
+ * If this is a winbindd provided username, remove
+ * the domain component before adding the service.
+ * Log a warning if the "path=" parameter does not
+ * include any macros.
+ */
+
+ {
+ const char *p = strchr(service,*lp_winbind_separator());
+
+ /* We only want the 'user' part of the string */
+ if (p) {
+ service = p + 1;
+ }
+ }
+
+ if (!lp_add_home(service, iHomeService, username, homedir)) {
+ return -1;
+ }
+
+ return lp_servicenumber(service);
+
+}
+
/**
* Find a service entry.
*
@@ -386,7 +389,10 @@ int find_service(fstring service)
if (iService < 0) {
int iPrinterService;
- if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) {
+ if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) < 0) {
+ iPrinterService = load_registry_service(PRINTERS_NAME);
+ }
+ if (iPrinterService) {
DEBUG(3,("checking whether %s is a valid printer name...\n", service));
if (pcap_printername_ok(service)) {
DEBUG(3,("%s is a valid printer name\n", service));
@@ -1165,16 +1171,8 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser,
* assumes that all the filesystem mounted withing a share path have
* the same characteristics, which is likely but not guaranteed.
*/
- {
- vfs_statvfs_struct svfs;
-
- conn->fs_capabilities =
- FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES;
- if (SMB_VFS_STATVFS(conn, conn->connectpath, &svfs) == 0) {
- conn->fs_capabilities = svfs.FsCapabilities;
- }
- }
+ conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn);
/*
* Print out the 'connected as' stuff here as we need
diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c
index aee8e498e9..9baa02977a 100644
--- a/source3/smbd/sesssetup.c
+++ b/source3/smbd/sesssetup.c
@@ -259,7 +259,7 @@ static void reply_spnego_kerberos(struct smb_request *req,
fstring user;
int sess_vuid = req->vuid;
NTSTATUS ret = NT_STATUS_OK;
- PAC_DATA *pac_data = NULL;
+ struct PAC_DATA *pac_data = NULL;
DATA_BLOB ap_rep, ap_rep_wrapped, response;
auth_serversupplied_info *server_info = NULL;
DATA_BLOB session_key = data_blob_null;
@@ -268,7 +268,7 @@ static void reply_spnego_kerberos(struct smb_request *req,
fstring real_username;
bool map_domainuser_to_guest = False;
bool username_was_mapped;
- PAC_LOGON_INFO *logon_info = NULL;
+ struct PAC_LOGON_INFO *logon_info = NULL;
ZERO_STRUCT(ticket);
ZERO_STRUCT(ap_rep);
@@ -394,10 +394,9 @@ static void reply_spnego_kerberos(struct smb_request *req,
domain = p+1;
- if (logon_info && logon_info->info3.hdr_logon_dom.uni_str_len) {
- unistr2_to_ascii(netbios_domain_name,
- &logon_info->info3.uni_logon_dom,
- sizeof(netbios_domain_name));
+ if (logon_info && logon_info->info3.base.domain.string) {
+ fstrcpy(netbios_domain_name,
+ logon_info->info3.base.domain.string);
domain = netbios_domain_name;
DEBUG(10, ("Mapped to [%s] (using PAC)\n", domain));
@@ -410,30 +409,24 @@ static void reply_spnego_kerberos(struct smb_request *req,
name. And even w2k3 does use ntlmssp if you for example
connect to an ip address. */
- struct winbindd_request wb_request;
- struct winbindd_response wb_response;
- NSS_STATUS wb_result;
-
- ZERO_STRUCT(wb_request);
- ZERO_STRUCT(wb_response);
+ wbcErr wbc_status;
+ struct wbcDomainInfo *info = NULL;
DEBUG(10, ("Mapping [%s] to short name\n", domain));
- fstrcpy(wb_request.domain_name, domain);
-
- wb_result = winbindd_request_response(WINBINDD_DOMAIN_INFO,
- &wb_request, &wb_response);
+ wbc_status = wbcDomainInfo(domain, &info);
- if (wb_result == NSS_STATUS_SUCCESS) {
+ if (WBC_ERROR_IS_OK(wbc_status)) {
fstrcpy(netbios_domain_name,
- wb_response.data.domain_info.name);
- domain = netbios_domain_name;
+ info->short_name);
+ wbcFreeMemory(info);
+ domain = netbios_domain_name;
DEBUG(10, ("Mapped to [%s] (using Winbind)\n", domain));
} else {
- DEBUG(3, ("Could not find short name -- winbind "
- "not running?\n"));
+ DEBUG(3, ("Could not find short name: %s\n",
+ wbcErrorString(wbc_status)));
}
}
@@ -626,6 +619,7 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
uint16 vuid,
AUTH_NTLMSSP_STATE **auth_ntlmssp_state,
DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status,
+ const char *OID,
bool wrap)
{
DATA_BLOB response;
@@ -686,7 +680,7 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
if (wrap) {
response = spnego_gen_auth_response(ntlmssp_blob,
- nt_status, OID_NTLMSSP);
+ nt_status, OID);
} else {
response = *ntlmssp_blob;
}
@@ -751,6 +745,28 @@ NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in, DATA_BLOB *pblob_out,
}
/****************************************************************************
+ Fall back from krb5 to NTLMSSP.
+****************************************************************************/
+
+static void reply_spnego_downgrade_to_ntlmssp(struct smb_request *req,
+ uint16 vuid)
+{
+ DATA_BLOB response;
+
+ reply_outbuf(req, 4, 0);
+ SSVAL(req->outbuf,smb_uid,vuid);
+
+ DEBUG(3,("reply_spnego_downgrade_to_ntlmssp: Got krb5 ticket in SPNEGO "
+ "but set to downgrade to NTLMSSP\n"));
+
+ response = spnego_gen_auth_response(NULL,
+ NT_STATUS_MORE_PROCESSING_REQUIRED,
+ OID_NTLMSSP);
+ reply_sesssetup_blob(req, response, NT_STATUS_MORE_PROCESSING_REQUIRED);
+ data_blob_free(&response);
+}
+
+/****************************************************************************
Reply to a session setup spnego negotiate packet.
****************************************************************************/
@@ -795,6 +811,15 @@ static void reply_spnego_negotiate(struct smb_request *req,
auth_ntlmssp_end(auth_ntlmssp_state);
}
+ if (got_kerberos_mechanism) {
+ data_blob_free(&secblob);
+ /* The mechtoken is a krb5 ticket, but
+ * we need to fall back to NTLM. */
+ reply_spnego_downgrade_to_ntlmssp(req,
+ vuid);
+ return;
+ }
+
status = auth_ntlmssp_start(auth_ntlmssp_state);
if (!NT_STATUS_IS_OK(status)) {
/* Kill the intermediate vuid */
@@ -809,7 +834,7 @@ static void reply_spnego_negotiate(struct smb_request *req,
data_blob_free(&secblob);
reply_spnego_ntlmssp(req, vuid, auth_ntlmssp_state,
- &chal, status, True);
+ &chal, status, OID_NTLMSSP, true);
data_blob_free(&chal);
@@ -829,7 +854,7 @@ static void reply_spnego_auth(struct smb_request *req,
DATA_BLOB auth = data_blob_null;
DATA_BLOB auth_reply = data_blob_null;
DATA_BLOB secblob = data_blob_null;
- NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+ NTSTATUS status = NT_STATUS_LOGON_FAILURE;
if (!spnego_parse_auth(blob1, &auth)) {
#if 0
@@ -839,7 +864,7 @@ static void reply_spnego_auth(struct smb_request *req,
invalidate_vuid(vuid);
reply_nterror(req, nt_status_squash(
- NT_STATUS_INVALID_PARAMETER));
+ NT_STATUS_LOGON_FAILURE));
return;
}
@@ -849,24 +874,43 @@ static void reply_spnego_auth(struct smb_request *req,
bool got_krb5_mechanism = False;
status = parse_spnego_mechanisms(auth, &secblob,
&got_krb5_mechanism);
- if (NT_STATUS_IS_OK(status)) {
- DEBUG(3,("reply_spnego_auth: Got secblob of size %lu\n",
- (unsigned long)secblob.length));
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Kill the intermediate vuid */
+ invalidate_vuid(vuid);
+ reply_nterror(req, nt_status_squash(status));
+ return;
+ }
+
+ DEBUG(3,("reply_spnego_auth: Got secblob of size %lu\n",
+ (unsigned long)secblob.length));
#ifdef HAVE_KRB5
- if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) ||
- lp_use_kerberos_keytab()) ) {
- bool destroy_vuid = True;
- reply_spnego_kerberos(req, &secblob,
- vuid, &destroy_vuid);
- data_blob_free(&secblob);
- data_blob_free(&auth);
- if (destroy_vuid) {
- /* Kill the intermediate vuid */
- invalidate_vuid(vuid);
- }
- return;
+ if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) ||
+ lp_use_kerberos_keytab()) ) {
+ bool destroy_vuid = True;
+ reply_spnego_kerberos(req, &secblob,
+ vuid, &destroy_vuid);
+ data_blob_free(&secblob);
+ data_blob_free(&auth);
+ if (destroy_vuid) {
+ /* Kill the intermediate vuid */
+ invalidate_vuid(vuid);
}
+ return;
+ }
#endif
+ /* Can't blunder into NTLMSSP auth if we have
+ * a krb5 ticket. */
+
+ if (got_krb5_mechanism) {
+ /* Kill the intermediate vuid */
+ invalidate_vuid(vuid);
+ DEBUG(3,("reply_spnego_auth: network "
+ "misconfiguration, client sent us a "
+ "krb5 ticket and kerberos security "
+ "not enabled"));
+ reply_nterror(req, nt_status_squash(
+ NT_STATUS_LOGON_FAILURE));
}
}
@@ -874,13 +918,13 @@ static void reply_spnego_auth(struct smb_request *req,
data_blob_free(&secblob);
if (!*auth_ntlmssp_state) {
- /* Kill the intermediate vuid */
- invalidate_vuid(vuid);
-
- /* auth before negotiatiate? */
- reply_nterror(req, nt_status_squash(
- NT_STATUS_INVALID_PARAMETER));
- return;
+ status = auth_ntlmssp_start(auth_ntlmssp_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Kill the intermediate vuid */
+ invalidate_vuid(vuid);
+ reply_nterror(req, nt_status_squash(status));
+ return;
+ }
}
status = auth_ntlmssp_update(*auth_ntlmssp_state,
@@ -888,9 +932,11 @@ static void reply_spnego_auth(struct smb_request *req,
data_blob_free(&auth);
+ /* Don't send the mechid as we've already sent this (RFC4178). */
+
reply_spnego_ntlmssp(req, vuid,
auth_ntlmssp_state,
- &auth_reply, status, True);
+ &auth_reply, status, NULL, true);
data_blob_free(&auth_reply);
@@ -1257,7 +1303,7 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req)
reply_spnego_ntlmssp(req, vuid,
&vuser->auth_ntlmssp_state,
- &chal, status, False);
+ &chal, status, OID_NTLMSSP, false);
data_blob_free(&chal);
return;
}
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index bf6802f2a6..dc908846b3 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -5,7 +5,7 @@
Copyright (C) Stefan (metze) Metzmacher 2003
Copyright (C) Volker Lendecke 2005-2007
Copyright (C) Steve French 2005
- Copyright (C) James Peach 2007
+ Copyright (C) James Peach 2006-2007
Extensively modified by Andrew Tridgell, 1995
@@ -105,17 +105,22 @@ static bool samba_private_attr_name(const char *unix_ea_name)
for (i = 0; prohibited_ea_names[i]; i++) {
if (strequal( prohibited_ea_names[i], unix_ea_name))
- return True;
+ return true;
}
- return False;
+ if (StrnCaseCmp(unix_ea_name, SAMBA_XATTR_DOSSTREAM_PREFIX,
+ strlen(SAMBA_XATTR_DOSSTREAM_PREFIX)) == 0) {
+ return true;
+ }
+ return false;
}
/****************************************************************************
Get one EA value. Fill in a struct ea_struct.
****************************************************************************/
-static bool get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
- const char *fname, char *ea_name, struct ea_struct *pea)
+NTSTATUS get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn,
+ files_struct *fsp, const char *fname,
+ const char *ea_name, struct ea_struct *pea)
{
/* Get the value of this xattr. Max size is 64k. */
size_t attr_size = 256;
@@ -126,7 +131,7 @@ static bool get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_str
val = TALLOC_REALLOC_ARRAY(mem_ctx, val, char, attr_size);
if (!val) {
- return False;
+ return NT_STATUS_NO_MEMORY;
}
if (fsp && fsp->fh->fd != -1) {
@@ -141,7 +146,7 @@ static bool get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_str
}
if (sizeret == -1) {
- return False;
+ return map_nt_error_from_unix(errno);
}
DEBUG(10,("get_ea_value: EA %s is of length %u\n", ea_name, (unsigned int)sizeret));
@@ -149,93 +154,192 @@ static bool get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_str
pea->flags = 0;
if (strnequal(ea_name, "user.", 5)) {
- pea->name = &ea_name[5];
+ pea->name = talloc_strdup(mem_ctx, &ea_name[5]);
} else {
- pea->name = ea_name;
+ pea->name = talloc_strdup(mem_ctx, ea_name);
+ }
+ if (pea->name == NULL) {
+ TALLOC_FREE(val);
+ return NT_STATUS_NO_MEMORY;
}
pea->value.data = (unsigned char *)val;
pea->value.length = (size_t)sizeret;
- return True;
+ return NT_STATUS_OK;
}
-/****************************************************************************
- Return a linked list of the total EA's. Plus the total size
-****************************************************************************/
-
-static struct ea_list *get_ea_list_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
- const char *fname, size_t *pea_total_len)
+NTSTATUS get_ea_names_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn,
+ files_struct *fsp, const char *fname,
+ char ***pnames, size_t *pnum_names)
{
/* Get a list of all xattrs. Max namesize is 64k. */
size_t ea_namelist_size = 1024;
- char *ea_namelist;
+ char *ea_namelist = NULL;
+
char *p;
+ char **names, **tmp;
+ size_t num_names;
ssize_t sizeret;
- int i;
- struct ea_list *ea_list_head = NULL;
-
- *pea_total_len = 0;
if (!lp_ea_support(SNUM(conn))) {
- return NULL;
+ *pnames = NULL;
+ *pnum_names = 0;
+ return NT_STATUS_OK;
}
- for (i = 0, ea_namelist = TALLOC_ARRAY(mem_ctx, char, ea_namelist_size); i < 6;
- ea_namelist = TALLOC_REALLOC_ARRAY(mem_ctx, ea_namelist, char, ea_namelist_size), i++) {
+ /*
+ * TALLOC the result early to get the talloc hierarchy right.
+ */
- if (!ea_namelist) {
- return NULL;
+ names = TALLOC_ARRAY(mem_ctx, char *, 1);
+ if (names == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (ea_namelist_size <= 65536) {
+
+ ea_namelist = TALLOC_REALLOC_ARRAY(
+ names, ea_namelist, char, ea_namelist_size);
+ if (ea_namelist == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ TALLOC_FREE(names);
+ return NT_STATUS_NO_MEMORY;
}
if (fsp && fsp->fh->fd != -1) {
- sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist, ea_namelist_size);
+ sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist,
+ ea_namelist_size);
} else {
- sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist, ea_namelist_size);
+ sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist,
+ ea_namelist_size);
}
- if (sizeret == -1 && errno == ERANGE) {
+ if ((sizeret == -1) && (errno == ERANGE)) {
ea_namelist_size *= 2;
- } else {
+ }
+ else {
break;
}
}
- if (sizeret == -1)
- return NULL;
+ if (sizeret == -1) {
+ TALLOC_FREE(names);
+ return map_nt_error_from_unix(errno);
+ }
- DEBUG(10,("get_ea_list_from_file: ea_namelist size = %u\n", (unsigned int)sizeret ));
+ DEBUG(10, ("get_ea_list_from_file: ea_namelist size = %u\n",
+ (unsigned int)sizeret));
- if (sizeret) {
- for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p) + 1) {
- struct ea_list *listp;
+ if (sizeret == 0) {
+ TALLOC_FREE(names);
+ *pnames = NULL;
+ *pnum_names = 0;
+ return NT_STATUS_OK;
+ }
- if (strnequal(p, "system.", 7) || samba_private_attr_name(p))
- continue;
+ /*
+ * Ensure the result is 0-terminated
+ */
- listp = TALLOC_P(mem_ctx, struct ea_list);
- if (!listp)
- return NULL;
+ if (ea_namelist[sizeret-1] != '\0') {
+ TALLOC_FREE(names);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
- if (!get_ea_value(mem_ctx, conn, fsp, fname, p, &listp->ea)) {
- return NULL;
- }
+ /*
+ * count the names
+ */
+ num_names = 0;
- {
- fstring dos_ea_name;
- push_ascii_fstring(dos_ea_name, listp->ea.name);
- *pea_total_len += 4 + strlen(dos_ea_name) + 1 + listp->ea.value.length;
- DEBUG(10,("get_ea_list_from_file: total_len = %u, %s, val len = %u\n",
- (unsigned int)*pea_total_len, dos_ea_name,
- (unsigned int)listp->ea.value.length ));
- }
- DLIST_ADD_END(ea_list_head, listp, struct ea_list *);
+ for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) {
+ num_names += 1;
+ }
+
+ tmp = TALLOC_REALLOC_ARRAY(mem_ctx, names, char *, num_names);
+ if (tmp == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ TALLOC_FREE(names);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ names = tmp;
+ num_names = 0;
+
+ for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) {
+ names[num_names++] = p;
+ }
+
+ *pnames = names;
+ *pnum_names = num_names;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Return a linked list of the total EA's. Plus the total size
+****************************************************************************/
+
+static struct ea_list *get_ea_list_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
+ const char *fname, size_t *pea_total_len)
+{
+ /* Get a list of all xattrs. Max namesize is 64k. */
+ size_t i, num_names;
+ char **names;
+ struct ea_list *ea_list_head = NULL;
+ NTSTATUS status;
+
+ *pea_total_len = 0;
+
+ if (!lp_ea_support(SNUM(conn))) {
+ return NULL;
+ }
+
+ status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
+ &names, &num_names);
+
+ if (!NT_STATUS_IS_OK(status) || (num_names == 0)) {
+ return NULL;
+ }
+
+ for (i=0; i<num_names; i++) {
+ struct ea_list *listp;
+ fstring dos_ea_name;
+
+ if (strnequal(names[i], "system.", 7)
+ || samba_private_attr_name(names[i]))
+ continue;
+
+ listp = TALLOC_P(mem_ctx, struct ea_list);
+ if (listp == NULL) {
+ return NULL;
}
- /* Add on 4 for total length. */
- if (*pea_total_len) {
- *pea_total_len += 4;
+
+ if (!NT_STATUS_IS_OK(get_ea_value(mem_ctx, conn, fsp,
+ fname, names[i],
+ &listp->ea))) {
+ return NULL;
}
+
+ push_ascii_fstring(dos_ea_name, listp->ea.name);
+
+ *pea_total_len +=
+ 4 + strlen(dos_ea_name) + 1 + listp->ea.value.length;
+
+ DEBUG(10,("get_ea_list_from_file: total_len = %u, %s, val len "
+ "= %u\n", (unsigned int)*pea_total_len, dos_ea_name,
+ (unsigned int)listp->ea.value.length));
+
+ DLIST_ADD_END(ea_list_head, listp, struct ea_list *);
+
+ }
+
+ /* Add on 4 for total length. */
+ if (*pea_total_len) {
+ *pea_total_len += 4;
}
- DEBUG(10,("get_ea_list_from_file: total_len = %u\n", (unsigned int)*pea_total_len));
+ DEBUG(10, ("get_ea_list_from_file: total_len = %u\n",
+ (unsigned int)*pea_total_len));
+
return ea_list_head;
}
@@ -913,7 +1017,7 @@ static void call_trans2open(connection_struct *conn,
}
size = get_file_size(sbuf);
- fattr = dos_mode(conn,fname,&sbuf);
+ fattr = dos_mode(conn,fsp->fsp_name,&sbuf);
mtime = sbuf.st_mtime;
inode = sbuf.st_ino;
if (fattr & aDIR) {
@@ -950,7 +1054,7 @@ static void call_trans2open(connection_struct *conn,
SIVAL(params,20,inode);
SSVAL(params,24,0); /* Padding. */
if (flags & 8) {
- uint32 ea_size = estimate_ea_size(conn, fsp, fname);
+ uint32 ea_size = estimate_ea_size(conn, fsp, fsp->fsp_name);
SIVAL(params, 26, ea_size);
} else {
SIVAL(params, 26, 0);
@@ -2355,6 +2459,41 @@ unsigned char *create_volume_objectid(connection_struct *conn, unsigned char obj
return objid;
}
+static void samba_extended_info_version(struct smb_extended_info *extended_info)
+{
+ SMB_ASSERT(extended_info != NULL);
+
+ extended_info->samba_magic = SAMBA_EXTENDED_INFO_MAGIC;
+ extended_info->samba_version = ((SAMBA_VERSION_MAJOR & 0xff) << 24)
+ | ((SAMBA_VERSION_MINOR & 0xff) << 16)
+ | ((SAMBA_VERSION_RELEASE & 0xff) << 8);
+#ifdef SAMBA_VERSION_REVISION
+ extended_info->samba_version |= (tolower(*SAMBA_VERSION_REVISION) - 'a' + 1) & 0xff;
+#endif
+ extended_info->samba_subversion = 0;
+#ifdef SAMBA_VERSION_RC_RELEASE
+ extended_info->samba_subversion |= (SAMBA_VERSION_RC_RELEASE & 0xff) << 24;
+#else
+#ifdef SAMBA_VERSION_PRE_RELEASE
+ extended_info->samba_subversion |= (SAMBA_VERSION_PRE_RELEASE & 0xff) << 16;
+#endif
+#endif
+#ifdef SAMBA_VERSION_VENDOR_PATCH
+ extended_info->samba_subversion |= (SAMBA_VERSION_VENDOR_PATCH & 0xffff);
+#endif
+ extended_info->samba_gitcommitdate = 0;
+#ifdef SAMBA_VERSION_GIT_COMMIT_TIME
+ unix_to_nt_time(&extended_info->samba_gitcommitdate, SAMBA_VERSION_GIT_COMMIT_TIME);
+#endif
+
+ memset(extended_info->samba_version_string, 0,
+ sizeof(extended_info->samba_version_string));
+
+ snprintf (extended_info->samba_version_string,
+ sizeof(extended_info->samba_version_string),
+ "%s", samba_version_string());
+}
+
/****************************************************************************
Reply to a TRANS2_QFSINFO (query filesystem info).
****************************************************************************/
@@ -2373,8 +2512,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 +2626,21 @@ 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;
+ }
+
+ /* Capabilities are filled in at connection time through STATVFS call */
+ additional_flags |= conn->fs_capabilities;
+
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
@@ -2688,7 +2832,14 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
case SMB_FS_OBJECTID_INFORMATION:
{
unsigned char objid[16];
+ struct smb_extended_info extended_info;
memcpy(pdata,create_volume_objectid(conn, objid),16);
+ samba_extended_info_version (&extended_info);
+ SIVAL(pdata,16,extended_info.samba_magic);
+ SIVAL(pdata,20,extended_info.samba_version);
+ SIVAL(pdata,24,extended_info.samba_subversion);
+ SBIG_UINT(pdata,28,extended_info.samba_gitcommitdate);
+ memcpy(pdata+36,extended_info.samba_version_string,28);
data_len = 64;
break;
}
@@ -3463,6 +3614,72 @@ static char *store_file_unix_basic_info2(connection_struct *conn,
return pdata;
}
+static NTSTATUS marshall_stream_info(unsigned int num_streams,
+ const struct stream_struct *streams,
+ char *data,
+ unsigned int max_data_bytes,
+ unsigned int *data_size)
+{
+ unsigned int i;
+ unsigned int ofs = 0;
+
+ for (i=0; i<num_streams; i++) {
+ unsigned int next_offset;
+ size_t namelen;
+ smb_ucs2_t *namebuf;
+
+ namelen = push_ucs2_talloc(talloc_tos(), &namebuf,
+ streams[i].name);
+
+ if ((namelen == (size_t)-1) || (namelen <= 2)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * name_buf is now null-terminated, we need to marshall as not
+ * terminated
+ */
+
+ namelen -= 2;
+
+ if (ofs + 24 + namelen > max_data_bytes) {
+ TALLOC_FREE(namebuf);
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ SIVAL(data, ofs+4, namelen);
+ SOFF_T(data, ofs+8, streams[i].size);
+ SOFF_T(data, ofs+16, streams[i].alloc_size);
+ memcpy(data+ofs+24, namebuf, namelen);
+ TALLOC_FREE(namebuf);
+
+ next_offset = ofs + 24 + namelen;
+
+ if (i == num_streams-1) {
+ SIVAL(data, ofs, 0);
+ }
+ else {
+ unsigned int align = ndr_align_size(next_offset, 8);
+
+ if (next_offset + align > max_data_bytes) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ memset(data+next_offset, 0, align);
+ next_offset += align;
+
+ SIVAL(data, ofs, next_offset - ofs);
+ ofs = next_offset;
+ }
+
+ ofs = next_offset;
+ }
+
+ *data_size = ofs;
+
+ return NT_STATUS_OK;
+}
+
/****************************************************************************
Reply to a TRANSACT2_QFILEINFO on a PIPE !
****************************************************************************/
@@ -3738,17 +3955,6 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
}
}
- nlink = sbuf.st_nlink;
-
- if ((nlink > 0) && S_ISDIR(sbuf.st_mode)) {
- /* NTFS does not seem to count ".." */
- nlink -= 1;
- }
-
- if ((nlink > 0) && delete_pending) {
- nlink -= 1;
- }
-
if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) {
reply_nterror(req, NT_STATUS_INVALID_LEVEL);
return;
@@ -3767,6 +3973,16 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
if (!mode)
mode = FILE_ATTRIBUTE_NORMAL;
+ nlink = sbuf.st_nlink;
+
+ if (nlink && (mode&aDIR)) {
+ nlink = 1;
+ }
+
+ if ((nlink > 0) && delete_pending) {
+ nlink -= 1;
+ }
+
fullpathname = fname;
if (!(mode & aDIR))
file_size = get_file_size(sbuf);
@@ -4161,28 +4377,49 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
data_size = 4;
break;
-#if 0
/*
- * NT4 server just returns "invalid query" to this - if we try to answer
- * it then NTws gets a BSOD! (tridge).
- * W2K seems to want this. JRA.
+ * NT4 server just returns "invalid query" to this - if we try
+ * to answer it then NTws gets a BSOD! (tridge). W2K seems to
+ * want this. JRA.
+ */
+ /* The first statement above is false - verified using Thursby
+ * client against NT4 -- gcolley.
*/
case SMB_QUERY_FILE_STREAM_INFO:
-#endif
- case SMB_FILE_STREAM_INFORMATION:
- DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_STREAM_INFORMATION\n"));
- if (mode & aDIR) {
- data_size = 0;
- } else {
- size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", (size_t)0xE, False);
- SIVAL(pdata,0,0); /* ??? */
- SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
- SOFF_T(pdata,8,file_size);
- SOFF_T(pdata,16,allocation_size);
- data_size = 24 + byte_len;
+ case SMB_FILE_STREAM_INFORMATION: {
+ unsigned int num_streams;
+ struct stream_struct *streams;
+ NTSTATUS status;
+
+ DEBUG(10,("call_trans2qfilepathinfo: "
+ "SMB_FILE_STREAM_INFORMATION\n"));
+
+ status = SMB_VFS_STREAMINFO(
+ conn, fsp, fname, talloc_tos(),
+ &num_streams, &streams);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("could not get stream info: %s\n",
+ nt_errstr(status)));
+ reply_nterror(req, status);
+ return;
}
- break;
+ status = marshall_stream_info(num_streams, streams,
+ pdata, max_data_bytes,
+ &data_size);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("marshall_stream_info failed: %s\n",
+ nt_errstr(status)));
+ reply_nterror(req, status);
+ return;
+ }
+
+ TALLOC_FREE(streams);
+
+ break;
+ }
case SMB_QUERY_COMPRESSION_INFO:
case SMB_FILE_COMPRESSION_INFORMATION:
DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_COMPRESSION_INFORMATION\n"));
diff --git a/source3/smbd/utmp.c b/source3/smbd/utmp.c
index e82bbea3b3..5931b2b1d0 100644
--- a/source3/smbd/utmp.c
+++ b/source3/smbd/utmp.c
@@ -223,7 +223,7 @@ static char *uw_pathname(TALLOC_CTX *ctx,
}
/* For u-files and non-explicit w-dir, look for "utmp dir" */
- if (!dirname == 0 || strlen(dirname) == 0) {
+ if ((dirname == NULL) || (strlen(dirname) == 0)) {
dirname = talloc_strdup(ctx, lp_utmpdir());
if (!dirname) {
return NULL;