diff --git a/src/nfs/nfs_kv_getattr.cpp b/src/nfs/nfs_kv_getattr.cpp index 619ea3a2..f9da64a8 100644 --- a/src/nfs/nfs_kv_getattr.cpp +++ b/src/nfs/nfs_kv_getattr.cpp @@ -13,6 +13,12 @@ void kv_read_inode(nfs_proxy_t *proxy, uint64_t ino, std::function cb, bool allow_cache) { + if (!ino) + { + // Zero value can not exist + cb(-ENOENT, "", json11::Json()); + return; + } auto key = kv_inode_key(ino); proxy->db->get(key, [=](int res, const std::string & value) { @@ -49,7 +55,7 @@ int kv_nfs3_getattr_proc(void *opaque, rpc_op_t *rop) auto ino = kv_fh_inode(fh); if (self->parent->trace) fprintf(stderr, "[%d] GETATTR %ju\n", self->nfs_fd, ino); - if (!kv_fh_valid(fh)) + if (!kv_fh_valid(fh) || !ino) { *reply = (GETATTR3res){ .status = NFS3ERR_INVAL }; rpc_queue_reply(rop); diff --git a/src/nfs/nfs_kv_lookup.cpp b/src/nfs/nfs_kv_lookup.cpp index 5b8c60ef..07b9d8eb 100644 --- a/src/nfs/nfs_kv_lookup.cpp +++ b/src/nfs/nfs_kv_lookup.cpp @@ -43,9 +43,30 @@ int kv_nfs3_lookup_proc(void *opaque, rpc_op_t *rop) uint64_t ino = direntry["ino"].uint64_value(); kv_read_inode(self->parent, ino, [=](int res, const std::string & value, json11::Json ientry) { - if (res < 0) + if (res == -ENOENT) { - *reply = (LOOKUP3res){ .status = vitastor_nfs_map_err(res == -ENOENT ? -EIO : res) }; + *reply = (LOOKUP3res){ + .status = NFS3_OK, + .resok = (LOOKUP3resok){ + .object = xdr_copy_string(rop->xdrs, kv_fh(ino)), + .obj_attributes = { + .attributes_follow = 1, + .attributes = (fattr3){ + .type = (ftype3)0, + .mode = 0666, + .nlink = 1, + .fsid = self->parent->fsid, + .fileid = ino, + }, + }, + }, + }; + rpc_queue_reply(rop); + return; + } + else if (res < 0) + { + *reply = (LOOKUP3res){ .status = vitastor_nfs_map_err(res) }; rpc_queue_reply(rop); return; } diff --git a/src/nfs/nfs_kv_remove.cpp b/src/nfs/nfs_kv_remove.cpp index 4344e16c..6e8f73b7 100644 --- a/src/nfs/nfs_kv_remove.cpp +++ b/src/nfs/nfs_kv_remove.cpp @@ -89,12 +89,23 @@ resume_1: resume_2: if (st->res < 0) { - fprintf(stderr, "error reading inode %s: %s (code %d)\n", - kv_inode_key(st->ino).c_str(), strerror(-st->res), st->res); - auto cb = std::move(st->cb); - cb(st->res); - return; + if (st->res == -ENOENT) + { + // Just delete direntry and skip inode + fprintf(stderr, "direntry %s references a non-existing inode %ju, deleting\n", + kv_direntry_key(st->dir_ino, st->filename).c_str(), st->ino); + st->ino = 0; + } + else + { + fprintf(stderr, "error reading inode %s: %s (code %d)\n", + kv_inode_key(st->ino).c_str(), strerror(-st->res), st->res); + auto cb = std::move(st->cb); + cb(st->res); + return; + } } + else { std::string err; st->ientry = json11::Json::parse(st->ientry_text, err); diff --git a/tests/test_nfs.sh b/tests/test_nfs.sh index 4663903a..00f9c5e4 100755 --- a/tests/test_nfs.sh +++ b/tests/test_nfs.sh @@ -165,4 +165,24 @@ if ls ./testdata/nfs | grep over1; then false; fi [[ "`cat ./testdata/nfs/linked1`" = "BABABA" ]] format_green "rename over existing file ok" +# check listing and removal of a bad direntry +sudo umount ./testdata/nfs/ +build/src/kv/vitastor-kv --etcd_address $ETCD_URL fsmeta set d11/settings.jsonLGNmGn '{"ino": 123}' +sudo mount localhost:/ ./testdata/nfs -o port=2050,mountport=2050,nfsvers=3,soft,nolock,tcp +ls -l ./testdata/nfs +ls -l ./testdata/nfs/settings.jsonLGNmGn +rm ./testdata/nfs/settings.jsonLGNmGn +build/src/kv/vitastor-kv --etcd_address $ETCD_URL fsmeta get d11/settings.jsonLGNmGn 2>&1 | grep '(code -2)' +ls -l ./testdata/nfs + +# repeat with ino=0 +sudo umount ./testdata/nfs/ +build/src/kv/vitastor-kv --etcd_address $ETCD_URL fsmeta set d11/settings.jsonLGNmGn '{"ino": 0}' +sudo mount localhost:/ ./testdata/nfs -o port=2050,mountport=2050,nfsvers=3,soft,nolock,tcp +ls -l ./testdata/nfs +ls -l ./testdata/nfs/settings.jsonLGNmGn +rm ./testdata/nfs/settings.jsonLGNmGn +build/src/kv/vitastor-kv --etcd_address $ETCD_URL fsmeta get d11/settings.jsonLGNmGn 2>&1 | grep '(code -2)' +ls -l ./testdata/nfs + format_green OK