#define _FILE_OFFSET_BITS 64 #include "fuse_attr.h" #include "fuse_dirent.h" #include "fuse_direntplus.h" #include "fuse_dirents.h" #include "fuse_entry.h" #include "linux_dirent64.h" #include "stat_utils.h" #include #include #include #include #include #include #include /* 32KB - same as glibc getdents buffer size */ #define DEFAULT_SIZE (1024 * 32) static uint64_t round_up(const uint64_t number_, const uint64_t multiple_) { return (((number_ + multiple_ - 1) / multiple_) * multiple_); } static uint64_t align_uint64_t(uint64_t v_) { return ((v_ + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)); } static uint64_t fuse_dirent_size(const uint64_t namelen_) { uint64_t rv; rv = offsetof(fuse_dirent_t,name); rv += namelen_; rv = align_uint64_t(rv); return rv; } static uint64_t fuse_direntplus_size(const uint64_t namelen_) { uint64_t rv; rv = offsetof(fuse_direntplus_t,dirent.name); rv += namelen_; rv = align_uint64_t(rv); return rv; } static int fuse_dirents_buf_resize(fuse_dirents_t *d_, uint64_t size_) { if((kv_size(d_->data) + size_) >= kv_max(d_->data)) { uint64_t new_size; new_size = round_up((kv_size(d_->data) + size_),DEFAULT_SIZE); kv_resize(char,d_->data,new_size); if(d_->data.a == NULL) return -ENOMEM; } return 0; } static void* fuse_dirents_dirent_alloc(fuse_dirents_t *d_, uint64_t namelen_) { int rv; uint64_t size; fuse_dirent_t *d; size = fuse_dirent_size(namelen_); rv = fuse_dirents_buf_resize(d_,size); if(rv) return NULL; d = (fuse_dirent_t*)&kv_end(d_->data); kv_size(d_->data) += 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*)&kv_end(d_->data); kv_size(d_->data) += size; return d; } static void fuse_dirents_fill_attr(fuse_attr_t *attr_, const struct stat *st_) { attr_->ino = st_->st_ino; attr_->size = st_->st_size; attr_->blocks = st_->st_blocks; attr_->atime = st_->st_atime; attr_->mtime = st_->st_mtime; attr_->ctime = st_->st_ctime; attr_->atimensec = ST_ATIM_NSEC(st_); attr_->mtimensec = ST_MTIM_NSEC(st_); attr_->ctimensec = ST_CTIM_NSEC(st_); attr_->mode = st_->st_mode; attr_->nlink = st_->st_nlink; attr_->uid = st_->st_uid; attr_->gid = st_->st_gid; attr_->rdev = st_->st_rdev; attr_->blksize = st_->st_blksize; } fuse_dirent_t* fuse_dirent_next(fuse_dirent_t *cur_) { char *buf; buf = (char*)cur_; buf += fuse_dirent_size(cur_->namelen); return (fuse_dirent_t*)buf; } fuse_direntplus_t* fuse_direntplus_next(fuse_direntplus_t *cur_) { char *buf; buf = (char*)cur_; buf += fuse_direntplus_size(cur_->dirent.namelen); return (fuse_direntplus_t*)buf; } fuse_dirent_t* fuse_dirent_find(fuse_dirents_t *d_, const uint64_t ino_) { fuse_dirent_t *cur; fuse_dirent_t *end; if(d_->type != NORMAL) return NULL; cur = (fuse_dirent_t*)&kv_first(d_->data); end = (fuse_dirent_t*)&kv_end(d_->data); while(cur < end) { if(cur->ino == ino_) return cur; cur = fuse_dirent_next(cur); } return NULL; } fuse_direntplus_t* fuse_direntplus_find(fuse_dirents_t *d_, const uint64_t ino_) { fuse_direntplus_t *cur; fuse_direntplus_t *end; if(d_->type != PLUS) return NULL; cur = (fuse_direntplus_t*)&kv_first(d_->data); end = (fuse_direntplus_t*)&kv_end(d_->data); while(cur < end) { if(cur->dirent.ino == ino_) return cur; cur = fuse_direntplus_next(cur); } return NULL; } void* fuse_dirents_find(fuse_dirents_t *d_, const uint64_t ino_) { switch(d_->type) { default: case UNSET: return NULL; case NORMAL: return fuse_dirent_find(d_,ino_); case PLUS: return fuse_direntplus_find(d_,ino_); } } int fuse_dirents_convert_plus2normal(fuse_dirents_t *d_) { return -ENOSYS; } int fuse_dirents_add(fuse_dirents_t *d_, const struct dirent *dirent_, const uint64_t namelen_) { fuse_dirent_t *d; switch(d_->type) { case UNSET: d_->type = NORMAL; break; case NORMAL: break; case PLUS: return -EINVAL; } d = fuse_dirents_dirent_alloc(d_,namelen_); if(d == NULL) return -ENOMEM; d->off = kv_size(d_->offs); kv_push(uint32_t,d_->offs,kv_size(d_->data)); d->ino = dirent_->d_ino; d->namelen = namelen_; d->type = dirent_->d_type; memcpy(d->name,dirent_->d_name,namelen_); return 0; } int fuse_dirents_add_plus(fuse_dirents_t *d_, const struct dirent *dirent_, const uint64_t namelen_, const fuse_entry_t *entry_, const struct stat *st_) { fuse_direntplus_t *d; switch(d_->type) { case UNSET: d_->type = PLUS; break; case NORMAL: return -EINVAL; case PLUS: break; } 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,kv_size(d_->data)); d->dirent.ino = dirent_->d_ino; d->dirent.namelen = namelen_; d->dirent.type = dirent_->d_type; memcpy(d->dirent.name,dirent_->d_name,namelen_); d->entry = *entry_; fuse_dirents_fill_attr(&d->attr,st_); return 0; } int fuse_dirents_add_linux(fuse_dirents_t *d_, const linux_dirent64_t *dirent_, const uint64_t namelen_) { fuse_dirent_t *d; switch(d_->type) { case UNSET: d_->type = NORMAL; break; case NORMAL: break; case PLUS: return -EINVAL; } d = fuse_dirents_dirent_alloc(d_,namelen_); if(d == NULL) return -ENOMEM; d->off = kv_size(d_->offs); kv_push(uint32_t,d_->offs,kv_size(d_->data)); d->ino = dirent_->ino; d->namelen = namelen_; d->type = dirent_->type; memcpy(d->name,dirent_->name,namelen_); return 0; } int fuse_dirents_add_linux_plus(fuse_dirents_t *d_, const linux_dirent64_t *dirent_, const uint64_t namelen_, const fuse_entry_t *entry_, const struct stat *st_) { fuse_direntplus_t *d; switch(d_->type) { case UNSET: d_->type = PLUS; break; case NORMAL: return -EINVAL; case PLUS: break; } 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,kv_size(d_->data)); d->dirent.ino = dirent_->ino; d->dirent.namelen = namelen_; d->dirent.type = dirent_->type; memcpy(d->dirent.name,dirent_->name,namelen_); d->entry = *entry_; fuse_dirents_fill_attr(&d->attr,st_); return 0; } void fuse_dirents_reset(fuse_dirents_t *d_) { d_->type = UNSET; kv_size(d_->data) = 0; kv_size(d_->offs) = 1; } int fuse_dirents_init(fuse_dirents_t *d_) { d_->type = UNSET; kv_init(d_->data); kv_resize(char,d_->data,DEFAULT_SIZE); if(d_->data.a == NULL) return -ENOMEM; kv_init(d_->offs); kv_resize(uint32_t,d_->offs,64); kv_push(uint32_t,d_->offs,0); return 0; } void fuse_dirents_free(fuse_dirents_t *d_) { kv_destroy(d_->data); kv_destroy(d_->offs); }