diff options
| author | bubulle <bubulle@alioth.debian.org> | 2010-09-06 20:54:34 +0000 |
|---|---|---|
| committer | bubulle <bubulle@alioth.debian.org> | 2010-09-06 20:54:34 +0000 |
| commit | 53601faba8f69c3454ad07acaceeef9165cb3743 (patch) | |
| tree | b31a4174a7f4d2650717c1902a6bc3f922e13117 /lib | |
| parent | 1b77db997b6a2ce389356509415a6e5e540bcfe0 (diff) | |
| download | samba-53601faba8f69c3454ad07acaceeef9165cb3743.tar.gz | |
Merge 3.5.4 in upstream branch
git-svn-id: svn://svn.debian.org/svn/pkg-samba/branches/samba/upstream@3574 fc4039ab-9d04-0410-8cac-899223bdd6b0
Diffstat (limited to 'lib')
190 files changed, 18903 insertions, 1777 deletions
diff --git a/lib/async_req/async_req.c b/lib/async_req/async_req.c deleted file mode 100644 index 90247389f6..0000000000 --- a/lib/async_req/async_req.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Infrastructure for async requests - Copyright (C) Volker Lendecke 2008 - - 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 3 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, see <http://www.gnu.org/licenses/>. -*/ - -#include "includes.h" -#include "lib/tevent/tevent.h" -#include "lib/talloc/talloc.h" -#include "lib/util/dlinklist.h" -#include "lib/async_req/async_req.h" - -#ifndef TALLOC_FREE -#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) -#endif - -/** - * @brief Print an async_req structure - * @param[in] mem_ctx The memory context for the result - * @param[in] req The request to be printed - * @retval Text representation of req - * - * This is a default print function for async requests. Implementations should - * override this with more specific information. - * - * This function should not be used by async API users, this is non-static - * only to allow implementations to easily provide default information in - * their specific functions. - */ - -char *async_req_print(TALLOC_CTX *mem_ctx, struct async_req *req) -{ - return talloc_asprintf(mem_ctx, "async_req: state=%d, error=%d, " - "priv=%s", req->state, (int)req->error, - talloc_get_name(req->private_data)); -} - -/** - * @brief Create an async request - * @param[in] mem_ctx The memory context for the result - * @param[in] ev The event context this async request will be driven by - * @retval A new async request - * - * The new async request will be initialized in state ASYNC_REQ_IN_PROGRESS - */ - -struct async_req *async_req_new(TALLOC_CTX *mem_ctx) -{ - struct async_req *result; - - result = talloc_zero(mem_ctx, struct async_req); - if (result == NULL) { - return NULL; - } - result->state = ASYNC_REQ_IN_PROGRESS; - result->print = async_req_print; - return result; -} - -static void async_req_finish(struct async_req *req, enum async_req_state state) -{ - req->state = state; - if (req->async.fn != NULL) { - req->async.fn(req); - } -} - -/** - * @brief An async request has successfully finished - * @param[in] req The finished request - * - * async_req_done is to be used by implementors of async requests. When a - * request is successfully finished, this function calls the user's completion - * function. - */ - -void async_req_done(struct async_req *req) -{ - async_req_finish(req, ASYNC_REQ_DONE); -} - -/** - * @brief An async request has seen an error - * @param[in] req The request with an error - * @param[in] error The error code - * - * async_req_done is to be used by implementors of async requests. When a - * request can not successfully completed, the implementation should call this - * function with the appropriate status code. - */ - -void async_req_error(struct async_req *req, uint64_t error) -{ - req->error = error; - async_req_finish(req, ASYNC_REQ_USER_ERROR); -} - -/** - * @brief Timed event callback - * @param[in] ev Event context - * @param[in] te The timed event - * @param[in] now zero time - * @param[in] priv The async request to be finished - */ - -static void async_trigger(struct tevent_context *ev, struct tevent_timer *te, - struct timeval now, void *priv) -{ - struct async_req *req = talloc_get_type_abort(priv, struct async_req); - - TALLOC_FREE(te); - if (req->error == 0) { - async_req_done(req); - } - else { - async_req_error(req, req->error); - } -} - -/** - * @brief Helper function for nomem check - * @param[in] p The pointer to be checked - * @param[in] req The request being processed - * - * Convenience helper to easily check alloc failure within a callback - * implementing the next step of an async request. - * - * Call pattern would be - * \code - * p = talloc(mem_ctx, bla); - * if (async_req_ntnomem(p, req)) { - * return; - * } - * \endcode - */ - -bool async_req_nomem(const void *p, struct async_req *req) -{ - if (p != NULL) { - return false; - } - async_req_finish(req, ASYNC_REQ_NO_MEMORY); - return true; -} - -/** - * @brief Finish a request before it started processing - * @param[in] req The finished request - * @param[in] status The success code - * - * An implementation of an async request might find that it can either finish - * the request without waiting for an external event, or it can't even start - * the engine. To present the illusion of a callback to the user of the API, - * the implementation can call this helper function which triggers an - * immediate timed event. This way the caller can use the same calling - * conventions, independent of whether the request was actually deferred. - */ - -bool async_post_error(struct async_req *req, struct tevent_context *ev, - uint64_t error) -{ - req->error = error; - - if (tevent_add_timer(ev, req, tevent_timeval_zero(), - async_trigger, req) == NULL) { - return false; - } - return true; -} - -bool async_req_is_error(struct async_req *req, enum async_req_state *state, - uint64_t *error) -{ - if (req->state == ASYNC_REQ_DONE) { - return false; - } - if (req->state == ASYNC_REQ_USER_ERROR) { - *error = req->error; - } - *state = req->state; - return true; -} - -struct async_queue_entry { - struct async_queue_entry *prev, *next; - struct async_req_queue *queue; - struct async_req *req; - void (*trigger)(struct async_req *req); -}; - -struct async_req_queue { - struct async_queue_entry *queue; -}; - -struct async_req_queue *async_req_queue_init(TALLOC_CTX *mem_ctx) -{ - return talloc_zero(mem_ctx, struct async_req_queue); -} - -static int async_queue_entry_destructor(struct async_queue_entry *e) -{ - struct async_req_queue *queue = e->queue; - - DLIST_REMOVE(queue->queue, e); - - if (queue->queue != NULL) { - queue->queue->trigger(queue->queue->req); - } - - return 0; -} - -static void async_req_immediate_trigger(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval now, - void *priv) -{ - struct async_queue_entry *e = talloc_get_type_abort( - priv, struct async_queue_entry); - - TALLOC_FREE(te); - e->trigger(e->req); -} - -bool async_req_enqueue(struct async_req_queue *queue, struct tevent_context *ev, - struct async_req *req, - void (*trigger)(struct async_req *req)) -{ - struct async_queue_entry *e; - bool busy; - - busy = (queue->queue != NULL); - - e = talloc(req, struct async_queue_entry); - if (e == NULL) { - return false; - } - - e->req = req; - e->trigger = trigger; - e->queue = queue; - - DLIST_ADD_END(queue->queue, e, struct async_queue_entry *); - talloc_set_destructor(e, async_queue_entry_destructor); - - if (!busy) { - struct tevent_timer *te; - - te = tevent_add_timer(ev, e, tevent_timeval_zero(), - async_req_immediate_trigger, - e); - if (te == NULL) { - TALLOC_FREE(e); - return false; - } - } - - return true; -} - -bool _async_req_setup(TALLOC_CTX *mem_ctx, struct async_req **preq, - void *pstate, size_t state_size, const char *_typename) -{ - struct async_req *req; - void **ppstate = (void **)pstate; - void *state; - - req = async_req_new(mem_ctx); - if (req == NULL) { - return false; - } - state = talloc_size(req, state_size); - if (state == NULL) { - TALLOC_FREE(req); - return false; - } - talloc_set_name_const(state, _typename); - req->private_data = state; - - *preq = req; - *ppstate = state; - - return true; -} diff --git a/lib/async_req/async_req.h b/lib/async_req/async_req.h deleted file mode 100644 index a06e824d95..0000000000 --- a/lib/async_req/async_req.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Infrastructure for async requests - Copyright (C) Volker Lendecke 2008 - - 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 3 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, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef __ASYNC_REQ_H__ -#define __ASYNC_REQ_H__ - -#include "lib/talloc/talloc.h" - -/** - * An async request moves between the following 4 states: - */ - -enum async_req_state { - /** - * we are creating the request - */ - ASYNC_REQ_INIT, - /** - * we are waiting the request to complete - */ - ASYNC_REQ_IN_PROGRESS, - /** - * the request is finished - */ - ASYNC_REQ_DONE, - /** - * A user error has occured - */ - ASYNC_REQ_USER_ERROR, - /** - * Request timed out - */ - ASYNC_REQ_TIMED_OUT, - /** - * No memory in between - */ - ASYNC_REQ_NO_MEMORY -}; - -/** - * @brief An async request - * - * This represents an async request being processed by callbacks via an event - * context. A user can issue for example a write request to a socket, giving - * an implementation function the fd, the buffer and the number of bytes to - * transfer. The function issuing the request will immediately return without - * blocking most likely without having sent anything. The API user then fills - * in req->async.fn and req->async.priv, functions that are called when the - * request is finished. - * - * It is up to the user of the async request to talloc_free it after it has - * finished. This can happen while the completion function is called. - */ - -struct async_req { - /** - * @brief The external state - will be queried by the caller - * - * While the async request is being processed, state will remain in - * ASYNC_REQ_IN_PROGRESS. A request is finished if - * req->state>=ASYNC_REQ_DONE. - */ - enum async_req_state state; - - /** - * @brief Private pointer for the actual implementation - * - * The implementation doing the work for the async request needs a - * current state like for example a fd event. The user of an async - * request should not touch this. - */ - void *private_data; - - /** - * @brief Print yourself, for debugging purposes - * - * Async requests are opaque data structures. The implementation of an - * async request can define a custom function to print more debug - * info. - */ - char *(*print)(TALLOC_CTX *mem_ctx, struct async_req *); - - /** - * @brief status code when finished - * - * This status can be queried in the async completion function. It - * will be set to 0 when everything went fine. - **/ - uint64_t error; - - /** - * @brief What to do on completion - * - * This is used for the user of an async request, fn is called when - * the request completes, either successfully or with an error. - */ - struct { - /** - * @brief Completion function - * Completion function, to be filled by the API user - */ - void (*fn)(struct async_req *); - /** - * @brief Private data for the completion function - */ - void *priv; - } async; -}; - -struct async_req *async_req_new(TALLOC_CTX *mem_ctx); - -char *async_req_print(TALLOC_CTX *mem_ctx, struct async_req *req); - -void async_req_done(struct async_req *req); - -void async_req_error(struct async_req *req, uint64_t error); - -bool async_req_nomem(const void *p, struct async_req *req); - -bool async_post_error(struct async_req *req, struct tevent_context *ev, - uint64_t error); - -bool async_req_is_error(struct async_req *req, enum async_req_state *state, - uint64_t *error); - -struct async_req_queue; - -struct async_req_queue *async_req_queue_init(TALLOC_CTX *mem_ctx); - -bool async_req_enqueue(struct async_req_queue *queue, - struct tevent_context *ev, - struct async_req *req, - void (*trigger)(struct async_req *req)); - -bool _async_req_setup(TALLOC_CTX *mem_ctx, struct async_req **preq, - void *pstate, size_t state_size, const char *_typename); - -#define async_req_setup(_mem_ctx, _preq, _pstate, type) \ - _async_req_setup((_mem_ctx), (_preq), (_pstate), sizeof(type), #type) - - -#endif diff --git a/lib/async_req/async_req_ntstatus.c b/lib/async_req/async_req_ntstatus.c deleted file mode 100644 index 65bc0f6510..0000000000 --- a/lib/async_req/async_req_ntstatus.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - Unix SMB/CIFS implementation. - NTSTATUS wrappers for async_req.h - Copyright (C) Volker Lendecke 2008, 2009 - - 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 3 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, see <http://www.gnu.org/licenses/>. -*/ - -#include "includes.h" -#include "lib/tevent/tevent.h" -#include "lib/talloc/talloc.h" -#include "lib/util/dlinklist.h" -#include "lib/async_req/async_req_ntstatus.h" - -void async_req_nterror(struct async_req *req, NTSTATUS status) -{ - async_req_error(req, NT_STATUS_V(status)); -} - -bool async_post_ntstatus(struct async_req *req, struct tevent_context *ev, - NTSTATUS status) -{ - return async_post_error(req, ev, NT_STATUS_V(status)); -} - -bool async_req_is_nterror(struct async_req *req, NTSTATUS *status) -{ - enum async_req_state state; - uint64_t error; - - if (!async_req_is_error(req, &state, &error)) { - return false; - } - switch (state) { - case ASYNC_REQ_USER_ERROR: - *status = NT_STATUS(error); - break; - case ASYNC_REQ_TIMED_OUT: - *status = NT_STATUS_IO_TIMEOUT; - break; - case ASYNC_REQ_NO_MEMORY: - *status = NT_STATUS_NO_MEMORY; - break; - default: - *status = NT_STATUS_INTERNAL_ERROR; - break; - } - return true; -} - -NTSTATUS async_req_simple_recv_ntstatus(struct async_req *req) -{ - NTSTATUS status; - - if (async_req_is_nterror(req, &status)) { - return status; - } - return NT_STATUS_OK; -} diff --git a/lib/async_req/async_req_ntstatus.h b/lib/async_req/async_req_ntstatus.h deleted file mode 100644 index 7555aac603..0000000000 --- a/lib/async_req/async_req_ntstatus.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - Unix SMB/CIFS implementation. - NTSTATUS wrappers for async_req.h - Copyright (C) Volker Lendecke 2008, 2009 - - 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 3 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, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef __ASYNC_REQ_NTSTATUS_H__ -#define __ASYNC_REQ_NTSTATUS_H__ - -#include "lib/async_req/async_req.h" -#include "includes.h" - -void async_req_nterror(struct async_req *req, NTSTATUS status); - -bool async_post_ntstatus(struct async_req *req, struct tevent_context *ev, - NTSTATUS status); - -bool async_req_is_nterror(struct async_req *req, NTSTATUS *status); - -NTSTATUS async_req_simple_recv_ntstatus(struct async_req *req); - -#endif diff --git a/lib/async_req/async_sock.c b/lib/async_req/async_sock.c index 39705f45bb..18adb42a0c 100644 --- a/lib/async_req/async_sock.c +++ b/lib/async_req/async_sock.c @@ -3,81 +3,39 @@ async socket syscalls Copyright (C) Volker Lendecke 2008 - 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 3 of the License, or - (at your option) any later version. + ** NOTE! The following LGPL license applies to the async_sock + ** library. This does NOT imply that all of Samba is released + ** under the LGPL - This program is distributed in the hope that it will be useful, + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "includes.h" -#include "lib/talloc/talloc.h" -#include "lib/tevent/tevent.h" -#include "lib/async_req/async_req.h" +#include "replace.h" +#include "system/network.h" +#include "system/filesys.h" +#include <talloc.h> +#include <tevent.h> #include "lib/async_req/async_sock.h" + +/* Note: lib/util/ is currently GPL */ #include "lib/util/tevent_unix.h" -#include <fcntl.h> +#include "lib/util/util.h" #ifndef TALLOC_FREE #define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) #endif -/** - * @brief Map async_req states to unix-style errnos - * @param[in] req The async req to get the state from - * @param[out] err Pointer to take the unix-style errno - * - * @return true if the async_req is in an error state, false otherwise - */ - -bool async_req_is_errno(struct async_req *req, int *err) -{ - enum async_req_state state; - uint64_t error; - - if (!async_req_is_error(req, &state, &error)) { - return false; - } - - switch (state) { - case ASYNC_REQ_USER_ERROR: - *err = (int)error; - break; - case ASYNC_REQ_TIMED_OUT: -#ifdef ETIMEDOUT - *err = ETIMEDOUT; -#else - *err = EAGAIN; -#endif - break; - case ASYNC_REQ_NO_MEMORY: - *err = ENOMEM; - break; - default: - *err = EIO; - break; - } - return true; -} - -int async_req_simple_recv_errno(struct async_req *req) -{ - int err; - - if (async_req_is_errno(req, &err)) { - return err; - } - - return 0; -} - struct async_send_state { int fd; const void *buf; @@ -127,6 +85,10 @@ static void async_send_handler(struct tevent_context *ev, tevent_req_data(req, struct async_send_state); state->sent = send(state->fd, state->buf, state->len, state->flags); + if ((state->sent == -1) && (errno == EINTR)) { + /* retry */ + return; + } if (state->sent == -1) { tevent_req_error(req, errno); return; @@ -194,6 +156,14 @@ static void async_recv_handler(struct tevent_context *ev, state->received = recv(state->fd, state->buf, state->len, state->flags); + if ((state->received == -1) && (errno == EINTR)) { + /* retry */ + return; + } + if (state->received == 0) { + tevent_req_error(req, EPIPE); + return; + } if (state->received == -1) { tevent_req_error(req, errno); return; @@ -217,6 +187,8 @@ struct async_connect_state { int result; int sys_errno; long old_sockflags; + socklen_t address_len; + struct sockaddr_storage address; }; static void async_connect_connected(struct tevent_context *ev, @@ -264,6 +236,13 @@ struct tevent_req *async_connect_send(TALLOC_CTX *mem_ctx, goto post_errno; } + state->address_len = address_len; + if (address_len > sizeof(state->address)) { + errno = EINVAL; + goto post_errno; + } + memcpy(&state->address, address, address_len); + set_blocking(fd, false); state->result = connect(fd, address, address_len); @@ -320,8 +299,6 @@ static void async_connect_connected(struct tevent_context *ev, struct async_connect_state *state = tevent_req_data(req, struct async_connect_state); - TALLOC_FREE(fde); - /* * Stevens, Network Programming says that if there's a * successful connect, the socket is only writable. Upon an @@ -329,20 +306,23 @@ static void async_connect_connected(struct tevent_context *ev, */ if ((flags & (TEVENT_FD_READ|TEVENT_FD_WRITE)) == (TEVENT_FD_READ|TEVENT_FD_WRITE)) { - int sockerr; - socklen_t err_len = sizeof(sockerr); - - if (getsockopt(state->fd, SOL_SOCKET, SO_ERROR, - (void *)&sockerr, &err_len) == 0) { - errno = sockerr; + int ret; + + ret = connect(state->fd, + (struct sockaddr *)(void *)&state->address, + state->address_len); + if (ret == 0) { + TALLOC_FREE(fde); + tevent_req_done(req); + return; } - state->sys_errno = errno; - - DEBUG(10, ("connect returned %s\n", strerror(errno))); - - fcntl(state->fd, F_SETFL, state->old_sockflags); - tevent_req_error(req, state->sys_errno); + if (errno == EINPROGRESS) { + /* Try again later, leave the fde around */ + return; + } + TALLOC_FREE(fde); + tevent_req_error(req, errno); return; } @@ -377,6 +357,7 @@ struct writev_state { struct iovec *iov; int count; size_t total_size; + uint16_t flags; }; static void writev_trigger(struct tevent_req *req, void *private_data); @@ -385,13 +366,14 @@ static void writev_handler(struct tevent_context *ev, struct tevent_fd *fde, struct tevent_req *writev_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tevent_queue *queue, int fd, + bool err_on_readability, struct iovec *iov, int count) { - struct tevent_req *result; + struct tevent_req *req; struct writev_state *state; - result = tevent_req_create(mem_ctx, &state, struct writev_state); - if (result == NULL) { + req = tevent_req_create(mem_ctx, &state, struct writev_state); + if (req == NULL) { return NULL; } state->ev = ev; @@ -403,13 +385,27 @@ struct tevent_req *writev_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, if (state->iov == NULL) { goto fail; } + state->flags = TEVENT_FD_WRITE; + if (err_on_readability) { + state->flags |= TEVENT_FD_READ; + } - if (!tevent_queue_add(queue, ev, result, writev_trigger, NULL)) { + if (queue == NULL) { + struct tevent_fd *fde; + fde = tevent_add_fd(state->ev, state, state->fd, + state->flags, writev_handler, req); + if (tevent_req_nomem(fde, req)) { + return tevent_req_post(req, ev); + } + return req; + } + + if (!tevent_queue_add(queue, ev, req, writev_trigger, NULL)) { goto fail; } - return result; + return req; fail: - TALLOC_FREE(result); + TALLOC_FREE(req); return NULL; } @@ -418,7 +414,7 @@ static void writev_trigger(struct tevent_req *req, void *private_data) struct writev_state *state = tevent_req_data(req, struct writev_state); struct tevent_fd *fde; - fde = tevent_add_fd(state->ev, state, state->fd, TEVENT_FD_WRITE, + fde = tevent_add_fd(state->ev, state, state->fd, state->flags, writev_handler, req); if (fde == NULL) { tevent_req_error(req, ENOMEM); @@ -437,11 +433,20 @@ static void writev_handler(struct tevent_context *ev, struct tevent_fd *fde, to_write = 0; + if ((state->flags & TEVENT_FD_READ) && (flags & TEVENT_FD_READ)) { + tevent_req_error(req, EPIPE); + return; + } + for (i=0; i<state->count; i++) { to_write += state->iov[i].iov_len; } - written = sys_writev(state->fd, state->iov, state->count); + written = writev(state->fd, state->iov, state->count); + if ((written == -1) && (errno == EINTR)) { + /* retry */ + return; + } if (written == -1) { tevent_req_error(req, errno); return; @@ -549,6 +554,10 @@ static void read_packet_handler(struct tevent_context *ev, nread = recv(state->fd, state->buf+state->nread, total-state->nread, 0); + if ((nread == -1) && (errno == EINTR)) { + /* retry */ + return; + } if (nread == -1) { tevent_req_error(req, errno); return; @@ -585,7 +594,7 @@ static void read_packet_handler(struct tevent_context *ev, return; } - tmp = TALLOC_REALLOC_ARRAY(state, state->buf, uint8_t, total+more); + tmp = talloc_realloc(state, state->buf, uint8_t, total+more); if (tevent_req_nomem(tmp, req)) { return; } diff --git a/lib/async_req/async_sock.h b/lib/async_req/async_sock.h index c5d9400eb6..e7ddff8c92 100644 --- a/lib/async_req/async_sock.h +++ b/lib/async_req/async_sock.h @@ -3,27 +3,29 @@ async socket operations Copyright (C) Volker Lendecke 2008 - 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 3 of the License, or - (at your option) any later version. + ** NOTE! The following LGPL license applies to the async_sock + ** library. This does NOT imply that all of Samba is released + ** under the LGPL - This program is distributed in the hope that it will be useful, + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __ASYNC_SOCK_H__ #define __ASYNC_SOCK_H__ -#include "includes.h" - -bool async_req_is_errno(struct async_req *req, int *err); -int async_req_simple_recv_errno(struct async_req *req); +#include <talloc.h> +#include <tevent.h> struct tevent_req *async_send_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -44,6 +46,7 @@ int async_connect_recv(struct tevent_req *req, int *perrno); struct tevent_req *writev_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tevent_queue *queue, int fd, + bool err_on_readability, struct iovec *iov, int count); ssize_t writev_recv(struct tevent_req *req, int *perrno); diff --git a/lib/async_req/config.mk b/lib/async_req/config.mk index 820f890fd0..64e537c088 100644 --- a/lib/async_req/config.mk +++ b/lib/async_req/config.mk @@ -1,3 +1,4 @@ [SUBSYSTEM::LIBASYNC_REQ] +PUBLIC_DEPENDENCIES = LIBREPLACE_NETWORK LIBTALLOC LIBTEVENT -LIBASYNC_REQ_OBJ_FILES = $(addprefix ../lib/async_req/, async_req.o async_sock.o async_req_ntstatus.o) +LIBASYNC_REQ_OBJ_FILES = $(addprefix ../lib/async_req/, async_sock.o) diff --git a/lib/crypto/aes.c b/lib/crypto/aes.c new file mode 100644 index 0000000000..7735e8ff37 --- /dev/null +++ b/lib/crypto/aes.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2003 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "replace.h" + +#include "rijndael-alg-fst.h" +#include "aes.h" + +int +AES_set_encrypt_key(const unsigned char *userkey, const int bits, AES_KEY *key) +{ + key->rounds = rijndaelKeySetupEnc(key->key, userkey, bits); + if (key->rounds == 0) + return -1; + return 0; +} + +int +AES_set_decrypt_key(const unsigned char *userkey, const int bits, AES_KEY *key) +{ + key->rounds = rijndaelKeySetupDec(key->key, userkey, bits); + if (key->rounds == 0) + return -1; + return 0; +} + +void +AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key) +{ + rijndaelEncrypt(key->key, key->rounds, in, out); +} + +void +AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key) +{ + rijndaelDecrypt(key->key, key->rounds, in, out); +} + +void +AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + unsigned long size, const AES_KEY *key, + unsigned char *iv, int forward_encrypt) +{ + unsigned char tmp[AES_BLOCK_SIZE]; + int i; + + if (forward_encrypt) { + while (size >= AES_BLOCK_SIZE) { + for (i = 0; i < AES_BLOCK_SIZE; i++) + tmp[i] = in[i] ^ iv[i]; + AES_encrypt(tmp, out, key); + memcpy(iv, out, AES_BLOCK_SIZE); + size -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (size) { + for (i = 0; i < size; i++) + tmp[i] = in[i] ^ iv[i]; + for (i = size; i < AES_BLOCK_SIZE; i++) + tmp[i] = iv[i]; + AES_encrypt(tmp, out, key); + memcpy(iv, out, AES_BLOCK_SIZE); + } + } else { + while (size >= AES_BLOCK_SIZE) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(tmp, out, key); + for (i = 0; i < AES_BLOCK_SIZE; i++) + out[i] ^= iv[i]; + memcpy(iv, tmp, AES_BLOCK_SIZE); + size -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (size) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(tmp, out, key); + for (i = 0; i < size; i++) + out[i] ^= iv[i]; + memcpy(iv, tmp, AES_BLOCK_SIZE); + } + } +} diff --git a/lib/crypto/aes.h b/lib/crypto/aes.h new file mode 100644 index 0000000000..e74d345215 --- /dev/null +++ b/lib/crypto/aes.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2003-2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id$ */ + +#ifndef LIB_CRYPTO_AES_H +#define LIB_CRYPTO_AES_H 1 + +/* symbol renaming */ +#define AES_set_encrypt_key samba_AES_set_encrypt_key +#define AES_set_decrypt_key samba_AES_decrypt_key +#define AES_encrypt samba_AES_encrypt +#define AES_decrypt samba_AES_decrypt +#define AES_cbc_encrypt samba_AES_cbc_encrypt + +/* + * + */ + +#define AES_BLOCK_SIZE 16 +#define AES_MAXNR 14 + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +typedef struct aes_key { + uint32_t key[(AES_MAXNR+1)*4]; + int rounds; +} AES_KEY; + +#ifdef __cplusplus +extern "C" { +#endif + +int AES_set_encrypt_key(const unsigned char *, const int, AES_KEY *); +int AES_set_decrypt_key(const unsigned char *, const int, AES_KEY *); + +void AES_encrypt(const unsigned char *, unsigned char *, const AES_KEY *); +void AES_decrypt(const unsigned char *, unsigned char *, const AES_KEY *); + +void AES_cbc_encrypt(const unsigned char *, unsigned char *, + const unsigned long, const AES_KEY *, + unsigned char *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* LIB_CRYPTO_AES_H */ diff --git a/lib/crypto/config.mk b/lib/crypto/config.mk index e39f06dcae..c9ba779be2 100644 --- a/lib/crypto/config.mk +++ b/lib/crypto/config.mk @@ -6,7 +6,8 @@ LIBCRYPTO_OBJ_FILES = $(addprefix $(libcryptosrcdir)/, \ crc32.o md5.o hmacmd5.o md4.o \ - arcfour.o sha256.o hmacsha256.o) + arcfour.o sha256.o hmacsha256.o \ + aes.o rijndael-alg-fst.o) [SUBSYSTEM::TORTURE_LIBCRYPTO] PRIVATE_DEPENDENCIES = LIBCRYPTO diff --git a/lib/crypto/crypto.h b/lib/crypto/crypto.h index 0a43cbe7d4..b5ea9c78d5 100644 --- a/lib/crypto/crypto.h +++ b/lib/crypto/crypto.h @@ -24,5 +24,5 @@ #include "../lib/crypto/sha256.h" #include "../lib/crypto/hmacsha256.h" #include "../lib/crypto/arcfour.h" - +#include "../lib/crypto/aes.h" diff --git a/lib/crypto/rijndael-alg-fst.c b/lib/crypto/rijndael-alg-fst.c new file mode 100644 index 0000000000..9f70d64918 --- /dev/null +++ b/lib/crypto/rijndael-alg-fst.c @@ -0,0 +1,1223 @@ +/* $NetBSD: rijndael-alg-fst.c,v 1.5 2001/11/13 01:40:10 lukem Exp $ */ +/* $KAME: rijndael-alg-fst.c,v 1.10 2003/07/15 10:47:16 itojun Exp $ */ +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> + * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> + * @author Paulo Barreto <paulo.barreto@terra.com.br> + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* "$NetBSD: rijndael-alg-fst.c,v 1.5 2001/11/13 01:40:10 lukem Exp $" */ + +#include "replace.h" + +#include "rijndael-alg-fst.h" + +/* the file should not be used from outside */ +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u32 Td4[256] = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) + +#ifdef _MSC_VER +#define GETU32(p) SWAP(*((u32 *)(p))) +#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } +#else +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } +#endif + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) { + int i = 0; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + if (keyBits == 128) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 10; + } + rk += 4; + } + } + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + if (keyBits == 192) { + for (;;) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 12; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + if (keyBits == 256) { + for (;;) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 14; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) ] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) { + int Nr, i, j; + u32 temp; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } + return Nr; +} + +void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]) { + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ct , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ct + 12, s3); +} + +void rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], u8 pt[16]) { + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(pt , s0); + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(pt + 4, s1); + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(pt + 8, s2); + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(pt + 12, s3); +} diff --git a/lib/crypto/rijndael-alg-fst.h b/lib/crypto/rijndael-alg-fst.h new file mode 100644 index 0000000000..d5d96ed5b4 --- /dev/null +++ b/lib/crypto/rijndael-alg-fst.h @@ -0,0 +1,46 @@ +/* $NetBSD: rijndael-alg-fst.h,v 1.2 2000/10/02 17:19:15 itojun Exp $ */ +/* $KAME: rijndael-alg-fst.h,v 1.5 2003/07/15 10:47:16 itojun Exp $ */ +/** + * rijndael-alg-fst.h + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> + * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> + * @author Paulo Barreto <paulo.barreto@terra.com.br> + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef LIB_CRYPTO_RIJNDAEL_ALG_FST_H +#define LIB_CRYPTO_RIJNDAEL_ALG_FST_H + +/* symbol renaming */ +#define rijndaelKeySetupEnc _samba_rijndaelKeySetupEnc +#define rijndaelKeySetupDec _samba_rijndaelKeySetupDec +#define rijndaelEncrypt _samba_rijndaelEncrypt +#define rijndaelDecrypt _samba_rijndaelDecrypt + +#define RIJNDAEL_MAXKC (256/32) +#define RIJNDAEL_MAXKB (256/8) +#define RIJNDAEL_MAXNR 14 + +int rijndaelKeySetupEnc(uint32_t rk[/*4*(Nr + 1)*/], const uint8_t cipherKey[], int keyBits); +int rijndaelKeySetupDec(uint32_t rk[/*4*(Nr + 1)*/], const uint8_t cipherKey[], int keyBits); +void rijndaelEncrypt(const uint32_t rk[/*4*(Nr + 1)*/], int Nr, const uint8_t pt[16], uint8_t ct[16]); +void rijndaelDecrypt(const uint32_t rk[/*4*(Nr + 1)*/], int Nr, const uint8_t ct[16], uint8_t pt[16]); + +#endif /* LIB_CRYPTO_RIJNDAEL_ALG_FST_H */ diff --git a/lib/crypto/sha256.c b/lib/crypto/sha256.c index a2def25814..233abe23f8 100644 --- a/lib/crypto/sha256.c +++ b/lib/crypto/sha256.c @@ -189,7 +189,7 @@ struct x32{ void SHA256_Update (SHA256_CTX *m, const void *v, size_t len) { - const unsigned char *p = v; + const unsigned char *p = (const unsigned char *)v; size_t old_sz = m->sz[0]; size_t offset; diff --git a/lib/nss_wrapper/nss_wrapper.c b/lib/nss_wrapper/nss_wrapper.c index 1875dc3e4f..dc2d4f58fd 100644 --- a/lib/nss_wrapper/nss_wrapper.c +++ b/lib/nss_wrapper/nss_wrapper.c @@ -1,5 +1,6 @@ /* * Copyright (C) Stefan Metzmacher 2007 <metze@samba.org> + * Copyright (C) Guenther Deschner 2009 <gd@samba.org> * * All rights reserved. * @@ -37,6 +38,7 @@ #include "../replace/replace.h" #include "system/passwd.h" #include "system/filesys.h" +#include "../nsswitch/nsstest.h" #else /* _SAMBA_BUILD_ */ @@ -61,13 +63,18 @@ #ifndef HAVE_GETGRNAM_R #define getgrnam_r(name, grdst, buf, buflen, grdstp) ENOSYS #endif -#ifndef HAVE_GETGRUID_R -#define getgrgid_r(uid, grdst, buf, buflen, grdstp) ENOSYS +#ifndef HAVE_GETGRGID_R +#define getgrgid_r(gid, grdst, buf, buflen, grdstp) ENOSYS #endif #ifndef HAVE_GETGRENT_R #define getgrent_r(grdst, buf, buflen, grdstp) ENOSYS #endif +/* not all systems have getgrouplist */ +#ifndef HAVE_GETGROUPLIST +#define getgrouplist(user, group, groups, ngroups) 0 +#endif + /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support * for now */ #define REWRITE_CALLS @@ -90,6 +97,7 @@ #define real_initgroups_dyn initgroups_dyn */ #define real_initgroups initgroups +#define real_getgrouplist getgrouplist #define real_getgrnam getgrnam #define real_getgrnam_r getgrnam_r @@ -133,6 +141,196 @@ #define NWRAP_VERBOSE(args) #endif +struct nwrap_module_nss_fns { + NSS_STATUS (*_nss_getpwnam_r)(const char *name, struct passwd *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_getpwuid_r)(uid_t uid, struct passwd *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_setpwent)(void); + NSS_STATUS (*_nss_getpwent_r)(struct passwd *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_endpwent)(void); + NSS_STATUS (*_nss_initgroups)(const char *user, gid_t group, long int *start, + long int *size, gid_t **groups, long int limit, int *errnop); + NSS_STATUS (*_nss_getgrnam_r)(const char *name, struct group *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_getgrgid_r)(gid_t gid, struct group *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_setgrent)(void); + NSS_STATUS (*_nss_getgrent_r)(struct group *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_endgrent)(void); +}; + +struct nwrap_backend { + const char *name; + const char *so_path; + void *so_handle; + struct nwrap_ops *ops; + struct nwrap_module_nss_fns *fns; +}; + +struct nwrap_ops { + struct passwd * (*nw_getpwnam)(struct nwrap_backend *b, + const char *name); + int (*nw_getpwnam_r)(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); + struct passwd * (*nw_getpwuid)(struct nwrap_backend *b, + uid_t uid); + int (*nw_getpwuid_r)(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); + void (*nw_setpwent)(struct nwrap_backend *b); + struct passwd * (*nw_getpwent)(struct nwrap_backend *b); + int (*nw_getpwent_r)(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp); + void (*nw_endpwent)(struct nwrap_backend *b); + int (*nw_initgroups)(struct nwrap_backend *b, + const char *user, gid_t group); + struct group * (*nw_getgrnam)(struct nwrap_backend *b, + const char *name); + int (*nw_getgrnam_r)(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); + struct group * (*nw_getgrgid)(struct nwrap_backend *b, + gid_t gid); + int (*nw_getgrgid_r)(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); + void (*nw_setgrent)(struct nwrap_backend *b); + struct group * (*nw_getgrent)(struct nwrap_backend *b); + int (*nw_getgrent_r)(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp); + void (*nw_endgrent)(struct nwrap_backend *b); +}; + +/* protoypes for files backend */ + + +static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b, + const char *name); +static int nwrap_files_getpwnam_r(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); +static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b, + uid_t uid); +static int nwrap_files_getpwuid_r(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); +static void nwrap_files_setpwent(struct nwrap_backend *b); +static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b); +static int nwrap_files_getpwent_r(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp); +static void nwrap_files_endpwent(struct nwrap_backend *b); +static int nwrap_files_initgroups(struct nwrap_backend *b, + const char *user, gid_t group); +static struct group *nwrap_files_getgrnam(struct nwrap_backend *b, + const char *name); +static int nwrap_files_getgrnam_r(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); +static struct group *nwrap_files_getgrgid(struct nwrap_backend *b, + gid_t gid); +static int nwrap_files_getgrgid_r(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); +static void nwrap_files_setgrent(struct nwrap_backend *b); +static struct group *nwrap_files_getgrent(struct nwrap_backend *b); +static int nwrap_files_getgrent_r(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp); +static void nwrap_files_endgrent(struct nwrap_backend *b); + +/* protoypes for module backend */ + +static struct passwd *nwrap_module_getpwent(struct nwrap_backend *b); +static int nwrap_module_getpwent_r(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp); +static struct passwd *nwrap_module_getpwnam(struct nwrap_backend *b, + const char *name); +static int nwrap_module_getpwnam_r(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); +static struct passwd *nwrap_module_getpwuid(struct nwrap_backend *b, + uid_t uid); +static int nwrap_module_getpwuid_r(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); +static void nwrap_module_setpwent(struct nwrap_backend *b); +static void nwrap_module_endpwent(struct nwrap_backend *b); +static struct group *nwrap_module_getgrent(struct nwrap_backend *b); +static int nwrap_module_getgrent_r(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp); +static struct group *nwrap_module_getgrnam(struct nwrap_backend *b, + const char *name); +static int nwrap_module_getgrnam_r(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); +static struct group *nwrap_module_getgrgid(struct nwrap_backend *b, + gid_t gid); +static int nwrap_module_getgrgid_r(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); +static void nwrap_module_setgrent(struct nwrap_backend *b); +static void nwrap_module_endgrent(struct nwrap_backend *b); +static int nwrap_module_initgroups(struct nwrap_backend *b, + const char *user, gid_t group); + +struct nwrap_ops nwrap_files_ops = { + .nw_getpwnam = nwrap_files_getpwnam, + .nw_getpwnam_r = nwrap_files_getpwnam_r, + .nw_getpwuid = nwrap_files_getpwuid, + .nw_getpwuid_r = nwrap_files_getpwuid_r, + .nw_setpwent = nwrap_files_setpwent, + .nw_getpwent = nwrap_files_getpwent, + .nw_getpwent_r = nwrap_files_getpwent_r, + .nw_endpwent = nwrap_files_endpwent, + .nw_initgroups = nwrap_files_initgroups, + .nw_getgrnam = nwrap_files_getgrnam, + .nw_getgrnam_r = nwrap_files_getgrnam_r, + .nw_getgrgid = nwrap_files_getgrgid, + .nw_getgrgid_r = nwrap_files_getgrgid_r, + .nw_setgrent = nwrap_files_setgrent, + .nw_getgrent = nwrap_files_getgrent, + .nw_getgrent_r = nwrap_files_getgrent_r, + .nw_endgrent = nwrap_files_endgrent, +}; + +struct nwrap_ops nwrap_module_ops = { + .nw_getpwnam = nwrap_module_getpwnam, + .nw_getpwnam_r = nwrap_module_getpwnam_r, + .nw_getpwuid = nwrap_module_getpwuid, + .nw_getpwuid_r = nwrap_module_getpwuid_r, + .nw_setpwent = nwrap_module_setpwent, + .nw_getpwent = nwrap_module_getpwent, + .nw_getpwent_r = nwrap_module_getpwent_r, + .nw_endpwent = nwrap_module_endpwent, + .nw_initgroups = nwrap_module_initgroups, + .nw_getgrnam = nwrap_module_getgrnam, + .nw_getgrnam_r = nwrap_module_getgrnam_r, + .nw_getgrgid = nwrap_module_getgrgid, + .nw_getgrgid_r = nwrap_module_getgrgid_r, + .nw_setgrent = nwrap_module_setgrent, + .nw_getgrent = nwrap_module_getgrent, + .nw_getgrent_r = nwrap_module_getgrent_r, + .nw_endgrent = nwrap_module_endgrent, +}; + +struct nwrap_main { + const char *nwrap_switch; + int num_backends; + struct nwrap_backend *backends; +}; + +struct nwrap_main *nwrap_main_global; +struct nwrap_main __nwrap_main_global; + struct nwrap_cache { const char *path; int fd; @@ -171,6 +369,142 @@ struct nwrap_gr nwrap_gr_global; static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line); static void nwrap_gr_unload(struct nwrap_cache *nwrap); +static void *nwrap_load_module_fn(struct nwrap_backend *b, + const char *fn_name) +{ + void *res; + char *s; + + if (!b->so_handle) { + NWRAP_ERROR(("%s: no handle\n", + __location__)); + return NULL; + } + + if (asprintf(&s, "_nss_%s_%s", b->name, fn_name) == -1) { + NWRAP_ERROR(("%s: out of memory\n", + __location__)); + return NULL; + } + + res = dlsym(b->so_handle, s); + if (!res) { + NWRAP_ERROR(("%s: cannot find function %s in %s\n", + __location__, s, b->so_path)); + } + free(s); + s = NULL; + return res; +} + +static struct nwrap_module_nss_fns *nwrap_load_module_fns(struct nwrap_backend *b) +{ + struct nwrap_module_nss_fns *fns; + + if (!b->so_handle) { + return NULL; + } + + fns = (struct nwrap_module_nss_fns *)malloc(sizeof(struct nwrap_module_nss_fns)); + if (!fns) { + return NULL; + } + + fns->_nss_getpwnam_r = (NSS_STATUS (*)(const char *, struct passwd *, char *, size_t, int *)) + nwrap_load_module_fn(b, "getpwnam_r"); + fns->_nss_getpwuid_r = (NSS_STATUS (*)(uid_t, struct passwd *, char *, size_t, int *)) + nwrap_load_module_fn(b, "getpwuid_r"); + fns->_nss_setpwent = (NSS_STATUS(*)(void)) + nwrap_load_module_fn(b, "setpwent"); + fns->_nss_getpwent_r = (NSS_STATUS (*)(struct passwd *, char *, size_t, int *)) + nwrap_load_module_fn(b, "getpwent_r"); + fns->_nss_endpwent = (NSS_STATUS(*)(void)) + nwrap_load_module_fn(b, "endpwent"); + fns->_nss_initgroups = (NSS_STATUS (*)(const char *, gid_t, long int *, long int *, gid_t **, long int, int *)) + nwrap_load_module_fn(b, "initgroups_dyn"); + fns->_nss_getgrnam_r = (NSS_STATUS (*)(const char *, struct group *, char *, size_t, int *)) + nwrap_load_module_fn(b, "getgrnam_r"); + fns->_nss_getgrgid_r = (NSS_STATUS (*)(gid_t, struct group *, char *, size_t, int *)) + nwrap_load_module_fn(b, "getgrgid_r"); + fns->_nss_setgrent = (NSS_STATUS(*)(void)) + nwrap_load_module_fn(b, "setgrent"); + fns->_nss_getgrent_r = (NSS_STATUS (*)(struct group *, char *, size_t, int *)) + nwrap_load_module_fn(b, "getgrent_r"); + fns->_nss_endgrent = (NSS_STATUS(*)(void)) + nwrap_load_module_fn(b, "endgrent"); + + return fns; +} + +static void *nwrap_load_module(const char *so_path) +{ + void *h; + + if (!so_path || !strlen(so_path)) { + return NULL; + } + + h = dlopen(so_path, RTLD_LAZY); + if (!h) { + NWRAP_ERROR(("%s: cannot open shared library %s\n", + __location__, so_path)); + return NULL; + } + + return h; +} + +static bool nwrap_module_init(const char *name, + struct nwrap_ops *ops, + const char *so_path, + int *num_backends, + struct nwrap_backend **backends) +{ + *backends = (struct nwrap_backend *)realloc(*backends, + sizeof(struct nwrap_backend) * ((*num_backends) + 1)); + if (!*backends) { + NWRAP_ERROR(("%s: out of memory\n", + __location__)); + return false; + } + + (*backends)[*num_backends].name = name; + (*backends)[*num_backends].ops = ops; + (*backends)[*num_backends].so_path = so_path; + (*backends)[*num_backends].so_handle = nwrap_load_module(so_path); + (*backends)[*num_backends].fns = nwrap_load_module_fns(&((*backends)[*num_backends])); + + (*num_backends)++; + + return true; +} + +static void nwrap_backend_init(struct nwrap_main *r) +{ + const char *winbind_so_path = getenv("NSS_WRAPPER_WINBIND_SO_PATH"); + + r->num_backends = 0; + r->backends = NULL; + + if (!nwrap_module_init("files", &nwrap_files_ops, NULL, + &r->num_backends, + &r->backends)) { + NWRAP_ERROR(("%s: failed to initialize 'files' backend\n", + __location__)); + return; + } + + if (winbind_so_path && strlen(winbind_so_path)) { + if (!nwrap_module_init("winbind", &nwrap_module_ops, winbind_so_path, + &r->num_backends, + &r->backends)) { + NWRAP_ERROR(("%s: failed to initialize 'winbind' backend\n", + __location__)); + return; + } + } +} + static void nwrap_init(void) { static bool initialized; @@ -178,6 +512,10 @@ static void nwrap_init(void) if (initialized) return; initialized = true; + nwrap_main_global = &__nwrap_main_global; + + nwrap_backend_init(nwrap_main_global); + nwrap_pw_global.cache = &__nwrap_cache_pw; nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD"); @@ -295,7 +633,7 @@ failed: return false; } -static void nwrap_cache_unload(struct nwrap_cache *nwrap) +static void nwrap_files_cache_unload(struct nwrap_cache *nwrap) { nwrap->unload(nwrap); @@ -304,7 +642,7 @@ static void nwrap_cache_unload(struct nwrap_cache *nwrap) nwrap->buf = NULL; } -static void nwrap_cache_reload(struct nwrap_cache *nwrap) +static void nwrap_files_cache_reload(struct nwrap_cache *nwrap) { struct stat st; int ret; @@ -355,13 +693,13 @@ reopen: nwrap->st = st; - nwrap_cache_unload(nwrap); + nwrap_files_cache_unload(nwrap); ok = nwrap_parse_file(nwrap); if (!ok) { NWRAP_ERROR(("%s: failed to reload %s\n", __location__, nwrap->path)); - nwrap_cache_unload(nwrap); + nwrap_files_cache_unload(nwrap); } NWRAP_DEBUG(("%s: reloaded %s\n", __location__, nwrap->path)); @@ -727,7 +1065,7 @@ static int nwrap_gr_copy_r(const struct group *src, struct group *dst, { char *first; char **lastm; - char *last; + char *last = NULL; off_t ofsb; off_t ofsm; off_t ofs; @@ -736,9 +1074,14 @@ static int nwrap_gr_copy_r(const struct group *src, struct group *dst, first = src->gr_name; lastm = src->gr_mem; - while (*lastm) lastm++; + while (*lastm) { + last = *lastm; + lastm++; + } - last = *lastm; + if (last == NULL) { + last = src->gr_passwd; + } while (*last) last++; ofsb = PTR_DIFF(last + 1, first); @@ -771,12 +1114,12 @@ static int nwrap_gr_copy_r(const struct group *src, struct group *dst, } /* user functions */ - -static struct passwd *nwrap_files_getpwnam(const char *name) +static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b, + const char *name) { int i; - nwrap_cache_reload(nwrap_pw_global.cache); + nwrap_files_cache_reload(nwrap_pw_global.cache); for (i=0; i<nwrap_pw_global.num; i++) { if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) { @@ -795,21 +1138,13 @@ static struct passwd *nwrap_files_getpwnam(const char *name) return NULL; } -_PUBLIC_ struct passwd *nwrap_getpwnam(const char *name) -{ - if (!nwrap_enabled()) { - return real_getpwnam(name); - } - - return nwrap_files_getpwnam(name); -} - -static int nwrap_files_getpwnam_r(const char *name, struct passwd *pwdst, +static int nwrap_files_getpwnam_r(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, char *buf, size_t buflen, struct passwd **pwdstp) { struct passwd *pw; - pw = nwrap_getpwnam(name); + pw = nwrap_files_getpwnam(b, name); if (!pw) { if (errno == 0) { return ENOENT; @@ -820,21 +1155,12 @@ static int nwrap_files_getpwnam_r(const char *name, struct passwd *pwdst, return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); } -_PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst, - char *buf, size_t buflen, struct passwd **pwdstp) -{ - if (!nwrap_enabled()) { - return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp); - } - - return nwrap_files_getpwnam_r(name, pwdst, buf, buflen, pwdstp); -} - -static struct passwd *nwrap_files_getpwuid(uid_t uid) +static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b, + uid_t uid) { int i; - nwrap_cache_reload(nwrap_pw_global.cache); + nwrap_files_cache_reload(nwrap_pw_global.cache); for (i=0; i<nwrap_pw_global.num; i++) { if (nwrap_pw_global.list[i].pw_uid == uid) { @@ -853,21 +1179,13 @@ static struct passwd *nwrap_files_getpwuid(uid_t uid) return NULL; } -_PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid) -{ - if (!nwrap_enabled()) { - return real_getpwuid(uid); - } - - return nwrap_files_getpwuid(uid); -} - -static int nwrap_files_getpwuid_r(uid_t uid, struct passwd *pwdst, +static int nwrap_files_getpwuid_r(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, char *buf, size_t buflen, struct passwd **pwdstp) { struct passwd *pw; - pw = nwrap_getpwuid(uid); + pw = nwrap_files_getpwuid(b, uid); if (!pw) { if (errno == 0) { return ENOENT; @@ -878,37 +1196,18 @@ static int nwrap_files_getpwuid_r(uid_t uid, struct passwd *pwdst, return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); } -_PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst, - char *buf, size_t buflen, struct passwd **pwdstp) -{ - if (!nwrap_enabled()) { - return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp); - } - - return nwrap_files_getpwuid_r(uid, pwdst, buf, buflen, pwdstp); -} - /* user enum functions */ -static void nwrap_files_setpwent(void) +static void nwrap_files_setpwent(struct nwrap_backend *b) { nwrap_pw_global.idx = 0; } -_PUBLIC_ void nwrap_setpwent(void) -{ - if (!nwrap_enabled()) { - real_setpwent(); - } - - nwrap_files_setpwent(); -} - -static struct passwd *nwrap_files_getpwent(void) +static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b) { struct passwd *pw; if (nwrap_pw_global.idx == 0) { - nwrap_cache_reload(nwrap_pw_global.cache); + nwrap_files_cache_reload(nwrap_pw_global.cache); } if (nwrap_pw_global.idx >= nwrap_pw_global.num) { @@ -924,21 +1223,13 @@ static struct passwd *nwrap_files_getpwent(void) return pw; } -_PUBLIC_ struct passwd *nwrap_getpwent(void) -{ - if (!nwrap_enabled()) { - return real_getpwent(); - } - - return nwrap_files_getpwent(); -} - -static int nwrap_files_getpwent_r(struct passwd *pwdst, char *buf, +static int nwrap_files_getpwent_r(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, size_t buflen, struct passwd **pwdstp) { struct passwd *pw; - pw = nwrap_getpwent(); + pw = nwrap_files_getpwent(b); if (!pw) { if (errno == 0) { return ENOENT; @@ -949,67 +1240,26 @@ static int nwrap_files_getpwent_r(struct passwd *pwdst, char *buf, return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); } -_PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf, - size_t buflen, struct passwd **pwdstp) -{ - if (!nwrap_enabled()) { -#ifdef SOLARIS_GETPWENT_R - struct passwd *pw; - pw = real_getpwent_r(pwdst, buf, buflen); - if (!pw) { - if (errno == 0) { - return ENOENT; - } - return errno; - } - if (pwdstp) { - *pwdstp = pw; - } - return 0; -#else - return real_getpwent_r(pwdst, buf, buflen, pwdstp); -#endif - } - - return nwrap_files_getpwent_r(pwdst, buf, buflen, pwdstp); -} - -static void nwrap_files_endpwent(void) +static void nwrap_files_endpwent(struct nwrap_backend *b) { nwrap_pw_global.idx = 0; } -_PUBLIC_ void nwrap_endpwent(void) -{ - if (!nwrap_enabled()) { - real_endpwent(); - } - - nwrap_files_endpwent(); -} - /* misc functions */ -static int nwrap_files_initgroups(const char *user, gid_t group) +static int nwrap_files_initgroups(struct nwrap_backend *b, + const char *user, gid_t group) { /* TODO: maybe we should also fake this... */ return EPERM; } -_PUBLIC_ int nwrap_initgroups(const char *user, gid_t group) -{ - if (!nwrap_enabled()) { - return real_initgroups(user, group); - } - - return nwrap_files_initgroups(user, group); -} - /* group functions */ -static struct group *nwrap_files_getgrnam(const char *name) +static struct group *nwrap_files_getgrnam(struct nwrap_backend *b, + const char *name) { int i; - nwrap_cache_reload(nwrap_gr_global.cache); + nwrap_files_cache_reload(nwrap_gr_global.cache); for (i=0; i<nwrap_gr_global.num; i++) { if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) { @@ -1028,21 +1278,13 @@ static struct group *nwrap_files_getgrnam(const char *name) return NULL; } -_PUBLIC_ struct group *nwrap_getgrnam(const char *name) -{ - if (!nwrap_enabled()) { - return real_getgrnam(name); - } - - return nwrap_files_getgrnam(name); -} - -static int nwrap_files_getgrnam_r(const char *name, struct group *grdst, +static int nwrap_files_getgrnam_r(struct nwrap_backend *b, + const char *name, struct group *grdst, char *buf, size_t buflen, struct group **grdstp) { struct group *gr; - gr = nwrap_getgrnam(name); + gr = nwrap_files_getgrnam(b, name); if (!gr) { if (errno == 0) { return ENOENT; @@ -1053,21 +1295,12 @@ static int nwrap_files_getgrnam_r(const char *name, struct group *grdst, return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); } -_PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst, - char *buf, size_t buflen, struct group **grdstp) -{ - if (!nwrap_enabled()) { - return real_getgrnam_r(name, grdst, buf, buflen, grdstp); - } - - return nwrap_files_getgrnam_r(name, grdst, buf, buflen, grdstp); -} - -static struct group *nwrap_files_getgrgid(gid_t gid) +static struct group *nwrap_files_getgrgid(struct nwrap_backend *b, + gid_t gid) { int i; - nwrap_cache_reload(nwrap_gr_global.cache); + nwrap_files_cache_reload(nwrap_gr_global.cache); for (i=0; i<nwrap_gr_global.num; i++) { if (nwrap_gr_global.list[i].gr_gid == gid) { @@ -1086,21 +1319,57 @@ static struct group *nwrap_files_getgrgid(gid_t gid) return NULL; } -_PUBLIC_ struct group *nwrap_getgrgid(gid_t gid) +static int nwrap_files_getgrgid_r(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) { - if (!nwrap_enabled()) { - return real_getgrgid(gid); + struct group *gr; + + gr = nwrap_files_getgrgid(b, gid); + if (!gr) { + if (errno == 0) { + return ENOENT; + } + return errno; } - return nwrap_files_getgrgid(gid); + return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); } -static int nwrap_files_getgrgid_r(gid_t gid, struct group *grdst, - char *buf, size_t buflen, struct group **grdstp) +/* group enum functions */ +static void nwrap_files_setgrent(struct nwrap_backend *b) +{ + nwrap_gr_global.idx = 0; +} + +static struct group *nwrap_files_getgrent(struct nwrap_backend *b) +{ + struct group *gr; + + if (nwrap_gr_global.idx == 0) { + nwrap_files_cache_reload(nwrap_gr_global.cache); + } + + if (nwrap_gr_global.idx >= nwrap_gr_global.num) { + errno = ENOENT; + return NULL; + } + + gr = &nwrap_gr_global.list[nwrap_gr_global.idx++]; + + NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n", + __location__, gr->gr_name, gr->gr_gid)); + + return gr; +} + +static int nwrap_files_getgrent_r(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp) { struct group *gr; - gr = nwrap_getgrgid(gid); + gr = nwrap_files_getgrent(b); if (!gr) { if (errno == 0) { return ENOENT; @@ -1109,84 +1378,745 @@ static int nwrap_files_getgrgid_r(gid_t gid, struct group *grdst, } return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); +} + +static void nwrap_files_endgrent(struct nwrap_backend *b) +{ + nwrap_gr_global.idx = 0; +} + +/* + * module backend + */ + +#ifndef SAFE_FREE +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0) +#endif + +static struct passwd *nwrap_module_getpwnam(struct nwrap_backend *b, + const char *name) +{ + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + if (!b->fns->_nss_getpwnam_r) { + return NULL; + } + + status = b->fns->_nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + return NULL; + } + return &pwd; +} + +static int nwrap_module_getpwnam_r(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + int ret; + + if (!b->fns->_nss_getpwnam_r) { + return NSS_STATUS_NOTFOUND; + } + + ret = b->fns->_nss_getpwnam_r(name, pwdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static struct passwd *nwrap_module_getpwuid(struct nwrap_backend *b, + uid_t uid) +{ + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + if (!b->fns->_nss_getpwuid_r) { + return NULL; + } + + status = b->fns->_nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + return NULL; + } + return &pwd; +} + +static int nwrap_module_getpwuid_r(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + int ret; + + if (!b->fns->_nss_getpwuid_r) { + return ENOENT; + } + + ret = b->fns->_nss_getpwuid_r(uid, pwdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static void nwrap_module_setpwent(struct nwrap_backend *b) +{ + if (!b->fns->_nss_setpwent) { + return; + } + + b->fns->_nss_setpwent(); +} + +static struct passwd *nwrap_module_getpwent(struct nwrap_backend *b) +{ + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + if (!b->fns->_nss_getpwent_r) { + return NULL; + } + + status = b->fns->_nss_getpwent_r(&pwd, buf, sizeof(buf), &errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + return NULL; + } + return &pwd; +} + +static int nwrap_module_getpwent_r(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp) +{ + int ret; + + if (!b->fns->_nss_getpwent_r) { + return ENOENT; + } + + ret = b->fns->_nss_getpwent_r(pwdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static void nwrap_module_endpwent(struct nwrap_backend *b) +{ + if (!b->fns->_nss_endpwent) { + return; + } + + b->fns->_nss_endpwent(); +} + +static int nwrap_module_initgroups(struct nwrap_backend *b, + const char *user, gid_t group) +{ + gid_t *groups; + long int start; + long int size; + + if (!b->fns->_nss_initgroups) { + return NSS_STATUS_UNAVAIL; + } + + return b->fns->_nss_initgroups(user, group, &start, &size, &groups, 0, &errno); +} + +static struct group *nwrap_module_getgrnam(struct nwrap_backend *b, + const char *name) +{ + static struct group grp; + static char *buf; + static int buflen = 1000; + NSS_STATUS status; + + if (!b->fns->_nss_getgrnam_r) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } +again: + status = b->fns->_nss_getgrnam_r(name, &grp, buf, buflen, &errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = (char *)realloc(buf, buflen); + if (!buf) { + return NULL; + } + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + return &grp; +} + +static int nwrap_module_getgrnam_r(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + int ret; + + if (!b->fns->_nss_getgrnam_r) { + return ENOENT; + } + + ret = b->fns->_nss_getgrnam_r(name, grdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static struct group *nwrap_module_getgrgid(struct nwrap_backend *b, + gid_t gid) +{ + static struct group grp; + static char *buf; + static int buflen = 1000; + NSS_STATUS status; + + if (!b->fns->_nss_getgrgid_r) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } + +again: + status = b->fns->_nss_getgrgid_r(gid, &grp, buf, buflen, &errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = (char *)realloc(buf, buflen); + if (!buf) { + return NULL; + } + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + return &grp; +} + +static int nwrap_module_getgrgid_r(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + int ret; + + if (!b->fns->_nss_getgrgid_r) { + return ENOENT; + } + + ret = b->fns->_nss_getgrgid_r(gid, grdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static void nwrap_module_setgrent(struct nwrap_backend *b) +{ + if (!b->fns->_nss_setgrent) { + return; + } + + b->fns->_nss_setgrent(); +} + +static struct group *nwrap_module_getgrent(struct nwrap_backend *b) +{ + static struct group grp; + static char *buf; + static int buflen = 1024; + NSS_STATUS status; + + if (!b->fns->_nss_getgrent_r) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } + +again: + status = b->fns->_nss_getgrent_r(&grp, buf, buflen, &errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = (char *)realloc(buf, buflen); + if (!buf) { + return NULL; + } + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + return &grp; +} + +static int nwrap_module_getgrent_r(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp) +{ + int ret; + + if (!b->fns->_nss_getgrent_r) { + return ENOENT; + } + + ret = b->fns->_nss_getgrent_r(grdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static void nwrap_module_endgrent(struct nwrap_backend *b) +{ + if (!b->fns->_nss_endgrent) { + return; + } + + b->fns->_nss_endgrent(); +} + +/* + * PUBLIC interface + */ + +_PUBLIC_ struct passwd *nwrap_getpwnam(const char *name) +{ + int i; + struct passwd *pwd; + + if (!nwrap_enabled()) { + return real_getpwnam(name); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + pwd = b->ops->nw_getpwnam(b, name); + if (pwd) { + return pwd; + } + } + + return NULL; +} + +_PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + int i,ret; + + if (!nwrap_enabled()) { + return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getpwnam_r(b, name, pwdst, buf, buflen, pwdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } return ENOENT; } -_PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst, - char *buf, size_t buflen, struct group **grdstp) +_PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid) { + int i; + struct passwd *pwd; + if (!nwrap_enabled()) { - return real_getgrgid_r(gid, grdst, buf, buflen, grdstp); + return real_getpwuid(uid); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + pwd = b->ops->nw_getpwuid(b, uid); + if (pwd) { + return pwd; + } } - return nwrap_files_getgrgid_r(gid, grdst, buf, buflen, grdstp); + return NULL; } -/* group enum functions */ -static void nwrap_files_setgrent(void) +_PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) { - nwrap_gr_global.idx = 0; + int i,ret; + + if (!nwrap_enabled()) { + return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getpwuid_r(b, uid, pwdst, buf, buflen, pwdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; } -_PUBLIC_ void nwrap_setgrent(void) +_PUBLIC_ void nwrap_setpwent(void) { + int i; + if (!nwrap_enabled()) { - real_setgrent(); + real_setpwent(); + return; } - nwrap_files_setgrent(); + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + b->ops->nw_setpwent(b); + } } -static struct group *nwrap_files_getgrent(void) +_PUBLIC_ struct passwd *nwrap_getpwent(void) { - struct group *gr; + int i; + struct passwd *pwd; - if (nwrap_gr_global.idx == 0) { - nwrap_cache_reload(nwrap_gr_global.cache); + if (!nwrap_enabled()) { + return real_getpwent(); } - if (nwrap_gr_global.idx >= nwrap_gr_global.num) { - errno = ENOENT; - return NULL; + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + pwd = b->ops->nw_getpwent(b); + if (pwd) { + return pwd; + } } - gr = &nwrap_gr_global.list[nwrap_gr_global.idx++]; + return NULL; +} - NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n", - __location__, gr->gr_name, gr->gr_gid)); +_PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp) +{ + int i,ret; - return gr; + if (!nwrap_enabled()) { +#ifdef SOLARIS_GETPWENT_R + struct passwd *pw; + pw = real_getpwent_r(pwdst, buf, buflen); + if (!pw) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + if (pwdstp) { + *pwdstp = pw; + } + return 0; +#else + return real_getpwent_r(pwdst, buf, buflen, pwdstp); +#endif + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getpwent_r(b, pwdst, buf, buflen, pwdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; } -_PUBLIC_ struct group *nwrap_getgrent(void) +_PUBLIC_ void nwrap_endpwent(void) { + int i; + if (!nwrap_enabled()) { - return real_getgrent(); + real_endpwent(); + return; } - return nwrap_files_getgrent(); + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + b->ops->nw_endpwent(b); + } } -static int nwrap_files_getgrent_r(struct group *grdst, char *buf, - size_t buflen, struct group **grdstp) +_PUBLIC_ int nwrap_initgroups(const char *user, gid_t group) { - struct group *gr; + int i; - gr = nwrap_getgrent(); - if (!gr) { - if (errno == 0) { - return ENOENT; + if (!nwrap_enabled()) { + return real_initgroups(user, group); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + return b->ops->nw_initgroups(b, user, group); + } + + errno = ENOENT; + return -1; +} + +_PUBLIC_ struct group *nwrap_getgrnam(const char *name) +{ + int i; + struct group *grp; + + if (!nwrap_enabled()) { + return real_getgrnam(name); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + grp = b->ops->nw_getgrnam(b, name); + if (grp) { + return grp; } - return errno; } - return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); + return NULL; +} + +_PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + int i,ret; + + if (!nwrap_enabled()) { + return real_getgrnam_r(name, grdst, buf, buflen, grdstp); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getgrnam_r(b, name, grdst, buf, buflen, grdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; +} + +_PUBLIC_ struct group *nwrap_getgrgid(gid_t gid) +{ + int i; + struct group *grp; + + if (!nwrap_enabled()) { + return real_getgrgid(gid); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + grp = b->ops->nw_getgrgid(b, gid); + if (grp) { + return grp; + } + } + + return NULL; +} + +_PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + int i,ret; + + if (!nwrap_enabled()) { + return real_getgrgid_r(gid, grdst, buf, buflen, grdstp); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getgrgid_r(b, gid, grdst, buf, buflen, grdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; +} + +_PUBLIC_ void nwrap_setgrent(void) +{ + int i; + + if (!nwrap_enabled()) { + real_setgrent(); + return; + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + b->ops->nw_setgrent(b); + } +} + +_PUBLIC_ struct group *nwrap_getgrent(void) +{ + int i; + struct group *grp; + + if (!nwrap_enabled()) { + return real_getgrent(); + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + grp = b->ops->nw_getgrent(b); + if (grp) { + return grp; + } + } + + return NULL; } _PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf, size_t buflen, struct group **grdstp) { + int i,ret; + if (!nwrap_enabled()) { #ifdef SOLARIS_GETGRENT_R struct group *gr; @@ -1206,19 +2136,102 @@ _PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf, #endif } - return nwrap_files_getgrent_r(grdst, buf, buflen, grdstp); -} + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getgrent_r(b, grdst, buf, buflen, grdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } -static void nwrap_files_endgrent(void) -{ - nwrap_gr_global.idx = 0; + return ENOENT; } _PUBLIC_ void nwrap_endgrent(void) { + int i; + if (!nwrap_enabled()) { real_endgrent(); + return; + } + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + b->ops->nw_endgrent(b); + } +} + +_PUBLIC_ int nwrap_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) +{ + struct group *grp; + gid_t *groups_tmp; + int count = 1; + const char *name_of_group = NULL; + + if (!nwrap_enabled()) { + return real_getgrouplist(user, group, groups, ngroups); + } + + NWRAP_DEBUG(("%s: getgrouplist called for %s\n", __location__, user)); + + groups_tmp = (gid_t *)malloc(count * sizeof(gid_t)); + if (!groups_tmp) { + NWRAP_ERROR(("%s:calloc failed\n",__location__)); + errno = ENOMEM; + return -1; + } + + memcpy(groups_tmp, &group, sizeof(gid_t)); + + grp = nwrap_getgrgid(group); + if (grp) { + name_of_group = grp->gr_name; } - nwrap_files_endgrent(); + nwrap_setgrent(); + while ((grp = nwrap_getgrent()) != NULL) { + int i = 0; + + NWRAP_VERBOSE(("%s: inspecting %s for group membership\n", + __location__, grp->gr_name)); + + for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) { + + if ((strcmp(user, grp->gr_mem[i]) == 0) && + (strcmp(name_of_group, grp->gr_name) != 0)) { + + NWRAP_DEBUG(("%s: %s is member of %s\n", + __location__, user, grp->gr_name)); + + groups_tmp = (gid_t *)realloc(groups_tmp, (count + 1) * sizeof(gid_t)); + if (!groups_tmp) { + NWRAP_ERROR(("%s:calloc failed\n",__location__)); + errno = ENOMEM; + return -1; + } + + memcpy(&groups_tmp[count], &grp->gr_gid, sizeof(gid_t)); + count++; + } + } + } + + nwrap_endgrent(); + + NWRAP_VERBOSE(("%s: %s is member of %d groups: %d\n", + __location__, user, *ngroups)); + + if (*ngroups < count) { + *ngroups = count; + free(groups_tmp); + return -1; + } + + *ngroups = count; + memcpy(groups, groups_tmp, count * sizeof(gid_t)); + free(groups_tmp); + + return count; } diff --git a/lib/nss_wrapper/nss_wrapper.h b/lib/nss_wrapper/nss_wrapper.h index 35a47348a8..5bcd42ead4 100644 --- a/lib/nss_wrapper/nss_wrapper.h +++ b/lib/nss_wrapper/nss_wrapper.h @@ -46,6 +46,7 @@ int nwrap_getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp); void nwrap_endpwent(void); int nwrap_initgroups(const char *user, gid_t group); +int nwrap_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups); struct group *nwrap_getgrnam(const char *name); int nwrap_getgrnam_r(const char *name, struct group *gbuf, char *buf, size_t buflen, struct group **gbufp); @@ -120,6 +121,11 @@ void nwrap_endgrent(void); #endif #define initgroups nwrap_initgroups +#ifdef getgrouplist +#undef getgrouplist +#endif +#define getgrouplist nwrap_getgrouplist + #ifdef getgrnam #undef getgrnam #endif diff --git a/lib/nss_wrapper/testsuite.c b/lib/nss_wrapper/testsuite.c new file mode 100644 index 0000000000..02a10e36c1 --- /dev/null +++ b/lib/nss_wrapper/testsuite.c @@ -0,0 +1,862 @@ +/* + Unix SMB/CIFS implementation. + + local testing of the nss wrapper + + Copyright (C) Guenther Deschner 2009 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "lib/replace/system/passwd.h" +#include "lib/nss_wrapper/nss_wrapper.h" + +static bool copy_passwd(struct torture_context *tctx, + const struct passwd *pwd, + struct passwd *p) +{ + p->pw_name = talloc_strdup(tctx, pwd->pw_name); + p->pw_passwd = talloc_strdup(tctx, pwd->pw_passwd); + p->pw_uid = pwd->pw_uid; + p->pw_gid = pwd->pw_gid; + p->pw_gecos = talloc_strdup(tctx, pwd->pw_gecos); + p->pw_dir = talloc_strdup(tctx, pwd->pw_dir); + p->pw_shell = talloc_strdup(tctx, pwd->pw_shell); + + return true; +} + +static void print_passwd(struct passwd *pwd) +{ + printf("%s:%s:%lu:%lu:%s:%s:%s\n", + pwd->pw_name, + pwd->pw_passwd, + (unsigned long)pwd->pw_uid, + (unsigned long)pwd->pw_gid, + pwd->pw_gecos, + pwd->pw_dir, + pwd->pw_shell); +} + + +static bool test_nwrap_getpwnam(struct torture_context *tctx, + const char *name, + struct passwd *pwd_p) +{ + struct passwd *pwd; + + torture_comment(tctx, "Testing getpwnam: %s\n", name); + + pwd = getpwnam(name); + if (pwd) { + print_passwd(pwd); + } + + if (pwd_p) { + copy_passwd(tctx, pwd, pwd_p); + } + + return pwd ? true : false; +} + +static bool test_nwrap_getpwnam_r(struct torture_context *tctx, + const char *name, + struct passwd *pwd_p) +{ + struct passwd pwd, *pwdp; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing getpwnam_r: %s\n", name); + + ret = getpwnam_r(name, &pwd, buffer, sizeof(buffer), &pwdp); + if (ret != 0) { + if (ret != ENOENT) { + torture_comment(tctx, "got %d return code\n", ret); + } + return false; + } + + print_passwd(&pwd); + + if (pwd_p) { + copy_passwd(tctx, &pwd, pwd_p); + } + + return true; +} + +static bool test_nwrap_getpwuid(struct torture_context *tctx, + uid_t uid, + struct passwd *pwd_p) +{ + struct passwd *pwd; + + torture_comment(tctx, "Testing getpwuid: %lu\n", (unsigned long)uid); + + pwd = getpwuid(uid); + if (pwd) { + print_passwd(pwd); + } + + if (pwd_p) { + copy_passwd(tctx, pwd, pwd_p); + } + + return pwd ? true : false; +} + +static bool test_nwrap_getpwuid_r(struct torture_context *tctx, + uid_t uid, + struct passwd *pwd_p) +{ + struct passwd pwd, *pwdp; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing getpwuid_r: %lu\n", (unsigned long)uid); + + ret = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &pwdp); + if (ret != 0) { + if (ret != ENOENT) { + torture_comment(tctx, "got %d return code\n", ret); + } + return false; + } + + print_passwd(&pwd); + + if (pwd_p) { + copy_passwd(tctx, &pwd, pwd_p); + } + + return true; +} + + +static bool copy_group(struct torture_context *tctx, + const struct group *grp, + struct group *g) +{ + int i; + + g->gr_name = talloc_strdup(tctx, grp->gr_name); + g->gr_passwd = talloc_strdup(tctx, grp->gr_passwd); + g->gr_gid = grp->gr_gid; + g->gr_mem = NULL; + + for (i=0; grp->gr_mem && grp->gr_mem[i]; i++) { + g->gr_mem = talloc_realloc(tctx, g->gr_mem, char *, i + 2); + g->gr_mem[i] = talloc_strdup(g->gr_mem, grp->gr_mem[i]); + g->gr_mem[i+1] = NULL; + } + + return true; +} + +static void print_group(struct group *grp) +{ + int i; + printf("%s:%s:%lu:", + grp->gr_name, + grp->gr_passwd, + (unsigned long)grp->gr_gid); + + if (!grp->gr_mem[0]) { + printf("\n"); + return; + } + + for (i=0; grp->gr_mem[i+1]; i++) { + printf("%s,", grp->gr_mem[i]); + } + printf("%s\n", grp->gr_mem[i]); +} + +static bool test_nwrap_getgrnam(struct torture_context *tctx, + const char *name, + struct group *grp_p) +{ + struct group *grp; + + torture_comment(tctx, "Testing getgrnam: %s\n", name); + + grp = getgrnam(name); + if (grp) { + print_group(grp); + } + + if (grp_p) { + copy_group(tctx, grp, grp_p); + } + + return grp ? true : false; +} + +static bool test_nwrap_getgrnam_r(struct torture_context *tctx, + const char *name, + struct group *grp_p) +{ + struct group grp, *grpp; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing getgrnam_r: %s\n", name); + + ret = getgrnam_r(name, &grp, buffer, sizeof(buffer), &grpp); + if (ret != 0) { + if (ret != ENOENT) { + torture_comment(tctx, "got %d return code\n", ret); + } + return false; + } + + print_group(&grp); + + if (grp_p) { + copy_group(tctx, &grp, grp_p); + } + + return true; +} + + +static bool test_nwrap_getgrgid(struct torture_context *tctx, + gid_t gid, + struct group *grp_p) +{ + struct group *grp; + + torture_comment(tctx, "Testing getgrgid: %lu\n", (unsigned long)gid); + + grp = getgrgid(gid); + if (grp) { + print_group(grp); + } + + if (grp_p) { + copy_group(tctx, grp, grp_p); + } + + return grp ? true : false; +} + +static bool test_nwrap_getgrgid_r(struct torture_context *tctx, + gid_t gid, + struct group *grp_p) +{ + struct group grp, *grpp; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing getgrgid_r: %lu\n", (unsigned long)gid); + + ret = getgrgid_r(gid, &grp, buffer, sizeof(buffer), &grpp); + if (ret != 0) { + if (ret != ENOENT) { + torture_comment(tctx, "got %d return code\n", ret); + } + return false; + } + + print_group(&grp); + + if (grp_p) { + copy_group(tctx, &grp, grp_p); + } + + return true; +} + +static bool test_nwrap_enum_passwd(struct torture_context *tctx, + struct passwd **pwd_array_p, + size_t *num_pwd_p) +{ + struct passwd *pwd; + struct passwd *pwd_array = NULL; + size_t num_pwd = 0; + + torture_comment(tctx, "Testing setpwent\n"); + setpwent(); + + while ((pwd = getpwent()) != NULL) { + torture_comment(tctx, "Testing getpwent\n"); + + print_passwd(pwd); + if (pwd_array_p && num_pwd_p) { + pwd_array = talloc_realloc(tctx, pwd_array, struct passwd, num_pwd+1); + torture_assert(tctx, pwd_array, "out of memory"); + copy_passwd(tctx, pwd, &pwd_array[num_pwd]); + num_pwd++; + } + } + + torture_comment(tctx, "Testing endpwent\n"); + endpwent(); + + if (pwd_array_p) { + *pwd_array_p = pwd_array; + } + if (num_pwd_p) { + *num_pwd_p = num_pwd; + } + + return true; +} + +static bool test_nwrap_enum_r_passwd(struct torture_context *tctx, + struct passwd **pwd_array_p, + size_t *num_pwd_p) +{ + struct passwd pwd, *pwdp; + struct passwd *pwd_array = NULL; + size_t num_pwd = 0; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing setpwent\n"); + setpwent(); + + while (1) { + torture_comment(tctx, "Testing getpwent_r\n"); + + ret = getpwent_r(&pwd, buffer, sizeof(buffer), &pwdp); + if (ret != 0) { + if (ret != ENOENT) { + torture_comment(tctx, "got %d return code\n", ret); + } + break; + } + print_passwd(&pwd); + if (pwd_array_p && num_pwd_p) { + pwd_array = talloc_realloc(tctx, pwd_array, struct passwd, num_pwd+1); + torture_assert(tctx, pwd_array, "out of memory"); + copy_passwd(tctx, &pwd, &pwd_array[num_pwd]); + num_pwd++; + } + } + + torture_comment(tctx, "Testing endpwent\n"); + endpwent(); + + if (pwd_array_p) { + *pwd_array_p = pwd_array; + } + if (num_pwd_p) { + *num_pwd_p = num_pwd; + } + + return true; +} + +static bool torture_assert_passwd_equal(struct torture_context *tctx, + const struct passwd *p1, + const struct passwd *p2, + const char *comment) +{ + torture_assert_str_equal(tctx, p1->pw_name, p2->pw_name, comment); + torture_assert_str_equal(tctx, p1->pw_passwd, p2->pw_passwd, comment); + torture_assert_int_equal(tctx, p1->pw_uid, p2->pw_uid, comment); + torture_assert_int_equal(tctx, p1->pw_gid, p2->pw_gid, comment); + torture_assert_str_equal(tctx, p1->pw_gecos, p2->pw_gecos, comment); + torture_assert_str_equal(tctx, p1->pw_dir, p2->pw_dir, comment); + torture_assert_str_equal(tctx, p1->pw_shell, p2->pw_shell, comment); + + return true; +} + +static bool test_nwrap_passwd(struct torture_context *tctx) +{ + int i; + struct passwd *pwd, pwd1, pwd2; + size_t num_pwd; + + torture_assert(tctx, test_nwrap_enum_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + + for (i=0; i < num_pwd; i++) { + torture_assert(tctx, test_nwrap_getpwnam(tctx, pwd[i].pw_name, &pwd1), + "failed to call getpwnam for enumerated user"); + torture_assert_passwd_equal(tctx, &pwd[i], &pwd1, + "getpwent and getpwnam gave different results"); + torture_assert(tctx, test_nwrap_getpwuid(tctx, pwd[i].pw_uid, &pwd2), + "failed to call getpwuid for enumerated user"); + torture_assert_passwd_equal(tctx, &pwd[i], &pwd2, + "getpwent and getpwuid gave different results"); + torture_assert_passwd_equal(tctx, &pwd1, &pwd2, + "getpwnam and getpwuid gave different results"); + } + + return true; +} + +static bool test_nwrap_passwd_r(struct torture_context *tctx) +{ + int i; + struct passwd *pwd, pwd1, pwd2; + size_t num_pwd; + + torture_assert(tctx, test_nwrap_enum_r_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + + for (i=0; i < num_pwd; i++) { + torture_assert(tctx, test_nwrap_getpwnam_r(tctx, pwd[i].pw_name, &pwd1), + "failed to call getpwnam_r for enumerated user"); + torture_assert_passwd_equal(tctx, &pwd[i], &pwd1, + "getpwent_r and getpwnam_r gave different results"); + torture_assert(tctx, test_nwrap_getpwuid_r(tctx, pwd[i].pw_uid, &pwd2), + "failed to call getpwuid_r for enumerated user"); + torture_assert_passwd_equal(tctx, &pwd[i], &pwd2, + "getpwent_r and getpwuid_r gave different results"); + torture_assert_passwd_equal(tctx, &pwd1, &pwd2, + "getpwnam_r and getpwuid_r gave different results"); + } + + return true; +} + +static bool test_nwrap_passwd_r_cross(struct torture_context *tctx) +{ + int i; + struct passwd *pwd, pwd1, pwd2, pwd3, pwd4; + size_t num_pwd; + + torture_assert(tctx, test_nwrap_enum_r_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + + for (i=0; i < num_pwd; i++) { + torture_assert(tctx, test_nwrap_getpwnam_r(tctx, pwd[i].pw_name, &pwd1), + "failed to call getpwnam_r for enumerated user"); + torture_assert_passwd_equal(tctx, &pwd[i], &pwd1, + "getpwent_r and getpwnam_r gave different results"); + torture_assert(tctx, test_nwrap_getpwuid_r(tctx, pwd[i].pw_uid, &pwd2), + "failed to call getpwuid_r for enumerated user"); + torture_assert_passwd_equal(tctx, &pwd[i], &pwd2, + "getpwent_r and getpwuid_r gave different results"); + torture_assert_passwd_equal(tctx, &pwd1, &pwd2, + "getpwnam_r and getpwuid_r gave different results"); + torture_assert(tctx, test_nwrap_getpwnam(tctx, pwd[i].pw_name, &pwd3), + "failed to call getpwnam for enumerated user"); + torture_assert_passwd_equal(tctx, &pwd[i], &pwd3, + "getpwent_r and getpwnam gave different results"); + torture_assert(tctx, test_nwrap_getpwuid(tctx, pwd[i].pw_uid, &pwd4), + "failed to call getpwuid for enumerated user"); + torture_assert_passwd_equal(tctx, &pwd[i], &pwd4, + "getpwent_r and getpwuid gave different results"); + torture_assert_passwd_equal(tctx, &pwd3, &pwd4, + "getpwnam and getpwuid gave different results"); + } + + return true; +} + +static bool test_nwrap_enum_group(struct torture_context *tctx, + struct group **grp_array_p, + size_t *num_grp_p) +{ + struct group *grp; + struct group *grp_array = NULL; + size_t num_grp = 0; + + torture_comment(tctx, "Testing setgrent\n"); + setgrent(); + + while ((grp = getgrent()) != NULL) { + torture_comment(tctx, "Testing getgrent\n"); + + print_group(grp); + if (grp_array_p && num_grp_p) { + grp_array = talloc_realloc(tctx, grp_array, struct group, num_grp+1); + torture_assert(tctx, grp_array, "out of memory"); + copy_group(tctx, grp, &grp_array[num_grp]); + num_grp++; + } + } + + torture_comment(tctx, "Testing endgrent\n"); + endgrent(); + + if (grp_array_p) { + *grp_array_p = grp_array; + } + if (num_grp_p) { + *num_grp_p = num_grp; + } + + return true; +} + +static bool test_nwrap_enum_r_group(struct torture_context *tctx, + struct group **grp_array_p, + size_t *num_grp_p) +{ + struct group grp, *grpp; + struct group *grp_array = NULL; + size_t num_grp = 0; + char buffer[4096]; + int ret; + + torture_comment(tctx, "Testing setgrent\n"); + setgrent(); + + while (1) { + torture_comment(tctx, "Testing getgrent_r\n"); + + ret = getgrent_r(&grp, buffer, sizeof(buffer), &grpp); + if (ret != 0) { + if (ret != ENOENT) { + torture_comment(tctx, "got %d return code\n", ret); + } + break; + } + print_group(&grp); + if (grp_array_p && num_grp_p) { + grp_array = talloc_realloc(tctx, grp_array, struct group, num_grp+1); + torture_assert(tctx, grp_array, "out of memory"); + copy_group(tctx, &grp, &grp_array[num_grp]); + num_grp++; + } + } + + torture_comment(tctx, "Testing endgrent\n"); + endgrent(); + + if (grp_array_p) { + *grp_array_p = grp_array; + } + if (num_grp_p) { + *num_grp_p = num_grp; + } + + return true; +} + +static bool torture_assert_group_equal(struct torture_context *tctx, + const struct group *g1, + const struct group *g2, + const char *comment) +{ + int i; + torture_assert_str_equal(tctx, g1->gr_name, g2->gr_name, comment); + torture_assert_str_equal(tctx, g1->gr_passwd, g2->gr_passwd, comment); + torture_assert_int_equal(tctx, g1->gr_gid, g2->gr_gid, comment); + if (g1->gr_mem && !g2->gr_mem) { + return false; + } + if (!g1->gr_mem && g2->gr_mem) { + return false; + } + if (!g1->gr_mem && !g2->gr_mem) { + return true; + } + for (i=0; g1->gr_mem[i] && g2->gr_mem[i]; i++) { + torture_assert_str_equal(tctx, g1->gr_mem[i], g2->gr_mem[i], comment); + } + + return true; +} + +static bool test_nwrap_group(struct torture_context *tctx) +{ + int i; + struct group *grp, grp1, grp2; + size_t num_grp; + + torture_assert(tctx, test_nwrap_enum_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_grp; i++) { + torture_assert(tctx, test_nwrap_getgrnam(tctx, grp[i].gr_name, &grp1), + "failed to call getgrnam for enumerated user"); + torture_assert_group_equal(tctx, &grp[i], &grp1, + "getgrent and getgrnam gave different results"); + torture_assert(tctx, test_nwrap_getgrgid(tctx, grp[i].gr_gid, &grp2), + "failed to call getgrgid for enumerated user"); + torture_assert_group_equal(tctx, &grp[i], &grp2, + "getgrent and getgrgid gave different results"); + torture_assert_group_equal(tctx, &grp1, &grp2, + "getgrnam and getgrgid gave different results"); + } + + return true; +} + +static bool test_nwrap_group_r(struct torture_context *tctx) +{ + int i; + struct group *grp, grp1, grp2; + size_t num_grp; + + torture_assert(tctx, test_nwrap_enum_r_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_grp; i++) { + torture_assert(tctx, test_nwrap_getgrnam_r(tctx, grp[i].gr_name, &grp1), + "failed to call getgrnam_r for enumerated user"); + torture_assert_group_equal(tctx, &grp[i], &grp1, + "getgrent_r and getgrnam_r gave different results"); + torture_assert(tctx, test_nwrap_getgrgid_r(tctx, grp[i].gr_gid, &grp2), + "failed to call getgrgid_r for enumerated user"); + torture_assert_group_equal(tctx, &grp[i], &grp2, + "getgrent_r and getgrgid_r gave different results"); + torture_assert_group_equal(tctx, &grp1, &grp2, + "getgrnam_r and getgrgid_r gave different results"); + } + + return true; +} + +static bool test_nwrap_group_r_cross(struct torture_context *tctx) +{ + int i; + struct group *grp, grp1, grp2, grp3, grp4; + size_t num_grp; + + torture_assert(tctx, test_nwrap_enum_r_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_grp; i++) { + torture_assert(tctx, test_nwrap_getgrnam_r(tctx, grp[i].gr_name, &grp1), + "failed to call getgrnam_r for enumerated user"); + torture_assert_group_equal(tctx, &grp[i], &grp1, + "getgrent_r and getgrnam_r gave different results"); + torture_assert(tctx, test_nwrap_getgrgid_r(tctx, grp[i].gr_gid, &grp2), + "failed to call getgrgid_r for enumerated user"); + torture_assert_group_equal(tctx, &grp[i], &grp2, + "getgrent_r and getgrgid_r gave different results"); + torture_assert_group_equal(tctx, &grp1, &grp2, + "getgrnam_r and getgrgid_r gave different results"); + torture_assert(tctx, test_nwrap_getgrnam(tctx, grp[i].gr_name, &grp3), + "failed to call getgrnam for enumerated user"); + torture_assert_group_equal(tctx, &grp[i], &grp3, + "getgrent_r and getgrnam gave different results"); + torture_assert(tctx, test_nwrap_getgrgid(tctx, grp[i].gr_gid, &grp4), + "failed to call getgrgid for enumerated user"); + torture_assert_group_equal(tctx, &grp[i], &grp4, + "getgrent_r and getgrgid gave different results"); + torture_assert_group_equal(tctx, &grp3, &grp4, + "getgrnam and getgrgid gave different results"); + } + + return true; +} + +static bool test_nwrap_getgrouplist(struct torture_context *tctx, + const char *user, + gid_t gid, + gid_t **gids_p, + int *num_gids_p) +{ + int ret; + int num_groups = 0; + gid_t *groups = NULL; + + torture_comment(tctx, "Testing getgrouplist: %s\n", user); + + ret = getgrouplist(user, gid, NULL, &num_groups); + if (ret == -1 || num_groups != 0) { + + groups = talloc_array(tctx, gid_t, num_groups); + torture_assert(tctx, groups, "out of memory\n"); + + ret = getgrouplist(user, gid, groups, &num_groups); + } + + torture_assert(tctx, (ret != -1), "failed to call getgrouplist"); + + torture_comment(tctx, "%s is member in %d groups\n", user, num_groups); + + if (gids_p) { + *gids_p = groups; + } + if (num_gids_p) { + *num_gids_p = num_groups; + } + + return true; +} + +static bool test_nwrap_user_in_group(struct torture_context *tctx, + const struct passwd *pwd, + const struct group *grp) +{ + int i; + + for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) { + if (strequal(grp->gr_mem[i], pwd->pw_name)) { + return true; + } + } + + return false; +} + +static bool test_nwrap_membership_user(struct torture_context *tctx, + const struct passwd *pwd, + struct group *grp_array, + size_t num_grp) +{ + int num_user_groups = 0; + int num_user_groups_from_enum = 0; + gid_t *user_groups = NULL; + int g, i; + bool primary_group_had_user_member = false; + + torture_assert(tctx, test_nwrap_getgrouplist(tctx, + pwd->pw_name, + pwd->pw_gid, + &user_groups, + &num_user_groups), + "failed to test getgrouplist"); + + for (g=0; g < num_user_groups; g++) { + torture_assert(tctx, test_nwrap_getgrgid(tctx, user_groups[g], NULL), + "failed to find the group the user is a member of"); + } + + + for (i=0; i < num_grp; i++) { + + struct group grp = grp_array[i]; + + if (test_nwrap_user_in_group(tctx, pwd, &grp)) { + + struct group current_grp; + num_user_groups_from_enum++; + + torture_assert(tctx, test_nwrap_getgrnam(tctx, grp.gr_name, ¤t_grp), + "failed to find the group the user is a member of"); + + if (current_grp.gr_gid == pwd->pw_gid) { + torture_comment(tctx, "primary group %s of user %s lists user as member\n", + current_grp.gr_name, + pwd->pw_name); + primary_group_had_user_member = true; + } + + continue; + } + } + + if (!primary_group_had_user_member) { + num_user_groups_from_enum++; + } + + torture_assert_int_equal(tctx, num_user_groups, num_user_groups_from_enum, + "getgrouplist and real inspection of grouplist gave different results\n"); + + return true; +} + +static bool test_nwrap_membership(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + struct passwd *pwd; + size_t num_pwd; + struct group *grp; + size_t num_grp; + int i; + + if (!old_pwd || !old_group) { + torture_skip(tctx, "nothing to test\n"); + return true; + } + + torture_assert(tctx, test_nwrap_enum_passwd(tctx, &pwd, &num_pwd), + "failed to enumerate passwd"); + torture_assert(tctx, test_nwrap_enum_group(tctx, &grp, &num_grp), + "failed to enumerate group"); + + for (i=0; i < num_pwd; i++) { + + torture_assert(tctx, test_nwrap_membership_user(tctx, &pwd[i], grp, num_grp), + "failed to test membership for user"); + + } + + return true; +} + +static bool test_nwrap_enumeration(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + if (!old_pwd || !old_group) { + torture_skip(tctx, "nothing to test\n"); + return true; + } + + torture_assert(tctx, test_nwrap_passwd(tctx), + "failed to test users"); + torture_assert(tctx, test_nwrap_group(tctx), + "failed to test groups"); + + return true; +} + +static bool test_nwrap_reentrant_enumeration(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + if (!old_pwd || !old_group) { + torture_skip(tctx, "nothing to test\n"); + return true; + } + + torture_comment(tctx, "Testing re-entrant calls\n"); + + torture_assert(tctx, test_nwrap_passwd_r(tctx), + "failed to test users"); + torture_assert(tctx, test_nwrap_group_r(tctx), + "failed to test groups"); + + return true; +} + +static bool test_nwrap_reentrant_enumeration_crosschecks(struct torture_context *tctx) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + if (!old_pwd || !old_group) { + torture_skip(tctx, "nothing to test\n"); + return true; + } + + torture_comment(tctx, "Testing re-entrant calls with cross checks\n"); + + torture_assert(tctx, test_nwrap_passwd_r_cross(tctx), + "failed to test users"); + torture_assert(tctx, test_nwrap_group_r_cross(tctx), + "failed to test groups"); + + return true; +} + +struct torture_suite *torture_local_nss_wrapper(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "NSS-WRAPPER"); + + torture_suite_add_simple_test(suite, "enumeration", test_nwrap_enumeration); + torture_suite_add_simple_test(suite, "reentrant enumeration", test_nwrap_reentrant_enumeration); + torture_suite_add_simple_test(suite, "reentrant enumeration crosschecks", test_nwrap_reentrant_enumeration_crosschecks); + torture_suite_add_simple_test(suite, "membership", test_nwrap_membership); + + return suite; +} diff --git a/lib/popt/README b/lib/popt/README index 0b5205bfdd..95f8f8dc24 100644 --- a/lib/popt/README +++ b/lib/popt/README @@ -12,7 +12,7 @@ popt is used by rpm, the Red Hat install program, and many other Red Hat utilities, all of which provide excellent examples of how to use popt. Complete documentation on popt is available in popt.ps (included in this tarball), which is excerpted with permission from the book "Linux -Application Development" by Michael K. Johnson and Erik Troan (availble +Application Development" by Michael K. Johnson and Erik Troan (available from Addison Wesley in May, 1998). Comments on popt should be addressed to ewt@redhat.com. diff --git a/lib/replace/config.guess b/lib/replace/config.guess index 4af85584ae..da83314608 100755 --- a/lib/replace/config.guess +++ b/lib/replace/config.guess @@ -808,7 +808,7 @@ EOF echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:[3456]*) - case ${UNAME_MACHINE} in + case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; @@ -984,7 +984,7 @@ EOF echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so diff --git a/lib/replace/config.sub b/lib/replace/config.sub index 80a785297e..a39437d015 100755 --- a/lib/replace/config.sub +++ b/lib/replace/config.sub @@ -1454,7 +1454,7 @@ case $basic_machine in os=-aout ;; c4x-* | tic4x-*) - os=-coff + os=-coff ;; # This must come before the *-dec entry. pdp10-*) diff --git a/lib/replace/crypt.m4 b/lib/replace/crypt.m4 index 684c3519f2..fae2a58685 100644 --- a/lib/replace/crypt.m4 +++ b/lib/replace/crypt.m4 @@ -3,4 +3,4 @@ AC_CHECK_HEADERS(crypt.h) AC_SEARCH_LIBS_EXT(crypt, [crypt], CRYPT_LIBS, [ AC_DEFINE(HAVE_CRYPT,1,[Whether the system has the crypt() function]) ], - [ LIBREPLACEOBJ="${LIBREPLACEOBJ} crypt.o" ]) + [ LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/crypt.o" ]) diff --git a/lib/replace/dlfcn.m4 b/lib/replace/dlfcn.m4 index 42f56f26be..722e0246b7 100644 --- a/lib/replace/dlfcn.m4 +++ b/lib/replace/dlfcn.m4 @@ -23,7 +23,7 @@ AC_VERIFY_C_PROTOTYPE([void *dlopen(const char* filename, unsigned int flags)], ]) if test x"${libreplace_cv_dlfcn}" = x"yes";then - LIBREPLACEOBJ="${LIBREPLACEOBJ} dlfcn.o" + LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/dlfcn.o" fi LIBDL="$LIBS" diff --git a/lib/replace/getpass.m4 b/lib/replace/getpass.m4 index b93817f9d3..78a0afe453 100644 --- a/lib/replace/getpass.m4 +++ b/lib/replace/getpass.m4 @@ -3,7 +3,7 @@ AC_CHECK_FUNC(getpassphrase, libreplace_cv_HAVE_GETPASSPHRASE=yes) if test x"$libreplace_cv_HAVE_GETPASS" = x"yes" -a x"$libreplace_cv_HAVE_GETPASSPHRASE" = x"yes"; then AC_DEFINE(REPLACE_GETPASS_BY_GETPASSPHRASE, 1, [getpass returns <9 chars where getpassphrase returns <265 chars]) AC_DEFINE(REPLACE_GETPASS,1,[Whether getpass should be replaced]) - LIBREPLACEOBJ="${LIBREPLACEOBJ} getpass.o" + LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/getpass.o" else AC_CACHE_CHECK([whether getpass should be replaced],libreplace_cv_REPLACE_GETPASS,[ @@ -18,7 +18,7 @@ CPPFLAGS="$SAVE_CPPFLAGS" ]) if test x"$libreplace_cv_REPLACE_GETPASS" = x"yes"; then AC_DEFINE(REPLACE_GETPASS,1,[Whether getpass should be replaced]) - LIBREPLACEOBJ="${LIBREPLACEOBJ} getpass.o" + LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/getpass.o" fi fi diff --git a/lib/replace/libreplace.m4 b/lib/replace/libreplace.m4 index 30d7017d0f..af8587938d 100644 --- a/lib/replace/libreplace.m4 +++ b/lib/replace/libreplace.m4 @@ -16,7 +16,7 @@ done if test x"$libreplacedir" = "x"; then AC_MSG_ERROR([cannot find libreplace in $libreplacepaths]) fi -LIBREPLACEOBJ="replace.o" +LIBREPLACEOBJ="$libreplacedir/replace.o" AC_SUBST(LIBREPLACEOBJ) AC_CANONICAL_BUILD @@ -46,10 +46,10 @@ if test x"$libreplacedir" = "x"; then AC_MSG_ERROR([cannot find libreplace in $libreplacepaths]) fi -LIBREPLACEOBJ="replace.o" +LIBREPLACEOBJ="$libreplacedir/replace.o" AC_SUBST(LIBREPLACEOBJ) -LIBREPLACEOBJ="${LIBREPLACEOBJ} snprintf.o" +LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/snprintf.o" AC_TYPE_SIGNAL AC_TYPE_UID_T @@ -103,10 +103,11 @@ AC_CHECK_HEADERS(sys/time.h time.h) AC_CHECK_HEADERS(stdarg.h vararg.h) AC_CHECK_HEADERS(sys/mount.h mntent.h) AC_CHECK_HEADERS(stropts.h) +AC_CHECK_HEADERS(unix.h) AC_CHECK_FUNCS(seteuid setresuid setegid setresgid chroot bzero strerror) AC_CHECK_FUNCS(vsyslog setlinebuf mktime ftruncate chsize rename) -AC_CHECK_FUNCS(waitpid strlcpy strlcat initgroups memmove strdup) +AC_CHECK_FUNCS(waitpid wait4 strlcpy strlcat initgroups memmove strdup) AC_CHECK_FUNCS(pread pwrite strndup strcasestr strtok_r mkdtemp dup2) AC_CHECK_FUNCS(isatty chown lchown link readlink symlink realpath) AC_HAVE_DECL(setresuid, [#include <unistd.h>]) @@ -279,7 +280,7 @@ m4_include(timegm.m4) m4_include(repdir.m4) m4_include(crypt.m4) -AC_CHECK_FUNCS([syslog printf memset memcpy],,[AC_MSG_ERROR([Required function not found])]) +AC_CHECK_FUNCS([printf memset memcpy],,[AC_MSG_ERROR([Required function not found])]) echo "LIBREPLACE_BROKEN_CHECKS: END" ]) dnl end AC_LIBREPLACE_BROKEN_CHECKS diff --git a/lib/replace/libreplace_cc.m4 b/lib/replace/libreplace_cc.m4 index 30c63f2f05..a26dee498e 100644 --- a/lib/replace/libreplace_cc.m4 +++ b/lib/replace/libreplace_cc.m4 @@ -141,9 +141,7 @@ AC_CHECK_SIZEOF(off_t) AC_CHECK_SIZEOF(size_t) AC_CHECK_SIZEOF(ssize_t) -AC_CHECK_TYPE(intptr_t, long long) -AC_CHECK_TYPE(uintptr_t, unsigned long long) -AC_CHECK_TYPE(ptrdiff_t, unsigned long long) +AC_CHECK_TYPES([intptr_t, uintptr_t, ptrdiff_t]) if test x"$ac_cv_type_long_long" != x"yes";then AC_MSG_ERROR([LIBREPLACE needs type 'long long']) diff --git a/lib/replace/libreplace_ld.m4 b/lib/replace/libreplace_ld.m4 index 9529c5e0a5..bf0df6186d 100644 --- a/lib/replace/libreplace_ld.m4 +++ b/lib/replace/libreplace_ld.m4 @@ -40,7 +40,7 @@ LDFLAGS="$saved_LDFLAGS" AC_DEFUN([AC_LD_PICFLAG], [ case "$host_os" in - *linux*) + *linux*|*gnu*) PICFLAG="-fPIC" ;; *solaris*) @@ -71,13 +71,12 @@ case "$host_os" in PICFLAG="-O2" ;; *hpux*) - if test $ac_cv_prog_cc_Ae = yes; then - PICFLAG="+z +ESnolit" - elif test "${GCC}" = "yes"; then + if test "${GCC}" = "yes"; then PICFLAG="-fPIC" - fi - if test "$host_cpu" = "ia64"; then - PICFLAG="+z" + elif test "$host_cpu" = "ia64"; then + PICFLAG="+z" + elif test $ac_cv_prog_cc_Ae = yes; then + PICFLAG="+z +ESnolit" fi ;; *osf*) @@ -111,7 +110,7 @@ AC_DEFUN([AC_LIBREPLACE_LD_SHLIB_FLAGS], LD_SHLIB_FLAGS="-shared" case "$host_os" in - *linux*) + *linux*|*gnu*) LD_SHLIB_FLAGS="-shared -Wl,-Bsymbolic" ;; *solaris*) @@ -209,7 +208,7 @@ AC_DEFUN([AC_LD_SONAMEFLAG], AC_SUBST(SONAMEFLAG) SONAMEFLAG="" case "$host_os" in - *linux*) + *linux*|*gnu*|*qnx*) SONAMEFLAG="-Wl,-soname=" ;; *solaris*) @@ -249,6 +248,22 @@ AC_DEFUN([AC_LD_SONAMEFLAG], esac ]) +AC_DEFUN([AC_LD_VERSIONSCRIPT], +[ + AC_SUBST(VERSIONSCRIPT) + VERSIONSCRIPT="" + case "$host_os" in + *linux*|*gnu*) + VERSIONSCRIPT="-Wl,--version-script" + ;; + *solaris*) + if test "${GCC}" = "yes"; then + VERSIONSCRIPT="-Wl,--version-script" + fi + ;; + esac +]) + AC_DEFUN([AC_LIBREPLACE_MDLD], [ AC_REQUIRE([AC_LIBREPLACE_LD_SHLIB_LINKER]) @@ -261,7 +276,7 @@ AC_DEFUN([AC_LIBREPLACE_LD_SHLIB_ALLOW_UNDEF_FLAG], LD_ALLOW_SHLIB_UNDEF_FLAG="" case "$host_os" in - *linux*) + *linux*|*gnu*) LD_SHLIB_ALLOW_UNDEF_FLAG="-Wl,--allow-shlib-undefined" ;; *osf*) @@ -289,7 +304,7 @@ AC_DEFUN([AC_LIBREPLACE_MDLD_FLAGS], AC_DEFUN([AC_LIBREPLACE_RUNTIME_LIB_PATH_VAR], [ case "$host_os" in - *linux*) + *linux*|*gnu*) LIB_PATH_VAR=LD_LIBRARY_PATH ;; *bsd*) @@ -305,7 +320,7 @@ AC_DEFUN([AC_LIBREPLACE_RUNTIME_LIB_PATH_VAR], LIB_PATH_VAR=LD_LIBRARY_PATH ;; *aix*) - LIB_PATH_VAR=LIB_PATH + LIB_PATH_VAR=LIBPATH ;; *irix*) LIB_PATH_VAR=LD_LIBRARY_PATH @@ -313,6 +328,9 @@ AC_DEFUN([AC_LIBREPLACE_RUNTIME_LIB_PATH_VAR], *darwin*) LIB_PATH_VAR=DYLD_LIBRARY_PATH ;; + *) + LIB_PATH_VAR=LD_LIBRARY_PATH + ;; esac AC_SUBST(LIB_PATH_VAR) diff --git a/lib/replace/libreplace_network.m4 b/lib/replace/libreplace_network.m4 index 2af02312ab..d8ed8a1d53 100644 --- a/lib/replace/libreplace_network.m4 +++ b/lib/replace/libreplace_network.m4 @@ -173,7 +173,7 @@ LIBS="${LIBREPLACE_NETWORK_LIBS}" libreplace_SAVE_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$libreplacedir" -AC_CHECK_FUNCS(socketpair,[],[LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} socketpair.o"]) +AC_CHECK_FUNCS(socketpair,[],[LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} $libreplacedir/socketpair.o"]) AC_CACHE_CHECK([for broken inet_ntoa],libreplace_cv_REPLACE_INET_NTOA,[ AC_TRY_RUN([ @@ -193,14 +193,14 @@ exit(1);}], AC_CHECK_FUNCS(inet_ntoa,[],[libreplace_cv_REPLACE_INET_NTOA=yes]) if test x"$libreplace_cv_REPLACE_INET_NTOA" = x"yes"; then AC_DEFINE(REPLACE_INET_NTOA,1,[Whether inet_ntoa should be replaced]) - LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} inet_ntoa.o" + LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} $libreplacedir/inet_ntoa.o" fi -AC_CHECK_FUNCS(inet_aton,[],[LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} inet_aton.o"]) +AC_CHECK_FUNCS(inet_aton,[],[LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} $libreplacedir/inet_aton.o"]) -AC_CHECK_FUNCS(inet_ntop,[],[LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} inet_ntop.o"]) +AC_CHECK_FUNCS(inet_ntop,[],[LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} $libreplacedir/inet_ntop.o"]) -AC_CHECK_FUNCS(inet_pton,[],[LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} inet_pton.o"]) +AC_CHECK_FUNCS(inet_pton,[],[LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} $libreplacedir/inet_pton.o"]) dnl test for getaddrinfo/getnameinfo AC_CACHE_CHECK([for getaddrinfo],libreplace_cv_HAVE_GETADDRINFO,[ @@ -232,7 +232,7 @@ if test x"$libreplace_cv_HAVE_GETADDRINFO" = x"yes"; then AC_DEFINE(HAVE_FREEADDRINFO,1,[Whether the system has freeaddrinfo]) AC_DEFINE(HAVE_GAI_STRERROR,1,[Whether the system has gai_strerror]) else - LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} getaddrinfo.o" + LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} $libreplacedir/getaddrinfo.o" fi AC_CHECK_HEADERS([ifaddrs.h]) @@ -287,7 +287,7 @@ AC_TRY_RUN([ if test x"$libreplace_cv_HAVE_IFACE_GETIFADDRS" = x"yes"; then iface=yes;AC_DEFINE(HAVE_IFACE_GETIFADDRS,1,[Whether iface getifaddrs is available]) else - LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} getifaddrs.o" + LIBREPLACE_NETWORK_OBJS="${LIBREPLACE_NETWORK_OBJS} $libreplacedir/getifaddrs.o" fi @@ -350,6 +350,50 @@ if test x"$libreplace_cv_HAVE_IFACE_IFREQ" = x"yes"; then fi fi +dnl Some old Linux systems have broken header files and +dnl miss the IPV6_V6ONLY define in netinet/in.h, +dnl but have it in linux/in6.h. +dnl We can't include both files so we just check if the value +dnl if defined and do the replacement in system/network.h +AC_CACHE_CHECK([for IPV6_V6ONLY support],libreplace_cv_HAVE_IPV6_V6ONLY,[ + AC_TRY_COMPILE([ +#include <stdlib.h> /* for NULL */ +#include <sys/socket.h> +#include <sys/types.h> +#include <netdb.h> +#include <netinet/in.h> + ], + [ +#ifndef IPV6_V6ONLY +#error no IPV6_V6ONLY +#endif + ],[ + libreplace_cv_HAVE_IPV6_V6ONLY=yes + ],[ + libreplace_cv_HAVE_IPV6_V6ONLY=no + ]) +]) +if test x"$libreplace_cv_HAVE_IPV6_V6ONLY" != x"yes"; then + dnl test for IPV6_V6ONLY + AC_CACHE_CHECK([for IPV6_V6ONLY in linux/in6.h],libreplace_cv_HAVE_LINUX_IPV6_V6ONLY_26,[ + AC_TRY_COMPILE([ + #include <linux/in6.h> + ], + [ + #if (IPV6_V6ONLY != 26) + #error no linux IPV6_V6ONLY + #endif + ],[ + libreplace_cv_HAVE_LINUX_IPV6_V6ONLY_26=yes + ],[ + libreplace_cv_HAVE_LINUX_IPV6_V6ONLY_26=no + ]) + ]) + if test x"$libreplace_cv_HAVE_LINUX_IPV6_V6ONLY_26" = x"yes"; then + AC_DEFINE(HAVE_LINUX_IPV6_V6ONLY_26,1,[Whether the system has IPV6_V6ONLY in linux/in6.h]) + fi +fi + dnl test for ipv6 AC_CACHE_CHECK([for ipv6 support],libreplace_cv_HAVE_IPV6,[ AC_TRY_LINK([ @@ -370,6 +414,14 @@ if (ret != 0) { const char *es = gai_strerror(ret); } freeaddrinfo(ai); +{ + int val = 1; + #ifdef HAVE_LINUX_IPV6_V6ONLY_26 + #define IPV6_V6ONLY 26 + #endif + ret = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *)&val, sizeof(val)); +} ],[ libreplace_cv_HAVE_IPV6=yes ],[ diff --git a/lib/replace/repdir.m4 b/lib/replace/repdir.m4 index fb3f414c95..682ab44c56 100644 --- a/lib/replace/repdir.m4 +++ b/lib/replace/repdir.m4 @@ -45,7 +45,7 @@ fi if test x"$libreplace_cv_READDIR_GETDIRENTRIES" = x"yes"; then AC_DEFINE(REPLACE_READDIR,1,[replace readdir]) AC_DEFINE(REPLACE_READDIR_GETDIRENTRIES,1,[replace readdir using getdirentries()]) - LIBREPLACEOBJ="${LIBREPLACEOBJ} repdir_getdirentries.o" + LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/repdir_getdirentries.o" libreplace_cv_READDIR_NEEDED=no fi @@ -68,7 +68,7 @@ fi if test x"$libreplace_cv_READDIR_GETDENTS" = x"yes"; then AC_DEFINE(REPLACE_READDIR,1,[replace readdir]) AC_DEFINE(REPLACE_READDIR_GETDENTS,1,[replace readdir using getdents()]) - LIBREPLACEOBJ="${LIBREPLACEOBJ} repdir_getdents.o" + LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/repdir_getdents.o" libreplace_cv_READDIR_NEEDED=no fi diff --git a/lib/replace/replace.c b/lib/replace/replace.c index 78c688d50c..fc15717349 100644 --- a/lib/replace/replace.c +++ b/lib/replace/replace.c @@ -31,6 +31,10 @@ #include "system/locale.h" #include "system/wait.h" +#ifdef _WIN32 +#define mkdir(d,m) _mkdir(d) +#endif + void replace_dummy(void); void replace_dummy(void) {} @@ -355,7 +359,7 @@ char *rep_strndup(const char *s, size_t n) } #endif -#ifndef HAVE_WAITPID +#if !defined(HAVE_WAITPID) && defined(HAVE_WAIT4) int rep_waitpid(pid_t pid,int *status,int options) { return wait4(pid, status, options, NULL); @@ -368,7 +372,8 @@ int rep_seteuid(uid_t euid) #ifdef HAVE_SETRESUID return setresuid(-1, euid, -1); #else -# error "You need a seteuid function" + errno = ENOSYS; + return -1; #endif } #endif @@ -379,7 +384,8 @@ int rep_setegid(gid_t egid) #ifdef HAVE_SETRESGID return setresgid(-1, egid, -1); #else -# error "You need a setegid function" + errno = ENOSYS; + return -1; #endif } #endif diff --git a/lib/replace/replace.h b/lib/replace/replace.h index c5b8676acf..6424d10c0f 100644 --- a/lib/replace/replace.h +++ b/lib/replace/replace.h @@ -258,6 +258,10 @@ char *rep_realpath(const char *path, char *resolved_path); int rep_lchown(const char *fname,uid_t uid,gid_t gid); #endif +#ifdef HAVE_UNIX_H +#include <unix.h> +#endif + #ifndef HAVE_SETLINEBUF #define setlinebuf rep_setlinebuf void rep_setlinebuf(FILE *); @@ -535,6 +539,18 @@ typedef int bool; #endif #endif +#if !defined(HAVE_INTPTR_T) +typedef long long intptr_t ; +#endif + +#if !defined(HAVE_UINTPTR_T) +typedef unsigned long long uintptr_t ; +#endif + +#if !defined(HAVE_PTRDIFF_T) +typedef unsigned long long ptrdiff_t ; +#endif + /* * to prevent <rpcsvc/yp_prot.h> from doing a redefine of 'bool' * @@ -688,4 +704,23 @@ char *ufc_crypt(const char *key, const char *salt); #endif #endif +/* these macros gain us a few percent of speed on gcc */ +#if (__GNUC__ >= 3) +/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 + as its first argument */ +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif +#else +#ifndef likely +#define likely(x) (x) +#endif +#ifndef unlikely +#define unlikely(x) (x) +#endif +#endif + #endif /* _LIBREPLACE_REPLACE_H */ diff --git a/lib/replace/samba.m4 b/lib/replace/samba.m4 index ccb6f2e20d..b7582202f1 100644 --- a/lib/replace/samba.m4 +++ b/lib/replace/samba.m4 @@ -17,10 +17,7 @@ LIBREPLACE_DIR=`echo ${libreplacedir} |sed -e 's/^\.\///g'` # build directory. LIBREPLACE_DIR=`echo ${LIBREPLACE_DIR} | sed -e "s|^$srcdir/||g"` -LIBREPLACE_OBJS="" -for obj in ${LIBREPLACEOBJ}; do - LIBREPLACE_OBJS="${LIBREPLACE_OBJS} ${LIBREPLACE_DIR}/${obj}" -done +LIBREPLACE_OBJS="${LIBREPLACEOBJ}" SMB_SUBSYSTEM(LIBREPLACE, [${LIBREPLACE_OBJS}], @@ -33,3 +30,5 @@ SMB_SUBSYSTEM(LIBREPLACE_HOSTCC, [${LIBREPLACE_HOSTCC_OBJS}], [], [-Ilib/replace]) + +AC_CHECK_FUNCS([syslog],,[AC_MSG_ERROR([Required function not found])]) diff --git a/lib/replace/snprintf.c b/lib/replace/snprintf.c index c54d721ce5..bca774263e 100644 --- a/lib/replace/snprintf.c +++ b/lib/replace/snprintf.c @@ -504,6 +504,7 @@ static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in break; case 'p': cnk->type = CNK_PTR; + cnk->flags |= DP_F_UNSIGNED; break; case 'n': cnk->type = CNK_NUM; diff --git a/lib/replace/strptime.m4 b/lib/replace/strptime.m4 index da22fc5a97..b1a56b4aab 100644 --- a/lib/replace/strptime.m4 +++ b/lib/replace/strptime.m4 @@ -9,5 +9,5 @@ AC_CACHE_CHECK([whether strptime is available and works],libreplace_cv_STRPTIME_ ]) if test x"$libreplace_cv_STRPTIME_OK" != x"yes"; then AC_DEFINE(REPLACE_STRPTIME,1,[Whether strptime should be replaced]) - LIBREPLACEOBJ="${LIBREPLACEOBJ} strptime.o" + LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/strptime.o" fi diff --git a/lib/replace/system/config.m4 b/lib/replace/system/config.m4 index 5c9b53d5c5..39c2f58283 100644 --- a/lib/replace/system/config.m4 +++ b/lib/replace/system/config.m4 @@ -116,9 +116,10 @@ AC_VERIFY_C_PROTOTYPE([struct group *getgrent_r(struct group *src, char *buf, si #include <unistd.h> #include <grp.h> ]) +AC_CHECK_FUNCS(getgrouplist) # locale -AC_CHECK_HEADERS(ctype.h locale.h) +AC_CHECK_HEADERS(ctype.h locale.h langinfo.h) # glob AC_CHECK_HEADERS(fnmatch.h) diff --git a/lib/replace/system/filesys.h b/lib/replace/system/filesys.h index 4bf1f64865..22e3d23f3e 100644 --- a/lib/replace/system/filesys.h +++ b/lib/replace/system/filesys.h @@ -1,16 +1,16 @@ #ifndef _system_filesys_h #define _system_filesys_h -/* +/* Unix SMB/CIFS implementation. filesystem system include wrappers Copyright (C) Andrew Tridgell 2004 - + ** NOTE! The following LGPL license applies to the replace ** library. This does NOT imply that all of Samba is released ** under the LGPL - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -53,23 +53,23 @@ #include <acl/libacl.h> #endif -#ifdef HAVE_SYS_FS_S5PARAM_H +#ifdef HAVE_SYS_FS_S5PARAM_H #include <sys/fs/s5param.h> #endif #if defined (HAVE_SYS_FILSYS_H) && !defined (_CRAY) -#include <sys/filsys.h> +#include <sys/filsys.h> #endif #ifdef HAVE_SYS_STATFS_H # include <sys/statfs.h> #endif -#ifdef HAVE_DUSTAT_H +#ifdef HAVE_DUSTAT_H #include <sys/dustat.h> #endif -#ifdef HAVE_SYS_STATVFS_H +#ifdef HAVE_SYS_STATVFS_H #include <sys/statvfs.h> #endif @@ -123,7 +123,7 @@ #endif /* Some POSIX definitions for those without */ - + #ifndef S_IFDIR #define S_IFDIR 0x4000 #endif @@ -179,4 +179,8 @@ #define SEEK_SET 0 #endif +#ifdef _WIN32 +#define mkdir(d,m) _mkdir(d) +#endif + #endif diff --git a/lib/replace/system/kerberos.h b/lib/replace/system/kerberos.h index 2981024bee..a1685ad333 100644 --- a/lib/replace/system/kerberos.h +++ b/lib/replace/system/kerberos.h @@ -59,7 +59,9 @@ /* Define to 1 if you have the `krb5_free_data_contents' function. */ #define HAVE_KRB5_FREE_DATA_CONTENTS 1 /* Define to 1 if you have the `krb5_free_error_string' function. */ -#define HAVE_KRB5_FREE_ERROR_STRING 1 +/* #undef HAVE_KRB5_FREE_ERROR_STRING */ +/* Define to 1 if you have the `krb5_free_error_message' function. */ +#define HAVE_KRB5_FREE_ERROR_MESSAGE 1 /* Define to 1 if you have the `krb5_free_keytab_entry_contents' function. */ /* #undef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS */ /* Define to 1 if you have the `krb5_free_ktypes' function. */ @@ -70,6 +72,8 @@ #define HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES 1 /* Define to 1 if you have the `krb5_get_error_string' function. */ #define HAVE_KRB5_GET_ERROR_STRING 1 +/* Define to 1 if you have the `krb5_get_error_message' function. */ +#define HAVE_KRB5_GET_ERROR_MESSAGE 1 /* Define to 1 if you have the `krb5_get_permitted_enctypes' function. */ /* #undef HAVE_KRB5_GET_PERMITTED_ENCTYPES */ /* Define to 1 if you have the `krb5_get_pw_salt' function. */ diff --git a/lib/replace/system/locale.h b/lib/replace/system/locale.h index e73a9bb274..504a3bb470 100644 --- a/lib/replace/system/locale.h +++ b/lib/replace/system/locale.h @@ -35,4 +35,8 @@ #include <locale.h> #endif +#ifdef HAVE_LANGINFO_H +#include <langinfo.h> +#endif + #endif diff --git a/lib/replace/system/network.h b/lib/replace/system/network.h index 4fe1084c1a..93d533c9b9 100644 --- a/lib/replace/system/network.h +++ b/lib/replace/system/network.h @@ -321,6 +321,22 @@ typedef unsigned short int sa_family_t; #endif #endif +#ifndef IOV_MAX +# ifdef UIO_MAXIOV +# define IOV_MAX UIO_MAXIOV +# else +# ifdef __sgi + /* + * IRIX 6.5 has sysconf(_SC_IOV_MAX) + * which might return 512 or bigger + */ +# define IOV_MAX 512 +# else +# error IOV_MAX and UIO_MAXIOV undefined +# endif +# endif +#endif + #ifndef HAVE_STRUCT_ADDRINFO #define HAVE_STRUCT_ADDRINFO struct addrinfo { @@ -344,6 +360,13 @@ struct addrinfo { #define ifr_netmask ifr_addr #endif +/* Some old Linux systems have broken header files */ +#ifdef HAVE_IPV6 +#ifdef HAVE_LINUX_IPV6_V6ONLY_26 +#define IPV6_V6ONLY 26 +#endif /* HAVE_LINUX_IPV6_V6ONLY_26 */ +#endif /* HAVE_IPV6 */ + #ifdef SOCKET_WRAPPER #ifndef SOCKET_WRAPPER_DISABLE #ifndef SOCKET_WRAPPER_NOT_REPLACE diff --git a/lib/replace/system/passwd.h b/lib/replace/system/passwd.h index 9d7de34bce..b41608c551 100644 --- a/lib/replace/system/passwd.h +++ b/lib/replace/system/passwd.h @@ -1,17 +1,17 @@ #ifndef _system_passwd_h #define _system_passwd_h -/* +/* Unix SMB/CIFS implementation. passwd system include wrappers Copyright (C) Andrew Tridgell 2004 - + ** NOTE! The following LGPL license applies to the replace ** library. This does NOT imply that all of Samba is released ** under the LGPL - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -80,7 +80,7 @@ char *rep_getpass(const char *prompt); #define NGROUPS_MAX 32 /* Guess... */ #endif -/* what is the longest significant password available on your system? +/* what is the longest significant password available on your system? Knowing this speeds up password searches a lot */ #ifndef PASSWORD_LENGTH #define PASSWORD_LENGTH 8 diff --git a/lib/replace/system/wait.h b/lib/replace/system/wait.h index 5784b1ae92..79583ad2ab 100644 --- a/lib/replace/system/wait.h +++ b/lib/replace/system/wait.h @@ -52,4 +52,8 @@ typedef int sig_atomic_t; #endif +#if !defined(HAVE_WAITPID) && defined(HAVE_WAIT4) +int rep_waitpid(pid_t pid,int *status,int options) +#endif + #endif diff --git a/lib/replace/test/os2_delete.c b/lib/replace/test/os2_delete.c index 44efeea08a..8b52837018 100644 --- a/lib/replace/test/os2_delete.c +++ b/lib/replace/test/os2_delete.c @@ -27,6 +27,10 @@ static int test_readdir_os2_delete_ret; #define MIN(a,b) ((a)<(b)?(a):(b)) #endif +#ifdef _WIN32 +#define mkdir(d,m) _mkdir(d) +#endif + static void cleanup(void) { /* I'm a lazy bastard */ diff --git a/lib/replace/timegm.m4 b/lib/replace/timegm.m4 index 59f3ae0521..9b76d0c727 100644 --- a/lib/replace/timegm.m4 +++ b/lib/replace/timegm.m4 @@ -1 +1 @@ -AC_CHECK_FUNCS(timegm,[],[LIBREPLACEOBJ="${LIBREPLACEOBJ} timegm.o"]) +AC_CHECK_FUNCS(timegm,[],[LIBREPLACEOBJ="${LIBREPLACEOBJ} $libreplacedir/timegm.o"]) diff --git a/lib/socket_wrapper/socket_wrapper.c b/lib/socket_wrapper/socket_wrapper.c index cdfdceef21..9d732ee652 100644 --- a/lib/socket_wrapper/socket_wrapper.c +++ b/lib/socket_wrapper/socket_wrapper.c @@ -118,8 +118,10 @@ #define real_setsockopt setsockopt #define real_recvfrom recvfrom #define real_sendto sendto +#define real_sendmsg sendmsg #define real_ioctl ioctl #define real_recv recv +#define real_read read #define real_send send #define real_readv readv #define real_writev writev @@ -218,6 +220,7 @@ struct socket_info int bcast; int is_server; int connected; + int defer_connect; char *path; char *tmp_path; @@ -346,7 +349,7 @@ static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *i if (bcast) *bcast = 0; - switch (si->family) { + switch (inaddr->sa_family) { case AF_INET: { const struct sockaddr_in *in = (const struct sockaddr_in *)inaddr; @@ -667,7 +670,7 @@ enum swrap_packet_type { SWRAP_SEND_RST, SWRAP_CLOSE_SEND, SWRAP_CLOSE_RECV, - SWRAP_CLOSE_ACK + SWRAP_CLOSE_ACK, }; struct swrap_file_hdr { @@ -1382,6 +1385,13 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) { struct socket_info *si; int fd; + int real_type = type; +#ifdef SOCK_CLOEXEC + real_type &= ~SOCK_CLOEXEC; +#endif +#ifdef SOCK_NONBLOCK + real_type &= ~SOCK_NONBLOCK; +#endif if (!socket_wrapper_dir()) { return real_socket(family, type, protocol); @@ -1400,7 +1410,7 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) return -1; } - switch (type) { + switch (real_type) { case SOCK_STREAM: break; case SOCK_DGRAM: @@ -1414,12 +1424,12 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) case 0: break; case 6: - if (type == SOCK_STREAM) { + if (real_type == SOCK_STREAM) { break; } /*fall through*/ case 17: - if (type == SOCK_DGRAM) { + if (real_type == SOCK_DGRAM) { break; } /*fall through*/ @@ -1428,6 +1438,8 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) return -1; } + /* We must call real_socket with type, from the caller, not the version we removed + SOCK_CLOEXEC and SOCK_NONBLOCK from */ fd = real_socket(AF_UNIX, type, 0); if (fd == -1) return -1; @@ -1435,7 +1447,10 @@ _PUBLIC_ int swrap_socket(int family, int type, int protocol) si = (struct socket_info *)calloc(1, sizeof(struct socket_info)); si->family = family; - si->type = type; + + /* however, the rest of the socket_wrapper code expects just + * the type, not the flags */ + si->type = real_type; si->protocol = protocol; si->fd = fd; @@ -1688,10 +1703,15 @@ _PUBLIC_ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t ad ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr, 0, NULL); if (ret == -1) return -1; - swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_SEND, NULL, 0); + if (si->type == SOCK_DGRAM) { + si->defer_connect = 1; + ret = 0; + } else { + swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_SEND, NULL, 0); - ret = real_connect(s, (struct sockaddr *)&un_addr, - sizeof(struct sockaddr_un)); + ret = real_connect(s, (struct sockaddr *)&un_addr, + sizeof(struct sockaddr_un)); + } /* to give better errors */ if (ret == -1 && errno == ENOENT) { @@ -1819,6 +1839,10 @@ _PUBLIC_ int swrap_setsockopt(int s, int level, int optname, const void *o switch (si->family) { case AF_INET: return 0; +#ifdef HAVE_IPV6 + case AF_INET6: + return 0; +#endif default: errno = ENOPROTOOPT; return -1; @@ -1843,7 +1867,12 @@ _PUBLIC_ ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct fromlen = &ss_len; } - len = MIN(len, 1500); + if (si->type == SOCK_STREAM) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + len = MIN(len, 1500); + } /* irix 6.4 forgets to null terminate the sun_path string :-( */ memset(&un_addr, 0, sizeof(un_addr)); @@ -1883,10 +1912,13 @@ _PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, con tolen = si->peername_len; } - len = MIN(len, 1500); - switch (si->type) { case SOCK_STREAM: + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + len = MIN(len, 1500); + ret = real_send(s, buf, len, flags); break; case SOCK_DGRAM: @@ -1919,7 +1951,22 @@ _PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, con return len; } - + + if (si->defer_connect) { + ret = real_connect(s, (struct sockaddr *)&un_addr, + sizeof(un_addr)); + + /* to give better errors */ + if (ret == -1 && errno == ENOENT) { + errno = EHOSTUNREACH; + } + + if (ret == -1) { + return ret; + } + si->defer_connect = 0; + } + ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr)); break; default: @@ -1978,7 +2025,12 @@ _PUBLIC_ ssize_t swrap_recv(int s, void *buf, size_t len, int flags) return real_recv(s, buf, len, flags); } - len = MIN(len, 1500); + if (si->type == SOCK_STREAM) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + len = MIN(len, 1500); + } ret = real_recv(s, buf, len, flags); if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) { @@ -1992,6 +2044,34 @@ _PUBLIC_ ssize_t swrap_recv(int s, void *buf, size_t len, int flags) return ret; } +_PUBLIC_ ssize_t swrap_read(int s, void *buf, size_t len) +{ + int ret; + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_read(s, buf, len); + } + + if (si->type == SOCK_STREAM) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + len = MIN(len, 1500); + } + + ret = real_read(s, buf, len); + if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) { + swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0); + } else if (ret == 0) { /* END OF FILE */ + swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0); + } else if (ret > 0) { + swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret); + } + + return ret; +} + _PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags) { @@ -2002,7 +2082,39 @@ _PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags) return real_send(s, buf, len, flags); } - len = MIN(len, 1500); + if (si->type == SOCK_STREAM) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ + len = MIN(len, 1500); + } + + if (si->defer_connect) { + struct sockaddr_un un_addr; + int bcast = 0; + + if (si->bound == 0) { + ret = swrap_auto_bind(si, si->family); + if (ret == -1) return -1; + } + + ret = sockaddr_convert_to_un(si, si->peername, si->peername_len, + &un_addr, 0, &bcast); + if (ret == -1) return -1; + + ret = real_connect(s, (struct sockaddr *)&un_addr, + sizeof(un_addr)); + + /* to give better errors */ + if (ret == -1 && errno == ENOENT) { + errno = EHOSTUNREACH; + } + + if (ret == -1) { + return ret; + } + si->defer_connect = 0; + } ret = real_send(s, buf, len, flags); @@ -2016,6 +2128,76 @@ _PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags) return ret; } +_PUBLIC_ ssize_t swrap_sendmsg(int s, const struct msghdr *msg, int flags) +{ + int ret; + uint8_t *buf; + off_t ofs = 0; + size_t i; + size_t remain; + + struct socket_info *si = find_socket_info(s); + + if (!si) { + return real_sendmsg(s, msg, flags); + } + + if (si->defer_connect) { + struct sockaddr_un un_addr; + int bcast = 0; + + if (si->bound == 0) { + ret = swrap_auto_bind(si, si->family); + if (ret == -1) return -1; + } + + ret = sockaddr_convert_to_un(si, si->peername, si->peername_len, + &un_addr, 0, &bcast); + if (ret == -1) return -1; + + ret = real_connect(s, (struct sockaddr *)&un_addr, + sizeof(un_addr)); + + /* to give better errors */ + if (ret == -1 && errno == ENOENT) { + errno = EHOSTUNREACH; + } + + if (ret == -1) { + return ret; + } + si->defer_connect = 0; + } + + ret = real_sendmsg(s, msg, flags); + remain = ret; + + /* we capture it as one single packet */ + buf = (uint8_t *)malloc(ret); + if (!buf) { + /* we just not capture the packet */ + errno = 0; + return ret; + } + + for (i=0; i < msg->msg_iovlen; i++) { + size_t this_time = MIN(remain, msg->msg_iov[i].iov_len); + memcpy(buf + ofs, + msg->msg_iov[i].iov_base, + this_time); + ofs += this_time; + remain -= this_time; + } + + swrap_dump_packet(si, NULL, SWRAP_SEND, buf, ret); + free(buf); + if (ret == -1) { + swrap_dump_packet(si, NULL, SWRAP_SEND_RST, NULL, 0); + } + + return ret; +} + int swrap_readv(int s, const struct iovec *vector, size_t count) { int ret; @@ -2026,10 +2208,12 @@ int swrap_readv(int s, const struct iovec *vector, size_t count) return real_readv(s, vector, count); } - /* we read 1500 bytes as maximum */ - if (count > 0) { + if (si->type == SOCK_STREAM && count > 0) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ size_t i, len = 0; - + for (i=0; i < count; i++) { size_t nlen; nlen = len + vector[i].iov_len; @@ -2091,8 +2275,10 @@ int swrap_writev(int s, const struct iovec *vector, size_t count) return real_writev(s, vector, count); } - /* we write 1500 bytes as maximum */ - if (count > 0) { + if (si->type == SOCK_STREAM && count > 0) { + /* cut down to 1500 byte packets for stream sockets, + * which makes it easier to format PCAP capture files + * (as the caller will simply continue from here) */ size_t i, len = 0; for (i=0; i < count; i++) { diff --git a/lib/socket_wrapper/socket_wrapper.h b/lib/socket_wrapper/socket_wrapper.h index b2d44769ff..472aa19ced 100644 --- a/lib/socket_wrapper/socket_wrapper.h +++ b/lib/socket_wrapper/socket_wrapper.h @@ -49,8 +49,10 @@ int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *opt int swrap_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen); ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); +ssize_t swrap_sendmsg(int s, const struct msghdr *msg, int flags); int swrap_ioctl(int s, int req, void *ptr); ssize_t swrap_recv(int s, void *buf, size_t len, int flags); +ssize_t swrap_read(int s, void *buf, size_t len); ssize_t swrap_send(int s, const void *buf, size_t len, int flags); int swrap_readv(int s, const struct iovec *vector, size_t count); int swrap_writev(int s, const struct iovec *vector, size_t count); @@ -108,6 +110,11 @@ int swrap_close(int); #endif #define sendto(s,buf,len,flags,to,tolen) swrap_sendto(s,buf,len,flags,to,tolen) +#ifdef sendmsg +#undef sendmsg +#endif +#define sendmsg(s,msg,flags) swrap_sendmsg(s,msg,flags) + #ifdef ioctl #undef ioctl #endif @@ -118,6 +125,11 @@ int swrap_close(int); #endif #define recv(s,buf,len,flags) swrap_recv(s,buf,len,flags) +#ifdef read +#undef read +#endif +#define read(s,buf,len) swrap_read(s,buf,len) + #ifdef send #undef send #endif diff --git a/lib/subunit/harness2subunit.pl b/lib/subunit/tap2subunit index 45f515540b..9e335168f5 100755 --- a/lib/subunit/harness2subunit.pl +++ b/lib/subunit/tap2subunit @@ -33,4 +33,3 @@ while(<STDIN>) { } } exit $error; - diff --git a/lib/talloc/Makefile.in b/lib/talloc/Makefile.in index c28693e2db..94f031a4bb 100644 --- a/lib/talloc/Makefile.in +++ b/lib/talloc/Makefile.in @@ -16,7 +16,10 @@ CC = @CC@ CFLAGS = @CFLAGS@ -DHAVE_CONFIG_H= -I. -I@srcdir@ EXTRA_TARGETS = @DOC_TARGET@ PICFLAG = @PICFLAG@ -PACKAGE_VERSION = @PACKAGE_VERSION@ +TALLOC_VERSION = @TALLOC_VERSION@ +TALLOC_VERSION_MAJOR = @TALLOC_VERSION_MAJOR@ +TALLOC_VERSION_MINOR = @TALLOC_VERSION_MINOR@ +TALLOC_VERSION_RELEASE = @TALLOC_VERSION_RELEASE@ SHLIBEXT = @SHLIBEXT@ SHLD = @SHLD@ SHLD_FLAGS = @SHLD_FLAGS@ @@ -24,13 +27,19 @@ tallocdir = @tallocdir@ LIBOBJ = $(TALLOC_OBJ) @LIBREPLACEOBJ@ +SONAMEFLAG = @SONAMEFLAG@ +VERSIONSCRIPT = @VERSIONSCRIPT@ +EXPORTSFILE = @EXPORTSFILE@ + all:: showflags $(EXTRA_TARGETS) include $(tallocdir)/rules.mk include $(tallocdir)/talloc.mk +@TALLOC_COMPAT1_MK@ + $(TALLOC_SOLIB): $(LIBOBJ) - $(SHLD) $(SHLD_FLAGS) -o $@ $(LIBOBJ) @SONAMEFLAG@$(TALLOC_SONAME) + $(SHLD) $(SHLD_FLAGS) -o $@ $(LIBOBJ) $(VERSIONSCRIPT) $(EXPORTSFILE) $(SONAMEFLAG)$(TALLOC_SONAME) shared-build: all ${INSTALLCMD} -d $(sharedbuilddir)/lib diff --git a/lib/talloc/compat/talloc_compat1.c b/lib/talloc/compat/talloc_compat1.c new file mode 100644 index 0000000000..519e8c3a83 --- /dev/null +++ b/lib/talloc/compat/talloc_compat1.c @@ -0,0 +1,51 @@ +/* + Samba trivial allocation library - compat functions + + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * This file contains only function to build a + * compat talloc.so.1 library on top of talloc.so.2 + */ + +#include "replace.h" +#include "talloc.h" + +void *_talloc_reference(const void *context, const void *ptr); +void *_talloc_reference(const void *context, const void *ptr) { + return _talloc_reference_loc(context, ptr, + "Called from talloc compat1 " + "_talloc_reference"); +} + +void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_steal(const void *new_ctx, const void *ptr) +{ + return talloc_reparent(talloc_parent(ptr), new_ctx, ptr); +} + +#undef talloc_free +int talloc_free(void *ptr); +int talloc_free(void *ptr) +{ + return talloc_unlink(talloc_parent(ptr), ptr); +} + diff --git a/lib/talloc/compat/talloc_compat1.m4 b/lib/talloc/compat/talloc_compat1.m4 new file mode 100644 index 0000000000..2ec530e079 --- /dev/null +++ b/lib/talloc/compat/talloc_compat1.m4 @@ -0,0 +1,14 @@ +TALLOC_COMPAT1_MK="" +AC_SUBST(TALLOC_COMPAT1_MK) + +AC_ARG_ENABLE(talloc-compat1, + [AS_HELP_STRING([--enable-talloc-compat1], + [Build talloc 1.x.x compat library [default=no]])], + [ enable_talloc_compat1=$enableval ], + [ enable_talloc_compat1=no ] +) + +if test "x$enable_talloc_compat1" = x"yes"; then + TALLOC_COMPAT1_MK='include $(tallocdir)/compat/talloc_compat1.mk' +fi + diff --git a/lib/talloc/compat/talloc_compat1.mk b/lib/talloc/compat/talloc_compat1.mk new file mode 100644 index 0000000000..d1817f0f71 --- /dev/null +++ b/lib/talloc/compat/talloc_compat1.mk @@ -0,0 +1,21 @@ +talloccompatdir := $(tallocdir)/compat + +TALLOC_COMPAT1_VERSION_MAJOR = 1 +TALLOC_COMPAT1_OBJ = $(talloccompatdir)/talloc_compat1.o + +TALLOC_COMPAT1_SOLIB = libtalloc-compat1-$(TALLOC_VERSION).$(SHLIBEXT) +TALLOC_COMPAT1_SONAME = libtalloc.$(SHLIBEXT).$(TALLOC_COMPAT1_VERSION_MAJOR) + +$(TALLOC_COMPAT1_SOLIB): $(TALLOC_COMPAT1_OBJ) $(TALLOC_SOLIB) + $(SHLD) $(SHLD_FLAGS) -o $@ $(TALLOC_COMPAT1_OBJ) \ + $(TALLOC_SOLIB) $(SONAMEFLAG)$(TALLOC_COMPAT1_SONAME) + +all:: $(TALLOC_COMPAT1_SOLIB) + +install:: + ${INSTALLCMD} -d $(DESTDIR)$(libdir) + ${INSTALLCMD} -m 755 $(TALLOC_COMPAT1_SOLIB) $(DESTDIR)$(libdir) + +clean:: + rm -f $(TALLOC_COMPAT1_OBJ) $(TALLOC_COMPAT1_SOLIB) + diff --git a/lib/talloc/config.guess b/lib/talloc/config.guess index 4af85584ae..da83314608 100755 --- a/lib/talloc/config.guess +++ b/lib/talloc/config.guess @@ -808,7 +808,7 @@ EOF echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:[3456]*) - case ${UNAME_MACHINE} in + case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; @@ -984,7 +984,7 @@ EOF echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so diff --git a/lib/talloc/config.sub b/lib/talloc/config.sub index 80a785297e..a39437d015 100755 --- a/lib/talloc/config.sub +++ b/lib/talloc/config.sub @@ -1454,7 +1454,7 @@ case $basic_machine in os=-aout ;; c4x-* | tic4x-*) - os=-coff + os=-coff ;; # This must come before the *-dec entry. pdp10-*) diff --git a/lib/talloc/configure.ac b/lib/talloc/configure.ac index 00e8242d4e..c1b1d2e4a1 100644 --- a/lib/talloc/configure.ac +++ b/lib/talloc/configure.ac @@ -1,12 +1,40 @@ AC_PREREQ(2.50) -AC_INIT(talloc, 1.3.0) +AC_INIT(talloc, 2.0.1) AC_CONFIG_SRCDIR([talloc.c]) AC_SUBST(datarootdir) AC_CONFIG_HEADER(config.h) +TALLOC_VERSION=${PACKAGE_VERSION} +TALLOC_VERSION_MAJOR=`echo ${PACKAGE_VERSION} | cut -d '.' -f1` +TALLOC_VERSION_MINOR=`echo ${PACKAGE_VERSION} | cut -d '.' -f2` +TALLOC_VERSION_RELEASE=`echo ${PACKAGE_VERSION} | cut -d '.' -f3` + +AC_SUBST(TALLOC_VERSION) +AC_SUBST(TALLOC_VERSION_MAJOR) +AC_SUBST(TALLOC_VERSION_MINOR) +AC_SUBST(TALLOC_VERSION_RELEASE) + +AC_DEFINE_UNQUOTED(TALLOC_BUILD_VERSION_MAJOR, + [${TALLOC_VERSION_MAJOR}], + [talloc major version]) +AC_DEFINE_UNQUOTED(TALLOC_BUILD_VERSION_MINOR, + [${TALLOC_VERSION_MINOR}], + [talloc minor version]) +AC_DEFINE_UNQUOTED(TALLOC_BUILD_VERSION_RELEASE, + [${TALLOC_VERSION_RELEASE}], + [talloc release version]) + AC_LIBREPLACE_ALL_CHECKS +AC_LD_PICFLAG +AC_LD_SHLIBEXT +AC_LD_SONAMEFLAG +AC_LD_VERSIONSCRIPT +AC_LIBREPLACE_SHLD +AC_LIBREPLACE_SHLD_FLAGS + m4_include(libtalloc.m4) +m4_include(compat/talloc_compat1.m4) AC_PATH_PROG(XSLTPROC,xsltproc) DOC_TARGET="" @@ -15,12 +43,6 @@ if test -n "$XSLTPROC"; then fi AC_SUBST(DOC_TARGET) -AC_LD_PICFLAG -AC_LD_SHLIBEXT -AC_LD_SONAMEFLAG -AC_LIBREPLACE_SHLD -AC_LIBREPLACE_SHLD_FLAGS - m4_include(build_macros.m4) BUILD_WITH_SHARED_BUILD_DIR diff --git a/lib/talloc/libtalloc.m4 b/lib/talloc/libtalloc.m4 index e6830fbef6..4b22c8e41a 100644 --- a/lib/talloc/libtalloc.m4 +++ b/lib/talloc/libtalloc.m4 @@ -31,3 +31,8 @@ if test $ac_cv_sizeof_size_t -lt $ac_cv_sizeof_void_p; then AC_WARN([sizeof(void *) = $ac_cv_sizeof_void_p]) AC_ERROR([sizeof(size_t) < sizeof(void *)]) fi + +if test x"$VERSIONSCRIPT" != "x"; then + EXPORTSFILE=talloc.exports + AC_SUBST(EXPORTSFILE) +fi diff --git a/lib/talloc/pytalloc.c b/lib/talloc/pytalloc.c index 30da9ee5c2..c6decf33f1 100644 --- a/lib/talloc/pytalloc.c +++ b/lib/talloc/pytalloc.c @@ -35,7 +35,7 @@ void py_talloc_dealloc(PyObject* self) /** * Import an existing talloc pointer into a Python object. */ -PyObject *py_talloc_import_ex(PyTypeObject *py_type, TALLOC_CTX *mem_ctx, +PyObject *py_talloc_steal_ex(PyTypeObject *py_type, TALLOC_CTX *mem_ctx, void *ptr) { py_talloc_Object *ret = (py_talloc_Object *)py_type->tp_alloc(py_type, 0); @@ -43,6 +43,26 @@ PyObject *py_talloc_import_ex(PyTypeObject *py_type, TALLOC_CTX *mem_ctx, if (ret->talloc_ctx == NULL) { return NULL; } + if (talloc_steal(ret->talloc_ctx, mem_ctx) == NULL) { + return NULL; + } + ret->ptr = ptr; + return (PyObject *)ret; +} + + +/** + * Import an existing talloc pointer into a Python object, leaving the + * original parent, and creating a reference to the object in the python + * object + */ +PyObject *py_talloc_reference_ex(PyTypeObject *py_type, TALLOC_CTX *mem_ctx, void *ptr) +{ + py_talloc_Object *ret = (py_talloc_Object *)py_type->tp_alloc(py_type, 0); + ret->talloc_ctx = talloc_new(NULL); + if (ret->talloc_ctx == NULL) { + return NULL; + } if (talloc_reference(ret->talloc_ctx, mem_ctx) == NULL) { return NULL; } @@ -58,6 +78,16 @@ PyObject *py_talloc_default_repr(PyObject *obj) py_talloc_Object *talloc_obj = (py_talloc_Object *)obj; PyTypeObject *type = (PyTypeObject*)PyObject_Type(obj); - return PyString_FromFormat("<%s talloc object at 0x%x>", - type->tp_name, (intptr_t)talloc_obj->ptr); + return PyString_FromFormat("<%s talloc object at 0x%p>", + type->tp_name, talloc_obj->ptr); +} + +static void py_cobject_talloc_free(void *ptr) +{ + talloc_free(ptr); +} + +PyObject *PyCObject_FromTallocPtr(void *ptr) +{ + return PyCObject_FromVoidPtr(ptr, py_cobject_talloc_free); } diff --git a/lib/talloc/pytalloc.h b/lib/talloc/pytalloc.h index c5a1428b29..9b6587261c 100644 --- a/lib/talloc/pytalloc.h +++ b/lib/talloc/pytalloc.h @@ -42,12 +42,16 @@ void py_talloc_dealloc(PyObject* self); #define py_talloc_get_ptr(py_obj) (((py_talloc_Object *)py_obj)->ptr) #define py_talloc_get_mem_ctx(py_obj) ((py_talloc_Object *)py_obj)->talloc_ctx -PyObject *py_talloc_import_ex(PyTypeObject *py_type, TALLOC_CTX *mem_ctx, void *ptr); -#define py_talloc_import(py_type, talloc_ptr) py_talloc_import_ex(py_type, talloc_ptr, talloc_ptr) +PyObject *py_talloc_steal_ex(PyTypeObject *py_type, TALLOC_CTX *mem_ctx, void *ptr); +PyObject *py_talloc_reference_ex(PyTypeObject *py_type, TALLOC_CTX *mem_ctx, void *ptr); +#define py_talloc_steal(py_type, talloc_ptr) py_talloc_steal_ex(py_type, talloc_ptr, talloc_ptr) +#define py_talloc_reference(py_type, talloc_ptr) py_talloc_reference_ex(py_type, talloc_ptr, talloc_ptr) /* Sane default implementation of reprfunc. */ PyObject *py_talloc_default_repr(PyObject *py_obj); -#define py_talloc_new(type, typeobj) py_talloc_import(typeobj, talloc_zero(NULL, type)) +#define py_talloc_new(type, typeobj) py_talloc_steal(typeobj, talloc_zero(NULL, type)) + +PyObject *PyCObject_FromTallocPtr(void *); #endif /* _PY_TALLOC_H_ */ diff --git a/lib/talloc/rules.mk b/lib/talloc/rules.mk index 6cee126529..00c909ee2b 100644 --- a/lib/talloc/rules.mk +++ b/lib/talloc/rules.mk @@ -6,13 +6,13 @@ showflags:: @echo ' LIBS = $(LIBS)' .c.o: - $(CC) $(PICFLAG) -o $@ -c $< $(CFLAGS) + $(CC) $(PICFLAG) $(ABI_CHECK) -o $@ -c $< $(CFLAGS) .3.xml.3: - -test -z "$(XSLTPROC)" || $(XSLTPROC) --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< + -test -z "$(XSLTPROC)" || $(XSLTPROC) -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< .xml.html: - -test -z "$(XSLTPROC)" || $(XSLTPROC) --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< + -test -z "$(XSLTPROC)" || $(XSLTPROC) -o $@ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< distclean:: rm -f *~ */*~ diff --git a/lib/talloc/script/abi_checks.sh b/lib/talloc/script/abi_checks.sh new file mode 100755 index 0000000000..66c4e60e45 --- /dev/null +++ b/lib/talloc/script/abi_checks.sh @@ -0,0 +1,80 @@ +#!/bin/sh + +# +# abi_checks.sh - check for possible abi changes +# +# Copyright (C) 2009 Michael Adam <obnox@samba.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +# +# USAGE: abi_checks.sh LIBNAME header1 [header2 ...] +# +# This script creates symbol and signature lists from the provided header +# files with the aid of the mksyms.sh and mksigs.pl scripts (saved as +# $LIBNAME.exports.check and $LIBNAME.sigatures.check). It then compares +# the resulting files with the files $LIBNAME.exports and $LIBNME.signatures +# which it expects to find in the current directory. +# + +LANG=C; export LANG +LC_ALL=C; export LC_ALL +LC_COLLATE=C; export LC_COLLATE + +exit_status=0 +script=$0 +dir_name=$(dirname ${script}) + +if test x"$1" = "x" ; then + echo "USAGE: ${script} libname header [header ...]" + exit 1 +fi + +libname="$1" +shift + +if test x"$1" = "x" ; then + echo "USAGE: ${script} libname header [header ...]" + exit 1 +fi + +headers="$*" + +exports_file=${libname}.exports +exports_file_check=${exports_file}.check +signatures_file=${libname}.signatures +signatures_file_check=${signatures_file}.check + + +${dir_name}/mksyms.sh awk ${exports_file_check} ${headers} 2>&1 > /dev/null +cat ${headers} | ${dir_name}/mksigs.pl | sort| uniq > ${signatures_file_check} 2> /dev/null + +diff -u ${exports_file} ${exports_file_check} +if test "x$?" != "x0" ; then + echo "WARNING: possible ABI change detected in exports!" + let exit_status++ +else + echo "exports check: OK" +fi + +diff -u ${signatures_file} ${signatures_file_check} +if test "x$?" != "x0" ; then + echo "WARNING: possible ABI change detected in signatures!" + let exit_status++ +else + echo "signatures check: OK" +fi + +exit $exit_status diff --git a/lib/talloc/script/abi_checks_gcc.sh b/lib/talloc/script/abi_checks_gcc.sh new file mode 100755 index 0000000000..e3d3b042fa --- /dev/null +++ b/lib/talloc/script/abi_checks_gcc.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +make clean + +mkdir abi +ABI_CHECKS="-aux-info abi/\$@.X" +make ABI_CHECK="$ABI_CHECKS" CC="/usr/bin/gcc" + +for i in abi/*.X; do cat $i | grep 'talloc\.h'; done | sort | uniq | awk -F "extern " '{ print $2 }' | sort > abi/signatures + +cat > abi/exports << EOF +{ + global: +EOF +cat abi/signatures | awk -F '(' '{ print $1 }' | awk -F ' ' '{ print " "$NF";" }' | tr -d '*' | sort >> abi/exports +# need to manually add talloc free for backward ABI compat +echo ' talloc_free;' >> abi/exports +cat >> abi/exports << EOF + + local: *; +}; +EOF + +rm -fr abi/*.X + +diff -u talloc.signatures abi/signatures +if [ "$?" != "0" ]; then + echo "WARNING: Possible ABI Change!!" +fi + +diff -u talloc.exports abi/exports +if [ "$?" != "0" ]; then + echo "WARNING: Export file may be outdated!!" +fi diff --git a/lib/talloc/script/mksigs.pl b/lib/talloc/script/mksigs.pl new file mode 100755 index 0000000000..755cd79603 --- /dev/null +++ b/lib/talloc/script/mksigs.pl @@ -0,0 +1,183 @@ +#!/usr/bin/perl + +# mksigs.pl - extract signatures from C headers +# +# Copyright (C) Michael Adam 2009 +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# USAGE: cat $header_files | mksigs.pl > $signature_file +# +# The header files to parse are read from stdin. +# The output is in a form as produced by gcc with the -aux-info switch +# and printed to stdout. + +use strict; +use warnings; + +my $in_comment = 0; +my $extern_C_block = 0; + +while (my $LINE = <>) { + # find end of started multi-line-comment + if ($in_comment) { + if ($LINE =~ /^.*?\*\/(.*)$/) { + $LINE = $1; + $in_comment = 0; + } else { + # whole line within comment + next; + } + } + + # strip C++-style comments + $LINE =~ s/^(.*?)\/\/.*$/$1/; + + # strip in-line-comments: + while ($LINE =~ /\/\*.*?\*\//) { + $LINE =~ s/\/\*.*?\*\///; + } + + # find starts of multi-line-comments + if ($LINE =~ /^(.*)\/\*/) { + $in_comment = 1; + $LINE = $1; + } + + # skip empty lines + next if $LINE =~ /^\s*$/; + + # remove leading spaces + $LINE =~ s/^\s*(.*)$/$1/; + + # concatenate lines split with "\" (usually macro defines) + while ($LINE =~ /^(.*?)\s+\\$/) { + my $LINE2 = <>; + $LINE = $1; + $LINE2 =~ s/^\s*(.*)$/$1/; + $LINE .= " " . $LINE2; + } + + # remove all preprocessor directives + next if ($LINE =~ /^#/); + + if ($LINE =~ /^extern\s+"C"\s+\{/) { + $extern_C_block = 1; + next; + } + + if (($LINE =~ /^[^\{]*\}/) and $extern_C_block) { + $extern_C_block = 0; + next; + } + + $LINE =~ s/^extern\s//; + + # concatenate braces stretched over multiple lines + # (from structs or enums) + my $REST = $LINE; + my $braces = 0; + while (($REST =~ /[\{\}]/) or ($braces)) { + while ($REST =~ /[\{\}]/) { + # collect opening + while ($REST =~ /^[^\{\}]*\{(.*)$/) { + $braces++; + $REST = $1; + } + + # collect closing + while ($REST =~ /^[^\{\}]*\}(.*)$/) { + $braces--; + $REST = $1; + } + } + + # concatenate if not balanced + if ($braces) { + if (my $LINE2 = <>) { + $LINE2 =~ s/^\s*(.*)$/$1/; + chomp($LINE); + $LINE .= " " . $LINE2; + chomp $REST; + $REST .= " " . $LINE2; + } else { + print "ERROR: unbalanced braces ($braces)\n"; + last; + } + } + } + + # concetenate function prototypes that stretch over multiple lines + $REST = $LINE; + my $parenthesis = 0; + while (($REST =~ /[\(\)]/) or ($parenthesis)) { + while ($REST =~ /[\(\)]/) { + # collect opening + while ($REST =~ /^[^\(\)]*\((.*)$/) { + $parenthesis++; + $REST = $1; + } + + # collect closing + while ($REST =~ /^[^\(\)]*\)(.*)$/) { + $parenthesis--; + $REST = $1; + } + } + + # concatenate if not balanced + if ($parenthesis) { + if (my $LINE2 = <>) { + $LINE2 =~ s/^\s*(.*)$/$1/; + chomp($LINE); + $LINE .= " " . $LINE2; + chomp($REST); + $REST .= " " . $LINE2; + } else { + print "ERROR: unbalanced parantheses ($parenthesis)\n"; + last; + } + } + } + + next if ($LINE =~ /^typedef\s/); + next if ($LINE =~ /^enum\s+[^\{\(]+\s+\{/); + next if ($LINE =~ /^struct\s+[^\{\(]+\s+\{.*\}\s*;/); + next if ($LINE =~ /^struct\s+[a-zA-Z0-9_]+\s*;/); + + # remove trailing spaces + $LINE =~ s/(.*?)\s*$/$1/; + + $LINE =~ s/^(.*\))\s+PRINTF_ATTRIBUTE\([^\)]*\)(\s*[;,])/$1$2/; + $LINE =~ s/^(.*\))\s*[a-zA-Z0-9_]+\s*;$/$1;/; + + # remove parameter names - slightly too coarse probably + $LINE =~ s/([\s\(]\*?)[_0-9a-zA-Z]+\s*([,\)])/$1$2/g; + + # remedy (void) from last line + $LINE =~ s/\(\)/(void)/g; + + # normalize spaces + $LINE =~ s/\s*\)\s*/)/g; + $LINE =~ s/\s*\(\s*/ (/g; + $LINE =~ s/\s*,\s*/, /g; + + # normalize unsigned + $LINE =~ s/([\s,\(])unsigned([,\)])/$1unsigned int$2/g; + + # normalize bool + $LINE =~ s/(\b)bool(\b)/_Bool/g; + + print $LINE . "\n"; +} diff --git a/lib/talloc/script/mksyms.awk b/lib/talloc/script/mksyms.awk new file mode 100644 index 0000000000..8775faff3f --- /dev/null +++ b/lib/talloc/script/mksyms.awk @@ -0,0 +1,63 @@ +# +# mksyms.awk +# +# Extract symbols to export from C-header files. +# output in version-script format for linking shared libraries. +# +# Copyright (C) 2008 Micheal Adam <obnox@samba.org> +# +BEGIN { + inheader=0; +} + +END { +} + +{ + if (inheader) { + if (match($0,"[)][^()]*[;][ \t]*$")) { + inheader = 0; + } + next; + } +} + +/^static/ || /^[ \t]*typedef/ || !/^[a-zA-Z\_]/ { + next; +} + +/^extern[ \t]+[^()]+[;][ \t]*$/ { + gsub(/[^ \t]+[ \t]+/, ""); + sub(/[;][ \t]*$/, ""); + printf " %s;\n", $0; + next; +} + +# look for function headers: +{ + gotstart = 0; + if ($0 ~ /^[A-Za-z_][A-Za-z0-9_]+/) { + gotstart = 1; + } + if(!gotstart) { + next; + } +} + +/[_A-Za-z0-9]+[ \t]*[(].*[)][^()]*;[ \t]*$/ { + sub(/[(].*$/, ""); + gsub(/[^ \t]+[ \t]+/, ""); + gsub(/^[*]+/, ""); + printf " %s;\n",$0; + next; +} + +/[_A-Za-z0-9]+[ \t]*[(]/ { + inheader=1; + sub(/[(].*$/, ""); + gsub(/[^ \t]+[ \t]+/, ""); + gsub(/^[*]/, ""); + printf " %s;\n",$0; + next; +} + diff --git a/lib/talloc/script/mksyms.sh b/lib/talloc/script/mksyms.sh new file mode 100755 index 0000000000..089344f8f0 --- /dev/null +++ b/lib/talloc/script/mksyms.sh @@ -0,0 +1,62 @@ +#! /bin/sh + +# +# mksyms.sh +# +# Extract symbols to export from C-header files. +# output in version-script format for linking shared libraries. +# +# This is the shell wrapper for the mksyms.awk core script. +# +# Copyright (C) 2008 Micheal Adam <obnox@samba.org> +# + +LANG=C; export LANG +LC_ALL=C; export LC_ALL +LC_COLLATE=C; export LC_COLLATE + +if [ $# -lt 2 ] +then + echo "Usage: $0 awk output_file header_files" + exit 1 +fi + +awk="$1" +shift + +symsfile="$1" +shift +symsfile_tmp="$symsfile.$$.tmp~" + +proto_src="`echo $@ | tr ' ' '\n' | sort | uniq `" + +echo creating $symsfile + +mkdir -p `dirname $symsfile` + +#Write header +cat > $symsfile_tmp << EOF +# This file is autogenerated, please DO NOT EDIT +{ + global: +EOF + +#loop on each header +for i in $proto_src; do +${awk} -f `dirname $0`/mksyms.awk $i | sort >> $symsfile_tmp +done; + +#Write tail +cat >> $symsfile_tmp << EOF + + local: *; +}; +EOF + +if cmp -s $symsfile $symsfile_tmp 2>/dev/null +then + echo "$symsfile unchanged" + rm $symsfile_tmp +else + mv $symsfile_tmp $symsfile +fi diff --git a/lib/talloc/script/release-script.sh b/lib/talloc/script/release-script.sh new file mode 100755 index 0000000000..fd5c1eff5d --- /dev/null +++ b/lib/talloc/script/release-script.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +if [ "$1" = "" ]; then + echo "Please provide version string, eg: 1.2.0" + exit 1 +fi + +if [ ! -d "lib/talloc" ]; then + echo "Run this script from the samba base directory." + exit 1 +fi + +# Check exports and signatures are up to date +pushd lib/talloc +./script/abi_checks.sh talloc talloc.h +abicheck=$? +popd +if [ ! "$abicheck" = "0" ]; then + echo "ERROR: ABI Checks produced warnings!" + exit 1 +fi + +git clean -f -x -d lib/talloc +git clean -f -x -d lib/replace + +curbranch=`git branch |grep "^*" | tr -d "* "` + +version=$1 +strver=`echo ${version} | tr "." "-"` + +# Checkout the release tag +git branch -f talloc-release-script-${strver} talloc-${strver} +if [ ! "$?" = "0" ]; then + echo "Unable to checkout talloc-${strver} release" + exit 1 +fi + +git checkout talloc-release-script-${strver} + +# Test configure agrees with us +confver=`grep "^AC_INIT" lib/talloc/configure.ac | tr -d "AC_INIT(talloc, " | tr -d ")"` +if [ ! "$confver" = "$version" ]; then + echo "Wrong version, requested release for ${version}, found ${confver}" + exit 1 +fi + +# Now build tarball +cp -a lib/talloc talloc-${version} +cp -a lib/replace talloc-${version}/libreplace +pushd talloc-${version} +./autogen.sh +popd +tar cvzf talloc-${version}.tar.gz talloc-${version} +rm -fr talloc-${version} + +#Clean up +git checkout $curbranch +git branch -d talloc-release-script-${strver} diff --git a/lib/talloc/talloc.3.xml b/lib/talloc/talloc.3.xml index 67de15bfc8..8d9e08226d 100644 --- a/lib/talloc/talloc.3.xml +++ b/lib/talloc/talloc.3.xml @@ -135,6 +135,30 @@ <para> talloc_free() operates recursively on its children. </para> + <para> + From the 2.0 version of talloc, as a special case, + talloc_free() is refused on pointers that have more than one + parent, as talloc would have no way of knowing which parent + should be removed. To free a pointer that has more than one + parent please use talloc_unlink(). + </para> + <para> + To help you find problems in your code caused by this behaviour, if + you do try and free a pointer with more than one parent then the + talloc logging function will be called to give output like this: + </para> + <para> + <screen format="linespecific"> + ERROR: talloc_free with references at some_dir/source/foo.c:123 + reference at some_dir/source/other.c:325 + reference at some_dir/source/third.c:121 + </screen> + </para> + <para> + Please see the documentation for talloc_set_log_fn() and + talloc_set_log_stderr() for more information on talloc logging + functions. + </para> </refsect2> <refsect2 id="talloc_reference"><title>void *talloc_reference(const void *ctx, const void *ptr);</title> <para> @@ -381,11 +405,49 @@ talloc_realloc(ctx, ptr, type, 0) ==> talloc_free(ptr);</programlisting> It does not have any failure modes. </para> <para> - NOTE: It is possible to produce loops in the parent/child + It is possible to produce loops in the parent/child relationship if you are not careful with talloc_steal(). No guarantees are provided as to your sanity or the safety of your data if you do this. </para> + <para> + Note that if you try and call talloc_steal() on a pointer that has + more than one parent then the result is ambiguous. Talloc will choose + to remove the parent that is currently indicated by talloc_parent() + and replace it with the chosen parent. You will also get a message + like this via the talloc logging functions: + </para> + <para> + <screen format="linespecific"> + WARNING: talloc_steal with references at some_dir/source/foo.c:123 + reference at some_dir/source/other.c:325 + reference at some_dir/source/third.c:121 + </screen> + </para> + <para> + To unambiguously change the parent of a pointer please see + the + function <link linkend="talloc_reference"><quote>talloc_reparent()</quote></link>. See + the talloc_set_log_fn() documentation for more information + on talloc logging. + </para> + </refsect2> + <refsect2><title>TYPE *talloc_reparent(const void *<emphasis role="italic">old_parent</emphasis>, const void *<emphasis role="italic">new_parent</emphasis>, const TYPE *<emphasis role="italic">ptr</emphasis>);</title> + <para> + The talloc_reparent() function changes the parent context of a talloc + pointer. It is typically used when the context that the pointer is + currently a child of is going to be freed and you wish to keep the + memory for a longer time. + </para> + <para> + The talloc_reparent() function returns the pointer that you pass it. It + does not have any failure modes. + </para> + <para> + The difference between talloc_reparent() and talloc_steal() is that + talloc_reparent() can specify which parent you wish to change. This is + useful when a pointer has multiple parents via references. + </para> </refsect2> <refsect2><title>TYPE *talloc_move(const void *<emphasis role="italic">new_ctx</emphasis>, TYPE **<emphasis role="italic">ptr</emphasis>);</title> <para> @@ -696,6 +758,18 @@ if (ptr) memcpy(ptr, p, strlen(p)+1);</programlisting> </para> <programlisting>talloc_set_name_const(ptr, #type)</programlisting> </refsect2> + <refsect2><title>talloc_set_log_fn(void (*log_fn)(const char *message));</title> + <para> + This function sets a logging function that talloc will use for + warnings and errors. By default talloc will not print any warnings or + errors. + </para> + </refsect2> + <refsect2><title>talloc_set_log_stderr(void);</title> + <para> + This sets the talloc log function to write log messages to stderr + </para> + </refsect2> </refsect1> <refsect1><title>PERFORMANCE</title> <para> diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c index 60a48ad811..f7b1ac3dbd 100644 --- a/lib/talloc/talloc.c +++ b/lib/talloc/talloc.c @@ -30,26 +30,20 @@ inspired by http://swapped.cc/halloc/ */ -#ifdef _SAMBA_BUILD_ -#include "version.h" -#if (SAMBA_VERSION_MAJOR<4) -#include "includes.h" -/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file - * we trust ourselves... */ -#ifdef malloc -#undef malloc +#include "replace.h" +#include "talloc.h" + +#ifdef TALLOC_BUILD_VERSION_MAJOR +#if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR) +#error "TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR" #endif -#ifdef realloc -#undef realloc #endif -#define _TALLOC_SAMBA3 -#endif /* (SAMBA_VERSION_MAJOR<4) */ -#endif /* _SAMBA_BUILD_ */ -#ifndef _TALLOC_SAMBA3 -#include "replace.h" -#include "talloc.h" -#endif /* not _TALLOC_SAMBA3 */ +#ifdef TALLOC_BUILD_VERSION_MINOR +#if (TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR) +#error "TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR" +#endif +#endif /* use this to force every realloc to change the pointer, to stress test code that might not cope */ @@ -57,7 +51,13 @@ #define MAX_TALLOC_SIZE 0x10000000 -#define TALLOC_MAGIC 0xe814ec70 +#define TALLOC_MAGIC_BASE 0xe814ec70 +#define TALLOC_MAGIC ( \ + TALLOC_MAGIC_BASE + \ + (TALLOC_VERSION_MAJOR << 12) + \ + (TALLOC_VERSION_MINOR << 4) \ +) + #define TALLOC_FLAG_FREE 0x01 #define TALLOC_FLAG_LOOP 0x02 #define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ @@ -107,6 +107,7 @@ static void *autofree_context; struct talloc_reference_handle { struct talloc_reference_handle *next, *prev; void *ptr; + const char *location; }; typedef int (*talloc_destructor_t)(void *); @@ -138,6 +139,51 @@ struct talloc_chunk { #define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) #define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) +int talloc_version_major(void) +{ + return TALLOC_VERSION_MAJOR; +} + +int talloc_version_minor(void) +{ + return TALLOC_VERSION_MINOR; +} + +static void (*talloc_log_fn)(const char *message); + +void talloc_set_log_fn(void (*log_fn)(const char *message)) +{ + talloc_log_fn = log_fn; +} + +static void talloc_log(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +static void talloc_log(const char *fmt, ...) +{ + va_list ap; + char *message; + + if (!talloc_log_fn) { + return; + } + + va_start(ap, fmt); + message = talloc_vasprintf(NULL, fmt, ap); + va_end(ap); + + talloc_log_fn(message); + talloc_free(message); +} + +static void talloc_log_stderr(const char *message) +{ + fprintf(stderr, "%s", message); +} + +void talloc_set_log_stderr(void) +{ + talloc_set_log_fn(talloc_log_stderr); +} + static void (*talloc_abort_fn)(const char *reason); void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) @@ -147,6 +193,8 @@ void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) static void talloc_abort(const char *reason) { + talloc_log("%s\n", reason); + if (!talloc_abort_fn) { TALLOC_ABORT(reason); } @@ -154,6 +202,17 @@ static void talloc_abort(const char *reason) talloc_abort_fn(reason); } +static void talloc_abort_magic(unsigned magic) +{ + unsigned striped = magic - TALLOC_MAGIC_BASE; + unsigned major = (striped & 0xFFFFF000) >> 12; + unsigned minor = (striped & 0x00000FF0) >> 4; + talloc_log("Bad talloc magic[0x%08X/%u/%u] expected[0x%08X/%u/%u]\n", + magic, major, minor, + TALLOC_MAGIC, TALLOC_VERSION_MAJOR, TALLOC_VERSION_MINOR); + talloc_abort("Bad talloc magic value - wrong talloc version used/mixed"); +} + static void talloc_abort_double_free(void) { talloc_abort("Bad talloc magic value - double free"); @@ -170,10 +229,18 @@ static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) const char *pp = (const char *)ptr; struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { + if ((tc->flags & (~0xFFF)) == TALLOC_MAGIC_BASE) { + talloc_abort_magic(tc->flags & (~0xF)); + return NULL; + } + if (tc->flags & TALLOC_FLAG_FREE) { + talloc_log("talloc: double free error - first free may be at %s\n", tc->name); talloc_abort_double_free(); + return NULL; } else { talloc_abort_unknown_value(); + return NULL; } } return tc; @@ -465,7 +532,7 @@ static inline void *_talloc_named_const(const void *context, size_t size, const same underlying data, and you want to be able to free the two instances separately, and in either order */ -void *_talloc_reference(const void *context, const void *ptr) +void *_talloc_reference_loc(const void *context, const void *ptr, const char *location) { struct talloc_chunk *tc; struct talloc_reference_handle *handle; @@ -482,15 +549,17 @@ void *_talloc_reference(const void *context, const void *ptr) own destructor on the context if they want to */ talloc_set_destructor(handle, talloc_reference_destructor); handle->ptr = discard_const_p(void, ptr); + handle->location = location; _TLIST_ADD(tc->refs, handle); return handle->ptr; } +static void *_talloc_steal_internal(const void *new_ctx, const void *ptr); /* internal talloc_free call */ -static inline int _talloc_free(void *ptr) +static inline int _talloc_free_internal(void *ptr, const char *location) { struct talloc_chunk *tc; @@ -510,9 +579,9 @@ static inline int _talloc_free(void *ptr) * pointer. */ is_child = talloc_is_parent(tc->refs, ptr); - _talloc_free(tc->refs); + _talloc_free_internal(tc->refs, location); if (is_child) { - return _talloc_free(ptr); + return _talloc_free_internal(ptr, location); } return -1; } @@ -559,17 +628,23 @@ static inline int _talloc_free(void *ptr) struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } - if (unlikely(_talloc_free(child) == -1)) { + if (unlikely(_talloc_free_internal(child, location) == -1)) { if (new_parent == null_context) { struct talloc_chunk *p = talloc_parent_chunk(ptr); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } - talloc_steal(new_parent, child); + _talloc_steal_internal(new_parent, child); } } tc->flags |= TALLOC_FLAG_FREE; + /* we mark the freed memory with where we called the free + * from. This means on a double free error we can report where + * the first free came from + */ + tc->name = location; + if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { struct talloc_chunk *pool; unsigned int *pool_object_count; @@ -581,6 +656,7 @@ static inline int _talloc_free(void *ptr) if (*pool_object_count == 0) { talloc_abort("Pool object count zero!"); + return 0; } *pool_object_count -= 1; @@ -600,7 +676,7 @@ static inline int _talloc_free(void *ptr) ptr on success, or NULL if it could not be transferred. passing NULL as ptr will always return NULL with no side effects. */ -void *_talloc_steal(const void *new_ctx, const void *ptr) +static void *_talloc_steal_internal(const void *new_ctx, const void *ptr) { struct talloc_chunk *tc, *new_tc; @@ -652,7 +728,69 @@ void *_talloc_steal(const void *new_ctx, const void *ptr) return discard_const_p(void, ptr); } +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs != NULL) && talloc_parent(ptr) != new_ctx) { + struct talloc_reference_handle *h; + + talloc_log("WARNING: talloc_steal with references at %s\n", + location); + + for (h=tc->refs; h; h=h->next) { + talloc_log("\treference at %s\n", + h->location); + } + } + + return _talloc_steal_internal(new_ctx, ptr); +} + +/* + this is like a talloc_steal(), but you must supply the old + parent. This resolves the ambiguity in a talloc_steal() which is + called on a context that has more than one parent (via references) + + The old parent can be either a reference or a parent +*/ +void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *h; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + if (old_parent == talloc_parent(ptr)) { + return _talloc_steal_internal(new_parent, ptr); + } + + tc = talloc_chunk_from_ptr(ptr); + for (h=tc->refs;h;h=h->next) { + if (talloc_parent(h) == old_parent) { + if (_talloc_steal_internal(new_parent, h) != h) { + return NULL; + } + return discard_const_p(void, ptr); + } + } + /* it wasn't a parent */ + return NULL; +} /* remove a secondary reference to a pointer. This undo's what @@ -680,7 +818,7 @@ static inline int talloc_unreference(const void *context, const void *ptr) return -1; } - return _talloc_free(h); + return _talloc_free_internal(h, __location__); } /* @@ -717,7 +855,7 @@ int talloc_unlink(const void *context, void *ptr) tc_p = talloc_chunk_from_ptr(ptr); if (tc_p->refs == NULL) { - return _talloc_free(ptr); + return _talloc_free_internal(ptr, __location__); } new_p = talloc_parent_chunk(tc_p->refs); @@ -731,7 +869,7 @@ int talloc_unlink(const void *context, void *ptr) return -1; } - talloc_steal(new_parent, ptr); + _talloc_steal_internal(new_parent, ptr); return 0; } @@ -784,7 +922,7 @@ void *talloc_named(const void *context, size_t size, const char *fmt, ...) va_end(ap); if (unlikely(name == NULL)) { - _talloc_free(ptr); + _talloc_free_internal(ptr, __location__); return NULL; } @@ -882,7 +1020,7 @@ void *talloc_init(const char *fmt, ...) va_end(ap); if (unlikely(name == NULL)) { - _talloc_free(ptr); + _talloc_free_internal(ptr, __location__); return NULL; } @@ -916,12 +1054,12 @@ void talloc_free_children(void *ptr) struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } - if (unlikely(_talloc_free(child) == -1)) { + if (unlikely(talloc_free(child) == -1)) { if (new_parent == null_context) { struct talloc_chunk *p = talloc_parent_chunk(ptr); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } - talloc_steal(new_parent, child); + _talloc_steal_internal(new_parent, child); } } @@ -969,9 +1107,30 @@ void *talloc_named_const(const void *context, size_t size, const char *name) will not be freed if the ref_count is > 1 or the destructor (if any) returns non-zero */ -int talloc_free(void *ptr) +int _talloc_free(void *ptr, const char *location) { - return _talloc_free(ptr); + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs != NULL)) { + struct talloc_reference_handle *h; + + talloc_log("ERROR: talloc_free with references at %s\n", + location); + + for (h=tc->refs; h; h=h->next) { + talloc_log("\treference at %s\n", + h->location); + } + return -1; + } + + return _talloc_free_internal(ptr, location); } @@ -988,7 +1147,7 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n /* size zero is equivalent to free() */ if (unlikely(size == 0)) { - _talloc_free(ptr); + talloc_unlink(context, ptr); return NULL; } @@ -1008,6 +1167,11 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n return NULL; } + /* don't let anybody try to realloc a talloc_pool */ + if (unlikely(tc->flags & TALLOC_FLAG_POOL)) { + return NULL; + } + /* don't shrink if we have less than 1k to gain */ if ((size < tc->size) && ((tc->size - size) < 1024)) { tc->size = size; @@ -1020,7 +1184,7 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n #if ALWAYS_REALLOC new_ptr = malloc(size + TC_HDR_SIZE); if (new_ptr) { - memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); + memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE); free(tc); } #else @@ -1080,7 +1244,7 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n void *_talloc_move(const void *new_ctx, const void *_pptr) { const void **pptr = discard_const_p(const void *,_pptr); - void *ret = _talloc_steal(new_ctx, *pptr); + void *ret = talloc_steal(new_ctx, discard_const_p(void, *pptr)); (*pptr) = NULL; return ret; } @@ -1108,7 +1272,9 @@ size_t talloc_total_size(const void *ptr) tc->flags |= TALLOC_FLAG_LOOP; - total = tc->size; + if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) { + total = tc->size; + } for (c=tc->child;c;c=c->next) { total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); } @@ -1124,7 +1290,16 @@ size_t talloc_total_size(const void *ptr) size_t talloc_total_blocks(const void *ptr) { size_t total = 0; - struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); if (tc->flags & TALLOC_FLAG_LOOP) { return 0; @@ -1246,8 +1421,10 @@ static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_ */ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) { - talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); - fflush(f); + if (f) { + talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); + fflush(f); + } } /* @@ -1293,6 +1470,20 @@ void talloc_enable_null_tracking(void) { if (null_context == NULL) { null_context = _talloc_named_const(NULL, 0, "null_context"); + if (autofree_context != NULL) { + talloc_reparent(NULL, null_context, autofree_context); + } + } +} + +/* + enable tracking of the NULL context, not moving the autofree context + into the NULL context. This is needed for the talloc testsuite +*/ +void talloc_enable_null_tracking_no_autofree(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); } } @@ -1301,7 +1492,23 @@ void talloc_enable_null_tracking(void) */ void talloc_disable_null_tracking(void) { - _talloc_free(null_context); + if (null_context != NULL) { + /* we have to move any children onto the real NULL + context */ + struct talloc_chunk *tc, *tc2; + tc = talloc_chunk_from_ptr(null_context); + for (tc2 = tc->child; tc2; tc2=tc2->next) { + if (tc2->parent == tc) tc2->parent = NULL; + if (tc2->prev == tc) tc2->prev = NULL; + } + for (tc2 = tc->next; tc2; tc2=tc2->next) { + if (tc2->parent == tc) tc2->parent = NULL; + if (tc2->prev == tc) tc2->prev = NULL; + } + tc->child = NULL; + tc->next = NULL; + } + talloc_free(null_context); null_context = NULL; } @@ -1683,7 +1890,7 @@ static int talloc_autofree_destructor(void *ptr) static void talloc_autofree(void) { - _talloc_free(autofree_context); + talloc_free(autofree_context); } /* @@ -1704,8 +1911,12 @@ size_t talloc_get_size(const void *context) { struct talloc_chunk *tc; - if (context == NULL) + if (context == NULL) { + context = null_context; + } + if (context == NULL) { return 0; + } tc = talloc_chunk_from_ptr(context); diff --git a/lib/talloc/talloc.exports b/lib/talloc/talloc.exports new file mode 100644 index 0000000000..1b8062f4a0 --- /dev/null +++ b/lib/talloc/talloc.exports @@ -0,0 +1,68 @@ +# This file is autogenerated, please DO NOT EDIT +{ + global: + _talloc; + _talloc_array; + _talloc_free; + _talloc_get_type_abort; + _talloc_memdup; + _talloc_move; + _talloc_realloc; + _talloc_realloc_array; + _talloc_reference_loc; + _talloc_set_destructor; + _talloc_steal_loc; + _talloc_zero; + _talloc_zero_array; + talloc_asprintf; + talloc_asprintf_append; + talloc_asprintf_append_buffer; + talloc_autofree_context; + talloc_check_name; + talloc_disable_null_tracking; + talloc_enable_leak_report; + talloc_enable_leak_report_full; + talloc_enable_null_tracking; + talloc_enable_null_tracking_no_autofree; + talloc_find_parent_byname; + talloc_free_children; + talloc_get_name; + talloc_get_size; + talloc_increase_ref_count; + talloc_init; + talloc_is_parent; + talloc_named; + talloc_named_const; + talloc_parent; + talloc_parent_name; + talloc_pool; + talloc_realloc_fn; + talloc_reference_count; + talloc_reparent; + talloc_report; + talloc_report_depth_cb; + talloc_report_depth_file; + talloc_report_full; + talloc_set_abort_fn; + talloc_set_log_fn; + talloc_set_log_stderr; + talloc_set_name; + talloc_set_name_const; + talloc_show_parents; + talloc_strdup; + talloc_strdup_append; + talloc_strdup_append_buffer; + talloc_strndup; + talloc_strndup_append; + talloc_strndup_append_buffer; + talloc_total_blocks; + talloc_total_size; + talloc_unlink; + talloc_vasprintf; + talloc_vasprintf_append; + talloc_vasprintf_append_buffer; + talloc_version_major; + talloc_version_minor; + + local: *; +}; diff --git a/lib/talloc/talloc.h b/lib/talloc/talloc.h index f87564a13b..f549a17fba 100644 --- a/lib/talloc/talloc.h +++ b/lib/talloc/talloc.h @@ -29,6 +29,12 @@ #include <stdio.h> #include <stdarg.h> +#define TALLOC_VERSION_MAJOR 2 +#define TALLOC_VERSION_MINOR 0 + +int talloc_version_major(void); +int talloc_version_minor(void); + /* this is only needed for compatibility with the old talloc */ typedef void TALLOC_CTX; @@ -69,15 +75,15 @@ typedef void TALLOC_CTX; } while(0) /* this extremely strange macro is to avoid some braindamaged warning stupidity in gcc 4.1.x */ -#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; }) +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal_loc((ctx),(ptr), __location__); __talloc_steal_ret; }) #else #define talloc_set_destructor(ptr, function) \ _talloc_set_destructor((ptr), (int (*)(void *))(function)) #define _TALLOC_TYPEOF(ptr) void * -#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal_loc((ctx),(ptr), __location__) #endif -#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference_loc((ctx),(ptr), __location__) #define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) /* useful macros for creating type checked pointers */ @@ -106,6 +112,8 @@ typedef void TALLOC_CTX; #define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__) #define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) +#define talloc_free(ctx) _talloc_free(ctx, __location__) + #if TALLOC_DEPRECATED #define talloc_zero_p(ctx, type) talloc_zero(ctx, type) @@ -121,10 +129,10 @@ typedef void TALLOC_CTX; /* The following definitions come from talloc.c */ void *_talloc(const void *context, size_t size); void *talloc_pool(const void *context, size_t size); -void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); +void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *)); int talloc_increase_ref_count(const void *ptr); size_t talloc_reference_count(const void *ptr); -void *_talloc_reference(const void *context, const void *ptr); +void *_talloc_reference_loc(const void *context, const void *ptr, const char *location); int talloc_unlink(const void *context, void *ptr); const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); void talloc_set_name_const(const void *ptr, const char *name); @@ -137,10 +145,11 @@ void *_talloc_get_type_abort(const void *ptr, const char *name, const char *loca void *talloc_parent(const void *ptr); const char *talloc_parent_name(const void *ptr); void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); -int talloc_free(void *ptr); +int _talloc_free(void *ptr, const char *location); void talloc_free_children(void *ptr); void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); -void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location); +void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr); void *_talloc_move(const void *new_ctx, const void *pptr); size_t talloc_total_size(const void *ptr); size_t talloc_total_blocks(const void *ptr); @@ -154,6 +163,7 @@ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f void talloc_report_full(const void *ptr, FILE *f); void talloc_report(const void *ptr, FILE *f); void talloc_enable_null_tracking(void); +void talloc_enable_null_tracking_no_autofree(void); void talloc_disable_null_tracking(void); void talloc_enable_leak_report(void); void talloc_enable_leak_report_full(void); @@ -186,5 +196,7 @@ char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3 char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); +void talloc_set_log_fn(void (*log_fn)(const char *message)); +void talloc_set_log_stderr(void); #endif diff --git a/lib/talloc/talloc.mk b/lib/talloc/talloc.mk index ce7784c8a2..fc90f4d41e 100644 --- a/lib/talloc/talloc.mk +++ b/lib/talloc/talloc.mk @@ -1,7 +1,8 @@ TALLOC_OBJ = $(tallocdir)/talloc.o -TALLOC_SOLIB = libtalloc.$(SHLIBEXT).$(PACKAGE_VERSION) -TALLOC_SONAME = libtalloc.$(SHLIBEXT).1 +TALLOC_SHLIB = libtalloc.$(SHLIBEXT) +TALLOC_SOLIB = libtalloc.$(SHLIBEXT).$(TALLOC_VERSION) +TALLOC_SONAME = libtalloc.$(SHLIBEXT).$(TALLOC_VERSION_MAJOR) TALLOC_STLIB = libtalloc.a all:: $(TALLOC_STLIB) $(TALLOC_SOLIB) testsuite @@ -25,14 +26,27 @@ install:: all if [ -f talloc.3 ];then ${INSTALLCMD} -m 644 talloc.3 $(DESTDIR)$(mandir)/man3; fi which swig >/dev/null 2>&1 && ${INSTALLCMD} -d $(DESTDIR)`swig -swiglib` || true which swig >/dev/null 2>&1 && ${INSTALLCMD} -m 644 talloc.i $(DESTDIR)`swig -swiglib` || true + rm -f $(DESTDIR)$(libdir)/$(TALLOC_SONAME) + ln -s $(TALLOC_SOLIB) $(DESTDIR)$(libdir)/$(TALLOC_SONAME) + rm -f $(DESTDIR)$(libdir)/$(TALLOC_SHLIB) + ln -s $(TALLOC_SOLIB) $(DESTDIR)$(libdir)/$(TALLOC_SHLIB) doc:: talloc.3 talloc.3.html clean:: rm -f *~ $(LIBOBJ) $(TALLOC_SOLIB) $(TALLOC_STLIB) testsuite testsuite.o testsuite_main.o *.gc?? talloc.3 talloc.3.html + rm -fr abi + rm -f talloc.exports.sort talloc.exports.check talloc.exports.check.sort + rm -f talloc.signatures.sort talloc.signatures.check talloc.signatures.check.sort test:: testsuite ./testsuite +abi_checks:: + @echo ABI checks: + @./script/abi_checks.sh talloc talloc.h + +test:: abi_checks + gcov:: gcov talloc.c diff --git a/lib/talloc/talloc.pc.in b/lib/talloc/talloc.pc.in index 459cce70b1..5ce2109866 100644 --- a/lib/talloc/talloc.pc.in +++ b/lib/talloc/talloc.pc.in @@ -5,7 +5,7 @@ includedir=@includedir@ Name: talloc Description: A hierarchical pool based memory system with destructors -Version: @PACKAGE_VERSION@ +Version: @TALLOC_VERSION@ Libs: -L${libdir} -ltalloc Cflags: -I${includedir} URL: http://talloc.samba.org/ diff --git a/lib/talloc/talloc.signatures b/lib/talloc/talloc.signatures new file mode 100644 index 0000000000..f2868e8269 --- /dev/null +++ b/lib/talloc/talloc.signatures @@ -0,0 +1,62 @@ +char *talloc_asprintf (const void *, const char *, ...); +char *talloc_asprintf_append (char *, const char *, ...); +char *talloc_asprintf_append_buffer (char *, const char *, ...); +char *talloc_strdup (const void *, const char *); +char *talloc_strdup_append (char *, const char *); +char *talloc_strdup_append_buffer (char *, const char *); +char *talloc_strndup (const void *, const char *, size_t); +char *talloc_strndup_append (char *, const char *, size_t); +char *talloc_strndup_append_buffer (char *, const char *, size_t); +char *talloc_vasprintf (const void *, const char *, va_list); +char *talloc_vasprintf_append (char *, const char *, va_list); +char *talloc_vasprintf_append_buffer (char *, const char *, va_list); +const char *talloc_get_name (const void *); +const char *talloc_parent_name (const void *); +const char *talloc_set_name (const void *, const char *, ...); +int _talloc_free (void *, const char *); +int talloc_increase_ref_count (const void *); +int talloc_is_parent (const void *, const void *); +int talloc_unlink (const void *, void *); +int talloc_version_major (void); +int talloc_version_minor (void); +size_t talloc_get_size (const void *); +size_t talloc_reference_count (const void *); +size_t talloc_total_blocks (const void *); +size_t talloc_total_size (const void *); +void *_talloc (const void *, size_t); +void *_talloc_array (const void *, size_t, unsigned int, const char *); +void *_talloc_get_type_abort (const void *, const char *, const char *); +void *_talloc_memdup (const void *, const void *, size_t, const char *); +void *_talloc_move (const void *, const void *); +void *_talloc_realloc (const void *, void *, size_t, const char *); +void *_talloc_realloc_array (const void *, void *, size_t, unsigned int, const char *); +void *_talloc_reference_loc (const void *, const void *, const char *); +void *_talloc_steal_loc (const void *, const void *, const char *); +void *_talloc_zero (const void *, size_t, const char *); +void *_talloc_zero_array (const void *, size_t, unsigned int, const char *); +void *talloc_autofree_context (void); +void *talloc_check_name (const void *, const char *); +void *talloc_find_parent_byname (const void *, const char *); +void *talloc_init (const char *, ...); +void *talloc_named (const void *, size_t, const char *, ...); +void *talloc_named_const (const void *, size_t, const char *); +void *talloc_parent (const void *); +void *talloc_pool (const void *, size_t); +void *talloc_realloc_fn (const void *, void *, size_t); +void *talloc_reparent (const void *, const void *, const void *); +void _talloc_set_destructor (const void *, int (*) (void *)); +void talloc_disable_null_tracking (void); +void talloc_enable_leak_report (void); +void talloc_enable_leak_report_full (void); +void talloc_enable_null_tracking (void); +void talloc_enable_null_tracking_no_autofree (void); +void talloc_free_children (void *); +void talloc_report (const void *, FILE *); +void talloc_report_depth_cb (const void *, int, int, void (*) (const void *, int, int, int, void *), void *); +void talloc_report_depth_file (const void *, int, int, FILE *); +void talloc_report_full (const void *, FILE *); +void talloc_set_abort_fn (void (*) (const char *)); +void talloc_set_log_fn (void (*) (const char *)); +void talloc_set_log_stderr (void); +void talloc_set_name_const (const void *, const char *); +void talloc_show_parents (const void *, FILE *); diff --git a/lib/talloc/talloc_guide.txt b/lib/talloc/talloc_guide.txt index 3201fe6f0f..01de806662 100644 --- a/lib/talloc/talloc_guide.txt +++ b/lib/talloc/talloc_guide.txt @@ -4,10 +4,10 @@ Using talloc in Samba4 .. contents:: Andrew Tridgell -September 2004 +August 2009 The most current version of this document is available at - http://samba.org/ftp/unpacked/samba4/source/lib/talloc/talloc_guide.txt + http://samba.org/ftp/unpacked/talloc/talloc_guide.txt If you are used to the "old" talloc from Samba3 before 3.0.20 then please read this carefully, as talloc has changed a lot. With 3.0.20 (or 3.0.14?) the @@ -131,6 +131,22 @@ For more control on which parent is removed, see talloc_unlink() talloc_free() operates recursively on its children. +From the 2.0 version of talloc, as a special case, talloc_free() is +refused on pointers that have more than one parent, as talloc would +have no way of knowing which parent should be removed. To free a +pointer that has more than one parent please use talloc_unlink(). + +To help you find problems in your code caused by this behaviour, if +you do try and free a pointer with more than one parent then the +talloc logging function will be called to give output like this: + + ERROR: talloc_free with references at some_dir/source/foo.c:123 + reference at some_dir/source/other.c:325 + reference at some_dir/source/third.c:121 + +Please see the documentation for talloc_set_log_fn() and +talloc_set_log_stderr() for more information on talloc logging +functions. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- int talloc_free_children(void *ptr); @@ -349,6 +365,42 @@ as to your sanity or the safety of your data if you do this. talloc_steal (new_ctx, NULL) will return NULL with no sideeffects. +Note that if you try and call talloc_steal() on a pointer that has +more than one parent then the result is ambiguous. Talloc will choose +to remove the parent that is currently indicated by talloc_parent() +and replace it with the chosen parent. You will also get a message +like this via the talloc logging functions: + + WARNING: talloc_steal with references at some_dir/source/foo.c:123 + reference at some_dir/source/other.c:325 + reference at some_dir/source/third.c:121 + +To unambiguously change the parent of a pointer please see the +function talloc_reparent(). See the talloc_set_log_fn() documentation +for more information on talloc logging. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr); + +The talloc_reparent() function changes the parent context of a talloc +pointer. It is typically used when the context that the pointer is +currently a child of is going to be freed and you wish to keep the +memory for a longer time. + +The talloc_reparent() function returns the pointer that you pass it. It +does not have any failure modes. + +The difference between talloc_reparent() and talloc_steal() is that +talloc_reparent() can specify which parent you wish to change. This is +useful when a pointer has multiple parents via references. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_parent(const void *ptr); + +The talloc_parent() function returns the current talloc parent. This +is usually the pointer under which this memory was originally created, +but it may have changed due to a talloc_steal() or talloc_reparent() + =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- size_t talloc_total_size(const void *ptr); @@ -692,3 +744,14 @@ know the structure you want is a parent of another context. Like talloc_find_parent_byname() but takes a type, making it typesafe. +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_set_log_fn(void (*log_fn)(const char *message)); + +This function sets a logging function that talloc will use for +warnings and errors. By default talloc will not print any warnings or +errors. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_set_log_stderr(void) + +This sets the talloc log function to write log messages to stderr diff --git a/lib/talloc/testsuite.c b/lib/talloc/testsuite.c index 3d490ddf49..08aa20863a 100644 --- a/lib/talloc/testsuite.c +++ b/lib/talloc/testsuite.c @@ -66,8 +66,8 @@ static double timeval_elapsed(struct timeval *tv) #define CHECK_SIZE(test, ptr, tsize) do { \ if (talloc_total_size(ptr) != (tsize)) { \ - printf("failed: %s [\nwrong '%s' tree size: got %u expected %u\n]\n", \ - test, #ptr, \ + printf("failed: %s [\n%s: wrong '%s' tree size: got %u expected %u\n]\n", \ + test, __location__, #ptr, \ (unsigned)talloc_total_size(ptr), \ (unsigned)tsize); \ talloc_report_full(ptr, stdout); \ @@ -77,8 +77,8 @@ static double timeval_elapsed(struct timeval *tv) #define CHECK_BLOCKS(test, ptr, tblocks) do { \ if (talloc_total_blocks(ptr) != (tblocks)) { \ - printf("failed: %s [\nwrong '%s' tree blocks: got %u expected %u\n]\n", \ - test, #ptr, \ + printf("failed: %s [\n%s: wrong '%s' tree blocks: got %u expected %u\n]\n", \ + test, __location__, #ptr, \ (unsigned)talloc_total_blocks(ptr), \ (unsigned)tblocks); \ talloc_report_full(ptr, stdout); \ @@ -88,8 +88,8 @@ static double timeval_elapsed(struct timeval *tv) #define CHECK_PARENT(test, ptr, parent) do { \ if (talloc_parent(ptr) != (parent)) { \ - printf("failed: %s [\n'%s' has wrong parent: got %p expected %p\n]\n", \ - test, #ptr, \ + printf("failed: %s [\n%s: '%s' has wrong parent: got %p expected %p\n]\n", \ + test, __location__, #ptr, \ talloc_parent(ptr), \ (parent)); \ talloc_report_full(ptr, stdout); \ @@ -99,6 +99,30 @@ static double timeval_elapsed(struct timeval *tv) } \ } while (0) +static unsigned int test_abort_count; + +static void test_abort_fn(const char *reason) +{ + printf("# test_abort_fn(%s)\n", reason); + test_abort_count++; +} + +static void test_abort_start(void) +{ + test_abort_count = 0; + talloc_set_abort_fn(test_abort_fn); +} + +static void test_abort_stop(void) +{ + test_abort_count = 0; + talloc_set_abort_fn(NULL); +} + +static void test_log_stdout(const char *message) +{ + fprintf(stdout, "%s", message); +} /* test references @@ -125,7 +149,7 @@ static bool test_ref1(void) CHECK_BLOCKS("ref1", r1, 2); fprintf(stderr, "Freeing p2\n"); - talloc_free(p2); + talloc_unlink(r1, p2); talloc_report_full(root, stderr); CHECK_BLOCKS("ref1", p1, 5); @@ -180,7 +204,7 @@ static bool test_ref2(void) CHECK_BLOCKS("ref2", r1, 2); fprintf(stderr, "Freeing ref\n"); - talloc_free(ref); + talloc_unlink(r1, ref); talloc_report_full(root, stderr); CHECK_BLOCKS("ref2", p1, 5); @@ -372,7 +396,7 @@ static bool test_misc(void) talloc_increase_ref_count(p1); CHECK_BLOCKS("misc", p1, 1); CHECK_BLOCKS("misc", root, 2); - talloc_free(p1); + talloc_unlink(NULL, p1); CHECK_BLOCKS("misc", p1, 1); CHECK_BLOCKS("misc", root, 2); talloc_unlink(NULL, p1); @@ -383,7 +407,7 @@ static bool test_misc(void) "failed: talloc_unlink() of non-reference context should return -1\n"); torture_assert("misc", talloc_unlink(p1, p2) == 0, "failed: talloc_unlink() of parent should succeed\n"); - talloc_free(p1); + talloc_unlink(NULL, p1); CHECK_BLOCKS("misc", p1, 1); CHECK_BLOCKS("misc", root, 2); @@ -494,6 +518,7 @@ static bool test_misc(void) CHECK_SIZE("misc", NULL, 0); + talloc_enable_null_tracking_no_autofree(); talloc_enable_leak_report(); talloc_enable_leak_report_full(); @@ -542,14 +567,18 @@ static bool test_realloc(void) talloc_realloc_size(NULL, p2, 0); talloc_realloc_size(NULL, p2, 0); + CHECK_BLOCKS("realloc", p1, 4); + talloc_realloc_size(p1, p2, 0); CHECK_BLOCKS("realloc", p1, 3); torture_assert("realloc", talloc_realloc_size(NULL, p1, 0x7fffffff) == NULL, "failed: oversize talloc should fail\n"); talloc_realloc_size(NULL, p1, 0); - + CHECK_BLOCKS("realloc", root, 4); + talloc_realloc_size(root, p1, 0); CHECK_BLOCKS("realloc", root, 1); + CHECK_SIZE("realloc", root, 0); talloc_free(root); @@ -868,7 +897,7 @@ static bool test_lifeless(void) (void)talloc_reference(child_owner, child); talloc_report_full(top, stderr); talloc_unlink(top, parent); - talloc_free(child); + talloc_unlink(top, child); talloc_report_full(top, stderr); talloc_free(top); talloc_free(child_owner); @@ -1103,6 +1132,14 @@ static bool test_pool(void) return true; } +static void test_reset(void) +{ + talloc_set_log_fn(test_log_stdout); + test_abort_stop(); + talloc_disable_null_tracking(); + talloc_enable_null_tracking_no_autofree(); +} + struct torture_context; bool torture_local_talloc(struct torture_context *tctx) { @@ -1110,33 +1147,53 @@ bool torture_local_talloc(struct torture_context *tctx) setlinebuf(stdout); - talloc_disable_null_tracking(); - talloc_enable_null_tracking(); - + test_reset(); ret &= test_ref1(); + test_reset(); ret &= test_ref2(); + test_reset(); ret &= test_ref3(); + test_reset(); ret &= test_ref4(); + test_reset(); ret &= test_unlink1(); + test_reset(); ret &= test_misc(); + test_reset(); ret &= test_realloc(); + test_reset(); ret &= test_realloc_child(); + test_reset(); ret &= test_steal(); + test_reset(); ret &= test_move(); + test_reset(); ret &= test_unref_reparent(); + test_reset(); ret &= test_realloc_fn(); + test_reset(); ret &= test_type(); + test_reset(); ret &= test_lifeless(); + test_reset(); ret &= test_loop(); + test_reset(); ret &= test_free_parent_deny_child(); + test_reset(); ret &= test_talloc_ptrtype(); + test_reset(); ret &= test_talloc_free_in_destructor(); + test_reset(); ret &= test_pool(); if (ret) { + test_reset(); ret &= test_speed(); } + test_reset(); ret &= test_autofree(); + test_reset(); + return ret; } diff --git a/lib/talloc/web/index.html b/lib/talloc/web/index.html index 5deab93665..388ec2cde2 100644 --- a/lib/talloc/web/index.html +++ b/lib/talloc/web/index.html @@ -8,12 +8,17 @@ <h1>talloc</h1> talloc is a hierarchical pool based memory allocator with -destructors. It is the core memory allocator used in Samba4, and has +destructors. It is the core memory allocator used in Samba, and has made a huge difference in many aspects of Samba4 development.<p> To get started with talloc, I would recommend you read the <a href="http://samba.org/ftp/unpacked/talloc/talloc_guide.txt">talloc guide</a>. +<h2>Download</h2> +You can download the latest releases of talloc from the <a +href="http://samba.org/ftp/talloc">talloc directory</a> on the samba public +source archive. + <h2>Discussion and bug reports</h2> talloc does not currently have its own mailing list or bug tracking @@ -22,18 +27,18 @@ href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical< mailing list, and the <a href="http://bugzilla.samba.org/">Samba bugzilla</a> bug tracking system. -<h2>Download</h2> +<h2>Development</h2> -You can download the latest release either via rsync or git.<br> +You can download the latest code either via git or rsync.<br> <br> To fetch via git see the following guide:<br> <a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a><br> -Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/talloc directory.<br> +Once you have cloned the tree switch to the master branch and cd into the lib/talloc directory.<br> <br> To fetch via rsync use this command: <pre> - rsync -Pavz samba.org::ftp/unpacked/talloc . + rsync -Pavz samba.org::ftp/unpacked/standalone_projects/lib/talloc . </pre> <hr> diff --git a/lib/tdb/Makefile.in b/lib/tdb/Makefile.in index df1b02be6a..dc22ee3fea 100644 --- a/lib/tdb/Makefile.in +++ b/lib/tdb/Makefile.in @@ -9,6 +9,7 @@ exec_prefix = @exec_prefix@ bindir = @bindir@ includedir = @includedir@ libdir = @libdir@ +mandir = @mandir@ VPATH = @srcdir@:@libreplacedir@ srcdir = @srcdir@ builddir = @builddir@ @@ -31,18 +32,26 @@ PYTHON_CHECK_TARGET = @PYTHON_CHECK_TARGET@ LIB_PATH_VAR = @LIB_PATH_VAR@ tdbdir = @tdbdir@ +EXTRA_TARGETS = @DOC_TARGET@ + TDB_OBJ = @TDB_OBJ@ @LIBREPLACEOBJ@ +SONAMEFLAG = @SONAMEFLAG@ +VERSIONSCRIPT = @VERSIONSCRIPT@ +EXPORTSFILE = @EXPORTSFILE@ + +XSLTPROC = @XSLTPROC@ + default: all include $(tdbdir)/tdb.mk include $(tdbdir)/rules.mk -all:: showflags dirs $(PROGS) $(TDB_SOLIB) libtdb.a $(PYTHON_BUILD_TARGET) +all:: showflags dirs $(PROGS) $(TDB_SOLIB) libtdb.a $(PYTHON_BUILD_TARGET) $(EXTRA_TARGETS) install:: all $(TDB_SOLIB): $(TDB_OBJ) - $(SHLD) $(SHLD_FLAGS) -o $@ $(TDB_OBJ) @SONAMEFLAG@$(TDB_SONAME) + $(SHLD) $(SHLD_FLAGS) -o $@ $(TDB_OBJ) $(VERSIONSCRIPT) $(EXPORTSFILE) $(SONAMEFLAG)$(TDB_SONAME) shared-build: all ${INSTALLCMD} -d $(sharedbuilddir)/lib @@ -60,6 +69,7 @@ installcheck:: test install clean:: rm -f *.o *.a */*.o + rm -fr abi distclean:: clean rm -f config.log config.status include/config.h config.cache diff --git a/lib/tdb/common/check.c b/lib/tdb/common/check.c new file mode 100644 index 0000000000..f0a15f801b --- /dev/null +++ b/lib/tdb/common/check.c @@ -0,0 +1,423 @@ + /* + Unix SMB/CIFS implementation. + + trivial database library + + Copyright (C) Rusty Russell 2009 + + ** NOTE! The following LGPL license applies to the tdb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#include "tdb_private.h" + +/* Since we opened it, these shouldn't fail unless it's recent corruption. */ +static bool tdb_check_header(struct tdb_context *tdb, tdb_off_t *recovery) +{ + struct tdb_header hdr; + + if (tdb->methods->tdb_read(tdb, 0, &hdr, sizeof(hdr), DOCONV()) == -1) + return false; + if (strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) + goto corrupt; + + CONVERT(hdr); + if (hdr.version != TDB_VERSION) + goto corrupt; + + if (hdr.rwlocks != 0) + goto corrupt; + + if (hdr.hash_size == 0) + goto corrupt; + + if (hdr.hash_size != tdb->header.hash_size) + goto corrupt; + + if (hdr.recovery_start != 0 && + hdr.recovery_start < TDB_DATA_START(tdb->header.hash_size)) + goto corrupt; + + *recovery = hdr.recovery_start; + return true; + +corrupt: + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_ERROR, "Header is corrupt\n")); + return false; +} + +/* Generic record header check. */ +static bool tdb_check_record(struct tdb_context *tdb, + tdb_off_t off, + const struct tdb_record *rec) +{ + tdb_off_t tailer; + + /* Check rec->next: 0 or points to record offset, aligned. */ + if (rec->next > 0 && rec->next < TDB_DATA_START(tdb->header.hash_size)){ + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Record offset %d too small next %d\n", + off, rec->next)); + goto corrupt; + } + if (rec->next + sizeof(*rec) < rec->next) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Record offset %d too large next %d\n", + off, rec->next)); + goto corrupt; + } + if ((rec->next % TDB_ALIGNMENT) != 0) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Record offset %d misaligned next %d\n", + off, rec->next)); + goto corrupt; + } + if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0)) + goto corrupt; + + /* Check rec_len: similar to rec->next, implies next record. */ + if ((rec->rec_len % TDB_ALIGNMENT) != 0) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Record offset %d misaligned length %d\n", + off, rec->rec_len)); + goto corrupt; + } + /* Must fit tailer. */ + if (rec->rec_len < sizeof(tailer)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Record offset %d too short length %d\n", + off, rec->rec_len)); + goto corrupt; + } + /* OOB allows "right at the end" access, so this works for last rec. */ + if (tdb->methods->tdb_oob(tdb, off+sizeof(*rec)+rec->rec_len, 0)) + goto corrupt; + + /* Check tailer. */ + if (tdb_ofs_read(tdb, off+sizeof(*rec)+rec->rec_len-sizeof(tailer), + &tailer) == -1) + goto corrupt; + if (tailer != sizeof(*rec) + rec->rec_len) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Record offset %d invalid tailer\n", off)); + goto corrupt; + } + + return true; + +corrupt: + tdb->ecode = TDB_ERR_CORRUPT; + return false; +} + +/* Grab some bytes: may copy if can't use mmap. + Caller has already done bounds check. */ +static TDB_DATA get_bytes(struct tdb_context *tdb, + tdb_off_t off, tdb_len_t len) +{ + TDB_DATA d; + + d.dsize = len; + + if (tdb->transaction == NULL && tdb->map_ptr != NULL) + d.dptr = (unsigned char *)tdb->map_ptr + off; + else + d.dptr = tdb_alloc_read(tdb, off, d.dsize); + return d; +} + +/* Frees data if we're not able to simply use mmap. */ +static void put_bytes(struct tdb_context *tdb, TDB_DATA d) +{ + if (tdb->transaction == NULL && tdb->map_ptr != NULL) + return; + free(d.dptr); +} + +/* We use the excellent Jenkins lookup3 hash; this is based on hash_word2. + * See: http://burtleburtle.net/bob/c/lookup3.c + */ +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) +static void hash(uint32_t key, uint32_t *pc, uint32_t *pb) +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + *pc; + c += *pb; + a += key; + c ^= b; c -= rot(b,14); + a ^= c; a -= rot(c,11); + b ^= a; b -= rot(a,25); + c ^= b; c -= rot(b,16); + a ^= c; a -= rot(c,4); + b ^= a; b -= rot(a,14); + c ^= b; c -= rot(b,24); + *pc=c; *pb=b; +} + +/* + We want to check that all free records are in the free list + (only once), and all free list entries are free records. Similarly + for each hash chain of used records. + + Doing that naively (without walking hash chains, since we want to be + linear) means keeping a list of records which have been seen in each + hash chain, and another of records pointed to (ie. next pointers + from records and the initial hash chain heads). These two lists + should be equal. This will take 8 bytes per record, and require + sorting at the end. + + So instead, we record each offset in a bitmap such a way that + recording it twice will cancel out. Since each offset should appear + exactly twice, the bitmap should be zero at the end. + + The approach was inspired by Bloom Filters (see Wikipedia). For + each value, we flip K bits in a bitmap of size N. The number of + distinct arrangements is: + + N! / (K! * (N-K)!) + + Of course, not all arrangements are actually distinct, but testing + shows this formula to be close enough. + + So, if K == 8 and N == 256, the probability of two things flipping the same + bits is 1 in 409,663,695,276,000. + + Given that ldb uses a hash size of 10000, using 32 bytes per hash chain + (320k) seems reasonable. +*/ +#define NUM_HASHES 8 +#define BITMAP_BITS 256 + +static void bit_flip(unsigned char bits[], unsigned int idx) +{ + bits[idx / CHAR_BIT] ^= (1 << (idx % CHAR_BIT)); +} + +/* We record offsets in a bitmap for the particular chain it should be in. */ +static void record_offset(unsigned char bits[], tdb_off_t off) +{ + uint32_t h1 = off, h2 = 0; + unsigned int i; + + /* We get two good hash values out of jhash2, so we use both. Then + * we keep going to produce further hash values. */ + for (i = 0; i < NUM_HASHES / 2; i++) { + hash(off, &h1, &h2); + bit_flip(bits, h1 % BITMAP_BITS); + bit_flip(bits, h2 % BITMAP_BITS); + h2++; + } +} + +/* Check that an in-use record is valid. */ +static bool tdb_check_used_record(struct tdb_context *tdb, + tdb_off_t off, + const struct tdb_record *rec, + unsigned char **hashes, + int (*check)(TDB_DATA, TDB_DATA, void *), + void *private_data) +{ + TDB_DATA key, data; + + if (!tdb_check_record(tdb, off, rec)) + return false; + + /* key + data + tailer must fit in record */ + if (rec->key_len + rec->data_len + sizeof(tdb_off_t) > rec->rec_len) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Record offset %d too short for contents\n", off)); + return false; + } + + key = get_bytes(tdb, off + sizeof(*rec), rec->key_len); + if (!key.dptr) + return false; + + if (tdb->hash_fn(&key) != rec->full_hash) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Record offset %d has incorrect hash\n", off)); + goto fail_put_key; + } + + /* Mark this offset as a known value for this hash bucket. */ + record_offset(hashes[BUCKET(rec->full_hash)+1], off); + /* And similarly if the next pointer is valid. */ + if (rec->next) + record_offset(hashes[BUCKET(rec->full_hash)+1], rec->next); + + /* If they supply a check function and this record isn't dead, + get data and feed it. */ + if (check && rec->magic != TDB_DEAD_MAGIC) { + data = get_bytes(tdb, off + sizeof(*rec) + rec->key_len, + rec->data_len); + if (!data.dptr) + goto fail_put_key; + + if (check(key, data, private_data) == -1) + goto fail_put_data; + put_bytes(tdb, data); + } + + put_bytes(tdb, key); + return true; + +fail_put_data: + put_bytes(tdb, data); +fail_put_key: + put_bytes(tdb, key); + return false; +} + +/* Check that an unused record is valid. */ +static bool tdb_check_free_record(struct tdb_context *tdb, + tdb_off_t off, + const struct tdb_record *rec, + unsigned char **hashes) +{ + if (!tdb_check_record(tdb, off, rec)) + return false; + + /* Mark this offset as a known value for the free list. */ + record_offset(hashes[0], off); + /* And similarly if the next pointer is valid. */ + if (rec->next) + record_offset(hashes[0], rec->next); + return true; +} + +int tdb_check(struct tdb_context *tdb, + int (*check)(TDB_DATA key, TDB_DATA data, void *private_data), + void *private_data) +{ + unsigned int h; + unsigned char **hashes; + tdb_off_t off, recovery_start; + struct tdb_record rec; + bool found_recovery = false; + + if (tdb_lockall(tdb) == -1) + return -1; + + /* Make sure we know true size of the underlying file. */ + tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); + + /* Header must be OK: also gets us the recovery ptr, if any. */ + if (!tdb_check_header(tdb, &recovery_start)) + goto unlock; + + /* We should have the whole header, too. */ + if (tdb->map_size < TDB_DATA_START(tdb->header.hash_size)) { + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_ERROR, "File too short for hashes\n")); + goto unlock; + } + + /* One big malloc: pointers then bit arrays. */ + hashes = (unsigned char **)calloc( + 1, sizeof(hashes[0]) * (1+tdb->header.hash_size) + + BITMAP_BITS / CHAR_BIT * (1+tdb->header.hash_size)); + if (!hashes) { + tdb->ecode = TDB_ERR_OOM; + goto unlock; + } + + /* Initialize pointers */ + hashes[0] = (unsigned char *)(&hashes[1+tdb->header.hash_size]); + for (h = 1; h < 1+tdb->header.hash_size; h++) + hashes[h] = hashes[h-1] + BITMAP_BITS / CHAR_BIT; + + /* Freelist and hash headers are all in a row: read them. */ + for (h = 0; h < 1+tdb->header.hash_size; h++) { + if (tdb_ofs_read(tdb, FREELIST_TOP + h*sizeof(tdb_off_t), + &off) == -1) + goto free; + if (off) + record_offset(hashes[h], off); + } + + /* For each record, read it in and check it's ok. */ + for (off = TDB_DATA_START(tdb->header.hash_size); + off < tdb->map_size; + off += sizeof(rec) + rec.rec_len) { + if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec), + DOCONV()) == -1) + goto free; + switch (rec.magic) { + case TDB_MAGIC: + case TDB_DEAD_MAGIC: + if (!tdb_check_used_record(tdb, off, &rec, hashes, + check, private_data)) + goto free; + break; + case TDB_FREE_MAGIC: + if (!tdb_check_free_record(tdb, off, &rec, hashes)) + goto free; + break; + case TDB_RECOVERY_MAGIC: + case 0: /* Used for invalid (or in-progress) recovery area. */ + if (recovery_start != off) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Unexpected recovery record at offset %d\n", + off)); + goto free; + } + found_recovery = true; + break; + default: + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Bad magic 0x%x at offset %d\n", + rec.magic, off)); + goto free; + } + } + + /* Now, hashes should all be empty: each record exists and is referred + * to by one other. */ + for (h = 0; h < 1+tdb->header.hash_size; h++) { + unsigned int i; + for (i = 0; i < BITMAP_BITS / CHAR_BIT; i++) { + if (hashes[h][i] != 0) { + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Hashes do not match records\n")); + goto free; + } + } + } + + /* We must have found recovery area if there was one. */ + if (recovery_start != 0 && !found_recovery) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, + "Expected %s recovery area, got %s\n", + recovery_start ? "a" : "no", + found_recovery ? "one" : "none")); + goto free; + } + + free(hashes); + tdb_unlockall(tdb); + return 0; + +free: + free(hashes); +unlock: + tdb_unlockall(tdb); + return -1; +} diff --git a/lib/tdb/common/dump.c b/lib/tdb/common/dump.c index d1c902ddfd..bdcbfab139 100644 --- a/lib/tdb/common/dump.c +++ b/lib/tdb/common/dump.c @@ -30,7 +30,7 @@ static tdb_off_t tdb_dump_record(struct tdb_context *tdb, int hash, tdb_off_t offset) { - struct list_struct rec; + struct tdb_record rec; tdb_off_t tailer_ofs, tailer; if (tdb->methods->tdb_read(tdb, offset, (char *)&rec, @@ -95,7 +95,7 @@ int tdb_printfreelist(struct tdb_context *tdb) int ret; long total_free = 0; tdb_off_t offset, rec_ptr; - struct list_struct rec; + struct tdb_record rec; if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0) return ret; diff --git a/lib/tdb/common/freelist.c b/lib/tdb/common/freelist.c index 2f2a4c379b..8113b54951 100644 --- a/lib/tdb/common/freelist.c +++ b/lib/tdb/common/freelist.c @@ -34,7 +34,7 @@ #define USE_RIGHT_MERGES 0 /* read a freelist record and check for simple errors */ -int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec) +int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct tdb_record *rec) { if (tdb->methods->tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1) return -1; @@ -54,7 +54,7 @@ int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read bad magic 0x%x at offset=%d\n", rec->magic, off)); - return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + return -1; } if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0) return -1; @@ -78,15 +78,16 @@ static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_ /* Follow chain (next offset is at start of record) */ last_ptr = i; } + tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off)); - return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + return -1; } #endif /* update a record tailer (must hold allocation lock) */ static int update_tailer(struct tdb_context *tdb, tdb_off_t offset, - const struct list_struct *rec) + const struct tdb_record *rec) { tdb_off_t totalsize; @@ -98,7 +99,7 @@ static int update_tailer(struct tdb_context *tdb, tdb_off_t offset, /* Add an element into the freelist. Merge adjacent records if neccessary. */ -int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec) { /* Allocation and tailer lock */ if (tdb_lock(tdb, -1, F_WRLCK) != 0) @@ -114,7 +115,7 @@ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) /* Look right first (I'm an Australian, dammit) */ if (offset + sizeof(*rec) + rec->rec_len + sizeof(*rec) <= tdb->map_size) { tdb_off_t right = offset + sizeof(*rec) + rec->rec_len; - struct list_struct r; + struct tdb_record r; if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right read failed at %u\n", right)); @@ -140,7 +141,7 @@ left: /* Look left */ if (offset - sizeof(tdb_off_t) > TDB_DATA_START(tdb->header.hash_size)) { tdb_off_t left = offset - sizeof(tdb_off_t); - struct list_struct l; + struct tdb_record l; tdb_off_t leftsize; /* Read in tailer and jump back to header */ @@ -219,9 +220,9 @@ update: */ static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, tdb_len_t length, tdb_off_t rec_ptr, - struct list_struct *rec, tdb_off_t last_ptr) + struct tdb_record *rec, tdb_off_t last_ptr) { -#define MIN_REC_SIZE (sizeof(struct list_struct) + sizeof(tdb_off_t) + 8) +#define MIN_REC_SIZE (sizeof(struct tdb_record) + sizeof(tdb_off_t) + 8) if (rec->rec_len < length + MIN_REC_SIZE) { /* we have to grab the whole record */ @@ -267,12 +268,12 @@ static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, } /* allocate some space from the free list. The offset returned points - to a unconnected list_struct within the database with room for at + to a unconnected tdb_record within the database with room for at least length bytes of total data 0 is returned if the space could not be allocated */ -tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec) +tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct tdb_record *rec) { tdb_off_t rec_ptr, last_ptr, newrec_ptr; struct { @@ -284,6 +285,9 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st if (tdb_lock(tdb, -1, F_WRLCK) == -1) return 0; + /* over-allocate to reduce fragmentation */ + length *= 1.25; + /* Extra bytes required for tailer */ length += sizeof(tdb_off_t); length = TDB_ALIGN(length, TDB_ALIGNMENT); diff --git a/lib/tdb/common/freelistcheck.c b/lib/tdb/common/freelistcheck.c index efc050df9c..8d1ebabe04 100644 --- a/lib/tdb/common/freelistcheck.c +++ b/lib/tdb/common/freelistcheck.c @@ -46,7 +46,7 @@ static int seen_insert(struct tdb_context *mem_tdb, tdb_off_t rec_ptr) int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries) { struct tdb_context *mem_tdb = NULL; - struct list_struct rec; + struct tdb_record rec; tdb_off_t rec_ptr, last_ptr; int ret = -1; @@ -67,7 +67,8 @@ int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries) /* Store the FREELIST_TOP record. */ if (seen_insert(mem_tdb, last_ptr) == -1) { - ret = TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + tdb->ecode = TDB_ERR_CORRUPT; + ret = -1; goto fail; } @@ -83,7 +84,8 @@ int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries) be corrupt. */ if (seen_insert(mem_tdb, rec_ptr)) { - ret = TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + tdb->ecode = TDB_ERR_CORRUPT; + ret = -1; goto fail; } diff --git a/lib/tdb/common/io.c b/lib/tdb/common/io.c index 661f761489..d549715f83 100644 --- a/lib/tdb/common/io.c +++ b/lib/tdb/common/io.c @@ -45,11 +45,12 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe) TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n", (int)len, (int)tdb->map_size)); } - return TDB_ERRCODE(TDB_ERR_IO, -1); + return -1; } if (fstat(tdb->fd, &st) == -1) { - return TDB_ERRCODE(TDB_ERR_IO, -1); + tdb->ecode = TDB_ERR_IO; + return -1; } if (st.st_size < (size_t)len) { @@ -59,12 +60,14 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe) TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n", (int)len, (int)st.st_size)); } - return TDB_ERRCODE(TDB_ERR_IO, -1); + return -1; } /* Unmap, update size, remap */ - if (tdb_munmap(tdb) == -1) - return TDB_ERRCODE(TDB_ERR_IO, -1); + if (tdb_munmap(tdb) == -1) { + tdb->ecode = TDB_ERR_IO; + return -1; + } tdb->map_size = st.st_size; tdb_mmap(tdb); return 0; @@ -92,27 +95,27 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off, ssize_t written = pwrite(tdb->fd, buf, len, off); if ((written != (ssize_t)len) && (written != -1)) { /* try once more */ + tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only " "%d of %d bytes at %d, trying once more\n", (int)written, len, off)); - errno = ENOSPC; - written = pwrite(tdb->fd, (const void *)((const char *)buf+written), + written = pwrite(tdb->fd, (const char *)buf+written, len-written, off+written); } if (written == -1) { - /* Ensure ecode is set for log fn. */ - tdb->ecode = TDB_ERR_IO; + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d " "len=%d (%s)\n", off, len, strerror(errno))); - return TDB_ERRCODE(TDB_ERR_IO, -1); + return -1; } else if (written != (ssize_t)len) { + tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to " "write %d bytes at %d in two attempts\n", len, off)); - errno = ENOSPC; - return TDB_ERRCODE(TDB_ERR_IO, -1); - } + return -1; + } } return 0; } @@ -146,7 +149,7 @@ static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, "len=%d ret=%d (%s) map_size=%d\n", (int)off, (int)len, (int)ret, strerror(errno), (int)tdb->map_size)); - return TDB_ERRCODE(TDB_ERR_IO, -1); + return -1; } } if (cv) { @@ -295,7 +298,7 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad file and doing the mmap again if necessary */ int tdb_expand(struct tdb_context *tdb, tdb_off_t size) { - struct list_struct rec; + struct tdb_record rec; tdb_off_t offset, new_size; if (tdb_lock(tdb, -1, F_WRLCK) == -1) { @@ -383,16 +386,13 @@ unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len unsigned char *buf; /* some systems don't like zero length malloc */ - if (len == 0) { - len = 1; - } - if (!(buf = (unsigned char *)malloc(len))) { + if (!(buf = (unsigned char *)malloc(len ? len : 1))) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_OOM; TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n", len, strerror(errno))); - return TDB_ERRCODE(TDB_ERR_OOM, buf); + return NULL; } if (tdb->methods->tdb_read(tdb, offset, buf, len, 0) == -1) { SAFE_FREE(buf); @@ -436,7 +436,7 @@ int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, } /* read/write a record */ -int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec) { if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1) return -1; @@ -444,14 +444,14 @@ int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct * /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); - return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + return -1; } return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0); } -int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec) { - struct list_struct r = *rec; + struct tdb_record r = *rec; return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r)); } diff --git a/lib/tdb/common/lock.c b/lib/tdb/common/lock.c index f156c0fa7b..0984e516ea 100644 --- a/lib/tdb/common/lock.c +++ b/lib/tdb/common/lock.c @@ -75,16 +75,15 @@ int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, } while (ret == -1 && errno == EINTR); if (ret == -1) { + tdb->ecode = TDB_ERR_LOCK; /* Generic lock error. errno set by fcntl. * EAGAIN is an expected return from non-blocking * locks. */ if (!probe && lck_type != F_SETLK) { - /* Ensure error code is set for log fun to examine. */ - tdb->ecode = TDB_ERR_LOCK; TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n", tdb->fd, offset, rw_type, lck_type, (int)len)); } - return TDB_ERRCODE(TDB_ERR_LOCK, -1); + return -1; } return 0; } @@ -133,10 +132,12 @@ static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op) } if (tdb->global_lock.count) { - return TDB_ERRCODE(TDB_ERR_LOCK, -1); + tdb->ecode = TDB_ERR_LOCK; + return -1; } if (list < -1 || list >= (int)tdb->header.hash_size) { + tdb->ecode = TDB_ERR_LOCK; TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n", list, ltype)); return -1; @@ -228,7 +229,8 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype) } if (tdb->global_lock.count) { - return TDB_ERRCODE(TDB_ERR_LOCK, -1); + tdb->ecode = TDB_ERR_LOCK; + return -1; } if (tdb->flags & TDB_NOLOCK) @@ -301,16 +303,21 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype) */ int tdb_transaction_lock(struct tdb_context *tdb, int ltype) { - if (tdb->have_transaction_lock || tdb->global_lock.count) { + if (tdb->global_lock.count) { return 0; } + if (tdb->transaction_lock_count > 0) { + tdb->transaction_lock_count++; + return 0; + } + if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype, F_SETLKW, 0, 1) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } - tdb->have_transaction_lock = 1; + tdb->transaction_lock_count++; return 0; } @@ -320,12 +327,16 @@ int tdb_transaction_lock(struct tdb_context *tdb, int ltype) int tdb_transaction_unlock(struct tdb_context *tdb) { int ret; - if (!tdb->have_transaction_lock) { + if (tdb->global_lock.count) { + return 0; + } + if (tdb->transaction_lock_count > 1) { + tdb->transaction_lock_count--; return 0; } ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); if (ret == 0) { - tdb->have_transaction_lock = 0; + tdb->transaction_lock_count = 0; } return ret; } @@ -341,8 +352,10 @@ static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op) ltype &= ~TDB_MARK_LOCK; /* There are no locks on read-only dbs */ - if (tdb->read_only || tdb->traverse_read) - return TDB_ERRCODE(TDB_ERR_LOCK, -1); + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_LOCK; + return -1; + } if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) { tdb->global_lock.count++; @@ -351,12 +364,14 @@ static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op) if (tdb->global_lock.count) { /* a global lock of a different type exists */ - return TDB_ERRCODE(TDB_ERR_LOCK, -1); + tdb->ecode = TDB_ERR_LOCK; + return -1; } if (tdb->num_locks != 0) { /* can't combine global and chain locks */ - return TDB_ERRCODE(TDB_ERR_LOCK, -1); + tdb->ecode = TDB_ERR_LOCK; + return -1; } if (!mark_lock && @@ -385,11 +400,13 @@ static int _tdb_unlockall(struct tdb_context *tdb, int ltype) /* There are no locks on read-only dbs */ if (tdb->read_only || tdb->traverse_read) { - return TDB_ERRCODE(TDB_ERR_LOCK, -1); + tdb->ecode = TDB_ERR_LOCK; + return -1; } if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) { - return TDB_ERRCODE(TDB_ERR_LOCK, -1); + tdb->ecode = TDB_ERR_LOCK; + return -1; } if (tdb->global_lock.count > 1) { @@ -413,48 +430,58 @@ static int _tdb_unlockall(struct tdb_context *tdb, int ltype) /* lock entire database with write lock */ int tdb_lockall(struct tdb_context *tdb) { + tdb_trace(tdb, "tdb_lockall"); return _tdb_lockall(tdb, F_WRLCK, F_SETLKW); } /* lock entire database with write lock - mark only */ int tdb_lockall_mark(struct tdb_context *tdb) { + tdb_trace(tdb, "tdb_lockall_mark"); return _tdb_lockall(tdb, F_WRLCK | TDB_MARK_LOCK, F_SETLKW); } /* unlock entire database with write lock - unmark only */ int tdb_lockall_unmark(struct tdb_context *tdb) { + tdb_trace(tdb, "tdb_lockall_unmark"); return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK); } /* lock entire database with write lock - nonblocking varient */ int tdb_lockall_nonblock(struct tdb_context *tdb) { - return _tdb_lockall(tdb, F_WRLCK, F_SETLK); + int ret = _tdb_lockall(tdb, F_WRLCK, F_SETLK); + tdb_trace_ret(tdb, "tdb_lockall_nonblock", ret); + return ret; } /* unlock entire database with write lock */ int tdb_unlockall(struct tdb_context *tdb) { + tdb_trace(tdb, "tdb_unlockall"); return _tdb_unlockall(tdb, F_WRLCK); } /* lock entire database with read lock */ int tdb_lockall_read(struct tdb_context *tdb) { + tdb_trace(tdb, "tdb_lockall_read"); return _tdb_lockall(tdb, F_RDLCK, F_SETLKW); } /* lock entire database with read lock - nonblock varient */ int tdb_lockall_read_nonblock(struct tdb_context *tdb) { - return _tdb_lockall(tdb, F_RDLCK, F_SETLK); + int ret = _tdb_lockall(tdb, F_RDLCK, F_SETLK); + tdb_trace_ret(tdb, "tdb_lockall_read_nonblock", ret); + return ret; } /* unlock entire database with read lock */ int tdb_unlockall_read(struct tdb_context *tdb) { + tdb_trace(tdb, "tdb_unlockall_read"); return _tdb_unlockall(tdb, F_RDLCK); } @@ -462,7 +489,9 @@ int tdb_unlockall_read(struct tdb_context *tdb) contention - it cannot guarantee how many records will be locked */ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key) { - return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); + int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); + tdb_trace_1rec(tdb, "tdb_chainlock", key); + return ret; } /* lock/unlock one hash chain, non-blocking. This is meant to be used @@ -470,33 +499,43 @@ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key) locked */ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key) { - return tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); + int ret = tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); + tdb_trace_1rec_ret(tdb, "tdb_chainlock_nonblock", key, ret); + return ret; } /* mark a chain as locked without actually locking it. Warning! use with great caution! */ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key) { - return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); + int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); + tdb_trace_1rec(tdb, "tdb_chainlock_mark", key); + return ret; } /* unmark a chain as locked without actually locking it. Warning! use with great caution! */ int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key) { + tdb_trace_1rec(tdb, "tdb_chainlock_unmark", key); return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); } int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) { + tdb_trace_1rec(tdb, "tdb_chainunlock", key); return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); } int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key) { - return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); + int ret; + ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); + tdb_trace_1rec(tdb, "tdb_chainlock_read", key); + return ret; } int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key) { + tdb_trace_1rec(tdb, "tdb_chainunlock_read", key); return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); } diff --git a/lib/tdb/common/open.c b/lib/tdb/common/open.c index 49b8e85f12..4d4f95a3da 100644 --- a/lib/tdb/common/open.c +++ b/lib/tdb/common/open.c @@ -55,8 +55,10 @@ static int tdb_new_database(struct tdb_context *tdb, int hash_size) /* We make it up in memory, then write it out if not internal */ size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t); - if (!(newdb = (struct tdb_header *)calloc(size, 1))) - return TDB_ERRCODE(TDB_ERR_OOM, -1); + if (!(newdb = (struct tdb_header *)calloc(size, 1))) { + tdb->ecode = TDB_ERR_OOM; + return -1; + } /* Fill in the header */ newdb->version = TDB_VERSION; @@ -161,6 +163,9 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, } tdb_io_init(tdb); tdb->fd = -1; +#ifdef TDB_TRACE + tdb->tracefd = -1; +#endif tdb->name = NULL; tdb->map_ptr = NULL; tdb->flags = tdb_flags; @@ -197,6 +202,23 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, tdb->flags &= ~TDB_CLEAR_IF_FIRST; } + if ((tdb->flags & TDB_ALLOW_NESTING) && + (tdb->flags & TDB_DISALLOW_NESTING)) { + tdb->ecode = TDB_ERR_NESTING; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " + "allow_nesting and disallow_nesting are not allowed together!")); + errno = EINVAL; + goto fail; + } + + /* + * TDB_ALLOW_NESTING is the default behavior. + * Note: this may change in future versions! + */ + if (!(tdb->flags & TDB_DISALLOW_NESTING)) { + tdb->flags |= TDB_ALLOW_NESTING; + } + /* internal databases don't mmap or lock, and start off cleared */ if (tdb->flags & TDB_INTERNAL) { tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP); @@ -240,17 +262,19 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, errno = 0; if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header) - || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0 - || (tdb->header.version != TDB_VERSION - && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) { - /* its not a valid database - possibly initialise it */ + || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0) { if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { if (errno == 0) { - errno = EIO; /* ie bad format or something */ + errno = EIO; /* ie bad format or something */ } goto fail; } rev = (tdb->flags & TDB_CONVERT); + } else if (tdb->header.version != TDB_VERSION + && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION)))) { + /* wrong version */ + errno = EIO; + goto fail; } vp = (unsigned char *)&tdb->header.version; vertest = (((uint32_t)vp[0]) << 24) | (((uint32_t)vp[1]) << 16) | @@ -313,6 +337,22 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, goto fail; } +#ifdef TDB_TRACE + { + char tracefile[strlen(name) + 32]; + + snprintf(tracefile, sizeof(tracefile), + "%s.trace.%li", name, (long)getpid()); + tdb->tracefd = open(tracefile, O_WRONLY|O_CREAT|O_EXCL, 0600); + if (tdb->tracefd >= 0) { + tdb_enable_seqnum(tdb); + tdb_trace_open(tdb, "tdb_open", hash_size, tdb_flags, + open_flags); + } else + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to open trace file %s!\n", tracefile)); + } +#endif + internal: /* Internal (memory-only) databases skip all the code above to * do with disk files, and resume here by releasing their @@ -328,7 +368,10 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, if (!tdb) return NULL; - + +#ifdef TDB_TRACE + close(tdb->tracefd); +#endif if (tdb->map_ptr) { if (tdb->flags & TDB_INTERNAL) SAFE_FREE(tdb->map_ptr); @@ -364,8 +407,9 @@ int tdb_close(struct tdb_context *tdb) struct tdb_context **i; int ret = 0; + tdb_trace(tdb, "tdb_close"); if (tdb->transaction) { - tdb_transaction_cancel(tdb); + _tdb_transaction_cancel(tdb); } if (tdb->map_ptr) { @@ -375,8 +419,10 @@ int tdb_close(struct tdb_context *tdb) tdb_munmap(tdb); } SAFE_FREE(tdb->name); - if (tdb->fd != -1) + if (tdb->fd != -1) { ret = close(tdb->fd); + tdb->fd = -1; + } SAFE_FREE(tdb->lockrecs); /* Remove from contexts list */ @@ -387,6 +433,9 @@ int tdb_close(struct tdb_context *tdb) } } +#ifdef TDB_TRACE + close(tdb->tracefd); +#endif memset(tdb, 0, sizeof(*tdb)); SAFE_FREE(tdb); @@ -405,11 +454,12 @@ void *tdb_get_logging_private(struct tdb_context *tdb) return tdb->log.log_private; } -/* reopen a tdb - this can be used after a fork to ensure that we have an independent - seek pointer from our parent and to re-establish locks */ -int tdb_reopen(struct tdb_context *tdb) +static int tdb_reopen_internal(struct tdb_context *tdb, bool active_lock) { +#if !defined(LIBREPLACE_PREAD_NOT_REPLACED) || \ + !defined(LIBREPLACE_PWRITE_NOT_REPLACED) struct stat st; +#endif if (tdb->flags & TDB_INTERNAL) { return 0; /* Nothing to do. */ @@ -450,7 +500,7 @@ int tdb_reopen(struct tdb_context *tdb) tdb_mmap(tdb); #endif /* fake pread or pwrite */ - if ((tdb->flags & TDB_CLEAR_IF_FIRST) && + if (active_lock && (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n")); goto fail; @@ -463,12 +513,21 @@ fail: return -1; } +/* reopen a tdb - this can be used after a fork to ensure that we have an independent + seek pointer from our parent and to re-establish locks */ +int tdb_reopen(struct tdb_context *tdb) +{ + return tdb_reopen_internal(tdb, tdb->flags & TDB_CLEAR_IF_FIRST); +} + /* reopen all tdb's */ int tdb_reopen_all(int parent_longlived) { struct tdb_context *tdb; for (tdb=tdbs; tdb; tdb = tdb->next) { + bool active_lock = (tdb->flags & TDB_CLEAR_IF_FIRST); + /* * If the parent is longlived (ie. a * parent daemon architecture), we know @@ -482,10 +541,10 @@ int tdb_reopen_all(int parent_longlived) */ if (parent_longlived) { /* Ensure no clear-if-first. */ - tdb->flags &= ~TDB_CLEAR_IF_FIRST; + active_lock = false; } - if (tdb_reopen(tdb) != 0) + if (tdb_reopen_internal(tdb, active_lock) != 0) return -1; } diff --git a/lib/tdb/common/tdb.c b/lib/tdb/common/tdb.c index b59bb1571c..d2688def04 100644 --- a/lib/tdb/common/tdb.c +++ b/lib/tdb/common/tdb.c @@ -76,7 +76,7 @@ static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data) /* Returns 0 on fail. On success, return offset of record, and fills in rec */ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, - struct list_struct *r) + struct tdb_record *r) { tdb_off_t rec_ptr; @@ -98,17 +98,19 @@ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, } /* detect tight infinite loop */ if (rec_ptr == r->next) { + tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_find: loop detected.\n")); - return TDB_ERRCODE(TDB_ERR_CORRUPT, 0); + return 0; } rec_ptr = r->next; } - return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); + tdb->ecode = TDB_ERR_NOEXIST; + return 0; } /* As tdb_find, but if you succeed, keep the lock */ tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, int locktype, - struct list_struct *rec) + struct tdb_record *rec) { uint32_t rec_ptr; @@ -119,6 +121,7 @@ tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t has return rec_ptr; } +static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key); /* update an entry in place - this only works if the new data size is <= the old data size and the key exists. @@ -126,13 +129,32 @@ tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t has */ static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, TDB_DATA dbuf) { - struct list_struct rec; + struct tdb_record rec; tdb_off_t rec_ptr; /* find entry */ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) return -1; + /* it could be an exact duplicate of what is there - this is + * surprisingly common (eg. with a ldb re-index). */ + if (rec.key_len == key.dsize && + rec.data_len == dbuf.dsize && + rec.full_hash == hash) { + TDB_DATA data = _tdb_fetch(tdb, key); + if (data.dsize == dbuf.dsize && + memcmp(data.dptr, dbuf.dptr, data.dsize) == 0) { + if (data.dptr) { + free(data.dptr); + } + return 0; + } + if (data.dptr) { + free(data.dptr); + } + } + + /* must be long enough key, data and tailer */ if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off_t)) { tdb->ecode = TDB_SUCCESS; /* Not really an error */ @@ -158,10 +180,10 @@ static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, * then the TDB_DATA will have zero length but * a non-zero pointer */ -TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key) +static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key) { tdb_off_t rec_ptr; - struct list_struct rec; + struct tdb_record rec; TDB_DATA ret; uint32_t hash; @@ -177,6 +199,14 @@ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key) return ret; } +TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key) +{ + TDB_DATA ret = _tdb_fetch(tdb, key); + + tdb_trace_1rec_retrec(tdb, "tdb_fetch", key, ret); + return ret; +} + /* * Find an entry in the database and hand the record's data to a parsing * function. The parsing function is executed under the chain read lock, so it @@ -199,7 +229,7 @@ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, void *private_data) { tdb_off_t rec_ptr; - struct list_struct rec; + struct tdb_record rec; int ret; uint32_t hash; @@ -207,8 +237,11 @@ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, hash = tdb->hash_fn(&key); if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) { - return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); + tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, -1); + tdb->ecode = TDB_ERR_NOEXIST; + return 0; } + tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, 0); ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len, rec.data_len, parser, private_data); @@ -226,7 +259,7 @@ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, */ static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash) { - struct list_struct rec; + struct tdb_record rec; if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0) return 0; @@ -237,14 +270,18 @@ static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash) int tdb_exists(struct tdb_context *tdb, TDB_DATA key) { uint32_t hash = tdb->hash_fn(&key); - return tdb_exists_hash(tdb, key, hash); + int ret; + + ret = tdb_exists_hash(tdb, key, hash); + tdb_trace_1rec_ret(tdb, "tdb_exists", key, ret); + return ret; } /* actually delete an entry in the database given the offset */ -int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec) +int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct tdb_record *rec) { tdb_off_t last_ptr, i; - struct list_struct lastrec; + struct tdb_record lastrec; if (tdb->read_only || tdb->traverse_read) return -1; @@ -280,7 +317,7 @@ static int tdb_count_dead(struct tdb_context *tdb, uint32_t hash) { int res = 0; tdb_off_t rec_ptr; - struct list_struct rec; + struct tdb_record rec; /* read in the hash top */ if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) @@ -304,7 +341,7 @@ static int tdb_count_dead(struct tdb_context *tdb, uint32_t hash) static int tdb_purge_dead(struct tdb_context *tdb, uint32_t hash) { int res = -1; - struct list_struct rec; + struct tdb_record rec; tdb_off_t rec_ptr; if (tdb_lock(tdb, -1, F_WRLCK) == -1) { @@ -340,7 +377,7 @@ static int tdb_purge_dead(struct tdb_context *tdb, uint32_t hash) static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash) { tdb_off_t rec_ptr; - struct list_struct rec; + struct tdb_record rec; int ret; if (tdb->max_dead_records != 0) { @@ -392,14 +429,18 @@ static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash) int tdb_delete(struct tdb_context *tdb, TDB_DATA key) { uint32_t hash = tdb->hash_fn(&key); - return tdb_delete_hash(tdb, key, hash); + int ret; + + ret = tdb_delete_hash(tdb, key, hash); + tdb_trace_1rec_ret(tdb, "tdb_delete", key, ret); + return ret; } /* * See if we have a dead record around with enough space */ static tdb_off_t tdb_find_dead(struct tdb_context *tdb, uint32_t hash, - struct list_struct *r, tdb_len_t length) + struct tdb_record *r, tdb_len_t length) { tdb_off_t rec_ptr; @@ -424,29 +465,14 @@ static tdb_off_t tdb_find_dead(struct tdb_context *tdb, uint32_t hash, return 0; } -/* store an element in the database, replacing any existing element - with the same key - - return 0 on success, -1 on failure -*/ -int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) +static int _tdb_store(struct tdb_context *tdb, TDB_DATA key, + TDB_DATA dbuf, int flag, uint32_t hash) { - struct list_struct rec; - uint32_t hash; + struct tdb_record rec; tdb_off_t rec_ptr; char *p = NULL; int ret = -1; - if (tdb->read_only || tdb->traverse_read) { - tdb->ecode = TDB_ERR_RDONLY; - return -1; - } - - /* find which hash bucket it is in */ - hash = tdb->hash_fn(&key); - if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) - return -1; - /* check for it existing, on insert. */ if (flag == TDB_INSERT) { if (tdb_exists_hash(tdb, key, hash)) { @@ -562,10 +588,35 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) } SAFE_FREE(p); - tdb_unlock(tdb, BUCKET(hash), F_WRLCK); return ret; } +/* store an element in the database, replacing any existing element + with the same key + + return 0 on success, -1 on failure +*/ +int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) +{ + uint32_t hash; + int ret; + + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_RDONLY; + tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag, -1); + return -1; + } + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + ret = _tdb_store(tdb, key, dbuf, flag, hash); + tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag, ret); + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + return ret; +} /* Append to an entry. Create if not exist. */ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf) @@ -579,13 +630,18 @@ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf) if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) return -1; - dbuf = tdb_fetch(tdb, key); + dbuf = _tdb_fetch(tdb, key); if (dbuf.dptr == NULL) { dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize); } else { - unsigned char *new_dptr = (unsigned char *)realloc(dbuf.dptr, - dbuf.dsize + new_dbuf.dsize); + unsigned int new_len = dbuf.dsize + new_dbuf.dsize; + unsigned char *new_dptr; + + /* realloc '0' is special: don't do that. */ + if (new_len == 0) + new_len = 1; + new_dptr = (unsigned char *)realloc(dbuf.dptr, new_len); if (new_dptr == NULL) { free(dbuf.dptr); } @@ -600,7 +656,8 @@ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf) memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize); dbuf.dsize += new_dbuf.dsize; - ret = tdb_store(tdb, key, dbuf, 0); + ret = _tdb_store(tdb, key, dbuf, 0, hash); + tdb_trace_2rec_retrec(tdb, "tdb_append", key, new_dbuf, dbuf); failed: tdb_unlock(tdb, BUCKET(hash), F_WRLCK); @@ -673,11 +730,41 @@ int tdb_get_flags(struct tdb_context *tdb) void tdb_add_flags(struct tdb_context *tdb, unsigned flags) { + if ((flags & TDB_ALLOW_NESTING) && + (flags & TDB_DISALLOW_NESTING)) { + tdb->ecode = TDB_ERR_NESTING; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_add_flags: " + "allow_nesting and disallow_nesting are not allowed together!")); + return; + } + + if (flags & TDB_ALLOW_NESTING) { + tdb->flags &= ~TDB_DISALLOW_NESTING; + } + if (flags & TDB_DISALLOW_NESTING) { + tdb->flags &= ~TDB_ALLOW_NESTING; + } + tdb->flags |= flags; } void tdb_remove_flags(struct tdb_context *tdb, unsigned flags) { + if ((flags & TDB_ALLOW_NESTING) && + (flags & TDB_DISALLOW_NESTING)) { + tdb->ecode = TDB_ERR_NESTING; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_remove_flags: " + "allow_nesting and disallow_nesting are not allowed together!")); + return; + } + + if (flags & TDB_ALLOW_NESTING) { + tdb->flags |= TDB_DISALLOW_NESTING; + } + if (flags & TDB_DISALLOW_NESTING) { + tdb->flags |= TDB_ALLOW_NESTING; + } + tdb->flags &= ~flags; } @@ -697,7 +784,7 @@ void tdb_enable_seqnum(struct tdb_context *tdb) */ static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t length) { - struct list_struct rec; + struct tdb_record rec; if (length <= sizeof(rec)) { /* the region is not worth adding */ return 0; @@ -734,6 +821,8 @@ int tdb_wipe_all(struct tdb_context *tdb) return -1; } + tdb_trace(tdb, "tdb_wipe_all"); + /* see if the tdb has a recovery area, and remember its size if so. We don't want to lose this as otherwise each tdb_wipe_all() in a transaction will increase the size of @@ -744,7 +833,7 @@ int tdb_wipe_all(struct tdb_context *tdb) } if (recovery_head != 0) { - struct list_struct rec; + struct tdb_record rec; if (tdb->methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_wipe_all: failed to read recovery record\n")); return -1; @@ -814,9 +903,9 @@ struct traverse_state { /* traverse function for repacking */ -static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private) +static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private_data) { - struct traverse_state *state = (struct traverse_state *)private; + struct traverse_state *state = (struct traverse_state *)private_data; if (tdb_store(state->dest_db, key, data, TDB_INSERT) != 0) { state->error = true; return -1; @@ -832,6 +921,8 @@ int tdb_repack(struct tdb_context *tdb) struct tdb_context *tmp_db; struct traverse_state state; + tdb_trace(tdb, "tdb_repack"); + if (tdb_transaction_start(tdb) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to start transaction\n")); return -1; @@ -894,3 +985,156 @@ int tdb_repack(struct tdb_context *tdb) return 0; } + +#ifdef TDB_TRACE +static void tdb_trace_write(struct tdb_context *tdb, const char *str) +{ + if (write(tdb->tracefd, str, strlen(str)) != strlen(str)) { + close(tdb->tracefd); + tdb->tracefd = -1; + } +} + +static void tdb_trace_start(struct tdb_context *tdb) +{ + tdb_off_t seqnum=0; + char msg[sizeof(tdb_off_t) * 4 + 1]; + + tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum); + snprintf(msg, sizeof(msg), "%u ", seqnum); + tdb_trace_write(tdb, msg); +} + +static void tdb_trace_end(struct tdb_context *tdb) +{ + tdb_trace_write(tdb, "\n"); +} + +static void tdb_trace_end_ret(struct tdb_context *tdb, int ret) +{ + char msg[sizeof(ret) * 4 + 4]; + snprintf(msg, sizeof(msg), " = %i\n", ret); + tdb_trace_write(tdb, msg); +} + +static void tdb_trace_record(struct tdb_context *tdb, TDB_DATA rec) +{ + char msg[20 + rec.dsize*2], *p; + unsigned int i; + + /* We differentiate zero-length records from non-existent ones. */ + if (rec.dptr == NULL) { + tdb_trace_write(tdb, " NULL"); + return; + } + + /* snprintf here is purely cargo-cult programming. */ + p = msg; + p += snprintf(p, sizeof(msg), " %zu:", rec.dsize); + for (i = 0; i < rec.dsize; i++) + p += snprintf(p, 2, "%02x", rec.dptr[i]); + + tdb_trace_write(tdb, msg); +} + +void tdb_trace(struct tdb_context *tdb, const char *op) +{ + tdb_trace_start(tdb); + tdb_trace_write(tdb, op); + tdb_trace_end(tdb); +} + +void tdb_trace_seqnum(struct tdb_context *tdb, uint32_t seqnum, const char *op) +{ + char msg[sizeof(tdb_off_t) * 4 + 1]; + + snprintf(msg, sizeof(msg), "%u ", seqnum); + tdb_trace_write(tdb, msg); + tdb_trace_write(tdb, op); + tdb_trace_end(tdb); +} + +void tdb_trace_open(struct tdb_context *tdb, const char *op, + unsigned hash_size, unsigned tdb_flags, unsigned open_flags) +{ + char msg[128]; + + snprintf(msg, sizeof(msg), + "%s %u 0x%x 0x%x", op, hash_size, tdb_flags, open_flags); + tdb_trace_start(tdb); + tdb_trace_write(tdb, msg); + tdb_trace_end(tdb); +} + +void tdb_trace_ret(struct tdb_context *tdb, const char *op, int ret) +{ + tdb_trace_start(tdb); + tdb_trace_write(tdb, op); + tdb_trace_end_ret(tdb, ret); +} + +void tdb_trace_retrec(struct tdb_context *tdb, const char *op, TDB_DATA ret) +{ + tdb_trace_start(tdb); + tdb_trace_write(tdb, op); + tdb_trace_write(tdb, " ="); + tdb_trace_record(tdb, ret); + tdb_trace_end(tdb); +} + +void tdb_trace_1rec(struct tdb_context *tdb, const char *op, + TDB_DATA rec) +{ + tdb_trace_start(tdb); + tdb_trace_write(tdb, op); + tdb_trace_record(tdb, rec); + tdb_trace_end(tdb); +} + +void tdb_trace_1rec_ret(struct tdb_context *tdb, const char *op, + TDB_DATA rec, int ret) +{ + tdb_trace_start(tdb); + tdb_trace_write(tdb, op); + tdb_trace_record(tdb, rec); + tdb_trace_end_ret(tdb, ret); +} + +void tdb_trace_1rec_retrec(struct tdb_context *tdb, const char *op, + TDB_DATA rec, TDB_DATA ret) +{ + tdb_trace_start(tdb); + tdb_trace_write(tdb, op); + tdb_trace_record(tdb, rec); + tdb_trace_write(tdb, " ="); + tdb_trace_record(tdb, ret); + tdb_trace_end(tdb); +} + +void tdb_trace_2rec_flag_ret(struct tdb_context *tdb, const char *op, + TDB_DATA rec1, TDB_DATA rec2, unsigned flag, + int ret) +{ + char msg[1 + sizeof(ret) * 4]; + + snprintf(msg, sizeof(msg), " %#x", flag); + tdb_trace_start(tdb); + tdb_trace_write(tdb, op); + tdb_trace_record(tdb, rec1); + tdb_trace_record(tdb, rec2); + tdb_trace_write(tdb, msg); + tdb_trace_end_ret(tdb, ret); +} + +void tdb_trace_2rec_retrec(struct tdb_context *tdb, const char *op, + TDB_DATA rec1, TDB_DATA rec2, TDB_DATA ret) +{ + tdb_trace_start(tdb); + tdb_trace_write(tdb, op); + tdb_trace_record(tdb, rec1); + tdb_trace_record(tdb, rec2); + tdb_trace_write(tdb, " ="); + tdb_trace_record(tdb, ret); + tdb_trace_end(tdb); +} +#endif diff --git a/lib/tdb/common/tdb_private.h b/lib/tdb/common/tdb_private.h index ffac89ff0e..be9be72b15 100644 --- a/lib/tdb/common/tdb_private.h +++ b/lib/tdb/common/tdb_private.h @@ -31,6 +31,7 @@ #include "system/wait.h" #include "tdb.h" +/* #define TDB_TRACE 1 */ #ifndef HAVE_GETPAGESIZE #define getpagesize() 0x2000 #endif @@ -68,6 +69,37 @@ typedef uint32_t tdb_off_t; * argument. */ #define TDB_LOG(x) tdb->log.log_fn x +#ifdef TDB_TRACE +void tdb_trace(struct tdb_context *tdb, const char *op); +void tdb_trace_seqnum(struct tdb_context *tdb, uint32_t seqnum, const char *op); +void tdb_trace_open(struct tdb_context *tdb, const char *op, + unsigned hash_size, unsigned tdb_flags, unsigned open_flags); +void tdb_trace_ret(struct tdb_context *tdb, const char *op, int ret); +void tdb_trace_retrec(struct tdb_context *tdb, const char *op, TDB_DATA ret); +void tdb_trace_1rec(struct tdb_context *tdb, const char *op, + TDB_DATA rec); +void tdb_trace_1rec_ret(struct tdb_context *tdb, const char *op, + TDB_DATA rec, int ret); +void tdb_trace_1rec_retrec(struct tdb_context *tdb, const char *op, + TDB_DATA rec, TDB_DATA ret); +void tdb_trace_2rec_flag_ret(struct tdb_context *tdb, const char *op, + TDB_DATA rec1, TDB_DATA rec2, unsigned flag, + int ret); +void tdb_trace_2rec_retrec(struct tdb_context *tdb, const char *op, + TDB_DATA rec1, TDB_DATA rec2, TDB_DATA ret); +#else +#define tdb_trace(tdb, op) +#define tdb_trace_seqnum(tdb, seqnum, op) +#define tdb_trace_open(tdb, op, hash_size, tdb_flags, open_flags) +#define tdb_trace_ret(tdb, op, ret) +#define tdb_trace_retrec(tdb, op, ret) +#define tdb_trace_1rec(tdb, op, rec) +#define tdb_trace_1rec_ret(tdb, op, rec, ret) +#define tdb_trace_1rec_retrec(tdb, op, rec, ret) +#define tdb_trace_2rec_flag_ret(tdb, op, rec1, rec2, flag, ret) +#define tdb_trace_2rec_retrec(tdb, op, rec1, rec2, ret) +#endif /* !TDB_TRACE */ + /* lock offsets */ #define GLOBAL_LOCK 0 #define ACTIVE_LOCK 4 @@ -84,9 +116,9 @@ typedef uint32_t tdb_off_t; #define CONVERT(x) (DOCONV() ? tdb_convert(&x, sizeof(x)) : &x) -/* the body of the database is made of one list_struct for the free space +/* the body of the database is made of one tdb_record for the free space plus a separate data list for each hash value */ -struct list_struct { +struct tdb_record { tdb_off_t next; /* offset of the next record in the list */ tdb_len_t rec_len; /* total byte length of record */ tdb_len_t key_len; /* byte length of key */ @@ -166,7 +198,10 @@ struct tdb_context { struct tdb_transaction *transaction; int page_size; int max_dead_records; - bool have_transaction_lock; + int transaction_lock_count; +#ifdef TDB_TRACE + int tracefd; +#endif volatile sig_atomic_t *interrupt_sig_ptr; }; @@ -188,15 +223,16 @@ int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off); int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); void *tdb_convert(void *buf, uint32_t size); -int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); -tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec); +int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec); +tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct tdb_record *rec); int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off); int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off); -int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); -int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); -int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec); +int _tdb_transaction_cancel(struct tdb_context *tdb); +int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec); +int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec); +int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct tdb_record *rec); unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len); int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, tdb_off_t offset, tdb_len_t len, @@ -204,10 +240,10 @@ int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, void *private_data), void *private_data); tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, int locktype, - struct list_struct *rec); + struct tdb_record *rec); void tdb_io_init(struct tdb_context *tdb); int tdb_expand(struct tdb_context *tdb, tdb_off_t size); int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, - struct list_struct *rec); + struct tdb_record *rec); diff --git a/lib/tdb/common/transaction.c b/lib/tdb/common/transaction.c index 7acda640c8..b8988ea830 100644 --- a/lib/tdb/common/transaction.c +++ b/lib/tdb/common/transaction.c @@ -85,6 +85,21 @@ still available, but no transaction recovery area is used and no fsync/msync calls are made. + - if TDB_ALLOW_NESTING is passed to flags in tdb open, or added using + tdb_add_flags() transaction nesting is enabled. + It resets the TDB_DISALLOW_NESTING flag, as both cannot be used together. + The default is that transaction nesting is allowed. + Note: this default may change in future versions of tdb. + + Beware. when transactions are nested a transaction successfully + completed with tdb_transaction_commit() can be silently unrolled later. + + - if TDB_DISALLOW_NESTING is passed to flags in tdb open, or added using + tdb_add_flags() transaction nesting is disabled. + It resets the TDB_ALLOW_NESTING flag, as both cannot be used together. + An attempt create a nested transaction will fail with TDB_ERR_NESTING. + The default is that transaction nesting is allowed. + Note: this default may change in future versions of tdb. */ @@ -116,8 +131,18 @@ struct tdb_transaction { but don't create a new transaction */ int nesting; + /* set when a prepare has already occurred */ + bool prepared; + tdb_off_t magic_offset; + + /* set when the GLOBAL_LOCK has been taken */ + bool global_lock_taken; + /* old file size before transaction */ tdb_len_t old_map_size; + + /* we should re-pack on commit */ + bool need_repack; }; @@ -187,6 +212,14 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, { uint32_t blk; + /* Only a commit is allowed on a prepared transaction */ + if (tdb->transaction->prepared) { + tdb->ecode = TDB_ERR_EINVAL; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: transaction already prepared, write not allowed\n")); + tdb->transaction->transaction_error = 1; + return -1; + } + /* if the write is to a hash head, then update the transaction hash heads */ if (len == sizeof(tdb_off_t) && off >= FREELIST_TOP && @@ -357,7 +390,8 @@ static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe) if (len <= tdb->map_size) { return 0; } - return TDB_ERRCODE(TDB_ERR_IO, -1); + tdb->ecode = TDB_ERR_IO; + return -1; } /* @@ -372,6 +406,8 @@ static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size, return -1; } + tdb->transaction->need_repack = true; + return 0; } @@ -409,6 +445,10 @@ int tdb_transaction_start(struct tdb_context *tdb) /* cope with nested tdb_transaction_start() calls */ if (tdb->transaction != NULL) { + if (!(tdb->flags & TDB_ALLOW_NESTING)) { + tdb->ecode = TDB_ERR_NESTING; + return -1; + } tdb->transaction->nesting++; TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n", tdb->transaction->nesting)); @@ -485,6 +525,8 @@ int tdb_transaction_start(struct tdb_context *tdb) tdb->transaction->io_methods = tdb->methods; tdb->methods = &transaction_methods; + /* Trace at the end, so we get sequence number correct. */ + tdb_trace(tdb, "tdb_transaction_start"); return 0; fail: @@ -498,11 +540,38 @@ fail: /* - cancel the current transaction + sync to disk */ -int tdb_transaction_cancel(struct tdb_context *tdb) +static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length) { - int i; + if (tdb->flags & TDB_NOSYNC) { + return 0; + } + + if (fsync(tdb->fd) != 0) { + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n")); + return -1; + } +#ifdef HAVE_MMAP + if (tdb->map_ptr) { + tdb_off_t moffset = offset & ~(tdb->page_size-1); + if (msync(moffset + (char *)tdb->map_ptr, + length + (offset - moffset), MS_SYNC) != 0) { + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n", + strerror(errno))); + return -1; + } + } +#endif + return 0; +} + + +int _tdb_transaction_cancel(struct tdb_context *tdb) +{ + int i, ret = 0; if (tdb->transaction == NULL) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n")); @@ -525,6 +594,23 @@ int tdb_transaction_cancel(struct tdb_context *tdb) } SAFE_FREE(tdb->transaction->blocks); + if (tdb->transaction->magic_offset) { + const struct tdb_methods *methods = tdb->transaction->io_methods; + uint32_t zero = 0; + + /* remove the recovery marker */ + if (methods->tdb_write(tdb, tdb->transaction->magic_offset, &zero, 4) == -1 || + transaction_sync(tdb, tdb->transaction->magic_offset, 4) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_cancel: failed to remove recovery magic\n")); + ret = -1; + } + } + + if (tdb->transaction->global_lock_taken) { + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb->transaction->global_lock_taken = false; + } + /* remove any global lock created during the transaction */ if (tdb->global_lock.count != 0) { tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size); @@ -550,35 +636,18 @@ int tdb_transaction_cancel(struct tdb_context *tdb) SAFE_FREE(tdb->transaction->hash_heads); SAFE_FREE(tdb->transaction); - return 0; + return ret; } /* - sync to disk + cancel the current transaction */ -static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length) -{ - if (fsync(tdb->fd) != 0) { - tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n")); - return -1; - } -#ifdef HAVE_MMAP - if (tdb->map_ptr) { - tdb_off_t moffset = offset & ~(tdb->page_size-1); - if (msync(moffset + (char *)tdb->map_ptr, - length + (offset - moffset), MS_SYNC) != 0) { - tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n", - strerror(errno))); - return -1; - } - } -#endif - return 0; +int tdb_transaction_cancel(struct tdb_context *tdb) +{ + tdb_trace(tdb, "tdb_transaction_cancel"); + return _tdb_transaction_cancel(tdb); } - /* work out how much space the linearised recovery data will consume */ @@ -615,7 +684,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, tdb_off_t *recovery_offset, tdb_len_t *recovery_max_size) { - struct list_struct rec; + struct tdb_record rec; const struct tdb_methods *methods = tdb->transaction->io_methods; tdb_off_t recovery_head; @@ -701,7 +770,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb, tdb_len_t recovery_size; unsigned char *data, *p; const struct tdb_methods *methods = tdb->transaction->io_methods; - struct list_struct *rec; + struct tdb_record *rec; tdb_off_t recovery_offset, recovery_max_size; tdb_off_t old_map_size = tdb->transaction->old_map_size; uint32_t magic, tailer; @@ -721,7 +790,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb, return -1; } - rec = (struct list_struct *)data; + rec = (struct tdb_record *)data; memset(rec, 0, sizeof(*rec)); rec->magic = 0; @@ -804,7 +873,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb, magic = TDB_RECOVERY_MAGIC; CONVERT(magic); - *magic_offset = recovery_offset + offsetof(struct list_struct, magic); + *magic_offset = recovery_offset + offsetof(struct tdb_record, magic); if (methods->tdb_write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery magic\n")); @@ -825,37 +894,36 @@ static int transaction_setup_recovery(struct tdb_context *tdb, return 0; } -/* - commit the current transaction -*/ -int tdb_transaction_commit(struct tdb_context *tdb) +static int _tdb_transaction_prepare_commit(struct tdb_context *tdb) { const struct tdb_methods *methods; - tdb_off_t magic_offset = 0; - uint32_t zero = 0; - int i; if (tdb->transaction == NULL) { - TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: no transaction\n")); + return -1; + } + + if (tdb->transaction->prepared) { + tdb->ecode = TDB_ERR_EINVAL; + _tdb_transaction_cancel(tdb); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: transaction already prepared\n")); return -1; } if (tdb->transaction->transaction_error) { tdb->ecode = TDB_ERR_IO; - tdb_transaction_cancel(tdb); - TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n")); + _tdb_transaction_cancel(tdb); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: transaction error pending\n")); return -1; } if (tdb->transaction->nesting != 0) { - tdb->transaction->nesting--; return 0; } /* check for a null transaction */ if (tdb->transaction->blocks == NULL) { - tdb_transaction_cancel(tdb); return 0; } @@ -865,53 +933,112 @@ int tdb_transaction_commit(struct tdb_context *tdb) nested their locks properly, so fail the transaction */ if (tdb->num_locks || tdb->global_lock.count) { tdb->ecode = TDB_ERR_LOCK; - TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: locks pending on commit\n")); - tdb_transaction_cancel(tdb); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: locks pending on commit\n")); + _tdb_transaction_cancel(tdb); return -1; } /* upgrade the main transaction lock region to a write lock */ if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) { - TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to upgrade hash locks\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to upgrade hash locks\n")); tdb->ecode = TDB_ERR_LOCK; - tdb_transaction_cancel(tdb); + _tdb_transaction_cancel(tdb); return -1; } /* get the global lock - this prevents new users attaching to the database during the commit */ if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { - TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: failed to get global lock\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to get global lock\n")); tdb->ecode = TDB_ERR_LOCK; - tdb_transaction_cancel(tdb); + _tdb_transaction_cancel(tdb); return -1; } + tdb->transaction->global_lock_taken = true; + if (!(tdb->flags & TDB_NOSYNC)) { /* write the recovery data to the end of the file */ - if (transaction_setup_recovery(tdb, &magic_offset) == -1) { - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to setup recovery data\n")); - tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); - tdb_transaction_cancel(tdb); + if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: failed to setup recovery data\n")); + _tdb_transaction_cancel(tdb); return -1; } } + tdb->transaction->prepared = true; + /* expand the file to the new size if needed */ if (tdb->map_size != tdb->transaction->old_map_size) { if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, tdb->map_size - tdb->transaction->old_map_size) == -1) { tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: expansion failed\n")); - tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); - tdb_transaction_cancel(tdb); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: expansion failed\n")); + _tdb_transaction_cancel(tdb); return -1; } tdb->map_size = tdb->transaction->old_map_size; methods->tdb_oob(tdb, tdb->map_size + 1, 1); } + /* Keep the global lock until the actual commit */ + + return 0; +} + +/* + prepare to commit the current transaction +*/ +int tdb_transaction_prepare_commit(struct tdb_context *tdb) +{ + tdb_trace(tdb, "tdb_transaction_prepare_commit"); + return _tdb_transaction_prepare_commit(tdb); +} + +/* + commit the current transaction +*/ +int tdb_transaction_commit(struct tdb_context *tdb) +{ + const struct tdb_methods *methods; + int i; + bool need_repack; + + if (tdb->transaction == NULL) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n")); + return -1; + } + + tdb_trace(tdb, "tdb_transaction_commit"); + + if (tdb->transaction->transaction_error) { + tdb->ecode = TDB_ERR_IO; + _tdb_transaction_cancel(tdb); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n")); + return -1; + } + + + if (tdb->transaction->nesting != 0) { + tdb->transaction->nesting--; + return 0; + } + + /* check for a null transaction */ + if (tdb->transaction->blocks == NULL) { + _tdb_transaction_cancel(tdb); + return 0; + } + + if (!tdb->transaction->prepared) { + int ret = _tdb_transaction_prepare_commit(tdb); + if (ret) + return ret; + } + + methods = tdb->transaction->io_methods; + /* perform all the writes */ for (i=0;i<tdb->transaction->num_blocks;i++) { tdb_off_t offset; @@ -936,8 +1063,7 @@ int tdb_transaction_commit(struct tdb_context *tdb) tdb->methods = methods; tdb_transaction_recover(tdb); - tdb_transaction_cancel(tdb); - tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + _tdb_transaction_cancel(tdb); TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n")); return -1; @@ -948,26 +1074,11 @@ int tdb_transaction_commit(struct tdb_context *tdb) SAFE_FREE(tdb->transaction->blocks); tdb->transaction->num_blocks = 0; - if (!(tdb->flags & TDB_NOSYNC)) { - /* ensure the new data is on disk */ - if (transaction_sync(tdb, 0, tdb->map_size) == -1) { - return -1; - } - - /* remove the recovery marker */ - if (methods->tdb_write(tdb, magic_offset, &zero, 4) == -1) { - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to remove recovery magic\n")); - return -1; - } - - /* ensure the recovery marker has been removed on disk */ - if (transaction_sync(tdb, magic_offset, 4) == -1) { - return -1; - } + /* ensure the new data is on disk */ + if (transaction_sync(tdb, 0, tdb->map_size) == -1) { + return -1; } - tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); - /* TODO: maybe write to some dummy hdr field, or write to magic offset without mmap, before the last sync, instead of the @@ -983,9 +1094,15 @@ int tdb_transaction_commit(struct tdb_context *tdb) utime(tdb->name, NULL); #endif + need_repack = tdb->transaction->need_repack; + /* use a transaction cancel to free memory and remove the transaction locks */ - tdb_transaction_cancel(tdb); + _tdb_transaction_cancel(tdb); + + if (need_repack) { + return tdb_repack(tdb); + } return 0; } @@ -1001,7 +1118,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) tdb_off_t recovery_head, recovery_eof; unsigned char *data, *p; uint32_t zero = 0; - struct list_struct rec; + struct tdb_record rec; /* find the recovery area */ if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { @@ -1088,7 +1205,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) } /* remove the recovery magic */ - if (tdb_ofs_write(tdb, recovery_head + offsetof(struct list_struct, magic), + if (tdb_ofs_write(tdb, recovery_head + offsetof(struct tdb_record, magic), &zero) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n")); tdb->ecode = TDB_ERR_IO; diff --git a/lib/tdb/common/traverse.c b/lib/tdb/common/traverse.c index 69c81e6e98..c340dd354b 100644 --- a/lib/tdb/common/traverse.c +++ b/lib/tdb/common/traverse.c @@ -27,9 +27,12 @@ #include "tdb_private.h" -/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */ -static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tlock, - struct list_struct *rec) +#define TDB_NEXT_LOCK_ERR ((tdb_off_t)-1) + +/* Uses traverse lock: 0 = finish, TDB_NEXT_LOCK_ERR = error, + other = record offset */ +static tdb_off_t tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tlock, + struct tdb_record *rec) { int want_next = (tlock->off != 0); @@ -71,7 +74,7 @@ static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tloc } if (tdb_lock(tdb, tlock->hash, tlock->lock_rw) == -1) - return -1; + return TDB_NEXT_LOCK_ERR; /* No previous record? Start at top of chain. */ if (!tlock->off) { @@ -99,6 +102,7 @@ static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tloc /* Detect infinite loops. From "Shlomi Yaakobovich" <Shlomi@exanet.com>. */ if (tlock->off == rec->next) { + tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: loop detected.\n")); goto fail; } @@ -121,13 +125,14 @@ static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tloc want_next = 0; } /* We finished iteration without finding anything */ - return TDB_ERRCODE(TDB_SUCCESS, 0); + tdb->ecode = TDB_SUCCESS; + return 0; fail: tlock->off = 0; if (tdb_unlock(tdb, tlock->hash, tlock->lock_rw) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: On error unlock failed!\n")); - return -1; + return TDB_NEXT_LOCK_ERR; } /* traverse the entire database - calling fn(tdb, key, data) on each element. @@ -140,8 +145,9 @@ static int tdb_traverse_internal(struct tdb_context *tdb, struct tdb_traverse_lock *tl) { TDB_DATA key, dbuf; - struct list_struct rec; - int ret, count = 0; + struct tdb_record rec; + int ret = 0, count = 0; + tdb_off_t off; /* This was in the initializaton, above, but the IRIX compiler * did not like it. crh @@ -152,7 +158,11 @@ static int tdb_traverse_internal(struct tdb_context *tdb, tdb->travlocks.next = tl; /* tdb_next_lock places locks on the record returned, and its chain */ - while ((ret = tdb_next_lock(tdb, tl, &rec)) > 0) { + while ((off = tdb_next_lock(tdb, tl, &rec)) != 0) { + if (off == TDB_NEXT_LOCK_ERR) { + ret = -1; + goto out; + } count++; /* now read the full record */ key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec), @@ -169,6 +179,8 @@ static int tdb_traverse_internal(struct tdb_context *tdb, dbuf.dptr = key.dptr + rec.key_len; dbuf.dsize = rec.data_len; + tdb_trace_1rec_retrec(tdb, "traverse", key, dbuf); + /* Drop chain lock, call out */ if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) { ret = -1; @@ -177,7 +189,7 @@ static int tdb_traverse_internal(struct tdb_context *tdb, } if (fn && fn(tdb, key, dbuf, private_data)) { /* They want us to terminate traversal */ - ret = count; + tdb_trace_ret(tdb, "tdb_traverse_end", count); if (tdb_unlock_record(tdb, tl->off) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));; ret = -1; @@ -187,6 +199,7 @@ static int tdb_traverse_internal(struct tdb_context *tdb, } SAFE_FREE(key.dptr); } + tdb_trace(tdb, "tdb_traverse_end"); out: tdb->travlocks.next = tl->next; if (ret < 0) @@ -204,23 +217,19 @@ int tdb_traverse_read(struct tdb_context *tdb, { struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK }; int ret; - bool in_transaction = (tdb->transaction != NULL); /* we need to get a read lock on the transaction lock here to cope with the lock ordering semantics of solaris10 */ - if (!in_transaction) { - if (tdb_transaction_lock(tdb, F_RDLCK)) { - return -1; - } + if (tdb_transaction_lock(tdb, F_RDLCK)) { + return -1; } tdb->traverse_read++; + tdb_trace(tdb, "tdb_traverse_read_start"); ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_read--; - if (!in_transaction) { - tdb_transaction_unlock(tdb); - } + tdb_transaction_unlock(tdb); return ret; } @@ -237,25 +246,21 @@ int tdb_traverse(struct tdb_context *tdb, { struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK }; int ret; - bool in_transaction = (tdb->transaction != NULL); if (tdb->read_only || tdb->traverse_read) { return tdb_traverse_read(tdb, fn, private_data); } - - if (!in_transaction) { - if (tdb_transaction_lock(tdb, F_WRLCK)) { - return -1; - } + + if (tdb_transaction_lock(tdb, F_WRLCK)) { + return -1; } tdb->traverse_write++; + tdb_trace(tdb, "tdb_traverse_start"); ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_write--; - if (!in_transaction) { - tdb_transaction_unlock(tdb); - } + tdb_transaction_unlock(tdb); return ret; } @@ -265,7 +270,8 @@ int tdb_traverse(struct tdb_context *tdb, TDB_DATA tdb_firstkey(struct tdb_context *tdb) { TDB_DATA key; - struct list_struct rec; + struct tdb_record rec; + tdb_off_t off; /* release any old lock */ if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) @@ -274,12 +280,17 @@ TDB_DATA tdb_firstkey(struct tdb_context *tdb) tdb->travlocks.lock_rw = F_RDLCK; /* Grab first record: locks chain and returned record. */ - if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0) + off = tdb_next_lock(tdb, &tdb->travlocks, &rec); + if (off == 0 || off == TDB_NEXT_LOCK_ERR) { + tdb_trace_retrec(tdb, "tdb_firstkey", tdb_null); return tdb_null; + } /* now read the key */ key.dsize = rec.key_len; key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize); + tdb_trace_retrec(tdb, "tdb_firstkey", key); + /* Unlock the hash chain of the record we just read. */ if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n")); @@ -291,8 +302,9 @@ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) { uint32_t oldhash; TDB_DATA key = tdb_null; - struct list_struct rec; + struct tdb_record rec; unsigned char *k = NULL; + tdb_off_t off; /* Is locked key the old key? If so, traverse will be reliable. */ if (tdb->travlocks.off) { @@ -304,6 +316,8 @@ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) { /* No, it wasn't: unlock it and start from scratch */ if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) { + tdb_trace_1rec_retrec(tdb, "tdb_nextkey", + oldkey, tdb_null); SAFE_FREE(k); return tdb_null; } @@ -320,8 +334,10 @@ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) if (!tdb->travlocks.off) { /* No previous element: do normal find, and lock record */ tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), tdb->travlocks.lock_rw, &rec); - if (!tdb->travlocks.off) + if (!tdb->travlocks.off) { + tdb_trace_1rec_retrec(tdb, "tdb_nextkey", oldkey, tdb_null); return tdb_null; + } tdb->travlocks.hash = BUCKET(rec.full_hash); if (tdb_lock_record(tdb, tdb->travlocks.off) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); @@ -332,7 +348,8 @@ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) /* Grab next record: locks chain and returned record, unlocks old record */ - if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) { + off = tdb_next_lock(tdb, &tdb->travlocks, &rec); + if (off != TDB_NEXT_LOCK_ERR && off != 0) { key.dsize = rec.key_len; key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec), key.dsize); @@ -343,6 +360,7 @@ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) /* Unlock the chain of old record */ if (tdb_unlock(tdb, BUCKET(oldhash), tdb->travlocks.lock_rw) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + tdb_trace_1rec_retrec(tdb, "tdb_nextkey", oldkey, key); return key; } diff --git a/lib/tdb/config.guess b/lib/tdb/config.guess index 4af85584ae..da83314608 100755 --- a/lib/tdb/config.guess +++ b/lib/tdb/config.guess @@ -808,7 +808,7 @@ EOF echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:[3456]*) - case ${UNAME_MACHINE} in + case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; @@ -984,7 +984,7 @@ EOF echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so diff --git a/lib/tdb/config.mk b/lib/tdb/config.mk index 38b03b93b6..b2e322cebc 100644 --- a/lib/tdb/config.mk +++ b/lib/tdb/config.mk @@ -10,7 +10,7 @@ CFLAGS = -I$(tdbsrcdir)/include LIBTDB_OBJ_FILES = $(addprefix $(tdbsrcdir)/common/, \ tdb.o dump.o io.o lock.o \ open.o traverse.o freelist.o \ - error.o transaction.o) + error.o transaction.o check.o) ################################################ # Start BINARY tdbtool diff --git a/lib/tdb/config.sub b/lib/tdb/config.sub index 80a785297e..a39437d015 100755 --- a/lib/tdb/config.sub +++ b/lib/tdb/config.sub @@ -1454,7 +1454,7 @@ case $basic_machine in os=-aout ;; c4x-* | tic4x-*) - os=-coff + os=-coff ;; # This must come before the *-dec entry. pdp10-*) diff --git a/lib/tdb/configure.ac b/lib/tdb/configure.ac index 2feaa6f5f5..395121937f 100644 --- a/lib/tdb/configure.ac +++ b/lib/tdb/configure.ac @@ -2,11 +2,12 @@ AC_PREREQ(2.50) AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""]) AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""]) AC_DEFUN([SMB_ENABLE], [echo -n ""]) -AC_INIT(tdb, 1.1.3) +AC_INIT(tdb, 1.2.1) AC_CONFIG_SRCDIR([common/tdb.c]) AC_CONFIG_HEADER(include/config.h) AC_LIBREPLACE_ALL_CHECKS AC_LD_SONAMEFLAG +AC_LD_VERSIONSCRIPT AC_LD_PICFLAG AC_LD_SHLIBEXT AC_LIBREPLACE_SHLD @@ -28,6 +29,22 @@ if test -z "$PYTHON_CONFIG"; then PYTHON_CHECK_TARGET="" fi +AC_ARG_ENABLE(python, + AS_HELP_STRING([--enable-python], [Enables python binding]), + [ if test "x$enableval" = "xno" ; then + PYTHON_BUILD_TARGET="" + PYTHON_INSTALL_TARGET="" + PYTHON_CHECK_TARGET="" + fi + ]) + +AC_PATH_PROG(XSLTPROC,xsltproc) +DOC_TARGET="" +if test -n "$XSLTPROC"; then + DOC_TARGET=doc +fi +AC_SUBST(DOC_TARGET) + m4_include(build_macros.m4) BUILD_WITH_SHARED_BUILD_DIR diff --git a/lib/tdb/docs/README b/lib/tdb/docs/README index 63fcf5e049..c02ee0e030 100644 --- a/lib/tdb/docs/README +++ b/lib/tdb/docs/README @@ -69,11 +69,15 @@ TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags, TDB_NOLOCK - don't do any locking TDB_NOMMAP - don't use mmap TDB_NOSYNC - don't synchronise transactions to disk + TDB_SEQNUM - maintain a sequence number + TDB_VOLATILE - activate the per-hashchain freelist, default 5 + TDB_ALLOW_NESTING - allow transactions to nest + TDB_DISALLOW_NESTING - disallow transactions to nest ---------------------------------------------------------------------- TDB_CONTEXT *tdb_open_ex(char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, - tdb_log_func log_fn, + const struct tdb_logging_context *log_ctx, tdb_hash_func hash_fn) This is like tdb_open(), but allows you to pass an initial logging and @@ -93,13 +97,6 @@ int tdb_close(TDB_CONTEXT *tdb); close a database ---------------------------------------------------------------------- -int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf); - - update an entry in place - this only works if the new data size - is <= the old data size and the key exists. - on failure return -1 - ----------------------------------------------------------------------- TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key); fetch an entry in the database given a key @@ -236,3 +233,22 @@ int tdb_transaction_commit(TDB_CONTEXT *tdb) commit a current transaction, updating the database and releasing the transaction locks. +---------------------------------------------------------------------- +int tdb_transaction_prepare_commit(TDB_CONTEXT *tdb) + + prepare to commit a current transaction, for two-phase commits. + Once prepared for commit, the only allowed calls are + tdb_transaction_commit() or tdb_transaction_cancel(). Preparing + allocates disk space for the pending updates, so a subsequent + commit should succeed (barring any hardware failures). + +---------------------------------------------------------------------- +int tdb_check(TDB_CONTEXT *tdb, + int (*check)(TDB_DATA key, TDB_DATA data, void *private_data), + void *private_data);) + + check the consistency of the database, calling back the check function + (if non-NULL) with each record. If some consistency check fails, or + the supplied check function returns -1, tdb_check returns -1, otherwise + 0. Note that logging function (if set) will be called with additional + information on the corruption found. diff --git a/lib/tdb/docs/tracing.txt b/lib/tdb/docs/tracing.txt new file mode 100644 index 0000000000..98c5db9a51 --- /dev/null +++ b/lib/tdb/docs/tracing.txt @@ -0,0 +1,46 @@ +How And Why To Use TDB Tracing +============================== + +You can trace all TDB operations, using TDB_TRACE. It is not complete +(error conditions which expect to the logged will not always be traced +correctly, so you should set up a logging function too), but is designed +to collect benchmark-style traces to allow us to optimize TDB. + +Note: tracing is not efficient, and the trace files are huge: a +traverse of the database is particularly large! But they compress very +well with rzip (http://rzip.samba.org) + +How to gather trace files: +-------------------------- +1) Uncomment /* #define TDB_TRACE 1 */ in tdb_private.h. +2) Rebuild TDB, and everything that uses it. +3) Run something. + +Your trace files will be called <tdbname>.trace.<pid>. These files +will not be overwritten: if the same process reopens the same TDB, an +error will be logged and tracing will be disabled. + +How to replay trace files: +-------------------------- +1) For benchmarking, remember to rebuild tdb with #define TDB_TRACE commented + out again! +2) Grab the latest "replace_trace.c" from CCAN's tdb module (tools/ dir): + http://ccan.ozlabs.org/tarballs/tdb.tar.bz2 +3) Compile up replay_trace, munging as necessary. +4) Run replay_trace <scratch-tdb-name> <tracefiles>... + +If given more than one trace file (presumably from the same tdb) +replay_trace will try to figure out the dependencies between the operations +and fire off a child to run each trace. Occasionally it gets stuck, in +which case it will add another dependency and retry. Eventually it will +give a speed value. + +replay_trace can intuit the existence of previous data in the tdb (ie. +activity prior to the trace(s) supplied) and will prepopulate as +neccessary. + +You can run --quiet for straight benchmark results, and -n to run multiple +times (this saves time, since it need only calculate dependencies once). + +Good luck! +Rusty Russell <rusty@rustcorp.com.au> diff --git a/lib/tdb/include/tdb.h b/lib/tdb/include/tdb.h index 94b5e366b9..c9e946a885 100644 --- a/lib/tdb/include/tdb.h +++ b/lib/tdb/include/tdb.h @@ -48,13 +48,14 @@ extern "C" { #define TDB_NOSYNC 64 /* don't use synchronous transactions */ #define TDB_SEQNUM 128 /* maintain a sequence number */ #define TDB_VOLATILE 256 /* Activate the per-hashchain freelist, default 5 */ - -#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret) +#define TDB_ALLOW_NESTING 512 /* Allow transactions to nest */ +#define TDB_DISALLOW_NESTING 1024 /* Disallow transactions to nest */ /* error codes */ enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT, - TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY}; + TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY, + TDB_ERR_NESTING}; /* debugging uses one of the following levels */ enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, @@ -129,6 +130,7 @@ int tdb_fd(struct tdb_context *tdb); tdb_log_func tdb_log_fn(struct tdb_context *tdb); void *tdb_get_logging_private(struct tdb_context *tdb); int tdb_transaction_start(struct tdb_context *tdb); +int tdb_transaction_prepare_commit(struct tdb_context *tdb); int tdb_transaction_commit(struct tdb_context *tdb); int tdb_transaction_cancel(struct tdb_context *tdb); int tdb_transaction_recover(struct tdb_context *tdb); @@ -140,6 +142,9 @@ void tdb_add_flags(struct tdb_context *tdb, unsigned flag); void tdb_remove_flags(struct tdb_context *tdb, unsigned flag); void tdb_enable_seqnum(struct tdb_context *tdb); void tdb_increment_seqnum_nonblock(struct tdb_context *tdb); +int tdb_check(struct tdb_context *tdb, + int (*check) (TDB_DATA key, TDB_DATA data, void *private_data), + void *private_data); /* Low level locking functions: use with care */ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key); diff --git a/lib/tdb/libtdb.m4 b/lib/tdb/libtdb.m4 index 252e0b0be3..feae1c2cc6 100644 --- a/lib/tdb/libtdb.m4 +++ b/lib/tdb/libtdb.m4 @@ -1,9 +1,9 @@ dnl find the tdb sources. This is meant to work both for dnl tdb standalone builds, and builds of packages using tdb tdbdir="" -tdbpaths="$srcdir $srcdir/lib/tdb $srcdir/tdb $srcdir/../tdb $srcdir/../lib/tdb" +tdbpaths=". lib/tdb tdb ../tdb ../lib/tdb" for d in $tdbpaths; do - if test -f "$d/common/tdb.c"; then + if test -f "$srcdir/$d/common/tdb.c"; then tdbdir="$d" AC_SUBST(tdbdir) break; @@ -13,7 +13,7 @@ if test x"$tdbdir" = "x"; then AC_MSG_ERROR([cannot find tdb source in $tdbpaths]) fi TDB_OBJ="common/tdb.o common/dump.o common/transaction.o common/error.o common/traverse.o" -TDB_OBJ="$TDB_OBJ common/freelist.o common/freelistcheck.o common/io.o common/lock.o common/open.o" +TDB_OBJ="$TDB_OBJ common/freelist.o common/freelistcheck.o common/io.o common/lock.o common/open.o common/check.o" AC_SUBST(TDB_OBJ) AC_SUBST(LIBREPLACEOBJ) @@ -28,3 +28,8 @@ AC_CHECK_HEADERS(getopt.h sys/select.h sys/time.h) AC_HAVE_DECL(pread, [#include <unistd.h>]) AC_HAVE_DECL(pwrite, [#include <unistd.h>]) + +if test x"$VERSIONSCRIPT" != "x"; then + EXPORTSFILE=tdb.exports + AC_SUBST(EXPORTSFILE) +fi diff --git a/lib/tdb/manpages/tdbbackup.8.xml b/lib/tdb/manpages/tdbbackup.8.xml new file mode 100644 index 0000000000..c3a6e2b051 --- /dev/null +++ b/lib/tdb/manpages/tdbbackup.8.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc"> +<refentry id="tdbbackup.8"> + +<refmeta> + <refentrytitle>tdbbackup</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">System Administration tools</refmiscinfo> + <refmiscinfo class="version">3.5</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>tdbbackup</refname> + <refpurpose>tool for backing up and for validating the integrity of samba .tdb files</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>tdbbackup</command> + <arg choice="opt">-s suffix</arg> + <arg choice="opt">-v</arg> + <arg choice="opt">-h</arg> + </cmdsynopsis> +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>1</manvolnum></citerefentry> suite.</para> + + <para><command>tdbbackup</command> is a tool that may be used to backup samba .tdb + files. This tool may also be used to verify the integrity of the .tdb files prior + to samba startup or during normal operation. If it finds file damage and it finds + a prior backup the backup file will be restored. + </para> +</refsect1> + + +<refsect1> + <title>OPTIONS</title> + + <variablelist> + + <varlistentry> + <term>-h</term> + <listitem><para> + Get help information. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-s suffix</term> + <listitem><para> + The <command>-s</command> option allows the adminisistrator to specify a file + backup extension. This way it is possible to keep a history of tdb backup + files by using a new suffix for each backup. + </para> </listitem> + </varlistentry> + + <varlistentry> + <term>-v</term> + <listitem><para> + The <command>-v</command> will check the database for damages (currupt data) + which if detected causes the backup to be restored. + </para></listitem> + </varlistentry> + + </variablelist> +</refsect1> + + +<refsect1> + <title>COMMANDS</title> + + <para><emphasis>GENERAL INFORMATION</emphasis></para> + + <para> + The <command>tdbbackup</command> utility can safely be run at any time. It was designed so + that it can be used at any time to validate the integrity of tdb files, even during Samba + operation. Typical usage for the command will be: + </para> + + <para>tdbbackup [-s suffix] *.tdb</para> + + <para> + Before restarting samba the following command may be run to validate .tdb files: + </para> + + <para>tdbbackup -v [-s suffix] *.tdb</para> + + <para> + Samba .tdb files are stored in various locations, be sure to run backup all + .tdb file on the system. Important files includes: + </para> + + <itemizedlist> + <listitem><para> + <command>secrets.tdb</command> - usual location is in the /usr/local/samba/private + directory, or on some systems in /etc/samba. + </para></listitem> + + <listitem><para> + <command>passdb.tdb</command> - usual location is in the /usr/local/samba/private + directory, or on some systems in /etc/samba. + </para></listitem> + + <listitem><para> + <command>*.tdb</command> located in the /usr/local/samba/var directory or on some + systems in the /var/cache or /var/lib/samba directories. + </para></listitem> + </itemizedlist> + +</refsect1> + +<refsect1> + <title>VERSION</title> + + <para>This man page is correct for version 3 of the Samba suite.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para> + The original Samba software and related utilities were created by Andrew Tridgell. + Samba is now developed by the Samba Team as an Open Source project similar to the way + the Linux kernel is developed. + </para> + + <para>The tdbbackup man page was written by John H Terpstra.</para> +</refsect1> + +</refentry> diff --git a/lib/tdb/manpages/tdbdump.8.xml b/lib/tdb/manpages/tdbdump.8.xml new file mode 100644 index 0000000000..5c0028db42 --- /dev/null +++ b/lib/tdb/manpages/tdbdump.8.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc"> +<refentry id="tdbdump.8"> + +<refmeta> + <refentrytitle>tdbdump</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">System Administration tools</refmiscinfo> + <refmiscinfo class="version">3.5</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>tdbdump</refname> + <refpurpose>tool for printing the contents of a TDB file</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>tdbdump</command> + <arg choice="req">filename</arg> + </cmdsynopsis> +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>1</manvolnum></citerefentry> suite.</para> + + <para><command>tdbdump</command> is a very simple utility that 'dumps' the + contents of a TDB (Trivial DataBase) file to standard output in a + human-readable format. + </para> + + <para>This tool can be used when debugging problems with TDB files. It is + intended for those who are somewhat familiar with Samba internals. + </para> +</refsect1> + + +<refsect1> + <title>VERSION</title> + + <para>This man page is correct for version 3 of the Samba suite.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para> + The original Samba software and related utilities were created by Andrew Tridgell. + Samba is now developed by the Samba Team as an Open Source project similar to the way + the Linux kernel is developed. + </para> + + <para>The tdbdump man page was written by Jelmer Vernooij.</para> +</refsect1> + +</refentry> diff --git a/lib/tdb/manpages/tdbtool.8.xml b/lib/tdb/manpages/tdbtool.8.xml new file mode 100644 index 0000000000..a755653106 --- /dev/null +++ b/lib/tdb/manpages/tdbtool.8.xml @@ -0,0 +1,235 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc"> +<refentry id="tdbtool.8"> + +<refmeta> + <refentrytitle>tdbtool</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">System Administration tools</refmiscinfo> + <refmiscinfo class="version">3.5</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>tdbtool</refname> + <refpurpose>manipulate the contents TDB files</refpurpose> +</refnamediv> + +<refsynopsisdiv> + + <cmdsynopsis> + <command>tdbtool</command> + </cmdsynopsis> + + <cmdsynopsis> + <command>tdbtool</command> + <arg choice="plain"> + <replaceable>TDBFILE</replaceable> + </arg> + <arg rep="repeat" choice="opt"> + <replaceable>COMMANDS</replaceable> + </arg> + </cmdsynopsis> + +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>This tool is part of the + <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>1</manvolnum></citerefentry> suite.</para> + + <para><command>tdbtool</command> a tool for displaying and + altering the contents of Samba TDB (Trivial DataBase) files. Each + of the commands listed below can be entered interactively or + provided on the command line.</para> + +</refsect1> + + +<refsect1> + <title>COMMANDS</title> + + <variablelist> + + <varlistentry> + <term><option>create</option> + <replaceable>TDBFILE</replaceable></term> + <listitem><para>Create a new database named + <replaceable>TDBFILE</replaceable>. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>open</option> + <replaceable>TDBFILE</replaceable></term> + <listitem><para>Open an existing database named + <replaceable>TDBFILE</replaceable>. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>erase</option></term> + <listitem><para>Erase the current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>dump</option></term> + <listitem><para>Dump the current database as strings. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>cdump</option></term> + <listitem><para>Dump the current database as connection records. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>keys</option></term> + <listitem><para>Dump the current database keys as strings. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>hexkeys</option></term> + <listitem><para>Dump the current database keys as hex values. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>info</option></term> + <listitem><para>Print summary information about the + current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>insert</option> + <replaceable>KEY</replaceable> + <replaceable>DATA</replaceable> + </term> + <listitem><para>Insert a record into the + current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>move</option> + <replaceable>KEY</replaceable> + <replaceable>TDBFILE</replaceable> + </term> + <listitem><para>Move a record from the + current database into <replaceable>TDBFILE</replaceable>. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>store</option> + <replaceable>KEY</replaceable> + <replaceable>DATA</replaceable> + </term> + <listitem><para>Store (replace) a record in the + current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>show</option> + <replaceable>KEY</replaceable> + </term> + <listitem><para>Show a record by key. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>delete</option> + <replaceable>KEY</replaceable> + </term> + <listitem><para>Delete a record by key. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>list</option> + </term> + <listitem><para>Print the current database hash table and free list. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>free</option> + </term> + <listitem><para>Print the current database and free list. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>!</option> + <replaceable>COMMAND</replaceable> + </term> + <listitem><para>Execute the given system command. + </para></listitem> + </varlistentry> + + <varlistentry> + <term> + <option>first</option> + </term> + <listitem><para>Print the first record in the current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term> + <option>next</option> + </term> + <listitem><para>Print the next record in the current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term> + <option>check</option> + </term> + <listitem><para>Check the integrity of the current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term> + <option>quit</option> + </term> + <listitem><para>Exit <command>tdbtool</command>. + </para></listitem> + </varlistentry> + + </variablelist> +</refsect1> + +<refsect1> + <title>CAVEATS</title> + <para>The contents of the Samba TDB files are private + to the implementation and should not be altered with + <command>tdbtool</command>. + </para> +</refsect1> + +<refsect1> + <title>VERSION</title> + <para>This man page is correct for version 3.0.25 of the Samba suite.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para> The original Samba software and related utilities were + created by Andrew Tridgell. Samba is now developed by the + Samba Team as an Open Source project similar to the way the + Linux kernel is developed.</para> +</refsect1> + +</refentry> diff --git a/lib/tdb/pytdb.c b/lib/tdb/pytdb.c index 159bc4dce5..202dca1571 100644 --- a/lib/tdb/pytdb.c +++ b/lib/tdb/pytdb.c @@ -1,7 +1,7 @@ /* Unix SMB/CIFS implementation. - Swig interface to tdb. + Python interface to tdb. Copyright (C) 2004-2006 Tim Potter <tpot@samba.org> Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org> @@ -337,7 +337,7 @@ static PyMethodDef tdb_object_methods[] = { { "read_lock_all", (PyCFunction)obj_lockall_read, METH_NOARGS, NULL }, { "read_unlock_all", (PyCFunction)obj_unlockall_read, METH_NOARGS, NULL }, { "close", (PyCFunction)obj_close, METH_NOARGS, NULL }, - { "get", (PyCFunction)obj_get, METH_VARARGS, "S.fetch(key) -> value\n" + { "get", (PyCFunction)obj_get, METH_VARARGS, "S.get(key) -> value\n" "Fetch a value." }, { "append", (PyCFunction)obj_append, METH_VARARGS, "S.append(key, value) -> None\n" "Append data to an existing key." }, diff --git a/lib/tdb/python/tests/simple.py b/lib/tdb/python/tests/simple.py index d242e665be..c7443c0d43 100644 --- a/lib/tdb/python/tests/simple.py +++ b/lib/tdb/python/tests/simple.py @@ -15,6 +15,15 @@ class OpenTdbTests(TestCase): def test_nonexistant_read(self): self.assertRaises(IOError, tdb.Tdb, "/some/nonexistant/file", 0, tdb.DEFAULT, os.O_RDWR) +class CloseTdbTests(TestCase): + def test_double_close(self): + self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR) + self.assertNotEqual(None, self.tdb) + + # ensure that double close does not crash python + self.tdb.close() + self.tdb.close() + class SimpleTdbTests(TestCase): def setUp(self): diff --git a/lib/tdb/release-script.sh b/lib/tdb/release-script.sh new file mode 100755 index 0000000000..273ca30be8 --- /dev/null +++ b/lib/tdb/release-script.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +if [ "$1" = "" ]; then + echo "Please provide version string, eg: 1.2.0" + exit 1 +fi + +if [ ! -d "lib/tdb" ]; then + echo "Run this script from the samba base directory." + exit 1 +fi + +git clean -f -x -d lib/tdb +git clean -f -x -d lib/replace + +curbranch=`git branch |grep "^*" | tr -d "* "` + +version=$1 +strver=`echo ${version} | tr "." "-"` + +# Checkout the release tag +git branch -f tdb-release-script-${strver} tdb-${strver} +if [ ! "$?" = "0" ]; then + echo "Unable to checkout tdb-${strver} release" + exit 1 +fi + +git checkout tdb-release-script-${strver} + +# Test configure agrees with us +confver=`grep "^AC_INIT" lib/tdb/configure.ac | tr -d "AC_INIT(tdb, " | tr -d ")"` +if [ ! "$confver" = "$version" ]; then + echo "Wrong version, requested release for ${version}, found ${confver}" + exit 1 +fi + +# Now build tarball +cp -a lib/tdb tdb-${version} +cp -a lib/replace tdb-${version}/libreplace +pushd tdb-${version} +./autogen.sh +popd +tar cvzf tdb-${version}.tar.gz tdb-${version} +rm -fr tdb-${version} + +#Clean up +git checkout $curbranch +git branch -d tdb-release-script-${strver} diff --git a/lib/tdb/rules.mk b/lib/tdb/rules.mk index 7b765625df..023e0ce534 100644 --- a/lib/tdb/rules.mk +++ b/lib/tdb/rules.mk @@ -1,8 +1,3 @@ -.SUFFIXES: .i _wrap.c - -.i_wrap.c: - $(SWIG) -O -Wall -python -keyword $< - showflags:: @echo 'tdb will be compiled with flags:' @echo ' CFLAGS = $(CFLAGS)' @@ -15,7 +10,7 @@ showflags:: .c.o: @echo Compiling $*.c @mkdir -p `dirname $@` - @$(CC) $(PICFLAG) $(CFLAGS) -c $< -o $@ + @$(CC) $(PICFLAG) $(CFLAGS) $(ABI_CHECK) -c $< -o $@ distclean:: rm -f *~ */*~ diff --git a/lib/tdb/script/abi_checks.sh b/lib/tdb/script/abi_checks.sh new file mode 100755 index 0000000000..ba60ed003a --- /dev/null +++ b/lib/tdb/script/abi_checks.sh @@ -0,0 +1,91 @@ +#!/bin/sh + +# +# abi_checks.sh - check for possible abi changes +# +# Copyright (C) 2009 Michael Adam <obnox@samba.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +# +# USAGE: abi_checks.sh LIBNAME header1 [header2 ...] +# +# This script creates symbol and signature lists from the provided header +# files with the aid of the mksyms.sh and mksigs.pl scripts (saved as +# $LIBNAME.exports.check and $LIBNAME.sigatures.check). It then compares +# the resulting files with the files $LIBNAME.exports and $LIBNME.signatures +# which it expects to find in the current directory. +# + +LANG=C; export LANG +LC_ALL=C; export LC_ALL +LC_COLLATE=C; export LC_COLLATE + +script=$0 +dir_name=$(dirname ${script}) + +if test x"$1" = "x" ; then + echo "USAGE: ${script} libname header [header ...]" + exit 1 +fi + +libname="$1" +shift + +if test x"$1" = "x" ; then + echo "USAGE: ${script} libname header [header ...]" + exit 1 +fi + +headers="$*" + +exports_file=${libname}.exports +exports_file_check=${exports_file}.check +signatures_file=${libname}.signatures +signatures_file_check=${signatures_file}.check + + +${dir_name}/mksyms.sh awk ${exports_file_check} ${headers} 2>&1 > /dev/null + +cat ${headers} | ${dir_name}/mksigs.pl > ${signatures_file_check} 2> /dev/null + +normalize_exports_file() { + filename=$1 + cat ${filename} \ + | sed -e 's/^[ \t]*//g' \ + | sed -e 's/^$//g' \ + | sed -e 's/^#.*$//g' \ + | sort | uniq > ${filename}.sort +} + +normalize_exports_file ${exports_file} +normalize_exports_file ${exports_file_check} + +normalize_exports_file ${signatures_file} +normalize_exports_file ${signatures_file_check} + +diff -u ${exports_file}.sort ${exports_file_check}.sort +if test "x$?" != "x0" ; then + echo "WARNING: possible ABI change detected in exports!" +else + echo "exports check: OK" +fi + +diff -u ${signatures_file}.sort ${signatures_file_check}.sort +if test "x$?" != "x0" ; then + echo "WARNING: possible ABI change detected in signatures!" +else + echo "signatures check: OK" +fi diff --git a/lib/tdb/script/abi_checks_gcc.sh b/lib/tdb/script/abi_checks_gcc.sh new file mode 100755 index 0000000000..f0e02f4825 --- /dev/null +++ b/lib/tdb/script/abi_checks_gcc.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +make clean + +mkdir -p abi/common +mkdir -p abi/tools +ABI_CHECKS="-aux-info abi/\$@.X" +make ABI_CHECK="$ABI_CHECKS" CC="/usr/bin/gcc" + +for i in abi/*/*.X; do cat $i | grep 'tdb\.h'; done | sort | uniq | awk -F "extern " '{ print $2 }' | sort > abi/signatures +grep '^extern' include/tdb.h | grep -v '"C"' | sort | uniq | awk -F "extern " '{ print $2 }' >> abi/signatures + +cat > abi/exports << EOF +{ + global: +EOF +#Functions +cat abi/signatures | grep "(" | awk -F '(' '{ print $1 }' | awk -F ' ' '{ print " "$NF";" }' | tr -d '*' | sort >> abi/exports +#global vars +cat abi/signatures | grep -v "(" | awk -F ';' '{print $1 }' | awk -F ' ' '{ print " "$NF";" }' | tr -d '*' | sort >> abi/exports +cat >> abi/exports << EOF + + local: *; +}; +EOF + +diff -u tdb.signatures abi/signatures +if [ "$?" != "0" ]; then + echo "WARNING: Possible ABI Change!!" +fi + +diff -u tdb.exports abi/exports +if [ "$?" != "0" ]; then + echo "WARNING: Export file may be outdated!!" +fi diff --git a/lib/tdb/script/mksigs.pl b/lib/tdb/script/mksigs.pl new file mode 100755 index 0000000000..755cd79603 --- /dev/null +++ b/lib/tdb/script/mksigs.pl @@ -0,0 +1,183 @@ +#!/usr/bin/perl + +# mksigs.pl - extract signatures from C headers +# +# Copyright (C) Michael Adam 2009 +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# USAGE: cat $header_files | mksigs.pl > $signature_file +# +# The header files to parse are read from stdin. +# The output is in a form as produced by gcc with the -aux-info switch +# and printed to stdout. + +use strict; +use warnings; + +my $in_comment = 0; +my $extern_C_block = 0; + +while (my $LINE = <>) { + # find end of started multi-line-comment + if ($in_comment) { + if ($LINE =~ /^.*?\*\/(.*)$/) { + $LINE = $1; + $in_comment = 0; + } else { + # whole line within comment + next; + } + } + + # strip C++-style comments + $LINE =~ s/^(.*?)\/\/.*$/$1/; + + # strip in-line-comments: + while ($LINE =~ /\/\*.*?\*\//) { + $LINE =~ s/\/\*.*?\*\///; + } + + # find starts of multi-line-comments + if ($LINE =~ /^(.*)\/\*/) { + $in_comment = 1; + $LINE = $1; + } + + # skip empty lines + next if $LINE =~ /^\s*$/; + + # remove leading spaces + $LINE =~ s/^\s*(.*)$/$1/; + + # concatenate lines split with "\" (usually macro defines) + while ($LINE =~ /^(.*?)\s+\\$/) { + my $LINE2 = <>; + $LINE = $1; + $LINE2 =~ s/^\s*(.*)$/$1/; + $LINE .= " " . $LINE2; + } + + # remove all preprocessor directives + next if ($LINE =~ /^#/); + + if ($LINE =~ /^extern\s+"C"\s+\{/) { + $extern_C_block = 1; + next; + } + + if (($LINE =~ /^[^\{]*\}/) and $extern_C_block) { + $extern_C_block = 0; + next; + } + + $LINE =~ s/^extern\s//; + + # concatenate braces stretched over multiple lines + # (from structs or enums) + my $REST = $LINE; + my $braces = 0; + while (($REST =~ /[\{\}]/) or ($braces)) { + while ($REST =~ /[\{\}]/) { + # collect opening + while ($REST =~ /^[^\{\}]*\{(.*)$/) { + $braces++; + $REST = $1; + } + + # collect closing + while ($REST =~ /^[^\{\}]*\}(.*)$/) { + $braces--; + $REST = $1; + } + } + + # concatenate if not balanced + if ($braces) { + if (my $LINE2 = <>) { + $LINE2 =~ s/^\s*(.*)$/$1/; + chomp($LINE); + $LINE .= " " . $LINE2; + chomp $REST; + $REST .= " " . $LINE2; + } else { + print "ERROR: unbalanced braces ($braces)\n"; + last; + } + } + } + + # concetenate function prototypes that stretch over multiple lines + $REST = $LINE; + my $parenthesis = 0; + while (($REST =~ /[\(\)]/) or ($parenthesis)) { + while ($REST =~ /[\(\)]/) { + # collect opening + while ($REST =~ /^[^\(\)]*\((.*)$/) { + $parenthesis++; + $REST = $1; + } + + # collect closing + while ($REST =~ /^[^\(\)]*\)(.*)$/) { + $parenthesis--; + $REST = $1; + } + } + + # concatenate if not balanced + if ($parenthesis) { + if (my $LINE2 = <>) { + $LINE2 =~ s/^\s*(.*)$/$1/; + chomp($LINE); + $LINE .= " " . $LINE2; + chomp($REST); + $REST .= " " . $LINE2; + } else { + print "ERROR: unbalanced parantheses ($parenthesis)\n"; + last; + } + } + } + + next if ($LINE =~ /^typedef\s/); + next if ($LINE =~ /^enum\s+[^\{\(]+\s+\{/); + next if ($LINE =~ /^struct\s+[^\{\(]+\s+\{.*\}\s*;/); + next if ($LINE =~ /^struct\s+[a-zA-Z0-9_]+\s*;/); + + # remove trailing spaces + $LINE =~ s/(.*?)\s*$/$1/; + + $LINE =~ s/^(.*\))\s+PRINTF_ATTRIBUTE\([^\)]*\)(\s*[;,])/$1$2/; + $LINE =~ s/^(.*\))\s*[a-zA-Z0-9_]+\s*;$/$1;/; + + # remove parameter names - slightly too coarse probably + $LINE =~ s/([\s\(]\*?)[_0-9a-zA-Z]+\s*([,\)])/$1$2/g; + + # remedy (void) from last line + $LINE =~ s/\(\)/(void)/g; + + # normalize spaces + $LINE =~ s/\s*\)\s*/)/g; + $LINE =~ s/\s*\(\s*/ (/g; + $LINE =~ s/\s*,\s*/, /g; + + # normalize unsigned + $LINE =~ s/([\s,\(])unsigned([,\)])/$1unsigned int$2/g; + + # normalize bool + $LINE =~ s/(\b)bool(\b)/_Bool/g; + + print $LINE . "\n"; +} diff --git a/lib/tdb/script/mksyms.awk b/lib/tdb/script/mksyms.awk new file mode 100644 index 0000000000..ca14da0f21 --- /dev/null +++ b/lib/tdb/script/mksyms.awk @@ -0,0 +1,76 @@ +# +# mksyms.awk +# +# Extract symbols to export from C-header files. +# output in version-script format for linking shared libraries. +# +# Copyright (C) 2008 Micheal Adam <obnox@samba.org> +# +BEGIN { + inheader=0; + current_file=""; + print "#" + print "# This file is automatically generated with \"make symbols\". DO NOT EDIT " + print "#" + print "{" + print "\tglobal:" +} + +END { + print"" + print "\tlocal: *;" + print "};" +} + +{ + if (FILENAME!=current_file) { + print "\t\t# The following definitions come from",FILENAME + current_file=FILENAME + } + if (inheader) { + if (match($0,"[)][^()]*[;][ \t]*$")) { + inheader = 0; + } + next; + } +} + +/^static/ || /^[ \t]*typedef/ || !/^[a-zA-Z\_]/ { + next; +} + +/^extern[ \t]+[^()]+[;][ \t]*$/ { + gsub(/[^ \t]+[ \t]+/, ""); + sub(/[;][ \t]*$/, ""); + printf "\t\t%s;\n", $0; + next; +} + +# look for function headers: +{ + gotstart = 0; + if ($0 ~ /^[A-Za-z_][A-Za-z0-9_]+/) { + gotstart = 1; + } + if(!gotstart) { + next; + } +} + +/[_A-Za-z0-9]+[ \t]*[(].*[)][^()]*;[ \t]*$/ { + sub(/[(].*$/, ""); + gsub(/[^ \t]+[ \t]+/, ""); + gsub(/^[*]+/, ""); + printf "\t\t%s;\n",$0; + next; +} + +/[_A-Za-z0-9]+[ \t]*[(]/ { + inheader=1; + sub(/[(].*$/, ""); + gsub(/[^ \t]+[ \t]+/, ""); + gsub(/^[*]/, ""); + printf "\t\t%s;\n",$0; + next; +} + diff --git a/lib/tdb/script/mksyms.sh b/lib/tdb/script/mksyms.sh new file mode 100755 index 0000000000..714d55abae --- /dev/null +++ b/lib/tdb/script/mksyms.sh @@ -0,0 +1,45 @@ +#! /bin/sh + +# +# mksyms.sh +# +# Extract symbols to export from C-header files. +# output in version-script format for linking shared libraries. +# +# This is the shell wrapper for the mksyms.awk core script. +# +# Copyright (C) 2008 Micheal Adam <obnox@samba.org> +# + +LANG=C; export LANG +LC_ALL=C; export LC_ALL +LC_COLLATE=C; export LC_COLLATE + +if [ $# -lt 2 ] +then + echo "Usage: $0 awk output_file header_files" + exit 1 +fi + +awk="$1" +shift + +symsfile="$1" +shift +symsfile_tmp="$symsfile.$$.tmp~" + +proto_src="`echo $@ | tr ' ' '\n' | sort | uniq `" + +echo creating $symsfile + +mkdir -p `dirname $symsfile` + +${awk} -f `dirname $0`/mksyms.awk $proto_src > $symsfile_tmp + +if cmp -s $symsfile $symsfile_tmp 2>/dev/null +then + echo "$symsfile unchanged" + rm $symsfile_tmp +else + mv $symsfile_tmp $symsfile +fi diff --git a/lib/tdb/tdb.exports b/lib/tdb/tdb.exports new file mode 100644 index 0000000000..cf287d8f32 --- /dev/null +++ b/lib/tdb/tdb.exports @@ -0,0 +1,65 @@ +{ + global: + tdb_add_flags; + tdb_append; + tdb_chainlock; + tdb_chainlock_mark; + tdb_chainlock_nonblock; + tdb_chainlock_read; + tdb_chainlock_unmark; + tdb_chainunlock; + tdb_chainunlock_read; + tdb_check; + tdb_close; + tdb_delete; + tdb_dump_all; + tdb_enable_seqnum; + tdb_error; + tdb_errorstr; + tdb_exists; + tdb_fd; + tdb_fetch; + tdb_firstkey; + tdb_freelist_size; + tdb_get_flags; + tdb_get_logging_private; + tdb_get_seqnum; + tdb_hash_size; + tdb_increment_seqnum_nonblock; + tdb_lockall; + tdb_lockall_mark; + tdb_lockall_nonblock; + tdb_lockall_read; + tdb_lockall_read_nonblock; + tdb_lockall_unmark; + tdb_log_fn; + tdb_map_size; + tdb_name; + tdb_nextkey; + tdb_open; + tdb_open_ex; + tdb_parse_record; + tdb_printfreelist; + tdb_remove_flags; + tdb_reopen; + tdb_reopen_all; + tdb_repack; + tdb_setalarm_sigptr; + tdb_set_logging_function; + tdb_set_max_dead; + tdb_store; + tdb_transaction_cancel; + tdb_transaction_commit; + tdb_transaction_prepare_commit; + tdb_transaction_recover; + tdb_transaction_start; + tdb_traverse; + tdb_traverse_read; + tdb_unlockall; + tdb_unlockall_read; + tdb_validate_freelist; + tdb_wipe_all; + tdb_null; + + local: *; +}; diff --git a/lib/tdb/tdb.mk b/lib/tdb/tdb.mk index b786bdbf54..ecc6f9fd08 100644 --- a/lib/tdb/tdb.mk +++ b/lib/tdb/tdb.mk @@ -26,13 +26,21 @@ bin/tdbdump$(EXEEXT): tools/tdbdump.o $(TDB_LIB) bin/tdbbackup$(EXEEXT): tools/tdbbackup.o $(TDB_LIB) $(CC) $(CFLAGS) $(LDFLAGS) -o bin/tdbbackup tools/tdbbackup.o -L. -ltdb +test:: abi_checks + test:: bin/tdbtorture$(EXEEXT) $(TDB_SONAME) $(LIB_PATH_VAR)=. bin/tdbtorture$(EXEEXT) +abi_checks:: + @echo ABI checks: + @./script/abi_checks.sh tdb include/tdb.h + clean:: rm -f test.db test.tdb torture.tdb test.gdbm rm -f $(TDB_SONAME) $(TDB_SOLIB) $(TDB_STLIB) libtdb.$(SHLIBEXT) rm -f $(ALL_PROGS) tdb.pc + rm -f tdb.exports.sort tdb.exports.check tdb.exports.check.sort + rm -f tdb.signatures.sort tdb.signatures.check tdb.signatures.check.sort build-python:: tdb.$(SHLIBEXT) @@ -43,7 +51,20 @@ tdb.$(SHLIBEXT): libtdb.$(SHLIBEXT) pytdb.o $(SHLD) $(SHLD_FLAGS) -o $@ pytdb.o -L. -ltdb `$(PYTHON_CONFIG) --ldflags` install:: installdirs installbin installheaders installlibs \ - $(PYTHON_INSTALL_TARGET) + $(PYTHON_INSTALL_TARGET) installdocs + +doc:: manpages/tdbbackup.8 manpages/tdbdump.8 manpages/tdbtool.8 + +.SUFFIXES: .8.xml .8 + +.8.xml.8: + -test -z "$(XSLTPROC)" || $(XSLTPROC) -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< + +installdocs:: + ${INSTALLCMD} -d $(DESTDIR)$(mandir)/man8 + for I in manpages/*.8; do \ + ${INSTALLCMD} -m 644 $$I $(DESTDIR)$(mandir)/man8; \ + done install-python:: build-python mkdir -p $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1, prefix='$(prefix)')"` @@ -70,6 +91,10 @@ installheaders:: installdirs installlibs:: all installdirs cp tdb.pc $(DESTDIR)$(libdir)/pkgconfig cp $(TDB_STLIB) $(TDB_SOLIB) $(DESTDIR)$(libdir) + rm -f $(DESTDIR)$(libdir)/libtdb.$(SHLIBEXT) + ln -s $(TDB_SOLIB) $(DESTDIR)$(libdir)/libtdb.$(SHLIBEXT) + rm -f $(DESTDIR)$(libdir)/$(TDB_SONAME) + ln -s $(TDB_SOLIB) $(DESTDIR)$(libdir)/$(TDB_SONAME) $(TDB_STLIB): $(TDB_OBJ) ar -rv $(TDB_STLIB) $(TDB_OBJ) diff --git a/lib/tdb/tdb.signatures b/lib/tdb/tdb.signatures new file mode 100644 index 0000000000..93edb071be --- /dev/null +++ b/lib/tdb/tdb.signatures @@ -0,0 +1,60 @@ +const char *tdb_errorstr (struct tdb_context *); +const char *tdb_name (struct tdb_context *); +enum TDB_ERROR tdb_error (struct tdb_context *); +int tdb_append (struct tdb_context *, TDB_DATA, TDB_DATA); +int tdb_chainlock_mark (struct tdb_context *, TDB_DATA); +int tdb_chainlock_nonblock (struct tdb_context *, TDB_DATA); +int tdb_chainlock_read (struct tdb_context *, TDB_DATA); +int tdb_chainlock (struct tdb_context *, TDB_DATA); +int tdb_chainlock_unmark (struct tdb_context *, TDB_DATA); +int tdb_chainunlock_read (struct tdb_context *, TDB_DATA); +int tdb_chainunlock (struct tdb_context *, TDB_DATA); +int tdb_close (struct tdb_context *); +int tdb_delete (struct tdb_context *, TDB_DATA); +int tdb_exists (struct tdb_context *, TDB_DATA); +int tdb_fd (struct tdb_context *); +int tdb_freelist_size (struct tdb_context *); +int tdb_get_flags (struct tdb_context *); +int tdb_get_seqnum (struct tdb_context *); +int tdb_hash_size (struct tdb_context *); +int tdb_lockall_mark (struct tdb_context *); +int tdb_lockall_nonblock (struct tdb_context *); +int tdb_lockall_read_nonblock (struct tdb_context *); +int tdb_lockall_read (struct tdb_context *); +int tdb_lockall (struct tdb_context *); +int tdb_lockall_unmark (struct tdb_context *); +int tdb_parse_record (struct tdb_context *, TDB_DATA, int (*) (TDB_DATA, TDB_DATA, void *), void *); +int tdb_printfreelist (struct tdb_context *); +int tdb_reopen_all (int); +int tdb_reopen (struct tdb_context *); +int tdb_repack (struct tdb_context *); +int tdb_store (struct tdb_context *, TDB_DATA, TDB_DATA, int); +int tdb_transaction_cancel (struct tdb_context *); +int tdb_transaction_commit (struct tdb_context *); +int tdb_transaction_prepare_commit (struct tdb_context *); +int tdb_transaction_recover (struct tdb_context *); +int tdb_transaction_start (struct tdb_context *); +int tdb_traverse_read (struct tdb_context *, tdb_traverse_func, void *); +int tdb_traverse (struct tdb_context *, tdb_traverse_func, void *); +int tdb_unlockall_read (struct tdb_context *); +int tdb_unlockall (struct tdb_context *); +int tdb_validate_freelist (struct tdb_context *, int *); +int tdb_wipe_all (struct tdb_context *); +size_t tdb_map_size (struct tdb_context *); +struct tdb_context *tdb_open (const char *, int, int, int, mode_t); +struct tdb_context *tdb_open_ex (const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func); +TDB_DATA tdb_fetch (struct tdb_context *, TDB_DATA); +TDB_DATA tdb_firstkey (struct tdb_context *); +TDB_DATA tdb_nextkey (struct tdb_context *, TDB_DATA); +tdb_log_func tdb_log_fn (struct tdb_context *); +void tdb_add_flags (struct tdb_context *, unsigned int); +void tdb_dump_all (struct tdb_context *); +void tdb_enable_seqnum (struct tdb_context *); +void *tdb_get_logging_private (struct tdb_context *); +void tdb_increment_seqnum_nonblock (struct tdb_context *); +void tdb_remove_flags (struct tdb_context *, unsigned int); +void tdb_setalarm_sigptr (struct tdb_context *, volatile sig_atomic_t *); +void tdb_set_logging_function (struct tdb_context *, const struct tdb_logging_context *); +void tdb_set_max_dead (struct tdb_context *, int); +int tdb_check (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *); +TDB_DATA tdb_null; diff --git a/lib/tdb/tools/tdbbackup.c b/lib/tdb/tools/tdbbackup.c index 83c0e16399..6aca8dd99c 100644 --- a/lib/tdb/tools/tdbbackup.c +++ b/lib/tdb/tools/tdbbackup.c @@ -53,6 +53,21 @@ static int failed; +static struct tdb_logging_context log_ctx; + +#ifdef PRINTF_ATTRIBUTE +static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4); +#endif +static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); + fflush(stdout); +} + static char *add_suffix(const char *name, const char *suffix) { char *ret; @@ -107,7 +122,8 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size) } /* open the old tdb */ - tdb = tdb_open(old_name, 0, 0, O_RDWR, 0); + tdb = tdb_open_ex(old_name, 0, 0, + O_RDWR, 0, &log_ctx, NULL); if (!tdb) { printf("Failed to open %s\n", old_name); free(tmp_name); @@ -116,10 +132,11 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size) /* create the new tdb */ unlink(tmp_name); - tdb_new = tdb_open(tmp_name, - hash_size ? hash_size : tdb_hash_size(tdb), - TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, - st.st_mode & 0777); + tdb_new = tdb_open_ex(tmp_name, + hash_size ? hash_size : tdb_hash_size(tdb), + TDB_DEFAULT, + O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777, + &log_ctx, NULL); if (!tdb_new) { perror(tmp_name); free(tmp_name); @@ -170,7 +187,11 @@ static int backup_tdb(const char *old_name, const char *new_name, int hash_size) /* close the new tdb and re-open read-only */ tdb_close(tdb_new); - tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0); + tdb_new = tdb_open_ex(tmp_name, + 0, + TDB_DEFAULT, + O_RDONLY, 0, + &log_ctx, NULL); if (!tdb_new) { fprintf(stderr,"failed to reopen %s\n", tmp_name); unlink(tmp_name); @@ -211,7 +232,8 @@ static int verify_tdb(const char *fname, const char *bak_name) int count = -1; /* open the tdb */ - tdb = tdb_open(fname, 0, 0, O_RDONLY, 0); + tdb = tdb_open_ex(fname, 0, 0, + O_RDONLY, 0, &log_ctx, NULL); /* traverse the tdb, then close it */ if (tdb) { @@ -264,6 +286,8 @@ static void usage(void) int hashsize = 0; const char *suffix = ".bak"; + log_ctx.log_fn = tdb_log; + while ((c = getopt(argc, argv, "vhs:n:")) != -1) { switch (c) { case 'h': diff --git a/lib/tdb/tools/tdbdump.c b/lib/tdb/tools/tdbdump.c index 8d930383b0..027fda3d42 100644 --- a/lib/tdb/tools/tdbdump.c +++ b/lib/tdb/tools/tdbdump.c @@ -65,8 +65,8 @@ static int dump_tdb(const char *fname, const char *keyname) if (!keyname) { tdb_traverse(tdb, traverse_fn, NULL); } else { - key.dptr = discard_const_p(uint8_t,keyname); - key.dsize = strlen( keyname); + key.dptr = discard_const_p(uint8_t, keyname); + key.dsize = strlen(keyname); value = tdb_fetch(tdb, key); if (!value.dptr) { return 1; diff --git a/lib/tdb/tools/tdbtool.c b/lib/tdb/tools/tdbtool.c index b4ec095a39..2ba7efc8ab 100644 --- a/lib/tdb/tools/tdbtool.c +++ b/lib/tdb/tools/tdbtool.c @@ -40,6 +40,9 @@ static int disable_mmap; enum commands { CMD_CREATE_TDB, CMD_OPEN_TDB, + CMD_TRANSACTION_START, + CMD_TRANSACTION_COMMIT, + CMD_TRANSACTION_CANCEL, CMD_ERASE, CMD_DUMP, CMD_INSERT, @@ -70,6 +73,9 @@ typedef struct { COMMAND_TABLE cmd_table[] = { {"create", CMD_CREATE_TDB}, {"open", CMD_OPEN_TDB}, + {"transaction_start", CMD_TRANSACTION_START}, + {"transaction_commit", CMD_TRANSACTION_COMMIT}, + {"transaction_cancel", CMD_TRANSACTION_CANCEL}, {"erase", CMD_ERASE}, {"dump", CMD_DUMP}, {"insert", CMD_INSERT}, @@ -109,6 +115,18 @@ static double _end_timer(void) (tp2.tv_usec - tp1.tv_usec)*1.0e-6); } +#ifdef PRINTF_ATTRIBUTE +static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4); +#endif +static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} + /* a tdb tool for manipulating a tdb database */ static TDB_CONTEXT *tdb; @@ -169,6 +187,9 @@ static void help(void) "tdbtool: \n" " create dbname : create a database\n" " open dbname : open an existing database\n" +" transaction_start : start a transaction\n" +" transaction_commit : commit a transaction\n" +" transaction_cancel : cancel a transaction\n" " erase : erase the database\n" " dump : dump the database as strings\n" " keys : dump the database keys as strings\n" @@ -182,6 +203,7 @@ static void help(void) " list : print the database hash table and freelist\n" " free : print the database freelist\n" " check : check the integrity of an opened database\n" +" speed : perform speed tests on the database\n" " ! command : execute system command\n" " 1 | first : print the first record\n" " n | next : print the next record\n" @@ -197,9 +219,12 @@ static void terror(const char *why) static void create_tdb(const char *tdbname) { + struct tdb_logging_context log_ctx; + log_ctx.log_fn = tdb_log; + if (tdb) tdb_close(tdb); - tdb = tdb_open(tdbname, 0, TDB_CLEAR_IF_FIRST | (disable_mmap?TDB_NOMMAP:0), - O_RDWR | O_CREAT | O_TRUNC, 0600); + tdb = tdb_open_ex(tdbname, 0, TDB_CLEAR_IF_FIRST | (disable_mmap?TDB_NOMMAP:0), + O_RDWR | O_CREAT | O_TRUNC, 0600, &log_ctx, NULL); if (!tdb) { printf("Could not create %s: %s\n", tdbname, strerror(errno)); } @@ -207,8 +232,12 @@ static void create_tdb(const char *tdbname) static void open_tdb(const char *tdbname) { + struct tdb_logging_context log_ctx; + log_ctx.log_fn = tdb_log; + if (tdb) tdb_close(tdb); - tdb = tdb_open(tdbname, 0, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600); + tdb = tdb_open_ex(tdbname, 0, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600, + &log_ctx, NULL); if (!tdb) { printf("Could not open %s: %s\n", tdbname, strerror(errno)); } @@ -390,17 +419,71 @@ static void info_tdb(void) static void speed_tdb(const char *tlimit) { + const char *str = "store test", *str2 = "transaction test"; unsigned timelimit = tlimit?atoi(tlimit):0; double t; - int ops=0; - if (timelimit == 0) timelimit = 10; + int ops; + if (timelimit == 0) timelimit = 5; + + ops = 0; + printf("Testing store speed for %u seconds\n", timelimit); + _start_timer(); + do { + long int r = random(); + TDB_DATA key, dbuf; + key.dptr = discard_const_p(uint8_t, str); + key.dsize = strlen((char *)key.dptr); + dbuf.dptr = (uint8_t *) &r; + dbuf.dsize = sizeof(r); + tdb_store(tdb, key, dbuf, TDB_REPLACE); + t = _end_timer(); + ops++; + } while (t < timelimit); + printf("%10.3f ops/sec\n", ops/t); + + ops = 0; + printf("Testing fetch speed for %u seconds\n", timelimit); + _start_timer(); + do { + long int r = random(); + TDB_DATA key, dbuf; + key.dptr = discard_const_p(uint8_t, str); + key.dsize = strlen((char *)key.dptr); + dbuf.dptr = (uint8_t *) &r; + dbuf.dsize = sizeof(r); + tdb_fetch(tdb, key); + t = _end_timer(); + ops++; + } while (t < timelimit); + printf("%10.3f ops/sec\n", ops/t); + + ops = 0; + printf("Testing transaction speed for %u seconds\n", timelimit); + _start_timer(); + do { + long int r = random(); + TDB_DATA key, dbuf; + key.dptr = discard_const_p(uint8_t, str2); + key.dsize = strlen((char *)key.dptr); + dbuf.dptr = (uint8_t *) &r; + dbuf.dsize = sizeof(r); + tdb_transaction_start(tdb); + tdb_store(tdb, key, dbuf, TDB_REPLACE); + tdb_transaction_commit(tdb); + t = _end_timer(); + ops++; + } while (t < timelimit); + printf("%10.3f ops/sec\n", ops/t); + + ops = 0; printf("Testing traverse speed for %u seconds\n", timelimit); _start_timer(); - while ((t=_end_timer()) < timelimit) { + do { tdb_traverse(tdb, traverse_fn, NULL); - printf("%10.3f ops/sec\r", (++ops)/t); - } - printf("\n"); + t = _end_timer(); + ops++; + } while (t < timelimit); + printf("%10.3f ops/sec\n", ops/t); } static void toggle_mmap(void) @@ -455,25 +538,22 @@ static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey) print_rec(the_tdb, *pkey, dbuf, NULL); } -static int test_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +static int count(TDB_DATA key, TDB_DATA data, void *private_data) { + (*(unsigned int *)private_data)++; return 0; } static void check_db(TDB_CONTEXT *the_tdb) { - int tdbcount=-1; - if (the_tdb) { - tdbcount = tdb_traverse(the_tdb, test_fn, NULL); - } else { + int tdbcount = 0; + if (!the_tdb) printf("Error: No database opened!\n"); - } - - if (tdbcount<0) { + else if (tdb_check(the_tdb, count, &tdbcount) == -1) printf("Integrity check for the opened database failed.\n"); - } else { - printf("Database integrity is OK and has %d records.\n", tdbcount); - } + else + printf("Database integrity is OK and has %d records.\n", + tdbcount); } static int do_command(void) @@ -483,117 +563,129 @@ static int do_command(void) int cmd_len; if (cmdname && strlen(cmdname) == 0) { - mycmd = CMD_NEXT; + mycmd = CMD_NEXT; } else { - while (ctp->name) { - cmd_len = strlen(ctp->name); - if (strncmp(ctp->name,cmdname,cmd_len) == 0) { - mycmd = ctp->cmd; - break; + while (ctp->name) { + cmd_len = strlen(ctp->name); + if (strncmp(ctp->name,cmdname,cmd_len) == 0) { + mycmd = ctp->cmd; + break; + } + ctp++; } - ctp++; - } } switch (mycmd) { case CMD_CREATE_TDB: - bIterate = 0; - create_tdb(arg1); - return 0; - case CMD_OPEN_TDB: - bIterate = 0; - open_tdb(arg1); - return 0; - case CMD_SYSTEM: - /* Shell command */ - if (system(arg1) == -1) { - terror("system() call failed\n"); - } - return 0; - case CMD_QUIT: - return 1; - default: - /* all the rest require a open database */ - if (!tdb) { bIterate = 0; - terror("database not open"); - help(); + create_tdb(arg1); return 0; - } - switch (mycmd) { - case CMD_ERASE: - bIterate = 0; - tdb_traverse(tdb, do_delete_fn, NULL); - return 0; - case CMD_DUMP: - bIterate = 0; - tdb_traverse(tdb, print_rec, NULL); - return 0; - case CMD_INSERT: - bIterate = 0; - insert_tdb(arg1, arg1len,arg2,arg2len); - return 0; - case CMD_MOVE: - bIterate = 0; - move_rec(arg1,arg1len,arg2); - return 0; - case CMD_STORE: - bIterate = 0; - store_tdb(arg1,arg1len,arg2,arg2len); - return 0; - case CMD_SHOW: - bIterate = 0; - show_tdb(arg1, arg1len); - return 0; - case CMD_KEYS: - tdb_traverse(tdb, print_key, NULL); - return 0; - case CMD_HEXKEYS: - tdb_traverse(tdb, print_hexkey, NULL); - return 0; - case CMD_DELETE: + case CMD_OPEN_TDB: bIterate = 0; - delete_tdb(arg1,arg1len); - return 0; - case CMD_LIST_HASH_FREE: - tdb_dump_all(tdb); - return 0; - case CMD_LIST_FREE: - tdb_printfreelist(tdb); - return 0; - case CMD_INFO: - info_tdb(); - return 0; - case CMD_SPEED: - speed_tdb(arg1); - return 0; - case CMD_MMAP: - toggle_mmap(); - return 0; - case CMD_FIRST: - bIterate = 1; - first_record(tdb, &iterate_kbuf); - return 0; - case CMD_NEXT: - if (bIterate) - next_record(tdb, &iterate_kbuf); + open_tdb(arg1); return 0; - case CMD_CHECK: - check_db(tdb); - return 0; - case CMD_HELP: - help(); + case CMD_SYSTEM: + /* Shell command */ + if (system(arg1) == -1) { + terror("system() call failed\n"); + } return 0; - case CMD_CREATE_TDB: - case CMD_OPEN_TDB: - case CMD_SYSTEM: - case CMD_QUIT: - /* - * unhandled commands. cases included here to avoid compiler - * warnings. - */ - return 0; - } + case CMD_QUIT: + return 1; + default: + /* all the rest require a open database */ + if (!tdb) { + bIterate = 0; + terror("database not open"); + help(); + return 0; + } + switch (mycmd) { + case CMD_TRANSACTION_START: + bIterate = 0; + tdb_transaction_start(tdb); + return 0; + case CMD_TRANSACTION_COMMIT: + bIterate = 0; + tdb_transaction_commit(tdb); + return 0; + case CMD_TRANSACTION_CANCEL: + bIterate = 0; + tdb_transaction_cancel(tdb); + return 0; + case CMD_ERASE: + bIterate = 0; + tdb_traverse(tdb, do_delete_fn, NULL); + return 0; + case CMD_DUMP: + bIterate = 0; + tdb_traverse(tdb, print_rec, NULL); + return 0; + case CMD_INSERT: + bIterate = 0; + insert_tdb(arg1, arg1len,arg2,arg2len); + return 0; + case CMD_MOVE: + bIterate = 0; + move_rec(arg1,arg1len,arg2); + return 0; + case CMD_STORE: + bIterate = 0; + store_tdb(arg1,arg1len,arg2,arg2len); + return 0; + case CMD_SHOW: + bIterate = 0; + show_tdb(arg1, arg1len); + return 0; + case CMD_KEYS: + tdb_traverse(tdb, print_key, NULL); + return 0; + case CMD_HEXKEYS: + tdb_traverse(tdb, print_hexkey, NULL); + return 0; + case CMD_DELETE: + bIterate = 0; + delete_tdb(arg1,arg1len); + return 0; + case CMD_LIST_HASH_FREE: + tdb_dump_all(tdb); + return 0; + case CMD_LIST_FREE: + tdb_printfreelist(tdb); + return 0; + case CMD_INFO: + info_tdb(); + return 0; + case CMD_SPEED: + speed_tdb(arg1); + return 0; + case CMD_MMAP: + toggle_mmap(); + return 0; + case CMD_FIRST: + bIterate = 1; + first_record(tdb, &iterate_kbuf); + return 0; + case CMD_NEXT: + if (bIterate) + next_record(tdb, &iterate_kbuf); + return 0; + case CMD_CHECK: + check_db(tdb); + return 0; + case CMD_HELP: + help(); + return 0; + case CMD_CREATE_TDB: + case CMD_OPEN_TDB: + case CMD_SYSTEM: + case CMD_QUIT: + /* + * unhandled commands. cases included here to avoid compiler + * warnings. + */ + return 0; + } } return 0; @@ -601,88 +693,87 @@ static int do_command(void) static char *convert_string(char *instring, size_t *sizep) { - size_t length = 0; - char *outp, *inp; - char temp[3]; - - - outp = inp = instring; - - while (*inp) { - if (*inp == '\\') { - inp++; - if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) { - temp[0] = *inp++; - temp[1] = '\0'; - if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) { - temp[1] = *inp++; - temp[2] = '\0'; + size_t length = 0; + char *outp, *inp; + char temp[3]; + + outp = inp = instring; + + while (*inp) { + if (*inp == '\\') { + inp++; + if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) { + temp[0] = *inp++; + temp[1] = '\0'; + if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) { + temp[1] = *inp++; + temp[2] = '\0'; + } + *outp++ = (char)strtol((const char *)temp,NULL,16); + } else { + *outp++ = *inp++; + } + } else { + *outp++ = *inp++; } - *outp++ = (char)strtol((const char *)temp,NULL,16); - } else { - *outp++ = *inp++; - } - } else { - *outp++ = *inp++; + length++; } - length++; - } - *sizep = length; - return instring; + *sizep = length; + return instring; } int main(int argc, char *argv[]) { - cmdname = ""; - arg1 = NULL; - arg1len = 0; - arg2 = NULL; - arg2len = 0; - - if (argv[1]) { - cmdname = "open"; - arg1 = argv[1]; - do_command(); - cmdname = ""; + cmdname = ""; arg1 = NULL; - } + arg1len = 0; + arg2 = NULL; + arg2len = 0; + + if (argv[1]) { + cmdname = "open"; + arg1 = argv[1]; + do_command(); + cmdname = ""; + arg1 = NULL; + } - switch (argc) { + switch (argc) { case 1: case 2: - /* Interactive mode */ - while ((cmdname = tdb_getline("tdb> "))) { - arg2 = arg1 = NULL; - if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) { - arg1++; - arg2 = arg1; - while (*arg2) { - if (*arg2 == ' ') { - *arg2++ = '\0'; - break; - } - if ((*arg2++ == '\\') && (*arg2 == ' ')) { - arg2++; + /* Interactive mode */ + while ((cmdname = tdb_getline("tdb> "))) { + arg2 = arg1 = NULL; + if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) { + arg1++; + arg2 = arg1; + while (*arg2) { + if (*arg2 == ' ') { + *arg2++ = '\0'; + break; + } + if ((*arg2++ == '\\') && (*arg2 == ' ')) { + arg2++; + } + } } - } + if (arg1) arg1 = convert_string(arg1,&arg1len); + if (arg2) arg2 = convert_string(arg2,&arg2len); + if (do_command()) break; } - if (arg1) arg1 = convert_string(arg1,&arg1len); - if (arg2) arg2 = convert_string(arg2,&arg2len); - if (do_command()) break; - } - break; + break; case 5: - arg2 = convert_string(argv[4],&arg2len); + arg2 = convert_string(argv[4],&arg2len); case 4: - arg1 = convert_string(argv[3],&arg1len); + arg1 = convert_string(argv[3],&arg1len); case 3: - cmdname = argv[2]; + cmdname = argv[2]; default: - do_command(); - break; - } + do_command(); + break; + } - if (tdb) tdb_close(tdb); + if (tdb) tdb_close(tdb); - return 0; + return 0; } diff --git a/lib/tdb/tools/tdbtorture.c b/lib/tdb/tools/tdbtorture.c index 9265cf07aa..b0221a2503 100644 --- a/lib/tdb/tools/tdbtorture.c +++ b/lib/tdb/tools/tdbtorture.c @@ -18,6 +18,7 @@ #define STORE_PROB 4 #define APPEND_PROB 6 #define TRANSACTION_PROB 10 +#define TRANSACTION_PREPARE_PROB 2 #define LOCKSTORE_PROB 5 #define TRAVERSE_PROB 20 #define TRAVERSE_READ_PROB 20 @@ -28,6 +29,7 @@ static struct tdb_context *db; static int in_transaction; static int error_count; +static int always_transaction = 0; #ifdef PRINTF_ATTRIBUTE static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4); @@ -35,8 +37,11 @@ static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const c static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) { va_list ap; - - error_count++; + + /* trace level messages do not indicate an error */ + if (level != TDB_DEBUG_TRACE) { + error_count++; + } va_start(ap, format); vfprintf(stdout, format, ap); @@ -100,8 +105,16 @@ static void addrec_db(void) data.dptr = (unsigned char *)d; data.dsize = dlen+1; +#if REOPEN_PROB + if (in_transaction == 0 && random() % REOPEN_PROB == 0) { + tdb_reopen_all(0); + goto next; + } +#endif + #if TRANSACTION_PROB - if (in_transaction == 0 && random() % TRANSACTION_PROB == 0) { + if (in_transaction == 0 && + (always_transaction || random() % TRANSACTION_PROB == 0)) { if (tdb_transaction_start(db) != 0) { fatal("tdb_transaction_start failed"); } @@ -109,6 +122,11 @@ static void addrec_db(void) goto next; } if (in_transaction && random() % TRANSACTION_PROB == 0) { + if (random() % TRANSACTION_PREPARE_PROB == 0) { + if (tdb_transaction_prepare_commit(db) != 0) { + fatal("tdb_transaction_prepare_commit failed"); + } + } if (tdb_transaction_commit(db) != 0) { fatal("tdb_transaction_commit failed"); } @@ -124,13 +142,6 @@ static void addrec_db(void) } #endif -#if REOPEN_PROB - if (in_transaction == 0 && random() % REOPEN_PROB == 0) { - tdb_reopen_all(0); - goto next; - } -#endif - #if DELETE_PROB if (random() % DELETE_PROB == 0) { tdb_delete(db, key); @@ -200,7 +211,7 @@ static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, static void usage(void) { - printf("Usage: tdbtorture [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n"); + printf("Usage: tdbtorture [-t] [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n"); exit(0); } @@ -217,7 +228,7 @@ static void usage(void) struct tdb_logging_context log_ctx; log_ctx.log_fn = tdb_log; - while ((c = getopt(argc, argv, "n:l:s:H:h")) != -1) { + while ((c = getopt(argc, argv, "n:l:s:H:th")) != -1) { switch (c) { case 'n': num_procs = strtol(optarg, NULL, 0); @@ -231,6 +242,9 @@ static void usage(void) case 's': seed = strtol(optarg, NULL, 0); break; + case 't': + always_transaction = 1; + break; default: usage(); } @@ -256,8 +270,8 @@ static void usage(void) } if (i == 0) { - printf("testing with %d processes, %d loops, %d hash_size, seed=%d\n", - num_procs, num_loops, hash_size, seed); + printf("testing with %d processes, %d loops, %d hash_size, seed=%d%s\n", + num_procs, num_loops, hash_size, seed, always_transaction ? " (all within transactions)" : ""); } srand(seed + i); @@ -269,8 +283,20 @@ static void usage(void) if (error_count == 0) { tdb_traverse_read(db, NULL, NULL); + if (always_transaction) { + while (in_transaction) { + tdb_transaction_cancel(db); + in_transaction--; + } + if (tdb_transaction_start(db) != 0) + fatal("tdb_transaction_start failed"); + } tdb_traverse(db, traverse_fn, NULL); tdb_traverse(db, traverse_fn, NULL); + if (always_transaction) { + if (tdb_transaction_commit(db) != 0) + fatal("tdb_transaction_commit failed"); + } } tdb_close(db); @@ -310,6 +336,8 @@ static void usage(void) pids[j] = 0; } + free(pids); + if (error_count == 0) { printf("OK\n"); } diff --git a/lib/tdb/web/index.html b/lib/tdb/web/index.html index a53da6b8f7..99e8a2fe42 100644 --- a/lib/tdb/web/index.html +++ b/lib/tdb/web/index.html @@ -12,6 +12,12 @@ except that it allows multiple simultaneous writers and uses locking internally to keep writers from trampling on each other. TDB is also extremely small. +<h2>Download</h2> +You can download the latest releases of tdb from the <a +href="http://samba.org/ftp/tdb">tdb directory</a> on the samba public +source archive. + + <h2>Discussion and bug reports</h2> tdb does not currently have its own mailing list or bug tracking @@ -22,17 +28,17 @@ bugzilla</a> bug tracking system. <h2>Download</h2> -You can download the latest release either via rsync or git.<br> +You can download the latest code either via git or rsync.<br> <br> To fetch via git see the following guide:<br> <a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a><br> -Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/tdb directory.<br> +Once you have cloned the tree switch to the master branch and cd into the source/lib/tdb directory.<br> <br> To fetch via rsync use these commands: <pre> - rsync -Pavz samba.org::ftp/unpacked/tdb . - rsync -Pavz samba.org::ftp/unpacked/libreplace . + rsync -Pavz samba.org::ftp/unpacked/standalone_projects/lib/tdb . + rsync -Pavz samba.org::ftp/unpacked/standalone_projects/lib/replace . </pre> and build in tdb. It will find the replace library in the directory diff --git a/lib/tdr/TODO b/lib/tdr/TODO new file mode 100644 index 0000000000..5093afd438 --- /dev/null +++ b/lib/tdr/TODO @@ -0,0 +1 @@ +- Support read/write (to fd) as well as push/pull (to DATA_BLOB) diff --git a/lib/tdr/config.mk b/lib/tdr/config.mk new file mode 100644 index 0000000000..07506ec647 --- /dev/null +++ b/lib/tdr/config.mk @@ -0,0 +1,9 @@ +[SUBSYSTEM::TDR] +CFLAGS = -Ilib/tdr +PUBLIC_DEPENDENCIES = LIBTALLOC LIBSAMBA-UTIL + +TDR_OBJ_FILES = $(libtdrsrcdir)/tdr.o + +$(eval $(call proto_header_template,$(libtdrsrcdir)/tdr_proto.h,$(TDR_OBJ_FILES:.o=.c))) + +PUBLIC_HEADERS += $(libtdrsrcdir)/tdr.h diff --git a/lib/tdr/tdr.c b/lib/tdr/tdr.c new file mode 100644 index 0000000000..ce67003f8b --- /dev/null +++ b/lib/tdr/tdr.c @@ -0,0 +1,407 @@ +/* + Unix SMB/CIFS implementation. + + TDR (Trivial Data Representation) helper functions + Based loosely on ndr.c by Andrew Tridgell. + + Copyright (C) Jelmer Vernooij 2005 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/network.h" +#include "lib/tdr/tdr.h" + +#define TDR_BASE_MARSHALL_SIZE 1024 + +#define TDR_PUSH_NEED_BYTES(tdr, n) TDR_CHECK(tdr_push_expand(tdr, tdr->data.length+(n))) + +#define TDR_PULL_NEED_BYTES(tdr, n) do { \ + if ((n) > tdr->data.length || tdr->offset + (n) > tdr->data.length) { \ + return NT_STATUS_BUFFER_TOO_SMALL; \ + } \ +} while(0) + +#define TDR_BE(tdr) ((tdr)->flags & TDR_BIG_ENDIAN) + +#define TDR_CVAL(tdr, ofs) CVAL(tdr->data.data,ofs) +#define TDR_SVAL(tdr, ofs) (TDR_BE(tdr)?RSVAL(tdr->data.data,ofs):SVAL(tdr->data.data,ofs)) +#define TDR_IVAL(tdr, ofs) (TDR_BE(tdr)?RIVAL(tdr->data.data,ofs):IVAL(tdr->data.data,ofs)) +#define TDR_SCVAL(tdr, ofs, v) SCVAL(tdr->data.data,ofs,v) +#define TDR_SSVAL(tdr, ofs, v) do { if (TDR_BE(tdr)) { RSSVAL(tdr->data.data,ofs,v); } else SSVAL(tdr->data.data,ofs,v); } while (0) +#define TDR_SIVAL(tdr, ofs, v) do { if (TDR_BE(tdr)) { RSIVAL(tdr->data.data,ofs,v); } else SIVAL(tdr->data.data,ofs,v); } while (0) + +/** + expand the available space in the buffer to 'size' +*/ +NTSTATUS tdr_push_expand(struct tdr_push *tdr, uint32_t size) +{ + if (talloc_get_size(tdr->data.data) >= size) { + return NT_STATUS_OK; + } + + tdr->data.data = talloc_realloc(tdr, tdr->data.data, uint8_t, tdr->data.length + TDR_BASE_MARSHALL_SIZE); + + if (tdr->data.data == NULL) + return NT_STATUS_NO_MEMORY; + + return NT_STATUS_OK; +} + + +NTSTATUS tdr_pull_uint8(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint8_t *v) +{ + TDR_PULL_NEED_BYTES(tdr, 1); + *v = TDR_CVAL(tdr, tdr->offset); + tdr->offset += 1; + return NT_STATUS_OK; +} + +NTSTATUS tdr_push_uint8(struct tdr_push *tdr, const uint8_t *v) +{ + TDR_PUSH_NEED_BYTES(tdr, 1); + TDR_SCVAL(tdr, tdr->data.length, *v); + tdr->data.length += 1; + return NT_STATUS_OK; +} + +NTSTATUS tdr_print_uint8(struct tdr_print *tdr, const char *name, uint8_t *v) +{ + tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v); + return NT_STATUS_OK; +} + +NTSTATUS tdr_pull_uint16(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint16_t *v) +{ + TDR_PULL_NEED_BYTES(tdr, 2); + *v = TDR_SVAL(tdr, tdr->offset); + tdr->offset += 2; + return NT_STATUS_OK; +} + +NTSTATUS tdr_pull_uint1632(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint16_t *v) +{ + return tdr_pull_uint16(tdr, ctx, v); +} + +NTSTATUS tdr_push_uint16(struct tdr_push *tdr, const uint16_t *v) +{ + TDR_PUSH_NEED_BYTES(tdr, 2); + TDR_SSVAL(tdr, tdr->data.length, *v); + tdr->data.length += 2; + return NT_STATUS_OK; +} + +NTSTATUS tdr_push_uint1632(struct tdr_push *tdr, const uint16_t *v) +{ + return tdr_push_uint16(tdr, v); +} + +NTSTATUS tdr_print_uint16(struct tdr_print *tdr, const char *name, uint16_t *v) +{ + tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v); + return NT_STATUS_OK; +} + +NTSTATUS tdr_pull_uint32(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint32_t *v) +{ + TDR_PULL_NEED_BYTES(tdr, 4); + *v = TDR_IVAL(tdr, tdr->offset); + tdr->offset += 4; + return NT_STATUS_OK; +} + +NTSTATUS tdr_push_uint32(struct tdr_push *tdr, const uint32_t *v) +{ + TDR_PUSH_NEED_BYTES(tdr, 4); + TDR_SIVAL(tdr, tdr->data.length, *v); + tdr->data.length += 4; + return NT_STATUS_OK; +} + +NTSTATUS tdr_print_uint32(struct tdr_print *tdr, const char *name, uint32_t *v) +{ + tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v); + return NT_STATUS_OK; +} + +NTSTATUS tdr_pull_charset(struct tdr_pull *tdr, TALLOC_CTX *ctx, const char **v, uint32_t length, uint32_t el_size, charset_t chset) +{ + size_t ret; + + if (length == -1) { + switch (chset) { + case CH_DOS: + length = ascii_len_n((const char*)tdr->data.data+tdr->offset, tdr->data.length-tdr->offset); + break; + case CH_UTF16: + length = utf16_len_n(tdr->data.data+tdr->offset, tdr->data.length-tdr->offset); + break; + + default: + return NT_STATUS_INVALID_PARAMETER; + } + } + + if (length == 0) { + *v = talloc_strdup(ctx, ""); + return NT_STATUS_OK; + } + + TDR_PULL_NEED_BYTES(tdr, el_size*length); + + if (!convert_string_talloc_convenience(ctx, tdr->iconv_convenience, chset, CH_UNIX, tdr->data.data+tdr->offset, el_size*length, discard_const_p(void *, v), &ret, false)) { + return NT_STATUS_INVALID_PARAMETER; + } + + tdr->offset += length * el_size; + + return NT_STATUS_OK; +} + +NTSTATUS tdr_push_charset(struct tdr_push *tdr, const char **v, uint32_t length, uint32_t el_size, charset_t chset) +{ + size_t ret, required; + + if (length == -1) { + length = strlen(*v) + 1; /* Extra element for null character */ + } + + required = el_size * length; + TDR_PUSH_NEED_BYTES(tdr, required); + + if (!convert_string_convenience(tdr->iconv_convenience, CH_UNIX, chset, *v, strlen(*v), tdr->data.data+tdr->data.length, required, &ret, false)) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* Make sure the remaining part of the string is filled with zeroes */ + if (ret < required) { + memset(tdr->data.data+tdr->data.length+ret, 0, required-ret); + } + + tdr->data.length += required; + + return NT_STATUS_OK; +} + +NTSTATUS tdr_print_charset(struct tdr_print *tdr, const char *name, const char **v, uint32_t length, uint32_t el_size, charset_t chset) +{ + tdr->print(tdr, "%-25s: %s", name, *v); + return NT_STATUS_OK; +} + +/** + parse a hyper +*/ +NTSTATUS tdr_pull_hyper(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint64_t *v) +{ + TDR_PULL_NEED_BYTES(tdr, 8); + *v = TDR_IVAL(tdr, tdr->offset); + *v |= (uint64_t)(TDR_IVAL(tdr, tdr->offset+4)) << 32; + tdr->offset += 8; + return NT_STATUS_OK; +} + +/** + push a hyper +*/ +NTSTATUS tdr_push_hyper(struct tdr_push *tdr, uint64_t *v) +{ + TDR_PUSH_NEED_BYTES(tdr, 8); + TDR_SIVAL(tdr, tdr->data.length, ((*v) & 0xFFFFFFFF)); + TDR_SIVAL(tdr, tdr->data.length+4, ((*v)>>32)); + tdr->data.length += 8; + return NT_STATUS_OK; +} + +/** + push a NTTIME +*/ +NTSTATUS tdr_push_NTTIME(struct tdr_push *tdr, NTTIME *t) +{ + TDR_CHECK(tdr_push_hyper(tdr, t)); + return NT_STATUS_OK; +} + +/** + pull a NTTIME +*/ +NTSTATUS tdr_pull_NTTIME(struct tdr_pull *tdr, TALLOC_CTX *ctx, NTTIME *t) +{ + TDR_CHECK(tdr_pull_hyper(tdr, ctx, t)); + return NT_STATUS_OK; +} + +/** + push a time_t +*/ +NTSTATUS tdr_push_time_t(struct tdr_push *tdr, time_t *t) +{ + return tdr_push_uint32(tdr, (uint32_t *)t); +} + +/** + pull a time_t +*/ +NTSTATUS tdr_pull_time_t(struct tdr_pull *tdr, TALLOC_CTX *ctx, time_t *t) +{ + uint32_t tt; + TDR_CHECK(tdr_pull_uint32(tdr, ctx, &tt)); + *t = tt; + return NT_STATUS_OK; +} + +NTSTATUS tdr_print_time_t(struct tdr_print *tdr, const char *name, time_t *t) +{ + if (*t == (time_t)-1 || *t == 0) { + tdr->print(tdr, "%-25s: (time_t)%d", name, (int)*t); + } else { + tdr->print(tdr, "%-25s: %s", name, timestring(tdr, *t)); + } + + return NT_STATUS_OK; +} + +NTSTATUS tdr_print_NTTIME(struct tdr_print *tdr, const char *name, NTTIME *t) +{ + tdr->print(tdr, "%-25s: %s", name, nt_time_string(tdr, *t)); + + return NT_STATUS_OK; +} + +NTSTATUS tdr_print_DATA_BLOB(struct tdr_print *tdr, const char *name, DATA_BLOB *r) +{ + tdr->print(tdr, "%-25s: DATA_BLOB length=%u", name, r->length); + if (r->length) { + dump_data(10, r->data, r->length); + } + + return NT_STATUS_OK; +} + +#define TDR_ALIGN(l,n) (((l) & ((n)-1)) == 0?0:((n)-((l)&((n)-1)))) + +/* + push a DATA_BLOB onto the wire. +*/ +NTSTATUS tdr_push_DATA_BLOB(struct tdr_push *tdr, DATA_BLOB *blob) +{ + if (tdr->flags & TDR_ALIGN2) { + blob->length = TDR_ALIGN(tdr->data.length, 2); + } else if (tdr->flags & TDR_ALIGN4) { + blob->length = TDR_ALIGN(tdr->data.length, 4); + } else if (tdr->flags & TDR_ALIGN8) { + blob->length = TDR_ALIGN(tdr->data.length, 8); + } + + TDR_PUSH_NEED_BYTES(tdr, blob->length); + + memcpy(tdr->data.data+tdr->data.length, blob->data, blob->length); + return NT_STATUS_OK; +} + +/* + pull a DATA_BLOB from the wire. +*/ +NTSTATUS tdr_pull_DATA_BLOB(struct tdr_pull *tdr, TALLOC_CTX *ctx, DATA_BLOB *blob) +{ + uint32_t length; + + if (tdr->flags & TDR_ALIGN2) { + length = TDR_ALIGN(tdr->offset, 2); + } else if (tdr->flags & TDR_ALIGN4) { + length = TDR_ALIGN(tdr->offset, 4); + } else if (tdr->flags & TDR_ALIGN8) { + length = TDR_ALIGN(tdr->offset, 8); + } else if (tdr->flags & TDR_REMAINING) { + length = tdr->data.length - tdr->offset; + } else { + return NT_STATUS_INVALID_PARAMETER; + } + + if (tdr->data.length - tdr->offset < length) { + length = tdr->data.length - tdr->offset; + } + + TDR_PULL_NEED_BYTES(tdr, length); + + *blob = data_blob_talloc(tdr, tdr->data.data+tdr->offset, length); + tdr->offset += length; + return NT_STATUS_OK; +} + +struct tdr_push *tdr_push_init(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *ic) +{ + struct tdr_push *push = talloc_zero(mem_ctx, struct tdr_push); + + if (push == NULL) + return NULL; + + push->iconv_convenience = talloc_reference(push, ic); + + return push; +} + +struct tdr_pull *tdr_pull_init(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *ic) +{ + struct tdr_pull *pull = talloc_zero(mem_ctx, struct tdr_pull); + + if (pull == NULL) + return NULL; + + pull->iconv_convenience = talloc_reference(pull, ic); + + return pull; +} + +NTSTATUS tdr_push_to_fd(int fd, struct smb_iconv_convenience *iconv_convenience, tdr_push_fn_t push_fn, const void *p) +{ + struct tdr_push *push = tdr_push_init(NULL, iconv_convenience); + + if (push == NULL) + return NT_STATUS_NO_MEMORY; + + if (NT_STATUS_IS_ERR(push_fn(push, p))) { + DEBUG(1, ("Error pushing data\n")); + talloc_free(push); + return NT_STATUS_UNSUCCESSFUL; + } + + if (write(fd, push->data.data, push->data.length) < push->data.length) { + DEBUG(1, ("Error writing all data\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + talloc_free(push); + + return NT_STATUS_OK; +} + +void tdr_print_debug_helper(struct tdr_print *tdr, const char *format, ...) _PRINTF_ATTRIBUTE(2,3) +{ + va_list ap; + char *s = NULL; + int i; + + va_start(ap, format); + vasprintf(&s, format, ap); + va_end(ap); + + for (i=0;i<tdr->level;i++) { DEBUG(0,(" ")); } + + DEBUG(0,("%s\n", s)); + free(s); +} diff --git a/lib/tdr/tdr.h b/lib/tdr/tdr.h new file mode 100644 index 0000000000..84f3e50c2b --- /dev/null +++ b/lib/tdr/tdr.h @@ -0,0 +1,67 @@ +/* + Unix SMB/CIFS implementation. + TDR definitions + Copyright (C) Jelmer Vernooij 2005 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __TDR_H__ +#define __TDR_H__ + +#include <talloc.h> +#include "../lib/util/charset/charset.h" + +#define TDR_BIG_ENDIAN 0x01 +#define TDR_ALIGN2 0x02 +#define TDR_ALIGN4 0x04 +#define TDR_ALIGN8 0x08 +#define TDR_REMAINING 0x10 + +struct tdr_pull { + DATA_BLOB data; + uint32_t offset; + int flags; + struct smb_iconv_convenience *iconv_convenience; +}; + +struct tdr_push { + DATA_BLOB data; + int flags; + struct smb_iconv_convenience *iconv_convenience; +}; + +struct tdr_print { + int level; + void (*print)(struct tdr_print *, const char *, ...); + int flags; +}; + +#define TDR_CHECK(call) do { NTSTATUS _status; \ + _status = call; \ + if (!NT_STATUS_IS_OK(_status)) \ + return _status; \ + } while (0) + +#define TDR_ALLOC(ctx, s, n) do { \ + (s) = talloc_array_ptrtype(ctx, (s), n); \ + if ((n) && !(s)) return NT_STATUS_NO_MEMORY; \ + } while (0) + +typedef NTSTATUS (*tdr_push_fn_t) (struct tdr_push *, const void *); +typedef NTSTATUS (*tdr_pull_fn_t) (struct tdr_pull *, TALLOC_CTX *, void *); + +#include "../lib/tdr/tdr_proto.h" + +#endif /* __TDR_H__ */ diff --git a/lib/tdr/testsuite.c b/lib/tdr/testsuite.c new file mode 100644 index 0000000000..36bb164a9a --- /dev/null +++ b/lib/tdr/testsuite.c @@ -0,0 +1,185 @@ +/* + Unix SMB/CIFS implementation. + test suite for basic tdr functions + + Copyright (C) Jelmer Vernooij 2007 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "lib/tdr/tdr.h" + +static bool test_push_uint8(struct torture_context *tctx) +{ + uint8_t v = 4; + struct tdr_push *tdr = tdr_push_init(tctx, global_iconv_convenience); + + torture_assert_ntstatus_ok(tctx, tdr_push_uint8(tdr, &v), "push failed"); + torture_assert_int_equal(tctx, tdr->data.length, 1, "length incorrect"); + torture_assert_int_equal(tctx, tdr->data.data[0], 4, "data incorrect"); + return true; +} + +static bool test_pull_uint8(struct torture_context *tctx) +{ + uint8_t d = 2; + uint8_t l; + struct tdr_pull *tdr = tdr_pull_init(tctx, global_iconv_convenience); + tdr->data.data = &d; + tdr->data.length = 1; + tdr->offset = 0; + tdr->flags = 0; + torture_assert_ntstatus_ok(tctx, tdr_pull_uint8(tdr, tctx, &l), + "pull failed"); + torture_assert_int_equal(tctx, 1, tdr->offset, + "offset invalid"); + return true; +} + +static bool test_push_uint16(struct torture_context *tctx) +{ + uint16_t v = 0xF32; + struct tdr_push *tdr = tdr_push_init(tctx, global_iconv_convenience); + + torture_assert_ntstatus_ok(tctx, tdr_push_uint16(tdr, &v), "push failed"); + torture_assert_int_equal(tctx, tdr->data.length, 2, "length incorrect"); + torture_assert_int_equal(tctx, tdr->data.data[0], 0x32, "data incorrect"); + torture_assert_int_equal(tctx, tdr->data.data[1], 0x0F, "data incorrect"); + return true; +} + +static bool test_pull_uint16(struct torture_context *tctx) +{ + uint8_t d[2] = { 782 & 0xFF, (782 & 0xFF00) / 0x100 }; + uint16_t l; + struct tdr_pull *tdr = tdr_pull_init(tctx, global_iconv_convenience); + tdr->data.data = d; + tdr->data.length = 2; + tdr->offset = 0; + tdr->flags = 0; + torture_assert_ntstatus_ok(tctx, tdr_pull_uint16(tdr, tctx, &l), + "pull failed"); + torture_assert_int_equal(tctx, 2, tdr->offset, "offset invalid"); + torture_assert_int_equal(tctx, 782, l, "right int read"); + return true; +} + +static bool test_push_uint32(struct torture_context *tctx) +{ + uint32_t v = 0x100F32; + struct tdr_push *tdr = tdr_push_init(tctx, global_iconv_convenience); + + torture_assert_ntstatus_ok(tctx, tdr_push_uint32(tdr, &v), "push failed"); + torture_assert_int_equal(tctx, tdr->data.length, 4, "length incorrect"); + torture_assert_int_equal(tctx, tdr->data.data[0], 0x32, "data incorrect"); + torture_assert_int_equal(tctx, tdr->data.data[1], 0x0F, "data incorrect"); + torture_assert_int_equal(tctx, tdr->data.data[2], 0x10, "data incorrect"); + torture_assert_int_equal(tctx, tdr->data.data[3], 0x00, "data incorrect"); + return true; +} + +static bool test_pull_uint32(struct torture_context *tctx) +{ + uint8_t d[4] = { 782 & 0xFF, (782 & 0xFF00) / 0x100, 0, 0 }; + uint32_t l; + struct tdr_pull *tdr = tdr_pull_init(tctx, global_iconv_convenience); + tdr->data.data = d; + tdr->data.length = 4; + tdr->offset = 0; + tdr->flags = 0; + torture_assert_ntstatus_ok(tctx, tdr_pull_uint32(tdr, tctx, &l), + "pull failed"); + torture_assert_int_equal(tctx, 4, tdr->offset, "offset invalid"); + torture_assert_int_equal(tctx, 782, l, "right int read"); + return true; +} + +static bool test_pull_charset(struct torture_context *tctx) +{ + struct tdr_pull *tdr = tdr_pull_init(tctx, global_iconv_convenience); + const char *l = NULL; + tdr->data.data = (uint8_t *)talloc_strdup(tctx, "bla"); + tdr->data.length = 4; + tdr->offset = 0; + tdr->flags = 0; + torture_assert_ntstatus_ok(tctx, tdr_pull_charset(tdr, tctx, &l, -1, 1, CH_DOS), + "pull failed"); + torture_assert_int_equal(tctx, 4, tdr->offset, "offset invalid"); + torture_assert_str_equal(tctx, "bla", l, "right int read"); + + tdr->offset = 0; + torture_assert_ntstatus_ok(tctx, tdr_pull_charset(tdr, tctx, &l, 2, 1, CH_UNIX), + "pull failed"); + torture_assert_int_equal(tctx, 2, tdr->offset, "offset invalid"); + torture_assert_str_equal(tctx, "bl", l, "right int read"); + + return true; +} + +static bool test_pull_charset_empty(struct torture_context *tctx) +{ + struct tdr_pull *tdr = tdr_pull_init(tctx, global_iconv_convenience); + const char *l = NULL; + tdr->data.data = (uint8_t *)talloc_strdup(tctx, "bla"); + tdr->data.length = 4; + tdr->offset = 0; + tdr->flags = 0; + torture_assert_ntstatus_ok(tctx, tdr_pull_charset(tdr, tctx, &l, 0, 1, CH_DOS), + "pull failed"); + torture_assert_int_equal(tctx, 0, tdr->offset, "offset invalid"); + torture_assert_str_equal(tctx, "", l, "right string read"); + + return true; +} + + + +static bool test_push_charset(struct torture_context *tctx) +{ + const char *l = "bloe"; + struct tdr_push *tdr = tdr_push_init(tctx, global_iconv_convenience); + torture_assert_ntstatus_ok(tctx, tdr_push_charset(tdr, &l, 4, 1, CH_UTF8), + "push failed"); + torture_assert_int_equal(tctx, 4, tdr->data.length, "offset invalid"); + torture_assert(tctx, strcmp("bloe", (const char *)tdr->data.data) == 0, "right string push"); + + torture_assert_ntstatus_ok(tctx, tdr_push_charset(tdr, &l, -1, 1, CH_UTF8), + "push failed"); + torture_assert_int_equal(tctx, 9, tdr->data.length, "offset invalid"); + torture_assert_str_equal(tctx, "bloe", (const char *)tdr->data.data+4, "right string read"); + + return true; +} + +struct torture_suite *torture_local_tdr(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "TDR"); + + torture_suite_add_simple_test(suite, "pull_uint8", test_pull_uint8); + torture_suite_add_simple_test(suite, "push_uint8", test_push_uint8); + + torture_suite_add_simple_test(suite, "pull_uint16", test_pull_uint16); + torture_suite_add_simple_test(suite, "push_uint16", test_push_uint16); + + torture_suite_add_simple_test(suite, "pull_uint32", test_pull_uint32); + torture_suite_add_simple_test(suite, "push_uint32", test_push_uint32); + + torture_suite_add_simple_test(suite, "pull_charset", test_pull_charset); + torture_suite_add_simple_test(suite, "pull_charset", test_pull_charset_empty); + torture_suite_add_simple_test(suite, "push_charset", test_push_charset); + + return suite; +} diff --git a/lib/tevent/config.guess b/lib/tevent/config.guess index 4af85584ae..da83314608 100755 --- a/lib/tevent/config.guess +++ b/lib/tevent/config.guess @@ -808,7 +808,7 @@ EOF echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:[3456]*) - case ${UNAME_MACHINE} in + case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; @@ -984,7 +984,7 @@ EOF echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so diff --git a/lib/tevent/config.sub b/lib/tevent/config.sub index 80a785297e..a39437d015 100755 --- a/lib/tevent/config.sub +++ b/lib/tevent/config.sub @@ -1454,7 +1454,7 @@ case $basic_machine in os=-aout ;; c4x-* | tic4x-*) - os=-coff + os=-coff ;; # This must come before the *-dec entry. pdp10-*) diff --git a/lib/tevent/release-script.sh b/lib/tevent/release-script.sh new file mode 100755 index 0000000000..077f562967 --- /dev/null +++ b/lib/tevent/release-script.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +if [ "$1" = "" ]; then + echo "Please provide version string, eg: 1.2.0" + exit 1 +fi + +if [ ! -d "lib/tevent" ]; then + echo "Run this script from the samba base directory." + exit 1 +fi + +git clean -f -x -d lib/tevent +git clean -f -x -d lib/replace + +curbranch=`git-branch |grep "^*" | tr -d "* "` + +version=$1 +strver=`echo ${version} | tr "." "-"` + +# Checkout the release tag +git branch -f tevent-release-script-${strver} tevent-${strver} +if [ ! "$?" = "0" ]; then + echo "Unable to checkout tevent-${strver} release" + exit 1 +fi + +git checkout tevent-release-script-${strver} + +# Test configure agrees with us +confver=`grep "^AC_INIT" lib/tevent/configure.ac | tr -d "AC_INIT(tevent, " | tr -d ")"` +if [ ! "$confver" = "$version" ]; then + echo "Wrong version, requested release for ${version}, found ${confver}" + exit 1 +fi + +# Now build tarball +cp -a lib/tevent tevent-${version} +cp -a lib/replace tevent-${version}/libreplace +pushd tevent-${version} +./autogen.sh +popd +tar cvzf tevent-${version}.tar.gz tevent-${version} +rm -fr tevent-${version} + +#Clean up +git checkout $curbranch +git branch -d tevent-release-script-${strver} diff --git a/lib/tevent/script/abi_checks.sh b/lib/tevent/script/abi_checks.sh new file mode 100755 index 0000000000..c34a659d73 --- /dev/null +++ b/lib/tevent/script/abi_checks.sh @@ -0,0 +1,91 @@ +#!/bin/sh + +# +# abi_checks.sh - check for possible abi changes +# +# Copyright (C) 2009 Micheal Adam <obnox@samba.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +# +# USAGE: abi_checks.sh LIBNAME header1 [header2 ...] +# +# This script creates symbol and signature lists from the provided header +# files with the aid of the mksyms.sh and mksigs.pl scripts (saved as +# $LIBNAME.exports.check and $LIBNAME.sigatures.check). It then compares +# the resulting files with the files $LIBNAME.exports and $LIBNME.signatures +# which it expects to find in the current directory. +# + +LANG=C; export LANG +LC_ALL=C; export LC_ALL +LC_COLLATE=C; export LC_COLLATE + +script=$0 +dir_name=$(dirname ${script}) + +if test x"$1" = "x" ; then + echo "USAGE: ${script} libname header [header ...]" + exit 1 +fi + +libname="$1" +shift + +if test x"$1" = "x" ; then + echo "USAGE: ${script} libname header [header ...]" + exit 1 +fi + +headers="$*" + +exports_file=${libname}.exports +exports_file_check=${exports_file}.check +signatures_file=${libname}.signatures +signatures_file_check=${signatures_file}.check + + +${dir_name}/mksyms.sh awk ${exports_file_check} ${headers} 2>&1 > /dev/null + +cat ${headers} | ${dir_name}/mksigs.pl > ${signatures_file_check} 2> /dev/null + +normalize_exports_file() { + filename=$1 + cat ${filename} \ + | sed -e 's/^[ \t]*//g' \ + | sed -e 's/^$//g' \ + | sed -e 's/^#.*$//g' \ + | sort | uniq > ${filename}.sort +} + +normalize_exports_file ${exports_file} +normalize_exports_file ${exports_file_check} + +normalize_exports_file ${signatures_file} +normalize_exports_file ${signatures_file_check} + +diff -u ${exports_file}.sort ${exports_file_check}.sort +if test "x$?" != "x0" ; then + echo "WARNING: possible ABI change detected in exports!" +else + echo "exports check: OK" +fi + +diff -u ${signatures_file}.sort ${signatures_file_check}.sort +if test "x$?" != "x0" ; then + echo "WARNING: possible ABI change detected in signatures!" +else + echo "signatures check: OK" +fi diff --git a/lib/tevent/abi_checks.sh b/lib/tevent/script/abi_checks_gcc.sh index 95182097d5..95182097d5 100755 --- a/lib/tevent/abi_checks.sh +++ b/lib/tevent/script/abi_checks_gcc.sh diff --git a/lib/tevent/script/mksigs.pl b/lib/tevent/script/mksigs.pl new file mode 100755 index 0000000000..755cd79603 --- /dev/null +++ b/lib/tevent/script/mksigs.pl @@ -0,0 +1,183 @@ +#!/usr/bin/perl + +# mksigs.pl - extract signatures from C headers +# +# Copyright (C) Michael Adam 2009 +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# USAGE: cat $header_files | mksigs.pl > $signature_file +# +# The header files to parse are read from stdin. +# The output is in a form as produced by gcc with the -aux-info switch +# and printed to stdout. + +use strict; +use warnings; + +my $in_comment = 0; +my $extern_C_block = 0; + +while (my $LINE = <>) { + # find end of started multi-line-comment + if ($in_comment) { + if ($LINE =~ /^.*?\*\/(.*)$/) { + $LINE = $1; + $in_comment = 0; + } else { + # whole line within comment + next; + } + } + + # strip C++-style comments + $LINE =~ s/^(.*?)\/\/.*$/$1/; + + # strip in-line-comments: + while ($LINE =~ /\/\*.*?\*\//) { + $LINE =~ s/\/\*.*?\*\///; + } + + # find starts of multi-line-comments + if ($LINE =~ /^(.*)\/\*/) { + $in_comment = 1; + $LINE = $1; + } + + # skip empty lines + next if $LINE =~ /^\s*$/; + + # remove leading spaces + $LINE =~ s/^\s*(.*)$/$1/; + + # concatenate lines split with "\" (usually macro defines) + while ($LINE =~ /^(.*?)\s+\\$/) { + my $LINE2 = <>; + $LINE = $1; + $LINE2 =~ s/^\s*(.*)$/$1/; + $LINE .= " " . $LINE2; + } + + # remove all preprocessor directives + next if ($LINE =~ /^#/); + + if ($LINE =~ /^extern\s+"C"\s+\{/) { + $extern_C_block = 1; + next; + } + + if (($LINE =~ /^[^\{]*\}/) and $extern_C_block) { + $extern_C_block = 0; + next; + } + + $LINE =~ s/^extern\s//; + + # concatenate braces stretched over multiple lines + # (from structs or enums) + my $REST = $LINE; + my $braces = 0; + while (($REST =~ /[\{\}]/) or ($braces)) { + while ($REST =~ /[\{\}]/) { + # collect opening + while ($REST =~ /^[^\{\}]*\{(.*)$/) { + $braces++; + $REST = $1; + } + + # collect closing + while ($REST =~ /^[^\{\}]*\}(.*)$/) { + $braces--; + $REST = $1; + } + } + + # concatenate if not balanced + if ($braces) { + if (my $LINE2 = <>) { + $LINE2 =~ s/^\s*(.*)$/$1/; + chomp($LINE); + $LINE .= " " . $LINE2; + chomp $REST; + $REST .= " " . $LINE2; + } else { + print "ERROR: unbalanced braces ($braces)\n"; + last; + } + } + } + + # concetenate function prototypes that stretch over multiple lines + $REST = $LINE; + my $parenthesis = 0; + while (($REST =~ /[\(\)]/) or ($parenthesis)) { + while ($REST =~ /[\(\)]/) { + # collect opening + while ($REST =~ /^[^\(\)]*\((.*)$/) { + $parenthesis++; + $REST = $1; + } + + # collect closing + while ($REST =~ /^[^\(\)]*\)(.*)$/) { + $parenthesis--; + $REST = $1; + } + } + + # concatenate if not balanced + if ($parenthesis) { + if (my $LINE2 = <>) { + $LINE2 =~ s/^\s*(.*)$/$1/; + chomp($LINE); + $LINE .= " " . $LINE2; + chomp($REST); + $REST .= " " . $LINE2; + } else { + print "ERROR: unbalanced parantheses ($parenthesis)\n"; + last; + } + } + } + + next if ($LINE =~ /^typedef\s/); + next if ($LINE =~ /^enum\s+[^\{\(]+\s+\{/); + next if ($LINE =~ /^struct\s+[^\{\(]+\s+\{.*\}\s*;/); + next if ($LINE =~ /^struct\s+[a-zA-Z0-9_]+\s*;/); + + # remove trailing spaces + $LINE =~ s/(.*?)\s*$/$1/; + + $LINE =~ s/^(.*\))\s+PRINTF_ATTRIBUTE\([^\)]*\)(\s*[;,])/$1$2/; + $LINE =~ s/^(.*\))\s*[a-zA-Z0-9_]+\s*;$/$1;/; + + # remove parameter names - slightly too coarse probably + $LINE =~ s/([\s\(]\*?)[_0-9a-zA-Z]+\s*([,\)])/$1$2/g; + + # remedy (void) from last line + $LINE =~ s/\(\)/(void)/g; + + # normalize spaces + $LINE =~ s/\s*\)\s*/)/g; + $LINE =~ s/\s*\(\s*/ (/g; + $LINE =~ s/\s*,\s*/, /g; + + # normalize unsigned + $LINE =~ s/([\s,\(])unsigned([,\)])/$1unsigned int$2/g; + + # normalize bool + $LINE =~ s/(\b)bool(\b)/_Bool/g; + + print $LINE . "\n"; +} diff --git a/lib/tevent/script/mksyms.awk b/lib/tevent/script/mksyms.awk new file mode 100644 index 0000000000..94a405ca68 --- /dev/null +++ b/lib/tevent/script/mksyms.awk @@ -0,0 +1,76 @@ +# +# mksyms.awk +# +# Extract symbols to export from C-header files. +# output in version-script format for linking shared libraries. +# +# Copyright (C) 2008 Michael Adam <obnox@samba.org> +# +BEGIN { + inheader=0; + current_file=""; + print "#" + print "# This file is automatically generated with \"make symbols\". DO NOT EDIT " + print "#" + print "{" + print "\tglobal:" +} + +END { + print"" + print "\tlocal: *;" + print "};" +} + +{ + if (FILENAME!=current_file) { + print "\t\t# The following definitions come from",FILENAME + current_file=FILENAME + } + if (inheader) { + if (match($0,"[)][^()]*[;][ \t]*$")) { + inheader = 0; + } + next; + } +} + +/^static/ || /^[ \t]*typedef/ || !/^[a-zA-Z\_]/ { + next; +} + +/^extern[ \t]+[^()]+[;][ \t]*$/ { + gsub(/[^ \t]+[ \t]+/, ""); + sub(/[;][ \t]*$/, ""); + printf "\t\t%s;\n", $0; + next; +} + +# look for function headers: +{ + gotstart = 0; + if ($0 ~ /^[A-Za-z_][A-Za-z0-9_]+/) { + gotstart = 1; + } + if(!gotstart) { + next; + } +} + +/[_A-Za-z0-9]+[ \t]*[(].*[)][^()]*;[ \t]*$/ { + sub(/[(].*$/, ""); + gsub(/[^ \t]+[ \t]+/, ""); + gsub(/^[*]+/, ""); + printf "\t\t%s;\n",$0; + next; +} + +/[_A-Za-z0-9]+[ \t]*[(]/ { + inheader=1; + sub(/[(].*$/, ""); + gsub(/[^ \t]+[ \t]+/, ""); + gsub(/^[*]/, ""); + printf "\t\t%s;\n",$0; + next; +} + diff --git a/lib/tevent/script/mksyms.sh b/lib/tevent/script/mksyms.sh new file mode 100755 index 0000000000..7fb4031e2b --- /dev/null +++ b/lib/tevent/script/mksyms.sh @@ -0,0 +1,45 @@ +#! /bin/sh + +# +# mksyms.sh +# +# Extract symbols to export from C-header files. +# output in version-script format for linking shared libraries. +# +# This is the shell wrapper for the mksyms.awk core script. +# +# Copyright (C) 2008 Michael Adam <obnox@samba.org> +# + +LANG=C; export LANG +LC_ALL=C; export LC_ALL +LC_COLLATE=C; export LC_COLLATE + +if [ $# -lt 2 ] +then + echo "Usage: $0 awk output_file header_files" + exit 1 +fi + +awk="$1" +shift + +symsfile="$1" +shift +symsfile_tmp="$symsfile.$$.tmp~" + +proto_src="`echo $@ | tr ' ' '\n' | sort | uniq `" + +echo creating $symsfile + +mkdir -p `dirname $symsfile` + +${awk} -f `dirname $0`/mksyms.awk $proto_src > $symsfile_tmp + +if cmp -s $symsfile $symsfile_tmp 2>/dev/null +then + echo "$symsfile unchanged" + rm $symsfile_tmp +else + mv $symsfile_tmp $symsfile +fi diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c index ae5fdbd08c..a0ee208663 100644 --- a/lib/tevent/tevent.c +++ b/lib/tevent/tevent.c @@ -436,7 +436,7 @@ void tevent_loop_set_nesting_hook(struct tevent_context *ev, tevent_nesting_hook hook, void *private_data) { - if (ev->nesting.hook_fn && + if (ev->nesting.hook_fn && (ev->nesting.hook_fn != hook || ev->nesting.hook_private != private_data)) { /* the way the nesting hook code is currently written diff --git a/lib/tevent/tevent.exports b/lib/tevent/tevent.exports index b1554dff4e..01d547ad85 100644 --- a/lib/tevent/tevent.exports +++ b/lib/tevent/tevent.exports @@ -55,6 +55,8 @@ tevent_timeval_zero; tevent_wakeup_recv; tevent_wakeup_send; + _tevent_req_cancel; + tevent_req_set_cancel_fn; local: *; }; diff --git a/lib/tevent/tevent.mk b/lib/tevent/tevent.mk index 480366e938..57bfd81222 100644 --- a/lib/tevent/tevent.mk +++ b/lib/tevent/tevent.mk @@ -26,9 +26,21 @@ installheaders:: installdirs installlibs:: installdirs cp tevent.pc $(DESTDIR)$(libdir)/pkgconfig cp $(TEVENT_STLIB) $(TEVENT_SOLIB) $(DESTDIR)$(libdir) + rm -f $(DESTDIR)$(libdir)/$(TEVENT_SONAME) + ln -s $(TEVENT_SOLIB) $(DESTDIR)$(libdir)/$(TEVENT_SONAME) + rm -f $(DESTDIR)$(libdir)/$(TEVENT_SOBASE) + ln -s $(TEVENT_SOLIB) $(DESTDIR)$(libdir)/$(TEVENT_SOBASE) install:: all installdirs installheaders installlibs $(PYTHON_INSTALL_TARGET) +abi_checks:: + @echo ABI checks: + @./script/abi_checks.sh tevent tevent.h + +test:: abi_checks + clean:: rm -f $(TEVENT_SOBASE) $(TEVENT_SONAME) $(TEVENT_SOLIB) $(TEVENT_STLIB) rm -f tevent.pc + rm -f tevent.exports.sort tevent.exports.check tevent.exports.check.sort + rm -f tevent.signatures.sort tevent.signatures.check tevent.signatures.check.sort diff --git a/lib/tevent/tevent.signatures b/lib/tevent/tevent.signatures index 75f43affb2..c752b9e933 100644 --- a/lib/tevent/tevent.signatures +++ b/lib/tevent/tevent.signatures @@ -53,3 +53,5 @@ void tevent_req_set_print_fn (struct tevent_req *, tevent_req_print_fn); void _tevent_schedule_immediate (struct tevent_immediate *, struct tevent_context *, tevent_immediate_handler_t, void *, const char *, const char *); void tevent_set_abort_fn (void (*) (const char *)); void tevent_set_default_backend (const char *); +_Bool _tevent_req_cancel (struct tevent_req *, const char *); +void tevent_req_set_cancel_fn (struct tevent_req *, tevent_req_cancel_fn); diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h index 0c15e35a73..7f5fd64854 100644 --- a/lib/tevent/tevent_internal.h +++ b/lib/tevent/tevent_internal.h @@ -4,7 +4,7 @@ generalised event loop handling INTERNAL STRUCTS. THERE ARE NO API GUARANTEES. - External users should only ever have to include this header when + External users should only ever have to include this header when implementing new tevent backends. Copyright (C) Stefan Metzmacher 2005-2009 @@ -142,7 +142,7 @@ struct tevent_req { struct tevent_immediate *trigger; /** - * @brief the timer event if tevent_req_set_timeout was used + * @brief the timer event if tevent_req_set_endtime was used * */ struct tevent_timer *timer; diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c index f3e22db287..45f65cf6dd 100644 --- a/lib/tevent/tevent_signal.c +++ b/lib/tevent/tevent_signal.c @@ -30,23 +30,23 @@ #include "tevent_internal.h" #include "tevent_util.h" -#define NUM_SIGNALS 64 +#define TEVENT_NUM_SIGNALS 64 /* maximum number of SA_SIGINFO signals to hold in the queue. NB. This *MUST* be a power of 2, in order for the ring buffer wrap to work correctly. Thanks to Petr Vandrovec <petr@vandrovec.name> for this. */ -#define SA_INFO_QUEUE_COUNT 64 +#define TEVENT_SA_INFO_QUEUE_COUNT 64 -struct sigcounter { +struct tevent_sigcounter { uint32_t count; uint32_t seen; }; -#define SIG_INCREMENT(s) (s).count++ -#define SIG_SEEN(s, n) (s).seen += (n) -#define SIG_PENDING(s) ((s).seen != (s).count) +#define TEVENT_SIG_INCREMENT(s) (s).count++ +#define TEVENT_SIG_SEEN(s, n) (s).seen += (n) +#define TEVENT_SIG_PENDING(s) ((s).seen != (s).count) struct tevent_common_signal_list { struct tevent_common_signal_list *prev, *next; @@ -56,22 +56,22 @@ struct tevent_common_signal_list { /* the poor design of signals means that this table must be static global */ -static struct sig_state { - struct tevent_common_signal_list *sig_handlers[NUM_SIGNALS+1]; - struct sigaction *oldact[NUM_SIGNALS+1]; - struct sigcounter signal_count[NUM_SIGNALS+1]; - struct sigcounter got_signal; +static struct tevent_sig_state { + struct tevent_common_signal_list *sig_handlers[TEVENT_NUM_SIGNALS+1]; + struct sigaction *oldact[TEVENT_NUM_SIGNALS+1]; + struct tevent_sigcounter signal_count[TEVENT_NUM_SIGNALS+1]; + struct tevent_sigcounter got_signal; #ifdef SA_SIGINFO /* with SA_SIGINFO we get quite a lot of info per signal */ - siginfo_t *sig_info[NUM_SIGNALS+1]; - struct sigcounter sig_blocked[NUM_SIGNALS+1]; + siginfo_t *sig_info[TEVENT_NUM_SIGNALS+1]; + struct tevent_sigcounter sig_blocked[TEVENT_NUM_SIGNALS+1]; #endif } *sig_state; /* return number of sigcounter events not processed yet */ -static uint32_t sig_count(struct sigcounter s) +static uint32_t tevent_sig_count(struct tevent_sigcounter s) { return s.count - s.seen; } @@ -87,8 +87,8 @@ static void tevent_common_signal_handler(int signum) struct tevent_context *ev = NULL; int saved_errno = errno; - SIG_INCREMENT(sig_state->signal_count[signum]); - SIG_INCREMENT(sig_state->got_signal); + TEVENT_SIG_INCREMENT(sig_state->signal_count[signum]); + TEVENT_SIG_INCREMENT(sig_state->got_signal); /* Write to each unique event context. */ for (sl = sig_state->sig_handlers[signum]; sl; sl = sl->next) { @@ -109,24 +109,24 @@ static void tevent_common_signal_handler(int signum) static void tevent_common_signal_handler_info(int signum, siginfo_t *info, void *uctx) { - uint32_t count = sig_count(sig_state->signal_count[signum]); - /* sig_state->signal_count[signum].seen % SA_INFO_QUEUE_COUNT + uint32_t count = tevent_sig_count(sig_state->signal_count[signum]); + /* sig_state->signal_count[signum].seen % TEVENT_SA_INFO_QUEUE_COUNT * is the base of the unprocessed signals in the ringbuffer. */ uint32_t ofs = (sig_state->signal_count[signum].seen + count) % - SA_INFO_QUEUE_COUNT; + TEVENT_SA_INFO_QUEUE_COUNT; sig_state->sig_info[signum][ofs] = *info; tevent_common_signal_handler(signum); /* handle SA_SIGINFO */ - if (count+1 == SA_INFO_QUEUE_COUNT) { + if (count+1 == TEVENT_SA_INFO_QUEUE_COUNT) { /* we've filled the info array - block this signal until these ones are delivered */ sigset_t set; sigemptyset(&set); sigaddset(&set, signum); sigprocmask(SIG_BLOCK, &set, NULL); - SIG_INCREMENT(sig_state->sig_blocked[signum]); + TEVENT_SIG_INCREMENT(sig_state->sig_blocked[signum]); } } #endif @@ -202,7 +202,7 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev, struct tevent_common_signal_list *sl; sigset_t set, oldset; - if (signum >= NUM_SIGNALS) { + if (signum >= TEVENT_NUM_SIGNALS) { errno = EINVAL; return NULL; } @@ -210,7 +210,7 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev, /* the sig_state needs to be on a global context as it can last across multiple event contexts */ if (sig_state == NULL) { - sig_state = talloc_zero(talloc_autofree_context(), struct sig_state); + sig_state = talloc_zero(talloc_autofree_context(), struct tevent_sig_state); if (sig_state == NULL) { return NULL; } @@ -273,7 +273,9 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev, act.sa_handler = NULL; act.sa_sigaction = tevent_common_signal_handler_info; if (sig_state->sig_info[signum] == NULL) { - sig_state->sig_info[signum] = talloc_zero_array(sig_state, siginfo_t, SA_INFO_QUEUE_COUNT); + sig_state->sig_info[signum] = + talloc_zero_array(sig_state, siginfo_t, + TEVENT_SA_INFO_QUEUE_COUNT); if (sig_state->sig_info[signum] == NULL) { talloc_free(se); return NULL; @@ -316,14 +318,14 @@ int tevent_common_check_signal(struct tevent_context *ev) { int i; - if (!sig_state || !SIG_PENDING(sig_state->got_signal)) { + if (!sig_state || !TEVENT_SIG_PENDING(sig_state->got_signal)) { return 0; } - for (i=0;i<NUM_SIGNALS+1;i++) { + for (i=0;i<TEVENT_NUM_SIGNALS+1;i++) { struct tevent_common_signal_list *sl, *next; - struct sigcounter counter = sig_state->signal_count[i]; - uint32_t count = sig_count(counter); + struct tevent_sigcounter counter = sig_state->signal_count[i]; + uint32_t count = tevent_sig_count(counter); #ifdef SA_SIGINFO /* Ensure we null out any stored siginfo_t entries * after processing for debugging purposes. */ @@ -344,11 +346,11 @@ int tevent_common_check_signal(struct tevent_context *ev) for (j=0;j<count;j++) { /* sig_state->signal_count[i].seen - * % SA_INFO_QUEUE_COUNT is + * % TEVENT_SA_INFO_QUEUE_COUNT is * the base position of the unprocessed * signals in the ringbuffer. */ uint32_t ofs = (counter.seen + j) - % SA_INFO_QUEUE_COUNT; + % TEVENT_SA_INFO_QUEUE_COUNT; se->handler(ev, se, i, 1, (void*)&sig_state->sig_info[i][ofs], se->private_data); @@ -370,7 +372,7 @@ int tevent_common_check_signal(struct tevent_context *ev) uint32_t j; for (j=0;j<count;j++) { uint32_t ofs = (counter.seen + j) - % SA_INFO_QUEUE_COUNT; + % TEVENT_SA_INFO_QUEUE_COUNT; memset((void*)&sig_state->sig_info[i][ofs], '\0', sizeof(siginfo_t)); @@ -378,23 +380,23 @@ int tevent_common_check_signal(struct tevent_context *ev) } #endif - SIG_SEEN(sig_state->signal_count[i], count); - SIG_SEEN(sig_state->got_signal, count); + TEVENT_SIG_SEEN(sig_state->signal_count[i], count); + TEVENT_SIG_SEEN(sig_state->got_signal, count); #ifdef SA_SIGINFO - if (SIG_PENDING(sig_state->sig_blocked[i])) { + if (TEVENT_SIG_PENDING(sig_state->sig_blocked[i])) { /* We'd filled the queue, unblock the signal now the queue is empty again. Note we MUST do this after the - SIG_SEEN(sig_state->signal_count[i], count) + TEVENT_SIG_SEEN(sig_state->signal_count[i], count) call to prevent a new signal running out of room in the sig_state->sig_info[i][] ring buffer. */ sigset_t set; sigemptyset(&set); sigaddset(&set, i); - SIG_SEEN(sig_state->sig_blocked[i], - sig_count(sig_state->sig_blocked[i])); + TEVENT_SIG_SEEN(sig_state->sig_blocked[i], + tevent_sig_count(sig_state->sig_blocked[i])); sigprocmask(SIG_UNBLOCK, &set, NULL); } #endif diff --git a/lib/torture/torture.c b/lib/torture/torture.c index 17adce94e5..392cb0ad4c 100644 --- a/lib/torture/torture.c +++ b/lib/torture/torture.c @@ -65,7 +65,6 @@ struct torture_context *torture_context_child(struct torture_context *parent) if (subtorture == NULL) return NULL; - subtorture->level = parent->level+1; subtorture->ev = talloc_reference(subtorture, parent->ev); subtorture->lp_ctx = talloc_reference(subtorture, parent->lp_ctx); subtorture->outputdir = talloc_reference(subtorture, parent->outputdir); @@ -107,6 +106,7 @@ void torture_comment(struct torture_context *context, const char *comment, ...) va_start(ap, comment); tmp = talloc_vasprintf(context, comment, ap); + va_end(ap); context->results->ui_ops->comment(context, tmp); @@ -126,6 +126,7 @@ void torture_warning(struct torture_context *context, const char *comment, ...) va_start(ap, comment); tmp = talloc_vasprintf(context, comment, ap); + va_end(ap); context->results->ui_ops->warning(context, tmp); @@ -255,7 +256,6 @@ bool torture_run_suite(struct torture_context *context, struct torture_suite *tsuite; char *old_testname; - context->level++; if (context->results->ui_ops->suite_start) context->results->ui_ops->suite_start(context, suite); @@ -280,8 +280,6 @@ bool torture_run_suite(struct torture_context *context, if (context->results->ui_ops->suite_finish) context->results->ui_ops->suite_finish(context, suite); - context->level--; - return ret; } @@ -376,8 +374,6 @@ bool torture_run_tcase(struct torture_context *context, char *old_testname; struct torture_test *test; - context->level++; - context->active_tcase = tcase; if (context->results->ui_ops->tcase_start) context->results->ui_ops->tcase_start(context, tcase); @@ -413,8 +409,6 @@ done: if (context->results->ui_ops->tcase_finish) context->results->ui_ops->tcase_finish(context, tcase); - context->level--; - return ret; } diff --git a/lib/torture/torture.h b/lib/torture/torture.h index 91448c6422..7f387cc1f2 100644 --- a/lib/torture/torture.h +++ b/lib/torture/torture.h @@ -86,9 +86,6 @@ struct torture_context /** Directory used for temporary test data */ const char *outputdir; - /** Indentation level */ - int level; - /** Event context */ struct tevent_context *ev; @@ -249,6 +246,15 @@ void torture_result(struct torture_context *test, }\ } while(0) +#define torture_assert_ntstatus_equal_goto(torture_ctx,got,expected,ret,label,cmt) \ + do { NTSTATUS __got = got, __expected = expected; \ + if (!NT_STATUS_EQUAL(__got, __expected)) { \ + torture_result(torture_ctx, TORTURE_FAIL, __location__": "#got" was %s, expected %s: %s", nt_errstr(__got), nt_errstr(__expected), cmt); \ + ret = false; \ + goto label; \ + }\ + } while(0) + #define torture_assert_ndr_err_equal(torture_ctx,got,expected,cmt) \ do { enum ndr_err_code __got = got, __expected = expected; \ if (__got != __expected) { \ @@ -275,11 +281,22 @@ void torture_result(struct torture_context *test, } \ } while(0) +#define torture_assert_str_equal_goto(torture_ctx,got,expected,ret,label,cmt)\ + do { const char *__got = (got), *__expected = (expected); \ + if (strcmp_safe(__got, __expected) != 0) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got" was %s, expected %s: %s", \ + __got, __expected, cmt); \ + ret = false; \ + goto label; \ + } \ + } while(0) + #define torture_assert_mem_equal(torture_ctx,got,expected,len,cmt)\ do { const void *__got = (got), *__expected = (expected); \ if (memcmp(__got, __expected, len) != 0) { \ torture_result(torture_ctx, TORTURE_FAIL, \ - __location__": "#got" of len %d did not match"#expected": %s", (int)len, cmt); \ + __location__": "#got" of len %d did not match "#expected": %s", (int)len, cmt); \ return false; \ } \ } while(0) @@ -294,7 +311,7 @@ void torture_result(struct torture_context *test, } \ if (memcmp(__got.data, __expected.data, __got.length) != 0) { \ torture_result(torture_ctx, TORTURE_FAIL, \ - __location__": "#got" of len %d did not match"#expected": %s", (int)__got.length, cmt); \ + __location__": "#got" of len %d did not match "#expected": %s", (int)__got.length, cmt); \ return false; \ } \ } while(0) @@ -346,6 +363,17 @@ void torture_result(struct torture_context *test, } \ } while(0) +#define torture_assert_int_equal_goto(torture_ctx,got,expected,ret,label,cmt)\ + do { int __got = (got), __expected = (expected); \ + if (__got != __expected) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got" was %d, expected %d: %s", \ + __got, __expected, cmt); \ + ret = false; \ + goto label; \ + } \ + } while(0) + #define torture_assert_u64_equal(torture_ctx,got,expected,cmt)\ do { uint64_t __got = (got), __expected = (expected); \ if (__got != __expected) { \ @@ -373,6 +401,10 @@ void torture_result(struct torture_context *test, torture_result(torture_ctx, TORTURE_SKIP, __location__": %s", cmt);\ return true; \ } while(0) +#define torture_skip_goto(torture_ctx,label,cmt) do {\ + torture_result(torture_ctx, TORTURE_SKIP, __location__": %s", cmt);\ + goto label; \ + } while(0) #define torture_fail(torture_ctx,cmt) do {\ torture_result(torture_ctx, TORTURE_FAIL, __location__": %s", cmt);\ return false; \ @@ -388,6 +420,9 @@ void torture_result(struct torture_context *test, #define torture_assert_ntstatus_ok(torture_ctx,expr,cmt) \ torture_assert_ntstatus_equal(torture_ctx,expr,NT_STATUS_OK,cmt) +#define torture_assert_ntstatus_ok_goto(torture_ctx,expr,ret,label,cmt) \ + torture_assert_ntstatus_equal_goto(torture_ctx,expr,NT_STATUS_OK,ret,label,cmt) + #define torture_assert_werr_ok(torture_ctx,expr,cmt) \ torture_assert_werr_equal(torture_ctx,expr,WERR_OK,cmt) diff --git a/lib/tsocket/config.mk b/lib/tsocket/config.mk new file mode 100644 index 0000000000..ca2978a426 --- /dev/null +++ b/lib/tsocket/config.mk @@ -0,0 +1,13 @@ +[SUBSYSTEM::LIBTSOCKET] +PRIVATE_DEPENDENCIES = LIBREPLACE_NETWORK +PUBLIC_DEPENDENCIES = LIBTALLOC LIBTEVENT + +LIBTSOCKET_OBJ_FILES = $(addprefix ../lib/tsocket/, \ + tsocket.o \ + tsocket_helpers.o \ + tsocket_bsd.o) + +PUBLIC_HEADERS += $(addprefix ../lib/tsocket/, \ + tsocket.h\ + tsocket_internal.h) + diff --git a/lib/tsocket/doxy.config b/lib/tsocket/doxy.config new file mode 100644 index 0000000000..584ae73d83 --- /dev/null +++ b/lib/tsocket/doxy.config @@ -0,0 +1,1538 @@ +# Doxyfile 1.6.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = tsocket + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.cpp \ + *.cc \ + *.c \ + *.h \ + *.hh \ + *.hpp \ + *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.git/* \ + */.svn/* \ + */cmake/* \ + */build/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# If the HTML_FOOTER_DESCRIPTION tag is set to YES, Doxygen will +# add generated date, project name and doxygen version to HTML footer. + +HTML_FOOTER_DESCRIPTION= NO + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NONE + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP) +# there is already a search function so this one should typically +# be disabled. + +SEARCHENGINE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DOXYGEN + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/lib/tsocket/tsocket.c b/lib/tsocket/tsocket.c new file mode 100644 index 0000000000..b8dd6c8936 --- /dev/null +++ b/lib/tsocket/tsocket.c @@ -0,0 +1,813 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tsocket + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "tsocket.h" +#include "tsocket_internal.h" + +int tsocket_simple_int_recv(struct tevent_req *req, int *perrno) +{ + enum tevent_req_state state; + uint64_t error; + + if (!tevent_req_is_error(req, &state, &error)) { + return 0; + } + + switch (state) { + case TEVENT_REQ_NO_MEMORY: + *perrno = ENOMEM; + return -1; + case TEVENT_REQ_TIMED_OUT: + *perrno = ETIMEDOUT; + return -1; + case TEVENT_REQ_USER_ERROR: + *perrno = (int)error; + return -1; + default: + *perrno = EIO; + return -1; + } + + *perrno = EIO; + return -1; +} + +struct tsocket_address *_tsocket_address_create(TALLOC_CTX *mem_ctx, + const struct tsocket_address_ops *ops, + void *pstate, + size_t psize, + const char *type, + const char *location) +{ + void **ppstate = (void **)pstate; + struct tsocket_address *addr; + + addr = talloc_zero(mem_ctx, struct tsocket_address); + if (!addr) { + return NULL; + } + addr->ops = ops; + addr->location = location; + addr->private_data = talloc_size(addr, psize); + if (!addr->private_data) { + talloc_free(addr); + return NULL; + } + talloc_set_name_const(addr->private_data, type); + + *ppstate = addr->private_data; + return addr; +} + +char *tsocket_address_string(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx) +{ + if (!addr) { + return talloc_strdup(mem_ctx, "NULL"); + } + return addr->ops->string(addr, mem_ctx); +} + +struct tsocket_address *_tsocket_address_copy(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx, + const char *location) +{ + return addr->ops->copy(addr, mem_ctx, location); +} + +struct tdgram_context { + const char *location; + const struct tdgram_context_ops *ops; + void *private_data; + + struct tevent_req *recvfrom_req; + struct tevent_req *sendto_req; +}; + +static int tdgram_context_destructor(struct tdgram_context *dgram) +{ + if (dgram->recvfrom_req) { + tevent_req_received(dgram->recvfrom_req); + } + + if (dgram->sendto_req) { + tevent_req_received(dgram->sendto_req); + } + + return 0; +} + +struct tdgram_context *_tdgram_context_create(TALLOC_CTX *mem_ctx, + const struct tdgram_context_ops *ops, + void *pstate, + size_t psize, + const char *type, + const char *location) +{ + struct tdgram_context *dgram; + void **ppstate = (void **)pstate; + void *state; + + dgram = talloc(mem_ctx, struct tdgram_context); + if (dgram == NULL) { + return NULL; + } + dgram->location = location; + dgram->ops = ops; + dgram->recvfrom_req = NULL; + dgram->sendto_req = NULL; + + state = talloc_size(dgram, psize); + if (state == NULL) { + talloc_free(dgram); + return NULL; + } + talloc_set_name_const(state, type); + + dgram->private_data = state; + + talloc_set_destructor(dgram, tdgram_context_destructor); + + *ppstate = state; + return dgram; +} + +void *_tdgram_context_data(struct tdgram_context *dgram) +{ + return dgram->private_data; +} + +struct tdgram_recvfrom_state { + const struct tdgram_context_ops *ops; + struct tdgram_context *dgram; + uint8_t *buf; + size_t len; + struct tsocket_address *src; +}; + +static int tdgram_recvfrom_destructor(struct tdgram_recvfrom_state *state) +{ + if (state->dgram) { + state->dgram->recvfrom_req = NULL; + } + + return 0; +} + +static void tdgram_recvfrom_done(struct tevent_req *subreq); + +struct tevent_req *tdgram_recvfrom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram) +{ + struct tevent_req *req; + struct tdgram_recvfrom_state *state; + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_recvfrom_state); + if (req == NULL) { + return NULL; + } + + state->ops = dgram->ops; + state->dgram = dgram; + state->buf = NULL; + state->len = 0; + state->src = NULL; + + if (dgram->recvfrom_req) { + tevent_req_error(req, EBUSY); + goto post; + } + dgram->recvfrom_req = req; + + talloc_set_destructor(state, tdgram_recvfrom_destructor); + + subreq = state->ops->recvfrom_send(state, ev, dgram); + if (tevent_req_nomem(subreq, req)) { + goto post; + } + tevent_req_set_callback(subreq, tdgram_recvfrom_done, req); + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tdgram_recvfrom_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tdgram_recvfrom_state *state = tevent_req_data(req, + struct tdgram_recvfrom_state); + ssize_t ret; + int sys_errno; + + ret = state->ops->recvfrom_recv(subreq, &sys_errno, state, + &state->buf, &state->src); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + + state->len = ret; + + tevent_req_done(req); +} + +ssize_t tdgram_recvfrom_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + uint8_t **buf, + struct tsocket_address **src) +{ + struct tdgram_recvfrom_state *state = tevent_req_data(req, + struct tdgram_recvfrom_state); + ssize_t ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + *buf = talloc_move(mem_ctx, &state->buf); + ret = state->len; + if (src) { + *src = talloc_move(mem_ctx, &state->src); + } + } + + tevent_req_received(req); + return ret; +} + +struct tdgram_sendto_state { + const struct tdgram_context_ops *ops; + struct tdgram_context *dgram; + ssize_t ret; +}; + +static int tdgram_sendto_destructor(struct tdgram_sendto_state *state) +{ + if (state->dgram) { + state->dgram->sendto_req = NULL; + } + + return 0; +} + +static void tdgram_sendto_done(struct tevent_req *subreq); + +struct tevent_req *tdgram_sendto_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + const uint8_t *buf, size_t len, + const struct tsocket_address *dst) +{ + struct tevent_req *req; + struct tdgram_sendto_state *state; + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_sendto_state); + if (req == NULL) { + return NULL; + } + + state->ops = dgram->ops; + state->dgram = dgram; + state->ret = -1; + + if (len == 0) { + tevent_req_error(req, EINVAL); + goto post; + } + + if (dgram->sendto_req) { + tevent_req_error(req, EBUSY); + goto post; + } + dgram->sendto_req = req; + + talloc_set_destructor(state, tdgram_sendto_destructor); + + subreq = state->ops->sendto_send(state, ev, dgram, + buf, len, dst); + if (tevent_req_nomem(subreq, req)) { + goto post; + } + tevent_req_set_callback(subreq, tdgram_sendto_done, req); + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tdgram_sendto_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tdgram_sendto_state *state = tevent_req_data(req, + struct tdgram_sendto_state); + ssize_t ret; + int sys_errno; + + ret = state->ops->sendto_recv(subreq, &sys_errno); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + + state->ret = ret; + + tevent_req_done(req); +} + +ssize_t tdgram_sendto_recv(struct tevent_req *req, + int *perrno) +{ + struct tdgram_sendto_state *state = tevent_req_data(req, + struct tdgram_sendto_state); + ssize_t ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + +struct tdgram_disconnect_state { + const struct tdgram_context_ops *ops; +}; + +static void tdgram_disconnect_done(struct tevent_req *subreq); + +struct tevent_req *tdgram_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram) +{ + struct tevent_req *req; + struct tdgram_disconnect_state *state; + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_disconnect_state); + if (req == NULL) { + return NULL; + } + + state->ops = dgram->ops; + + if (dgram->recvfrom_req || dgram->sendto_req) { + tevent_req_error(req, EBUSY); + goto post; + } + + subreq = state->ops->disconnect_send(state, ev, dgram); + if (tevent_req_nomem(subreq, req)) { + goto post; + } + tevent_req_set_callback(subreq, tdgram_disconnect_done, req); + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tdgram_disconnect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tdgram_disconnect_state *state = tevent_req_data(req, + struct tdgram_disconnect_state); + int ret; + int sys_errno; + + ret = state->ops->disconnect_recv(subreq, &sys_errno); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + + tevent_req_done(req); +} + +int tdgram_disconnect_recv(struct tevent_req *req, + int *perrno) +{ + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + + tevent_req_received(req); + return ret; +} + +struct tstream_context { + const char *location; + const struct tstream_context_ops *ops; + void *private_data; + + struct tevent_req *readv_req; + struct tevent_req *writev_req; +}; + +static int tstream_context_destructor(struct tstream_context *stream) +{ + if (stream->readv_req) { + tevent_req_received(stream->readv_req); + } + + if (stream->writev_req) { + tevent_req_received(stream->writev_req); + } + + return 0; +} + +struct tstream_context *_tstream_context_create(TALLOC_CTX *mem_ctx, + const struct tstream_context_ops *ops, + void *pstate, + size_t psize, + const char *type, + const char *location) +{ + struct tstream_context *stream; + void **ppstate = (void **)pstate; + void *state; + + stream = talloc(mem_ctx, struct tstream_context); + if (stream == NULL) { + return NULL; + } + stream->location = location; + stream->ops = ops; + stream->readv_req = NULL; + stream->writev_req = NULL; + + state = talloc_size(stream, psize); + if (state == NULL) { + talloc_free(stream); + return NULL; + } + talloc_set_name_const(state, type); + + stream->private_data = state; + + talloc_set_destructor(stream, tstream_context_destructor); + + *ppstate = state; + return stream; +} + +void *_tstream_context_data(struct tstream_context *stream) +{ + return stream->private_data; +} + +ssize_t tstream_pending_bytes(struct tstream_context *stream) +{ + return stream->ops->pending_bytes(stream); +} + +struct tstream_readv_state { + const struct tstream_context_ops *ops; + struct tstream_context *stream; + int ret; +}; + +static int tstream_readv_destructor(struct tstream_readv_state *state) +{ + if (state->stream) { + state->stream->readv_req = NULL; + } + + return 0; +} + +static void tstream_readv_done(struct tevent_req *subreq); + +struct tevent_req *tstream_readv_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct iovec *vector, + size_t count) +{ + struct tevent_req *req; + struct tstream_readv_state *state; + struct tevent_req *subreq; + int to_read = 0; + size_t i; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_readv_state); + if (req == NULL) { + return NULL; + } + + state->ops = stream->ops; + state->stream = stream; + state->ret = -1; + + /* first check if the input is ok */ +#ifdef IOV_MAX + if (count > IOV_MAX) { + tevent_req_error(req, EMSGSIZE); + goto post; + } +#endif + + for (i=0; i < count; i++) { + int tmp = to_read; + tmp += vector[i].iov_len; + + if (tmp < to_read) { + tevent_req_error(req, EMSGSIZE); + goto post; + } + + to_read = tmp; + } + + if (to_read == 0) { + tevent_req_error(req, EINVAL); + goto post; + } + + if (stream->readv_req) { + tevent_req_error(req, EBUSY); + goto post; + } + stream->readv_req = req; + + talloc_set_destructor(state, tstream_readv_destructor); + + subreq = state->ops->readv_send(state, ev, stream, vector, count); + if (tevent_req_nomem(subreq, req)) { + goto post; + } + tevent_req_set_callback(subreq, tstream_readv_done, req); + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tstream_readv_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tstream_readv_state *state = tevent_req_data(req, + struct tstream_readv_state); + ssize_t ret; + int sys_errno; + + ret = state->ops->readv_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + + state->ret = ret; + + tevent_req_done(req); +} + +int tstream_readv_recv(struct tevent_req *req, + int *perrno) +{ + struct tstream_readv_state *state = tevent_req_data(req, + struct tstream_readv_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + +struct tstream_writev_state { + const struct tstream_context_ops *ops; + struct tstream_context *stream; + int ret; +}; + +static int tstream_writev_destructor(struct tstream_writev_state *state) +{ + if (state->stream) { + state->stream->writev_req = NULL; + } + + return 0; +} + +static void tstream_writev_done(struct tevent_req *subreq); + +struct tevent_req *tstream_writev_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + const struct iovec *vector, + size_t count) +{ + struct tevent_req *req; + struct tstream_writev_state *state; + struct tevent_req *subreq; + int to_write = 0; + size_t i; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_writev_state); + if (req == NULL) { + return NULL; + } + + state->ops = stream->ops; + state->stream = stream; + state->ret = -1; + + /* first check if the input is ok */ +#ifdef IOV_MAX + if (count > IOV_MAX) { + tevent_req_error(req, EMSGSIZE); + goto post; + } +#endif + + for (i=0; i < count; i++) { + int tmp = to_write; + tmp += vector[i].iov_len; + + if (tmp < to_write) { + tevent_req_error(req, EMSGSIZE); + goto post; + } + + to_write = tmp; + } + + if (to_write == 0) { + tevent_req_error(req, EINVAL); + goto post; + } + + if (stream->writev_req) { + tevent_req_error(req, EBUSY); + goto post; + } + stream->writev_req = req; + + talloc_set_destructor(state, tstream_writev_destructor); + + subreq = state->ops->writev_send(state, ev, stream, vector, count); + if (tevent_req_nomem(subreq, req)) { + goto post; + } + tevent_req_set_callback(subreq, tstream_writev_done, req); + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tstream_writev_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tstream_writev_state *state = tevent_req_data(req, + struct tstream_writev_state); + ssize_t ret; + int sys_errno; + + ret = state->ops->writev_recv(subreq, &sys_errno); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + + state->ret = ret; + + tevent_req_done(req); +} + +int tstream_writev_recv(struct tevent_req *req, + int *perrno) +{ + struct tstream_writev_state *state = tevent_req_data(req, + struct tstream_writev_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + +struct tstream_disconnect_state { + const struct tstream_context_ops *ops; +}; + +static void tstream_disconnect_done(struct tevent_req *subreq); + +struct tevent_req *tstream_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream) +{ + struct tevent_req *req; + struct tstream_disconnect_state *state; + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_disconnect_state); + if (req == NULL) { + return NULL; + } + + state->ops = stream->ops; + + if (stream->readv_req || stream->writev_req) { + tevent_req_error(req, EBUSY); + goto post; + } + + subreq = state->ops->disconnect_send(state, ev, stream); + if (tevent_req_nomem(subreq, req)) { + goto post; + } + tevent_req_set_callback(subreq, tstream_disconnect_done, req); + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tstream_disconnect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tstream_disconnect_state *state = tevent_req_data(req, + struct tstream_disconnect_state); + int ret; + int sys_errno; + + ret = state->ops->disconnect_recv(subreq, &sys_errno); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + + tevent_req_done(req); +} + +int tstream_disconnect_recv(struct tevent_req *req, + int *perrno) +{ + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + + tevent_req_received(req); + return ret; +} + diff --git a/lib/tsocket/tsocket.h b/lib/tsocket/tsocket.h new file mode 100644 index 0000000000..d983325c45 --- /dev/null +++ b/lib/tsocket/tsocket.h @@ -0,0 +1,1071 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tsocket + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _TSOCKET_H +#define _TSOCKET_H + +#include <talloc.h> +#include <tevent.h> + +struct tsocket_address; +struct tdgram_context; +struct tstream_context; +struct iovec; + +/** + * @mainpage + * + * The tsocket abstraction is an API ... + */ + +/** + * @defgroup tsocket The tsocket API + * + * The tsocket abstraction is split into two different kinds of + * communication interfaces. + * + * There's the "tstream_context" interface with abstracts the communication + * through a bidirectional byte stream between two endpoints. + * + * And there's the "tdgram_context" interface with abstracts datagram based + * communication between any number of endpoints. + * + * Both interfaces share the "tsocket_address" abstraction for endpoint + * addresses. + * + * The whole library is based on the talloc(3) and 'tevent' libraries and + * provides "tevent_req" based "foo_send()"/"foo_recv()" functions pairs for + * all abstracted methods that need to be async. + * + * @section vsock Virtual Sockets + * + * The abstracted layout of tdgram_context and tstream_context allow + * implementations around virtual sockets for encrypted tunnels (like TLS, + * SASL or GSSAPI) or named pipes over smb. + * + * @section npa Named Pipe Auth (NPA) Sockets + * + * Samba has an implementation to abstract named pipes over smb (within the + * server side). See libcli/named_pipe_auth/npa_tstream.[ch] for the core code. + * The current callers are located in source4/ntvfs/ipc/vfs_ipc.c and + * source4/rpc_server/service_rpc.c for the users. + */ + +/** + * @defgroup tsocket_address The tsocket_address abstraction + * @ingroup tsocket + * + * The tsocket_address represents an socket endpoint genericly. + * As it's like an abstract class it has no specific constructor. + * The specific constructors are descripted in later sections. + * + * @{ + */ + +/** + * @brief Get a string representaion of the endpoint. + * + * This function creates a string representation of the endpoint for debugging. + * The output will look as followed: + * prefix:address:port + * + * e.g. + * ipv4:192.168.1.1:143 + * + * Callers should not try to parse the string! The should use additional methods + * of the specific tsocket_address implemention to get more details. + * + * @param[in] addr The address to convert. + * + * @param[in] mem_ctx The talloc memory context to allocate the memory. + * + * @return The address as a string representation, NULL on error. + * + * @see tsocket_address_inet_addr_string() + * @see tsocket_address_inet_port() + */ +char *tsocket_address_string(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); + +#ifdef DOXYGEN +/** + * @brief This creates a copy of a tsocket_address. + * + * This is useful when before doing modifications to a socket via additional + * methods of the specific tsocket_address implementation. + * + * @param[in] addr The address to create the copy from. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @return A newly allocated copy of addr (tsocket_address *), NULL + * on error. + */ +struct tsocket_address *tsocket_address_copy(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); +#else +struct tsocket_address *_tsocket_address_copy(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx, + const char *location); + +#define tsocket_address_copy(addr, mem_ctx) \ + _tsocket_address_copy(addr, mem_ctx, __location__) +#endif + +/** + * @} + */ + +/** + * @defgroup tdgram_context The tdgram_context abstraction + * @ingroup tsocket + * + * The tdgram_context is like an abstract class for datagram based sockets. The + * interface provides async 'tevent_req' based functions on top functionality + * is similar to the recvfrom(2)/sendto(2)/close(2) syscalls. + * + * @note You can always use talloc_free(tdgram) to cleanup the resources + * of the tdgram_context on a fatal error. + * @{ + */ + +/** + * @brief Ask for next available datagram on the abstracted tdgram_context. + * + * It returns a 'tevent_req' handle, where the caller can register + * a callback with tevent_req_set_callback(). The callback is triggered + * when a datagram is available or an error happened. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] dgram The dgram context to work on. + * + * @return Returns a 'tevent_req' handle, where the caller can + * register a callback with tevent_req_set_callback(). + * NULL on fatal error. + * + * @see tdgram_inet_udp_socket() + * @see tdgram_unix_socket() + */ +struct tevent_req *tdgram_recvfrom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram); + +/** + * @brief Receive the next available datagram on the abstracted tdgram_context. + * + * This function should be called by the callback when a datagram is available + * or an error happened. + * + * The caller can only have one outstanding tdgram_recvfrom_send() at a time + * otherwise the caller will get '*perrno = EBUSY'. + * + * @param[in] req The tevent request from tdgram_recvfrom_send(). + * + * @param[out] perrno The error number, set if an error occurred. + * + * @param[in] mem_ctx The memory context to use. + * + * @param[out] buf This will hold the buffer of the datagram. + * + * @param[out] src The abstracted tsocket_address of the sender of the + * received datagram. + * + * @return The length of the datagram (0 is never returned!), + * -1 on error with perrno set to the actual errno. + * + * @see tdgram_recvfrom_send() + */ +ssize_t tdgram_recvfrom_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + uint8_t **buf, + struct tsocket_address **src); + +/** + * @brief Send a datagram to a destination endpoint. + * + * The function can be called to send a datagram (specified by a buf/len) to a + * destination endpoint (specified by dst). It's not allowed for len to be 0. + * + * It returns a 'tevent_req' handle, where the caller can register a callback + * with tevent_req_set_callback(). The callback is triggered when the specific + * implementation (assumes it) has delivered the datagram to the "wire". + * + * The callback is then supposed to get the result by calling + * tdgram_sendto_recv() on the 'tevent_req'. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] dgram The dgram context to work on. + * + * @param[in] buf The buffer to send. + * + * @param[in] len The length of the buffer to send. It has to be bigger + * than 0. + * + * @param[in] dst The destination to send the datagram to in form of a + * tsocket_address. + * + * @return Returns a 'tevent_req' handle, where the caller can + * register a callback with tevent_req_set_callback(). + * NULL on fatal error. + * + * @see tdgram_inet_udp_socket() + * @see tdgram_unix_socket() + * @see tdgram_sendto_recv() + */ +struct tevent_req *tdgram_sendto_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + const uint8_t *buf, size_t len, + const struct tsocket_address *dst); + +/** + * @brief Receive the result of the sent datagram. + * + * The caller can only have one outstanding tdgram_sendto_send() at a time + * otherwise the caller will get '*perrno = EBUSY'. + * + * @param[in] req The tevent request from tdgram_sendto_send(). + * + * @param[out] perrno The error number, set if an error occurred. + * + * @return The length of the datagram (0 is never returned!), -1 on + * error with perrno set to the actual errno. + * + * @see tdgram_sendto_send() + */ +ssize_t tdgram_sendto_recv(struct tevent_req *req, + int *perrno); + +/** + * @brief Shutdown/close an abstracted socket. + * + * It returns a 'tevent_req' handle, where the caller can register a callback + * with tevent_req_set_callback(). The callback is triggered when the specific + * implementation (assumes it) has delivered the datagram to the "wire". + * + * The callback is then supposed to get the result by calling + * tdgram_sendto_recv() on the 'tevent_req'. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] dgram The dgram context diconnect from. + * + * @return Returns a 'tevent_req' handle, where the caller can + * register a callback with tevent_req_set_callback(). + * NULL on fatal error. + * + * @see tdgram_disconnect_recv() + */ +struct tevent_req *tdgram_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram); + +/** + * @brief Receive the result from a tdgram_disconnect_send() request. + * + * The caller should make sure there're no outstanding tdgram_recvfrom_send() + * and tdgram_sendto_send() calls otherwise the caller will get + * '*perrno = EBUSY'. + * + * @param[in] req The tevent request from tdgram_disconnect_send(). + * + * @param[out] perrno The error number, set if an error occurred. + * + * @return The length of the datagram (0 is never returned!), -1 on + * error with perrno set to the actual errno. + * + * @see tdgram_disconnect_send() + */ +int tdgram_disconnect_recv(struct tevent_req *req, + int *perrno); + +/** + * @} + */ + +/** + * @defgroup tstream_context The tstream_context abstraction + * @ingroup tsocket + * + * The tstream_context is like an abstract class for stream based sockets. The + * interface provides async 'tevent_req' based functions on top functionality + * is similar to the readv(2)/writev(2)/close(2) syscalls. + * + * @note You can always use talloc_free(tstream) to cleanup the resources + * of the tstream_context on a fatal error. + * + * @{ + */ + +/** + * @brief Report the number of bytes received but not consumed yet. + * + * The tstream_pending_bytes() function reports how much bytes of the incoming + * stream have been received but not consumed yet. + * + * @param[in] stream The tstream_context to check for pending bytes. + * + * @return The number of bytes received, -1 on error with errno + * set. + */ +ssize_t tstream_pending_bytes(struct tstream_context *stream); + +/** + * @brief Read a specific amount of bytes from a stream socket. + * + * The function can be called to read for a specific amount of bytes from the + * stream into given buffers. The caller has to preallocate the buffers. + * + * The caller might need to use tstream_pending_bytes() if the protocol doesn't + * have a fixed pdu header containing the pdu size. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] stream The tstream context to work on. + * + * @param[out] vector A preallocated iovec to store the data to read. + * + * @param[in] count The number of buffers in the vector allocated. + * + * @return A 'tevent_req' handle, where the caller can register + * a callback with tevent_req_set_callback(). NULL on + * fatal error. + * + * @see tstream_unix_connect_send() + * @see tstream_inet_tcp_connect_send() + */ +struct tevent_req *tstream_readv_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct iovec *vector, + size_t count); + +/** + * @brief Get the result of a tstream_readv_send(). + * + * The caller can only have one outstanding tstream_readv_send() + * at a time otherwise the caller will get *perrno = EBUSY. + * + * @param[in] req The tevent request from tstream_readv_send(). + * + * @param[out] perrno The error number, set if an error occurred. + * + * @return The length of the stream (0 is never returned!), -1 on + * error with perrno set to the actual errno. + */ +int tstream_readv_recv(struct tevent_req *req, + int *perrno); + +/** + * @brief Write buffers from a vector into a stream socket. + * + * The function can be called to write buffers from a given vector + * to a stream socket. + * + * You have to ensure that the vector is not empty. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] stream The tstream context to work on. + * + * @param[in] vector The iovec vector with data to write on a stream socket. + * + * @param[in] count The number of buffers in the vector to write. + * + * @return A 'tevent_req' handle, where the caller can register + * a callback with tevent_req_set_callback(). NULL on + * fatal error. + */ +struct tevent_req *tstream_writev_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + const struct iovec *vector, + size_t count); + +/** + * @brief Get the result of a tstream_writev_send(). + * + * The caller can only have one outstanding tstream_writev_send() + * at a time otherwise the caller will get *perrno = EBUSY. + * + * @param[in] req The tevent request from tstream_writev_send(). + * + * @param[out] perrno The error number, set if an error occurred. + * + * @return The length of the stream (0 is never returned!), -1 on + * error with perrno set to the actual errno. + */ +int tstream_writev_recv(struct tevent_req *req, + int *perrno); + +/** + * @brief Shutdown/close an abstracted socket. + * + * It returns a 'tevent_req' handle, where the caller can register a callback + * with tevent_req_set_callback(). The callback is triggered when the specific + * implementation (assumes it) has delivered the stream to the "wire". + * + * The callback is then supposed to get the result by calling + * tdgram_sendto_recv() on the 'tevent_req'. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] stream The tstream context to work on. + * + * @return A 'tevent_req' handle, where the caller can register + * a callback with tevent_req_set_callback(). NULL on + * fatal error. + */ +struct tevent_req *tstream_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream); + +/** + * @brief Get the result of a tstream_disconnect_send(). + * + * The caller can only have one outstanding tstream_writev_send() + * at a time otherwise the caller will get *perrno = EBUSY. + * + * @param[in] req The tevent request from tstream_disconnect_send(). + * + * @param[out] perrno The error number, set if an error occurred. + * + * @return The length of the stream (0 is never returned!), -1 on + * error with perrno set to the actual errno. + */ +int tstream_disconnect_recv(struct tevent_req *req, + int *perrno); + +/** + * @} + */ + + +/** + * @defgroup tsocket_bsd tsocket_bsd - inet, inet6 and unix + * @ingroup tsocket + * + * The main tsocket library comes with implentations for BSD style ipv4, ipv6 + * and unix sockets. + * + * @{ + */ + +#if DOXYGEN +/** + * @brief Create a tsocket_address for ipv4 and ipv6 endpoint addresses. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] fam The family can be can be "ipv4", "ipv6" or "ip". With + * "ip" is autodetects "ipv4" or "ipv6" based on the + * addr. + * + * @param[in] addr A valid ip address string based on the selected family + * (dns names are not allowed!). It's valid to pass NULL, + * which gets mapped to "0.0.0.0" or "::". + * + * @param[in] port A valid port number. + * + * @param[out] _addr A tsocket_address pointer to store the information. + * + * @return 0 on success, -1 on error with errno set. + */ +int tsocket_address_inet_from_strings(TALLOC_CTX *mem_ctx, + const char *fam, + const char *addr, + uint16_t port, + struct tsocket_address **_addr); +#else +int _tsocket_address_inet_from_strings(TALLOC_CTX *mem_ctx, + const char *fam, + const char *addr, + uint16_t port, + struct tsocket_address **_addr, + const char *location); + +#define tsocket_address_inet_from_strings(mem_ctx, fam, addr, port, _addr) \ + _tsocket_address_inet_from_strings(mem_ctx, fam, addr, port, _addr, \ + __location__) +#endif + +/** + * @brief Get the address of an 'inet' tsocket_address as a string. + * + * @param[in] addr The address to convert to a string. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @return A newly allocated string of the address, NULL on error + * with errno set. + */ +char *tsocket_address_inet_addr_string(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); + +/** + * @brief Get the port number as an integer from an 'inet' tsocket_address. + * + * @param[in] addr The tsocket address to use. + * + * @return The port number, 0 on error with errno set. + */ +uint16_t tsocket_address_inet_port(const struct tsocket_address *addr); + +/** + * @brief Set the port number of an existing 'inet' tsocket_address. + * + * @param[in] addr The existing tsocket_address to use. + * + * @param[in] port The valid port number to set. + * + * @return 0 on success, -1 on error with errno set. + */ +int tsocket_address_inet_set_port(struct tsocket_address *addr, + uint16_t port); + +#ifdef DOXYGEN +/** + * @brief Create a tsocket_address for a unix domain endpoint addresses. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] path The filesystem path, NULL will map "". + * + * @param[in] _addr The tsocket_address pointer to store the information. + * + * @return 0 on success, -1 on error with errno set. + */ +int tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx, + const char *path, + struct tsocket_address **_addr); +#else +int _tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx, + const char *path, + struct tsocket_address **_addr, + const char *location); + +#define tsocket_address_unix_from_path(mem_ctx, path, _addr) \ + _tsocket_address_unix_from_path(mem_ctx, path, _addr, \ + __location__) +#endif + +/** + * @brief Get the address of an 'unix' tsocket_address. + * + * @param[in] addr A valid 'unix' tsocket_address. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @return The path of the unix domain socket, NULL on error or if + * the tsocket_address doesn't represent an unix domain + * endpoint path. + */ +char *tsocket_address_unix_path(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); + +#ifdef DOXYGEN +/** + * @brief Create a tdgram_context for a ipv4 or ipv6 UDP communication. + * + * @param[in] local An 'inet' tsocket_address for the local endpoint. + * + * @param[in] remote An 'inet' tsocket_address for the remote endpoint or + * NULL (??? to create a listener?). + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] dgram The tdgram_context pointer to setup the udp + * communication. The function will allocate the memory. + * + * @return 0 on success, -1 on error with errno set. + */ +int tdgram_inet_udp_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram); +#else +int _tdgram_inet_udp_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram, + const char *location); +#define tdgram_inet_udp_socket(local, remote, mem_ctx, dgram) \ + _tdgram_inet_udp_socket(local, remote, mem_ctx, dgram, __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Create a tdgram_context for unix domain datagram communication. + * + * @param[in] local An 'unix' tsocket_address for the local endpoint. + * + * @param[in] remote An 'unix' tsocket_address for the remote endpoint or + * NULL (??? to create a listener?). + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] dgram The tdgram_context pointer to setup the udp + * communication. The function will allocate the memory. + * + * @return 0 on success, -1 on error with errno set. + */ +int tdgram_unix_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram); +#else +int _tdgram_unix_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram, + const char *location); + +#define tdgram_unix_socket(local, remote, mem_ctx, dgram) \ + _tdgram_unix_socket(local, remote, mem_ctx, dgram, __location__) +#endif + +/** + * @brief Connect async to a TCP endpoint and create a tstream_context for the + * stream based communication. + * + * Use this function to connenct asynchronously to a remote ipv4 or ipv6 TCP + * endpoint and create a tstream_context for the stream based communication. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] local An 'inet' tsocket_address for the local endpoint. + * + * @param[in] remote An 'inet' tsocket_address for the remote endpoint. + * + * @return A 'tevent_req' handle, where the caller can register a + * callback with tevent_req_set_callback(). NULL on a fatal + * error. + * + * @see tstream_inet_tcp_connect_recv() + */ +struct tevent_req *tstream_inet_tcp_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct tsocket_address *local, + const struct tsocket_address *remote); + +#ifdef DOXYGEN +/** + * @brief Receive the result from a tstream_inet_tcp_connect_send(). + * + * @param[in] req The tevent request from tstream_inet_tcp_connect_send(). + * + * @param[out] perrno The error number, set if an error occurred. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] stream A tstream_context pointer to setup the tcp communication + * on. This function will allocate the memory. + * + * @return 0 on success, -1 on error with perrno set. + */ +int tstream_inet_tcp_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream); +#else +int _tstream_inet_tcp_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream, + const char *location); +#define tstream_inet_tcp_connect_recv(req, perrno, mem_ctx, stream) \ + _tstream_inet_tcp_connect_recv(req, perrno, mem_ctx, stream, \ + __location__) +#endif + +/** + * @brief Connect async to a unix domain endpoint and create a tstream_context + * for the stream based communication. + * + * Use this function to connenct asynchronously to a unix domainendpoint and + * create a tstream_context for the stream based communication. + * + * The callback is triggered when a socket is connected and ready for IO or an + * error happened. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] local An 'unix' tsocket_address for the local endpoint. + * + * @param[in] remote An 'unix' tsocket_address for the remote endpoint. + * + * @return A 'tevent_req' handle, where the caller can register a + * callback with tevent_req_set_callback(). NULL on a falal + * error. + * + * @see tstream_unix_connect_recv() + */ +struct tevent_req * tstream_unix_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct tsocket_address *local, + const struct tsocket_address *remote); + +#ifdef DOXYGEN +/** + * @brief Receive the result from a tstream_unix_connect_send(). + * + * @param[in] req The tevent request from tstream_inet_tcp_connect_send(). + * + * @param[out] perrno The error number, set if an error occurred. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] stream The tstream context to work on. + * + * @return 0 on success, -1 on error with perrno set. + */ +int tstream_unix_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream); +#else +int _tstream_unix_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream, + const char *location); +#define tstream_unix_connect_recv(req, perrno, mem_ctx, stream) \ + _tstream_unix_connect_recv(req, perrno, mem_ctx, stream, \ + __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Create two connected 'unix' tsocket_contexts for stream based + * communication. + * + * @param[in] mem_ctx1 The talloc memory context to use for stream1. + * + * @param[in] stream1 The first stream to connect. + * + * @param[in] mem_ctx2 The talloc memory context to use for stream2. + * + * @param[in] stream2 The second stream to connect. + * + * @return 0 on success, -1 on error with errno set. + */ +int tstream_unix_socketpair(TALLOC_CTX *mem_ctx1, + struct tstream_context **stream1, + TALLOC_CTX *mem_ctx2, + struct tstream_context **stream2); +#else +int _tstream_unix_socketpair(TALLOC_CTX *mem_ctx1, + struct tstream_context **_stream1, + TALLOC_CTX *mem_ctx2, + struct tstream_context **_stream2, + const char *location); + +#define tstream_unix_socketpair(mem_ctx1, stream1, mem_ctx2, stream2) \ + _tstream_unix_socketpair(mem_ctx1, stream1, mem_ctx2, stream2, \ + __location__) +#endif + +struct sockaddr; + +#ifdef DOXYGEN +/** + * @brief Convert a tsocket address to a bsd socket address. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] sa The sockaddr structure to convert. + * + * @param[in] sa_socklen The lenth of the sockaddr sturucte. + * + * @param[out] addr The tsocket pointer to allocate and fill. + * + * @return 0 on success, -1 on error with errno set. + */ +int tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx, + struct sockaddr *sa, + size_t sa_socklen, + struct tsocket_address **addr); +#else +int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx, + struct sockaddr *sa, + size_t sa_socklen, + struct tsocket_address **_addr, + const char *location); + +#define tsocket_address_bsd_from_sockaddr(mem_ctx, sa, sa_socklen, _addr) \ + _tsocket_address_bsd_from_sockaddr(mem_ctx, sa, sa_socklen, _addr, \ + __location__) +#endif + +/** + * @brief Fill a bsd sockaddr structure. + * + * @param[in] addr The tsocket address structure to use. + * + * @param[in] sa The bsd sockaddr structure to fill out. + * + * @param[in] sa_socklen The length of the bsd sockaddr structure to fill out. + * + * @return The actual size of the sockaddr structure, -1 on error + * with errno set. The size could differ from sa_socklen. + * + * @code + * ssize_t socklen; + * struct sockaddr_storage ss; + * + * socklen = tsocket_address_bsd_sockaddr(taddr, + * (struct sockaddr *) &ss, + * sizeof(struct sockaddr_storage)); + * if (socklen < 0) { + * return -1; + * } + * @endcode + */ +ssize_t tsocket_address_bsd_sockaddr(const struct tsocket_address *addr, + struct sockaddr *sa, + size_t sa_socklen); + +#ifdef DOXYGEN +/** + * @brief Wrap an existing file descriptors into the tstream abstraction. + * + * You can use this function to wrap an existing file descriptors into the + * tstream abstraction. After that you're not able to use this file descriptor + * for anything else. The file descriptor will be closed when the stream gets + * freed. If you still want to use the fd you have have to create a duplicate. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] fd The non blocking fd to use! + * + * @param[in] stream The filed tstream_context you allocated before. + * + * @return 0 on success, -1 on error with errno set. + * + * @warning You should read the tsocket_bsd.c code and unterstand it in order + * use this function. + */ +int tstream_bsd_existing_socket(TALLOC_CTX *mem_ctx, + int fd, + struct tstream_context **stream); +#else +int _tstream_bsd_existing_socket(TALLOC_CTX *mem_ctx, + int fd, + struct tstream_context **_stream, + const char *location); +#define tstream_bsd_existing_socket(mem_ctx, fd, stream) \ + _tstream_bsd_existing_socket(mem_ctx, fd, stream, \ + __location__) +#endif + +/** + * @} + */ + +/** + * @defgroup tsocket_helper Queue and PDU helpers + * @ingroup tsocket + * + * In order to make the live easier for callers which want to implement a + * function to receive a full PDU with a single async function pair, there're + * some helper functions. + * + * There're some cases where the caller wants doesn't care about the order of + * doing IO on the abstracted sockets. + * + * @{ + */ + +/** + * @brief Queue a dgram blob for sending through the socket. + * + * This function queues a blob for sending to destination through an existing + * dgram socket. The async callback is triggered when the whole blob is + * delivered to the underlying system socket. + * + * The caller needs to make sure that all non-scalar input parameters hang + * around for the whole lifetime of the request. + * + * @param[in] mem_ctx The memory context for the result. + * + * @param[in] ev The event context the operation should work on. + * + * @param[in] dgram The tdgram_context to send the message buffer. + * + * @param[in] queue The existing dgram queue. + * + * @param[in] buf The message buffer to send. + * + * @param[in] len The message length. + * + * @param[in] dst The destination socket address. + * + * @return The async request handle. NULL on fatal error. + * + * @see tdgram_sendto_queue_recv() + */ +struct tevent_req *tdgram_sendto_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + struct tevent_queue *queue, + const uint8_t *buf, + size_t len, + struct tsocket_address *dst); + +/** + * @brief Receive the result of the sent dgram blob. + * + * @param[in] req The tevent request from tdgram_sendto_queue_send(). + * + * @param[out] perrno The error set to the actual errno. + * + * @return The length of the datagram (0 is never returned!), -1 on + * error with perrno set to the actual errno. + */ +ssize_t tdgram_sendto_queue_recv(struct tevent_req *req, int *perrno); + +typedef int (*tstream_readv_pdu_next_vector_t)(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **vector, + size_t *count); + +struct tevent_req *tstream_readv_pdu_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + tstream_readv_pdu_next_vector_t next_vector_fn, + void *next_vector_private); +int tstream_readv_pdu_recv(struct tevent_req *req, int *perrno); + +/** + * @brief Queue a read request for a PDU on the socket. + * + * This function queues a read request for a PDU on a stream socket. The async + * callback is triggered when a full PDU has been read from the socket. + * + * The caller needs to make sure that all non-scalar input parameters hang + * around for the whole lifetime of the request. + * + * @param[in] mem_ctx The memory context for the result + * + * @param[in] ev The tevent_context to run on + * + * @param[in] stream The stream to send data through + * + * @param[in] queue The existing send queue + * + * @param[in] next_vector_fn The next vector function + * + * @param[in] next_vector_private The private_data of the next vector function + * + * @return The async request handle. NULL on fatal error. + * + * @see tstream_readv_pdu_queue_recv() + */ +struct tevent_req *tstream_readv_pdu_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct tevent_queue *queue, + tstream_readv_pdu_next_vector_t next_vector_fn, + void *next_vector_private); + +/** + * @brief Receive the PDU blob read from the stream. + * + * @param[in] req The tevent request from tstream_readv_pdu_queue_send(). + * + * @param[out] perrno The error set to the actual errno. + * + * @return The number of bytes read on success, -1 on error with + * perrno set to the actual errno. + */ +int tstream_readv_pdu_queue_recv(struct tevent_req *req, int *perrno); + +/** + * @brief Queue an iovector for sending through the socket + * + * This function queues an iovector for sending to destination through an + * existing stream socket. The async callback is triggered when the whole + * vectror has been delivered to the underlying system socket. + * + * The caller needs to make sure that all non-scalar input parameters hang + * around for the whole lifetime of the request. + * + * @param[in] mem_ctx The memory context for the result. + * + * @param[in] ev The tevent_context to run on. + * + * @param[in] stream The stream to send data through. + * + * @param[in] queue The existing send queue. + * + * @param[in] vector The iovec vector so write. + * + * @param[in] count The size of the vector. + * + * @return The async request handle. NULL on fatal error. + */ +struct tevent_req *tstream_writev_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct tevent_queue *queue, + const struct iovec *vector, + size_t count); + +/** + * @brief Receive the result of the sent iovector. + * + * @param[in] req The tevent request from tstream_writev_queue_send(). + * + * @param[out] perrno The error set to the actual errno. + * + * @return The length of the iovector (0 is never returned!), -1 on + * error with perrno set to the actual errno. + */ +int tstream_writev_queue_recv(struct tevent_req *req, int *perrno); + +/** + * @} + */ + +#endif /* _TSOCKET_H */ + diff --git a/lib/tsocket/tsocket_bsd.c b/lib/tsocket/tsocket_bsd.c new file mode 100644 index 0000000000..43defb30c8 --- /dev/null +++ b/lib/tsocket/tsocket_bsd.c @@ -0,0 +1,2304 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tsocket + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/network.h" +#include "tsocket.h" +#include "tsocket_internal.h" + +static int tsocket_bsd_error_from_errno(int ret, + int sys_errno, + bool *retry) +{ + *retry = false; + + if (ret >= 0) { + return 0; + } + + if (ret != -1) { + return EIO; + } + + if (sys_errno == 0) { + return EIO; + } + + if (sys_errno == EINTR) { + *retry = true; + return sys_errno; + } + + if (sys_errno == EINPROGRESS) { + *retry = true; + return sys_errno; + } + + if (sys_errno == EAGAIN) { + *retry = true; + return sys_errno; + } + +#ifdef EWOULDBLOCK + if (sys_errno == EWOULDBLOCK) { + *retry = true; + return sys_errno; + } +#endif + + return sys_errno; +} + +static int tsocket_bsd_common_prepare_fd(int fd, bool high_fd) +{ + int i; + int sys_errno = 0; + int fds[3]; + int num_fds = 0; + + int result, flags; + + if (fd == -1) { + return -1; + } + + /* first make a fd >= 3 */ + if (high_fd) { + while (fd < 3) { + fds[num_fds++] = fd; + fd = dup(fd); + if (fd == -1) { + sys_errno = errno; + break; + } + } + for (i=0; i<num_fds; i++) { + close(fds[i]); + } + if (fd == -1) { + errno = sys_errno; + return fd; + } + } + + /* fd should be nonblocking. */ + +#ifdef O_NONBLOCK +#define FLAG_TO_SET O_NONBLOCK +#else +#ifdef SYSV +#define FLAG_TO_SET O_NDELAY +#else /* BSD */ +#define FLAG_TO_SET FNDELAY +#endif +#endif + + if ((flags = fcntl(fd, F_GETFL)) == -1) { + goto fail; + } + + flags |= FLAG_TO_SET; + if (fcntl(fd, F_SETFL, flags) == -1) { + goto fail; + } + +#undef FLAG_TO_SET + + /* fd should be closed on exec() */ +#ifdef FD_CLOEXEC + result = flags = fcntl(fd, F_GETFD, 0); + if (flags >= 0) { + flags |= FD_CLOEXEC; + result = fcntl(fd, F_SETFD, flags); + } + if (result < 0) { + goto fail; + } +#endif + return fd; + + fail: + if (fd != -1) { + sys_errno = errno; + close(fd); + errno = sys_errno; + } + return -1; +} + +static ssize_t tsocket_bsd_pending(int fd) +{ + int ret, error; + int value = 0; + socklen_t len; + + ret = ioctl(fd, FIONREAD, &value); + if (ret == -1) { + return ret; + } + + if (ret != 0) { + /* this should not be reached */ + errno = EIO; + return -1; + } + + if (value != 0) { + return value; + } + + error = 0; + len = sizeof(error); + + /* + * if no data is available check if the socket is in error state. For + * dgram sockets it's the way to return ICMP error messages of + * connected sockets to the caller. + */ + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret == -1) { + return ret; + } + if (error != 0) { + errno = error; + return -1; + } + return 0; +} + +static const struct tsocket_address_ops tsocket_address_bsd_ops; + +struct tsocket_address_bsd { + socklen_t sa_socklen; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifdef HAVE_IPV6 + struct sockaddr_in6 in6; +#endif + struct sockaddr_un un; + struct sockaddr_storage ss; + } u; +}; + +int _tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx, + struct sockaddr *sa, + size_t sa_socklen, + struct tsocket_address **_addr, + const char *location) +{ + struct tsocket_address *addr; + struct tsocket_address_bsd *bsda; + + if (sa_socklen < sizeof(sa->sa_family)) { + errno = EINVAL; + return -1; + } + + switch (sa->sa_family) { + case AF_UNIX: + if (sa_socklen > sizeof(struct sockaddr_un)) { + sa_socklen = sizeof(struct sockaddr_un); + } + break; + case AF_INET: + if (sa_socklen < sizeof(struct sockaddr_in)) { + errno = EINVAL; + return -1; + } + sa_socklen = sizeof(struct sockaddr_in); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + if (sa_socklen < sizeof(struct sockaddr_in6)) { + errno = EINVAL; + return -1; + } + sa_socklen = sizeof(struct sockaddr_in6); + break; +#endif + default: + errno = EAFNOSUPPORT; + return -1; + } + + if (sa_socklen > sizeof(struct sockaddr_storage)) { + errno = EINVAL; + return -1; + } + + addr = tsocket_address_create(mem_ctx, + &tsocket_address_bsd_ops, + &bsda, + struct tsocket_address_bsd, + location); + if (!addr) { + errno = ENOMEM; + return -1; + } + + ZERO_STRUCTP(bsda); + + memcpy(&bsda->u.ss, sa, sa_socklen); + + bsda->sa_socklen = sa_socklen; + + *_addr = addr; + return 0; +} + +ssize_t tsocket_address_bsd_sockaddr(const struct tsocket_address *addr, + struct sockaddr *sa, + size_t sa_socklen) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + + if (!bsda) { + errno = EINVAL; + return -1; + } + + if (sa_socklen < bsda->sa_socklen) { + errno = EINVAL; + return -1; + } + + if (sa_socklen > bsda->sa_socklen) { + memset(sa, 0, sa_socklen); + sa_socklen = bsda->sa_socklen; + } + + memcpy(sa, &bsda->u.ss, sa_socklen); + return sa_socklen; +} + +int _tsocket_address_inet_from_strings(TALLOC_CTX *mem_ctx, + const char *fam, + const char *addr, + uint16_t port, + struct tsocket_address **_addr, + const char *location) +{ + struct addrinfo hints; + struct addrinfo *result = NULL; + char port_str[6]; + int ret; + + ZERO_STRUCT(hints); + /* + * we use SOCKET_STREAM here to get just one result + * back from getaddrinfo(). + */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + + if (strcasecmp(fam, "ip") == 0) { + hints.ai_family = AF_UNSPEC; + if (!addr) { +#ifdef HAVE_IPV6 + addr = "::"; +#else + addr = "0.0.0.0"; +#endif + } + } else if (strcasecmp(fam, "ipv4") == 0) { + hints.ai_family = AF_INET; + if (!addr) { + addr = "0.0.0.0"; + } +#ifdef HAVE_IPV6 + } else if (strcasecmp(fam, "ipv6") == 0) { + hints.ai_family = AF_INET6; + if (!addr) { + addr = "::"; + } +#endif + } else { + errno = EAFNOSUPPORT; + return -1; + } + + snprintf(port_str, sizeof(port_str) - 1, "%u", port); + + ret = getaddrinfo(addr, port_str, &hints, &result); + if (ret != 0) { + switch (ret) { + case EAI_FAIL: + errno = EINVAL; + break; + } + ret = -1; + goto done; + } + + if (result->ai_socktype != SOCK_STREAM) { + errno = EINVAL; + ret = -1; + goto done; + } + + ret = _tsocket_address_bsd_from_sockaddr(mem_ctx, + result->ai_addr, + result->ai_addrlen, + _addr, + location); + +done: + if (result) { + freeaddrinfo(result); + } + return ret; +} + +char *tsocket_address_inet_addr_string(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + char addr_str[INET6_ADDRSTRLEN+1]; + const char *str; + + if (!bsda) { + errno = EINVAL; + return NULL; + } + + switch (bsda->u.sa.sa_family) { + case AF_INET: + str = inet_ntop(bsda->u.in.sin_family, + &bsda->u.in.sin_addr, + addr_str, sizeof(addr_str)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + str = inet_ntop(bsda->u.in6.sin6_family, + &bsda->u.in6.sin6_addr, + addr_str, sizeof(addr_str)); + break; +#endif + default: + errno = EINVAL; + return NULL; + } + + if (!str) { + return NULL; + } + + return talloc_strdup(mem_ctx, str); +} + +uint16_t tsocket_address_inet_port(const struct tsocket_address *addr) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + uint16_t port = 0; + + if (!bsda) { + errno = EINVAL; + return 0; + } + + switch (bsda->u.sa.sa_family) { + case AF_INET: + port = ntohs(bsda->u.in.sin_port); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + port = ntohs(bsda->u.in6.sin6_port); + break; +#endif + default: + errno = EINVAL; + return 0; + } + + return port; +} + +int tsocket_address_inet_set_port(struct tsocket_address *addr, + uint16_t port) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + + if (!bsda) { + errno = EINVAL; + return -1; + } + + switch (bsda->u.sa.sa_family) { + case AF_INET: + bsda->u.in.sin_port = htons(port); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + bsda->u.in6.sin6_port = htons(port); + break; +#endif + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +int _tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx, + const char *path, + struct tsocket_address **_addr, + const char *location) +{ + struct sockaddr_un un; + void *p = &un; + int ret; + + if (!path) { + path = ""; + } + + if (strlen(path) > sizeof(un.sun_path)-1) { + errno = ENAMETOOLONG; + return -1; + } + + ZERO_STRUCT(un); + un.sun_family = AF_UNIX; + strncpy(un.sun_path, path, sizeof(un.sun_path)-1); + + ret = _tsocket_address_bsd_from_sockaddr(mem_ctx, + (struct sockaddr *)p, + sizeof(un), + _addr, + location); + + return ret; +} + +char *tsocket_address_unix_path(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + const char *str; + + if (!bsda) { + errno = EINVAL; + return NULL; + } + + switch (bsda->u.sa.sa_family) { + case AF_UNIX: + str = bsda->u.un.sun_path; + break; + default: + errno = EINVAL; + return NULL; + } + + return talloc_strdup(mem_ctx, str); +} + +static char *tsocket_address_bsd_string(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + char *str; + char *addr_str; + const char *prefix = NULL; + uint16_t port; + + switch (bsda->u.sa.sa_family) { + case AF_UNIX: + return talloc_asprintf(mem_ctx, "unix:%s", + bsda->u.un.sun_path); + case AF_INET: + prefix = "ipv4"; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + prefix = "ipv6"; + break; +#endif + default: + errno = EINVAL; + return NULL; + } + + addr_str = tsocket_address_inet_addr_string(addr, mem_ctx); + if (!addr_str) { + return NULL; + } + + port = tsocket_address_inet_port(addr); + + str = talloc_asprintf(mem_ctx, "%s:%s:%u", + prefix, addr_str, port); + talloc_free(addr_str); + + return str; +} + +static struct tsocket_address *tsocket_address_bsd_copy(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx, + const char *location) +{ + struct tsocket_address_bsd *bsda = talloc_get_type(addr->private_data, + struct tsocket_address_bsd); + struct tsocket_address *copy; + int ret; + + ret = _tsocket_address_bsd_from_sockaddr(mem_ctx, + &bsda->u.sa, + bsda->sa_socklen, + ©, + location); + if (ret != 0) { + return NULL; + } + + return copy; +} + +static const struct tsocket_address_ops tsocket_address_bsd_ops = { + .name = "bsd", + .string = tsocket_address_bsd_string, + .copy = tsocket_address_bsd_copy, +}; + +struct tdgram_bsd { + int fd; + + void *event_ptr; + struct tevent_fd *fde; + + void *readable_private; + void (*readable_handler)(void *private_data); + void *writeable_private; + void (*writeable_handler)(void *private_data); +}; + +static void tdgram_bsd_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct tdgram_bsd *bsds = talloc_get_type_abort(private_data, + struct tdgram_bsd); + + if (flags & TEVENT_FD_WRITE) { + bsds->writeable_handler(bsds->writeable_private); + return; + } + if (flags & TEVENT_FD_READ) { + if (!bsds->readable_handler) { + TEVENT_FD_NOT_READABLE(bsds->fde); + return; + } + bsds->readable_handler(bsds->readable_private); + return; + } +} + +static int tdgram_bsd_set_readable_handler(struct tdgram_bsd *bsds, + struct tevent_context *ev, + void (*handler)(void *private_data), + void *private_data) +{ + if (ev == NULL) { + if (handler) { + errno = EINVAL; + return -1; + } + if (!bsds->readable_handler) { + return 0; + } + bsds->readable_handler = NULL; + bsds->readable_private = NULL; + + return 0; + } + + /* read and write must use the same tevent_context */ + if (bsds->event_ptr != ev) { + if (bsds->readable_handler || bsds->writeable_handler) { + errno = EINVAL; + return -1; + } + bsds->event_ptr = NULL; + TALLOC_FREE(bsds->fde); + } + + if (tevent_fd_get_flags(bsds->fde) == 0) { + TALLOC_FREE(bsds->fde); + + bsds->fde = tevent_add_fd(ev, bsds, + bsds->fd, TEVENT_FD_READ, + tdgram_bsd_fde_handler, + bsds); + if (!bsds->fde) { + errno = ENOMEM; + return -1; + } + + /* cache the event context we're running on */ + bsds->event_ptr = ev; + } else if (!bsds->readable_handler) { + TEVENT_FD_READABLE(bsds->fde); + } + + bsds->readable_handler = handler; + bsds->readable_private = private_data; + + return 0; +} + +static int tdgram_bsd_set_writeable_handler(struct tdgram_bsd *bsds, + struct tevent_context *ev, + void (*handler)(void *private_data), + void *private_data) +{ + if (ev == NULL) { + if (handler) { + errno = EINVAL; + return -1; + } + if (!bsds->writeable_handler) { + return 0; + } + bsds->writeable_handler = NULL; + bsds->writeable_private = NULL; + TEVENT_FD_NOT_WRITEABLE(bsds->fde); + + return 0; + } + + /* read and write must use the same tevent_context */ + if (bsds->event_ptr != ev) { + if (bsds->readable_handler || bsds->writeable_handler) { + errno = EINVAL; + return -1; + } + bsds->event_ptr = NULL; + TALLOC_FREE(bsds->fde); + } + + if (tevent_fd_get_flags(bsds->fde) == 0) { + TALLOC_FREE(bsds->fde); + + bsds->fde = tevent_add_fd(ev, bsds, + bsds->fd, TEVENT_FD_WRITE, + tdgram_bsd_fde_handler, + bsds); + if (!bsds->fde) { + errno = ENOMEM; + return -1; + } + + /* cache the event context we're running on */ + bsds->event_ptr = ev; + } else if (!bsds->writeable_handler) { + TEVENT_FD_WRITEABLE(bsds->fde); + } + + bsds->writeable_handler = handler; + bsds->writeable_private = private_data; + + return 0; +} + +struct tdgram_bsd_recvfrom_state { + struct tdgram_context *dgram; + + uint8_t *buf; + size_t len; + struct tsocket_address *src; +}; + +static int tdgram_bsd_recvfrom_destructor(struct tdgram_bsd_recvfrom_state *state) +{ + struct tdgram_bsd *bsds = tdgram_context_data(state->dgram, + struct tdgram_bsd); + + tdgram_bsd_set_readable_handler(bsds, NULL, NULL, NULL); + + return 0; +} + +static void tdgram_bsd_recvfrom_handler(void *private_data); + +static struct tevent_req *tdgram_bsd_recvfrom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram) +{ + struct tevent_req *req; + struct tdgram_bsd_recvfrom_state *state; + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_bsd_recvfrom_state); + if (!req) { + return NULL; + } + + state->dgram = dgram; + state->buf = NULL; + state->len = 0; + state->src = NULL; + + talloc_set_destructor(state, tdgram_bsd_recvfrom_destructor); + + if (bsds->fd == -1) { + tevent_req_error(req, ENOTCONN); + goto post; + } + + /* + * this is a fast path, not waiting for the + * socket to become explicit readable gains + * about 10%-20% performance in benchmark tests. + */ + tdgram_bsd_recvfrom_handler(req); + if (!tevent_req_is_in_progress(req)) { + goto post; + } + + ret = tdgram_bsd_set_readable_handler(bsds, ev, + tdgram_bsd_recvfrom_handler, + req); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; + } + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tdgram_bsd_recvfrom_handler(void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort(private_data, + struct tevent_req); + struct tdgram_bsd_recvfrom_state *state = tevent_req_data(req, + struct tdgram_bsd_recvfrom_state); + struct tdgram_context *dgram = state->dgram; + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct tsocket_address_bsd *bsda; + ssize_t ret; + int err; + bool retry; + + ret = tsocket_bsd_pending(bsds->fd); + if (ret == 0) { + /* retry later */ + return; + } + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; + } + + state->buf = talloc_array(state, uint8_t, ret); + if (tevent_req_nomem(state->buf, req)) { + return; + } + state->len = ret; + + state->src = tsocket_address_create(state, + &tsocket_address_bsd_ops, + &bsda, + struct tsocket_address_bsd, + __location__ "bsd_recvfrom"); + if (tevent_req_nomem(state->src, req)) { + return; + } + + ZERO_STRUCTP(bsda); + bsda->sa_socklen = sizeof(bsda->u.ss); + + ret = recvfrom(bsds->fd, state->buf, state->len, 0, + &bsda->u.sa, &bsda->sa_socklen); + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; + } + + /* + * Some systems (FreeBSD, see bug #7115) return too much + * bytes in tsocket_bsd_pending()/ioctl(fd, FIONREAD, ...), + * the return value includes some IP/UDP header bytes, + * while recvfrom() just returns the payload. + */ + state->buf = talloc_realloc(state, state->buf, uint8_t, ret); + if (tevent_req_nomem(state->buf, req)) { + return; + } + state->len = ret; + + tevent_req_done(req); +} + +static ssize_t tdgram_bsd_recvfrom_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + uint8_t **buf, + struct tsocket_address **src) +{ + struct tdgram_bsd_recvfrom_state *state = tevent_req_data(req, + struct tdgram_bsd_recvfrom_state); + ssize_t ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + *buf = talloc_move(mem_ctx, &state->buf); + ret = state->len; + if (src) { + *src = talloc_move(mem_ctx, &state->src); + } + } + + tevent_req_received(req); + return ret; +} + +struct tdgram_bsd_sendto_state { + struct tdgram_context *dgram; + + const uint8_t *buf; + size_t len; + const struct tsocket_address *dst; + + ssize_t ret; +}; + +static int tdgram_bsd_sendto_destructor(struct tdgram_bsd_sendto_state *state) +{ + struct tdgram_bsd *bsds = tdgram_context_data(state->dgram, + struct tdgram_bsd); + + tdgram_bsd_set_writeable_handler(bsds, NULL, NULL, NULL); + + return 0; +} + +static void tdgram_bsd_sendto_handler(void *private_data); + +static struct tevent_req *tdgram_bsd_sendto_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + const uint8_t *buf, + size_t len, + const struct tsocket_address *dst) +{ + struct tevent_req *req; + struct tdgram_bsd_sendto_state *state; + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_bsd_sendto_state); + if (!req) { + return NULL; + } + + state->dgram = dgram; + state->buf = buf; + state->len = len; + state->dst = dst; + state->ret = -1; + + talloc_set_destructor(state, tdgram_bsd_sendto_destructor); + + if (bsds->fd == -1) { + tevent_req_error(req, ENOTCONN); + goto post; + } + + /* + * this is a fast path, not waiting for the + * socket to become explicit writeable gains + * about 10%-20% performance in benchmark tests. + */ + tdgram_bsd_sendto_handler(req); + if (!tevent_req_is_in_progress(req)) { + goto post; + } + + ret = tdgram_bsd_set_writeable_handler(bsds, ev, + tdgram_bsd_sendto_handler, + req); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; + } + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tdgram_bsd_sendto_handler(void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort(private_data, + struct tevent_req); + struct tdgram_bsd_sendto_state *state = tevent_req_data(req, + struct tdgram_bsd_sendto_state); + struct tdgram_context *dgram = state->dgram; + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct sockaddr *sa = NULL; + socklen_t sa_socklen = 0; + ssize_t ret; + int err; + bool retry; + + if (state->dst) { + struct tsocket_address_bsd *bsda = + talloc_get_type(state->dst->private_data, + struct tsocket_address_bsd); + + sa = &bsda->u.sa; + sa_socklen = bsda->sa_socklen; + } + + ret = sendto(bsds->fd, state->buf, state->len, 0, sa, sa_socklen); + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; + } + + state->ret = ret; + + tevent_req_done(req); +} + +static ssize_t tdgram_bsd_sendto_recv(struct tevent_req *req, int *perrno) +{ + struct tdgram_bsd_sendto_state *state = tevent_req_data(req, + struct tdgram_bsd_sendto_state); + ssize_t ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + +struct tdgram_bsd_disconnect_state { + uint8_t __dummy; +}; + +static struct tevent_req *tdgram_bsd_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram) +{ + struct tdgram_bsd *bsds = tdgram_context_data(dgram, struct tdgram_bsd); + struct tevent_req *req; + struct tdgram_bsd_disconnect_state *state; + int ret; + int err; + bool dummy; + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_bsd_disconnect_state); + if (req == NULL) { + return NULL; + } + + if (bsds->fd == -1) { + tevent_req_error(req, ENOTCONN); + goto post; + } + + ret = close(bsds->fd); + bsds->fd = -1; + err = tsocket_bsd_error_from_errno(ret, errno, &dummy); + if (tevent_req_error(req, err)) { + goto post; + } + + tevent_req_done(req); +post: + tevent_req_post(req, ev); + return req; +} + +static int tdgram_bsd_disconnect_recv(struct tevent_req *req, + int *perrno) +{ + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + + tevent_req_received(req); + return ret; +} + +static const struct tdgram_context_ops tdgram_bsd_ops = { + .name = "bsd", + + .recvfrom_send = tdgram_bsd_recvfrom_send, + .recvfrom_recv = tdgram_bsd_recvfrom_recv, + + .sendto_send = tdgram_bsd_sendto_send, + .sendto_recv = tdgram_bsd_sendto_recv, + + .disconnect_send = tdgram_bsd_disconnect_send, + .disconnect_recv = tdgram_bsd_disconnect_recv, +}; + +static int tdgram_bsd_destructor(struct tdgram_bsd *bsds) +{ + TALLOC_FREE(bsds->fde); + if (bsds->fd != -1) { + close(bsds->fd); + bsds->fd = -1; + } + return 0; +} + +static int tdgram_bsd_dgram_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + bool broadcast, + TALLOC_CTX *mem_ctx, + struct tdgram_context **_dgram, + const char *location) +{ + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + struct tsocket_address_bsd *rbsda = NULL; + struct tdgram_context *dgram; + struct tdgram_bsd *bsds; + int fd; + int ret; + bool do_bind = false; + bool do_reuseaddr = false; + bool do_ipv6only = false; + bool is_inet = false; + int sa_fam = lbsda->u.sa.sa_family; + + if (remote) { + rbsda = talloc_get_type_abort(remote->private_data, + struct tsocket_address_bsd); + } + + switch (lbsda->u.sa.sa_family) { + case AF_UNIX: + if (broadcast) { + errno = EINVAL; + return -1; + } + if (lbsda->u.un.sun_path[0] != 0) { + do_reuseaddr = true; + do_bind = true; + } + break; + case AF_INET: + if (lbsda->u.in.sin_port != 0) { + do_reuseaddr = true; + do_bind = true; + } + if (lbsda->u.in.sin_addr.s_addr != INADDR_ANY) { + do_bind = true; + } + is_inet = true; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + if (lbsda->u.in6.sin6_port != 0) { + do_reuseaddr = true; + do_bind = true; + } + if (memcmp(&in6addr_any, + &lbsda->u.in6.sin6_addr, + sizeof(in6addr_any)) != 0) { + do_bind = true; + } + is_inet = true; + do_ipv6only = true; + break; +#endif + default: + errno = EINVAL; + return -1; + } + + if (!do_bind && is_inet && rbsda) { + sa_fam = rbsda->u.sa.sa_family; + switch (sa_fam) { + case AF_INET: + do_ipv6only = false; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + do_ipv6only = true; + break; +#endif + } + } + + fd = socket(sa_fam, SOCK_DGRAM, 0); + if (fd < 0) { + return fd; + } + + fd = tsocket_bsd_common_prepare_fd(fd, true); + if (fd < 0) { + return fd; + } + + dgram = tdgram_context_create(mem_ctx, + &tdgram_bsd_ops, + &bsds, + struct tdgram_bsd, + location); + if (!dgram) { + int saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; + } + ZERO_STRUCTP(bsds); + bsds->fd = fd; + talloc_set_destructor(bsds, tdgram_bsd_destructor); + +#ifdef HAVE_IPV6 + if (do_ipv6only) { + int val = 1; + + ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *)&val, sizeof(val)); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return ret; + } + } +#endif + + if (broadcast) { + int val = 1; + + ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, + (const void *)&val, sizeof(val)); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return ret; + } + } + + if (do_reuseaddr) { + int val = 1; + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const void *)&val, sizeof(val)); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return ret; + } + } + + if (do_bind) { + ret = bind(fd, &lbsda->u.sa, lbsda->sa_socklen); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return ret; + } + } + + if (rbsda) { + if (rbsda->u.sa.sa_family != sa_fam) { + talloc_free(dgram); + errno = EINVAL; + return -1; + } + + ret = connect(fd, &rbsda->u.sa, rbsda->sa_socklen); + if (ret == -1) { + int saved_errno = errno; + talloc_free(dgram); + errno = saved_errno; + return ret; + } + } + + *_dgram = dgram; + return 0; +} + +int _tdgram_inet_udp_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram, + const char *location) +{ + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + int ret; + + switch (lbsda->u.sa.sa_family) { + case AF_INET: + break; +#ifdef HAVE_IPV6 + case AF_INET6: + break; +#endif + default: + errno = EINVAL; + return -1; + } + + ret = tdgram_bsd_dgram_socket(local, remote, false, + mem_ctx, dgram, location); + + return ret; +} + +int _tdgram_unix_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram, + const char *location) +{ + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + int ret; + + switch (lbsda->u.sa.sa_family) { + case AF_UNIX: + break; + default: + errno = EINVAL; + return -1; + } + + ret = tdgram_bsd_dgram_socket(local, remote, false, + mem_ctx, dgram, location); + + return ret; +} + +struct tstream_bsd { + int fd; + + void *event_ptr; + struct tevent_fd *fde; + + void *readable_private; + void (*readable_handler)(void *private_data); + void *writeable_private; + void (*writeable_handler)(void *private_data); +}; + +static void tstream_bsd_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct tstream_bsd *bsds = talloc_get_type_abort(private_data, + struct tstream_bsd); + + if (flags & TEVENT_FD_WRITE) { + bsds->writeable_handler(bsds->writeable_private); + return; + } + if (flags & TEVENT_FD_READ) { + if (!bsds->readable_handler) { + if (bsds->writeable_handler) { + bsds->writeable_handler(bsds->writeable_private); + return; + } + TEVENT_FD_NOT_READABLE(bsds->fde); + return; + } + bsds->readable_handler(bsds->readable_private); + return; + } +} + +static int tstream_bsd_set_readable_handler(struct tstream_bsd *bsds, + struct tevent_context *ev, + void (*handler)(void *private_data), + void *private_data) +{ + if (ev == NULL) { + if (handler) { + errno = EINVAL; + return -1; + } + if (!bsds->readable_handler) { + return 0; + } + bsds->readable_handler = NULL; + bsds->readable_private = NULL; + + return 0; + } + + /* read and write must use the same tevent_context */ + if (bsds->event_ptr != ev) { + if (bsds->readable_handler || bsds->writeable_handler) { + errno = EINVAL; + return -1; + } + bsds->event_ptr = NULL; + TALLOC_FREE(bsds->fde); + } + + if (tevent_fd_get_flags(bsds->fde) == 0) { + TALLOC_FREE(bsds->fde); + + bsds->fde = tevent_add_fd(ev, bsds, + bsds->fd, TEVENT_FD_READ, + tstream_bsd_fde_handler, + bsds); + if (!bsds->fde) { + errno = ENOMEM; + return -1; + } + + /* cache the event context we're running on */ + bsds->event_ptr = ev; + } else if (!bsds->readable_handler) { + TEVENT_FD_READABLE(bsds->fde); + } + + bsds->readable_handler = handler; + bsds->readable_private = private_data; + + return 0; +} + +static int tstream_bsd_set_writeable_handler(struct tstream_bsd *bsds, + struct tevent_context *ev, + void (*handler)(void *private_data), + void *private_data) +{ + if (ev == NULL) { + if (handler) { + errno = EINVAL; + return -1; + } + if (!bsds->writeable_handler) { + return 0; + } + bsds->writeable_handler = NULL; + bsds->writeable_private = NULL; + TEVENT_FD_NOT_WRITEABLE(bsds->fde); + + return 0; + } + + /* read and write must use the same tevent_context */ + if (bsds->event_ptr != ev) { + if (bsds->readable_handler || bsds->writeable_handler) { + errno = EINVAL; + return -1; + } + bsds->event_ptr = NULL; + TALLOC_FREE(bsds->fde); + } + + if (tevent_fd_get_flags(bsds->fde) == 0) { + TALLOC_FREE(bsds->fde); + + bsds->fde = tevent_add_fd(ev, bsds, + bsds->fd, + TEVENT_FD_READ | TEVENT_FD_WRITE, + tstream_bsd_fde_handler, + bsds); + if (!bsds->fde) { + errno = ENOMEM; + return -1; + } + + /* cache the event context we're running on */ + bsds->event_ptr = ev; + } else if (!bsds->writeable_handler) { + uint16_t flags = tevent_fd_get_flags(bsds->fde); + flags |= TEVENT_FD_READ | TEVENT_FD_WRITE; + tevent_fd_set_flags(bsds->fde, flags); + } + + bsds->writeable_handler = handler; + bsds->writeable_private = private_data; + + return 0; +} + +static ssize_t tstream_bsd_pending_bytes(struct tstream_context *stream) +{ + struct tstream_bsd *bsds = tstream_context_data(stream, + struct tstream_bsd); + ssize_t ret; + + if (bsds->fd == -1) { + errno = ENOTCONN; + return -1; + } + + ret = tsocket_bsd_pending(bsds->fd); + + return ret; +} + +struct tstream_bsd_readv_state { + struct tstream_context *stream; + + struct iovec *vector; + size_t count; + + int ret; +}; + +static int tstream_bsd_readv_destructor(struct tstream_bsd_readv_state *state) +{ + struct tstream_bsd *bsds = tstream_context_data(state->stream, + struct tstream_bsd); + + tstream_bsd_set_readable_handler(bsds, NULL, NULL, NULL); + + return 0; +} + +static void tstream_bsd_readv_handler(void *private_data); + +static struct tevent_req *tstream_bsd_readv_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct iovec *vector, + size_t count) +{ + struct tevent_req *req; + struct tstream_bsd_readv_state *state; + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_bsd_readv_state); + if (!req) { + return NULL; + } + + state->stream = stream; + /* we make a copy of the vector so that we can modify it */ + state->vector = talloc_array(state, struct iovec, count); + if (tevent_req_nomem(state->vector, req)) { + goto post; + } + memcpy(state->vector, vector, sizeof(struct iovec)*count); + state->count = count; + state->ret = 0; + + talloc_set_destructor(state, tstream_bsd_readv_destructor); + + if (bsds->fd == -1) { + tevent_req_error(req, ENOTCONN); + goto post; + } + + /* + * this is a fast path, not waiting for the + * socket to become explicit readable gains + * about 10%-20% performance in benchmark tests. + */ + tstream_bsd_readv_handler(req); + if (!tevent_req_is_in_progress(req)) { + goto post; + } + + ret = tstream_bsd_set_readable_handler(bsds, ev, + tstream_bsd_readv_handler, + req); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; + } + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tstream_bsd_readv_handler(void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort(private_data, + struct tevent_req); + struct tstream_bsd_readv_state *state = tevent_req_data(req, + struct tstream_bsd_readv_state); + struct tstream_context *stream = state->stream; + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); + int ret; + int err; + bool retry; + + ret = readv(bsds->fd, state->vector, state->count); + if (ret == 0) { + /* propagate end of file */ + tevent_req_error(req, EPIPE); + return; + } + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; + } + + state->ret += ret; + + while (ret > 0) { + if (ret < state->vector[0].iov_len) { + uint8_t *base; + base = (uint8_t *)state->vector[0].iov_base; + base += ret; + state->vector[0].iov_base = base; + state->vector[0].iov_len -= ret; + break; + } + ret -= state->vector[0].iov_len; + state->vector += 1; + state->count -= 1; + } + + /* + * there're maybe some empty vectors at the end + * which we need to skip, otherwise we would get + * ret == 0 from the readv() call and return EPIPE + */ + while (state->count > 0) { + if (state->vector[0].iov_len > 0) { + break; + } + state->vector += 1; + state->count -= 1; + } + + if (state->count > 0) { + /* we have more to read */ + return; + } + + tevent_req_done(req); +} + +static int tstream_bsd_readv_recv(struct tevent_req *req, + int *perrno) +{ + struct tstream_bsd_readv_state *state = tevent_req_data(req, + struct tstream_bsd_readv_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + +struct tstream_bsd_writev_state { + struct tstream_context *stream; + + struct iovec *vector; + size_t count; + + int ret; +}; + +static int tstream_bsd_writev_destructor(struct tstream_bsd_writev_state *state) +{ + struct tstream_bsd *bsds = tstream_context_data(state->stream, + struct tstream_bsd); + + tstream_bsd_set_writeable_handler(bsds, NULL, NULL, NULL); + + return 0; +} + +static void tstream_bsd_writev_handler(void *private_data); + +static struct tevent_req *tstream_bsd_writev_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + const struct iovec *vector, + size_t count) +{ + struct tevent_req *req; + struct tstream_bsd_writev_state *state; + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_bsd_writev_state); + if (!req) { + return NULL; + } + + state->stream = stream; + /* we make a copy of the vector so that we can modify it */ + state->vector = talloc_array(state, struct iovec, count); + if (tevent_req_nomem(state->vector, req)) { + goto post; + } + memcpy(state->vector, vector, sizeof(struct iovec)*count); + state->count = count; + state->ret = 0; + + talloc_set_destructor(state, tstream_bsd_writev_destructor); + + if (bsds->fd == -1) { + tevent_req_error(req, ENOTCONN); + goto post; + } + + /* + * this is a fast path, not waiting for the + * socket to become explicit writeable gains + * about 10%-20% performance in benchmark tests. + */ + tstream_bsd_writev_handler(req); + if (!tevent_req_is_in_progress(req)) { + goto post; + } + + ret = tstream_bsd_set_writeable_handler(bsds, ev, + tstream_bsd_writev_handler, + req); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; + } + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tstream_bsd_writev_handler(void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort(private_data, + struct tevent_req); + struct tstream_bsd_writev_state *state = tevent_req_data(req, + struct tstream_bsd_writev_state); + struct tstream_context *stream = state->stream; + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); + ssize_t ret; + int err; + bool retry; + + ret = writev(bsds->fd, state->vector, state->count); + if (ret == 0) { + /* propagate end of file */ + tevent_req_error(req, EPIPE); + return; + } + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; + } + + state->ret += ret; + + while (ret > 0) { + if (ret < state->vector[0].iov_len) { + uint8_t *base; + base = (uint8_t *)state->vector[0].iov_base; + base += ret; + state->vector[0].iov_base = base; + state->vector[0].iov_len -= ret; + break; + } + ret -= state->vector[0].iov_len; + state->vector += 1; + state->count -= 1; + } + + /* + * there're maybe some empty vectors at the end + * which we need to skip, otherwise we would get + * ret == 0 from the writev() call and return EPIPE + */ + while (state->count > 0) { + if (state->vector[0].iov_len > 0) { + break; + } + state->vector += 1; + state->count -= 1; + } + + if (state->count > 0) { + /* we have more to read */ + return; + } + + tevent_req_done(req); +} + +static int tstream_bsd_writev_recv(struct tevent_req *req, int *perrno) +{ + struct tstream_bsd_writev_state *state = tevent_req_data(req, + struct tstream_bsd_writev_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + +struct tstream_bsd_disconnect_state { + void *__dummy; +}; + +static struct tevent_req *tstream_bsd_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream) +{ + struct tstream_bsd *bsds = tstream_context_data(stream, struct tstream_bsd); + struct tevent_req *req; + struct tstream_bsd_disconnect_state *state; + int ret; + int err; + bool dummy; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_bsd_disconnect_state); + if (req == NULL) { + return NULL; + } + + if (bsds->fd == -1) { + tevent_req_error(req, ENOTCONN); + goto post; + } + + ret = close(bsds->fd); + bsds->fd = -1; + err = tsocket_bsd_error_from_errno(ret, errno, &dummy); + if (tevent_req_error(req, err)) { + goto post; + } + + tevent_req_done(req); +post: + tevent_req_post(req, ev); + return req; +} + +static int tstream_bsd_disconnect_recv(struct tevent_req *req, + int *perrno) +{ + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + + tevent_req_received(req); + return ret; +} + +static const struct tstream_context_ops tstream_bsd_ops = { + .name = "bsd", + + .pending_bytes = tstream_bsd_pending_bytes, + + .readv_send = tstream_bsd_readv_send, + .readv_recv = tstream_bsd_readv_recv, + + .writev_send = tstream_bsd_writev_send, + .writev_recv = tstream_bsd_writev_recv, + + .disconnect_send = tstream_bsd_disconnect_send, + .disconnect_recv = tstream_bsd_disconnect_recv, +}; + +static int tstream_bsd_destructor(struct tstream_bsd *bsds) +{ + TALLOC_FREE(bsds->fde); + if (bsds->fd != -1) { + close(bsds->fd); + bsds->fd = -1; + } + return 0; +} + +int _tstream_bsd_existing_socket(TALLOC_CTX *mem_ctx, + int fd, + struct tstream_context **_stream, + const char *location) +{ + struct tstream_context *stream; + struct tstream_bsd *bsds; + + stream = tstream_context_create(mem_ctx, + &tstream_bsd_ops, + &bsds, + struct tstream_bsd, + location); + if (!stream) { + return -1; + } + ZERO_STRUCTP(bsds); + bsds->fd = fd; + talloc_set_destructor(bsds, tstream_bsd_destructor); + + *_stream = stream; + return 0; +} + +struct tstream_bsd_connect_state { + int fd; + struct tevent_fd *fde; + struct tstream_conext *stream; +}; + +static int tstream_bsd_connect_destructor(struct tstream_bsd_connect_state *state) +{ + TALLOC_FREE(state->fde); + if (state->fd != -1) { + close(state->fd); + state->fd = -1; + } + + return 0; +} + +static void tstream_bsd_connect_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data); + +static struct tevent_req * tstream_bsd_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int sys_errno, + const struct tsocket_address *local, + const struct tsocket_address *remote) +{ + struct tevent_req *req; + struct tstream_bsd_connect_state *state; + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + struct tsocket_address_bsd *rbsda = + talloc_get_type_abort(remote->private_data, + struct tsocket_address_bsd); + int ret; + int err; + bool retry; + bool do_bind = false; + bool do_reuseaddr = false; + bool do_ipv6only = false; + bool is_inet = false; + int sa_fam = lbsda->u.sa.sa_family; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_bsd_connect_state); + if (!req) { + return NULL; + } + state->fd = -1; + state->fde = NULL; + + talloc_set_destructor(state, tstream_bsd_connect_destructor); + + /* give the wrappers a chance to report an error */ + if (sys_errno != 0) { + tevent_req_error(req, sys_errno); + goto post; + } + + switch (lbsda->u.sa.sa_family) { + case AF_UNIX: + if (lbsda->u.un.sun_path[0] != 0) { + do_reuseaddr = true; + do_bind = true; + } + break; + case AF_INET: + if (lbsda->u.in.sin_port != 0) { + do_reuseaddr = true; + do_bind = true; + } + if (lbsda->u.in.sin_addr.s_addr != INADDR_ANY) { + do_bind = true; + } + is_inet = true; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + if (lbsda->u.in6.sin6_port != 0) { + do_reuseaddr = true; + do_bind = true; + } + if (memcmp(&in6addr_any, + &lbsda->u.in6.sin6_addr, + sizeof(in6addr_any)) != 0) { + do_bind = true; + } + is_inet = true; + do_ipv6only = true; + break; +#endif + default: + tevent_req_error(req, EINVAL); + goto post; + } + + if (!do_bind && is_inet) { + sa_fam = rbsda->u.sa.sa_family; + switch (sa_fam) { + case AF_INET: + do_ipv6only = false; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + do_ipv6only = true; + break; +#endif + } + } + + state->fd = socket(sa_fam, SOCK_STREAM, 0); + if (state->fd == -1) { + tevent_req_error(req, errno); + goto post; + } + + state->fd = tsocket_bsd_common_prepare_fd(state->fd, true); + if (state->fd == -1) { + tevent_req_error(req, errno); + goto post; + } + +#ifdef HAVE_IPV6 + if (do_ipv6only) { + int val = 1; + + ret = setsockopt(state->fd, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *)&val, sizeof(val)); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; + } + } +#endif + + if (do_reuseaddr) { + int val = 1; + + ret = setsockopt(state->fd, SOL_SOCKET, SO_REUSEADDR, + (const void *)&val, sizeof(val)); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; + } + } + + if (do_bind) { + ret = bind(state->fd, &lbsda->u.sa, lbsda->sa_socklen); + if (ret == -1) { + tevent_req_error(req, errno); + goto post; + } + } + + if (rbsda->u.sa.sa_family != sa_fam) { + tevent_req_error(req, EINVAL); + goto post; + } + + ret = connect(state->fd, &rbsda->u.sa, rbsda->sa_socklen); + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + goto async; + } + if (tevent_req_error(req, err)) { + goto post; + } + + tevent_req_done(req); + goto post; + + async: + state->fde = tevent_add_fd(ev, state, + state->fd, + TEVENT_FD_READ | TEVENT_FD_WRITE, + tstream_bsd_connect_fde_handler, + req); + if (tevent_req_nomem(state->fde, req)) { + goto post; + } + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tstream_bsd_connect_fde_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort(private_data, + struct tevent_req); + struct tstream_bsd_connect_state *state = tevent_req_data(req, + struct tstream_bsd_connect_state); + int ret; + int error=0; + socklen_t len = sizeof(error); + int err; + bool retry; + + ret = getsockopt(state->fd, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret == 0) { + if (error != 0) { + errno = error; + ret = -1; + } + } + err = tsocket_bsd_error_from_errno(ret, errno, &retry); + if (retry) { + /* retry later */ + return; + } + if (tevent_req_error(req, err)) { + return; + } + + tevent_req_done(req); +} + +static int tstream_bsd_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream, + const char *location) +{ + struct tstream_bsd_connect_state *state = tevent_req_data(req, + struct tstream_bsd_connect_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = _tstream_bsd_existing_socket(mem_ctx, + state->fd, + stream, + location); + if (ret == -1) { + *perrno = errno; + goto done; + } + TALLOC_FREE(state->fde); + state->fd = -1; + } + +done: + tevent_req_received(req); + return ret; +} + +struct tevent_req * tstream_inet_tcp_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct tsocket_address *local, + const struct tsocket_address *remote) +{ + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + struct tevent_req *req; + int sys_errno = 0; + + switch (lbsda->u.sa.sa_family) { + case AF_INET: + break; +#ifdef HAVE_IPV6 + case AF_INET6: + break; +#endif + default: + sys_errno = EINVAL; + break; + } + + req = tstream_bsd_connect_send(mem_ctx, ev, sys_errno, local, remote); + + return req; +} + +int _tstream_inet_tcp_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream, + const char *location) +{ + return tstream_bsd_connect_recv(req, perrno, mem_ctx, stream, location); +} + +struct tevent_req * tstream_unix_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct tsocket_address *local, + const struct tsocket_address *remote) +{ + struct tsocket_address_bsd *lbsda = + talloc_get_type_abort(local->private_data, + struct tsocket_address_bsd); + struct tevent_req *req; + int sys_errno = 0; + + switch (lbsda->u.sa.sa_family) { + case AF_UNIX: + break; + default: + sys_errno = EINVAL; + break; + } + + req = tstream_bsd_connect_send(mem_ctx, ev, sys_errno, local, remote); + + return req; +} + +int _tstream_unix_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream, + const char *location) +{ + return tstream_bsd_connect_recv(req, perrno, mem_ctx, stream, location); +} + +int _tstream_unix_socketpair(TALLOC_CTX *mem_ctx1, + struct tstream_context **_stream1, + TALLOC_CTX *mem_ctx2, + struct tstream_context **_stream2, + const char *location) +{ + int ret; + int fds[2]; + int fd1; + int fd2; + struct tstream_context *stream1 = NULL; + struct tstream_context *stream2 = NULL; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + if (ret == -1) { + return -1; + } + fd1 = fds[0]; + fd2 = fds[1]; + + fd1 = tsocket_bsd_common_prepare_fd(fd1, true); + if (fd1 == -1) { + int sys_errno = errno; + close(fd2); + errno = sys_errno; + return -1; + } + + fd2 = tsocket_bsd_common_prepare_fd(fd2, true); + if (fd2 == -1) { + int sys_errno = errno; + close(fd1); + errno = sys_errno; + return -1; + } + + ret = _tstream_bsd_existing_socket(mem_ctx1, + fd1, + &stream1, + location); + if (ret == -1) { + int sys_errno = errno; + close(fd1); + close(fd2); + errno = sys_errno; + return -1; + } + + ret = _tstream_bsd_existing_socket(mem_ctx2, + fd2, + &stream2, + location); + if (ret == -1) { + int sys_errno = errno; + talloc_free(stream1); + close(fd2); + errno = sys_errno; + return -1; + } + + *_stream1 = stream1; + *_stream2 = stream2; + return 0; +} + diff --git a/lib/tsocket/tsocket_guide.txt b/lib/tsocket/tsocket_guide.txt new file mode 100644 index 0000000000..dfe2dd44e1 --- /dev/null +++ b/lib/tsocket/tsocket_guide.txt @@ -0,0 +1,484 @@ + +Basic design of the tsocket abstraction +======================================= + +The tsocket abstraction is splitted into two +different kinds of communitation interfaces. + +There's the "tstream_context" interface with abstracts +the communication through a bidirectional +byte stream between two endpoints. + +And there's the "tdgram_context" interface +with abstracts datagram based communication between any +number of endpoints. + +Both interfaces share the "tsocket_address" abstraction +for endpoint addresses. + +The whole library is based on the talloc(3) and 'tevent' libraries +and provides "tevent_req" based "foo_send()"/"foo_recv()" functions pairs +for all abstracted methods that need to be async. + +The tsocket_address abstraction +=============================== + +The tsocket_address represents an socket endpoint genericly. +As it's like an abstract class it has no specific constructor. +The specific constructors are descripted later sections. + +There's a function get the string representation of the +endpoint for debugging. Callers should not try to parse +the string! The should use additional methods of the specific +tsocket_address implemention to get more details. + + char *tsocket_address_string(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); + +There's a function to create a copy of the tsocket_address. +This is useful when before doing modifications to a socket +via additional methods of the specific tsocket_address implementation. + + struct tsocket_address *tsocket_address_copy(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); + +The tdgram_context abstraction +============================== + +The tdgram_context is like an abstract class for datagram +based sockets. The interface provides async 'tevent_req' based +functions on top functionality is similar to the +recvfrom(2)/sendto(2)/close(2) syscalls. + +The tdgram_recvfrom_send() method can be called to ask for the +next available datagram on the abstracted tdgram_context. +It returns a 'tevent_req' handle, where the caller can register +a callback with tevent_req_set_callback(). The callback is triggered +when a datagram is available or an error happened. + +The callback is then supposed to get the result by calling +tdgram_recvfrom_recv() on the 'tevent_req'. It returns -1 +and sets '*perrno' to the actual 'errno' on failure. +Otherwise it returns the length of the datagram +(0 is never returned!). '*buf' will contain the buffer of the +datagram and '*src' the abstracted tsocket_address of the sender +of the received datagram. + +The caller can only have one outstanding tdgram_recvfrom_send() +at a time otherwise the caller will get '*perrno = EBUSY'. + + struct tevent_req *tdgram_recvfrom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram); + + ssize_t tdgram_recvfrom_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + uint8_t **buf, + struct tsocket_address **src); + +The tdgram_sendto_send() method can be called to send a +datagram (specified by a buf/len) to a destination endpoint +(specified by dst). It's not allowed for len to be 0. +It returns a 'tevent_req' handle, where the caller can register a +callback with tevent_req_set_callback(). The callback is triggered +when the specific implementation (thinks it) +has delivered the datagram to the "wire". + +The callback is then supposed to get the result by calling +tdgram_sendto_recv() on the 'tevent_req'. It returns -1 +and sets '*perrno' to the actual 'errno' on failure. +Otherwise it returns the length of the datagram +(0 is never returned!). + +The caller can only have one outstanding tdgram_sendto_send() +at a time otherwise the caller will get '*perrno = EBUSY'. + + struct tevent_req *tdgram_sendto_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + const uint8_t *buf, size_t len, + const struct tsocket_address *dst); + + ssize_t tdgram_sendto_recv(struct tevent_req *req, + int *perrno); + +The tdgram_disconnect_send() method should be used to normally +shutdown/close the abstracted socket. + +The caller should make sure there're no outstanding tdgram_recvfrom_send() +and tdgram_sendto_send() calls otherwise the caller will get '*perrno = EBUSY'. + +Note: you can always use talloc_free(tdgram) to cleanup the resources +of the tdgram_context on a fatal error. + + struct tevent_req *tdgram_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram); + + int tdgram_disconnect_recv(struct tevent_req *req, + int *perrno); + +The tstream_context abstraction +=============================== + +The tstream_context is like an abstract class for stream +based sockets. The interface provides async 'tevent_req' based +functions on top functionality is similar to the +readv(2)/writev(2)/close(2) syscalls. + +The tstream_pending_bytes() function is able to report +how much bytes of the incoming stream have arrived +but not consumed yet. It returns -1 and sets 'errno' on failure. +Otherwise it returns the number of uncomsumed bytes +(it can return 0!). + + ssize_t tstream_pending_bytes(struct tstream_context *stream); + +The tstream_readv_send() method can be called to read for a +specific amount of bytes from the stream into the buffers +of the given iovec vector. The caller has to preallocate the buffers +in the iovec vector. The caller might need to use +tstream_pending_bytes() if the protocol doesn't have a fixed pdu header +containing the pdu size. tstream_readv_send() returns a 'tevent_req' handle, +where the caller can register a callback with tevent_req_set_callback(). +The callback is triggered when all iovec buffers are completely +filled with bytes from the socket or an error happened. + +The callback is then supposed to get the result by calling +tstream_readv_recv() on the 'tevent_req'. It returns -1 +and sets '*perrno' to the actual 'errno' on failure. +Otherwise it returns the length of the datagram +(0 is never returned!). + +The caller can only have one outstanding tstream_readv_send() +at a time otherwise the caller will get *perrno = EBUSY. + + struct tevent_req *tstream_readv_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct iovec *vector, + size_t count); + + int tstream_readv_recv(struct tevent_req *req, + int *perrno); + +The tstream_writev_send() method can be called to write +buffers in the given iovec vector into the stream socket. +It's invalid to pass an empty vector. +tstream_writev_send() returns a 'tevent_req' handle, +where the caller can register a callback with tevent_req_set_callback(). +The callback is triggered when the specific implementation (thinks it) +has delivered the all buffers to the "wire". + +The callback is then supposed to get the result by calling +tstream_writev_recv() on the 'tevent_req'. It returns -1 +and sets '*perrno' to the actual 'errno' on failure. +Otherwise it returns the total amount of bytes sent. +(0 is never returned!). + +The caller can only have one outstanding tstream_writev_send() +at a time otherwise the caller will get '*perrno = EBUSY'. + + struct tevent_req *tstream_writev_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + const struct iovec *vector, + size_t count); + + int tstream_writev_recv(struct tevent_req *req, + int *perrno); + +The tstream_disconnect_send() method should be used to normally +shutdown/close the abstracted socket. + +The caller should make sure there're no outstanding tstream_readv_send() +and tstream_writev_send() calls otherwise the caller will get '*perrno = EBUSY'. + +Note: you can always use talloc_free(tstream) to cleanup the resources +of the tstream_context on a fatal error. + + struct tevent_req *tstream_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream); + + int tstream_disconnect_recv(struct tevent_req *req, + int *perrno); + +PDU receive helper functions +============================ + +In order to make the live easier for callers which want to implement +a function to receive a full PDU with a single async function pair, +there're some helper functions. + +The caller can use the tstream_readv_pdu_send() function +to ask for the next available PDU on the abstracted tstream_context. +The caller needs to provide a "next_vector" function and a private +state for this function. The tstream_readv_pdu engine will ask +the next_vector function for the next iovec vetor to be filled. +There's a tstream_readv_send/recv pair for each vector returned +by the next_vector function. If the next_vector function detects +it received a full pdu, it returns an empty vector. The the callback +of the tevent_req (returned by tstream_readv_pdu_send()) is triggered. +Note: the buffer allocation is completely up to the next_vector function +and it's private state. + +See the 'dcerpc_read_ncacn_packet_send/recv' functions in Samba as an +example. + + typedef int (*tstream_readv_pdu_next_vector_t)(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **vector, + size_t *count); + + struct tevent_req *tstream_readv_pdu_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + tstream_readv_pdu_next_vector_t next_vector_fn, + void *next_vector_private); + + int tstream_readv_pdu_recv(struct tevent_req *req, int *perrno); + +Async 'tevent_queue' based helper functions +=========================================== + +There're some cases where the caller wants doesn't care about the +order of doing IO on the abstracted sockets. +(Remember at the low level there's always only one IO in a specific + direction allowed, only one tdgram_sendto_send() at a time). + +There're some helpers using 'tevent_queue' to make it easier +for callers. The functions just get a 'queue' argument +and serialize the operations. + + struct tevent_req *tdgram_sendto_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + struct tevent_queue *queue, + const uint8_t *buf, + size_t len, + struct tsocket_address *dst); + + ssize_t tdgram_sendto_queue_recv(struct tevent_req *req, int *perrno); + + struct tevent_req *tstream_readv_pdu_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct tevent_queue *queue, + tstream_readv_pdu_next_vector_t next_vector_fn, + void *next_vector_private); + + int tstream_readv_pdu_queue_recv(struct tevent_req *req, int *perrno); + + struct tevent_req *tstream_writev_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct tevent_queue *queue, + const struct iovec *vector, + size_t count); + + int tstream_writev_queue_recv(struct tevent_req *req, int *perrno); + +BSD sockets: ipv4, ipv6 and unix +================================ + +The main tsocket library comes with implentations +for BSD style ipv4, ipv6 and unix sockets. + +You can use the tsocket_address_inet_from_strings() +function to create a tsocket_address for ipv4 and ipv6 +endpoint addresses. "family" can be "ipv4", "ipv6" or "ip". +With "ip" is autodetects "ipv4" or "ipv6" based on the +"addr_string" string. "addr_string" must be a valid +ip address string based on the selected family +(dns names are not allowed!). But it's valid to pass NULL, +which gets mapped to "0.0.0.0" or "::". +It return -1 and set errno on error. Otherwise it returns 0. + + int tsocket_address_inet_from_strings(TALLOC_CTX *mem_ctx, + const char *family, + const char *addr_string, + uint16_t port, + struct tsocket_address **addr); + +To get the ip address string of an existing 'inet' tsocket_address +you can use the tsocket_address_inet_addr_string() function. +It will return NULL and set errno to EINVAL if the tsocket_address +doesn't represent an ipv4 or ipv6 endpoint address. + + char *tsocket_address_inet_addr_string(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); + +To get the port number of an existing 'inet' tsocket_address +you can use the tsocket_address_inet_port() function. +It will return 0 and set errno to EINVAL if the tsocket_address +doesn't represent an ipv4 or ipv6 endpoint address. + + uint16_t tsocket_address_inet_port(const struct tsocket_address *addr); + +To set the port number of an existing 'inet' tsocket_address +you can use the tsocket_address_inet_set_port() function. +It will return -1 and set errno to EINVAL if the tsocket_address +doesn't represent an ipv4 or ipv6 endpoint address. +It returns 0 on success. + + int tsocket_address_inet_set_port(struct tsocket_address *addr, + uint16_t port); + +You can use the tsocket_address_unix_from_path() +function to create a tsocket_address for unix domain +endpoint addresses. "path" is the filesystem path +(NULL will map ""). If the path is longer than +the low level kernel supports the function will +return -1 and set errno to ENAMETOOLONG. +On success it returns 0. + + int tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx, + const char *path, + struct tsocket_address **addr); + +To get the path of an 'unix' tsocket_address +you can use the tsocket_address_unix_path() function. +It will return NULL and set errno to EINVAL if the tsocket_address +doesn't represent an unix domain endpoint path. + + char *tsocket_address_unix_path(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); + +You can use tdgram_inet_udp_socket() to create a tdgram_context +for ipv4 or ipv6 UDP communication. "local_address" has to be +an 'inet' tsocket_address and it has to represent the local +endpoint. "remote_address" can be NULL or an 'inet' tsocket_address +presenting a remote endpoint. It returns -1 ans sets errno on error +and it returns 0 on success. + + int tdgram_inet_udp_socket(const struct tsocket_address *local_address, + const struct tsocket_address *remote_address, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram); + +You can use tdgram_unix_socket() to create a tdgram_context +for unix domain datagram communication. "local_address" has to be +an 'unix' tsocket_address and it has to represent the local +endpoint. "remote_address" can be NULL or an 'unix' tsocket_address +presenting a remote endpoint. It returns -1 ans sets errno on error +and it returns 0 on success. + + int tdgram_unix_socket(const struct tsocket_address *local, + const struct tsocket_address *remote, + TALLOC_CTX *mem_ctx, + struct tdgram_context **dgram); + +You can use tstream_inet_tcp_connect_send to async +connect to a remote ipv4 or ipv6 TCP endpoint and create a +tstream_context for the stream based communication. "local_address" has to be +an 'inet' tsocket_address and it has to represent the local +endpoint. "remote_address" has to be an 'inet' tsocket_address +presenting a remote endpoint. It returns a 'tevent_req' handle, +where the caller can register a callback with tevent_req_set_callback(). +The callback is triggered when a socket is connected and ready for IO +or an error happened. + +The callback is then supposed to get the result by calling +tstream_inet_tcp_connect_recv() on the 'tevent_req'. It returns -1 +and sets '*perrno' to the actual 'errno' on failure. +It returns 0 on success and returns the new tstream_context +in '*stream'. + + struct tevent_req *tstream_inet_tcp_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct tsocket_address *local_address, + const struct tsocket_address *remote_address); + + int tstream_inet_tcp_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream); + +You can use tstream_unix_connect_send to async +connect to a unix domain endpoint and create a +tstream_context for the stream based communication. +"local_address" has to be an 'unix' tsocket_address and +it has to represent the local endpoint. "remote_address" +has to be an 'inet' tsocket_address presenting a remote endpoint. +It returns a 'tevent_req' handle, where the caller can register +a callback with tevent_req_set_callback(). The callback is +triggered when a socket is connected and ready for IO +or an error happened. + +The callback is then supposed to get the result by calling +tstream_unix_connect_recv() on the 'tevent_req'. It returns -1 +and sets '*perrno' to the actual 'errno' on failure. +It returns 0 on success and returns the new tstream_context +in '*stream'. + + struct tevent_req *tstream_unix_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct tsocket_address *local, + const struct tsocket_address *remote); + + int _tstream_unix_connect_recv(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + struct tstream_context **stream); + +You can use tstream_unix_socketpair to create two connected +'unix' tsocket_contexts for the stream based communication. +It returns -1 and sets errno on error and it returns 0 on +success. + + int tstream_unix_socketpair(TALLOC_CTX *mem_ctx1, + struct tstream_context **stream1, + TALLOC_CTX *mem_ctx2, + struct tstream_context **stream2); + +In some situations it's needed to create a tsocket_address from +a given 'struct sockaddr'. You can use tsocket_address_bsd_from_sockaddr() +for that. This should only be used if really needed, because of +already existing fixed APIs. Only AF_INET, AF_INET6 and AF_UNIX +sockets are allowed. The function returns -1 and set errno on error. +Otherwise it returns 0. + + int tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx, + struct sockaddr *sa, + socklen_t sa_socklen, + struct tsocket_address **addr); + +In some situations it's needed to get a 'struct sockaddr' from a +given tsocket_address . You can use tsocket_address_bsd_sockaddr() +for that. This should only be used if really needed. Only AF_INET, +AF_INET6 and AF_UNIX are supported. It returns the size of '*sa' on +success, otherwise it returns -1 and sets 'errno'. + + ssize_t tsocket_address_bsd_sockaddr(const struct tsocket_address *addr, + struct sockaddr *sa, + socklen_t sa_socklen); + +In some situations it's needed to wrap existing file descriptors +into the tstream abstraction. You can use tstream_bsd_existing_socket() +for that. But you should read the tsocket_bsd.c code and unterstand it +in order use this function. E.g. the fd has to be non blocking already. +It will return -1 and set errno on error. Otherwise it returns 0 +and sets '*stream' to point to the new tstream_context. + + int tstream_bsd_existing_socket(TALLOC_CTX *mem_ctx, + int fd, + struct tstream_context **stream); + +Virtual Sockets +=============== + +The abstracted layout of tdgram_context and tstream_context +allow implementations arround virtual sockets for encrypted tunnels +(like TLS, SASL or GSSAPI) or named pipes over smb. + +Named Pipe Auth (NPA) Sockets +============================= + +Samba has an implementation to abstract named pipes over smb +(within the server side). See libcli/named_pipe_auth/npa_tstream.[ch] +for the core code. The current callers are located in source4/ntvfs/ipc/vfs_ipc.c +and source4/rpc_server/service_rpc.c for the users. + diff --git a/lib/tsocket/tsocket_helpers.c b/lib/tsocket/tsocket_helpers.c new file mode 100644 index 0000000000..3a41a3efc3 --- /dev/null +++ b/lib/tsocket/tsocket_helpers.c @@ -0,0 +1,518 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tsocket + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "tsocket.h" +#include "tsocket_internal.h" + +struct tdgram_sendto_queue_state { + /* this structs are owned by the caller */ + struct { + struct tevent_context *ev; + struct tdgram_context *dgram; + const uint8_t *buf; + size_t len; + const struct tsocket_address *dst; + } caller; + ssize_t ret; +}; + +static void tdgram_sendto_queue_trigger(struct tevent_req *req, + void *private_data); +static void tdgram_sendto_queue_done(struct tevent_req *subreq); + +struct tevent_req *tdgram_sendto_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + struct tevent_queue *queue, + const uint8_t *buf, + size_t len, + struct tsocket_address *dst) +{ + struct tevent_req *req; + struct tdgram_sendto_queue_state *state; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct tdgram_sendto_queue_state); + if (!req) { + return NULL; + } + + state->caller.ev = ev; + state->caller.dgram = dgram; + state->caller.buf = buf; + state->caller.len = len; + state->caller.dst = dst; + state->ret = -1; + + ok = tevent_queue_add(queue, + ev, + req, + tdgram_sendto_queue_trigger, + NULL); + if (!ok) { + tevent_req_nomem(NULL, req); + goto post; + } + + return req; + + post: + tevent_req_post(req, ev); + return req; +} + +static void tdgram_sendto_queue_trigger(struct tevent_req *req, + void *private_data) +{ + struct tdgram_sendto_queue_state *state = tevent_req_data(req, + struct tdgram_sendto_queue_state); + struct tevent_req *subreq; + + subreq = tdgram_sendto_send(state, + state->caller.ev, + state->caller.dgram, + state->caller.buf, + state->caller.len, + state->caller.dst); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, tdgram_sendto_queue_done, req); +} + +static void tdgram_sendto_queue_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tdgram_sendto_queue_state *state = tevent_req_data(req, + struct tdgram_sendto_queue_state); + ssize_t ret; + int sys_errno; + + ret = tdgram_sendto_recv(subreq, &sys_errno); + talloc_free(subreq); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + state->ret = ret; + + tevent_req_done(req); +} + +ssize_t tdgram_sendto_queue_recv(struct tevent_req *req, int *perrno) +{ + struct tdgram_sendto_queue_state *state = tevent_req_data(req, + struct tdgram_sendto_queue_state); + ssize_t ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + +struct tstream_readv_pdu_state { + /* this structs are owned by the caller */ + struct { + struct tevent_context *ev; + struct tstream_context *stream; + tstream_readv_pdu_next_vector_t next_vector_fn; + void *next_vector_private; + } caller; + + /* + * Each call to the callback resets iov and count + * the callback allocated the iov as child of our state, + * that means we are allowed to modify and free it. + * + * we should call the callback every time we filled the given + * vector and ask for a new vector. We return if the callback + * ask for 0 bytes. + */ + struct iovec *vector; + size_t count; + + /* + * the total number of bytes we read, + * the return value of the _recv function + */ + int total_read; +}; + +static void tstream_readv_pdu_ask_for_next_vector(struct tevent_req *req); +static void tstream_readv_pdu_readv_done(struct tevent_req *subreq); + +struct tevent_req *tstream_readv_pdu_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + tstream_readv_pdu_next_vector_t next_vector_fn, + void *next_vector_private) +{ + struct tevent_req *req; + struct tstream_readv_pdu_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_readv_pdu_state); + if (!req) { + return NULL; + } + + state->caller.ev = ev; + state->caller.stream = stream; + state->caller.next_vector_fn = next_vector_fn; + state->caller.next_vector_private = next_vector_private; + + state->vector = NULL; + state->count = 0; + state->total_read = 0; + + tstream_readv_pdu_ask_for_next_vector(req); + if (!tevent_req_is_in_progress(req)) { + goto post; + } + + return req; + + post: + return tevent_req_post(req, ev); +} + +static void tstream_readv_pdu_ask_for_next_vector(struct tevent_req *req) +{ + struct tstream_readv_pdu_state *state = tevent_req_data(req, + struct tstream_readv_pdu_state); + int ret; + size_t to_read = 0; + size_t i; + struct tevent_req *subreq; + + TALLOC_FREE(state->vector); + state->count = 0; + + ret = state->caller.next_vector_fn(state->caller.stream, + state->caller.next_vector_private, + state, &state->vector, &state->count); + if (ret == -1) { + tevent_req_error(req, errno); + return; + } + + if (state->count == 0) { + tevent_req_done(req); + return; + } + + for (i=0; i < state->count; i++) { + size_t tmp = to_read; + tmp += state->vector[i].iov_len; + + if (tmp < to_read) { + tevent_req_error(req, EMSGSIZE); + return; + } + + to_read = tmp; + } + + /* + * this is invalid the next vector function should have + * reported count == 0. + */ + if (to_read == 0) { + tevent_req_error(req, EINVAL); + return; + } + + if (state->total_read + to_read < state->total_read) { + tevent_req_error(req, EMSGSIZE); + return; + } + + subreq = tstream_readv_send(state, + state->caller.ev, + state->caller.stream, + state->vector, + state->count); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, tstream_readv_pdu_readv_done, req); +} + +static void tstream_readv_pdu_readv_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tstream_readv_pdu_state *state = tevent_req_data(req, + struct tstream_readv_pdu_state); + int ret; + int sys_errno; + + ret = tstream_readv_recv(subreq, &sys_errno); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + + state->total_read += ret; + + /* ask the callback for a new vector we should fill */ + tstream_readv_pdu_ask_for_next_vector(req); +} + +int tstream_readv_pdu_recv(struct tevent_req *req, int *perrno) +{ + struct tstream_readv_pdu_state *state = tevent_req_data(req, + struct tstream_readv_pdu_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->total_read; + } + + tevent_req_received(req); + return ret; +} + +struct tstream_readv_pdu_queue_state { + /* this structs are owned by the caller */ + struct { + struct tevent_context *ev; + struct tstream_context *stream; + tstream_readv_pdu_next_vector_t next_vector_fn; + void *next_vector_private; + } caller; + int ret; +}; + +static void tstream_readv_pdu_queue_trigger(struct tevent_req *req, + void *private_data); +static void tstream_readv_pdu_queue_done(struct tevent_req *subreq); + +struct tevent_req *tstream_readv_pdu_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct tevent_queue *queue, + tstream_readv_pdu_next_vector_t next_vector_fn, + void *next_vector_private) +{ + struct tevent_req *req; + struct tstream_readv_pdu_queue_state *state; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_readv_pdu_queue_state); + if (!req) { + return NULL; + } + + state->caller.ev = ev; + state->caller.stream = stream; + state->caller.next_vector_fn = next_vector_fn; + state->caller.next_vector_private = next_vector_private; + state->ret = -1; + + ok = tevent_queue_add(queue, + ev, + req, + tstream_readv_pdu_queue_trigger, + NULL); + if (!ok) { + tevent_req_nomem(NULL, req); + goto post; + } + + return req; + + post: + return tevent_req_post(req, ev); +} + +static void tstream_readv_pdu_queue_trigger(struct tevent_req *req, + void *private_data) +{ + struct tstream_readv_pdu_queue_state *state = tevent_req_data(req, + struct tstream_readv_pdu_queue_state); + struct tevent_req *subreq; + + subreq = tstream_readv_pdu_send(state, + state->caller.ev, + state->caller.stream, + state->caller.next_vector_fn, + state->caller.next_vector_private); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, tstream_readv_pdu_queue_done ,req); +} + +static void tstream_readv_pdu_queue_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tstream_readv_pdu_queue_state *state = tevent_req_data(req, + struct tstream_readv_pdu_queue_state); + int ret; + int sys_errno; + + ret = tstream_readv_pdu_recv(subreq, &sys_errno); + talloc_free(subreq); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + state->ret = ret; + + tevent_req_done(req); +} + +int tstream_readv_pdu_queue_recv(struct tevent_req *req, int *perrno) +{ + struct tstream_readv_pdu_queue_state *state = tevent_req_data(req, + struct tstream_readv_pdu_queue_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + +struct tstream_writev_queue_state { + /* this structs are owned by the caller */ + struct { + struct tevent_context *ev; + struct tstream_context *stream; + const struct iovec *vector; + size_t count; + } caller; + int ret; +}; + +static void tstream_writev_queue_trigger(struct tevent_req *req, + void *private_data); +static void tstream_writev_queue_done(struct tevent_req *subreq); + +struct tevent_req *tstream_writev_queue_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct tevent_queue *queue, + const struct iovec *vector, + size_t count) +{ + struct tevent_req *req; + struct tstream_writev_queue_state *state; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_writev_queue_state); + if (!req) { + return NULL; + } + + state->caller.ev = ev; + state->caller.stream = stream; + state->caller.vector = vector; + state->caller.count = count; + state->ret = -1; + + ok = tevent_queue_add(queue, + ev, + req, + tstream_writev_queue_trigger, + NULL); + if (!ok) { + tevent_req_nomem(NULL, req); + goto post; + } + + return req; + + post: + return tevent_req_post(req, ev); +} + +static void tstream_writev_queue_trigger(struct tevent_req *req, + void *private_data) +{ + struct tstream_writev_queue_state *state = tevent_req_data(req, + struct tstream_writev_queue_state); + struct tevent_req *subreq; + + subreq = tstream_writev_send(state, + state->caller.ev, + state->caller.stream, + state->caller.vector, + state->caller.count); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, tstream_writev_queue_done ,req); +} + +static void tstream_writev_queue_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct tstream_writev_queue_state *state = tevent_req_data(req, + struct tstream_writev_queue_state); + int ret; + int sys_errno; + + ret = tstream_writev_recv(subreq, &sys_errno); + talloc_free(subreq); + if (ret == -1) { + tevent_req_error(req, sys_errno); + return; + } + state->ret = ret; + + tevent_req_done(req); +} + +int tstream_writev_queue_recv(struct tevent_req *req, int *perrno) +{ + struct tstream_writev_queue_state *state = tevent_req_data(req, + struct tstream_writev_queue_state); + int ret; + + ret = tsocket_simple_int_recv(req, perrno); + if (ret == 0) { + ret = state->ret; + } + + tevent_req_received(req); + return ret; +} + diff --git a/lib/tsocket/tsocket_internal.h b/lib/tsocket/tsocket_internal.h new file mode 100644 index 0000000000..154b2ce6f8 --- /dev/null +++ b/lib/tsocket/tsocket_internal.h @@ -0,0 +1,144 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tsocket + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _TSOCKET_INTERNAL_H +#define _TSOCKET_INTERNAL_H + +#include <unistd.h> +#include <sys/uio.h> + +struct tsocket_address_ops { + const char *name; + + char *(*string)(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx); + + struct tsocket_address *(*copy)(const struct tsocket_address *addr, + TALLOC_CTX *mem_ctx, + const char *location); +}; + +struct tsocket_address { + const char *location; + const struct tsocket_address_ops *ops; + + void *private_data; +}; + +struct tsocket_address *_tsocket_address_create(TALLOC_CTX *mem_ctx, + const struct tsocket_address_ops *ops, + void *pstate, + size_t psize, + const char *type, + const char *location); +#define tsocket_address_create(mem_ctx, ops, state, type, location) \ + _tsocket_address_create(mem_ctx, ops, state, sizeof(type), \ + #type, location) + +struct tdgram_context_ops { + const char *name; + + struct tevent_req *(*recvfrom_send)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram); + ssize_t (*recvfrom_recv)(struct tevent_req *req, + int *perrno, + TALLOC_CTX *mem_ctx, + uint8_t **buf, + struct tsocket_address **src); + + struct tevent_req *(*sendto_send)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram, + const uint8_t *buf, size_t len, + const struct tsocket_address *dst); + ssize_t (*sendto_recv)(struct tevent_req *req, + int *perrno); + + struct tevent_req *(*disconnect_send)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *dgram); + int (*disconnect_recv)(struct tevent_req *req, + int *perrno); +}; + +struct tdgram_context *_tdgram_context_create(TALLOC_CTX *mem_ctx, + const struct tdgram_context_ops *ops, + void *pstate, + size_t psize, + const char *type, + const char *location); +#define tdgram_context_create(mem_ctx, ops, state, type, location) \ + _tdgram_context_create(mem_ctx, ops, state, sizeof(type), \ + #type, location) + +void *_tdgram_context_data(struct tdgram_context *dgram); +#define tdgram_context_data(_req, _type) \ + talloc_get_type_abort(_tdgram_context_data(_req), _type) + +struct tstream_context_ops { + const char *name; + + ssize_t (*pending_bytes)(struct tstream_context *stream); + + struct tevent_req *(*readv_send)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct iovec *vector, + size_t count); + int (*readv_recv)(struct tevent_req *req, + int *perrno); + + struct tevent_req *(*writev_send)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + const struct iovec *vector, + size_t count); + int (*writev_recv)(struct tevent_req *req, + int *perrno); + + struct tevent_req *(*disconnect_send)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream); + int (*disconnect_recv)(struct tevent_req *req, + int *perrno); +}; + +struct tstream_context *_tstream_context_create(TALLOC_CTX *mem_ctx, + const struct tstream_context_ops *ops, + void *pstate, + size_t psize, + const char *type, + const char *location); +#define tstream_context_create(mem_ctx, ops, state, type, location) \ + _tstream_context_create(mem_ctx, ops, state, sizeof(type), \ + #type, location) + +void *_tstream_context_data(struct tstream_context *stream); +#define tstream_context_data(_req, _type) \ + talloc_get_type_abort(_tstream_context_data(_req), _type) + +int tsocket_simple_int_recv(struct tevent_req *req, int *perrno); + +#endif /* _TSOCKET_H */ + diff --git a/lib/uid_wrapper/config.m4 b/lib/uid_wrapper/config.m4 new file mode 100644 index 0000000000..db6537bf95 --- /dev/null +++ b/lib/uid_wrapper/config.m4 @@ -0,0 +1,16 @@ +AC_ARG_ENABLE(uid-wrapper, +AS_HELP_STRING([--enable-uid-wrapper], [Turn on uid wrapper library (default=no)])) + +HAVE_UID_WRAPPER=no + +if eval "test x$developer = xyes"; then + enable_uid_wrapper=yes +fi + +if eval "test x$enable_uid_wrapper = xyes"; then + AC_DEFINE(UID_WRAPPER,1,[Use uid wrapper library]) + HAVE_UID_WRAPPER=yes +fi + +AC_SUBST(HAVE_UID_WRAPPER) +AC_SUBST(UID_WRAPPER_OBJS) diff --git a/lib/uid_wrapper/config.mk b/lib/uid_wrapper/config.mk new file mode 100644 index 0000000000..1bebc68118 --- /dev/null +++ b/lib/uid_wrapper/config.mk @@ -0,0 +1,9 @@ +############################## +# Start SUBSYSTEM UID_WRAPPER +[SUBSYSTEM::UID_WRAPPER] +PRIVATE_DEPENDENCIES = LIBTALLOC +# End SUBSYSTEM UID_WRAPPER +############################## + +UID_WRAPPER_OBJ_FILES = $(uidwrappersrcdir)/uid_wrapper.o + diff --git a/lib/uid_wrapper/uid_wrapper.c b/lib/uid_wrapper/uid_wrapper.c new file mode 100644 index 0000000000..f7f04316bf --- /dev/null +++ b/lib/uid_wrapper/uid_wrapper.c @@ -0,0 +1,161 @@ +/* + Copyright (C) Andrew Tridgell 2009 + + 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#define UID_WRAPPER_NOT_REPLACE +#include "includes.h" +#include "system/passwd.h" +#include "system/filesys.h" + +#ifndef _PUBLIC_ +#define _PUBLIC_ +#endif + +/* + we keep the virtualised euid/egid/groups information here + */ +static struct { + bool initialised; + bool enabled; + uid_t euid; + gid_t egid; + unsigned ngroups; + gid_t *groups; +} uwrap; + +static void uwrap_init(void) +{ + if (uwrap.initialised) return; + uwrap.initialised = true; + if (getenv("UID_WRAPPER")) { + uwrap.enabled = true; + /* put us in one group */ + uwrap.ngroups = 1; + uwrap.groups = talloc_array(talloc_autofree_context(), gid_t, 1); + uwrap.groups[0] = 0; + } +} + +#undef uwrap_enabled +_PUBLIC_ int uwrap_enabled(void) +{ + uwrap_init(); + return uwrap.enabled?1:0; +} + +_PUBLIC_ int uwrap_seteuid(uid_t euid) +{ + uwrap_init(); + if (!uwrap.enabled) { + return seteuid(euid); + } + /* assume for now that the ruid stays as root */ + uwrap.euid = euid; + return 0; +} + +_PUBLIC_ uid_t uwrap_geteuid(void) +{ + uwrap_init(); + if (!uwrap.enabled) { + return geteuid(); + } + return uwrap.euid; +} + +_PUBLIC_ int uwrap_setegid(gid_t egid) +{ + uwrap_init(); + if (!uwrap.enabled) { + return setegid(egid); + } + /* assume for now that the ruid stays as root */ + uwrap.egid = egid; + return 0; +} + +_PUBLIC_ uid_t uwrap_getegid(void) +{ + uwrap_init(); + if (!uwrap.enabled) { + return getegid(); + } + return uwrap.egid; +} + +_PUBLIC_ int uwrap_setgroups(size_t size, const gid_t *list) +{ + uwrap_init(); + if (!uwrap.enabled) { + return setgroups(size, list); + } + + talloc_free(uwrap.groups); + uwrap.ngroups = 0; + uwrap.groups = NULL; + + if (size != 0) { + uwrap.groups = talloc_array(talloc_autofree_context(), gid_t, size); + if (uwrap.groups == NULL) { + errno = ENOMEM; + return -1; + } + memcpy(uwrap.groups, list, size*sizeof(gid_t)); + uwrap.ngroups = size; + } + return 0; +} + +_PUBLIC_ int uwrap_getgroups(int size, gid_t *list) +{ + uwrap_init(); + if (!uwrap.enabled) { + return getgroups(size, list); + } + + if (size > uwrap.ngroups) { + size = uwrap.ngroups; + } + if (size == 0) { + return uwrap.ngroups; + } + if (size < uwrap.ngroups) { + errno = EINVAL; + return -1; + } + memcpy(list, uwrap.groups, size*sizeof(gid_t)); + return uwrap.ngroups; +} + +_PUBLIC_ uid_t uwrap_getuid(void) +{ + uwrap_init(); + if (!uwrap.enabled) { + return getuid(); + } + /* we don't simulate ruid changing */ + return 0; +} + +_PUBLIC_ gid_t uwrap_getgid(void) +{ + uwrap_init(); + if (!uwrap.enabled) { + return getgid(); + } + /* we don't simulate rgid changing */ + return 0; +} diff --git a/lib/uid_wrapper/uid_wrapper.h b/lib/uid_wrapper/uid_wrapper.h new file mode 100644 index 0000000000..5d7c99d2b2 --- /dev/null +++ b/lib/uid_wrapper/uid_wrapper.h @@ -0,0 +1,63 @@ +/* + Copyright (C) Andrew Tridgell 2009 + + 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __UID_WRAPPER_H__ +#define __UID_WRAPPER_H__ + +#ifdef seteuid +#undef seteuid +#endif +#define seteuid uwrap_seteuid + +#ifdef setegid +#undef setegid +#endif +#define setegid uwrap_setegid + +#ifdef geteuid +#undef geteuid +#endif +#define geteuid uwrap_geteuid + +#ifdef getegid +#undef getegid +#endif +#define getegid uwrap_getegid + +#ifdef setgroups +#undef setgroups +#endif +#define setgroups uwrap_setgroups + +#ifdef getgroups +#undef getgroups +#endif +#define getgroups uwrap_getgroups + +#ifdef getuid +#undef getuid +#endif +#define getuid uwrap_getuid + +#ifdef getgid +#undef getgid +#endif +#define getgid uwrap_getgid + +int uwrap_enabled(void); + +#endif /* __UID_WRAPPER_H__ */ diff --git a/lib/util/asn1.c b/lib/util/asn1.c index a2665ed539..70c2c57450 100644 --- a/lib/util/asn1.c +++ b/lib/util/asn1.c @@ -205,6 +205,15 @@ bool asn1_write_Integer(struct asn1_data *data, int i) return asn1_pop_tag(data); } +/* write a BIT STRING */ +bool asn1_write_BitString(struct asn1_data *data, const void *p, size_t length, uint8_t padding) +{ + if (!asn1_push_tag(data, ASN1_BIT_STRING)) return false; + if (!asn1_write_uint8(data, padding)) return false; + if (!asn1_write(data, p, length)) return false; + return asn1_pop_tag(data); +} + bool ber_write_OID_String(DATA_BLOB *blob, const char *OID) { uint_t v, v2; @@ -262,6 +271,7 @@ bool asn1_write_OID(struct asn1_data *data, const char *OID) } if (!asn1_write(data, blob.data, blob.length)) { + data_blob_free(&blob); data->has_error = true; return false; } @@ -332,6 +342,29 @@ bool asn1_read_BOOLEAN(struct asn1_data *data, bool *v) return !data->has_error; } +/* write a BOOLEAN in a simple context */ +bool asn1_write_BOOLEAN_context(struct asn1_data *data, bool v, int context) +{ + asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(context)); + asn1_write_uint8(data, v ? 0xFF : 0); + asn1_pop_tag(data); + return !data->has_error; +} + +bool asn1_read_BOOLEAN_context(struct asn1_data *data, bool *v, int context) +{ + uint8_t tmp = 0; + asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(context)); + asn1_read_uint8(data, &tmp); + if (tmp == 0xFF) { + *v = true; + } else { + *v = false; + } + asn1_end_tag(data); + return !data->has_error; +} + /* check a BOOLEAN */ bool asn1_check_BOOLEAN(struct asn1_data *data, bool v) { @@ -653,7 +686,7 @@ bool asn1_read_OctetString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLO if (data->has_error) { data_blob_free(blob); - *blob = data_blob(NULL, 0); + *blob = data_blob_null; return false; } return true; @@ -703,6 +736,39 @@ bool asn1_read_Integer(struct asn1_data *data, int *i) return asn1_end_tag(data); } +/* read a BIT STRING */ +bool asn1_read_BitString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLOB *blob, uint8_t *padding) +{ + int len; + ZERO_STRUCTP(blob); + if (!asn1_start_tag(data, ASN1_BIT_STRING)) return false; + len = asn1_tag_remaining(data); + if (len < 0) { + data->has_error = true; + return false; + } + if (!asn1_read_uint8(data, padding)) return false; + + *blob = data_blob_talloc(mem_ctx, NULL, len); + if (!blob->data) { + data->has_error = true; + return false; + } + if (asn1_read(data, blob->data, len - 1)) { + blob->length--; + blob->data[len] = 0; + asn1_end_tag(data); + } + + if (data->has_error) { + data_blob_free(blob); + *blob = data_blob_null; + *padding = 0; + return false; + } + return true; +} + /* read an integer */ bool asn1_read_enumerated(struct asn1_data *data, int *v) { @@ -741,6 +807,32 @@ bool asn1_write_enumerated(struct asn1_data *data, uint8_t v) } /* + Get us the data just written without copying +*/ +bool asn1_blob(const struct asn1_data *asn1, DATA_BLOB *blob) +{ + if (asn1->has_error) { + return false; + } + if (asn1->nesting != NULL) { + return false; + } + blob->data = asn1->data; + blob->length = asn1->length; + return true; +} + +/* + Fill in an asn1 struct without making a copy +*/ +void asn1_load_nocopy(struct asn1_data *data, uint8_t *buf, size_t len) +{ + ZERO_STRUCTP(data); + data->data = buf; + data->length = len; +} + +/* check if a ASN.1 blob is a full tag */ NTSTATUS asn1_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size) diff --git a/lib/util/asn1.h b/lib/util/asn1.h index 8ecb85cb81..9abae50d64 100644 --- a/lib/util/asn1.h +++ b/lib/util/asn1.h @@ -46,7 +46,7 @@ typedef struct asn1_data ASN1_DATA; #define ASN1_OID 0x6 #define ASN1_BOOLEAN 0x1 #define ASN1_INTEGER 0x2 -#define ASN1_BITFIELD 0x3 +#define ASN1_BIT_STRING 0x3 #define ASN1_ENUMERATED 0xa #define ASN1_SET 0x31 @@ -60,6 +60,7 @@ bool asn1_push_tag(struct asn1_data *data, uint8_t tag); bool asn1_pop_tag(struct asn1_data *data); bool asn1_write_implicit_Integer(struct asn1_data *data, int i); bool asn1_write_Integer(struct asn1_data *data, int i); +bool asn1_write_BitString(struct asn1_data *data, const void *p, size_t length, uint8_t padding); bool ber_write_OID_String(DATA_BLOB *blob, const char *OID); bool asn1_write_OID(struct asn1_data *data, const char *OID); bool asn1_write_OctetString(struct asn1_data *data, const void *p, size_t length); @@ -70,6 +71,8 @@ bool asn1_write_ContextSimple(struct asn1_data *data, uint8_t num, DATA_BLOB *bl bool asn1_write_BOOLEAN(struct asn1_data *data, bool v); bool asn1_read_BOOLEAN(struct asn1_data *data, bool *v); bool asn1_check_BOOLEAN(struct asn1_data *data, bool v); +bool asn1_write_BOOLEAN_context(struct asn1_data *data, bool v, int context); +bool asn1_read_BOOLEAN_context(struct asn1_data *data, bool *v, int context); bool asn1_load(struct asn1_data *data, DATA_BLOB blob); bool asn1_peek(struct asn1_data *data, void *p, int len); bool asn1_read(struct asn1_data *data, void *p, int len); @@ -88,9 +91,12 @@ bool asn1_read_OctetString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLO bool asn1_read_ContextSimple(struct asn1_data *data, uint8_t num, DATA_BLOB *blob); bool asn1_read_implicit_Integer(struct asn1_data *data, int *i); bool asn1_read_Integer(struct asn1_data *data, int *i); +bool asn1_read_BitString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLOB *blob, uint8_t *padding); bool asn1_read_enumerated(struct asn1_data *data, int *v); bool asn1_check_enumerated(struct asn1_data *data, int v); bool asn1_write_enumerated(struct asn1_data *data, uint8_t v); +bool asn1_blob(const struct asn1_data *asn1, DATA_BLOB *blob); +void asn1_load_nocopy(struct asn1_data *data, uint8_t *buf, size_t len); NTSTATUS asn1_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size); #endif /* _ASN_1_H */ diff --git a/lib/util/blocking.c b/lib/util/blocking.c new file mode 100644 index 0000000000..f5933cc92b --- /dev/null +++ b/lib/util/blocking.c @@ -0,0 +1,62 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2001-2002 + Copyright (C) Simo Sorce 2001 + Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003. + Copyright (C) James J Myers 2003 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/network.h" +#include "system/filesys.h" +#include "system/locale.h" +#undef malloc +#undef strcasecmp +#undef strncasecmp +#undef strdup +#undef realloc + +/** + Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, + else + if SYSV use O_NDELAY + if BSD use FNDELAY +**/ + +_PUBLIC_ int set_blocking(int fd, bool set) +{ + int val; +#ifdef O_NONBLOCK +#define FLAG_TO_SET O_NONBLOCK +#else +#ifdef SYSV +#define FLAG_TO_SET O_NDELAY +#else /* BSD */ +#define FLAG_TO_SET FNDELAY +#endif +#endif + + if((val = fcntl(fd, F_GETFL, 0)) == -1) + return -1; + if(set) /* Turn blocking on - ie. clear nonblock flag */ + val &= ~FLAG_TO_SET; + else + val |= FLAG_TO_SET; + return fcntl( fd, F_SETFL, val); +#undef FLAG_TO_SET +} diff --git a/lib/util/charset/charcnv.c b/lib/util/charset/charcnv.c index 94d47a9f7f..a479f44426 100644 --- a/lib/util/charset/charcnv.c +++ b/lib/util/charset/charcnv.c @@ -430,7 +430,7 @@ _PUBLIC_ codepoint_t next_codepoint_convenience(struct smb_iconv_convenience *ic return the number of bytes occupied by the CH_UNIX character, or -1 on failure */ -_PUBLIC_ ssize_t push_codepoint(struct smb_iconv_convenience *ic, +_PUBLIC_ ssize_t push_codepoint_convenience(struct smb_iconv_convenience *ic, char *str, codepoint_t c) { smb_iconv_t descriptor; @@ -478,3 +478,5 @@ _PUBLIC_ ssize_t push_codepoint(struct smb_iconv_convenience *ic, } return 5 - olen; } + + diff --git a/lib/util/charset/charset.h b/lib/util/charset/charset.h index 37c5acafaf..2c8aa41ad5 100644 --- a/lib/util/charset/charset.h +++ b/lib/util/charset/charset.h @@ -151,11 +151,12 @@ ssize_t iconv_talloc(TALLOC_CTX *mem_ctx, extern struct smb_iconv_convenience *global_iconv_convenience; codepoint_t next_codepoint(const char *str, size_t *size); +ssize_t push_codepoint(char *str, codepoint_t c); /* codepoints */ codepoint_t next_codepoint_convenience(struct smb_iconv_convenience *ic, const char *str, size_t *size); -ssize_t push_codepoint(struct smb_iconv_convenience *ic, +ssize_t push_codepoint_convenience(struct smb_iconv_convenience *ic, char *str, codepoint_t c); codepoint_t toupper_m(codepoint_t val); codepoint_t tolower_m(codepoint_t val); diff --git a/lib/util/charset/iconv.c b/lib/util/charset/iconv.c index 9825e4be01..8256dc665c 100644 --- a/lib/util/charset/iconv.c +++ b/lib/util/charset/iconv.c @@ -22,7 +22,6 @@ #include "../lib/util/dlinklist.h" #include "system/iconv.h" #include "system/filesys.h" -#undef strcasecmp /** @@ -50,7 +49,6 @@ static size_t ascii_pull (void *,const char **, size_t *, char **, size_t *); static size_t ascii_push (void *,const char **, size_t *, char **, size_t *); -static size_t latin1_push (void *,const char **, size_t *, char **, size_t *); static size_t utf8_pull (void *,const char **, size_t *, char **, size_t *); static size_t utf8_push (void *,const char **, size_t *, char **, size_t *); static size_t utf16_munged_pull(void *,const char **, size_t *, char **, size_t *); @@ -74,8 +72,6 @@ static const struct charset_functions builtin_functions[] = { {"UTF16_MUNGED", utf16_munged_pull, iconv_copy}, {"ASCII", ascii_pull, ascii_push}, - {"646", ascii_pull, ascii_push}, - {"ISO-8859-1", ascii_pull, latin1_push}, {"UCS2-HEX", ucs2hex_pull, ucs2hex_push} }; @@ -162,7 +158,19 @@ static bool is_utf16(const char *name) strcasecmp(name, "UTF-16LE") == 0; } +int smb_iconv_t_destructor(smb_iconv_t hwd) +{ +#ifdef HAVE_NATIVE_ICONV + if (hwd->cd_pull != NULL && hwd->cd_pull != (iconv_t)-1) + iconv_close(hwd->cd_pull); + if (hwd->cd_push != NULL && hwd->cd_push != (iconv_t)-1) + iconv_close(hwd->cd_push); + if (hwd->cd_direct != NULL && hwd->cd_direct != (iconv_t)-1) + iconv_close(hwd->cd_direct); +#endif + return 0; +} _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, const char *fromcode, bool native_iconv) @@ -179,6 +187,7 @@ _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, return (smb_iconv_t)-1; } memset(ret, 0, sizeof(*ret)); + talloc_set_destructor(ret, smb_iconv_t_destructor); /* check for the simplest null conversion */ if (strcmp(fromcode, tocode) == 0) { @@ -251,6 +260,9 @@ _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, } if (is_utf16(tocode)) { ret->direct = sys_iconv; + /* could be set just above - so we need to close iconv */ + if (ret->cd_direct != NULL && ret->cd_direct != (iconv_t)-1) + iconv_close(ret->cd_direct); ret->cd_direct = ret->cd_pull; ret->cd_pull = NULL; return ret; @@ -273,7 +285,7 @@ failed: */ _PUBLIC_ smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode) { - return smb_iconv_open_ex(NULL, tocode, fromcode, true); + return smb_iconv_open_ex(talloc_autofree_context(), tocode, fromcode, true); } /* @@ -281,12 +293,6 @@ _PUBLIC_ smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode) */ _PUBLIC_ int smb_iconv_close(smb_iconv_t cd) { -#ifdef HAVE_NATIVE_ICONV - if (cd->cd_direct) iconv_close((iconv_t)cd->cd_direct); - if (cd->cd_pull) iconv_close((iconv_t)cd->cd_pull); - if (cd->cd_push) iconv_close((iconv_t)cd->cd_push); -#endif - talloc_free(cd); return 0; } @@ -344,32 +350,6 @@ static size_t ascii_push(void *cd, const char **inbuf, size_t *inbytesleft, return ir_count; } -static size_t latin1_push(void *cd, const char **inbuf, size_t *inbytesleft, - char **outbuf, size_t *outbytesleft) -{ - int ir_count=0; - - while (*inbytesleft >= 2 && *outbytesleft >= 1) { - (*outbuf)[0] = (*inbuf)[0]; - if ((*inbuf)[1]) ir_count++; - (*inbytesleft) -= 2; - (*outbytesleft) -= 1; - (*inbuf) += 2; - (*outbuf) += 1; - } - - if (*inbytesleft == 1) { - errno = EINVAL; - return -1; - } - - if (*inbytesleft > 1) { - errno = E2BIG; - return -1; - } - - return ir_count; -} static size_t ucs2hex_pull(void *cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) diff --git a/lib/util/charset/tests/iconv.c b/lib/util/charset/tests/iconv.c index 091876f63b..3e2546dc01 100644 --- a/lib/util/charset/tests/iconv.c +++ b/lib/util/charset/tests/iconv.c @@ -27,6 +27,7 @@ #include "libcli/raw/libcliraw.h" #include "param/param.h" #include "torture/util.h" +#include "talloc.h" #if HAVE_NATIVE_ICONV @@ -288,7 +289,7 @@ static bool test_codepoint(struct torture_context *tctx, unsigned int codepoint) size_t size, size2; codepoint_t c; - size = push_codepoint(lp_iconv_convenience(tctx->lp_ctx), (char *)buf, codepoint); + size = push_codepoint_convenience(lp_iconv_convenience(tctx->lp_ctx), (char *)buf, codepoint); torture_assert(tctx, size != -1 || (codepoint >= 0xd800 && codepoint <= 0x10000), "Invalid Codepoint range"); @@ -440,7 +441,8 @@ static bool test_string2key(struct torture_context *tctx) torture_fail(tctx, "Failed to convert fixed buffer to UTF8\n"); } - torture_assert(tctx, strcmp(correct, out1) == 0, "conversion gave incorrect result\n"); + torture_assert(tctx, strcmp(correct, (const char *) out1) == 0, + "conversion gave incorrect result\n"); talloc_free(mem_ctx); diff --git a/lib/util/charset/util_unistr.c b/lib/util/charset/util_unistr.c index ea2bfeab9f..045aa4a3e3 100644 --- a/lib/util/charset/util_unistr.c +++ b/lib/util/charset/util_unistr.c @@ -444,7 +444,7 @@ _PUBLIC_ char *strlower_talloc(TALLOC_CTX *ctx, const char *src) c = tolower_m(c); - c_size = push_codepoint(iconv_convenience, dest+size, c); + c_size = push_codepoint_convenience(iconv_convenience, dest+size, c); if (c_size == -1) { talloc_free(dest); return NULL; @@ -483,14 +483,14 @@ _PUBLIC_ char *strupper_talloc_n(TALLOC_CTX *ctx, const char *src, size_t n) return NULL; } - while (*src && n--) { + while (n-- && *src) { size_t c_size; codepoint_t c = next_codepoint_convenience(iconv_convenience, src, &c_size); src += c_size; c = toupper_m(c); - c_size = push_codepoint(iconv_convenience, dest+size, c); + c_size = push_codepoint_convenience(iconv_convenience, dest+size, c); if (c_size == -1) { talloc_free(dest); return NULL; @@ -551,7 +551,7 @@ _PUBLIC_ void strlower_m(char *s) while (*s) { size_t c_size, c_size2; codepoint_t c = next_codepoint_convenience(iconv_convenience, s, &c_size); - c_size2 = push_codepoint(iconv_convenience, d, tolower_m(c)); + c_size2 = push_codepoint_convenience(iconv_convenience, d, tolower_m(c)); if (c_size2 > c_size) { DEBUG(0,("FATAL: codepoint 0x%x (0x%x) expanded from %d to %d bytes in strlower_m\n", c, tolower_m(c), (int)c_size, (int)c_size2)); @@ -590,7 +590,7 @@ _PUBLIC_ void strupper_m(char *s) while (*s) { size_t c_size, c_size2; codepoint_t c = next_codepoint_convenience(iconv_convenience, s, &c_size); - c_size2 = push_codepoint(iconv_convenience, d, toupper_m(c)); + c_size2 = push_codepoint_convenience(iconv_convenience, d, toupper_m(c)); if (c_size2 > c_size) { DEBUG(0,("FATAL: codepoint 0x%x (0x%x) expanded from %d to %d bytes in strupper_m\n", c, toupper_m(c), (int)c_size, (int)c_size2)); @@ -992,3 +992,8 @@ _PUBLIC_ codepoint_t next_codepoint(const char *str, size_t *size) { return next_codepoint_convenience(get_iconv_convenience(), str, size); } + +_PUBLIC_ ssize_t push_codepoint(char *str, codepoint_t c) +{ + return push_codepoint_convenience(get_iconv_convenience(), str, c); +} diff --git a/lib/util/config.mk b/lib/util/config.mk index 14bdb2a277..b6125563fb 100644 --- a/lib/util/config.mk +++ b/lib/util/config.mk @@ -1,11 +1,14 @@ -[SUBSYSTEM::LIBSAMBA-UTIL] +[LIBRARY::LIBSAMBA-UTIL] PUBLIC_DEPENDENCIES = \ LIBTALLOC LIBCRYPTO \ SOCKET_WRAPPER LIBREPLACE_NETWORK \ - CHARSET EXECINFO + CHARSET EXECINFO UID_WRAPPER + +LIBSAMBA-UTIL_VERSION = 0.0.1 +LIBSAMBA-UTIL_SOVERSION = 0 LIBSAMBA-UTIL_OBJ_FILES = $(addprefix $(libutilsrcdir)/, \ - xfile.o \ + xfile.o \ debug.o \ fault.o \ signal.o \ @@ -14,11 +17,13 @@ LIBSAMBA-UTIL_OBJ_FILES = $(addprefix $(libutilsrcdir)/, \ genrand.o \ dprintf.o \ util_str.o \ + rfc1738.o \ substitute.o \ util_strlist.o \ util_file.o \ data_blob.o \ util.o \ + blocking.o \ util_net.o \ fsusage.o \ ms_fnmatch.o \ @@ -27,7 +32,10 @@ LIBSAMBA-UTIL_OBJ_FILES = $(addprefix $(libutilsrcdir)/, \ become_daemon.o \ rbtree.o \ talloc_stack.o \ - params.o) + smb_threads.o \ + params.o \ + parmlist.o \ + util_id.o) PUBLIC_HEADERS += $(addprefix $(libutilsrcdir)/, util.h \ dlinklist.h \ @@ -48,6 +56,7 @@ PUBLIC_HEADERS += $(addprefix $(libutilsrcdir)/, util.h \ ASN1_UTIL_OBJ_FILES = $(libutilsrcdir)/asn1.o [SUBSYSTEM::UNIX_PRIVS] +PRIVATE_DEPENDENCIES = UID_WRAPPER UNIX_PRIVS_OBJ_FILES = $(libutilsrcdir)/unix_privs.o @@ -68,6 +77,13 @@ PUBLIC_DEPENDENCIES = LIBTDB UTIL_TDB_OBJ_FILES = $(libutilsrcdir)/util_tdb.o +[SUBSYSTEM::UTIL_TEVENT] +PUBLIC_DEPENDENCIES = LIBTEVENT + +UTIL_TEVENT_OBJ_FILES = $(addprefix $(libutilsrcdir)/, \ + tevent_unix.o \ + tevent_ntstatus.o) + [SUBSYSTEM::UTIL_LDB] PUBLIC_DEPENDENCIES = LIBLDB diff --git a/lib/util/data_blob.c b/lib/util/data_blob.c index c7d01bacc7..825d8cf88c 100644 --- a/lib/util/data_blob.c +++ b/lib/util/data_blob.c @@ -163,8 +163,11 @@ _PUBLIC_ char *data_blob_hex_string(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob) return NULL; } + /* this must be lowercase or w2k8 cannot join a samba domain, + as this routine is used to encode extended DNs and windows + only accepts lowercase hexadecimal numbers */ for (i = 0; i < blob->length; i++) - slprintf(&hex_string[i*2], 3, "%02X", blob->data[i]); + slprintf(&hex_string[i*2], 3, "%02x", blob->data[i]); hex_string[(blob->length*2)] = '\0'; return hex_string; diff --git a/lib/util/debug.c b/lib/util/debug.c index 578822088f..996efdff7e 100644 --- a/lib/util/debug.c +++ b/lib/util/debug.c @@ -50,6 +50,7 @@ static struct { int fd; enum debug_logtype logtype; const char *prog_name; + bool reopening_logs; } state; static bool reopen_logs_scheduled; @@ -139,6 +140,9 @@ _PUBLIC_ void reopen_logs(void) { char *fname = NULL; int old_fd = state.fd; + if (state.reopening_logs) { + return; + } switch (state.logtype) { case DEBUG_STDOUT: @@ -150,6 +154,7 @@ _PUBLIC_ void reopen_logs(void) break; case DEBUG_FILE: + state.reopening_logs = true; if (logfile && (*logfile) == '/') { fname = strdup(logfile); } else { @@ -167,6 +172,7 @@ _PUBLIC_ void reopen_logs(void) } else { DEBUG(1, ("Failed to find name for file-based logfile!\n")); } + state.reopening_logs = false; break; } @@ -192,6 +198,14 @@ _PUBLIC_ void setup_logging(const char *prog_name, enum debug_logtype new_logtyp } /** + Just run logging to stdout for this program +*/ +_PUBLIC_ void setup_logging_stdout(void) +{ + setup_logging(NULL, DEBUG_STDOUT); +} + +/** return a string constant containing n tabs no more than 10 tabs are returned */ diff --git a/lib/util/debug.h b/lib/util/debug.h index 85e64fb861..f0d16952a9 100644 --- a/lib/util/debug.h +++ b/lib/util/debug.h @@ -74,8 +74,12 @@ extern int DEBUGLEVEL; */ #define DEBUGTAB(n) do_debug_tab(n) -/** Possible destinations for the debug log */ -enum debug_logtype {DEBUG_FILE = 0, DEBUG_STDOUT = 1, DEBUG_STDERR = 2}; +/** Possible destinations for the debug log (in order of precedence - + * once set to DEBUG_FILE, it is not possible to reset to DEBUG_STDOUT + * for example. This makes it easy to override for debug to stderr on + * the command line, as the smb.conf cannot reset it back to + * file-based logging */ +enum debug_logtype {DEBUG_STDOUT = 0, DEBUG_FILE = 1, DEBUG_STDERR = 2}; /** the backend for debug messages. Note that the DEBUG() macro has already @@ -102,6 +106,11 @@ _PUBLIC_ void debug_schedule_reopen_logs(void); _PUBLIC_ void setup_logging(const char *prog_name, enum debug_logtype new_logtype); /** + Just run logging to stdout for this program +*/ +_PUBLIC_ void setup_logging_stdout(void); + +/** return a string constant containing n tabs no more than 10 tabs are returned */ diff --git a/lib/util/dlinklist.h b/lib/util/dlinklist.h index 1a4ebb6fa0..693b43dd27 100644 --- a/lib/util/dlinklist.h +++ b/lib/util/dlinklist.h @@ -87,11 +87,11 @@ do { \ }\ } while (0) -/* demote an element to the end of the list, needs a tmp pointer */ -#define DLIST_DEMOTE(list, p, tmp) \ +/* demote an element to the end of the list, needs the entry type */ +#define DLIST_DEMOTE(list, p, type) \ do { \ DLIST_REMOVE(list, p); \ - DLIST_ADD_END(list, p, tmp); \ + DLIST_ADD_END(list, p, type); \ } while (0) /* concatenate two lists - putting all elements of the 2nd list at the diff --git a/lib/util/dprintf.c b/lib/util/dprintf.c index 3e6d0e8bca..e9a15dcbe6 100644 --- a/lib/util/dprintf.c +++ b/lib/util/dprintf.c @@ -39,6 +39,9 @@ static smb_iconv_t display_cd = (smb_iconv_t)-1; void d_set_iconv(smb_iconv_t cd) { + if (display_cd != (smb_iconv_t)-1) + talloc_free(display_cd); + display_cd = cd; } diff --git a/lib/util/fault.m4 b/lib/util/fault.m4 index da077af31d..c22976998e 100644 --- a/lib/util/fault.m4 +++ b/lib/util/fault.m4 @@ -8,6 +8,9 @@ if test x"$ac_cv_header_execinfo_h" = x"yes" -a x"$ac_cv_func_ext_backtrace" = x EXECINFO_CFLAGS="$CFLAGS" EXECINFO_CPPFLAGS="$CPPFLAGS" EXECINFO_LDFLAGS="$LDFLAGS" + LIB_REMOVE_USR_LIB(EXECINFO_LDFLAGS) + CFLAGS_REMOVE_USR_INCLUDE(EXECINFO_CFLAGS) + CFLAGS_REMOVE_USR_INCLUDE(EXECINFO_CPPFLAGS) else SMB_ENABLE(EXECINFO,NO) fi diff --git a/lib/util/genrand.c b/lib/util/genrand.c index cd1823a9a0..f0544023f1 100644 --- a/lib/util/genrand.c +++ b/lib/util/genrand.c @@ -294,6 +294,7 @@ _PUBLIC_ uint32_t generate_random(void) _PUBLIC_ bool check_password_quality(const char *s) { int has_digit=0, has_capital=0, has_lower=0, has_special=0, has_high=0; + const char* reals = s; while (*s) { if (isdigit((unsigned char)*s)) { has_digit |= 1; @@ -310,7 +311,7 @@ _PUBLIC_ bool check_password_quality(const char *s) } return ((has_digit + has_lower + has_capital + has_special) >= 3 - || (has_high > strlen(s)/2)); + || (has_high > strlen(reals)/2)); } /** @@ -359,3 +360,53 @@ again: return retstr; } + +/** + * Generate an array of unique text strings all of the same length. + * The returned string will be allocated. + * Returns NULL if the number of unique combinations cannot be created. + * + * Characters used are: abcdefghijklmnopqrstuvwxyz0123456789+_-#., + */ +_PUBLIC_ char** generate_unique_strs(TALLOC_CTX *mem_ctx, size_t len, + uint32_t num) +{ + const char *c_list = "abcdefghijklmnopqrstuvwxyz0123456789+_-#.,"; + const unsigned c_size = 42; + int i, j; + unsigned rem; + char ** strs = NULL; + + if (num == 0 || len == 0) + return NULL; + + strs = talloc_array(mem_ctx, char *, num); + if (strs == NULL) return NULL; + + for (i = 0; i < num; i++) { + char *retstr = (char *)talloc_size(strs, len + 1); + if (retstr == NULL) { + talloc_free(strs); + return NULL; + } + rem = i; + for (j = 0; j < len; j++) { + retstr[j] = c_list[rem % c_size]; + rem = rem / c_size; + } + retstr[j] = 0; + strs[i] = retstr; + if (rem != 0) { + /* we were not able to fit the number of + * combinations asked for in the length + * specified */ + DEBUG(0,(__location__ ": Too many combinations %u for length %u\n", + num, (unsigned)len)); + + talloc_free(strs); + return NULL; + } + } + + return strs; +} diff --git a/lib/util/idtree.c b/lib/util/idtree.c index c8a8b6346a..0af93a229d 100644 --- a/lib/util/idtree.c +++ b/lib/util/idtree.c @@ -372,12 +372,16 @@ _PUBLIC_ int idr_get_new_random(struct idr_context *idp, void *ptr, int limit) /* first try a random starting point in the whole range, and if that fails, then start randomly in the bottom half of the range. This can only - fail if the range is over half full */ + fail if the range is over half full, and finally fallback to any + free id */ id = idr_get_new_above(idp, ptr, 1+(generate_random() % limit), limit); if (id == -1) { id = idr_get_new_above(idp, ptr, 1+(generate_random()%(limit/2)), limit); } - + if (id == -1) { + id = idr_get_new_above(idp, ptr, 1, limit); + } + return id; } diff --git a/lib/util/parmlist.c b/lib/util/parmlist.c new file mode 100644 index 0000000000..6658fa7e33 --- /dev/null +++ b/lib/util/parmlist.c @@ -0,0 +1,106 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Jelmer Vernooij 2009 + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "../lib/util/dlinklist.h" +#include "../lib/util/parmlist.h" + +struct parmlist_entry *parmlist_get(struct parmlist *ctx, const char *name) +{ + struct parmlist_entry *e; + for (e = ctx->entries; e; e = e->next) { + if (strcasecmp(e->key, name) == 0) + return e; + } + + return NULL; +} + +int parmlist_get_int(struct parmlist *ctx, const char *name, int default_v) +{ + struct parmlist_entry *p = parmlist_get(ctx, name); + + if (p != NULL) + return strtol(p->value, NULL, 0); + + return default_v; +} + +bool parmlist_get_bool(struct parmlist *ctx, const char *name, bool default_v) +{ + struct parmlist_entry *p = parmlist_get(ctx, name); + bool ret; + + if (p == NULL) + return default_v; + + if (!set_boolean(p->value, &ret)) { + DEBUG(0,("lp_bool(%s): value is not boolean!\n", p->value)); + return default_v; + } + + return ret; +} + +const char *parmlist_get_string(struct parmlist *ctx, const char *name, + const char *default_v) +{ + struct parmlist_entry *p = parmlist_get(ctx, name); + + if (p == NULL) + return default_v; + + return p->value; +} + +const char **parmlist_get_string_list(struct parmlist *ctx, const char *name, + const char *separator) +{ + struct parmlist_entry *p = parmlist_get(ctx, name); + + if (p == NULL) + return NULL; + + return (const char **)str_list_make(ctx, p->value, separator); +} + +static struct parmlist_entry *parmlist_get_add(struct parmlist *ctx, const char *name) +{ + struct parmlist_entry *e = parmlist_get(ctx, name); + + if (e != NULL) + return e; + + e = talloc(ctx, struct parmlist_entry); + if (e == NULL) + return NULL; + e->key = talloc_strdup(e, name); + DLIST_ADD(ctx->entries, e); + return e; +} + +int parmlist_set_string(struct parmlist *ctx, const char *name, + const char *value) +{ + struct parmlist_entry *e = parmlist_get_add(ctx, name); + if (e == NULL) + return -1; + + e->value = talloc_strdup(e, value); + return 0; +} diff --git a/lib/util/parmlist.h b/lib/util/parmlist.h new file mode 100644 index 0000000000..b320afee47 --- /dev/null +++ b/lib/util/parmlist.h @@ -0,0 +1,56 @@ +/* + Unix SMB/CIFS implementation. + Generic parameter parsing interface + Copyright (C) Jelmer Vernooij 2009 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _PARMLIST_H /* _PARMLIST_H */ +#define _PARMLIST_H + +struct parmlist_entry { + struct parmlist_entry *prev, *next; + char *key; + char *value; + int priority; +}; + +struct parmlist { + struct parmlist_entry *entries; +}; + +/** Retrieve an integer from a parameter list. If not found, return default_v. */ +int parmlist_get_int(struct parmlist *ctx, const char *name, int default_v); + +/** Retrieve a string from a parameter list. If not found, return default_v. */ +const char *parmlist_get_string(struct parmlist *ctx, const char *name, + const char *default_v); + +/** Retrieve the struct for an entry in a parmlist. */ +struct parmlist_entry *parmlist_get(struct parmlist *ctx, const char *name); + +/** Retrieve a string list from a parameter list. + * separator can contain characters to consider separators or can be + * NULL for the default set. */ +const char **parmlist_get_string_list(struct parmlist *ctx, const char *name, + const char *separator); + +/** Retrieve boolean from a parameter list. If not set, return default_v. */ +bool parmlist_get_bool(struct parmlist *ctx, const char *name, bool default_v); + +/** Set a parameter. */ +int parmlist_set_string(struct parmlist *ctx, const char *name, const char *value); + +#endif /* _PARMLIST_H */ diff --git a/lib/util/rfc1738.c b/lib/util/rfc1738.c new file mode 100644 index 0000000000..b45310a34b --- /dev/null +++ b/lib/util/rfc1738.c @@ -0,0 +1,225 @@ +/* + * NOTE: + * + * This file imported from the Squid project. The licence below is + * reproduced intact, but refers to files in Squid's repository, not + * in Samba. See COPYING for the GPLv3 notice (being the later + * version mentioned below). + * + * This file has also been modified, in particular to use talloc to + * allocate in rfc1738_escape() + * + * - Andrew Bartlett Oct-2009 + * + */ + + +/* + * $Id$ + * + * DEBUG: + * AUTHOR: Harvest Derived + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "includes.h" + +#include "util.h" + +/* + * RFC 1738 defines that these characters should be escaped, as well + * any non-US-ASCII character or anything between 0x00 - 0x1F. + */ +static char rfc1738_unsafe_chars[] = { + (char) 0x3C, /* < */ + (char) 0x3E, /* > */ + (char) 0x22, /* " */ + (char) 0x23, /* # */ +#if 0 /* done in code */ + (char) 0x25, /* % */ +#endif + (char) 0x7B, /* { */ + (char) 0x7D, /* } */ + (char) 0x7C, /* | */ + (char) 0x5C, /* \ */ + (char) 0x5E, /* ^ */ + (char) 0x7E, /* ~ */ + (char) 0x5B, /* [ */ + (char) 0x5D, /* ] */ + (char) 0x60, /* ` */ + (char) 0x27, /* ' */ + (char) 0x20 /* space */ +}; + +static char rfc1738_reserved_chars[] = { + (char) 0x3b, /* ; */ + (char) 0x2f, /* / */ + (char) 0x3f, /* ? */ + (char) 0x3a, /* : */ + (char) 0x40, /* @ */ + (char) 0x3d, /* = */ + (char) 0x26 /* & */ +}; + +/* + * rfc1738_escape - Returns a static buffer contains the RFC 1738 + * compliant, escaped version of the given url. + * + */ +static char * +rfc1738_do_escape(TALLOC_CTX *mem_ctx, const char *url, int encode_reserved) +{ + size_t bufsize = 0; + const char *p; + char *buf; + char *q; + unsigned int i, do_escape; + + bufsize = strlen(url) * 3 + 1; + buf = talloc_array(mem_ctx, char, bufsize); + if (!buf) { + return NULL; + } + + talloc_set_name_const(buf, buf); + buf[0] = '\0'; + + for (p = url, q = buf; *p != '\0' && q < (buf + bufsize - 1); p++, q++) { + do_escape = 0; + + /* RFC 1738 defines these chars as unsafe */ + for (i = 0; i < sizeof(rfc1738_unsafe_chars); i++) { + if (*p == rfc1738_unsafe_chars[i]) { + do_escape = 1; + break; + } + } + /* Handle % separately */ + if (encode_reserved >= 0 && *p == '%') + do_escape = 1; + /* RFC 1738 defines these chars as reserved */ + for (i = 0; i < sizeof(rfc1738_reserved_chars) && encode_reserved > 0; i++) { + if (*p == rfc1738_reserved_chars[i]) { + do_escape = 1; + break; + } + } + /* RFC 1738 says any control chars (0x00-0x1F) are encoded */ + if ((unsigned char) *p <= (unsigned char) 0x1F) { + do_escape = 1; + } + /* RFC 1738 says 0x7f is encoded */ + if (*p == (char) 0x7F) { + do_escape = 1; + } + /* RFC 1738 says any non-US-ASCII are encoded */ + if (((unsigned char) *p >= (unsigned char) 0x80)) { + do_escape = 1; + } + /* Do the triplet encoding, or just copy the char */ + /* note: while we do not need snprintf here as q is appropriately + * allocated, Samba does to avoid our macro banning it -- abartlet */ + + if (do_escape == 1) { + (void) snprintf(q, 4, "%%%02X", (unsigned char) *p); + q += sizeof(char) * 2; + } else { + *q = *p; + } + } + *q = '\0'; + return (buf); +} + +/* + * rfc1738_escape - Returns a buffer that contains the RFC + * 1738 compliant, escaped version of the given url. (escapes unsafe and % characters) + */ +char * +rfc1738_escape(TALLOC_CTX *mem_ctx, const char *url) +{ + return rfc1738_do_escape(mem_ctx, url, 0); +} + +/* + * rfc1738_escape_unescaped - Returns a buffer that contains + * the RFC 1738 compliant, escaped version of the given url (escapes unsafe chars only) + */ +char * +rfc1738_escape_unescaped(TALLOC_CTX *mem_ctx, const char *url) +{ + return rfc1738_do_escape(mem_ctx, url, -1); +} + +/* + * rfc1738_escape_part - Returns a buffer that contains the RFC + * 1738 compliant, escaped version of the given url segment. (escapes + * unsafe, reserved and % chars) It would mangle the :// in http://, + * and mangle paths (because of /). + */ +char * +rfc1738_escape_part(TALLOC_CTX *mem_ctx, const char *url) +{ + return rfc1738_do_escape(mem_ctx, url, 1); +} + +/* + * rfc1738_unescape() - Converts escaped characters (%xy numbers) in + * given the string. %% is a %. %ab is the 8-bit hexadecimal number "ab" + */ +_PUBLIC_ void +rfc1738_unescape(char *s) +{ + char hexnum[3]; + int i, j; /* i is write, j is read */ + unsigned int x; + for (i = j = 0; s[j]; i++, j++) { + s[i] = s[j]; + if (s[i] != '%') + continue; + if (s[j + 1] == '%') { /* %% case */ + j++; + continue; + } + if (s[j + 1] && s[j + 2]) { + if (s[j + 1] == '0' && s[j + 2] == '0') { /* %00 case */ + j += 2; + continue; + } + hexnum[0] = s[j + 1]; + hexnum[1] = s[j + 2]; + hexnum[2] = '\0'; + if (1 == sscanf(hexnum, "%x", &x)) { + s[i] = (char) (0x0ff & x); + j += 2; + } + } + } + s[i] = '\0'; +} diff --git a/lib/util/smb_threads.c b/lib/util/smb_threads.c new file mode 100644 index 0000000000..58ea2daa67 --- /dev/null +++ b/lib/util/smb_threads.c @@ -0,0 +1,202 @@ +/* + Unix SMB/CIFS implementation. + SMB client library implementation (thread interface functions). + Copyright (C) Jeremy Allison, 2009. + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * This code is based in the ideas in openssl + * but somewhat simpler and expended to include + * thread local storage. + */ + +#include "includes.h" +#include "smb_threads.h" + +/********************************************************* + Functions to vector the locking primitives used internally + by libsmbclient. +*********************************************************/ + +const struct smb_thread_functions *global_tfp; + +/********************************************************* + Dynamic lock array. +*********************************************************/ + +void **global_lock_array; + +/********************************************************* + Mutex used for our internal "once" function +*********************************************************/ + +static void *once_mutex = NULL; + + +/********************************************************* + Function to set the locking primitives used by libsmbclient. +*********************************************************/ + +int smb_thread_set_functions(const struct smb_thread_functions *tf) +{ + int i; + + global_tfp = tf; + +#if defined(PARANOID_MALLOC_CHECKER) +#ifdef malloc +#undef malloc +#endif +#endif + + /* Here we initialize any static locks we're using. */ + global_lock_array = (void **)malloc(sizeof(void *) *NUM_GLOBAL_LOCKS); + +#if defined(PARANOID_MALLOC_CHECKER) +#define malloc(s) __ERROR_DONT_USE_MALLOC_DIRECTLY +#endif + + if (global_lock_array == NULL) { + return ENOMEM; + } + + for (i = 0; i < NUM_GLOBAL_LOCKS; i++) { + char *name = NULL; + if (asprintf(&name, "global_lock_%d", i) == -1) { + SAFE_FREE(global_lock_array); + return ENOMEM; + } + if (global_tfp->create_mutex(name, + &global_lock_array[i], + __location__)) { + smb_panic("smb_thread_set_functions: create mutexes failed"); + } + SAFE_FREE(name); + } + + /* Create the mutex we'll use for our "once" function */ + if (SMB_THREAD_CREATE_MUTEX("smb_once", once_mutex) != 0) { + smb_panic("smb_thread_set_functions: failed to create 'once' mutex"); + } + + return 0; +} + +/******************************************************************* + Call a function only once. We implement this ourselves + using our own mutex rather than using the thread implementation's + *_once() function because each implementation has its own + type for the variable which keeps track of whether the function + has been called, and there's no easy way to allocate the correct + size variable in code internal to Samba without knowing the + implementation's "once" type. +********************************************************************/ + +int smb_thread_once(smb_thread_once_t *ponce, + void (*init_fn)(void *pdata), + void *pdata) +{ + int ret; + + /* Lock our "once" mutex in order to test and initialize ponce */ + if (SMB_THREAD_LOCK(once_mutex) != 0) { + smb_panic("error locking 'once'"); + } + + /* Keep track of whether we ran their init function */ + ret = ! *ponce; + + /* + * See if another thread got here after we tested it initially but + * before we got our lock. + */ + if (! *ponce) { + /* Nope, we need to run the initialization function */ + (*init_fn)(pdata); + + /* Now we can indicate that the function has been run */ + *ponce = true; + } + + /* Unlock the mutex */ + if (SMB_THREAD_UNLOCK(once_mutex) != 0) { + smb_panic("error unlocking 'once'"); + } + + /* + * Tell 'em whether we ran their init function. If they passed a data + * pointer to the init function and the init function could change + * something in the pointed-to data, this will tell them whether that + * data is valid or not. + */ + return ret; +} + + +#if 0 +/* Test. - pthread implementations. */ +#include <pthread.h> + +#ifdef malloc +#undef malloc +#endif + +SMB_THREADS_DEF_PTHREAD_IMPLEMENTATION(tf); + +static smb_thread_once_t ot = SMB_THREAD_ONCE_INIT; +void *pkey = NULL; + +static void init_fn(void) +{ + int ret; + + if (!global_tfp) { + /* Non-thread safe init case. */ + if (ot) { + return; + } + ot = true; + } + + if ((ret = SMB_THREAD_CREATE_TLS("test_tls", pkey)) != 0) { + printf("Create tls once error: %d\n", ret); + } +} + +/* Test function. */ +int test_threads(void) +{ + int ret; + void *plock = NULL; + smb_thread_set_functions(&tf); + + SMB_THREAD_ONCE(&ot, init_fn); + + if ((ret = SMB_THREAD_CREATE_MUTEX("test", plock)) != 0) { + printf("Create lock error: %d\n", ret); + } + if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_LOCK)) != 0) { + printf("lock error: %d\n", ret); + } + if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_UNLOCK)) != 0) { + printf("unlock error: %d\n", ret); + } + SMB_THREAD_DESTROY_MUTEX(plock); + SMB_THREAD_DESTROY_TLS(pkey); + + return 0; +} +#endif diff --git a/lib/util/smb_threads.h b/lib/util/smb_threads.h new file mode 100644 index 0000000000..9a09616774 --- /dev/null +++ b/lib/util/smb_threads.h @@ -0,0 +1,134 @@ +/* + Unix SMB/CIFS implementation. + SMB thread interface functions. + Copyright (C) Jeremy Allison, 2009. + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _smb_threads_h_ +#define _smb_threads_h_ + +typedef bool smb_thread_once_t; +#define SMB_THREAD_ONCE_INIT false + +enum smb_thread_lock_type { + SMB_THREAD_LOCK = 1, + SMB_THREAD_UNLOCK +}; + +struct smb_thread_functions { + /* Mutex and tls functions. */ + int (*create_mutex)(const char *lockname, + void **pplock, + const char *location); + void (*destroy_mutex)(void *plock, + const char *location); + int (*lock_mutex)(void *plock, + int lock_type, + const char *location); + + /* Thread local storage. */ + int (*create_tls)(const char *keyname, + void **ppkey, + const char *location); + void (*destroy_tls)(void **pkey, + const char *location); + int (*set_tls)(void *pkey, const void *pval, const char *location); + void *(*get_tls)(void *pkey, const char *location); +}; + +int smb_thread_set_functions(const struct smb_thread_functions *tf); +int smb_thread_once(smb_thread_once_t *ponce, + void (*init_fn)(void *pdata), + void *pdata); + +extern const struct smb_thread_functions *global_tfp; + +/* Define the pthread version of the functions. */ + +#define SMB_THREADS_DEF_PTHREAD_IMPLEMENTATION(tf) \ + \ +static int smb_create_mutex_pthread(const char *lockname, void **pplock, const char *location) \ +{ \ + pthread_mutex_t *pmut = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); \ + if (!pmut) { \ + return ENOMEM; \ + } \ + pthread_mutex_init(pmut, NULL); \ + *pplock = (void *)pmut; \ + return 0; \ +} \ + \ +static void smb_destroy_mutex_pthread(void *plock, const char *location) \ +{ \ + pthread_mutex_destroy((pthread_mutex_t *)plock); \ + free(plock); \ +} \ + \ +static int smb_lock_pthread(void *plock, int lock_type, const char *location) \ +{ \ + if (lock_type == SMB_THREAD_UNLOCK) { \ + return pthread_mutex_unlock((pthread_mutex_t *)plock); \ + } else { \ + return pthread_mutex_lock((pthread_mutex_t *)plock); \ + } \ +} \ + \ +static int smb_create_tls_pthread(const char *keyname, void **ppkey, const char *location) \ +{ \ + int ret; \ + pthread_key_t *pkey; \ + pkey = (pthread_key_t *)malloc(sizeof(pthread_key_t)); \ + if (!pkey) { \ + return ENOMEM; \ + } \ + ret = pthread_key_create(pkey, NULL); \ + if (ret) { \ + free(pkey); \ + return ret; \ + } \ + *ppkey = (void *)pkey; \ + return 0; \ +} \ + \ +static void smb_destroy_tls_pthread(void **ppkey, const char *location) \ +{ \ + if (*ppkey) { \ + pthread_key_delete(*(pthread_key_t *)ppkey); \ + free(*ppkey); \ + *ppkey = NULL; \ + } \ +} \ + \ +static int smb_set_tls_pthread(void *pkey, const void *pval, const char *location) \ +{ \ + return pthread_setspecific(*(pthread_key_t *)pkey, pval); \ +} \ + \ +static void *smb_get_tls_pthread(void *pkey, const char *location) \ +{ \ + return pthread_getspecific(*(pthread_key_t *)pkey); \ +} \ + \ +static const struct smb_thread_functions (tf) = { \ + smb_create_mutex_pthread, \ + smb_destroy_mutex_pthread, \ + smb_lock_pthread, \ + smb_create_tls_pthread, \ + smb_destroy_tls_pthread, \ + smb_set_tls_pthread, \ + smb_get_tls_pthread } + +#endif diff --git a/lib/util/smb_threads_internal.h b/lib/util/smb_threads_internal.h new file mode 100644 index 0000000000..afd7559f3b --- /dev/null +++ b/lib/util/smb_threads_internal.h @@ -0,0 +1,74 @@ +/* + SMB/CIFS implementation. + SMB thread interface internal macros. + Copyright (C) Jeremy Allison, 2009. + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _smb_threads_internal_h_ +#define _smb_threads_internal_h_ + +#define SMB_THREAD_CREATE_MUTEX(name, lockvar) \ + (global_tfp ? global_tfp->create_mutex((name), &(lockvar), __location__) : 0) + +#define SMB_THREAD_DESTROY_MUTEX(plock) \ + do { \ + if (global_tfp) { \ + global_tfp->destroy_mutex(plock, __location__); \ + }; \ + } while (0) + +#define SMB_THREAD_LOCK_INTERNAL(plock, type, location) \ + (global_tfp ? global_tfp->lock_mutex((plock), (type), location) : 0) + +#define SMB_THREAD_LOCK(plock) \ + SMB_THREAD_LOCK_INTERNAL(plock, SMB_THREAD_LOCK, __location__) + +#define SMB_THREAD_UNLOCK(plock) \ + SMB_THREAD_LOCK_INTERNAL(plock, SMB_THREAD_UNLOCK, __location__) + +#define SMB_THREAD_ONCE(ponce, init_fn, pdata) \ + (global_tfp \ + ? (! *(ponce) \ + ? smb_thread_once((ponce), (init_fn), (pdata)) \ + : 0) \ + : ((init_fn(pdata)), *(ponce) = true, 1)) + +#define SMB_THREAD_CREATE_TLS(keyname, key) \ + (global_tfp ? global_tfp->create_tls((keyname), &(key), __location__) : 0) + +#define SMB_THREAD_DESTROY_TLS(key) \ + do { \ + if (global_tfp) { \ + global_tfp->destroy_tls(&(key), __location__); \ + }; \ + } while (0) + +#define SMB_THREAD_SET_TLS(key, val) \ + (global_tfp ? global_tfp->set_tls((key),(val),__location__) : \ + ((key) = (val), 0)) + +#define SMB_THREAD_GET_TLS(key) \ + (global_tfp ? global_tfp->get_tls((key), __location__) : (key)) + +/* + * Global thread lock list. + */ + +#define NUM_GLOBAL_LOCKS 1 + +#define GLOBAL_LOCK(locknum) (global_lock_array ? global_lock_array[(locknum)] : NULL) + +#endif diff --git a/lib/util/talloc_stack.c b/lib/util/talloc_stack.c index 2f3ea11377..f34d495294 100644 --- a/lib/util/talloc_stack.c +++ b/lib/util/talloc_stack.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. Implement a stack of talloc contexts Copyright (C) Volker Lendecke 2007 + Copyright (C) Jeremy Allison 2009 - made thread safe. 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 @@ -38,22 +39,75 @@ #include "includes.h" -static int talloc_stacksize; -static int talloc_stack_arraysize; -static TALLOC_CTX **talloc_stack; +struct talloc_stackframe { + int talloc_stacksize; + int talloc_stack_arraysize; + TALLOC_CTX **talloc_stack; +}; + +/* + * In the single threaded case this is a pointer + * to the global talloc_stackframe. In the MT-case + * this is the pointer to the thread-specific key + * used to look up the per-thread talloc_stackframe + * pointer. + */ + +static void *global_ts; + +/* Variable to ensure TLS value is only initialized once. */ +static smb_thread_once_t ts_initialized = SMB_THREAD_ONCE_INIT; + +static void talloc_stackframe_init(void * unused) +{ + if (SMB_THREAD_CREATE_TLS("talloc_stackframe", global_ts)) { + smb_panic("talloc_stackframe_init create_tls failed"); + } +} + +static struct talloc_stackframe *talloc_stackframe_create(void) +{ +#if defined(PARANOID_MALLOC_CHECKER) +#ifdef malloc +#undef malloc +#endif +#endif + struct talloc_stackframe *ts = + (struct talloc_stackframe *)malloc(sizeof(struct talloc_stackframe)); +#if defined(PARANOID_MALLOC_CHECKER) +#define malloc(s) __ERROR_DONT_USE_MALLOC_DIRECTLY +#endif + + if (!ts) { + smb_panic("talloc_stackframe_init malloc failed"); + } + + ZERO_STRUCTP(ts); + + SMB_THREAD_ONCE(&ts_initialized, talloc_stackframe_init, NULL); + + if (SMB_THREAD_SET_TLS(global_ts, ts)) { + smb_panic("talloc_stackframe_init set_tls failed"); + } + return ts; +} static int talloc_pop(TALLOC_CTX *frame) { + struct talloc_stackframe *ts = + (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts); int i; - for (i=talloc_stacksize-1; i>0; i--) { - if (frame == talloc_stack[i]) { + for (i=ts->talloc_stacksize-1; i>0; i--) { + if (frame == ts->talloc_stack[i]) { break; } - talloc_free(talloc_stack[i]); + talloc_free(ts->talloc_stack[i]); + ts->talloc_stack[i] = NULL; } - talloc_stacksize = i; + ts->talloc_stack[i] = NULL; + ts->talloc_stacksize = i; return 0; } @@ -67,22 +121,27 @@ static int talloc_pop(TALLOC_CTX *frame) static TALLOC_CTX *talloc_stackframe_internal(size_t poolsize) { TALLOC_CTX **tmp, *top, *parent; + struct talloc_stackframe *ts = + (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts); - if (talloc_stack_arraysize < talloc_stacksize + 1) { - tmp = talloc_realloc(NULL, talloc_stack, TALLOC_CTX *, - talloc_stacksize + 1); + if (ts == NULL) { + ts = talloc_stackframe_create(); + } + + if (ts->talloc_stack_arraysize < ts->talloc_stacksize + 1) { + tmp = talloc_realloc(NULL, ts->talloc_stack, TALLOC_CTX *, + ts->talloc_stacksize + 1); if (tmp == NULL) { goto fail; } - talloc_stack = tmp; - talloc_stack_arraysize = talloc_stacksize + 1; + ts->talloc_stack = tmp; + ts->talloc_stack_arraysize = ts->talloc_stacksize + 1; } - if (talloc_stacksize == 0) { - parent = talloc_stack; - } - else { - parent = talloc_stack[talloc_stacksize-1]; + if (ts->talloc_stacksize == 0) { + parent = ts->talloc_stack; + } else { + parent = ts->talloc_stack[ts->talloc_stacksize-1]; } if (poolsize) { @@ -97,7 +156,7 @@ static TALLOC_CTX *talloc_stackframe_internal(size_t poolsize) talloc_set_destructor(top, talloc_pop); - talloc_stack[talloc_stacksize++] = top; + ts->talloc_stack[ts->talloc_stacksize++] = top; return top; fail: @@ -121,10 +180,14 @@ TALLOC_CTX *talloc_stackframe_pool(size_t poolsize) TALLOC_CTX *talloc_tos(void) { - if (talloc_stacksize == 0) { + struct talloc_stackframe *ts = + (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts); + + if (ts == NULL || ts->talloc_stacksize == 0) { talloc_stackframe(); + ts = (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts); DEBUG(0, ("no talloc stackframe around, leaking memory\n")); } - return talloc_stack[talloc_stacksize-1]; + return ts->talloc_stack[ts->talloc_stacksize-1]; } diff --git a/lib/util/talloc_stack.h b/lib/util/talloc_stack.h index bb22b8a029..777671164d 100644 --- a/lib/util/talloc_stack.h +++ b/lib/util/talloc_stack.h @@ -35,7 +35,7 @@ #ifndef _TALLOC_STACK_H #define _TALLOC_STACK_H -#include "../talloc/talloc.h" +#include "talloc.h" /* * Create a new talloc stack frame. diff --git a/lib/util/tests/genrand.c b/lib/util/tests/genrand.c index 5fe229c089..20a20ac7fa 100644 --- a/lib/util/tests/genrand.c +++ b/lib/util/tests/genrand.c @@ -40,6 +40,8 @@ static bool test_check_password_quality(struct torture_context *tctx) torture_assert(tctx, !check_password_quality("aaaaaaaaaaaa"), "same char password"); torture_assert(tctx, !check_password_quality("BLA"), "multiple upcases password"); torture_assert(tctx, !check_password_quality("123"), "digits only"); + torture_assert(tctx, !check_password_quality("matthiéu"), "not enough high symbols"); + torture_assert(tctx, check_password_quality("abcdééà çè"), "valid"); torture_assert(tctx, check_password_quality("A2e"), "valid"); torture_assert(tctx, check_password_quality("BA2eLi443"), "valid"); return true; diff --git a/lib/util/tests/parmlist.c b/lib/util/tests/parmlist.c new file mode 100644 index 0000000000..4b1d875715 --- /dev/null +++ b/lib/util/tests/parmlist.c @@ -0,0 +1,106 @@ +/* + Unix SMB/CIFS implementation. + + parmlist testing + + Copyright (C) Jelmer Vernooij 2009 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "../lib/util/parmlist.h" + +static bool test_get_int(struct torture_context *tctx) +{ + struct parmlist *pctx = talloc_zero(tctx, struct parmlist); + parmlist_set_string(pctx, "bar", "3"); + parmlist_set_string(pctx, "notint", "bla"); + torture_assert_int_equal(tctx, 3, parmlist_get_int(pctx, "bar", 42), + "existing"); + torture_assert_int_equal(tctx, 42, parmlist_get_int(pctx, "foo", 42), + "default"); + torture_assert_int_equal(tctx, 0, parmlist_get_int(pctx, "notint", 42), + "Not an integer"); + return true; +} + +static bool test_get_string(struct torture_context *tctx) +{ + struct parmlist *pctx = talloc_zero(tctx, struct parmlist); + parmlist_set_string(pctx, "bar", "mystring"); + torture_assert_str_equal(tctx, "mystring", + parmlist_get_string(pctx, "bar", "bla"), "existing"); + torture_assert_str_equal(tctx, "bla", + parmlist_get_string(pctx, "foo", "bla"), "default"); + return true; +} + +static bool test_get(struct torture_context *tctx) +{ + struct parmlist *pctx = talloc_zero(tctx, struct parmlist); + struct parmlist_entry *e; + parmlist_set_string(pctx, "bar", "mystring"); + + e = parmlist_get(pctx, "bar"); + torture_assert(tctx, e != NULL, "entry"); + torture_assert_str_equal(tctx, e->key, "bar", "key"); + torture_assert_str_equal(tctx, e->value, "mystring", "value"); + + e = parmlist_get(pctx, "nonexistant"); + torture_assert(tctx, e == NULL, "nonexistant"); + return true; +} + +static bool test_get_bool(struct torture_context *tctx) +{ + struct parmlist *pctx = talloc_zero(tctx, struct parmlist); + parmlist_set_string(pctx, "bar", "true"); + parmlist_set_string(pctx, "gasoline", "invalid"); + + torture_assert(tctx, parmlist_get_bool(pctx, "bar", false), "set"); + torture_assert(tctx, !parmlist_get_bool(pctx, "foo", false), "default"); + torture_assert(tctx, !parmlist_get_bool(pctx, "gasoline", false), + "invalid"); + return true; +} + +static bool test_get_string_list(struct torture_context *tctx) +{ + struct parmlist *pctx = talloc_zero(tctx, struct parmlist); + const char **ret; + parmlist_set_string(pctx, "bar", "true, false"); + + ret = parmlist_get_string_list(pctx, "bar", NULL); + torture_assert_int_equal(tctx, str_list_length(ret), 2, "length"); + torture_assert_str_equal(tctx, "true", ret[0], "ret[0]"); + torture_assert_str_equal(tctx, "false", ret[1], "ret[1]"); + torture_assert(tctx, NULL == parmlist_get_string_list(pctx, "nonexistant", NULL), "nonexistant"); + + return true; +} + +struct torture_suite *torture_local_util_parmlist(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "PARMLIST"); + + torture_suite_add_simple_test(suite, "get_int", test_get_int); + torture_suite_add_simple_test(suite, "get_string", test_get_string); + torture_suite_add_simple_test(suite, "get", test_get); + torture_suite_add_simple_test(suite, "get_bool", test_get_bool); + torture_suite_add_simple_test(suite, "get_string_list", test_get_string_list); + + return suite; +} diff --git a/lib/util/tests/strlist.c b/lib/util/tests/strlist.c index 8605102954..a974f58184 100644 --- a/lib/util/tests/strlist.c +++ b/lib/util/tests/strlist.c @@ -4,6 +4,7 @@ util_strlist testing Copyright (C) Jelmer Vernooij 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009 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 @@ -21,28 +22,96 @@ #include "includes.h" #include "torture/torture.h" +#include "param/param.h" -static const char *test_lists_shell_strings[] = { - "", - "foo", - "foo bar", - "foo bar \"bla \"", - "foo \"\" bla", - "bla \"\"\"\" blie", - NULL +struct test_list_element { + const char *list_as_string; + const char *seperators; + const char *list[5]; +}; + +struct test_list_element test_lists_strings[] = { + { + .list_as_string = "", + .list = { NULL } + }, + { + .list_as_string = "foo", + .list = { "foo", NULL } + }, + { + .list_as_string = "foo bar", + .list = { "foo", "bar", NULL } + }, + { + .list_as_string = "foo bar", + .list = { "foo bar", NULL }, + .seperators = ";" + }, + { + .list_as_string = "\"foo bar\"", + .list = { "\"foo", "bar\"", NULL } + }, + { + .list_as_string = "\"foo bar\",comma\ttab", + .list = { "\"foo", "bar\"", "comma", "tab", NULL } + }, + { + .list_as_string = "\"foo bar\",comma;semicolon", + .list = { "\"foo bar\",comma", "semicolon", NULL }, + .seperators = ";" + } +}; + +struct test_list_element test_lists_shell_strings[] = { + { + .list_as_string = "", + .list = { NULL } + }, + { + .list_as_string = "foo", + .list = { "foo", NULL } + }, + { + .list_as_string = "foo bar", + .list = { "foo", "bar", NULL } + }, + { + .list_as_string = "foo bar", + .list = { "foo bar", NULL }, + .seperators = ";" + }, + { + .list_as_string = "\"foo bar\"", + .list = { "foo bar", NULL } + }, + { + .list_as_string = "foo bar \"bla \"", + .list = { "foo", "bar", "bla ", NULL } + }, + { + .list_as_string = "foo \"\" bla", + .list = { "foo", "", "bla", NULL }, + }, + { + .list_as_string = "bla \"\"\"\" blie", + .list = { "bla", "", "", "blie", NULL }, + } }; static bool test_lists_shell(struct torture_context *tctx, - const void *test_data) + const void *data) { - const char *data = (const char *)test_data; + const struct test_list_element *element = data; const char **ret1, **ret2, *tmp; bool match = true; TALLOC_CTX *mem_ctx = tctx; - ret1 = str_list_make_shell(mem_ctx, data, " "); - tmp = str_list_join_shell(mem_ctx, ret1, ' '); - ret2 = str_list_make_shell(mem_ctx, tmp, " "); + ret1 = str_list_make_shell(mem_ctx, element->list_as_string, element->seperators); + + torture_assert(tctx, ret1, "str_list_make_shell() must not return NULL"); + tmp = str_list_join_shell(mem_ctx, ret1, element->seperators ? *element->seperators : ' '); + ret2 = str_list_make_shell(mem_ctx, tmp, element->seperators); if ((ret1 == NULL || ret2 == NULL) && ret2 != ret1) { match = false; @@ -60,7 +129,25 @@ static bool test_lists_shell(struct torture_context *tctx, } torture_assert(tctx, match, talloc_asprintf(tctx, - "str_list_{make,join}_shell: Error double parsing, first run:\n%s\nSecond run: \n%s", data, tmp)); + "str_list_{make,join}_shell: Error double parsing, first run:\n%s\nSecond run: \n%s", element->list_as_string, tmp)); + torture_assert(tctx, str_list_equal(ret1, element->list), + talloc_asprintf(tctx, + "str_list_make_shell(%s) failed to create correct list", + element->list_as_string)); + + return true; +} + +static bool test_list_make(struct torture_context *tctx, const void *data) +{ + const struct test_list_element *element = data; + char **result; + result = str_list_make(tctx, element->list_as_string, element->seperators); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + torture_assert(tctx, str_list_equal((const char **)result, element->list), + talloc_asprintf(tctx, + "str_list_make(%s) failed to create correct list", + element->list_as_string)); return true; } @@ -87,17 +174,325 @@ static bool test_list_copy(struct torture_context *tctx) return true; } +static bool test_list_make_empty(struct torture_context *tctx) +{ + char **result; + + result = str_list_make_empty(tctx); + torture_assert(tctx, result, "str_list_make_empty() must not return NULL"); + torture_assert(tctx, result[0] == NULL, "first element in str_list_make_empty() result must be NULL"); + + result = str_list_make(tctx, NULL, NULL); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + torture_assert(tctx, result[0] == NULL, "first element in str_list_make(ctx, NULL, NULL) result must be NULL"); + + result = str_list_make(tctx, "", NULL); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + torture_assert(tctx, result[0] == NULL, "first element in str_list_make(ctx, "", NULL) result must be NULL"); + + return true; +} + +static bool test_list_make_single(struct torture_context *tctx) +{ + char **result; + + result = str_list_make_single(tctx, "foo"); + + torture_assert(tctx, result, "str_list_make_single() must not return NULL"); + torture_assert_str_equal(tctx, result[0], "foo", "element 0"); + torture_assert(tctx, result[1] == NULL, "second element in result must be NULL"); + + return true; +} + +static bool test_list_copy_const(struct torture_context *tctx) +{ + const char **result; + const char *list[] = { + "element_0", + "element_1", + "element_2", + "element_3", + NULL + }; + result = str_list_copy_const(tctx, list); + torture_assert(tctx, result, "str_list_copy() must not return NULL"); + torture_assert(tctx, str_list_equal(result, list), + "str_list_copy() failed"); + + return true; +} + +static bool test_list_length(struct torture_context *tctx) +{ + const char *list[] = { + "element_0", + "element_1", + "element_2", + "element_3", + NULL + }; + const char *list2[] = { + NULL + }; + torture_assert_int_equal(tctx, str_list_length(list), 4, + "str_list_length() failed"); + + torture_assert_int_equal(tctx, str_list_length(list2), 0, + "str_list_length() failed"); + + torture_assert_int_equal(tctx, str_list_length(NULL), 0, + "str_list_length() failed"); + + return true; +} + +static bool test_list_add(struct torture_context *tctx) +{ + char **result, **result2; + const char *list[] = { + "element_0", + "element_1", + "element_2", + "element_3", + NULL + }; + result = str_list_make(tctx, "element_0, element_1, element_2", NULL); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + result2 = str_list_add(result, "element_3"); + torture_assert(tctx, result2, "str_list_add() must not return NULL"); + torture_assert(tctx, str_list_equal(result2, list), + "str_list_add() failed"); + + return true; +} + +static bool test_list_add_const(struct torture_context *tctx) +{ + char **result, **result2; + const char *list[] = { + "element_0", + "element_1", + "element_2", + "element_3", + NULL + }; + result = str_list_make(tctx, "element_0, element_1, element_2", NULL); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + result2 = str_list_add_const(result, "element_3"); + torture_assert(tctx, result2, "str_list_add_const() must not return NULL"); + torture_assert(tctx, str_list_equal(result2, list), + "str_list_add() failed"); + + return true; +} + +static bool test_list_remove(struct torture_context *tctx) +{ + char **result; + const char *list[] = { + "element_0", + "element_1", + "element_3", + NULL + }; + result = str_list_make(tctx, "element_0, element_1, element_2, element_3", NULL); + torture_assert(tctx, result, "str_list_make() must not return NULL"); + str_list_remove(result, "element_2"); + torture_assert(tctx, str_list_equal(result, list), + "str_list_remove() failed"); + + return true; +} + +static bool test_list_check(struct torture_context *tctx) +{ + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + torture_assert(tctx, str_list_check(list, "element_1"), + "str_list_check() failed"); + + return true; +} + +static bool test_list_check_ci(struct torture_context *tctx) +{ + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + torture_assert(tctx, str_list_check_ci(list, "ELEMENT_1"), + "str_list_check_ci() failed"); + + return true; +} + +static bool test_list_unique(struct torture_context *tctx) +{ + char **result; + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + const char *list_dup[] = { + "element_0", + "element_1", + "element_2", + "element_0", + "element_2", + "element_1", + "element_1", + "element_2", + NULL + }; + result = str_list_copy(tctx, list_dup); + /* We must copy the list, as str_list_unique does a talloc_realloc() on it's parameter */ + result = str_list_unique(result); + torture_assert(tctx, result, "str_list_unique() must not return NULL"); + + torture_assert(tctx, str_list_equal(list, result), + "str_list_unique() failed"); + + return true; +} + +static bool test_list_unique_2(struct torture_context *tctx) +{ + int i; + int count, num_dups; + const char **result; + const char **list = (const char **)str_list_make_empty(tctx); + const char **list_dup = (const char **)str_list_make_empty(tctx); + + count = lp_parm_int(tctx->lp_ctx, NULL, "list_unique", "count", 9); + num_dups = lp_parm_int(tctx->lp_ctx, NULL, "list_unique", "dups", 7); + torture_comment(tctx, "test_list_unique_2() with %d elements and %d dups\n", count, num_dups); + + for (i = 0; i < count; i++) { + list = str_list_add_const(list, (const char *)talloc_asprintf(tctx, "element_%03d", i)); + } + + for (i = 0; i < num_dups; i++) { + list_dup = str_list_append(list_dup, list); + } + + result = (const char **)str_list_copy(tctx, list_dup); + /* We must copy the list, as str_list_unique does a talloc_realloc() on it's parameter */ + result = str_list_unique(result); + torture_assert(tctx, result, "str_list_unique() must not return NULL"); + + torture_assert(tctx, str_list_equal(list, result), + "str_list_unique() failed"); + + return true; +} + +static bool test_list_append(struct torture_context *tctx) +{ + char **result; + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + const char *list2[] = { + "element_3", + "element_4", + "element_5", + NULL + }; + const char *list_combined[] = { + "element_0", + "element_1", + "element_2", + "element_3", + "element_4", + "element_5", + NULL + }; + result = str_list_copy(tctx, list); + torture_assert(tctx, result, "str_list_copy() must not return NULL"); + result = str_list_append(result, list2); + torture_assert(tctx, result, "str_list_append() must not return NULL"); + torture_assert(tctx, str_list_equal(list_combined, result), + "str_list_unique() failed"); + + return true; +} + +static bool test_list_append_const(struct torture_context *tctx) +{ + char **result; + const char *list[] = { + "element_0", + "element_1", + "element_2", + NULL + }; + const char *list2[] = { + "element_3", + "element_4", + "element_5", + NULL + }; + const char *list_combined[] = { + "element_0", + "element_1", + "element_2", + "element_3", + "element_4", + "element_5", + NULL + }; + result = str_list_copy(tctx, list); + torture_assert(tctx, result, "str_list_copy() must not return NULL"); + result = str_list_append_const(result, list2); + torture_assert(tctx, result, "str_list_append_const() must not return NULL"); + torture_assert(tctx, str_list_equal(list_combined, result), + "str_list_unique() failed"); + + return true; +} + struct torture_suite *torture_local_util_strlist(TALLOC_CTX *mem_ctx) { struct torture_suite *suite = torture_suite_create(mem_ctx, "STRLIST"); int i; - for (i = 0; test_lists_shell_strings[i]; i++) { + for (i = 0; i < ARRAY_SIZE(test_lists_shell_strings); i++) { torture_suite_add_simple_tcase_const(suite, "lists_shell", test_lists_shell, &test_lists_shell_strings[i]); } + for (i = 0; i < ARRAY_SIZE(test_lists_strings); i++) { + torture_suite_add_simple_tcase_const(suite, "lists", + test_list_make, &test_lists_strings[i]); + } + torture_suite_add_simple_test(suite, "list_copy", test_list_copy); + torture_suite_add_simple_test(suite, "make_empty", test_list_make_empty); + torture_suite_add_simple_test(suite, "make_single", test_list_make_single); + torture_suite_add_simple_test(suite, "list_copy_const", test_list_copy_const); + torture_suite_add_simple_test(suite, "list_length", test_list_length); + torture_suite_add_simple_test(suite, "list_add", test_list_add); + torture_suite_add_simple_test(suite, "list_add_const", test_list_add_const); + torture_suite_add_simple_test(suite, "list_remove", test_list_remove); + torture_suite_add_simple_test(suite, "list_check", test_list_check); + torture_suite_add_simple_test(suite, "list_check_ci", test_list_check_ci); + torture_suite_add_simple_test(suite, "list_unique", test_list_unique); + torture_suite_add_simple_test(suite, "list_unique_2", test_list_unique_2); + torture_suite_add_simple_test(suite, "list_append", test_list_append); + torture_suite_add_simple_test(suite, "list_append_const", test_list_append_const); return suite; } diff --git a/lib/util/tests/time.c b/lib/util/tests/time.c index b7cb608611..d08a4e79d1 100644 --- a/lib/util/tests/time.c +++ b/lib/util/tests/time.c @@ -44,7 +44,17 @@ static bool test_http_timestring(struct torture_context *tctx) { const char *start = "Thu, 01 Jan 1970"; char *result; - result = http_timestring(tctx, 42); + /* + * Correct test for negative UTC offset. Without the correction, the + * test fails when run on hosts with negative UTC offsets, as the date + * returned is back in 1969 (pre-epoch). + */ + time_t now = time(NULL); + struct tm local = *localtime(&now); + struct tm gmt = *gmtime(&now); + time_t utc_offset = mktime(&local) - mktime(&gmt); + + result = http_timestring(tctx, 42 - (utc_offset < 0 ? utc_offset : 0)); torture_assert(tctx, !strncmp(start, result, strlen(start)), result); torture_assert_str_equal(tctx, "never", @@ -55,7 +65,18 @@ static bool test_http_timestring(struct torture_context *tctx) static bool test_timestring(struct torture_context *tctx) { const char *start = "Thu Jan 1"; - char *result = timestring(tctx, 42); + char *result; + /* + * Correct test for negative UTC offset. Without the correction, the + * test fails when run on hosts with negative UTC offsets, as the date + * returned is back in 1969 (pre-epoch). + */ + time_t now = time(NULL); + struct tm local = *localtime(&now); + struct tm gmt = *gmtime(&now); + time_t utc_offset = mktime(&local) - mktime(&gmt); + + result = timestring(tctx, 42 - (utc_offset < 0 ? utc_offset : 0)); torture_assert(tctx, !strncmp(start, result, strlen(start)), result); return true; diff --git a/lib/util/tevent_ntstatus.c b/lib/util/tevent_ntstatus.c index 6aa576da66..d6cb0affd9 100644 --- a/lib/util/tevent_ntstatus.c +++ b/lib/util/tevent_ntstatus.c @@ -49,3 +49,13 @@ bool tevent_req_is_nterror(struct tevent_req *req, NTSTATUS *status) } return true; } + +NTSTATUS tevent_req_simple_recv_ntstatus(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + return NT_STATUS_OK; +} diff --git a/lib/util/tevent_ntstatus.h b/lib/util/tevent_ntstatus.h index acfb903961..22fe9188d0 100644 --- a/lib/util/tevent_ntstatus.h +++ b/lib/util/tevent_ntstatus.h @@ -27,5 +27,6 @@ bool tevent_req_nterror(struct tevent_req *req, NTSTATUS status); bool tevent_req_is_nterror(struct tevent_req *req, NTSTATUS *pstatus); +NTSTATUS tevent_req_simple_recv_ntstatus(struct tevent_req *req); #endif diff --git a/lib/util/tevent_unix.c b/lib/util/tevent_unix.c index b89d5cd4d4..0a8c4c6b30 100644 --- a/lib/util/tevent_unix.c +++ b/lib/util/tevent_unix.c @@ -3,17 +3,21 @@ Wrap unix errno around tevent_req Copyright (C) Volker Lendecke 2009 - 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 3 of the License, or - (at your option) any later version. + ** NOTE! The following LGPL license applies to the tevent_unix + ** helper library. This does NOT imply that all of Samba is released + ** under the LGPL - This program is distributed in the hope that it will be useful, + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ diff --git a/lib/util/tevent_unix.h b/lib/util/tevent_unix.h index dc3ffaef33..bc2cea9199 100644 --- a/lib/util/tevent_unix.h +++ b/lib/util/tevent_unix.h @@ -3,17 +3,21 @@ Wrap unix errno around tevent_req Copyright (C) Volker Lendecke 2009 - 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 3 of the License, or - (at your option) any later version. + ** NOTE! The following LGPL license applies to the tevent_unix + ** helper library. This does NOT imply that all of Samba is released + ** under the LGPL - This program is distributed in the hope that it will be useful, + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ diff --git a/lib/util/time.m4 b/lib/util/time.m4 index f61ae4cd25..675e20129f 100644 --- a/lib/util/time.m4 +++ b/lib/util/time.m4 @@ -3,7 +3,7 @@ AC_TRY_RUN([ #include <sys/time.h> #include <unistd.h> main() { struct timeval tv; exit(gettimeofday(&tv, NULL));}], - samba_cv_HAVE_GETTIMEOFDAY_TZ=yes,samba_cv_HAVE_GETTIMEOFDAY_TZ=no,samba_cv_HAVE_GETTIMEOFDAY_TZ=cross)]) + samba_cv_HAVE_GETTIMEOFDAY_TZ=yes,samba_cv_HAVE_GETTIMEOFDAY_TZ=no,samba_cv_HAVE_GETTIMEOFDAY_TZ=yes)]) if test x"$samba_cv_HAVE_GETTIMEOFDAY_TZ" = x"yes"; then AC_DEFINE(HAVE_GETTIMEOFDAY_TZ,1,[Whether gettimeofday() is available]) fi diff --git a/lib/util/util.c b/lib/util/util.c index af682ac15f..d1297a09dd 100644 --- a/lib/util/util.c +++ b/lib/util/util.c @@ -133,8 +133,13 @@ _PUBLIC_ bool directory_create_or_exist(const char *dname, uid_t uid, umask(old_umask); return false; } - if ((st.st_uid != uid) || - ((st.st_mode & 0777) != dir_perms)) { + if (st.st_uid != uid && !uwrap_enabled()) { + DEBUG(0, ("invalid ownership on directory " + "%s\n", dname)); + umask(old_umask); + return false; + } + if ((st.st_mode & 0777) != dir_perms) { DEBUG(0, ("invalid permissions on directory " "%s\n", dname)); umask(old_umask); @@ -146,37 +151,6 @@ _PUBLIC_ bool directory_create_or_exist(const char *dname, uid_t uid, /** - Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, - else - if SYSV use O_NDELAY - if BSD use FNDELAY -**/ - -_PUBLIC_ int set_blocking(int fd, bool set) -{ - int val; -#ifdef O_NONBLOCK -#define FLAG_TO_SET O_NONBLOCK -#else -#ifdef SYSV -#define FLAG_TO_SET O_NDELAY -#else /* BSD */ -#define FLAG_TO_SET FNDELAY -#endif -#endif - - if((val = fcntl(fd, F_GETFL, 0)) == -1) - return -1; - if(set) /* Turn blocking on - ie. clear nonblock flag */ - val &= ~FLAG_TO_SET; - else - val |= FLAG_TO_SET; - return fcntl( fd, F_SETFL, val); -#undef FLAG_TO_SET -} - - -/** Sleep for a specified number of milliseconds. **/ @@ -296,15 +270,13 @@ static void _dump_data(int level, const uint8_t *buf, int len, bool omit_zero_bytes) { int i=0; - const uint8_t empty[16]; + static const uint8_t empty[16] = { 0, }; bool skipped = false; if (len<=0) return; if (!DEBUGLVL(level)) return; - memset(&empty, '\0', 16); - for (i=0;i<len;) { if (i%16 == 0) { @@ -695,41 +667,6 @@ _PUBLIC_ char *hex_encode_talloc(TALLOC_CTX *mem_ctx, const unsigned char *buff_ } /** - Unescape a URL encoded string, in place. -**/ - -_PUBLIC_ void rfc1738_unescape(char *buf) -{ - char *p=buf; - - while (p && *p && (p=strchr(p,'%'))) { - int c1 = p[1]; - int c2 = p[2]; - - if (c1 >= '0' && c1 <= '9') - c1 = c1 - '0'; - else if (c1 >= 'A' && c1 <= 'F') - c1 = 10 + c1 - 'A'; - else if (c1 >= 'a' && c1 <= 'f') - c1 = 10 + c1 - 'a'; - else {p++; continue;} - - if (c2 >= '0' && c2 <= '9') - c2 = c2 - '0'; - else if (c2 >= 'A' && c2 <= 'F') - c2 = 10 + c2 - 'A'; - else if (c2 >= 'a' && c2 <= 'f') - c2 = 10 + c2 - 'a'; - else {p++; continue;} - - *p = (c1<<4) | c2; - - memmove(p+1, p+3, strlen(p+3)+1); - p++; - } -} - -/** varient of strcmp() that handles NULL ptrs **/ _PUBLIC_ int strcmp_safe(const char *s1, const char *s2) diff --git a/lib/util/util.h b/lib/util/util.h index defef127d9..159f812d98 100644 --- a/lib/util/util.h +++ b/lib/util/util.h @@ -21,8 +21,6 @@ #ifndef _SAMBA_UTIL_H_ #define _SAMBA_UTIL_H_ -#include <netinet/in.h> - #if _SAMBA_BUILD_ == 4 #include "../lib/util/charset/charset.h" #endif @@ -192,6 +190,16 @@ _PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const c */ _PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len); +/** + * Generate an array of unique text strings all of the same length. + * The returned strings will be allocated. + * Returns NULL if the number of unique combinations cannot be created. + * + * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#., + */ +_PUBLIC_ char** generate_unique_strs(TALLOC_CTX *mem_ctx, size_t len, + uint32_t num); + /* The following definitions come from lib/util/dprintf.c */ #if _SAMBA_BUILD_ == 4 @@ -299,6 +307,31 @@ _PUBLIC_ void all_string_sub(char *s,const char *pattern,const char *insert, siz **/ _PUBLIC_ void rfc1738_unescape(char *buf); + +/** + * rfc1738_escape + * Returns a static buffer that contains the RFC + * 1738 compliant, escaped version of the given url. (escapes unsafe and % characters) + **/ +_PUBLIC_ char *rfc1738_escape(TALLOC_CTX *mem_ctx, const char *url); + +/** + * rfc1738_escape_unescaped + * + * Returns a static buffer that contains + * the RFC 1738 compliant, escaped version of the given url (escapes unsafe chars only) + **/ +_PUBLIC_ char *rfc1738_escape_unescaped(TALLOC_CTX *mem_ctx, const char *url); + +/** + * rfc1738_escape_part + * Returns a static buffer that contains the RFC + * 1738 compliant, escaped version of the given url segment. (escapes + * unsafe, reserved and % chars) It would mangle the :// in http://, + * and mangle paths (because of /). + **/ +_PUBLIC_ char *rfc1738_escape_part(TALLOC_CTX *mem_ctx, const char *url); + /** format a string into length-prefixed dotted domain format, as used in NBT and in some ADS structures @@ -397,18 +430,30 @@ _PUBLIC_ bool strequal(const char *s1, const char *s2); #endif /** + build an empty (only NULL terminated) list of strings (for expansion with str_list_add() etc) +*/ +_PUBLIC_ char **str_list_make_empty(TALLOC_CTX *mem_ctx); + +/** + place the only element 'entry' into a new, NULL terminated string list +*/ +_PUBLIC_ char **str_list_make_single(TALLOC_CTX *mem_ctx, + const char *entry); + +/** build a null terminated list of strings from a input string and a separator list. The separator list must contain characters less than or equal to 0x2f for this to work correctly on multi-byte strings */ -_PUBLIC_ char **str_list_make(TALLOC_CTX *mem_ctx, const char *string, const char *sep); +_PUBLIC_ char **str_list_make(TALLOC_CTX *mem_ctx, const char *string, + const char *sep); /** * build a null terminated list of strings from an argv-like input string * Entries are seperated by spaces and can be enclosed by quotes. * Does NOT support escaping */ -_PUBLIC_ const char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *string, const char *sep); +_PUBLIC_ char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *string, const char *sep); /** * join a list back to one string @@ -453,6 +498,43 @@ _PUBLIC_ bool str_list_check(const char **list, const char *s); return true if a string is in a list, case insensitively */ _PUBLIC_ bool str_list_check_ci(const char **list, const char *s); +/** + append one list to another - expanding list1 +*/ +_PUBLIC_ const char **str_list_append(const char **list1, + const char * const *list2); + +/** + remove duplicate elements from a list +*/ +_PUBLIC_ const char **str_list_unique(const char **list); + +/* + very useful when debugging complex list related code + */ +_PUBLIC_ void str_list_show(const char **list); + + +/** + append one list to another - expanding list1 + this assumes the elements of list2 are const pointers, so we can re-use them +*/ +_PUBLIC_ const char **str_list_append_const(const char **list1, + const char **list2); + +/** + add an entry to a string list + this assumes s will not change +*/ +_PUBLIC_ const char **str_list_add_const(const char **list, const char *s); + +/** + copy a string list + this assumes list will not change +*/ +_PUBLIC_ const char **str_list_copy_const(TALLOC_CTX *mem_ctx, + const char **list); + /* The following definitions come from lib/util/util_file.c */ @@ -771,4 +853,17 @@ bool unmap_file(void *start, size_t size); void print_asc(int level, const uint8_t *buf,int len); +/** + * Add an id to an array of ids. + * + * num should be a pointer to an integer that holds the current + * number of elements in ids. It will be updated by this function. + */ + +bool add_uid_to_array_unique(TALLOC_CTX *mem_ctx, uid_t uid, + uid_t **uids, size_t *num_uids); +bool add_gid_to_array_unique(TALLOC_CTX *mem_ctx, gid_t gid, + gid_t **gids, size_t *num_gids); + + #endif /* _SAMBA_UTIL_H_ */ diff --git a/lib/util/util_file.c b/lib/util/util_file.c index 0275e78c54..7466004e5c 100644 --- a/lib/util/util_file.c +++ b/lib/util/util_file.c @@ -380,6 +380,7 @@ _PUBLIC_ bool file_save(const char *fname, const void *packet, size_t length) return false; } if (write(fd, packet, length) != (size_t)length) { + close(fd); return false; } close(fd); diff --git a/lib/util/util_id.c b/lib/util/util_id.c new file mode 100644 index 0000000000..8744ce4e4e --- /dev/null +++ b/lib/util/util_id.c @@ -0,0 +1,88 @@ +/* + Unix SMB/CIFS implementation. + + Helper routines for uid and gid arrays + + Copyright (C) Guenther Deschner 2009 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/**************************************************************************** + Add a gid to an array of gids if it's not already there. +****************************************************************************/ + +bool add_gid_to_array_unique(TALLOC_CTX *mem_ctx, gid_t gid, + gid_t **gids, size_t *num_gids) +{ + int i; + + if ((*num_gids != 0) && (*gids == NULL)) { + /* + * A former call to this routine has failed to allocate memory + */ + return false; + } + + for (i=0; i<*num_gids; i++) { + if ((*gids)[i] == gid) { + return true; + } + } + + *gids = talloc_realloc(mem_ctx, *gids, gid_t, *num_gids+1); + if (*gids == NULL) { + *num_gids = 0; + return false; + } + + (*gids)[*num_gids] = gid; + *num_gids += 1; + return true; +} + +/**************************************************************************** + Add a uid to an array of uids if it's not already there. +****************************************************************************/ + +bool add_uid_to_array_unique(TALLOC_CTX *mem_ctx, uid_t uid, + uid_t **uids, size_t *num_uids) +{ + int i; + + if ((*num_uids != 0) && (*uids == NULL)) { + /* + * A former call to this routine has failed to allocate memory + */ + return false; + } + + for (i=0; i<*num_uids; i++) { + if ((*uids)[i] == uid) { + return true; + } + } + + *uids = talloc_realloc(mem_ctx, *uids, uid_t, *num_uids+1); + if (*uids == NULL) { + *num_uids = 0; + return false; + } + + (*uids)[*num_uids] = uid; + *num_uids += 1; + return true; +} diff --git a/lib/util/util_ldb.c b/lib/util/util_ldb.c index c11b6879d2..ac1e11566e 100644 --- a/lib/util/util_ldb.c +++ b/lib/util/util_ldb.c @@ -130,3 +130,99 @@ char *wrap_casefold(void *context, void *mem_ctx, const char *s, size_t n) } + +/* + search the LDB for a single record, with the extended_dn control + return LDB_SUCCESS on success, or an ldb error code on error + + if the search returns 0 entries, return LDB_ERR_NO_SUCH_OBJECT + if the search returns more than 1 entry, return LDB_ERR_CONSTRAINT_VIOLATION +*/ +int gendb_search_single_extended_dn(struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *basedn, + enum ldb_scope scope, + struct ldb_message **msg, + const char * const *attrs, + const char *format, ...) +{ + va_list ap; + int ret; + struct ldb_request *req; + char *filter; + TALLOC_CTX *tmp_ctx; + struct ldb_result *res; + struct ldb_extended_dn_control *ctrl; + + tmp_ctx = talloc_new(mem_ctx); + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + return LDB_ERR_OPERATIONS_ERROR; + } + + va_start(ap, format); + filter = talloc_vasprintf(tmp_ctx, format, ap); + va_end(ap); + + if (filter == NULL) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_build_search_req(&req, ldb, tmp_ctx, + basedn, + scope, + filter, + attrs, + NULL, + res, + ldb_search_default_callback, + NULL); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + ctrl = talloc(tmp_ctx, struct ldb_extended_dn_control); + if (ctrl == NULL) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + ctrl->type = 1; + + ret = ldb_request_add_control(req, LDB_CONTROL_EXTENDED_DN_OID, true, ctrl); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_request(ldb, req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + if (res->count == 0) { + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_OBJECT; + } + + if (res->count > 1) { + /* the function is only supposed to return a single entry */ + DEBUG(0,(__location__ ": More than one return for baseDN %s filter %s\n", + ldb_dn_get_linearized(basedn), filter)); + talloc_free(tmp_ctx); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + + *msg = talloc_steal(mem_ctx, res->msgs[0]); + + talloc_free(tmp_ctx); + + return LDB_SUCCESS; +} diff --git a/lib/util/util_ldb.h b/lib/util/util_ldb.h index f9eb028916..4575c6565a 100644 --- a/lib/util/util_ldb.h +++ b/lib/util/util_ldb.h @@ -26,4 +26,12 @@ int gendb_search_dn(struct ldb_context *ldb, int gendb_add_ldif(struct ldb_context *ldb, const char *ldif_string); char *wrap_casefold(void *context, void *mem_ctx, const char *s, size_t n); +int gendb_search_single_extended_dn(struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *basedn, + enum ldb_scope scope, + struct ldb_message **msg, + const char * const *attrs, + const char *format, ...) PRINTF_ATTRIBUTE(7,8); + #endif /* __LIB_UTIL_UTIL_LDB_H__ */ diff --git a/lib/util/util_net.c b/lib/util/util_net.c index d1dadc2494..0ce495e57c 100644 --- a/lib/util/util_net.c +++ b/lib/util/util_net.c @@ -3,10 +3,11 @@ Samba utility functions Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 Copyright (C) Andrew Tridgell 1992-1998 - Copyright (C) Jeremy Allison 2001-2007 + Copyright (C) Jeremy Allison 1992-2007 Copyright (C) Simo Sorce 2001 Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003. Copyright (C) James J Myers 2003 + Copyright (C) Tim Potter 2000-2001 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 @@ -28,6 +29,17 @@ #include "system/filesys.h" #undef strcasecmp +/******************************************************************* + Set an address to INADDR_ANY. +******************************************************************/ + +void zero_sockaddr(struct sockaddr_storage *pss) +{ + memset(pss, '\0', sizeof(*pss)); + /* Ensure we're at least a valid sockaddr-storage. */ + pss->ss_family = AF_INET; +} + /** * Wrap getaddrinfo... */ @@ -59,6 +71,110 @@ bool interpret_string_addr_internal(struct addrinfo **ppres, return true; } +/******************************************************************* + Map a text hostname or IP address (IPv4 or IPv6) into a + struct sockaddr_storage. Takes a flag which allows it to + prefer an IPv4 address (needed for DC's). +******************************************************************/ + +static bool interpret_string_addr_pref(struct sockaddr_storage *pss, + const char *str, + int flags, + bool prefer_ipv4) +{ + struct addrinfo *res = NULL; +#if defined(HAVE_IPV6) + char addr[INET6_ADDRSTRLEN]; + unsigned int scope_id = 0; + + if (strchr_m(str, ':')) { + char *p = strchr_m(str, '%'); + + /* + * Cope with link-local. + * This is IP:v6:addr%ifname. + */ + + if (p && (p > str) && ((scope_id = if_nametoindex(p+1)) != 0)) { + strlcpy(addr, str, + MIN(PTR_DIFF(p,str)+1, + sizeof(addr))); + str = addr; + } + } +#endif + + zero_sockaddr(pss); + + if (!interpret_string_addr_internal(&res, str, flags|AI_ADDRCONFIG)) { + return false; + } + if (!res) { + return false; + } + + if (prefer_ipv4) { + struct addrinfo *p; + + for (p = res; p; p = p->ai_next) { + if (p->ai_family == AF_INET) { + memcpy(pss, p->ai_addr, p->ai_addrlen); + break; + } + } + if (p == NULL) { + /* Copy the first sockaddr. */ + memcpy(pss, res->ai_addr, res->ai_addrlen); + } + } else { + /* Copy the first sockaddr. */ + memcpy(pss, res->ai_addr, res->ai_addrlen); + } + +#if defined(HAVE_IPV6) + if (pss->ss_family == AF_INET6 && scope_id) { + struct sockaddr_in6 *ps6 = (struct sockaddr_in6 *)pss; + if (IN6_IS_ADDR_LINKLOCAL(&ps6->sin6_addr) && + ps6->sin6_scope_id == 0) { + ps6->sin6_scope_id = scope_id; + } + } +#endif + + freeaddrinfo(res); + return true; +} + +/******************************************************************* + Map a text hostname or IP address (IPv4 or IPv6) into a + struct sockaddr_storage. Address agnostic version. +******************************************************************/ + +bool interpret_string_addr(struct sockaddr_storage *pss, + const char *str, + int flags) +{ + return interpret_string_addr_pref(pss, + str, + flags, + false); +} + +/******************************************************************* + Map a text hostname or IP address (IPv4 or IPv6) into a + struct sockaddr_storage. Version that prefers IPv4. +******************************************************************/ + +bool interpret_string_addr_prefer_ipv4(struct sockaddr_storage *pss, + const char *str, + int flags) +{ + return interpret_string_addr_pref(pss, + str, + flags, + true); +} + /** * Interpret an internet address or name into an IP address in 4 byte form. * RETURNS IN NETWORK BYTE ORDER (big endian). diff --git a/lib/util/util_net.h b/lib/util/util_net.h new file mode 100644 index 0000000000..6eacfc395f --- /dev/null +++ b/lib/util/util_net.h @@ -0,0 +1,46 @@ +/* + Unix SMB/CIFS implementation. + Utility functions for Samba + Copyright (C) Andrew Tridgell 1992-1999 + Copyright (C) Jelmer Vernooij 2005 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SAMBA_UTIL_NET_H_ +#define _SAMBA_UTIL_NET_H_ + +#include "system/network.h" + +/* The following definitions come from lib/util/util_net.c */ + +void zero_sockaddr(struct sockaddr_storage *pss); + +bool interpret_string_addr_internal(struct addrinfo **ppres, + const char *str, int flags); + +bool interpret_string_addr(struct sockaddr_storage *pss, + const char *str, + int flags); + +/******************************************************************* + Map a text hostname or IP address (IPv4 or IPv6) into a + struct sockaddr_storage. Version that prefers IPv4. +******************************************************************/ + +bool interpret_string_addr_prefer_ipv4(struct sockaddr_storage *pss, + const char *str, + int flags); + +#endif /* _SAMBA_UTIL_NET_H_ */ diff --git a/lib/util/util_strlist.c b/lib/util/util_strlist.c index b069a11e38..8d69eef233 100644 --- a/lib/util/util_strlist.c +++ b/lib/util/util_strlist.c @@ -29,6 +29,45 @@ */ /** + build an empty (only NULL terminated) list of strings (for expansion with str_list_add() etc) +*/ +_PUBLIC_ char **str_list_make_empty(TALLOC_CTX *mem_ctx) +{ + char **ret = NULL; + + ret = talloc_array(mem_ctx, char *, 1); + if (ret == NULL) { + return NULL; + } + + ret[0] = NULL; + + return ret; +} + +/** + place the only element 'entry' into a new, NULL terminated string list +*/ +_PUBLIC_ char **str_list_make_single(TALLOC_CTX *mem_ctx, const char *entry) +{ + char **ret = NULL; + + ret = talloc_array(mem_ctx, char *, 2); + if (ret == NULL) { + return NULL; + } + + ret[0] = talloc_strdup(ret, entry); + if (!ret[0]) { + talloc_free(ret); + return NULL; + } + ret[1] = NULL; + + return ret; +} + +/** build a null terminated list of strings from a input string and a separator list. The separator list must contain characters less than or equal to 0x2f for this to work correctly on multi-byte strings @@ -56,7 +95,8 @@ _PUBLIC_ char **str_list_make(TALLOC_CTX *mem_ctx, const char *string, const cha continue; } - ret2 = talloc_realloc(mem_ctx, ret, char *, num_elements+2); + ret2 = talloc_realloc(mem_ctx, ret, char *, + num_elements+2); if (ret2 == NULL) { talloc_free(ret); return NULL; @@ -83,12 +123,12 @@ _PUBLIC_ char **str_list_make(TALLOC_CTX *mem_ctx, const char *string, const cha * Entries are seperated by spaces and can be enclosed by quotes. * Does NOT support escaping */ -_PUBLIC_ const char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *string, const char *sep) +_PUBLIC_ char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *string, const char *sep) { int num_elements = 0; - const char **ret = NULL; + char **ret = NULL; - ret = talloc_array(mem_ctx, const char *, 1); + ret = talloc_array(mem_ctx, char *, 1); if (ret == NULL) { return NULL; } @@ -99,7 +139,7 @@ _PUBLIC_ const char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *strin while (string && *string) { size_t len = strcspn(string, sep); char *element; - const char **ret2; + char **ret2; if (len == 0) { string += strspn(string, sep); @@ -121,7 +161,7 @@ _PUBLIC_ const char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *strin return NULL; } - ret2 = talloc_realloc(mem_ctx, ret, const char *, num_elements+2); + ret2 = talloc_realloc(mem_ctx, ret, char *, num_elements+2); if (ret2 == NULL) { talloc_free(ret); return NULL; @@ -187,7 +227,7 @@ _PUBLIC_ char *str_list_join_shell(TALLOC_CTX *mem_ctx, const char **list, char /** return the number of elements in a string list */ -_PUBLIC_ size_t str_list_length(const char * const*list) +_PUBLIC_ size_t str_list_length(const char * const *list) { size_t ret; for (ret=0;list && list[ret];ret++) /* noop */ ; @@ -308,3 +348,140 @@ _PUBLIC_ bool str_list_check_ci(const char **list, const char *s) } +/** + append one list to another - expanding list1 +*/ +_PUBLIC_ const char **str_list_append(const char **list1, + const char * const *list2) +{ + size_t len1 = str_list_length(list1); + size_t len2 = str_list_length(list2); + const char **ret; + int i; + + ret = talloc_realloc(NULL, list1, const char *, len1+len2+1); + if (ret == NULL) return NULL; + + for (i=len1;i<len1+len2;i++) { + ret[i] = talloc_strdup(ret, list2[i-len1]); + if (ret[i] == NULL) { + return NULL; + } + } + ret[i] = NULL; + + return ret; +} + +static int list_cmp(const char **el1, const char **el2) +{ + return strcmp(*el1, *el2); +} + +/* + return a list that only contains the unique elements of a list, + removing any duplicates + */ +_PUBLIC_ const char **str_list_unique(const char **list) +{ + size_t len = str_list_length(list); + const char **list2; + int i, j; + if (len < 2) { + return list; + } + list2 = (const char **)talloc_memdup(list, list, + sizeof(list[0])*(len+1)); + qsort(list2, len, sizeof(list2[0]), QSORT_CAST list_cmp); + list[0] = list2[0]; + for (i=j=1;i<len;i++) { + if (strcmp(list2[i], list[j-1]) != 0) { + list[j] = list2[i]; + j++; + } + } + list[j] = NULL; + list = talloc_realloc(NULL, list, const char *, j + 1); + talloc_free(list2); + return list; +} + +/* + very useful when debugging complex list related code + */ +_PUBLIC_ void str_list_show(const char **list) +{ + int i; + DEBUG(0,("{ ")); + for (i=0;list && list[i];i++) { + DEBUG(0,("\"%s\", ", list[i])); + } + DEBUG(0,("}\n")); +} + + + +/** + append one list to another - expanding list1 + this assumes the elements of list2 are const pointers, so we can re-use them +*/ +_PUBLIC_ const char **str_list_append_const(const char **list1, + const char **list2) +{ + size_t len1 = str_list_length(list1); + size_t len2 = str_list_length(list2); + const char **ret; + int i; + + ret = talloc_realloc(NULL, list1, const char *, len1+len2+1); + if (ret == NULL) return NULL; + + for (i=len1;i<len1+len2;i++) { + ret[i] = list2[i-len1]; + } + ret[i] = NULL; + + return ret; +} + +/** + add an entry to a string list + this assumes s will not change +*/ +_PUBLIC_ const char **str_list_add_const(const char **list, const char *s) +{ + size_t len = str_list_length(list); + const char **ret; + + ret = talloc_realloc(NULL, list, const char *, len+2); + if (ret == NULL) return NULL; + + ret[len] = s; + ret[len+1] = NULL; + + return ret; +} + +/** + copy a string list + this assumes list will not change +*/ +_PUBLIC_ const char **str_list_copy_const(TALLOC_CTX *mem_ctx, + const char **list) +{ + int i; + const char **ret; + + if (list == NULL) + return NULL; + + ret = talloc_array(mem_ctx, const char *, str_list_length(list)+1); + if (ret == NULL) + return NULL; + + for (i=0;list && list[i];i++) { + ret[i] = list[i]; + } + ret[i] = NULL; + return ret; +} diff --git a/lib/util/util_tdb.c b/lib/util/util_tdb.c index e107cbdc4a..46dbf6d324 100644 --- a/lib/util/util_tdb.c +++ b/lib/util/util_tdb.c @@ -20,7 +20,7 @@ */ #include "includes.h" -#include "../tdb/include/tdb.h" +#include "tdb.h" #include "../lib/util/util_tdb.h" /* these are little tdb utility functions that are meant to make @@ -38,6 +38,14 @@ TDB_DATA make_tdb_data(const uint8_t *dptr, size_t dsize) return ret; } +bool tdb_data_equal(TDB_DATA t1, TDB_DATA t2) +{ + if (t1.dsize != t2.dsize) { + return false; + } + return (memcmp(t1.dptr, t2.dptr, t1.dsize) == 0); +} + TDB_DATA string_tdb_data(const char *string) { return make_tdb_data((const uint8_t *)string, string ? strlen(string) : 0 ); diff --git a/lib/util/util_tdb.h b/lib/util/util_tdb.h index da6378ee6a..79c46714f9 100644 --- a/lib/util/util_tdb.h +++ b/lib/util/util_tdb.h @@ -6,6 +6,7 @@ Make a TDB_DATA and keep the const warning in one place ****************************************************************/ TDB_DATA make_tdb_data(const uint8_t *dptr, size_t dsize); +bool tdb_data_equal(TDB_DATA t1, TDB_DATA t2); TDB_DATA string_tdb_data(const char *string); TDB_DATA string_term_tdb_data(const char *string); |
