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/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 +++++++++++++++++++ 5 files changed, 258 insertions(+), 29 deletions(-) create mode 100644 source4/lib/unix_privs.c (limited to 'source4/lib') 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; +} + -- cgit