From c4a85f5fad24896de2efe1fb179933633cf69d83 Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Fri, 10 Jul 2020 13:50:14 -0400 Subject: [PATCH] readdir: add dirent index array This removes the risk of corrupted data being sent back to the kernel should it use the offset from one opendir-readdir in another. In this case it would at most skip dirents. According to the standards the offset is only valid when used within the opendir -> releasedir but NFS uses offsets across independent calls. --- libfuse/include/fuse_dirents.h | 2 + libfuse/include/kvec.h | 90 ++++++++++++++++++++++++++ libfuse/lib/fuse.c | 45 ++++++------- libfuse/lib/fuse_dirents.c | 114 ++++++++++++++++----------------- 4 files changed, 166 insertions(+), 85 deletions(-) create mode 100644 libfuse/include/kvec.h diff --git a/libfuse/include/fuse_dirents.h b/libfuse/include/fuse_dirents.h index 8bd9e82f..8c8459bc 100644 --- a/libfuse/include/fuse_dirents.h +++ b/libfuse/include/fuse_dirents.h @@ -22,6 +22,7 @@ extern "C" { #endif +#include "kvec.h" #include "fuse_dirent.h" #include "fuse_direntplus.h" #include "fuse_entry.h" @@ -48,6 +49,7 @@ struct fuse_dirents_s char *buf; uint64_t buf_len; uint64_t data_len; + kvec_t(uint32_t) offs; fuse_dirents_type_t type; }; diff --git a/libfuse/include/kvec.h b/libfuse/include/kvec.h new file mode 100644 index 00000000..038de9e7 --- /dev/null +++ b/libfuse/include/kvec.h @@ -0,0 +1,90 @@ +/* The MIT License + + Copyright (c) 2008, by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: + + #include "kvec.h" + int main() { + kvec_t(int) array; + kv_init(array); + kv_push(int, array, 10); // append + kv_a(int, array, 20) = 5; // dynamic + kv_A(array, 20) = 4; // static + kv_destroy(array); + return 0; + } +*/ + +/* + 2008-09-22 (0.1.0): + + * The initial version. + + */ + +#ifndef AC_KVEC_H +#define AC_KVEC_H + +#include + +#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) + +#define kvec_t(type) struct { size_t n, m; type *a; } +#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) +#define kv_destroy(v) free((v).a) +#define kv_A(v, i) ((v).a[(i)]) +#define kv_pop(v) ((v).a[--(v).n]) +#define kv_size(v) ((v).n) +#define kv_max(v) ((v).m) + +#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m)) + +#define kv_copy(type, v1, v0) do { \ + if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \ + (v1).n = (v0).n; \ + memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ + } while (0) \ + +#define kv_push(type, v, x) do { \ + if ((v).n == (v).m) { \ + (v).m = (v).m? (v).m<<1 : 2; \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \ + } \ + (v).a[(v).n++] = (x); \ + } while (0) + +#define kv_pushp(type, v) (((v).n == (v).m)? \ + ((v).m = ((v).m? (v).m<<1 : 2), \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ + : 0), ((v).a + ((v).n++)) + +#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \ + ((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ + : (v).n <= (size_t)(i)? (v).n = (i) + 1 \ + : 0), (v).a[(i)]) + +#endif diff --git a/libfuse/lib/fuse.c b/libfuse/lib/fuse.c index e3453ff5..2c3c7392 100644 --- a/libfuse/lib/fuse.c +++ b/libfuse/lib/fuse.c @@ -3387,20 +3387,24 @@ readdir_plus_fill(struct fuse *f_, } static -uint64_t -convert_plus2normal(fuse_dirents_t *d_, - uint64_t off_) +size_t +readdir_buf_size(fuse_dirents_t *d_, + size_t size_, + off_t off_) { - uint64_t ino; - fuse_dirent_t *d; - fuse_direntplus_t *dp; - - dp = (fuse_direntplus_t*)&d_->buf[off_]; - ino = dp->dirent.ino; - fuse_dirents_convert_plus2normal(d_); - d = fuse_dirents_find(d_,ino); + if(off_ >= kv_size(d_->offs)) + return 0; + if((kv_A(d_->offs,off_) + size_) > d_->data_len) + return (d_->data_len - kv_A(d_->offs,off_)); + return size_; +} - return d->off; +static +char* +readdir_buf(fuse_dirents_t *d_, + off_t off_) +{ + return &d_->buf[kv_A(d_->offs,off_)]; } static @@ -3433,16 +3437,10 @@ fuse_lib_readdir(fuse_req_t req_, goto out; } - if(off_ >= d->data_len) - size_ = 0; - else if((off_ + size_) > d->data_len) - size_ = (d->data_len - off_); - - /* if((size_ > 0) && (d->type == PLUS)) */ - /* off_ = convert_plus2normal(d,off_); */ + size_ = readdir_buf_size(d,size_,off_); fuse_reply_buf(req_, - &d->buf[off_], + readdir_buf(d,off_), size_); out: @@ -3479,13 +3477,10 @@ fuse_lib_readdir_plus(fuse_req_t req_, goto out; } - if(off_ >= d->data_len) - size_ = 0; - else if((off_ + size_) > d->data_len) - size_ = (d->data_len - off_); + size_ = readdir_buf_size(d,size_,off_); fuse_reply_buf(req_, - &d->buf[off_], + readdir_buf(d,off_), size_); out: diff --git a/libfuse/lib/fuse_dirents.c b/libfuse/lib/fuse_dirents.c index a59f9222..baf23282 100644 --- a/libfuse/lib/fuse_dirents.c +++ b/libfuse/lib/fuse_dirents.c @@ -54,8 +54,8 @@ fuse_direntplus_size(const uint64_t namelen_) static int -fuse_dirents_resize(fuse_dirents_t *d_, - uint64_t size_) +fuse_dirents_buf_resize(fuse_dirents_t *d_, + uint64_t size_) { void *p; @@ -74,19 +74,44 @@ fuse_dirents_resize(fuse_dirents_t *d_, static void* -fuse_dirents_alloc(fuse_dirents_t *d_, - uint64_t size_) +fuse_dirents_dirent_alloc(fuse_dirents_t *d_, + uint64_t namelen_) { int rv; + uint64_t size; fuse_dirent_t *d; - rv = fuse_dirents_resize(d_,size_); + size = fuse_dirent_size(namelen_); + + rv = fuse_dirents_buf_resize(d_,size); if(rv) return NULL; d = (fuse_dirent_t*)&d_->buf[d_->data_len]; - d_->data_len += size_; + d_->data_len += size; + + return d; +} + +static +void* +fuse_dirents_direntplus_alloc(fuse_dirents_t *d_, + uint64_t namelen_) +{ + int rv; + uint64_t size; + fuse_dirent_t *d; + + size = fuse_direntplus_size(namelen_); + + rv = fuse_dirents_buf_resize(d_,size); + if(rv) + return NULL; + + d = (fuse_dirent_t*)&d_->buf[d_->data_len]; + + d_->data_len += size; return d; } @@ -200,38 +225,7 @@ fuse_dirents_find(fuse_dirents_t *d_, int fuse_dirents_convert_plus2normal(fuse_dirents_t *d_) { - int rv; - uint64_t size; - fuse_dirent_t *d; - fuse_dirents_t normal; - fuse_direntplus_t *cur; - fuse_direntplus_t *end; - - rv = fuse_dirents_init(&normal); - if(rv < 0) - return rv; - - cur = (fuse_direntplus_t*)&d_->buf[0]; - end = (fuse_direntplus_t*)&d_->buf[d_->data_len]; - while(cur < end) - { - size = fuse_dirent_size(cur->dirent.namelen); - d = fuse_dirents_alloc(&normal,size); - if(d == NULL) - return -ENOMEM; - - memcpy(d,&cur->dirent,size); - d->off = normal.data_len;; - - cur = fuse_direntplus_next(cur); - } - - fuse_dirents_free(d_); - - normal.type = NORMAL; - *d_ = normal; - - return 0; + return -ENOSYS; } int @@ -239,7 +233,6 @@ fuse_dirents_add(fuse_dirents_t *d_, const struct dirent *dirent_, const uint64_t namelen_) { - uint64_t size; fuse_dirent_t *d; switch(d_->type) @@ -253,14 +246,13 @@ fuse_dirents_add(fuse_dirents_t *d_, return -EINVAL; } - size = fuse_dirent_size(namelen_); - - d = fuse_dirents_alloc(d_,size); + d = fuse_dirents_dirent_alloc(d_,namelen_); if(d == NULL) return -ENOMEM; + d->off = kv_size(d_->offs); + kv_push(uint32_t,d_->offs,d_->data_len); d->ino = dirent_->d_ino; - d->off = d_->data_len; d->namelen = namelen_; d->type = dirent_->d_type; memcpy(d->name,dirent_->d_name,namelen_); @@ -275,7 +267,6 @@ fuse_dirents_add_plus(fuse_dirents_t *d_, const fuse_entry_t *entry_, const struct stat *st_) { - uint64_t size; fuse_direntplus_t *d; switch(d_->type) @@ -289,14 +280,13 @@ fuse_dirents_add_plus(fuse_dirents_t *d_, break; } - size = fuse_direntplus_size(namelen_); - - d = fuse_dirents_alloc(d_,size); + d = fuse_dirents_direntplus_alloc(d_,namelen_); if(d == NULL) return -ENOMEM; + d->dirent.off = kv_size(d_->offs); + kv_push(uint32_t,d_->offs,d_->data_len); d->dirent.ino = dirent_->d_ino; - d->dirent.off = d_->data_len; d->dirent.namelen = namelen_; d->dirent.type = dirent_->d_type; memcpy(d->dirent.name,dirent_->d_name,namelen_); @@ -313,7 +303,6 @@ fuse_dirents_add_linux(fuse_dirents_t *d_, const struct linux_dirent *dirent_, const uint64_t namelen_) { - uint64_t size; fuse_dirent_t *d; switch(d_->type) @@ -327,14 +316,13 @@ fuse_dirents_add_linux(fuse_dirents_t *d_, return -EINVAL; } - size = fuse_dirent_size(namelen_); - - d = fuse_dirents_alloc(d_,size); + d = fuse_dirents_dirent_alloc(d_,namelen_); if(d == NULL) return -ENOMEM; + d->off = kv_size(d_->offs); + kv_push(uint32_t,d_->offs,d_->data_len); d->ino = dirent_->ino; - d->off = d_->data_len; d->namelen = namelen_; d->type = *((char*)dirent_ + dirent_->reclen - 1); memcpy(d->name,dirent_->name,namelen_); @@ -349,7 +337,6 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_, const fuse_entry_t *entry_, const struct stat *st_) { - uint64_t size; fuse_direntplus_t *d; switch(d_->type) @@ -363,14 +350,13 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_, break; } - size = fuse_direntplus_size(namelen_); - - d = fuse_dirents_alloc(d_,size); + d = fuse_dirents_direntplus_alloc(d_,namelen_); if(d == NULL) return -ENOMEM; + d->dirent.off = kv_size(d_->offs); + kv_push(uint32_t,d_->offs,d_->data_len); d->dirent.ino = dirent_->ino; - d->dirent.off = d_->data_len; d->dirent.namelen = namelen_; d->dirent.type = *((char*)dirent_ + dirent_->reclen - 1); memcpy(d->dirent.name,dirent_->name,namelen_); @@ -385,8 +371,9 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_, void fuse_dirents_reset(fuse_dirents_t *d_) { - d_->data_len = 0; - d_->type = UNSET; + d_->data_len = 0; + d_->type = UNSET; + kv_size(d_->offs) = 1; } int @@ -403,6 +390,10 @@ fuse_dirents_init(fuse_dirents_t *d_) d_->data_len = 0; d_->type = UNSET; + kv_init(d_->offs); + kv_resize(uint32_t,d_->offs,64); + kv_push(uint32_t,d_->offs,0); + return 0; } @@ -412,5 +403,8 @@ fuse_dirents_free(fuse_dirents_t *d_) d_->buf_len = 0; d_->data_len = 0; d_->type = UNSET; + + kv_destroy(d_->offs); + free(d_->buf); }