Compare commits
2 Commits
2da50a7b5a
...
3b23cad061
Author | SHA1 | Date |
---|---|---|
Vitaliy Filippov | 3b23cad061 | |
Vitaliy Filippov | e0c16c9aa0 |
356
src/nfs_conn.cpp
356
src/nfs_conn.cpp
|
@ -1550,172 +1550,80 @@ struct nfs_kv_readdir_state
|
||||||
std::vector<entryplus3> entries;
|
std::vector<entryplus3> entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void kv_getattr_next(nfs_kv_readdir_state *st)
|
static void nfs3_readdir_common(void *opaque, rpc_op_t *rop, bool is_plus)
|
||||||
{
|
{
|
||||||
while (st->is_plus && st->getattr_cur < st->entries.size() && st->getattr_running < st->self->parent->readdir_getattr_parallel)
|
auto st = new nfs_kv_rename_state;
|
||||||
{
|
st->self = (nfs_client_t*)opaque;
|
||||||
auto idx = st->getattr_cur++;
|
st->rop = rop;
|
||||||
st->getattr_running++;
|
if (is_plus)
|
||||||
kv_read_inode(st->self, st->entries[idx]->fileid, [st, idx](int res, const std::string & value, json11::Json ientry)
|
st->args = *((READDIRPLUS3args*)rop->request);
|
||||||
{
|
|
||||||
if (res == 0)
|
|
||||||
{
|
|
||||||
st->entries[idx]->name_attributes = (post_op_attr){
|
|
||||||
// FIXME: maybe do not read parent attributes and leave them to a GETATTR?
|
|
||||||
.attributes_follow = 1,
|
|
||||||
.attributes = get_kv_attributes(self, st->entries[idx]->fileid, ientry),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
st->getattr_running--;
|
|
||||||
kv_getattr_next(st);
|
|
||||||
if (st->getattr_running == 0 && !st->list_handle)
|
|
||||||
{
|
|
||||||
nfs_kv_continue_readdir(st, 4);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nfs_kv_continue_readdir(nfs_kv_readdir_state *st, int state)
|
|
||||||
{
|
|
||||||
if (state == 0) {}
|
|
||||||
else if (state == 1) goto resume_1;
|
|
||||||
else if (state == 2) goto resume_2;
|
|
||||||
else if (state == 3) goto resume_3;
|
|
||||||
else if (state == 4) goto resume_4;
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf("BUG: invalid state in nfs_kv_continue_readdir()");
|
READDIR3args *in_args = ((READDIR3args*)rop->request);
|
||||||
abort();
|
st->args.dir = in_args->dir;
|
||||||
|
st->args.cookie = in_args->cookie;
|
||||||
|
*((uint64_t*)st->args.cookieverf) = *((uint64_t*)in_args->cookieverf);
|
||||||
|
st->args.dircount = 512;
|
||||||
|
st->args.maxcount = in_args->count;
|
||||||
}
|
}
|
||||||
|
st->dir_ino = kv_fh_inode(st->args.dir);
|
||||||
st->prefix = kv_direntry_key(dir_ino, "");
|
st->prefix = kv_direntry_key(dir_ino, "");
|
||||||
|
st->list_handle = self->parent->db->list_start(prefix);
|
||||||
st->eof = true;
|
st->eof = true;
|
||||||
// Limit results based on maximum reply size
|
// Limit results based on maximum reply size
|
||||||
// Sadly we have to calculate reply size by hand
|
// Sadly we have to calculate reply size by hand
|
||||||
// reply without entries is 4+4+(dir_attributes ? sizeof(fattr3) : 0)+8+4 bytes
|
// reply without entries is 4+4+(dir_attributes ? sizeof(fattr3) : 0)+8+4 bytes
|
||||||
st->reply_size = 20;
|
st->reply_size = 20;
|
||||||
if (reply_size > st->args.maxcount)
|
if (st->reply_size > st->args.maxcount)
|
||||||
{
|
{
|
||||||
// Error, too small max reply size
|
// Error, too small max reply size
|
||||||
auto cb = std::move(st->cb);
|
if (is_plus)
|
||||||
cb(-NFS3ERR_TOOSMALL);
|
{
|
||||||
|
READDIRPLUS3res *reply = (READDIRPLUS3res*)rop->reply;
|
||||||
|
*reply = (READDIRPLUS3res){ .status = NFS3ERR_TOOSMALL };
|
||||||
|
rpc_queue_reply(rop);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
READDIR3res *reply = (READDIR3res*)rop->reply;
|
||||||
|
*reply = (READDIR3res){ .status = NFS3ERR_TOOSMALL };
|
||||||
|
rpc_queue_reply(rop);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Add . and ..
|
// Add . and ..
|
||||||
if (st->args.cookie <= 1)
|
if (st->args.cookie == 0)
|
||||||
{
|
{
|
||||||
kv_read_inode(st->self, kv_fh_inode(st->args.dir), [st](int res, const std::string & value, json11::Json ientry)
|
kv_read_inode(st->self, kv_fh_inode(st->args.dir), [st](int res, const std::string & value, json11::Json ientry)
|
||||||
{
|
{
|
||||||
st->res = res;
|
|
||||||
st->ientry_text = value;
|
|
||||||
st->ientry = ientry;
|
|
||||||
nfs_kv_continue_readdir(st, 1);
|
|
||||||
});
|
});
|
||||||
return;
|
auto dir_id_it = self->parent->dir_info.find(dir);
|
||||||
resume_1:
|
fill_dir_entry(self, rop, dir_id_it, &entries["."], is_plus);
|
||||||
if (st->res < 0)
|
auto sl = dir.rfind("/");
|
||||||
|
if (sl != std::string::npos)
|
||||||
{
|
{
|
||||||
auto cb = std::move(st->cb);
|
auto dir_id_it = self->parent->dir_info.find(dir.substr(0, sl));
|
||||||
cb(st->res);
|
fill_dir_entry(self, rop, dir_id_it, &entries[".."], is_plus);
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (st->args.cookie == 0)
|
|
||||||
{
|
|
||||||
auto fh = kv_fh(st->dir_ino);
|
|
||||||
auto entry_size = 20 + 4/*len_pad4(".")*/ + (is_plus ? 8 + 88 + len_pad4(fh.size()) : 0);
|
|
||||||
if (st->reply_size + entry_size > st->args.maxcount)
|
|
||||||
{
|
|
||||||
auto cb = std::move(st->cb);
|
|
||||||
cb(-NFS3ERR_TOOSMALL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entryplus3 dot;
|
|
||||||
dot.name = xdr_copy_string(st->rop->xdrs, ".");
|
|
||||||
dot.fileid = st->dir_ino;
|
|
||||||
dot.name_attributes = (post_op_attr){
|
|
||||||
.attributes_follow = 1,
|
|
||||||
.attributes = get_kv_attributes(self, st->dir_ino, st->ientry),
|
|
||||||
};
|
|
||||||
dot.name_handle = (post_op_fh3){
|
|
||||||
.handle_follows = 1,
|
|
||||||
.handle = xdr_copy_string(fh),
|
|
||||||
};
|
|
||||||
st->entries.push_back(dot);
|
|
||||||
st->reply_size += entry_size;
|
|
||||||
}
|
|
||||||
st->parent_ino = st->ientry["parent_ino"].uint64_value();
|
|
||||||
if (st->parent_ino)
|
|
||||||
{
|
|
||||||
kv_read_inode(st->self, st->ientry["parent_ino"].uint64_value(), [st](int res, const std::string & value, json11::Json ientry)
|
|
||||||
{
|
|
||||||
st->res = res;
|
|
||||||
st->parent_ientry_text = value;
|
|
||||||
st->parent_ientry = ientry;
|
|
||||||
nfs_kv_continue_readdir(st, 2);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
resume_2:
|
|
||||||
if (st->res < 0)
|
|
||||||
{
|
|
||||||
auto cb = std::move(st->cb);
|
|
||||||
cb(st->res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto fh = kv_fh(st->parent_ino);
|
|
||||||
auto entry_size = 20 + 4/*len_pad4("..")*/ + (is_plus ? 8 + 88 + len_pad4(fh.size()) : 0);
|
|
||||||
if (st->reply_size + entry_size > st->args.maxcount)
|
|
||||||
{
|
|
||||||
st->eof = false;
|
|
||||||
auto cb = std::move(st->cb);
|
|
||||||
cb(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entryplus3 dotdot;
|
|
||||||
dotdot.name = xdr_copy_string(st->rop->xdrs, "..");
|
|
||||||
dotdot.fileid = st->dir_ino;
|
|
||||||
dotdot.name_attributes = (post_op_attr){
|
|
||||||
// FIXME: maybe do not read parent attributes and leave them to a GETATTR?
|
|
||||||
.attributes_follow = 1,
|
|
||||||
.attributes = get_kv_attributes(self, st->parent_ino, st->parent_ientry),
|
|
||||||
};
|
|
||||||
dotdot.name_handle = (post_op_fh3){
|
|
||||||
.handle_follows = 1,
|
|
||||||
.handle = xdr_copy_string(fh),
|
|
||||||
};
|
|
||||||
st->entries.push_back(dotdot);
|
|
||||||
st->reply_size += entry_size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
st->getattr_cur = st->entries.size();
|
|
||||||
st->list_handle = self->parent->db->list_start(prefix);
|
|
||||||
st->self->parent->db->list_next(st->list_handle, [=](int res, const std::string & key, const std::string & value)
|
st->self->parent->db->list_next(st->list_handle, [=](int res, const std::string & key, const std::string & value)
|
||||||
{
|
{
|
||||||
st->res = res;
|
if (res == -ENOENT || key.size() > prefix.size() || key.substr(0, prefix.size()) != prefix)
|
||||||
st->cur_key = key;
|
|
||||||
st->cur_value = value;
|
|
||||||
nfs_kv_continue_readdir(st, 3);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
while (st->list_handle)
|
|
||||||
{
|
{
|
||||||
st->self->parent->db->list_next(st->list_handle, NULL);
|
self->parent->db->list_close(list_handle);
|
||||||
return;
|
return;
|
||||||
resume_3:
|
|
||||||
if (st->res == -ENOENT || st->key.size() > st->prefix.size() || st->key.substr(0, st->prefix.size()) != st->prefix)
|
|
||||||
{
|
|
||||||
self->parent->db->list_close(st->list_handle);
|
|
||||||
st->list_handle = NULL;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
auto direntry = json11::Json::parse(st->cur_value, err);
|
auto direntry = json11::Json::parse(value, err);
|
||||||
if (err != "")
|
if (err != "")
|
||||||
{
|
{
|
||||||
fprintf(stderr, "readdir: direntry %s contains invalid JSON: %s, skipping\n",
|
fprintf(stderr, "readdir: direntry %s contains invalid JSON: %s, skipping\n",
|
||||||
st->cur_key.c_str(), st->cur_value.c_str());
|
key.c_str(), value.c_str());
|
||||||
continue;
|
self->parent->db->list_next(list_handle);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
auto ino = direntry["ino"].uint64_value();
|
auto ino = direntry["ino"].uint64_value();
|
||||||
auto name = kv_direntry_filename(st->cur_key);
|
auto name = kv_direntry_filename(key);
|
||||||
auto fh = kv_fh(ino);
|
auto fh = kv_fh(ino);
|
||||||
// 1 entry3 is (8+4+(filename_len+3)/4*4+8) bytes
|
// 1 entry3 is (8+4+(filename_len+3)/4*4+8) bytes
|
||||||
// 1 entryplus3 is (8+4+(filename_len+3)/4*4+8
|
// 1 entryplus3 is (8+4+(filename_len+3)/4*4+8
|
||||||
|
@ -1726,35 +1634,91 @@ resume_3:
|
||||||
{
|
{
|
||||||
st->eof = false;
|
st->eof = false;
|
||||||
self->parent->db->list_close(list_handle);
|
self->parent->db->list_close(list_handle);
|
||||||
st->list_handle = NULL;
|
return;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
st->reply_size += entry_size;
|
|
||||||
auto idx = st->entries.size();
|
auto idx = st->entries.size();
|
||||||
st->entries.push_back((entryplus3){});
|
st->entries.push_back((entryplus3){});
|
||||||
auto entry = &st->entries[idx];
|
auto entry = &st->entries[idx];
|
||||||
entry->name = xdr_copy_string(st->rop->xdrs, name);
|
entry->name = xdr_copy_string(rop->xdrs, name);
|
||||||
entry->fileid = ino;
|
entry->fileid = ino;
|
||||||
if (st->is_plus)
|
if (is_plus)
|
||||||
{
|
{
|
||||||
|
st->getattr_queue.push_back(idx);
|
||||||
|
if (st->getattr_running < self->parent->readdir_getattr_parallel)
|
||||||
|
{
|
||||||
|
kv_readdir_getattr_next(st);
|
||||||
|
}
|
||||||
entry->name_handle = (post_op_fh3){
|
entry->name_handle = (post_op_fh3){
|
||||||
.handle_follows = 1,
|
.handle_follows = 1,
|
||||||
.handle = xdr_copy_string(fh),
|
.handle = xdr_copy_string(fh),
|
||||||
};
|
};
|
||||||
kv_getattr_next(st);
|
|
||||||
}
|
}
|
||||||
st->self->parent->db->list_next(list_handle);
|
self->parent->db->list_next(list_handle);
|
||||||
}
|
});
|
||||||
resume_4:
|
|
||||||
while (st->getattr_running > 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
for (auto & ic: self->parent->cli->st_cli.inode_config)
|
||||||
{
|
{
|
||||||
return;
|
auto & inode_cfg = ic.second;
|
||||||
|
if (prefix != "" && inode_cfg.name.substr(0, prefix.size()) != prefix)
|
||||||
|
continue;
|
||||||
|
std::string subname = inode_cfg.name.substr(prefix.size());
|
||||||
|
int p = 0;
|
||||||
|
while (p < subname.size() && subname[p] == '/')
|
||||||
|
p++;
|
||||||
|
if (p > 0)
|
||||||
|
subname = subname.substr(p);
|
||||||
|
if (subname.size() == 0)
|
||||||
|
continue;
|
||||||
|
p = 0;
|
||||||
|
while (p < subname.size() && subname[p] != '/')
|
||||||
|
p++;
|
||||||
|
if (p >= subname.size())
|
||||||
|
{
|
||||||
|
// fileid will change when the user creates snapshots
|
||||||
|
// however, we hope that clients tolerate it well
|
||||||
|
// Linux does, even though it complains about "fileid changed" in dmesg
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// skip directories, they will be added from dir_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add directories from dir_info
|
||||||
|
for (auto dir_id_it = self->parent->dir_info.lower_bound(prefix);
|
||||||
|
dir_id_it != self->parent->dir_info.end(); dir_id_it++)
|
||||||
|
{
|
||||||
|
if (prefix != "" && dir_id_it->first.substr(0, prefix.size()) != prefix)
|
||||||
|
break;
|
||||||
|
if (dir_id_it->first.size() == prefix.size() ||
|
||||||
|
dir_id_it->first.find("/", prefix.size()) != std::string::npos)
|
||||||
|
continue;
|
||||||
|
std::string subname = dir_id_it->first.substr(prefix.size());
|
||||||
|
// for directories, fileid changes when the user restarts proxy
|
||||||
|
fill_dir_entry(self, rop, dir_id_it, &entries[subname], is_plus);
|
||||||
|
}
|
||||||
|
// Add . and ..
|
||||||
|
{
|
||||||
|
auto dir_id_it = self->parent->dir_info.find(dir);
|
||||||
|
fill_dir_entry(self, rop, dir_id_it, &entries["."], is_plus);
|
||||||
|
auto sl = dir.rfind("/");
|
||||||
|
if (sl != std::string::npos)
|
||||||
|
{
|
||||||
|
auto dir_id_it = self->parent->dir_info.find(dir.substr(0, sl));
|
||||||
|
fill_dir_entry(self, rop, dir_id_it, &entries[".."], is_plus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Offset results by the continuation cookie (equal to index in the listing)
|
||||||
|
uint64_t idx = 1;
|
||||||
void *prev = NULL;
|
void *prev = NULL;
|
||||||
for (int i = 0; i < st->entries.size(); i++)
|
for (auto it = entries.begin(); it != entries.end();)
|
||||||
{
|
{
|
||||||
entryplus3 *entry = &st->entries[i];
|
entryplus3 *entry = &it->second;
|
||||||
entry->cookie = st->offset + i;
|
// First fields of entry3 and entryplus3 are the same: fileid, name, cookie
|
||||||
|
entry->name = xdr_copy_string(rop->xdrs, it->first);
|
||||||
|
entry->cookie = idx++;
|
||||||
if (prev)
|
if (prev)
|
||||||
{
|
{
|
||||||
if (is_plus)
|
if (is_plus)
|
||||||
|
@ -1763,6 +1727,60 @@ resume_4:
|
||||||
((entry3*)prev)->nextentry = (entry3*)entry;
|
((entry3*)prev)->nextentry = (entry3*)entry;
|
||||||
}
|
}
|
||||||
prev = entry;
|
prev = entry;
|
||||||
|
if (args->cookie > 0 && entry->cookie == args->cookie)
|
||||||
|
entries.erase(entries.begin(), ++it);
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
// Now limit results based on maximum reply size
|
||||||
|
// Sadly we have to calculate reply size by hand
|
||||||
|
// reply without entries is 4+4+(dir_attributes ? sizeof(fattr3) : 0)+8+4 bytes
|
||||||
|
int reply_size = 20;
|
||||||
|
if (reply_size > args->maxcount)
|
||||||
|
{
|
||||||
|
// Error, too small max reply size
|
||||||
|
if (is_plus)
|
||||||
|
{
|
||||||
|
READDIRPLUS3res *reply = (READDIRPLUS3res*)rop->reply;
|
||||||
|
*reply = (READDIRPLUS3res){ .status = NFS3ERR_TOOSMALL };
|
||||||
|
rpc_queue_reply(rop);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
READDIR3res *reply = (READDIR3res*)rop->reply;
|
||||||
|
*reply = (READDIR3res){ .status = NFS3ERR_TOOSMALL };
|
||||||
|
rpc_queue_reply(rop);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 1 entry3 is (8+4+(filename_len+3)/4*4+8) bytes
|
||||||
|
// 1 entryplus3 is (8+4+(filename_len+3)/4*4+8
|
||||||
|
// + 4+(name_attributes ? (sizeof(fattr3) = 84) : 0)
|
||||||
|
// + 4+(name_handle ? 4+(handle_len+3)/4*4 : 0)) bytes
|
||||||
|
bool eof = true;
|
||||||
|
for (auto it = entries.begin(); it != entries.end(); it++)
|
||||||
|
{
|
||||||
|
reply_size += 20+len_pad4(it->first.size())+(is_plus
|
||||||
|
? 8+88+len_pad4(it->second.name_handle.handle.size) : 0);
|
||||||
|
if (reply_size > args->maxcount)
|
||||||
|
{
|
||||||
|
// Stop
|
||||||
|
entries.erase(it, entries.end());
|
||||||
|
eof = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (entries.end() != entries.begin())
|
||||||
|
{
|
||||||
|
auto last_it = entries.end();
|
||||||
|
last_it--;
|
||||||
|
if (is_plus)
|
||||||
|
((entryplus3*)&last_it->second)->nextentry = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry3* e = ((entry3*)&last_it->second);
|
||||||
|
e->nextentry = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Send reply
|
// Send reply
|
||||||
if (is_plus)
|
if (is_plus)
|
||||||
|
@ -1784,48 +1802,6 @@ resume_4:
|
||||||
rpc_queue_reply(rop);
|
rpc_queue_reply(rop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nfs3_readdir_common(void *opaque, rpc_op_t *rop, bool is_plus)
|
|
||||||
{
|
|
||||||
auto st = new nfs_kv_readdir_state;
|
|
||||||
st->self = (nfs_client_t*)opaque;
|
|
||||||
st->rop = rop;
|
|
||||||
st->is_plus = is_plus;
|
|
||||||
if (st->is_plus)
|
|
||||||
st->args = *((READDIRPLUS3args*)rop->request);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
READDIR3args *in_args = ((READDIR3args*)rop->request);
|
|
||||||
st->args.dir = in_args->dir;
|
|
||||||
st->args.cookie = in_args->cookie;
|
|
||||||
*((uint64_t*)st->args.cookieverf) = *((uint64_t*)in_args->cookieverf);
|
|
||||||
st->args.dircount = 512;
|
|
||||||
st->args.maxcount = in_args->count;
|
|
||||||
}
|
|
||||||
st->dir_ino = kv_fh_inode(st->args.dir);
|
|
||||||
st->cb = [st](int res)
|
|
||||||
{
|
|
||||||
if (st->is_plus)
|
|
||||||
{
|
|
||||||
READDIRPLUS3res *reply = (READDIRPLUS3res*)rop->reply;
|
|
||||||
if (res < 0)
|
|
||||||
*reply = (READDIRPLUS3res){ .status = vitastor_nfs_map_err(res) };
|
|
||||||
else
|
|
||||||
reply->status = NFS3_OK;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
READDIR3res *reply = (READDIR3res*)rop->reply;
|
|
||||||
if (res < 0)
|
|
||||||
*reply = (READDIR3res){ .status = vitastor_nfs_map_err(res) };
|
|
||||||
else
|
|
||||||
reply->status = NFS3_OK;
|
|
||||||
}
|
|
||||||
rpc_queue_reply(rop);
|
|
||||||
delete st;
|
|
||||||
};
|
|
||||||
nfs_kv_continue_readdir(st, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nfs3_readdir_proc(void *opaque, rpc_op_t *rop)
|
static int nfs3_readdir_proc(void *opaque, rpc_op_t *rop)
|
||||||
{
|
{
|
||||||
nfs3_readdir_common(opaque, rop, false);
|
nfs3_readdir_common(opaque, rop, false);
|
||||||
|
|
Loading…
Reference in New Issue