|
Packit |
8480eb |
/* ----------------------------------------------------------------------- *
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* cache.c - mount entry cache management routines
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* Copyright 2002-2005 Ian Kent <raven@themaw.net> - All Rights Reserved
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
8480eb |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
8480eb |
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
|
|
Packit |
8480eb |
* USA; either version 2 of the License, or (at your option) any later
|
|
Packit |
8480eb |
* version; incorporated herein by reference.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* ----------------------------------------------------------------------- */
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
#include <stdio.h>
|
|
Packit |
8480eb |
#include <malloc.h>
|
|
Packit |
8480eb |
#include <stdlib.h>
|
|
Packit |
8480eb |
#include <string.h>
|
|
Packit |
8480eb |
#include <ctype.h>
|
|
Packit |
8480eb |
#include <stdio.h>
|
|
Packit |
8480eb |
#include <sys/param.h>
|
|
Packit |
8480eb |
#include <sys/stat.h>
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
#include "automount.h"
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_dump_multi(struct list_head *list)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *p;
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, list) {
|
|
Packit |
8480eb |
me = list_entry(p, struct mapent, multi_list);
|
|
Packit |
8480eb |
logmsg("key=%s", me->key);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_dump_cache(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
unsigned int i;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (i = 0; i < mc->size; i++) {
|
|
Packit |
8480eb |
me = mc->hash[i];
|
|
Packit |
8480eb |
if (me == NULL)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
while (me) {
|
|
Packit |
8480eb |
logmsg("me->key=%s me->multi=%p dev=%ld ino=%ld",
|
|
Packit |
8480eb |
me->key, me->multi, me->dev, me->ino);
|
|
Packit |
8480eb |
me = me->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_readlock(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_rdlock(&mc->rwlock);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
logmsg("mapent cache rwlock lock failed");
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_writelock(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_wrlock(&mc->rwlock);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
logmsg("mapent cache rwlock lock failed");
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int cache_try_writelock(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_trywrlock(&mc->rwlock);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
logmsg("mapent cache rwlock busy");
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_unlock(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_unlock(&mc->rwlock);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
logmsg("mapent cache rwlock unlock failed");
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_lock_cleanup(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent_cache *mc = (struct mapent_cache *) arg;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_multi_readlock(struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_rdlock(&me->multi_rwlock);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
logmsg("mapent cache multi mutex lock failed");
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_multi_writelock(struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_wrlock(&me->multi_rwlock);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
logmsg("mapent cache multi mutex lock failed");
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_multi_unlock(struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_unlock(&me->multi_rwlock);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
logmsg("mapent cache multi mutex unlock failed");
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_multi_lock_cleanup(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me = (struct mapent *) arg;
|
|
Packit |
8480eb |
cache_multi_unlock(me);
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static inline void ino_index_lock(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status = pthread_mutex_lock(&mc->ino_index_mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static inline void ino_index_unlock(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status = pthread_mutex_unlock(&mc->ino_index_mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Save the cache entry mapent field onto a stack and set a new mapent */
|
|
Packit |
8480eb |
int cache_push_mapent(struct mapent *me, char *mapent)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct stack *s;
|
|
Packit |
8480eb |
char *new;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!me->mapent)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!mapent)
|
|
Packit |
8480eb |
new = NULL;
|
|
Packit |
8480eb |
else {
|
|
Packit |
8480eb |
new = strdup(mapent);
|
|
Packit |
8480eb |
if (!new)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
s = malloc(sizeof(struct stack));
|
|
Packit |
8480eb |
if (!s) {
|
|
Packit |
8480eb |
if (new)
|
|
Packit |
8480eb |
free(new);
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
memset(s, 0, sizeof(*s));
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
s->mapent = me->mapent;
|
|
Packit |
8480eb |
s->age = me->age;
|
|
Packit |
8480eb |
me->mapent = new;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->stack)
|
|
Packit |
8480eb |
s->next = me->stack;
|
|
Packit |
8480eb |
me->stack = s;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return CHE_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Restore cache entry mapent to a previously saved mapent, discard current */
|
|
Packit |
8480eb |
int cache_pop_mapent(struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct stack *s = me->stack;
|
|
Packit |
8480eb |
char *mapent;
|
|
Packit |
8480eb |
time_t age;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!s || !s->mapent)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mapent = s->mapent;
|
|
Packit |
8480eb |
age = s->age;
|
|
Packit |
8480eb |
me->stack = s->next;
|
|
Packit |
8480eb |
free(s);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (age < me->age) {
|
|
Packit |
8480eb |
free(mapent);
|
|
Packit |
8480eb |
return CHE_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
me->mapent = mapent;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return CHE_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
struct mapent_cache *cache_init(struct autofs_point *ap, struct map_source *map)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent_cache *mc;
|
|
Packit |
8480eb |
unsigned int i;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (map->mc)
|
|
Packit |
8480eb |
cache_release(map);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc = malloc(sizeof(struct mapent_cache));
|
|
Packit |
8480eb |
if (!mc)
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc->size = defaults_get_map_hash_table_size();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc->hash = malloc(mc->size * sizeof(struct mapent *));
|
|
Packit |
8480eb |
if (!mc->hash) {
|
|
Packit |
8480eb |
free(mc);
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc->ino_index = malloc(mc->size * sizeof(struct list_head));
|
|
Packit |
8480eb |
if (!mc->ino_index) {
|
|
Packit |
8480eb |
free(mc->hash);
|
|
Packit |
8480eb |
free(mc);
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_init(&mc->ino_index_mutex, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_init(&mc->rwlock, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_writelock(mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (i = 0; i < mc->size; i++) {
|
|
Packit |
8480eb |
mc->hash[i] = NULL;
|
|
Packit |
8480eb |
INIT_LIST_HEAD(&mc->ino_index[i]);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc->ap = ap;
|
|
Packit |
8480eb |
mc->map = map;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return mc;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_clean_null_cache(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me, *next;
|
|
Packit |
8480eb |
int i;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (i = 0; i < mc->size; i++) {
|
|
Packit |
8480eb |
me = mc->hash[i];
|
|
Packit |
8480eb |
if (me == NULL)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
next = me->next;
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (next != NULL) {
|
|
Packit |
8480eb |
me = next;
|
|
Packit |
8480eb |
next = me->next;
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
mc->hash[i] = NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
struct mapent_cache *cache_init_null_cache(struct master *master)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent_cache *mc;
|
|
Packit |
8480eb |
unsigned int i;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc = malloc(sizeof(struct mapent_cache));
|
|
Packit |
8480eb |
if (!mc)
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc->size = NULL_MAP_HASHSIZE;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc->hash = malloc(mc->size * sizeof(struct mapent *));
|
|
Packit |
8480eb |
if (!mc->hash) {
|
|
Packit |
8480eb |
free(mc);
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc->ino_index = malloc(mc->size * sizeof(struct list_head));
|
|
Packit |
8480eb |
if (!mc->ino_index) {
|
|
Packit |
8480eb |
free(mc->hash);
|
|
Packit |
8480eb |
free(mc);
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_init(&mc->ino_index_mutex, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_init(&mc->rwlock, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (i = 0; i < mc->size; i++) {
|
|
Packit |
8480eb |
mc->hash[i] = NULL;
|
|
Packit |
8480eb |
INIT_LIST_HEAD(&mc->ino_index[i]);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc->ap = NULL;
|
|
Packit |
8480eb |
mc->map = NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return mc;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static u_int32_t ino_hash(dev_t dev, ino_t ino, unsigned int size)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
u_int32_t hashval;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
hashval = dev + ino;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return hashval % size;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int cache_set_ino_index(struct mapent_cache *mc, const char *key, dev_t dev, ino_t ino)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
u_int32_t ino_index = ino_hash(dev, ino, mc->size);
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = cache_lookup_distinct(mc, key);
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ino_index_lock(mc);
|
|
Packit |
8480eb |
list_del_init(&me->ino_index);
|
|
Packit |
8480eb |
list_add(&me->ino_index, &mc->ino_index[ino_index]);
|
|
Packit |
8480eb |
me->dev = dev;
|
|
Packit |
8480eb |
me->ino = ino;
|
|
Packit |
8480eb |
ino_index_unlock(mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_lookup_ino(struct mapent_cache *mc, dev_t dev, ino_t ino)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me = NULL;
|
|
Packit |
8480eb |
struct list_head *head, *p;
|
|
Packit |
8480eb |
u_int32_t ino_index;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ino_index_lock(mc);
|
|
Packit |
8480eb |
ino_index = ino_hash(dev, ino, mc->size);
|
|
Packit |
8480eb |
head = &mc->ino_index[ino_index];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
me = list_entry(p, struct mapent, ino_index);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->dev != dev || me->ino != ino)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ino_index_unlock(mc);
|
|
Packit |
8480eb |
return me;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ino_index_unlock(mc);
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_lookup_first(struct mapent_cache *mc)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me = NULL;
|
|
Packit |
8480eb |
unsigned int i;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (i = 0; i < mc->size; i++) {
|
|
Packit |
8480eb |
me = mc->hash[i];
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (me) {
|
|
Packit |
8480eb |
/* Multi mount entries are not primary */
|
|
Packit |
8480eb |
if (me->multi && me->multi != me) {
|
|
Packit |
8480eb |
me = me->next;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return me;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_lookup_next(struct mapent_cache *mc, struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *this;
|
|
Packit |
8480eb |
u_int32_t hashval;
|
|
Packit |
8480eb |
unsigned int i;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
this = me->next;
|
|
Packit |
8480eb |
while (this) {
|
|
Packit |
8480eb |
/* Multi mount entries are not primary */
|
|
Packit |
8480eb |
if (this->multi && this->multi != this) {
|
|
Packit |
8480eb |
this = this->next;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return this;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
hashval = hash(me->key, mc->size) + 1;
|
|
Packit |
8480eb |
if (hashval < mc->size) {
|
|
Packit |
8480eb |
for (i = (unsigned int) hashval; i < mc->size; i++) {
|
|
Packit |
8480eb |
this = mc->hash[i];
|
|
Packit |
8480eb |
if (!this)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (this) {
|
|
Packit |
8480eb |
/* Multi mount entries are not primary */
|
|
Packit |
8480eb |
if (this->multi && this->multi != this) {
|
|
Packit |
8480eb |
this = this->next;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return this;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_lookup_key_next(struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
next = me->next;
|
|
Packit |
8480eb |
while (next) {
|
|
Packit |
8480eb |
/* Multi mount entries are not primary */
|
|
Packit |
8480eb |
if (me->multi && me->multi != me)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
if (!strcmp(me->key, next->key))
|
|
Packit |
8480eb |
return next;
|
|
Packit |
8480eb |
next = next->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_lookup(struct mapent_cache *mc, const char *key)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me = NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!key)
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (me = mc->hash[hash(key, mc->size)]; me != NULL; me = me->next) {
|
|
Packit |
8480eb |
if (strcmp(key, me->key) == 0)
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = cache_lookup_first(mc);
|
|
Packit |
8480eb |
if (me != NULL) {
|
|
Packit |
8480eb |
/* Can't have wildcard in direct map */
|
|
Packit |
8480eb |
if (*me->key == '/') {
|
|
Packit |
8480eb |
me = NULL;
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (me = mc->hash[hash("*", mc->size)]; me != NULL; me = me->next)
|
|
Packit |
8480eb |
if (strcmp("*", me->key) == 0)
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
done:
|
|
Packit |
8480eb |
return me;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_lookup_distinct(struct mapent_cache *mc, const char *key)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!key)
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (me = mc->hash[hash(key, mc->size)]; me != NULL; me = me->next) {
|
|
Packit |
8480eb |
if (strcmp(key, me->key) == 0)
|
|
Packit |
8480eb |
return me;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Lookup an offset within a multi-mount entry */
|
|
Packit |
8480eb |
struct mapent *cache_lookup_offset(const char *prefix, const char *offset, int start, struct list_head *head)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *p;
|
|
Packit |
8480eb |
struct mapent *this;
|
|
Packit |
8480eb |
/* Keys for direct maps may be as long as a path name */
|
|
Packit |
8480eb |
char o_key[PATH_MAX];
|
|
Packit |
8480eb |
/* Avoid "//" at the beginning of paths */
|
|
Packit |
8480eb |
const char *path_prefix = strlen(prefix) > 1 ? prefix : "";
|
|
Packit |
8480eb |
size_t size;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* root offset duplicates "/" */
|
|
Packit |
8480eb |
size = snprintf(o_key, sizeof(o_key), "%s%s", path_prefix, offset);
|
|
Packit |
8480eb |
if (size >= sizeof(o_key))
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
this = list_entry(p, struct mapent, multi_list);
|
|
Packit |
8480eb |
if (!strcmp(&this->key[start], o_key))
|
|
Packit |
8480eb |
return this;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
static struct mapent *__cache_partial_match(struct mapent_cache *mc,
|
|
Packit |
8480eb |
const char *prefix,
|
|
Packit |
8480eb |
unsigned int type)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me = NULL;
|
|
Packit |
8480eb |
size_t len = strlen(prefix);
|
|
Packit |
8480eb |
unsigned int i;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (i = 0; i < mc->size; i++) {
|
|
Packit |
8480eb |
me = mc->hash[i];
|
|
Packit |
8480eb |
if (me == NULL)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (len < strlen(me->key) &&
|
|
Packit |
8480eb |
(strncmp(prefix, me->key, len) == 0) &&
|
|
Packit |
8480eb |
me->key[len] == '/') {
|
|
Packit |
8480eb |
if (type == LKP_NORMAL)
|
|
Packit |
8480eb |
return me;
|
|
Packit |
8480eb |
if (type == LKP_WILD &&
|
|
Packit |
8480eb |
me->key[len] != '\0' &&
|
|
Packit |
8480eb |
me->key[len + 1] == '*')
|
|
Packit |
8480eb |
return me;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = me->next;
|
|
Packit |
8480eb |
while (me != NULL) {
|
|
Packit |
8480eb |
if (len < strlen(me->key) &&
|
|
Packit |
8480eb |
(strncmp(prefix, me->key, len) == 0 &&
|
|
Packit |
8480eb |
me->key[len] == '/')) {
|
|
Packit |
8480eb |
if (type == LKP_NORMAL)
|
|
Packit |
8480eb |
return me;
|
|
Packit |
8480eb |
if (type == LKP_WILD &&
|
|
Packit |
8480eb |
me->key[len] != '\0' &&
|
|
Packit |
8480eb |
me->key[len + 1] == '*')
|
|
Packit |
8480eb |
return me;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
me = me->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_partial_match(struct mapent_cache *mc, const char *prefix)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
return __cache_partial_match(mc, prefix, LKP_NORMAL);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_partial_match_wild(struct mapent_cache *mc, const char *prefix)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
return __cache_partial_match(mc, prefix, LKP_WILD);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be write locked by caller */
|
|
Packit |
8480eb |
int cache_add(struct mapent_cache *mc, struct map_source *ms, const char *key, const char *mapent, time_t age)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me, *existing = NULL;
|
|
Packit |
8480eb |
char *pkey, *pent;
|
|
Packit |
8480eb |
u_int32_t hashval = hash(key, mc->size);
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = (struct mapent *) malloc(sizeof(struct mapent));
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pkey = malloc(strlen(key) + 1);
|
|
Packit |
8480eb |
if (!pkey) {
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
me->key = strcpy(pkey, key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (mapent) {
|
|
Packit |
8480eb |
pent = malloc(strlen(mapent) + 1);
|
|
Packit |
8480eb |
if (!pent) {
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
free(pkey);
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
me->mapent = strcpy(pent, mapent);
|
|
Packit |
8480eb |
} else
|
|
Packit |
8480eb |
me->mapent = NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me->stack = NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me->age = age;
|
|
Packit |
8480eb |
me->status = 0;
|
|
Packit |
8480eb |
me->mc = mc;
|
|
Packit |
8480eb |
me->source = ms;
|
|
Packit |
8480eb |
INIT_LIST_HEAD(&me->ino_index);
|
|
Packit |
8480eb |
INIT_LIST_HEAD(&me->multi_list);
|
|
Packit |
8480eb |
me->multi = NULL;
|
|
Packit |
8480eb |
me->parent = NULL;
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
me->dev = (dev_t) -1;
|
|
Packit |
8480eb |
me->ino = (ino_t) -1;
|
|
Packit |
8480eb |
me->flags = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_init(&me->multi_rwlock, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* We need to add to the end if values exist in order to
|
|
Packit |
8480eb |
* preserve the order in which the map was read on lookup.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
existing = cache_lookup_distinct(mc, key);
|
|
Packit |
8480eb |
if (!existing) {
|
|
Packit |
8480eb |
me->next = mc->hash[hashval];
|
|
Packit |
8480eb |
mc->hash[hashval] = me;
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
while (1) {
|
|
Packit |
8480eb |
struct mapent *next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
next = cache_lookup_key_next(existing);
|
|
Packit |
8480eb |
if (!next)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
existing = next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
me->next = existing->next;
|
|
Packit |
8480eb |
existing->next = me;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return CHE_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be write locked by caller */
|
|
Packit |
8480eb |
static void cache_add_ordered_offset(struct mapent *me, struct list_head *head)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *p;
|
|
Packit |
8480eb |
struct mapent *this;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
size_t tlen;
|
|
Packit |
8480eb |
int eq;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
this = list_entry(p, struct mapent, multi_list);
|
|
Packit |
8480eb |
tlen = strlen(this->key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
eq = strncmp(this->key, me->key, tlen);
|
|
Packit |
8480eb |
if (!eq && tlen == strlen(me->key))
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (eq > 0) {
|
|
Packit |
8480eb |
list_add_tail(&me->multi_list, p);
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
list_add_tail(&me->multi_list, p);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be write locked by caller */
|
|
Packit |
8480eb |
int cache_update_offset(struct mapent_cache *mc, const char *mkey, const char *key, const char *mapent, time_t age)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
unsigned logopt = mc->ap ? mc->ap->logopt : master_get_logopt();
|
|
Packit |
8480eb |
struct mapent *me, *owner;
|
|
Packit |
8480eb |
int ret = CHE_OK;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
owner = cache_lookup_distinct(mc, mkey);
|
|
Packit |
8480eb |
if (!owner)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = cache_lookup_distinct(mc, key);
|
|
Packit |
8480eb |
if (me && me->age == age) {
|
|
Packit |
8480eb |
if (me == owner || strcmp(me->key, key) == 0) {
|
|
Packit |
8480eb |
char *pent;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
warn(logopt,
|
|
Packit |
8480eb |
"duplcate offset detected for key %s", me->key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pent = malloc(strlen(mapent) + 1);
|
|
Packit |
8480eb |
if (!pent)
|
|
Packit |
8480eb |
warn(logopt,
|
|
Packit |
8480eb |
"map entry not updated: %s", me->mapent);
|
|
Packit |
8480eb |
else {
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
me->mapent = strcpy(pent, mapent);
|
|
Packit |
8480eb |
warn(logopt,
|
|
Packit |
8480eb |
"map entry updated with: %s", mapent);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return CHE_DUPLICATE;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ret = cache_update(mc, owner->source, key, mapent, age);
|
|
Packit |
8480eb |
if (ret == CHE_FAIL) {
|
|
Packit |
8480eb |
warn(logopt, "failed to add key %s to cache", key);
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = cache_lookup_distinct(mc, key);
|
|
Packit |
8480eb |
if (me) {
|
|
Packit |
8480eb |
cache_add_ordered_offset(me, &owner->multi_list);
|
|
Packit |
8480eb |
me->multi = owner;
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ret = CHE_FAIL;
|
|
Packit |
8480eb |
done:
|
|
Packit |
8480eb |
return ret;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_update_negative(struct mapent_cache *mc,
|
|
Packit |
8480eb |
struct map_source *ms, const char *key,
|
|
Packit |
8480eb |
time_t timeout)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
time_t now = monotonic_time(NULL);
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
int rv = CHE_OK;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Don't update the wildcard */
|
|
Packit |
8480eb |
if (strlen(key) == 1 && *key == '*')
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = cache_lookup_distinct(mc, key);
|
|
Packit |
8480eb |
if (me)
|
|
Packit |
8480eb |
rv = cache_push_mapent(me, NULL);
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
rv = cache_update(mc, ms, key, NULL, now);
|
|
Packit |
8480eb |
if (rv != CHE_FAIL) {
|
|
Packit |
8480eb |
me = cache_lookup_distinct(mc, key);
|
|
Packit |
8480eb |
if (me)
|
|
Packit |
8480eb |
me->status = now + timeout;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static struct mapent *get_parent(const char *key, struct list_head *head, struct list_head **pos)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *next;
|
|
Packit |
8480eb |
struct mapent *this, *last;
|
|
Packit |
8480eb |
int eq;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
last = NULL;
|
|
Packit |
8480eb |
next = *pos ? (*pos)->next : head->next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(next, head) {
|
|
Packit |
8480eb |
this = list_entry(next, struct mapent, multi_list);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!strcmp(this->key, key))
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
eq = strncmp(this->key, key, strlen(this->key));
|
|
Packit |
8480eb |
if (eq == 0) {
|
|
Packit |
8480eb |
*pos = next;
|
|
Packit |
8480eb |
last = this;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return last;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int cache_set_parents(struct mapent *mm)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *multi_head, *p, *pos;
|
|
Packit |
8480eb |
struct mapent *this;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!mm->multi)
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pos = NULL;
|
|
Packit |
8480eb |
multi_head = &mm->multi->multi_list;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, multi_head) {
|
|
Packit |
8480eb |
struct mapent *parent;
|
|
Packit |
8480eb |
this = list_entry(p, struct mapent, multi_list);
|
|
Packit |
8480eb |
parent = get_parent(this->key, multi_head, &pos;;
|
|
Packit |
8480eb |
if (parent)
|
|
Packit |
8480eb |
this->parent = parent;
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
this->parent = mm->multi;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be write locked by caller */
|
|
Packit |
8480eb |
int cache_update(struct mapent_cache *mc, struct map_source *ms, const char *key, const char *mapent, time_t age)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
unsigned logopt = mc->ap ? mc->ap->logopt : master_get_logopt();
|
|
Packit |
8480eb |
struct mapent *me = NULL;
|
|
Packit |
8480eb |
char *pent;
|
|
Packit |
8480eb |
int ret = CHE_OK;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = cache_lookup(mc, key);
|
|
Packit |
8480eb |
while (me && me->source != ms)
|
|
Packit |
8480eb |
me = cache_lookup_key_next(me);
|
|
Packit |
8480eb |
if (!me || (!strcmp(me->key, "*") && strcmp(key, "*"))) {
|
|
Packit |
8480eb |
ret = cache_add(mc, ms, key, mapent, age);
|
|
Packit |
8480eb |
if (!ret) {
|
|
Packit |
8480eb |
debug(logopt, "failed for %s", key);
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ret = CHE_UPDATED;
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
/* Already seen one of these */
|
|
Packit |
8480eb |
if (me->age == age)
|
|
Packit |
8480eb |
return CHE_OK;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!mapent) {
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
me->mapent = NULL;
|
|
Packit |
8480eb |
} else if (!me->mapent || strcmp(me->mapent, mapent) != 0) {
|
|
Packit |
8480eb |
pent = malloc(strlen(mapent) + 1);
|
|
Packit |
8480eb |
if (pent == NULL)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
me->mapent = strcpy(pent, mapent);
|
|
Packit |
8480eb |
ret = CHE_UPDATED;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
me->age = age;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return ret;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache_multi_lock of the multi mount owner must be held by caller */
|
|
Packit |
8480eb |
int cache_delete_offset(struct mapent_cache *mc, const char *key)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
u_int32_t hashval = hash(key, mc->size);
|
|
Packit |
8480eb |
struct mapent *me = NULL, *pred;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = mc->hash[hashval];
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (strcmp(key, me->key) == 0) {
|
|
Packit |
8480eb |
if (me->multi && me->multi == me)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
mc->hash[hashval] = me->next;
|
|
Packit |
8480eb |
goto delete;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (me->next != NULL) {
|
|
Packit |
8480eb |
pred = me;
|
|
Packit |
8480eb |
me = me->next;
|
|
Packit |
8480eb |
if (strcmp(key, me->key) == 0) {
|
|
Packit |
8480eb |
if (me->multi && me->multi == me)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
pred->next = me->next;
|
|
Packit |
8480eb |
goto delete;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
delete:
|
|
Packit |
8480eb |
status = pthread_rwlock_destroy(&me->multi_rwlock);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
list_del(&me->multi_list);
|
|
Packit |
8480eb |
ino_index_lock(mc);
|
|
Packit |
8480eb |
list_del(&me->ino_index);
|
|
Packit |
8480eb |
ino_index_unlock(mc);
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return CHE_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be write locked by caller */
|
|
Packit |
8480eb |
int cache_delete(struct mapent_cache *mc, const char *key)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent *me = NULL, *pred;
|
|
Packit |
8480eb |
u_int32_t hashval = hash(key, mc->size);
|
|
Packit |
8480eb |
int status, ret = CHE_OK;
|
|
Packit |
8480eb |
char this[PATH_MAX];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
strcpy(this, key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = mc->hash[hashval];
|
|
Packit |
8480eb |
if (!me) {
|
|
Packit |
8480eb |
ret = CHE_FAIL;
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (me->next != NULL) {
|
|
Packit |
8480eb |
pred = me;
|
|
Packit |
8480eb |
me = me->next;
|
|
Packit |
8480eb |
if (strcmp(this, me->key) == 0) {
|
|
Packit |
8480eb |
struct stack *s = me->stack;
|
|
Packit |
8480eb |
if (me->multi && !list_empty(&me->multi_list)) {
|
|
Packit |
8480eb |
ret = CHE_FAIL;
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
pred->next = me->next;
|
|
Packit |
8480eb |
status = pthread_rwlock_destroy(&me->multi_rwlock);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
ino_index_lock(mc);
|
|
Packit |
8480eb |
list_del(&me->ino_index);
|
|
Packit |
8480eb |
ino_index_unlock(mc);
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
while (s) {
|
|
Packit |
8480eb |
struct stack *next = s->next;
|
|
Packit |
8480eb |
if (s->mapent)
|
|
Packit |
8480eb |
free(s->mapent);
|
|
Packit |
8480eb |
free(s);
|
|
Packit |
8480eb |
s = next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
me = pred;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = mc->hash[hashval];
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (strcmp(this, me->key) == 0) {
|
|
Packit |
8480eb |
struct stack *s = me->stack;
|
|
Packit |
8480eb |
if (me->multi && !list_empty(&me->multi_list)) {
|
|
Packit |
8480eb |
ret = CHE_FAIL;
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
mc->hash[hashval] = me->next;
|
|
Packit |
8480eb |
status = pthread_rwlock_destroy(&me->multi_rwlock);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
ino_index_lock(mc);
|
|
Packit |
8480eb |
list_del(&me->ino_index);
|
|
Packit |
8480eb |
ino_index_unlock(mc);
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
while (s) {
|
|
Packit |
8480eb |
struct stack *next = s->next;
|
|
Packit |
8480eb |
if (s->mapent)
|
|
Packit |
8480eb |
free(s->mapent);
|
|
Packit |
8480eb |
free(s);
|
|
Packit |
8480eb |
s = next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
done:
|
|
Packit |
8480eb |
return ret;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be write locked by caller */
|
|
Packit |
8480eb |
int cache_delete_offset_list(struct mapent_cache *mc, const char *key)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
unsigned logopt = mc->ap ? mc->ap->logopt : master_get_logopt();
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
struct mapent *this;
|
|
Packit |
8480eb |
struct list_head *head, *next;
|
|
Packit |
8480eb |
int remain = 0;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = cache_lookup_distinct(mc, key);
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Not offset list owner */
|
|
Packit |
8480eb |
if (me->multi != me)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &me->multi_list;
|
|
Packit |
8480eb |
next = head->next;
|
|
Packit |
8480eb |
while (next != head) {
|
|
Packit |
8480eb |
this = list_entry(next, struct mapent, multi_list);
|
|
Packit |
8480eb |
next = next->next;
|
|
Packit |
8480eb |
if (this->ioctlfd != -1) {
|
|
Packit |
8480eb |
error(logopt,
|
|
Packit |
8480eb |
"active offset mount key %s", this->key);
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &me->multi_list;
|
|
Packit |
8480eb |
next = head->next;
|
|
Packit |
8480eb |
while (next != head) {
|
|
Packit |
8480eb |
this = list_entry(next, struct mapent, multi_list);
|
|
Packit |
8480eb |
next = next->next;
|
|
Packit |
8480eb |
list_del_init(&this->multi_list);
|
|
Packit |
8480eb |
this->multi = NULL;
|
|
Packit |
8480eb |
debug(logopt, "deleting offset key %s", this->key);
|
|
Packit |
8480eb |
status = cache_delete(mc, this->key);
|
|
Packit |
8480eb |
if (status == CHE_FAIL) {
|
|
Packit |
8480eb |
warn(logopt,
|
|
Packit |
8480eb |
"failed to delete offset %s", this->key);
|
|
Packit |
8480eb |
this->multi = me;
|
|
Packit |
8480eb |
/* TODO: add list back in */
|
|
Packit |
8480eb |
remain++;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!remain) {
|
|
Packit |
8480eb |
list_del_init(&me->multi_list);
|
|
Packit |
8480eb |
me->multi = NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (remain)
|
|
Packit |
8480eb |
return CHE_FAIL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return CHE_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_release(struct map_source *map)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent_cache *mc;
|
|
Packit |
8480eb |
struct mapent *me, *next;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
unsigned int i;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc = map->mc;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_writelock(mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (i = 0; i < mc->size; i++) {
|
|
Packit |
8480eb |
me = mc->hash[i];
|
|
Packit |
8480eb |
if (me == NULL)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
next = me->next;
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (next != NULL) {
|
|
Packit |
8480eb |
me = next;
|
|
Packit |
8480eb |
next = me->next;
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
map->mc = NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_destroy(&mc->ino_index_mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_destroy(&mc->rwlock);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
free(mc->hash);
|
|
Packit |
8480eb |
free(mc->ino_index);
|
|
Packit |
8480eb |
free(mc);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void cache_release_null_cache(struct master *master)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent_cache *mc;
|
|
Packit |
8480eb |
struct mapent *me, *next;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
unsigned int i;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc = master->nc;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_writelock(mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
for (i = 0; i < mc->size; i++) {
|
|
Packit |
8480eb |
me = mc->hash[i];
|
|
Packit |
8480eb |
if (me == NULL)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
next = me->next;
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
if (me->mapent)
|
|
Packit |
8480eb |
free(me->mapent);
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (next != NULL) {
|
|
Packit |
8480eb |
me = next;
|
|
Packit |
8480eb |
next = me->next;
|
|
Packit |
8480eb |
free(me->key);
|
|
Packit |
8480eb |
free(me);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
master->nc = NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_destroy(&mc->ino_index_mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_rwlock_destroy(&mc->rwlock);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
free(mc->hash);
|
|
Packit |
8480eb |
free(mc->ino_index);
|
|
Packit |
8480eb |
free(mc);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
struct mapent *cache_enumerate(struct mapent_cache *mc, struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
return cache_lookup_first(mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return cache_lookup_next(mc, me);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Get each offset from list head under prefix.
|
|
Packit |
8480eb |
* Maintain traversal current position in pos for subsequent calls.
|
|
Packit |
8480eb |
* Return each offset into offset.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
/* cache must be read locked by caller */
|
|
Packit |
8480eb |
char *cache_get_offset(const char *prefix, char *offset, int start,
|
|
Packit |
8480eb |
struct list_head *head, struct list_head **pos)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *next;
|
|
Packit |
8480eb |
struct mapent *this;
|
|
Packit |
8480eb |
size_t plen = strlen(prefix);
|
|
Packit |
8480eb |
size_t len = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (*pos == head)
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Find an offset */
|
|
Packit |
8480eb |
*offset = '\0';
|
|
Packit |
8480eb |
next = *pos ? (*pos)->next : head->next;
|
|
Packit |
8480eb |
while (next != head) {
|
|
Packit |
8480eb |
char *offset_start, *pstart, *pend;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
this = list_entry(next, struct mapent, multi_list);
|
|
Packit |
8480eb |
*pos = next;
|
|
Packit |
8480eb |
next = next->next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
offset_start = &this->key[start];
|
|
Packit |
8480eb |
if (strlen(offset_start) <= plen)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!strncmp(prefix, offset_start, plen)) {
|
|
Packit |
8480eb |
struct mapent *np = NULL;
|
|
Packit |
8480eb |
char pe[PATH_MAX + 1];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* "/" doesn't count for root offset */
|
|
Packit |
8480eb |
if (plen == 1)
|
|
Packit |
8480eb |
pstart = &offset_start[plen - 1];
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
pstart = &offset_start[plen];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* not part of this sub-tree */
|
|
Packit |
8480eb |
if (*pstart != '/')
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* get next offset */
|
|
Packit |
8480eb |
pend = pstart;
|
|
Packit |
8480eb |
while (*pend++) {
|
|
Packit |
8480eb |
size_t nest_pt_offset;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (*pend != '/')
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
nest_pt_offset = start + pend - pstart;
|
|
Packit |
8480eb |
if (plen > 1)
|
|
Packit |
8480eb |
nest_pt_offset += plen;
|
|
Packit |
8480eb |
strcpy(pe, this->key);
|
|
Packit |
8480eb |
pe[nest_pt_offset] = '\0';
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
np = cache_lookup_distinct(this->mc, pe);
|
|
Packit |
8480eb |
if (np)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
if (np)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
len = pend - pstart - 1;
|
|
Packit |
8480eb |
strncpy(offset, pstart, len);
|
|
Packit |
8480eb |
offset[len] ='\0';
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Seek to next offset */
|
|
Packit |
8480eb |
while (next != head) {
|
|
Packit |
8480eb |
char *offset_start, *pstart;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
this = list_entry(next, struct mapent, multi_list);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
offset_start = &this->key[start];
|
|
Packit |
8480eb |
if (strlen(offset_start) <= plen + len)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* "/" doesn't count for root offset */
|
|
Packit |
8480eb |
if (plen == 1)
|
|
Packit |
8480eb |
pstart = &offset_start[plen - 1];
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
pstart = &offset_start[plen];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* not part of this sub-tree */
|
|
Packit |
8480eb |
if (*pstart != '/')
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* new offset */
|
|
Packit |
8480eb |
if (!*(pstart + len + 1))
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* compare offset */
|
|
Packit |
8480eb |
if (pstart[len] != '/' ||
|
|
Packit |
8480eb |
strlen(pstart) != len ||
|
|
Packit |
8480eb |
strncmp(offset, pstart, len))
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
*pos = next;
|
|
Packit |
8480eb |
next = next->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return *offset ? offset : NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|