From dde07058075d357cfdc63624c8dcaa67ebd40add Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 3 Nov 2004 10:09:48 +0000 Subject: r3507: - added deferred replies on sharing violation in pvfs open. The deferred reply is short-circuited immediately when the file is closed by another user, allowing it to be opened by the waiting user. - added a sane set of timeval manipulation routines - converted all the events code and code that uses it to use struct timeval instead of time_t, which allows for microsecond resolution instead of 1 second resolution. This was needed for doing the pvfs deferred open code, and is why the patch is so big. (This used to be commit 0d51511d408d91eb5f68a35e980e0875299b1831) --- source4/include/events.h | 11 +- source4/include/messages.h | 1 + source4/ldap_server/ldap_server.c | 6 +- source4/lib/basic.mk | 3 +- source4/lib/events.c | 23 ++-- source4/lib/messaging/messaging.c | 51 ++++++-- source4/lib/time.c | 141 +++++++++++++++++++- source4/lib/unix_privs.c | 69 ++++++++++ source4/libcli/raw/clitransport.c | 16 +-- source4/libcli/raw/libcliraw.h | 2 +- source4/librpc/rpc/dcerpc_sock.c | 2 +- source4/ntvfs/cifs/vfs_cifs.c | 5 +- source4/ntvfs/common/brlock.c | 7 +- source4/ntvfs/common/opendb.c | 122 ++++++++++++++++++ source4/ntvfs/posix/pvfs_lock.c | 7 +- source4/ntvfs/posix/pvfs_open.c | 254 +++++++++++++++++++++++++++++-------- source4/ntvfs/posix/pvfs_wait.c | 12 +- source4/ntvfs/posix/vfs_posix.h | 2 + source4/rpc_server/dcerpc_server.c | 13 +- source4/rpc_server/dcerpc_sock.c | 12 +- source4/smb_server/smb_server.c | 9 +- source4/smbd/process_model.h | 6 +- source4/smbd/process_single.c | 5 +- source4/smbd/process_standard.c | 2 +- source4/smbd/service.c | 18 +-- source4/smbd/service.h | 8 +- source4/torture/basic/dir.c | 7 +- source4/torture/gentest.c | 2 +- source4/torture/local/messaging.c | 10 +- source4/torture/local/talloc.c | 29 +++-- source4/torture/nbench/nbench.c | 11 +- source4/torture/nbench/nbio.c | 7 +- source4/torture/raw/mux.c | 21 +++ source4/torture/raw/open.c | 9 +- source4/torture/torture.c | 30 ++--- source4/torture/torture_util.c | 15 --- 36 files changed, 726 insertions(+), 222 deletions(-) create mode 100644 source4/lib/unix_privs.c (limited to 'source4') diff --git a/source4/include/events.h b/source4/include/events.h index c44acc77ae..36fedc6e8b 100644 --- a/source4/include/events.h +++ b/source4/include/events.h @@ -28,7 +28,8 @@ struct event_context { struct fd_event *next, *prev; int fd; uint16_t flags; /* see EVENT_FD_* flags */ - void (*handler)(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags); + void (*handler)(struct event_context *ev, struct fd_event *fde, + struct timeval t, uint16_t flags); void *private; int ref_count; } *fd_events; @@ -36,8 +37,9 @@ struct event_context { /* list of timed events */ struct timed_event { struct timed_event *next, *prev; - time_t next_event; - void (*handler)(struct event_context *ev, struct timed_event *te, time_t t); + struct timeval next_event; + void (*handler)(struct event_context *ev, struct timed_event *te, + struct timeval t); void *private; int ref_count; } *timed_events; @@ -45,7 +47,8 @@ struct event_context { /* list of loop events - called on each select() */ struct loop_event { struct loop_event *next, *prev; - void (*handler)(struct event_context *ev, struct loop_event *le, time_t t); + void (*handler)(struct event_context *ev, struct loop_event *le, + struct timeval t); void *private; int ref_count; } *loop_events; diff --git a/source4/include/messages.h b/source4/include/messages.h index 96c1e81a31..48376efa03 100644 --- a/source4/include/messages.h +++ b/source4/include/messages.h @@ -29,5 +29,6 @@ struct messaging_context; #define MSG_PING 2 #define MSG_PONG 3 #define MSG_BRL_RETRY 4 +#define MSG_PVFS_RETRY_OPEN 5 #endif diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index 3b4ccb6b73..9338baa165 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -440,7 +440,7 @@ NTSTATUS ldapsrv_flush_responses(struct ldapsrv_connection *conn) /* called when a LDAP socket becomes readable */ -static void ldapsrv_recv(struct server_connection *conn, time_t t, +static void ldapsrv_recv(struct server_connection *conn, struct timeval t, uint16_t flags) { struct ldapsrv_connection *ldap_conn = conn->private_data; @@ -536,7 +536,7 @@ static void ldapsrv_recv(struct server_connection *conn, time_t t, /* called when a LDAP socket becomes writable */ -static void ldapsrv_send(struct server_connection *conn, time_t t, +static void ldapsrv_send(struct server_connection *conn, struct timeval t, uint16_t flags) { struct ldapsrv_connection *ldap_conn = conn->private_data; @@ -558,7 +558,7 @@ static void ldapsrv_send(struct server_connection *conn, time_t t, /* called when connection is idle */ -static void ldapsrv_idle(struct server_connection *conn, time_t t) +static void ldapsrv_idle(struct server_connection *conn, struct timeval t) { DEBUG(10,("ldapsrv_idle: not implemented!\n")); return; diff --git a/source4/lib/basic.mk b/source4/lib/basic.mk index e7d710eec6..827aa0a57c 100644 --- a/source4/lib/basic.mk +++ b/source4/lib/basic.mk @@ -72,7 +72,8 @@ ADD_OBJ_FILES = \ lib/events.o \ lib/db_wrap.o \ lib/server_mutex.o \ - lib/idtree.o + lib/idtree.o \ + lib/unix_privs.o REQUIRED_SUBSYSTEMS = \ LIBTDB CHARSET LIBREPLACE LIBNETIF LIBCRYPTO # End SUBSYSTEM LIBBASIC diff --git a/source4/lib/events.c b/source4/lib/events.c index f53a244c6d..0ca6b66598 100644 --- a/source4/lib/events.c +++ b/source4/lib/events.c @@ -285,15 +285,14 @@ void event_loop_exit(struct event_context *ev, int code) */ int event_loop_once(struct event_context *ev) { - time_t t; fd_set r_fds, w_fds; struct fd_event *fe; struct loop_event *le; struct timed_event *te; int selrtn; - struct timeval tval; + struct timeval tval, t; - t = time(NULL); + t = timeval_current(); /* the loop events are called on each loop. Be careful to allow the event to remove itself */ @@ -310,7 +309,6 @@ int event_loop_once(struct event_context *ev) le = next; } - ZERO_STRUCT(tval); FD_ZERO(&r_fds); FD_ZERO(&w_fds); @@ -336,17 +334,12 @@ int event_loop_once(struct event_context *ev) /* start with a reasonable max timeout */ tval.tv_sec = 600; + tval.tv_usec = 0; /* work out the right timeout for all timed events */ for (te=ev->timed_events;te;te=te->next) { - int timeout = te->next_event - t; - if (timeout < 0) { - timeout = 0; - } - if (te->ref_count && - timeout < tval.tv_sec) { - tval.tv_sec = timeout; - } + struct timeval tv = timeval_diff(&te->next_event, &t); + tval = timeval_min(&tv, &tval); } /* only do a select() if there're fd_events @@ -368,7 +361,7 @@ int event_loop_once(struct event_context *ev) */ selrtn = select(ev->maxfd+1, &r_fds, &w_fds, NULL, &tval); - t = time(NULL); + t = timeval_current(); if (selrtn == -1 && errno == EBADF) { /* the socket is dead! this should never @@ -404,11 +397,11 @@ int event_loop_once(struct event_context *ev) if (te->ref_count == 0) { DLIST_REMOVE(ev->timed_events, te); talloc_free(te); - } else if (te->next_event <= t) { + } else if (timeval_compare(&te->next_event, &t) >= 0) { te->ref_count++; te->handler(ev, te, t); te->ref_count--; - if (te->next_event <= t) { + if (timeval_compare(&te->next_event, &t) >= 0) { /* the handler didn't set a time for the next event - remove the event */ event_remove_timed(ev, te); diff --git a/source4/lib/messaging/messaging.c b/source4/lib/messaging/messaging.c index f862b2d505..041554a7c0 100644 --- a/source4/lib/messaging/messaging.c +++ b/source4/lib/messaging/messaging.c @@ -29,6 +29,9 @@ /* change the message version with any incompatible changes in the protocol */ #define MESSAGING_VERSION 1 +/* the number of microseconds to backoff in retrying to send a message */ +#define MESSAGING_BACKOFF 250000 + struct messaging_context { servid_t server_id; struct socket_context *sock; @@ -119,7 +122,7 @@ static void messaging_dispatch(struct messaging_context *msg, struct messaging_r handle IO for a single message */ static void messaging_recv_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) + struct timeval t, uint16_t flags) { struct messaging_rec *rec = fde->private; struct messaging_context *msg = rec->msg; @@ -200,7 +203,7 @@ static int rec_destructor(void *ptr) handle a new incoming connection */ static void messaging_listen_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) + struct timeval t, uint16_t flags) { struct messaging_context *msg = fde->private; struct messaging_rec *rec; @@ -272,7 +275,7 @@ void messaging_deregister(struct messaging_context *msg, uint32_t msg_type, void handle IO for sending a message */ static void messaging_send_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) + struct timeval t, uint16_t flags) { struct messaging_rec *rec = fde->private; NTSTATUS status; @@ -323,20 +326,34 @@ static void messaging_send_handler(struct event_context *ev, struct fd_event *fd } +/* + wrapper around socket_connect with raised privileges +*/ +static NTSTATUS try_connect(struct messaging_rec *rec) +{ + NTSTATUS status; + void *priv = root_privileges(); + status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0); + talloc_free(priv); + return status; +} + + /* when the servers listen queue is full we use this to backoff the message */ -static void messaging_backoff_handler(struct event_context *ev, struct timed_event *te, time_t t) +static void messaging_backoff_handler(struct event_context *ev, struct timed_event *te, + struct timeval t) { struct messaging_rec *rec = te->private; struct messaging_context *msg = rec->msg; NTSTATUS status; struct fd_event fde; - status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0); + status = try_connect(rec); if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { /* backoff again */ - te->next_event = t+1; + te->next_event = timeval_add(&t, 0, MESSAGING_BACKOFF); return; } @@ -356,7 +373,7 @@ static void messaging_backoff_handler(struct event_context *ev, struct timed_eve talloc_set_destructor(rec, rec_destructor); - messaging_send_handler(msg->event.ev, rec->fde, 0, EVENT_FD_WRITE); + messaging_send_handler(msg->event.ev, rec->fde, timeval_zero(), EVENT_FD_WRITE); } @@ -396,11 +413,11 @@ NTSTATUS messaging_send(struct messaging_context *msg, servid_t server, uint32_t rec->path = messaging_path(rec, server); - status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0); + status = try_connect(rec); if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { /* backoff on this message - the servers listen queue is full */ struct timed_event te; - te.next_event = time(NULL)+1; + te.next_event = timeval_current_ofs(0, MESSAGING_BACKOFF); te.handler = messaging_backoff_handler; te.private = rec; event_add_timed(msg->event.ev, &te); @@ -421,11 +438,25 @@ NTSTATUS messaging_send(struct messaging_context *msg, servid_t server, uint32_t talloc_set_destructor(rec, rec_destructor); - messaging_send_handler(msg->event.ev, rec->fde, 0, EVENT_FD_WRITE); + messaging_send_handler(msg->event.ev, rec->fde, timeval_zero(), EVENT_FD_WRITE); return NT_STATUS_OK; } +/* + Send a message to a particular server, with the message containing a single pointer +*/ +NTSTATUS messaging_send_ptr(struct messaging_context *msg, servid_t server, + uint32_t msg_type, void *ptr) +{ + DATA_BLOB blob; + + blob.data = (void *)&ptr; + blob.length = sizeof(void *); + + return messaging_send(msg, server, msg_type, &blob); +} + /* destroy the messaging context diff --git a/source4/lib/time.c b/source4/lib/time.c index cfceebf9cb..9e6da2cfa6 100644 --- a/source4/lib/time.c +++ b/source4/lib/time.c @@ -386,9 +386,144 @@ NTTIME nttime_from_string(const char *s) return strtoull(s, NULL, 0); } -int64_t usec_time_diff(struct timeval *larget, struct timeval *smallt) +/* + return (tv1 - tv2) in microseconds +*/ +int64_t usec_time_diff(struct timeval *tv1, struct timeval *tv2) +{ + int64_t sec_diff = tv1->tv_sec - tv2->tv_sec; + return (sec_diff * 1000000) + (int64_t)(tv1->tv_usec - tv2->tv_usec); +} + + +/* + return a zero timeval +*/ +struct timeval timeval_zero(void) { - int64_t sec_diff = larget->tv_sec - smallt->tv_sec; - return (sec_diff * 1000000) + (int64_t)(larget->tv_usec - smallt->tv_usec); + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + return tv; } +/* + return a timeval for the current time +*/ +struct timeval timeval_current(void) +{ + struct timeval tv; + GetTimeOfDay(&tv); + return tv; +} + +/* + return a timeval struct with the given elements +*/ +struct timeval timeval_set(uint32_t secs, uint32_t usecs) +{ + struct timeval tv; + tv.tv_sec = secs; + tv.tv_usec = usecs; + return tv; +} + + +/* + return a timeval ofs microseconds after tv +*/ +struct timeval timeval_add(struct timeval *tv, uint32_t secs, uint32_t usecs) +{ + struct timeval tv2 = *tv; + const uint_t million = 1000000; + tv2.tv_sec += secs; + tv2.tv_usec += usecs; + tv2.tv_sec += tv2.tv_usec / million; + tv2.tv_usec = tv2.tv_usec % million; + return tv2; +} + +/* + return the sum of two timeval structures +*/ +struct timeval timeval_sum(struct timeval *tv1, struct timeval *tv2) +{ + return timeval_add(tv1, tv2->tv_sec, tv2->tv_usec); +} + +/* + return a timeval secs/usecs into the future +*/ +struct timeval timeval_current_ofs(uint32_t secs, uint32_t usecs) +{ + struct timeval tv = timeval_current(); + return timeval_add(&tv, secs, usecs); +} + +/* + compare two timeval structures. + Return 1 if tv2 > tv1 + Return 0 if tv2 == tv1 + Return -1 if tv2 < tv1 +*/ +int timeval_compare(struct timeval *tv1, struct timeval *tv2) +{ + if (tv2->tv_sec > tv1->tv_sec) return 1; + if (tv2->tv_sec < tv1->tv_sec) return -1; + if (tv2->tv_usec > tv1->tv_usec) return 1; + if (tv2->tv_usec < tv1->tv_usec) return -1; + return 0; +} + +/* + return True if a timer is in the past +*/ +BOOL timeval_expired(struct timeval *tv) +{ + struct timeval tv2 = timeval_current(); + if (tv2.tv_sec > tv->tv_sec) return True; + if (tv2.tv_sec < tv->tv_sec) return False; + return (tv2.tv_usec >= tv->tv_usec); +} + +/* + return the number of seconds elapsed since a given time +*/ +double timeval_elapsed(struct timeval *tv) +{ + struct timeval tv2 = timeval_current(); + return (tv2.tv_sec - tv->tv_sec) + + (tv2.tv_usec - tv->tv_usec)*1.0e-6; +} + +/* + return the lesser of two timevals +*/ +struct timeval timeval_min(struct timeval *tv1, struct timeval *tv2) +{ + if (tv1->tv_sec < tv2->tv_sec) return *tv1; + if (tv1->tv_sec > tv2->tv_sec) return *tv2; + if (tv1->tv_usec < tv2->tv_usec) return *tv1; + return *tv2; +} + +/* + return the difference between two timevals as a timeval + if tv2 comes after tv1, then return a zero timeval + (this is *tv1 - *tv2) +*/ +struct timeval timeval_diff(struct timeval *tv1, struct timeval *tv2) +{ + struct timeval t; + if (timeval_compare(tv1, tv2) >= 0) { + return timeval_zero(); + } + t.tv_sec = tv1->tv_sec - tv2->tv_sec; + if (tv2->tv_usec > tv1->tv_usec) { + t.tv_sec--; + t.tv_usec = 1000000 - (tv2->tv_usec - tv1->tv_usec); + } else { + t.tv_usec = tv1->tv_usec - tv2->tv_usec; + } + return t; +} diff --git a/source4/lib/unix_privs.c b/source4/lib/unix_privs.c new file mode 100644 index 0000000000..c65f490aeb --- /dev/null +++ b/source4/lib/unix_privs.c @@ -0,0 +1,69 @@ +/* + Unix SMB/CIFS implementation. + + gain/lose root privileges + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/passwd.h" + +/* + there are times when smbd needs to temporarily gain root privileges + to do some operation. To do this you call root_privileges(), which + returns a talloc handle. To restore your previous privileges + talloc_free() this pointer. + + Note that this call is considered successful even if it does not + manage to gain too privileges, but it will call smb_abort() if it + fails to restore the privileges afterwards. The logic is that + failing to gain root access can be caught by whatever operation + needs to be run as root failing, but failing to lose the root + privileges is dangerous. + + This also means that this code is safe to be called from completely + unprivileged processes. +*/ + +struct saved_state { + uid_t uid; +}; + +static int privileges_destructor(void *ptr) +{ + struct saved_state *s = ptr; + if (geteuid() != s->uid && + seteuid(s->uid) != 0) { + smb_panic("Failed to restore privileges"); + } + return 0; +} + +void *root_privileges(void) +{ + struct saved_state *s; + s = talloc_p(NULL, struct saved_state); + if (!s) return NULL; + s->uid = geteuid(); + if (s->uid != 0) { + seteuid(0); + } + talloc_set_destructor(s, privileges_destructor); + return s; +} + diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c index 00e52f3a14..52cb4d8beb 100644 --- a/source4/libcli/raw/clitransport.c +++ b/source4/libcli/raw/clitransport.c @@ -33,7 +33,7 @@ static void smbcli_transport_process_send(struct smbcli_transport *transport); an event has happened on the socket */ static void smbcli_transport_event_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) + struct timeval t, uint16_t flags) { struct smbcli_transport *transport = fde->private; @@ -233,21 +233,21 @@ again: } static void idle_handler(struct event_context *ev, - struct timed_event *te, time_t t) + struct timed_event *te, struct timeval t) { struct smbcli_transport *transport = te->private; - te->next_event = t + transport->idle.period; + te->next_event = timeval_add(&te->next_event, 0, transport->idle.period); transport->idle.func(transport, transport->idle.private); } /* setup the idle handler for a transport - the period is in seconds + the period is in microseconds */ void smbcli_transport_idle_handler(struct smbcli_transport *transport, - void (*idle_func)(struct smbcli_transport *, void *), - uint_t period, - void *private) + void (*idle_func)(struct smbcli_transport *, void *), + uint64_t period, + void *private) { struct timed_event te; transport->idle.func = idle_func; @@ -258,7 +258,7 @@ void smbcli_transport_idle_handler(struct smbcli_transport *transport, event_remove_timed(transport->event.ctx, transport->event.te); } - te.next_event = time(NULL) + period; + te.next_event = timeval_current_ofs(0, period); te.handler = idle_handler; te.private = transport; transport->event.te = event_add_timed(transport->event.ctx, &te); diff --git a/source4/libcli/raw/libcliraw.h b/source4/libcli/raw/libcliraw.h index 9b03ab713b..b00af846b7 100644 --- a/source4/libcli/raw/libcliraw.h +++ b/source4/libcli/raw/libcliraw.h @@ -114,7 +114,7 @@ struct smbcli_transport { uint_t readbraw_pending:1; /* an idle function - if this is defined then it will be - called once every period seconds while we are waiting + called once every period microseconds while we are waiting for a packet */ struct { void (*func)(struct smbcli_transport *, void *); diff --git a/source4/librpc/rpc/dcerpc_sock.c b/source4/librpc/rpc/dcerpc_sock.c index 41fde60ea0..26464884bc 100644 --- a/source4/librpc/rpc/dcerpc_sock.c +++ b/source4/librpc/rpc/dcerpc_sock.c @@ -190,7 +190,7 @@ static void sock_process_recv(struct dcerpc_pipe *p) called when a IO is triggered by the events system */ static void sock_io_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) + struct timeval t, uint16_t flags) { struct dcerpc_pipe *p = fde->private; struct sock_private *sock = p->transport.private; diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c index 4fd5650f9b..3e9899cb8c 100644 --- a/source4/ntvfs/cifs/vfs_cifs.c +++ b/source4/ntvfs/cifs/vfs_cifs.c @@ -78,7 +78,8 @@ static BOOL oplock_handler(struct smbcli_transport *transport, uint16_t tid, uin /* a handler for read events on a connection to a backend server */ -static void cifs_socket_handler(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags) +static void cifs_socket_handler(struct event_context *ev, struct fd_event *fde, + struct timeval t, uint16_t flags) { struct cvfs_private *private = fde->private; struct smbsrv_tcon *tcon = private->tcon; @@ -149,7 +150,7 @@ static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, /* we need to receive oplock break requests from the server */ smbcli_oplock_handler(private->transport, oplock_handler, private); - smbcli_transport_idle_handler(private->transport, idle_func, 1, private); + smbcli_transport_idle_handler(private->transport, idle_func, 50000, private); private->transport->event.fde->handler = cifs_socket_handler; private->transport->event.fde->private = private; diff --git a/source4/ntvfs/common/brlock.c b/source4/ntvfs/common/brlock.c index d1df0413ce..6fae7c6e4c 100644 --- a/source4/ntvfs/common/brlock.c +++ b/source4/ntvfs/common/brlock.c @@ -333,17 +333,14 @@ static void brl_notify_unlock(struct brl_context *brl, for (i=0;i= PENDING_READ_LOCK && brl_overlap(&locks[i], removed_lock)) { - DATA_BLOB data; - if (last_notice != -1 && brl_overlap(&locks[i], &locks[last_notice])) { continue; } if (locks[i].lock_type == PENDING_WRITE_LOCK) { last_notice = i; } - data.data = (void *)&locks[i].notify_ptr; - data.length = sizeof(void *); - messaging_send(brl->messaging_ctx, locks[i].context.server, MSG_BRL_RETRY, &data); + messaging_send_ptr(brl->messaging_ctx, locks[i].context.server, + MSG_BRL_RETRY, locks[i].notify_ptr); } } } diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c index 5dc68e5382..39d4f37ec2 100644 --- a/source4/ntvfs/common/opendb.c +++ b/source4/ntvfs/common/opendb.c @@ -39,6 +39,7 @@ */ #include "includes.h" +#include "messages.h" struct odb_context { struct tdb_wrap *w; @@ -58,6 +59,8 @@ struct odb_entry { uint32_t share_access; uint32_t create_options; uint32_t access_mask; + void *notify_ptr; + BOOL pending; }; @@ -152,6 +155,8 @@ static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2) { #define CHECK_MASK(am, sa, right, share) if (((am) & (right)) && !((sa) & (share))) return True + if (e1->pending || e2->pending) return False; + /* if either open involves no read.write or delete access then it can't conflict */ if (!(e1->access_mask & (SA_RIGHT_FILE_WRITE_APPEND | @@ -219,6 +224,8 @@ NTSTATUS odb_open_file(struct odb_lock *lck, uint16_t fnum, e.share_access = share_access; e.create_options = create_options; e.access_mask = access_mask; + e.notify_ptr = NULL; + e.pending = False; /* check the existing file opens to see if they conflict */ @@ -254,6 +261,56 @@ NTSTATUS odb_open_file(struct odb_lock *lck, uint16_t fnum, } +/* + register a pending open file in the open files database +*/ +NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private) +{ + struct odb_context *odb = lck->odb; + TDB_DATA dbuf; + struct odb_entry e; + char *tp; + struct odb_entry *elist; + int count; + + dbuf = tdb_fetch(odb->w->tdb, lck->key); + + e.server = odb->server; + e.tid = odb->tid; + e.fnum = 0; + e.share_access = 0; + e.create_options = 0; + e.access_mask = 0; + e.notify_ptr = private; + e.pending = True; + + /* check the existing file opens to see if they + conflict */ + elist = (struct odb_entry *)dbuf.dptr; + count = dbuf.dsize / sizeof(struct odb_entry); + + tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry)); + if (tp == NULL) { + if (dbuf.dptr) free(dbuf.dptr); + return NT_STATUS_NO_MEMORY; + } + + dbuf.dptr = tp; + dbuf.dsize = (count+1) * sizeof(struct odb_entry); + + memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)), + &e, sizeof(struct odb_entry)); + + if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) { + free(dbuf.dptr); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + free(dbuf.dptr); + return NT_STATUS_OK; +} + + /* remove a opendb entry */ @@ -274,6 +331,15 @@ NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum) elist = (struct odb_entry *)dbuf.dptr; count = dbuf.dsize / sizeof(struct odb_entry); + /* send any pending notifications */ + for (i=0;imessaging_ctx, elist[i].server, + MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr); + + } + } + /* find the entry, and delete it */ for (i=0;iodb; + TDB_DATA dbuf; + struct odb_entry *elist; + int i, count; + NTSTATUS status; + + dbuf = tdb_fetch(odb->w->tdb, lck->key); + + if (dbuf.dptr == NULL) { + return NT_STATUS_UNSUCCESSFUL; + } + + elist = (struct odb_entry *)dbuf.dptr; + count = dbuf.dsize / sizeof(struct odb_entry); + + /* find the entry, and delete it */ + for (i=0;iserver == elist[i].server && + odb->tid == elist[i].tid) { + if (i < count-1) { + memmove(elist+i, elist+i+1, + (count - (i+1)) * sizeof(struct odb_entry)); + } + break; + } + } + + status = NT_STATUS_OK; + + if (i == count) { + status = NT_STATUS_UNSUCCESSFUL; + } else if (count == 1) { + if (tdb_delete(odb->w->tdb, lck->key) != 0) { + status = NT_STATUS_INTERNAL_DB_CORRUPTION; + } + } else { + dbuf.dsize = (count-1) * sizeof(struct odb_entry); + if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) { + status = NT_STATUS_INTERNAL_DB_CORRUPTION; + } + } + + free(dbuf.dptr); + + return status; +} + + /* update create options on an open file */ @@ -386,6 +506,8 @@ NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key, e.share_access = share_access; e.create_options = create_options; e.access_mask = access_mask; + e.notify_ptr = NULL; + e.pending = False; for (i=0;if = f; pending->req = req; - /* round up to the nearest second */ - pending->end_time = time(NULL) + ((lck->lockx.in.timeout+999)/1000); + pending->end_time = + timeval_current_ofs(lck->lockx.in.timeout/1000, + 1000*(lck->lockx.in.timeout%1000)); } if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) { diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index f3ef72f4ed..8ad6ad0389 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -25,6 +25,7 @@ #include "system/time.h" #include "system/filesys.h" #include "dlinklist.h" +#include "messages.h" /* create file handles with convenient numbers for sniffers @@ -33,6 +34,8 @@ #define PVFS_MIN_NEW_FNUM 0x200 #define PVFS_MIN_DIR_FNUM 0x300 +#define SHARING_VIOLATION_DELAY 1000000 + /* find open file handle given fnum */ @@ -125,7 +128,6 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX); if (fnum == -1) { - talloc_free(f); return NT_STATUS_TOO_MANY_OPENED_FILES; } @@ -228,10 +230,12 @@ static int pvfs_fd_destructor(void *p) return 0; } - status = odb_close_file(lck, f->fnum); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", - f->name->full_name, nt_errstr(status))); + if (f->have_opendb_entry) { + status = odb_close_file(lck, f->fnum); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", + f->name->full_name, nt_errstr(status))); + } } talloc_free(lck); @@ -370,6 +374,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, f->access_mask = access_mask; f->seek_offset = 0; f->position = 0; + f->have_opendb_entry = True; DLIST_ADD(pvfs->open_files, f); @@ -398,6 +403,166 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, } +/* + open am existing file - called from both the open retry code + and the main open code +*/ +NTSTATUS pvfs_open_existing(struct pvfs_file *f, + union smb_open *io, + int open_flags) +{ + int fd; + NTSTATUS status; + + /* do the actual open */ + fd = open(f->name->full_name, open_flags); + if (fd == -1) { + return pvfs_map_errno(f->pvfs, errno); + } + + f->fd = fd; + + /* re-resolve the open fd */ + status = pvfs_resolve_name_fd(f->pvfs, fd, f->name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + io->generic.out.oplock_level = NO_OPLOCK; + io->generic.out.fnum = f->fnum; + io->generic.out.create_action = NTCREATEX_ACTION_EXISTED; + io->generic.out.create_time = f->name->dos.create_time; + io->generic.out.access_time = f->name->dos.access_time; + io->generic.out.write_time = f->name->dos.write_time; + io->generic.out.change_time = f->name->dos.change_time; + io->generic.out.attrib = f->name->dos.attrib; + io->generic.out.alloc_size = f->name->dos.alloc_size; + io->generic.out.size = f->name->st.st_size; + io->generic.out.file_type = FILE_TYPE_DISK; + io->generic.out.ipc_state = 0; + io->generic.out.is_directory = 0; + + /* success - keep the file handle */ + talloc_steal(f->pvfs, f); + + return NT_STATUS_OK; +} + +/* + state of a pending open retry +*/ +struct pvfs_open_retry { + union smb_open *io; + struct pvfs_file *f; + struct smbsrv_request *req; + void *wait_handle; + struct timeval end_time; + int open_flags; +}; + +/* destroy a pending open request */ +static int pvfs_retry_destructor(void *ptr) +{ + struct pvfs_open_retry *r = ptr; + struct odb_lock *lck; + lck = odb_lock(r->req, r->f->pvfs->odb_context, &r->f->locking_key); + if (lck != NULL) { + odb_remove_pending(lck, r); + } + return 0; +} + +/* + retry an open +*/ +static void pvfs_open_retry(void *private, BOOL timed_out) +{ + struct pvfs_open_retry *r = private; + struct odb_lock *lck; + struct pvfs_file *f = r->f; + struct smbsrv_request *req = r->req; + NTSTATUS status; + + lck = odb_lock(req, f->pvfs->odb_context, &f->locking_key); + if (lck == NULL) { + req->async_states->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + req->async_states->send_fn(req); + return; + } + + /* see if we are allowed to open at the same time as existing opens */ + status = odb_open_file(lck, f->fnum, f->share_access, + f->create_options, f->access_mask); + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) && !timed_out) { + talloc_free(lck); + return; + } + + talloc_free(r->wait_handle); + + if (!NT_STATUS_IS_OK(status)) { + req->async_states->status = status; + req->async_states->send_fn(req); + return; + } + + f->have_opendb_entry = True; + + /* do the rest of the open work */ + status = pvfs_open_existing(f, r->io, r->open_flags); + + if (NT_STATUS_IS_OK(status)) { + talloc_steal(f->pvfs, f); + } + + req->async_states->status = status; + req->async_states->send_fn(req); +} + +/* + setup for a open retry after a sharing violation +*/ +static NTSTATUS pvfs_open_setup_retry(struct smbsrv_request *req, + union smb_open *io, + struct pvfs_file *f, + struct odb_lock *lck, + int open_flags) +{ + struct pvfs_open_retry *r; + struct pvfs_state *pvfs = f->pvfs; + NTSTATUS status; + + r = talloc_p(req, struct pvfs_open_retry); + if (r == NULL) { + return NT_STATUS_NO_MEMORY; + } + + r->io = io; + r->f = f; + r->req = req; + r->end_time = timeval_current_ofs(0, SHARING_VIOLATION_DELAY); + r->open_flags = open_flags; + + /* setup a pending lock */ + status = odb_open_file_pending(lck, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, r->end_time, + pvfs_open_retry, r); + if (r->wait_handle == NULL) { + return NT_STATUS_NO_MEMORY; + } + + talloc_free(lck); + talloc_steal(pvfs, req); + + talloc_set_destructor(r, pvfs_retry_destructor); + + return NT_STATUS_OK; +} + /* open a file */ @@ -405,7 +570,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_open *io) { struct pvfs_state *pvfs = ntvfs->private_data; - int fd, flags; + int flags; struct pvfs_filename *name; struct pvfs_file *f; NTSTATUS status; @@ -539,11 +704,26 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return NT_STATUS_TOO_MANY_OPENED_FILES; } + f->fnum = fnum; + f->fd = -1; + f->name = talloc_steal(f, name); + f->session = req->session; + f->smbpid = req->smbpid; + f->pvfs = pvfs; + f->pending_list = NULL; + f->lock_count = 0; + f->create_options = io->generic.in.create_options; + f->share_access = io->generic.in.share_access; + f->access_mask = access_mask; + f->seek_offset = 0; + f->position = 0; + f->have_opendb_entry = False; + /* form the lock context used for byte range locking and opendb locking */ status = pvfs_locking_key(name, f, &f->locking_key); if (!NT_STATUS_IS_OK(status)) { - idr_remove(pvfs->idtree_fnum, fnum); + idr_remove(pvfs->idtree_fnum, f->fnum); return status; } @@ -558,65 +738,31 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return NT_STATUS_INTERNAL_DB_CORRUPTION; } - /* see if we are allowed to open at the same time as existing opens */ - status = odb_open_file(lck, fnum, share_access, create_options, access_mask); - if (!NT_STATUS_IS_OK(status)) { - idr_remove(pvfs->idtree_fnum, fnum); - return status; - } - - f->fnum = fnum; - f->fd = -1; - f->name = talloc_steal(f, name); - f->session = req->session; - f->smbpid = req->smbpid; - f->pvfs = pvfs; - f->pending_list = NULL; - f->lock_count = 0; - f->create_options = io->generic.in.create_options; - f->share_access = io->generic.in.share_access; - f->access_mask = access_mask; - f->seek_offset = 0; - f->position = 0; - DLIST_ADD(pvfs->open_files, f); /* setup a destructor to avoid file descriptor leaks on abnormal termination */ talloc_set_destructor(f, pvfs_fd_destructor); - /* do the actual open */ - fd = open(name->full_name, flags); - if (fd == -1) { - return pvfs_map_errno(pvfs, errno); - } - f->fd = fd; + /* see if we are allowed to open at the same time as existing opens */ + status = odb_open_file(lck, f->fnum, share_access, create_options, access_mask); + + /* on a sharing violation we need to retry when the file is closed by + the other user, or after 1 second */ + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) && + (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { + return pvfs_open_setup_retry(req, io, f, lck, flags); + } - /* re-resolve the open fd */ - status = pvfs_resolve_name_fd(pvfs, fd, name); if (!NT_STATUS_IS_OK(status)) { return status; } - io->generic.out.oplock_level = NO_OPLOCK; - io->generic.out.fnum = f->fnum; - io->generic.out.create_action = NTCREATEX_ACTION_EXISTED; - io->generic.out.create_time = name->dos.create_time; - io->generic.out.access_time = name->dos.access_time; - io->generic.out.write_time = name->dos.write_time; - io->generic.out.change_time = name->dos.change_time; - io->generic.out.attrib = name->dos.attrib; - io->generic.out.alloc_size = name->dos.alloc_size; - io->generic.out.size = name->st.st_size; - io->generic.out.file_type = FILE_TYPE_DISK; - io->generic.out.ipc_state = 0; - io->generic.out.is_directory = 0; - - /* success - keep the file handle */ - talloc_steal(pvfs, f); + f->have_opendb_entry = True; - return NT_STATUS_OK; + /* do the rest of the open work */ + return pvfs_open_existing(f, io, flags); } @@ -677,7 +823,6 @@ NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs, for (f=pvfs->open_files;f;f=next) { next = f->next; if (f->session == req->session) { - DLIST_REMOVE(pvfs->open_files, f); talloc_free(f); } } @@ -698,7 +843,6 @@ NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs, for (f=pvfs->open_files;f;f=next) { next = f->next; if (f->smbpid == req->smbpid) { - DLIST_REMOVE(pvfs->open_files, f); talloc_free(f); } } diff --git a/source4/ntvfs/posix/pvfs_wait.c b/source4/ntvfs/posix/pvfs_wait.c index f01bd0ea18..0faab8ef55 100644 --- a/source4/ntvfs/posix/pvfs_wait.c +++ b/source4/ntvfs/posix/pvfs_wait.c @@ -58,10 +58,9 @@ static void pvfs_wait_dispatch(struct messaging_context *msg, void *private, uin struct pvfs_wait *pwait = private; struct smbsrv_request *req; - /* we need to check that this one is for us. This sender sends - the private pointer as the body of the message. This might - seem a little unusual, but as the pointer is guaranteed - unique for this server, it is a good token */ + /* we need to check that this one is for us. See + messaging_send_ptr() for the other side of this. + */ if (data->length != sizeof(void *) || *(void **)data->data != pwait->private) { return; @@ -82,7 +81,8 @@ static void pvfs_wait_dispatch(struct messaging_context *msg, void *private, uin /* receive a timeout on a message wait */ -static void pvfs_wait_timeout(struct event_context *ev, struct timed_event *te, time_t t) +static void pvfs_wait_timeout(struct event_context *ev, + struct timed_event *te, struct timeval t) { struct pvfs_wait *pwait = te->private; struct smbsrv_request *req = pwait->req; @@ -116,7 +116,7 @@ static int pvfs_wait_destructor(void *ptr) void *pvfs_wait_message(struct pvfs_state *pvfs, struct smbsrv_request *req, int msg_type, - time_t end_time, + struct timeval end_time, void (*fn)(void *, BOOL), void *private) { diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index 530a2deae3..265649f5a3 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -112,6 +112,8 @@ struct pvfs_file { /* yes, we need 2 independent positions ... */ uint64_t seek_offset; uint64_t position; + + BOOL have_opendb_entry; }; diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index f9e2d8d28e..3aeb7033d1 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -1057,21 +1057,18 @@ static void dcesrv_accept(struct server_connection *srv_conn) dcesrv_sock_accept(srv_conn); } -static void dcesrv_recv(struct server_connection *srv_conn, time_t t, uint16_t flags) +static void dcesrv_recv(struct server_connection *srv_conn, + struct timeval t, uint16_t flags) { dcesrv_sock_recv(srv_conn, t, flags); } -static void dcesrv_send(struct server_connection *srv_conn, time_t t, uint16_t flags) +static void dcesrv_send(struct server_connection *srv_conn, + struct timeval t, uint16_t flags) { dcesrv_sock_send(srv_conn, t, flags); } -static void dcesrv_idle(struct server_connection *srv_conn, time_t t) -{ - dcesrv_sock_idle(srv_conn, t); -} - static void dcesrv_close(struct server_connection *srv_conn, const char *reason) { dcesrv_sock_close(srv_conn, reason); @@ -1190,7 +1187,7 @@ static const struct server_service_ops dcesrv_ops = { .accept_connection = dcesrv_accept, .recv_handler = dcesrv_recv, .send_handler = dcesrv_send, - .idle_handler = dcesrv_idle, + .idle_handler = NULL, .close_connection = dcesrv_close, .service_exit = dcesrv_exit, }; diff --git a/source4/rpc_server/dcerpc_sock.c b/source4/rpc_server/dcerpc_sock.c index 544082471e..f8d0bbfc6d 100644 --- a/source4/rpc_server/dcerpc_sock.c +++ b/source4/rpc_server/dcerpc_sock.c @@ -248,7 +248,7 @@ void dcesrv_sock_accept(struct server_connection *conn) return; } -void dcesrv_sock_recv(struct server_connection *conn, time_t t, uint16_t flags) +void dcesrv_sock_recv(struct server_connection *conn, struct timeval t, uint16_t flags) { NTSTATUS status; struct dcesrv_connection *dce_conn = conn->private_data; @@ -288,7 +288,7 @@ void dcesrv_sock_recv(struct server_connection *conn, time_t t, uint16_t flags) return; } -void dcesrv_sock_send(struct server_connection *conn, time_t t, uint16_t flags) +void dcesrv_sock_send(struct server_connection *conn, struct timeval t, uint16_t flags) { struct dcesrv_connection *dce_conn = conn->private_data; NTSTATUS status; @@ -308,14 +308,6 @@ void dcesrv_sock_send(struct server_connection *conn, time_t t, uint16_t flags) return; } -void dcesrv_sock_idle(struct server_connection *conn, time_t t) -{ - DEBUG(10,("dcesrv_sock_idle\n")); - conn->event.idle->next_event = t + 5; - - return; -} - void dcesrv_sock_close(struct server_connection *conn, const char *reason) { struct dcesrv_connection *dce_conn = conn->private_data; diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c index e1eb4e3179..861186a8d7 100644 --- a/source4/smb_server/smb_server.c +++ b/source4/smb_server/smb_server.c @@ -723,7 +723,7 @@ static void smbsrv_init(struct server_service *service, const struct model_ops * /* called when a SMB socket becomes readable */ -static void smbsrv_recv(struct server_connection *conn, time_t t, uint16_t flags) +static void smbsrv_recv(struct server_connection *conn, struct timeval t, uint16_t flags) { struct smbsrv_connection *smb_conn = conn->private_data; NTSTATUS status; @@ -744,7 +744,7 @@ static void smbsrv_recv(struct server_connection *conn, time_t t, uint16_t flags /* called when a SMB socket becomes writable */ -static void smbsrv_send(struct server_connection *conn, time_t t, uint16_t flags) +static void smbsrv_send(struct server_connection *conn, struct timeval t, uint16_t flags) { struct smbsrv_connection *smb_conn = conn->private_data; @@ -787,11 +787,10 @@ static void smbsrv_send(struct server_connection *conn, time_t t, uint16_t flags /* called when connection is idle */ -static void smbsrv_idle(struct server_connection *conn, time_t t) +static void smbsrv_idle(struct server_connection *conn, struct timeval t) { DEBUG(10,("smbsrv_idle: not implemented!\n")); - conn->event.idle->next_event = t + 5; - + conn->event.idle->next_event = timeval_add(&t, 5, 0); return; } diff --git a/source4/smbd/process_model.h b/source4/smbd/process_model.h index 79373d8a39..92d92a70ad 100644 --- a/source4/smbd/process_model.h +++ b/source4/smbd/process_model.h @@ -40,10 +40,12 @@ struct model_ops { void (*model_startup)(void); /* function to accept new connection */ - void (*accept_connection)(struct event_context *, struct fd_event *, time_t, uint16_t); + void (*accept_connection)(struct event_context *, struct fd_event *, + struct timeval t, uint16_t); /* function to terminate a connection */ - void (*terminate_connection)(struct server_connection *srv_conn, const char *reason); + void (*terminate_connection)(struct server_connection *srv_conn, + const char *reason); /* function to exit server */ void (*exit_server)(struct server_context *srv_ctx, const char *reason); diff --git a/source4/smbd/process_single.c b/source4/smbd/process_single.c index 6ce0479b0c..66074d166d 100644 --- a/source4/smbd/process_single.c +++ b/source4/smbd/process_single.c @@ -38,7 +38,8 @@ static void single_start_server(void) /* called when a listening socket becomes readable */ -static void single_accept_connection(struct event_context *ev, struct fd_event *srv_fde, time_t t, uint16_t flags) +static void single_accept_connection(struct event_context *ev, struct fd_event *srv_fde, + struct timeval t, uint16_t flags) { NTSTATUS status; struct socket_context *sock; @@ -55,7 +56,7 @@ static void single_accept_connection(struct event_context *ev, struct fd_event * conn = server_setup_connection(ev, server_socket, sock, t, socket_get_fd(sock)); if (!conn) { - DEBUG(0,("server_setup_connection(ev, server_socket, sock, t) failed\n")); + DEBUG(0,("server_setup_connection failed\n")); return; } diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c index 3741ce1b46..c794605dc5 100644 --- a/source4/smbd/process_standard.c +++ b/source4/smbd/process_standard.c @@ -39,7 +39,7 @@ static void standard_model_startup(void) called when a listening socket becomes readable */ static void standard_accept_connection(struct event_context *ev, struct fd_event *srv_fde, - time_t t, uint16_t flags) + struct timeval t, uint16_t flags) { NTSTATUS status; struct socket_context *sock; diff --git a/source4/smbd/service.c b/source4/smbd/service.c index 6d2f9a8149..767d310e2b 100644 --- a/source4/smbd/service.c +++ b/source4/smbd/service.c @@ -205,7 +205,7 @@ static int server_destructor(void *ptr) struct server_connection *server_setup_connection(struct event_context *ev, struct server_socket *server_socket, struct socket_context *sock, - time_t t, + struct timeval t, servid_t server_id) { struct fd_event fde; @@ -226,13 +226,13 @@ struct server_connection *server_setup_connection(struct event_context *ev, fde.handler = server_io_handler; idle.private = srv_conn; - idle.next_event = t + SERVER_DEFAULT_IDLE_TIME; + idle.next_event = timeval_add(&t, SERVER_DEFAULT_IDLE_TIME, 0); idle.handler = server_idle_handler; srv_conn->event.ctx = ev; srv_conn->event.fde = &fde; srv_conn->event.idle = &idle; - srv_conn->event.idle_time = SERVER_DEFAULT_IDLE_TIME; + srv_conn->event.idle_time = timeval_set(SERVER_DEFAULT_IDLE_TIME, 0); srv_conn->server_socket = server_socket; srv_conn->service = server_socket->service; @@ -269,11 +269,12 @@ void server_terminate_connection(struct server_connection *srv_conn, const char srv_conn->service->model_ops->terminate_connection(srv_conn, reason); } -void server_io_handler(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags) +void server_io_handler(struct event_context *ev, struct fd_event *fde, + struct timeval t, uint16_t flags) { struct server_connection *conn = fde->private; - conn->event.idle->next_event = t + conn->event.idle_time; + conn->event.idle->next_event = timeval_sum(&t, &conn->event.idle_time); if (flags & EVENT_FD_WRITE) { conn->service->ops->send_handler(conn, t, flags); @@ -286,13 +287,14 @@ void server_io_handler(struct event_context *ev, struct fd_event *fde, time_t t, } -void server_idle_handler(struct event_context *ev, struct timed_event *idle, time_t t) +void server_idle_handler(struct event_context *ev, struct timed_event *idle, + struct timeval t) { struct server_connection *conn = idle->private; - conn->event.idle->next_event = t + conn->event.idle_time; + conn->event.idle->next_event = timeval_sum(&t, &conn->event.idle_time); - conn->service->ops->idle_handler(conn,t); + conn->service->ops->idle_handler(conn, t); } /* return the operations structure for a named backend of the specified type diff --git a/source4/smbd/service.h b/source4/smbd/service.h index 2ac1988f3d..5bb43a74fa 100644 --- a/source4/smbd/service.h +++ b/source4/smbd/service.h @@ -48,13 +48,13 @@ struct server_service_ops { void (*accept_connection)(struct server_connection *); /* function to accept new connection */ - void (*recv_handler)(struct server_connection *, time_t, uint16_t); + void (*recv_handler)(struct server_connection *, struct timeval, uint16_t); /* function to accept new connection */ - void (*send_handler)(struct server_connection *, time_t, uint16_t); + void (*send_handler)(struct server_connection *, struct timeval, uint16_t); /* function to accept new connection */ - void (*idle_handler)(struct server_connection *, time_t); + void (*idle_handler)(struct server_connection *, struct timeval); /* function to close a connection */ void (*close_connection)(struct server_connection *, const char *reason); @@ -114,7 +114,7 @@ struct server_connection { struct event_context *ctx; struct fd_event *fde; struct timed_event *idle; - time_t idle_time; + struct timeval idle_time; } event; servid_t server_id; diff --git a/source4/torture/basic/dir.c b/source4/torture/basic/dir.c index 5d3ac8d1de..7921b3eb03 100644 --- a/source4/torture/basic/dir.c +++ b/source4/torture/basic/dir.c @@ -35,9 +35,9 @@ BOOL torture_dirtest1(void) int i; struct smbcli_state *cli; int fnum; - double t1; BOOL correct = True; extern int torture_numops; + struct timeval tv; printf("starting dirtest1\n"); @@ -48,6 +48,7 @@ BOOL torture_dirtest1(void) printf("Creating %d random filenames\n", torture_numops); srandom(0); + tv = timeval_current(); for (i=0;itree, "a*.*", 0, list_fn, NULL)); printf("Matched %d\n", smbcli_list(cli->tree, "b*.*", 0, list_fn, NULL)); printf("Matched %d\n", smbcli_list(cli->tree, "xyzabc", 0, list_fn, NULL)); - printf("dirtest core %g seconds\n", end_timer() - t1); + printf("dirtest core %g seconds\n", timeval_elapsed(&tv)); srandom(0); for (i=0;itransport, oplock_handler, NULL); - smbcli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 1, NULL); + smbcli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 50000, NULL); } } diff --git a/source4/torture/local/messaging.c b/source4/torture/local/messaging.c index c0dab06cca..303ffa8fe9 100644 --- a/source4/torture/local/messaging.c +++ b/source4/torture/local/messaging.c @@ -58,6 +58,7 @@ static BOOL test_ping_speed(TALLOC_CTX *mem_ctx) int ping_count = 0; int pong_count = 0; BOOL ret = True; + struct timeval tv; if (fork() == 0) { struct messaging_context *msg_ctx2 = messaging_init(mem_ctx, 1, ev); @@ -83,10 +84,10 @@ static BOOL test_ping_speed(TALLOC_CTX *mem_ctx) messaging_register(msg_ctx, &pong_count, MY_PONG, pong_message); - start_timer(); + tv = timeval_current(); printf("Sending pings for 10 seconds\n"); - while (end_timer() < 10.0) { + while (timeval_elapsed(&tv) < 10.0) { DATA_BLOB data; NTSTATUS status1, status2; @@ -113,7 +114,7 @@ static BOOL test_ping_speed(TALLOC_CTX *mem_ctx) printf("waiting for %d remaining replies (done %d)\n", ping_count - pong_count, pong_count); - while (end_timer() < 30 && pong_count < ping_count) { + while (timeval_elapsed(&tv) < 30 && pong_count < ping_count) { event_loop_once(ev); } @@ -125,7 +126,8 @@ static BOOL test_ping_speed(TALLOC_CTX *mem_ctx) ret = False; } - printf("ping rate of %.0f messages/sec\n", (ping_count+pong_count)/end_timer()); + printf("ping rate of %.0f messages/sec\n", + (ping_count+pong_count)/timeval_elapsed(&tv)); talloc_free(msg_ctx); diff --git a/source4/torture/local/talloc.c b/source4/torture/local/talloc.c index 332312200a..348b037753 100644 --- a/source4/torture/local/talloc.c +++ b/source4/torture/local/talloc.c @@ -36,18 +36,18 @@ #ifdef _STANDALONE_ typedef enum {False=0,True=1} BOOL; -static struct timeval tp1,tp2; - -static void start_timer(void) +static struct timeval current_time(void) { - gettimeofday(&tp1,NULL); + struct timeval tv; + GetTimeOfDay(&tv); + return tv; } -static double end_timer(void) +static double elapsed_time(struct timeval *tv) { - gettimeofday(&tp2,NULL); - return((tp2.tv_sec - tp1.tv_sec) + - (tp2.tv_usec - tp1.tv_usec)*1.0e-6); + struct timeval tv2 = current_time(); + return (tv2.tv_sec - tv->tv_sec) + + (tv2.tv_usec - tv->tv_usec)*1.0e-6; } #endif /* _STANDALONE_ */ @@ -642,10 +642,11 @@ static BOOL test_speed(void) { void *ctx = talloc(NULL, 0); unsigned count; + struct timeval tv; printf("MEASURING TALLOC VS MALLOC SPEED\n"); - start_timer(); + tv = timeval_current(); count = 0; do { void *p1, *p2, *p3; @@ -654,13 +655,13 @@ static BOOL test_speed(void) p3 = talloc(p1, 300); talloc_free(p1); count += 3; - } while (end_timer() < 5.0); + } while (timeval_elapsed(&tv) < 5.0); - printf("talloc: %.0f ops/sec\n", count/end_timer()); + printf("talloc: %.0f ops/sec\n", count/timeval_elapsed(&tv)); talloc_free(ctx); - start_timer(); + tv = timeval_current(); count = 0; do { void *p1, *p2, *p3; @@ -671,9 +672,9 @@ static BOOL test_speed(void) free(p2); free(p3); count += 3; - } while (end_timer() < 5.0); + } while (timeval_elapsed(&tv) < 5.0); - printf("malloc: %.0f ops/sec\n", count/end_timer()); + printf("malloc: %.0f ops/sec\n", count/timeval_elapsed(&tv)); return True; } diff --git a/source4/torture/nbench/nbench.c b/source4/torture/nbench/nbench.c index 0b4bf4bbf1..1c90658f49 100644 --- a/source4/torture/nbench/nbench.c +++ b/source4/torture/nbench/nbench.c @@ -37,6 +37,9 @@ static BOOL run_netbench(struct smbcli_state *cli, int client) fstring params[20]; const char *p; BOOL correct = True; + struct timeval tv; + + tv = timeval_current(); nb_setup(cli, client, warmup); @@ -52,15 +55,15 @@ static BOOL run_netbench(struct smbcli_state *cli, int client) again: while (fgets(line, sizeof(line)-1, f)) { NTSTATUS status; - double t = end_timer(); - if (warmup && t >= warmup) { + if (warmup && + timeval_elapsed(&tv) >= warmup) { warmup = 0; nb_warmup_done(); - start_timer(); + tv = timeval_current(); } - if (end_timer() >= timelimit) { + if (timeval_elapsed(&tv) >= timelimit) { goto done; } diff --git a/source4/torture/nbench/nbio.c b/source4/torture/nbench/nbio.c index 98b1c08c97..98967f2523 100644 --- a/source4/torture/nbench/nbio.c +++ b/source4/torture/nbench/nbio.c @@ -31,6 +31,7 @@ static int nbio_id; static int nprocs; static BOOL bypass_io; static int warmup; +static struct timeval tv; struct ftable { struct ftable *next, *prev; @@ -76,7 +77,7 @@ void nb_alarm(int sig) if (!children[i].done) num_clients++; } - t = end_timer(); + t = timeval_elapsed(&tv); if (warmup) { printf("%4d %8d %.2f MB/sec warmup %.0f sec \n", @@ -91,7 +92,7 @@ void nb_alarm(int sig) } if (warmup && t >= warmup) { - start_timer(); + tv = timeval_current(); warmup = 0; } @@ -156,7 +157,7 @@ void nb_setup(struct smbcli_state *cli, int id, int warmupt) warmup = warmupt; nbio_id = id; c = cli; - start_timer(); + tv = timeval_current(); if (children) { children[nbio_id].done = 0; } diff --git a/source4/torture/raw/mux.c b/source4/torture/raw/mux.c index c184fb79a7..c02045817e 100644 --- a/source4/torture/raw/mux.c +++ b/source4/torture/raw/mux.c @@ -42,6 +42,8 @@ static BOOL test_mux_open(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) int fnum; BOOL ret = True; struct smbcli_request *req; + struct timeval tv; + double d; printf("testing multiplexed open/open/close\n"); @@ -64,14 +66,25 @@ static BOOL test_mux_open(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) CHECK_STATUS(status, NT_STATUS_OK); fnum = io.ntcreatex.out.fnum; + tv = timeval_current(); + /* send an open that will conflict */ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; status = smb_raw_open(cli->tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + d = timeval_elapsed(&tv); + if (d < 0.5 || d > 1.5) { + printf("bad timeout for conflict - %.2f should be 1.0\n", d); + ret = False; + } else { + printf("open delay %.2f\n", d); + } + /* same request, but async */ + tv = timeval_current(); req = smb_raw_open_send(cli->tree, &io); /* and close the file */ @@ -81,6 +94,14 @@ static BOOL test_mux_open(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) status = smb_raw_open_recv(req, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); + d = timeval_elapsed(&tv); + if (d > 0.25) { + printf("bad timeout for async conflict - %.2f should be <0.25\n", d); + ret = False; + } else { + printf("async open delay %.2f\n", d); + } + smbcli_close(cli->tree, io.ntcreatex.out.fnum); done: diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c index 81681bbd74..c810984900 100644 --- a/source4/torture/raw/open.c +++ b/source4/torture/raw/open.c @@ -266,6 +266,7 @@ static BOOL test_openx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) int fnum = -1, fnum2; BOOL ret = True; int i; + struct timeval tv; struct { uint16_t open_func; BOOL with_file; @@ -398,13 +399,13 @@ static BOOL test_openx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) fnum = io.openx.out.fnum; io.openx.in.timeout = 20000; - start_timer(); + tv = timeval_current(); io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_NONE; status = smb_raw_open(cli->tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); - if (end_timer() > 3) { - printf("(%s) Incorrect timing in openx with timeout - waited %d seconds\n", - __location__, (int)end_timer()); + if (timeval_elapsed(&tv) > 3.0) { + printf("(%s) Incorrect timing in openx with timeout - waited %.2f seconds\n", + __location__, timeval_elapsed(&tv)); ret = False; } smbcli_close(cli->tree, fnum); diff --git a/source4/torture/torture.c b/source4/torture/torture.c index 656607d934..19dc311dd9 100644 --- a/source4/torture/torture.c +++ b/source4/torture/torture.c @@ -937,22 +937,19 @@ static BOOL run_deferopen(struct smbcli_state *cli, int dummy) int fnum = -1; do { - struct timeval tv_start, tv_end; - GetTimeOfDay(&tv_start); + struct timeval tv; + tv = timeval_current(); fnum = smbcli_nt_create_full(cli->tree, fname, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); if (fnum != -1) { break; } - GetTimeOfDay(&tv_end); if (NT_STATUS_EQUAL(smbcli_nt_error(cli->tree),NT_STATUS_SHARING_VIOLATION)) { - /* Sharing violation errors need to be 1 second apart. */ - int64_t tdif = usec_time_diff(&tv_end, &tv_start); - if (tdif < 500000 || tdif > 1500000) { - fprintf(stderr,"Timing incorrect %lld.%lld for share violation\n", - tdif / (int64_t)1000000, - tdif % (int64_t)1000000); + double e = timeval_elapsed(&tv); + if (e < 0.5 || e > 1.5) { + fprintf(stderr,"Timing incorrect %.2f violation\n", + e); } } } while (NT_STATUS_EQUAL(smbcli_nt_error(cli->tree),NT_STATUS_SHARING_VIOLATION)); @@ -2269,6 +2266,7 @@ double torture_create_procs(BOOL (*fn)(struct smbcli_state *, int), BOOL *result char **unc_list = NULL; const char *p; int num_unc_names = 0; + struct timeval tv; synccount = 0; @@ -2300,7 +2298,7 @@ double torture_create_procs(BOOL (*fn)(struct smbcli_state *, int), BOOL *result child_status_out[i] = True; } - start_timer(); + tv = timeval_current(); for (i=0;i