nsssrv_mmap_cache.c revision 8e09148b68a3fd8b06fcb746054f3cfa250b40f8
/*
SSSD
NSS Responder - Mmap Cache
Copyright (C) Simo Sorce <ssorce@redhat.com> 2011
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 <fcntl.h>
#include "util/mmap_cache.h"
#include "responder/nss/nsssrv_mmap_cache.h"
/* short group name and no gids (private user group */
#define MC_RAISE_BARRIER(m) do { \
__sync_synchronize(); \
} while (0)
#define MC_LOWER_BARRIER(m) do { \
__sync_synchronize(); \
} while (0)
#define MC_RAISE_INVALID_BARRIER(m) do { \
m->b2 = MC_INVALID_VAL; \
__sync_synchronize(); \
} while (0)
struct sss_mc_ctx {
char *name; /* mmap cache name */
char *file; /* mmap cache file name */
int fd; /* file descriptor */
void *mmap_base; /* base address of mmap */
};
*b |= c; \
} while (0)
*b &= ~c; \
} while (0)
if (*b & c) used = true; \
else used = false; \
} while (0)
static inline
{
} else {
/* it should never happen. */
return MC_INVALID_VAL;
}
}
static inline
{
/* changing a single uint32_t is atomic, so there is no
* need to use barriers in this case */
}
}
/* This function will store corrupted memcache to disk for later
* analysis. */
{
int err;
int fd = -1;
"Cannot store uninitialized cache. Nothing to do.\n");
return;
}
return;
}
goto done;
}
/* We will always store only the last problematic cache state */
if (fd == -1) {
"Failed to open file '%s' [%d]: %s\n",
goto done;
}
if (written == -1) {
} else {
"write() returned %zd (expected (%zd))\n",
}
goto done;
}
"Stored copy of corrupted mmap cache in file '%s\n'", file);
done:
if (fd != -1) {
if (written == -1) {
if (err != 0) {
"Failed to remove file '%s': %s.\n", file,
}
}
}
}
{
}
struct sss_mc_rec *rec,
{
struct sss_mc_rec *cur;
/* Invalid hash. This should never happen, but better
* return than trying to access out of bounds memory */
return;
}
if (slot == MC_INVALID_VAL) {
return;
}
do {
/* rec already stored in hash chain */
return;
}
} while (slot != MC_INVALID_VAL);
/* end of chain, append our record here */
}
struct sss_mc_rec *rec,
{
/* It can happen if rec->hash1 and rec->hash2 was the same.
* or it is invalid hash. It is better to return
* than trying to access out of bounds memory
*/
return;
}
if (slot == MC_INVALID_VAL) {
/* record has already been removed. It may happen if rec->hash1 and
* rec->has2 are the same. (It is not very likely).
*/
return;
}
} else {
while (slot != MC_INVALID_VAL) {
} else {
}
}
}
}
{
uint32_t i;
for (i = 0; i < num; i++) {
}
}
struct sss_mc_rec *rec)
{
/* record already invalid */
return;
}
/* Remove from hash chains */
/* hash chain 1 */
/* hash chain 2 */
/* Clear from free_table */
/* Invalidate record fields */
- sizeof(struct sss_mc_rec)));
}
{
struct sss_mc_rec *self;
return false;
}
return false;
}
return false;
}
return false;
}
/* next record can be invalid if there are no next records */
return false;
} else {
}
return false;
}
}
}
return false;
}
}
/* all tests passed */
return true;
}
/* FIXME: This is a very simplistic, inefficient, memory allocator,
* it will just free the oldest entries regardless of expiration if it
* cycled the whole freebits map and found no empty slot */
{
struct sss_mc_rec *rec;
uint32_t i;
uint32_t t;
bool used;
/* Try to find a free slot w/o removing anything first */
/* FIXME: is it really worth it ? May be it is easier to
* just recycle the next set of slots ? */
cur = 0;
} else {
}
/* search for enough (num_slots) consecutive zero bits, indicating
* consecutive empty slots */
t = cur / 8;
/* if all full in this byte skip directly to the next */
cur = 0;
}
continue;
}
/* at least one bit in this byte is marked as empty */
if (!used) break;
}
/* check if we have enough slots before hitting the table end */
cur = 0;
continue;
}
/* check if we have at least num_slots empty starting from the first
* we found in the previous steps */
if (used) break;
}
if (cur == t) {
/* ok found num_slots consecutive free bits */
return EOK;
}
}
/* no free slots found, free occupied slots after next_slot */
cur = 0;
} else {
}
for (i = 0; i < num_slots; i++) {
if (used) {
/* the first used slot should be a record header, however we
* carefully check it is a valid header and hardfail if not */
/* this is a fatal error, the caller should probaly just
* invalidate the whole cache */
return EFAULT;
}
/* next loop skip the whole record */
/* finally invalidate record completely */
}
}
return EOK;
}
{
case SSS_MC_PASSWD:
return EOK;
case SSS_MC_GROUP:
return EOK;
default:
return EINVAL;
}
}
struct sss_mc_rec *rec,
{
case SSS_MC_PASSWD:
return EOK;
case SSS_MC_GROUP:
return EOK;
default:
return EINVAL;
}
}
struct sized_string *key)
{
struct sss_mc_rec *rec;
char *t_key;
return NULL;
}
/* Get max address of data table. */
return NULL;
}
while (slot != MC_INVALID_VAL) {
"Corrupted fastcache. Slot number too big.\n");
return NULL;
}
return NULL;
}
"Corrupted fastcache. name_ptr value is %u.\n", name_ptr);
return NULL;
}
break;
}
}
if (slot == MC_INVALID_VAL) {
return NULL;
}
return rec;
}
struct sized_string *key,
struct sss_mc_rec **_rec)
{
struct sss_mc_rec *rec;
int old_slots;
int num_slots;
int i;
if (old_rec) {
return EOK;
}
/* slot size changed, invalidate record and fall through to get a
* fully new record */
}
/* we are going to use more space, find enough free slots */
"Fatal internal mmap cache error, invalidating cache!\n");
}
return ret;
}
/* mark as not valid yet */
/* and now mark slots as used */
for (i = 0; i < num_slots; i++) {
}
return EOK;
}
struct sss_mc_rec *rec,
{
}
struct sss_mc_rec *rec)
{
/* name first */
}
/***************************************************************************
* generic invalidation
***************************************************************************/
struct sized_string *key)
{
struct sss_mc_rec *rec;
/* cache not initialized ? */
return EINVAL;
}
/* nothing to invalidate */
return ENOENT;
}
return EOK;
}
/***************************************************************************
* passwd map
***************************************************************************/
struct sized_string *name,
struct sized_string *pw,
struct sized_string *gecos,
struct sized_string *homedir,
struct sized_string *shell)
{
struct sss_mc_rec *rec;
struct sss_mc_pwd_data *data;
struct sized_string uidkey;
char uidstr[11];
int ret;
/* cache not initialized ? */
return EINVAL;
}
if (ret > 10) {
return EINVAL;
}
rec_len = sizeof(struct sss_mc_rec) +
sizeof(struct sss_mc_pwd_data) +
return ENOMEM;
}
return ret;
}
pos = 0;
/* header */
/* passwd struct */
/* finally chain the rec in the hash table */
return EOK;
}
struct sized_string *name)
{
}
{
struct sss_mc_rec *rec;
struct sss_mc_pwd_data *data;
char *uidstr;
/* cache not initialized ? */
return EINVAL;
}
if (!uidstr) {
return ENOMEM;
}
goto done;
}
while (slot != MC_INVALID_VAL) {
goto done;
}
break;
}
}
if (slot == MC_INVALID_VAL) {
goto done;
}
done:
return ret;
}
/***************************************************************************
* group map
***************************************************************************/
struct sized_string *name,
struct sized_string *pw,
{
struct sss_mc_rec *rec;
struct sss_mc_grp_data *data;
struct sized_string gidkey;
char gidstr[11];
int ret;
/* cache not initialized ? */
return EINVAL;
}
if (ret > 10) {
return EINVAL;
}
rec_len = sizeof(struct sss_mc_rec) +
sizeof(struct sss_mc_grp_data) +
return ENOMEM;
}
return ret;
}
pos = 0;
/* header */
/* group struct */
/* finally chain the rec in the hash table */
return EOK;
}
struct sized_string *name)
{
}
{
struct sss_mc_rec *rec;
struct sss_mc_grp_data *data;
char *gidstr;
/* cache not initialized ? */
return EINVAL;
}
if (!gidstr) {
return ENOMEM;
}
goto done;
}
while (slot != MC_INVALID_VAL) {
goto done;
}
break;
}
}
if (slot == MC_INVALID_VAL) {
goto done;
}
done:
return ret;
}
/***************************************************************************
* initialization
***************************************************************************/
/* Copy of sss_mc_set_recycled is present in the src/tools/tools_mc_util.c.
* If you modify this function, you should modify the duplicated function
* too. */
{
struct sss_mc_header h;
int ret;
if (pos == -1) {
/* What do we do now ? */
return errno;
}
errno = 0;
if (ret == -1) {
return errno;
}
/* Write error */
return EIO;
}
return EOK;
}
/*
* When we (re)create a new file we must mark the current file as recycled
* so active clients will abandon its use asap.
* We unlink the current file and make a new one
*/
{
int ofd;
useconds_t t = 50000;
int retries = 3;
if (ofd != -1) {
}
if (ret) {
" recycled: %d(%s)\n",
}
"Failed to open old memory cache file %s: %d(%s).\n",
}
errno = 0;
}
/* temporarily relax umask as we need the file to be readable
* by everyone for now */
errno = 0;
return ret;
}
/* Report on unlink failures but don't overwrite the errno
* from sss_br_lock_file
*/
errno = 0;
if (uret == -1) {
}
return ret;
}
return ret;
}
{
struct sss_mc_header *h;
/* update header using barriers */
MC_RAISE_BARRIER(h);
if (status == SSS_MC_HEADER_ALIVE) {
/* no reason to update anything else if the file is recycled or
* right before reset */
h->major_vno = SSS_MC_MAJOR_VNO;
h->minor_vno = SSS_MC_MINOR_VNO;
h->reserved = 0;
}
MC_LOWER_BARRIER(h);
}
{
int ret;
/* Print debug message to logs if munmap() or close()
* fail but always return 0 */
if (ret == -1) {
"Failed to unmap old memory cache file."
}
}
if (ret == -1) {
"Failed to close old memory cache file."
}
}
return 0;
}
{
unsigned int rseed;
int payload;
switch (type) {
case SSS_MC_PASSWD:
break;
case SSS_MC_GROUP:
break;
default:
return EINVAL;
}
if (!mc_ctx) {
return ENOMEM;
}
goto done;
}
goto done;
}
/* elements must always be multiple of 8 to make things easier to handle,
* so we increase by the necessary amount if they are not a multiple */
/* We can use MC_ALIGN64 for this */
/* hash table is double the size because it will store both forward and
/* for now ALWAYS create a new file on restart */
if (ret) {
goto done;
}
if (ret == -1) {
goto done;
}
goto done;
}
/* generate a pseudo-random seed.
* Needed to fend off dictionary based collision attacks */
done:
if (ret) {
/* Closing the file descriptor and ummaping the file
* from memory is done in the mc_ctx_destructor. */
if (dret == -1) {
}
}
} else {
}
return ret;
}
{
char *name;
enum sss_mc_type type;
"Unable to re-init unitialized memory cache.\n");
return EINVAL;
}
return ENOMEM;
}
goto done;
}
}
}
/* make sure we do not leave a potentially freed pointer around */
goto done;
}
done:
return ret;
}
/* Erase all contents of the mmap cache. This will bring the cache
* to the same state as if it was just initialized. */
{
"Fastcache not initialized. Nothing to do.\n");
return;
}
/* Reset the mmaped area */
}