diff options
author | Andrew Tridgell <tridge@samba.org> | 2004-11-03 10:09:48 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:05:23 -0500 |
commit | dde07058075d357cfdc63624c8dcaa67ebd40add (patch) | |
tree | c3f29090e37f1bc103a3d6051e708d1ebbe305a5 | |
parent | 90a8c4acc7e673e6439197776d19cc4b095ac322 (diff) | |
download | samba-dde07058075d357cfdc63624c8dcaa67ebd40add.tar.gz samba-dde07058075d357cfdc63624c8dcaa67ebd40add.tar.bz2 samba-dde07058075d357cfdc63624c8dcaa67ebd40add.zip |
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)
36 files changed, 726 insertions, 222 deletions
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; @@ -324,19 +327,33 @@ 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<count;i++) { if (locks[i].lock_type >= 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 */ @@ -255,6 +262,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 */ NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum) @@ -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;i<count;i++) { + if (elist[i].pending) { + messaging_send_ptr(odb->messaging_ctx, elist[i].server, + MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr); + + } + } + /* find the entry, and delete it */ for (i=0;i<count;i++) { if (fnum == elist[i].fnum && @@ -309,6 +375,60 @@ NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum) /* + remove a pending opendb entry +*/ +NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private) +{ + struct odb_context *odb = lck->odb; + 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;i<count;i++) { + if (private == elist[i].notify_ptr && + odb->server == 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 */ NTSTATUS odb_set_create_options(struct odb_lock *lck, @@ -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;i<count;i++) { if (share_conflict(elist+i, &e)) { diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c index 2668eec004..82ac9ebad5 100644 --- a/source4/ntvfs/posix/pvfs_lock.c +++ b/source4/ntvfs/posix/pvfs_lock.c @@ -56,7 +56,7 @@ struct pvfs_pending_lock { struct smbsrv_request *req; int pending_lock; void *wait_handle; - time_t end_time; + struct timeval end_time; }; /* @@ -301,8 +301,9 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, pending->f = 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); @@ -399,13 +404,173 @@ 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 */ 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;i<torture_numops;i++) { char *fname; asprintf(&fname, "\\%x", (int)random()); @@ -61,13 +62,11 @@ BOOL torture_dirtest1(void) free(fname); } - t1 = end_timer(); - printf("Matched %d\n", smbcli_list(cli->tree, "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;i<torture_numops;i++) { diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c index 07ad0929de..310dec1bc7 100644 --- a/source4/torture/gentest.c +++ b/source4/torture/gentest.c @@ -189,7 +189,7 @@ static BOOL connect_servers(void) } smbcli_oplock_handler(servers[i].cli[j]->transport, 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<torture_nprocs;i++) { procnum = i; @@ -2364,18 +2362,18 @@ double torture_create_procs(BOOL (*fn)(struct smbcli_state *, int), BOOL *result } if (synccount == torture_nprocs) break; msleep(100); - } while (end_timer() < start_time_limit); + } while (timeval_elapsed(&tv) < start_time_limit); if (synccount != torture_nprocs) { printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount); *result = False; - return end_timer(); + return timeval_elapsed(&tv); } printf("Starting %d clients\n", torture_nprocs); /* start the client load */ - start_timer(); + tv = timeval_current(); for (i=0;i<torture_nprocs;i++) { child_status[i] = 0; } @@ -2398,7 +2396,7 @@ double torture_create_procs(BOOL (*fn)(struct smbcli_state *, int), BOOL *result *result = False; } } - return end_timer(); + return timeval_elapsed(&tv); } #define FLAG_MULTIPROC 1 @@ -2557,12 +2555,12 @@ static BOOL run_test(const char *name) } } else { - start_timer(); + struct timeval tv = timeval_current(); if (!torture_ops[i].fn()) { ret = False; printf("TEST %s FAILED!\n", torture_ops[i].name); } - t = end_timer(); + t = timeval_elapsed(&tv); } printf("%s took %g secs\n\n", torture_ops[i].name, t); } diff --git a/source4/torture/torture_util.c b/source4/torture/torture_util.c index 0c0464507d..22aa9ffedd 100644 --- a/source4/torture/torture_util.c +++ b/source4/torture/torture_util.c @@ -24,21 +24,6 @@ #include "system/time.h" -static struct timeval tp1,tp2; - -void start_timer(void) -{ - gettimeofday(&tp1,NULL); -} - -double end_timer(void) -{ - gettimeofday(&tp2,NULL); - return((tp2.tv_sec - tp1.tv_sec) + - (tp2.tv_usec - tp1.tv_usec)*1.0e-6); -} - - /* create a directory, returning a handle to it */ |