(yeah, the summary line needs s/cld:/chunkd:/) Just committed the following change to chunkd's storage backend API, preparing chunkd for variable-length opaque binary keys. commit 0a8c3225355de86484eba71279c646f7790d5bb1 Author: Jeff Garzik <jeff@xxxxxxxxxx> Date: Wed Nov 4 00:44:55 2009 -0500 cld: update storage backend to store based on opaque binary key + key length Change internal structure to utilize an opaque binary key, rather than the existing probably-nul-terminated, fixed length C hexidecimal string key. Storage backend now names files based on the SHA256 of the key. The key, in its entirety, is written to each object's header in the filesystem, to assist in recovery. This work prepares the server for an API and protocol change permitting arbitrary length binary keys. Signed-off-by: Jeff Garzik <jgarzik@xxxxxxxxxx> diff --git a/include/chunk_msg.h b/include/chunk_msg.h index 38c7364..610f8c5 100644 --- a/include/chunk_msg.h +++ b/include/chunk_msg.h @@ -31,7 +31,7 @@ enum errcode { NoSuchKey, PreconditionFailed, SignatureDoesNotMatch, - InvalidCookie, + InvalidKey, }; struct chunksrv_req { diff --git a/server/be-fs.c b/server/be-fs.c index 66686af..7371d4c 100644 --- a/server/be-fs.c +++ b/server/be-fs.c @@ -34,6 +34,7 @@ struct fs_obj { struct be_fs_obj_hdr { char checksum[128]; char owner[128]; + uint32_t key_len; }; static struct fs_obj *fs_obj_alloc(void) @@ -52,17 +53,23 @@ static struct fs_obj *fs_obj_alloc(void) return obj; } -static char *fs_obj_pathname(const char *cookie) +static char *fs_obj_pathname(const void *key, size_t key_len) { char *s = NULL; char prefix[5] = ""; struct stat st; size_t slen; + unsigned char md[SHA256_DIGEST_LENGTH]; + char mdstr[(SHA256_DIGEST_LENGTH * 2) + 1]; - /* cookies are guaranteed elsewhere to be at least 7 chars */ - memcpy(prefix, cookie, 4); + SHA256(key, key_len, md); + hexstr(md, SHA256_DIGEST_LENGTH, mdstr); - slen = strlen(chunkd_srv.vol_path) + strlen(prefix) + strlen(cookie) + 3; + memcpy(prefix, mdstr, 4); + + slen = strlen(chunkd_srv.vol_path) + 1 + + strlen(prefix) + 1 + + strlen(mdstr) + 1; s = malloc(slen); if (!s) return NULL; @@ -85,7 +92,7 @@ static char *fs_obj_pathname(const char *cookie) goto err_out; } - sprintf(s, "%s/%s/%s", chunkd_srv.vol_path, prefix, cookie); + sprintf(s, "%s/%s/%s", chunkd_srv.vol_path, prefix, mdstr); return s; @@ -94,35 +101,15 @@ err_out: return NULL; } -static bool cookie_valid(const char *cookie) +static bool key_valid(const void *key, size_t key_len) { - int len = 0; - - /* empty strings are not valid cookies */ - if (!cookie || !*cookie) + if (!key || key_len < 1 || key_len > CHD_KEY_SZ) return false; - - /* cookies MUST consist of 100% lowercase hex digits */ - while (*cookie) { - switch (*cookie) { - case '0' ... '9': - case 'a' ... 'f': - cookie++; - len++; - break; - - default: - return false; - } - } - - if (len < STD_COOKIE_MIN) - return false; - + return true; } -struct backend_obj *fs_obj_new(const char *cookie, +struct backend_obj *fs_obj_new(const void *key, size_t key_len, enum errcode *err_code) { struct fs_obj *obj; @@ -132,10 +119,8 @@ struct backend_obj *fs_obj_new(const char *cookie, memset(&hdr, 0, sizeof(hdr)); - if (!cookie_valid(cookie)) { - if (debugging) - applog(LOG_ERR, "Bad cookie '%s'", cookie); - *err_code = InvalidCookie; + if (!key_valid(key, key_len)) { + *err_code = InvalidKey; return NULL; } @@ -146,7 +131,7 @@ struct backend_obj *fs_obj_new(const char *cookie, } /* build local fs pathname */ - fn = fs_obj_pathname(cookie); + fn = fs_obj_pathname(key, key_len); if (!fn) { applog(LOG_ERR, "OOM in object_put"); *err_code = InternalError; @@ -164,7 +149,7 @@ struct backend_obj *fs_obj_new(const char *cookie, goto err_out; } - /* write object header */ + /* write fixed-length portion of object header */ wrc = write(obj->out_fd, &hdr, sizeof(hdr)); if (wrc != sizeof(hdr)) { if (wrc < 0) @@ -177,8 +162,22 @@ struct backend_obj *fs_obj_new(const char *cookie, goto err_out; } + /* write variable-length portion of object header */ + wrc = write(obj->out_fd, key, key_len); + if (wrc != key_len) { + if (wrc < 0) + applog(LOG_ERR, "obj hdr key write(%s) failed: %s", + fn, strerror(errno)); + else + applog(LOG_ERR, "obj hdr key write(%s) failed for %s", + fn, "unknown raisins!!!"); + *err_code = InternalError; + goto err_out; + } + obj->out_fn = fn; - strcpy(obj->bo.cookie, cookie); + obj->bo.key = g_memdup(key, key_len); + obj->bo.key_len = key_len; return &obj->bo; @@ -187,18 +186,16 @@ err_out: return NULL; } -struct backend_obj *fs_obj_open(const char *user, const char *cookie, - enum errcode *err_code) +struct backend_obj *fs_obj_open(const char *user, const void *key, + size_t key_len, enum errcode *err_code) { struct fs_obj *obj; struct stat st; struct be_fs_obj_hdr hdr; ssize_t rrc; - if (!cookie_valid(cookie)) { - if (debugging) - applog(LOG_ERR, "Bad cookie '%s'", cookie); - *err_code = InvalidCookie; + if (!key_valid(key, key_len)) { + *err_code = InvalidKey; return NULL; } @@ -209,7 +206,7 @@ struct backend_obj *fs_obj_open(const char *user, const char *cookie, } /* build local fs pathname */ - obj->in_fn = fs_obj_pathname(cookie); + obj->in_fn = fs_obj_pathname(key, key_len); if (!obj->in_fn) { *err_code = InternalError; goto err_out; @@ -233,7 +230,7 @@ struct backend_obj *fs_obj_open(const char *user, const char *cookie, goto err_out_fd; } - /* read object header */ + /* read object fixed-length header */ rrc = read(obj->in_fd, &hdr, sizeof(hdr)); if (rrc != sizeof(hdr)) { if (rrc < 0) @@ -252,9 +249,35 @@ struct backend_obj *fs_obj_open(const char *user, const char *cookie, goto err_out_fd; } + /* verify object key length matches input key length */ + if (GUINT32_FROM_LE(hdr.key_len) != key_len) { + *err_code = InternalError; + goto err_out_fd; + } + + obj->bo.key = malloc(key_len); + obj->bo.key_len = key_len; + if (!obj->bo.key) { + *err_code = InternalError; + goto err_out_fd; + } + + /* read object variable-length header */ + rrc = read(obj->in_fd, obj->bo.key, key_len); + if ((rrc != key_len) || (memcmp(key, obj->bo.key, key_len))) { + if (rrc < 0) + applog(LOG_ERR, "read hdr key obj(%s) failed: %s", + obj->in_fn, strerror(errno)); + else + applog(LOG_ERR, "invalid object header key for %s", + obj->in_fn); + *err_code = InternalError; + goto err_out_fd; + } + strncpy(obj->bo.hashstr, hdr.checksum, sizeof(obj->bo.hashstr)); obj->bo.hashstr[sizeof(obj->bo.hashstr) - 1] = 0; - obj->bo.size = st.st_size - sizeof(hdr); + obj->bo.size = st.st_size - sizeof(hdr) - key_len; obj->bo.mtime = st.st_mtime; return &obj->bo; @@ -278,6 +301,9 @@ void fs_obj_free(struct backend_obj *bo) obj = bo->private; g_assert(obj != NULL); + if (bo->key) + free(bo->key); + if (obj->out_fn) { unlink(obj->out_fn); free(obj->out_fn); @@ -327,8 +353,10 @@ ssize_t fs_obj_sendfile(struct backend_obj *bo, int out_fd, size_t len) struct fs_obj *obj = bo->private; ssize_t rc; - if (obj->sendfile_ofs == 0) + if (obj->sendfile_ofs == 0) { obj->sendfile_ofs += sizeof(struct be_fs_obj_hdr); + obj->sendfile_ofs += bo->key_len; + } rc = sendfile(out_fd, obj->in_fd, &obj->sendfile_ofs, len); if (rc < 0) @@ -346,8 +374,10 @@ ssize_t fs_obj_sendfile(struct backend_obj *bo, int out_fd, size_t len) ssize_t rc; off_t sbytes = 0; - if (obj->sendfile_ofs == 0) + if (obj->sendfile_ofs == 0) { obj->sendfile_ofs += sizeof(struct be_fs_obj_hdr); + obj->sendfile_ofs += bo->key_len; + } rc = sendfile(obj->in_fd, out_fd, obj->sendfile_ofs, len, NULL, &sbytes, 0); @@ -382,6 +412,7 @@ bool fs_obj_write_commit(struct backend_obj *bo, const char *user, memset(&hdr, 0, sizeof(hdr)); strncpy(hdr.checksum, hashstr, sizeof(hdr.checksum)); strncpy(hdr.owner, user, sizeof(hdr.owner)); + hdr.key_len = GUINT32_TO_LE(bo->key_len); /* go back to beginning of file */ if (lseek(obj->out_fd, 0, SEEK_SET) < 0) { @@ -418,7 +449,8 @@ bool fs_obj_write_commit(struct backend_obj *bo, const char *user, return true; } -bool fs_obj_delete(const char *user, const char *cookie, enum errcode *err_code) +bool fs_obj_delete(const char *user, const void *key, size_t key_len, + enum errcode *err_code) { char *fn = NULL; int fd; @@ -427,10 +459,13 @@ bool fs_obj_delete(const char *user, const char *cookie, enum errcode *err_code) *err_code = InternalError; - /* FIXME: check owner */ + if (!key_valid(key, key_len)) { + *err_code = InvalidKey; + return false; + } /* build local fs pathname */ - fn = fs_obj_pathname(cookie); + fn = fs_obj_pathname(key, key_len); if (!fn) goto err_out; @@ -528,12 +563,13 @@ GList *fs_list_objs(const char *user) struct volume_entry *ve; void *p; size_t alloc_len; + void *key_in; + uint32_t key_len_in; if (de->d_name[0] == '.') continue; - fn = fs_obj_pathname(de->d_name); - if (!fn) + if (asprintf(&fn, "%s/%s", sub, de->d_name) < 0) break; fd = open(fn, O_RDONLY); @@ -545,6 +581,7 @@ GList *fs_list_objs(const char *user) if (fstat(fd, &st) < 0) { syslogerr(fn); + close(fd); free(fn); break; } @@ -555,10 +592,38 @@ GList *fs_list_objs(const char *user) syslogerr(fn); else applog(LOG_ERR, "%s hdr read failed", fn); + close(fd); + free(fn); + break; + } + + key_len_in = GUINT32_FROM_LE(hdr.key_len); + if (key_len_in < 1 || key_len_in > CHD_KEY_SZ) { + applog(LOG_ERR, "%s hdr key len invalid", fn); + close(fd); free(fn); break; } + key_in = malloc(key_len_in); + if (!key_in) { + close(fd); + free(fn); + break; + } + + rrc = read(fd, key_in, key_len_in); + if (rrc != key_len_in) { + if (rrc < 0) + syslogerr(fn); + else + applog(LOG_ERR, "%s hdr read failed", fn); + close(fd); + free(fn); + free(key_in); + break; + } + if (close(fd) < 0) syslogerr(fn); @@ -572,7 +637,6 @@ GList *fs_list_objs(const char *user) /* one alloc, for fixed + var length struct */ alloc_len = sizeof(*ve) + - strlen(de->d_name) + 1 + strlen(hdr.checksum) + 1 + strlen(hdr.owner) + 1; @@ -584,19 +648,19 @@ GList *fs_list_objs(const char *user) /* store fixed-length portion of struct */ st.st_size -= sizeof(struct be_fs_obj_hdr); + st.st_size -= key_len_in; + ve->size = st.st_size; ve->mtime = st.st_mtime; + ve->key = key_in; + ve->key_len = key_len_in; /* * store variable-length portion of struct: - * name, checksum, owner strings + * checksum, owner strings */ p = (ve + 1); - ve->name = p; - strcpy(ve->name, de->d_name); - - p += strlen(ve->name) + 1; ve->hash = p; strcpy(ve->hash, hdr.checksum); diff --git a/server/chunkd.h b/server/chunkd.h index b7cdfcf..d2418d1 100644 --- a/server/chunkd.h +++ b/server/chunkd.h @@ -107,7 +107,8 @@ struct client { struct backend_obj { void *private; - char cookie[MAX_COOKIE_LEN + 1]; + void *key; + size_t key_len; uint64_t size; time_t mtime; @@ -134,7 +135,8 @@ struct geo { struct volume_entry { unsigned long long size; /* obj size */ time_t mtime; /* obj last-mod time */ - char *name; /* obj id */ + void *key; /* obj id */ + int key_len; char *hash; /* obj SHA1 checksum */ char *owner; /* obj owner username */ }; @@ -183,16 +185,18 @@ struct server { }; /* be-fs.c */ -extern struct backend_obj *fs_obj_new(const char *cookie, +extern struct backend_obj *fs_obj_new(const void *kbuf, size_t klen, enum errcode *err_code); -extern struct backend_obj *fs_obj_open(const char *user, const char *cookie, +extern struct backend_obj *fs_obj_open(const char *user, + const void *kbuf, size_t klen, enum errcode *err_code); extern ssize_t fs_obj_write(struct backend_obj *bo, const void *ptr, size_t len); extern ssize_t fs_obj_read(struct backend_obj *bo, void *ptr, size_t len); extern void fs_obj_free(struct backend_obj *bo); extern bool fs_obj_write_commit(struct backend_obj *bo, const char *user, const char *hashstr, bool sync_data); -extern bool fs_obj_delete(const char *user, const char *cookie, +extern bool fs_obj_delete(const char *user, + const void *kbuf, size_t klen, enum errcode *err_code); extern GList *fs_list_objs(const char *user); extern ssize_t fs_obj_sendfile(struct backend_obj *bo, int out_fd, size_t len); @@ -223,7 +227,7 @@ extern void timer_add(struct timer *timer, time_t expires); extern void timer_del(struct timer *timer); extern time_t timers_run(void); extern char *time2str(char *strbuf, time_t time); -extern void shastr(const unsigned char *digest, char *outstr); +extern void hexstr(const unsigned char *buf, size_t buf_len, char *outstr); static inline void timer_init(struct timer *timer, const char *name, void (*cb)(struct timer *), diff --git a/server/object.c b/server/object.c index 503770c..f49cd63 100644 --- a/server/object.c +++ b/server/object.c @@ -17,7 +17,7 @@ static bool object_get_more(struct client *cli, struct client_write *wr, bool object_del(struct client *cli) { - const char *obj_name = cli->creq.key; + const char *obj_key = cli->creq.key; int rc; enum errcode err = InternalError; bool rcb; @@ -31,7 +31,8 @@ bool object_del(struct client *cli) memcpy(resp, &cli->creq, sizeof(cli->creq)); - rcb = fs_obj_delete(cli->creq.user, obj_name, &err); + rcb = fs_obj_delete(cli->creq.user, obj_key, + strnlen(obj_key, CHD_KEY_SZ), &err); if (!rcb) return cli_err(cli, err, true); @@ -78,7 +79,7 @@ static bool object_put_end(struct client *cli) cli->state = evt_recycle; SHA1_Final(md, &cli->out_hash); - shastr(md, hashstr); + hexstr(md, SHA_DIGEST_LENGTH, hashstr); rcb = fs_obj_write_commit(cli->out_bo, cli->out_user, hashstr, cli->out_sync); @@ -191,7 +192,7 @@ bool object_put(struct client *cli) if (!user) return cli_err(cli, AccessDenied, true); - cli->out_bo = fs_obj_new(key, &err); + cli->out_bo = fs_obj_new(key, strnlen(key, CHD_KEY_SZ), &err); if (!cli->out_bo) return cli_err(cli, err, true); @@ -269,7 +270,7 @@ err_out_buf: bool object_get(struct client *cli, bool want_body) { - const char *obj_name = cli->creq.key; + const char *obj_key = cli->creq.key; int rc; enum errcode err = InternalError; struct backend_obj *obj; @@ -283,7 +284,8 @@ bool object_get(struct client *cli, bool want_body) memcpy(resp, &cli->creq, sizeof(cli->creq)); - cli->in_obj = obj = fs_obj_open(cli->creq.user, obj_name, &err); + cli->in_obj = obj = fs_obj_open(cli->creq.user, obj_key, + strnlen(obj_key, CHD_KEY_SZ), &err); if (!obj) { free(resp); return cli_err(cli, err, true); diff --git a/server/server.c b/server/server.c index bed5208..878ec56 100644 --- a/server/server.c +++ b/server/server.c @@ -118,9 +118,9 @@ static struct { { "SignatureDoesNotMatch", 403, "The calculated request signature does not match your provided one" }, - [InvalidCookie] = - { "InvalidCookie", 400, - "Cookie check failed" }, + [InvalidKey] = + { "InvalidKey", 400, + "Invalid key presented" }, }; void applog(int prio, const char *fmt, ...) @@ -738,13 +738,16 @@ static bool volume_list(struct client *cli) tmpl = res; while (tmpl) { - char timestr[50]; + char timestr[50], *esc_key; struct volume_entry *ve; ve = tmpl->data; tmpl = tmpl->next; - s = g_markup_printf_escaped( + /* copy-and-escape key into nul-terminated buffer */ + esc_key = g_markup_escape_text(ve->key, ve->key_len); + + s = g_strdup_printf( " <Contents>\r\n" " <Name>%s</Name>\r\n" " <LastModified>%s</LastModified>\r\n" @@ -753,7 +756,7 @@ static bool volume_list(struct client *cli) " <Owner>%s</Owner>\r\n" " </Contents>\r\n", - ve->name, + esc_key, time2str(timestr, ve->mtime), ve->hash, ve->size, @@ -761,6 +764,8 @@ static bool volume_list(struct client *cli) content = g_list_append(content, s); + free(esc_key); + free(ve->key); free(ve); } diff --git a/server/util.c b/server/util.c index e43623b..9d06174 100644 --- a/server/util.c +++ b/server/util.c @@ -155,17 +155,17 @@ int fsetflags(const char *prefix, int fd, int or_flags) return rc; } -void shastr(const unsigned char *digest, char *outstr) +void hexstr(const unsigned char *buf, size_t buf_len, char *outstr) { static const char hex[] = "0123456789abcdef"; int i; - for (i = 0; i < SHA_DIGEST_LENGTH; i++) { - outstr[i * 2] = hex[(digest[i] & 0xF0) >> 4]; - outstr[(i * 2) + 1] = hex[(digest[i] & 0x0F) ]; + for (i = 0; i < buf_len; i++) { + outstr[i * 2] = hex[(buf[i] & 0xF0) >> 4]; + outstr[(i * 2) + 1] = hex[(buf[i] & 0x0F) ]; } - outstr[SHA_DIGEST_LENGTH * 2] = 0; + outstr[buf_len * 2] = 0; } char *time2str(char *strbuf, time_t src_time) -- To unsubscribe from this list: send the line "unsubscribe hail-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html