diff --git a/kmod/src/kvec.c b/kmod/src/kvec.c index f87e318d..349e55b6 100644 --- a/kmod/src/kvec.c +++ b/kmod/src/kvec.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "super.h" #include "format.h" @@ -33,6 +34,7 @@ struct iter { struct kvec *kvec; + size_t count; size_t off; size_t i; }; @@ -40,6 +42,7 @@ struct iter { static void iter_advance(struct iter *iter, size_t len) { iter->off += len; + iter->count -= len; while (iter->i < SCOUTFS_KVEC_NR && iter->off >= iter->kvec->iov_len) { iter->off -= iter->kvec->iov_len; @@ -53,6 +56,7 @@ static void iter_init(struct iter *iter, struct kvec *kvec) iter->kvec = kvec; iter->i = 0; iter->off = 0; + iter->count = scoutfs_kvec_length(kvec); iter_advance(iter, 0); } @@ -65,6 +69,7 @@ static void *iter_ptr(struct iter *iter) return NULL; } +/* count of contiguous bytes available at the next vector */ static size_t iter_contig(struct iter *iter) { if (iter->i < SCOUTFS_KVEC_NR) @@ -73,6 +78,12 @@ static size_t iter_contig(struct iter *iter) return 0; } +/* count of bytes remaining in the iteration */ +static size_t iter_count(struct iter *iter) +{ + return iter->count; +} + /* * Return the result of memcmp between the min of the two total lengths. * If their shorter lengths are equal than the shorter length is considered @@ -101,8 +112,9 @@ int scoutfs_kvec_memcmp(struct kvec *a, struct kvec *b) } /* - * Returns 0 if [a,b] overlaps with [c,d]. Returns -1 if a < c and - * 1 if b > d. + * Return -1 if [a,b] doesn't overlap with and is to the left of [c,d], + * 1 if it doesn't overlap and is to the right of, and 0 if they + * overlap. */ int scoutfs_kvec_cmp_overlap(struct kvec *a, struct kvec *b, struct kvec *c, struct kvec *d) @@ -181,8 +193,10 @@ int scoutfs_kvec_dup_flatten(struct kvec *dst, struct kvec *src) size_t len = scoutfs_kvec_length(src); ptr = kmalloc(len, GFP_NOFS); - if (!ptr) + if (!ptr) { + scoutfs_kvec_init_null(dst); return -ENOMEM; + } scoutfs_kvec_init(dst, ptr, len); scoutfs_kvec_memcpy(dst, src); @@ -215,3 +229,184 @@ void scoutfs_kvec_swap(struct kvec *a, struct kvec *b) memcpy(a, b, SCOUTFS_KVEC_BYTES); memcpy(b, tmp, SCOUTFS_KVEC_BYTES); } + +int scoutfs_kvec_alloc_key(struct kvec *kvec) +{ + const size_t len = SCOUTFS_MAX_KEY_SIZE; + void *ptr; + + ptr = kzalloc(len, GFP_NOFS); + if (!ptr) { + scoutfs_kvec_init_null(kvec); + return -ENOMEM; + } + + scoutfs_kvec_init(kvec, ptr, len); + return 0; +} + +void scoutfs_kvec_init_key(struct kvec *kvec) +{ + scoutfs_kvec_init(kvec, kvec[0].iov_base, SCOUTFS_MAX_KEY_SIZE); +} + +void scoutfs_kvec_set_max_key(struct kvec *kvec) +{ + __u8 *type = kvec[0].iov_base; + + *type = SCOUTFS_MAX_UNUSED_KEY; + scoutfs_kvec_init(kvec, type, 1); +} + +/* + * Clone the source kvec into the dst if the dst is empty or if + * the src kvec is less than the dst. + */ +void scoutfs_kvec_clone_less(struct kvec *dst, struct kvec *src) +{ + if (scoutfs_kvec_length(dst) == 0 || + scoutfs_kvec_memcmp(src, dst) < 0) + scoutfs_kvec_clone(dst, src); +} + +/* + * Copy bytes from the kvec iterator into the dest buffer, zeroing the + * remainder of the buffer if there aren't enough bytes available in + * the iterator. If the tail bool is set then the kvec data is copied + * into the tail of the buffer and the head is zeroed. + */ +static bool iter_memcpy_zero(void *dst, struct iter *src, size_t len, bool tail) +{ + size_t ctg; + size_t diff; + + if (len == 0 || iter_count(src) == 0) + return false; + + if (iter_count(src) < len) { + diff = len - iter_count(src); + if (tail) { + memset(dst, 0, diff); + dst += diff; + } else { + memset(dst + len - diff, 0, diff); + } + len = iter_count(src); + } + + while ((ctg = min(len, iter_contig(src)))) { + memcpy(dst, iter_ptr(src), ctg); + iter_advance(src, ctg); + dst += ctg; + len -= ctg; + } + + return true; +} + +static int iter_puts_printable(char *dst, struct iter *src) +{ + int len = iter_count(src); + size_t ctg; + int i; + + while ((ctg = iter_contig(src))) { + memcpy(dst, iter_ptr(src), ctg); + iter_advance(src, ctg); + + for (i = 0; i < ctg; i++) { + if (!isprint(dst[i])) + dst[i] = '_'; + } + + dst += ctg; + } + + return len; +} + +#define EMPTY_STR "''" +#define U64_U_BYTES 20 +#define U64_D_BYTES 21 +#define U64_X_BYTES 16 + +/* + * XXX figure out what to do about corrupt keys. + */ + +unsigned scoutfs_kvec_key_strlen(struct kvec *key) +{ + struct iter iter; + unsigned len = 0; + u8 type; + + iter_init(&iter, key); + + if (iter_count(&iter) == 0) { + len = sizeof(EMPTY_STR) - 1; + goto out; + } + + iter_memcpy_zero(&type, &iter, sizeof(type), false); + + len = 4; /* "typ." */ + + switch(type) { + case SCOUTFS_INODE_KEY: + len += U64_U_BYTES; + break; + case SCOUTFS_DIRENT_KEY: + len += U64_U_BYTES + (iter_count(&iter) - 8); + break; + case SCOUTFS_MAX_UNUSED_KEY: + break; + default: + /* hex of everything after the type */ + len += (scoutfs_kvec_length(key) - 1) * 2; + break; + } + +out: + return len + 1; /* null term */ +} + +void scoutfs_kvec_key_sprintf(char *buf, struct kvec *key) +{ + struct iter iter; + __be64 be; + u8 type; + + iter_init(&iter, key); + + if (iter_contig(&iter) == 0) { + buf += sprintf(buf, EMPTY_STR); + goto done; + } + + iter_memcpy_zero(&type, &iter, sizeof(type), false); + + switch(type) { + case SCOUTFS_INODE_KEY: + buf += sprintf(buf, "ino."); + iter_memcpy_zero(&be, &iter, sizeof(be), false); + buf += sprintf(buf, "%llu", be64_to_cpu(be)); + break; + case SCOUTFS_DIRENT_KEY: + buf += sprintf(buf, "den."); + iter_memcpy_zero(&be, &iter, sizeof(be), false); + buf += sprintf(buf, "%llu.", be64_to_cpu(be)); + buf += iter_puts_printable(buf, &iter); + break; + case SCOUTFS_MAX_UNUSED_KEY: + buf += sprintf(buf, "max"); + break; + default: + buf += sprintf(buf, "unk."); + while (iter_memcpy_zero(&be, &iter, sizeof(be), true)) + buf += sprintf(buf, "%llx", be64_to_cpu(be)); + break; + } + +done: + *buf = '\0'; +} diff --git a/kmod/src/kvec.h b/kmod/src/kvec.h index 2eb6528b..93ddcd36 100644 --- a/kmod/src/kvec.h +++ b/kmod/src/kvec.h @@ -62,5 +62,11 @@ int scoutfs_kvec_dup_flatten(struct kvec *dst, struct kvec *src); void scoutfs_kvec_kfree(struct kvec *kvec); void scoutfs_kvec_init_null(struct kvec *kvec); void scoutfs_kvec_swap(struct kvec *a, struct kvec *b); +int scoutfs_kvec_alloc_key(struct kvec *kvec); +void scoutfs_kvec_init_key(struct kvec *kvec); +void scoutfs_kvec_set_max_key(struct kvec *kvec); +void scoutfs_kvec_clone_less(struct kvec *dst, struct kvec *src); +unsigned scoutfs_kvec_key_strlen(struct kvec *key); +void scoutfs_kvec_key_sprintf(char *buf, struct kvec *key); #endif