From: Liu Yuan <tailai.ly at taobao.com> trunk object is meta data that describes the structure of the data objects at the timeline of snapshot being taken. Signed-off-by: Liu Yuan <tailai.ly at taobao.com> --- sheep/farm/farm.h | 25 ++++++ sheep/farm/trunk.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+), 0 deletions(-) create mode 100644 sheep/farm/trunk.c diff --git a/sheep/farm/farm.h b/sheep/farm/farm.h index 0debbfd..5734903 100644 --- a/sheep/farm/farm.h +++ b/sheep/farm/farm.h @@ -18,11 +18,17 @@ #include "sheepdog_proto.h" #include "sheep.h" #include "logger.h" +#include "strbuf.h" #define SHA1_LEN 20 #define HEX_LEN 40 #define NAME_LEN HEX_LEN +#define TAG_LEN 6 +#define TAG_DATA "data\0\0" +#define TAG_TRUNK "trunk\0" +#define TAG_SNAP "snap\0\0" + struct sha1_file_hdr { char tag[TAG_LEN]; uint64_t size; @@ -30,6 +36,18 @@ struct sha1_file_hdr { uint64_t reserved; }; +struct trunk_entry { + uint64_t oid; + unsigned char sha1[SHA1_LEN]; +}; + +struct trunk_entry_incore { + struct trunk_entry raw; + int flags; + struct list_head active_list; + struct hlist_node hash; +}; + /* sha1_file.c */ extern char *sha1_to_path(const unsigned char *sha1); extern int sha1_file_write(unsigned char *buf, unsigned len, unsigned char *outsha1); @@ -38,4 +56,11 @@ extern char * sha1_to_hex(const unsigned char *sha1); extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern int sha1_file_try_delete(const unsigned char *sha1); +/* trunk.c */ +extern int trunk_init(void); +extern int trunk_file_write(unsigned char *outsha1, int user); +extern void *trunk_file_read(unsigned char *sha1, struct sha1_file_hdr *); +extern int trunk_update_entry(uint64_t oid); +extern void trunk_reset(void); + #endif diff --git a/sheep/farm/trunk.c b/sheep/farm/trunk.c new file mode 100644 index 0000000..0506f3f --- /dev/null +++ b/sheep/farm/trunk.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2011 Taobao Inc. + * + * Liu Yuan <namei.unix at gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Trunk object is meta data that describes the structure of the data objects + * at the time of snapshot being taken. It ties data objects together into a flat + * directory structure. + */ +#include <pthread.h> +#include <dirent.h> + +#include "farm.h" +#include "strbuf.h" +#include "list.h" +#include "util.h" +#include "sheepdog_proto.h" + +#define TRUNK_ENTRY_DIRTY 0x00000001 + +#define HASH_BITS 10 +#define HASH_SIZE (1 << HASH_BITS) + +static LIST_HEAD(trunk_active_list); +static pthread_mutex_t active_list_lock = PTHREAD_MUTEX_INITIALIZER; +static struct hlist_head trunk_hashtable[HASH_SIZE]; +static pthread_mutex_t hashtable_lock[HASH_SIZE] = { [0 ... HASH_SIZE - 1] = PTHREAD_MUTEX_INITIALIZER }; +static unsigned int trunk_entry_active_nr; + +static inline int trunk_entry_is_dirty(struct trunk_entry_incore *entry) +{ + return entry->flags & TRUNK_ENTRY_DIRTY; +} + +static inline void dirty_trunk_entry(struct trunk_entry_incore *entry) +{ + entry->flags |= TRUNK_ENTRY_DIRTY; +} + +static inline void undirty_trunk_entry(struct trunk_entry_incore *entry) +{ + entry->flags &= ~TRUNK_ENTRY_DIRTY; +} + +static inline int hash(uint64_t oid) +{ + return hash_64(oid, HASH_BITS); +} + +static void inline get_entry(struct trunk_entry_incore *entry, struct hlist_head *head) +{ + hlist_add_head(&entry->hash, head); + pthread_mutex_lock(&active_list_lock); + list_add(&entry->active_list, &trunk_active_list); + trunk_entry_active_nr++; + pthread_mutex_unlock(&active_list_lock); +} + +static struct trunk_entry_incore *lookup_trunk_entry(uint64_t oid, int create) +{ + int h = hash(oid); + struct hlist_head *head = trunk_hashtable + h; + struct trunk_entry_incore *entry = NULL; + struct hlist_node *node; + + pthread_mutex_lock(&hashtable_lock[h]); + if (hlist_empty(head)) + goto not_found; + + hlist_for_each_entry(entry, node, head, hash) { + if (entry->raw.oid == oid) + goto out; + } +not_found: + if (create) { + entry = xzalloc(sizeof(*entry)); + entry->raw.oid = oid; + get_entry(entry, head); + } +out: + pthread_mutex_unlock(&hashtable_lock[h]); + return entry; +} + +int trunk_init(void) +{ + DIR *dir; + struct dirent *d; + uint64_t oid; + + dir = opendir(obj_path); + if (!dir) + return -1; + + while ((d = readdir(dir))) { + if (!strncmp(d->d_name, ".", 1)) + continue; + oid = strtoull(d->d_name, NULL, 16); + if (oid == 0 || oid == ULLONG_MAX) + continue; + lookup_trunk_entry(oid, 1); + } + closedir(dir); + return 0; +} + +static int fill_entry_new_sha1(struct trunk_entry_incore *entry) +{ + struct strbuf buf = STRBUF_INIT; + int fd, ret = 0; + struct sha1_file_hdr hdr = { .priv = 0 }; + + memcpy(hdr.tag, TAG_DATA, TAG_LEN); + strbuf_addstr(&buf, obj_path); + strbuf_addf(&buf, "%016" PRIx64, entry->raw.oid); + fd = open(buf.buf, O_RDONLY); + strbuf_reset(&buf); + + if (fd < 0) { + ret = -1; + goto out; + } + if (!strbuf_read(&buf, fd, SD_DATA_OBJ_SIZE) == SD_DATA_OBJ_SIZE) { + ret = -1; + close(fd); + goto out; + } + hdr.size = buf.len; + strbuf_insert(&buf, 0, &hdr, sizeof(hdr)); + + if (sha1_file_write((void *)buf.buf, buf.len, entry->raw.sha1) < 0) { + ret = -1; + close(fd); + goto out; + } + dprintf("oid: %"PRIx64"\n", entry->raw.oid); +out: + strbuf_release(&buf); + return ret; +} + +static inline int trunk_entry_no_sha1(struct trunk_entry_incore *entry) +{ + return !strlen((char *)entry->raw.sha1); +} + +static void inline put_entry(struct trunk_entry_incore *entry) +{ + hlist_del(&entry->hash); + pthread_mutex_lock(&active_list_lock); + list_del(&entry->active_list); + trunk_entry_active_nr--; + pthread_mutex_unlock(&active_list_lock); + free(entry); +} + +int trunk_file_write(unsigned char *outsha1, int user) +{ + struct strbuf buf; + uint64_t data_size = sizeof(struct trunk_entry) * trunk_entry_active_nr; + struct sha1_file_hdr hdr = { .size = data_size, + .priv = trunk_entry_active_nr }; + struct trunk_entry_incore *entry, *t; + int ret = 0; + + memcpy(hdr.tag, TAG_TRUNK, TAG_LEN); + strbuf_init(&buf, sizeof(hdr) + data_size); + + strbuf_add(&buf, &hdr, sizeof(hdr)); + list_for_each_entry_safe(entry, t, &trunk_active_list, active_list) { + if (trunk_entry_no_sha1(entry) || trunk_entry_is_dirty(entry)) { + if (fill_entry_new_sha1(entry) < 0) { + ret = -1; + goto out; + } + } + strbuf_add(&buf, &entry->raw, sizeof(struct trunk_entry)); + undirty_trunk_entry(entry); + } + if (sha1_file_write((void *)buf.buf, buf.len, outsha1) < 0) { + ret = -1; + goto out; + } + dprintf("trunk sha1: %s\n", sha1_to_hex(outsha1)); +out: + strbuf_release(&buf); + return ret; +} + +void *trunk_file_read(unsigned char *sha1, struct sha1_file_hdr *outhdr) +{ + void *buffer; + + dprintf("%s\n", sha1_to_hex(sha1)); + buffer = sha1_file_read(sha1, outhdr); + if (!buffer) + return NULL; + if (strcmp(outhdr->tag, TAG_TRUNK) != 0) { + free(buffer); + return NULL; + } + + return buffer; +} + +int trunk_update_entry(uint64_t oid) +{ + struct trunk_entry_incore *entry; + + entry = lookup_trunk_entry(oid, 1); + if (!trunk_entry_is_dirty(entry)) + dirty_trunk_entry(entry); + + return 0; +} + +void trunk_reset(void) +{ + struct trunk_entry_incore *entry, *t; + list_for_each_entry_safe(entry, t, &trunk_active_list, active_list) { + /* This is supposed to be called by format operation, so no lock needed */ + put_entry(entry); + } + eprintf("%s\n", trunk_entry_active_nr ? "WARN: active_list not clean" : + "clean"); +} -- 1.7.8.2 |