[vfs] Plumb a hash table around to track koids of tokens
This is a reland of 0f31d965b40a34d3da7447c6494fe4b641793dd1, now that
https://fuchsia-review.googlesource.com/c/garnet/+/248677 has landed
and all clients of libfs have correct header dependencies.
Original change's description:
> [vfs] Plumb a hash table around to track koids of tokens
>
> Rather than rely on set/get_cookie
>
> Test: fs unit tests
> Change-Id: I03d6b5de4eae90e8bbcef09958fb983b84e86e64
Test: fs unit tests
Change-Id: If1632111373282bd82be113e5760c255fc3ef0f0
diff --git a/system/ulib/fs/include/fs/mount_channel.h b/system/ulib/fs/include/fs/mount_channel.h
new file mode 100644
index 0000000..65a7551
--- /dev/null
+++ b/system/ulib/fs/include/fs/mount_channel.h
@@ -0,0 +1,51 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <zircon/types.h>
+
+#ifdef __Fuchsia__
+#include <lib/zx/channel.h>
+#include <fs/client.h>
+#endif // __Fuchsia__
+
+#include <utility>
+
+namespace fs {
+
+#ifdef __Fuchsia__
+
+// MountChannel functions exactly the same as a channel, except that it
+// intentionally destructs by sending a clean "shutdown" signal to the
+// underlying filesystem. Up until the point that a remote handle is
+// attached to a vnode, this wrapper guarantees not only that the
+// underlying handle gets closed on error, but also that the sub-filesystem
+// is released (which cleans up the underlying connection to the block
+// device).
+class MountChannel {
+public:
+ constexpr MountChannel() = default;
+ explicit MountChannel(zx_handle_t handle)
+ : channel_(handle) {}
+ explicit MountChannel(zx::channel channel)
+ : channel_(std::move(channel)) {}
+ MountChannel(MountChannel&& other)
+ : channel_(std::move(other.channel_)) {}
+
+ zx::channel TakeChannel() { return std::move(channel_); }
+
+ ~MountChannel() {
+ if (channel_.is_valid()) {
+ vfs_unmount_handle(channel_.release(), 0);
+ }
+ }
+
+private:
+ zx::channel channel_;
+};
+
+#endif // __Fuchsia__
+
+} // namespace fs
diff --git a/system/ulib/fs/include/fs/vfs.h b/system/ulib/fs/include/fs/vfs.h
index 26898ba..3e4e271 100644
--- a/system/ulib/fs/include/fs/vfs.h
+++ b/system/ulib/fs/include/fs/vfs.h
@@ -22,8 +22,11 @@
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <lib/zx/vmo.h>
+#include <fbl/intrusive_hash_table.h>
#include <fbl/mutex.h>
#include <fs/client.h>
+#include <fs/mount_channel.h>
+#include <fs/vnode.h>
#endif // __Fuchsia__
#include <fbl/function.h>
@@ -69,39 +72,6 @@
void* p;
} vdircookie_t;
-#ifdef __Fuchsia__
-
-// MountChannel functions exactly the same as a channel, except that it
-// intentionally destructs by sending a clean "shutdown" signal to the
-// underlying filesystem. Up until the point that a remote handle is
-// attached to a vnode, this wrapper guarantees not only that the
-// underlying handle gets closed on error, but also that the sub-filesystem
-// is released (which cleans up the underlying connection to the block
-// device).
-class MountChannel {
-public:
- constexpr MountChannel() = default;
- explicit MountChannel(zx_handle_t handle)
- : channel_(handle) {}
- explicit MountChannel(zx::channel channel)
- : channel_(std::move(channel)) {}
- MountChannel(MountChannel&& other)
- : channel_(std::move(other.channel_)) {}
-
- zx::channel TakeChannel() { return std::move(channel_); }
-
- ~MountChannel() {
- if (channel_.is_valid()) {
- vfs_unmount_handle(channel_.release(), 0);
- }
- }
-
-private:
- zx::channel channel_;
-};
-
-#endif // __Fuchsia__
-
// The Vfs object contains global per-filesystem state, which
// may be valid across a collection of Vnodes.
//
@@ -216,6 +186,8 @@
zx_status_t UninstallRemoteLocked(fbl::RefPtr<Vnode> vn,
zx::channel* h) FS_TA_REQUIRES(vfs_lock_);
+ fbl::HashTable<zx_koid_t, std::unique_ptr<VnodeToken>> vnode_tokens_;
+
// Non-intrusive node in linked list of vnodes acting as mount points
class MountNode final : public fbl::DoublyLinkedListable<fbl::unique_ptr<MountNode>> {
public:
diff --git a/system/ulib/fs/include/fs/vnode.h b/system/ulib/fs/include/fs/vnode.h
index 20d7e00..299a2b1 100644
--- a/system/ulib/fs/include/fs/vnode.h
+++ b/system/ulib/fs/include/fs/vnode.h
@@ -10,12 +10,14 @@
#include <sys/types.h>
#include <fbl/function.h>
+#include <fbl/intrusive_single_list.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/macros.h>
#include <fbl/ref_counted_internal.h>
#include <fbl/ref_ptr.h>
+#include <fbl/string_piece.h>
+#include <fs/mount_channel.h>
#include <fs/ref_counted.h>
-#include <fs/vfs.h>
#include <lib/fdio/io.h>
#include <lib/fdio/vfs.h>
#include <zircon/assert.h>
@@ -33,6 +35,9 @@
namespace fs {
+class Vfs;
+typedef struct vdircookie vdircookie_t;
+
inline bool vfs_valid_name(fbl::StringPiece name) {
return name.length() <= NAME_MAX &&
memchr(name.data(), '/', name.length()) == nullptr &&
@@ -243,4 +248,24 @@
const size_t len_;
};
+// Helper class to track outstanding operations associated to a
+// particular Vnode.
+class VnodeToken : public fbl::SinglyLinkedListable<std::unique_ptr<VnodeToken>> {
+public:
+ VnodeToken(zx_koid_t koid, fbl::RefPtr<Vnode> vnode) :
+ koid_(koid), vnode_(std::move(vnode)) {
+ }
+
+ zx_koid_t get_koid() const { return koid_; }
+ fbl::RefPtr<Vnode> get_vnode() const { return vnode_; }
+
+ // Trait implementation for fbl::HashTable
+ zx_koid_t GetKey() const { return koid_; }
+ static size_t GetHash(zx_koid_t koid) { return koid; }
+
+private:
+ zx_koid_t koid_;
+ fbl::RefPtr<Vnode> vnode_;
+};
+
} // namespace fs
diff --git a/system/ulib/fs/lazy-dir.cpp b/system/ulib/fs/lazy-dir.cpp
index a5846e6..1b5d750 100644
--- a/system/ulib/fs/lazy-dir.cpp
+++ b/system/ulib/fs/lazy-dir.cpp
@@ -3,6 +3,9 @@
// found in the LICENSE file.
#include <fs/lazy-dir.h>
+
+#include <fs/vfs.h>
+#include <fs/vnode.h>
#include <fuchsia/io/c/fidl.h>
namespace fs {
diff --git a/system/ulib/fs/pseudo-file.cpp b/system/ulib/fs/pseudo-file.cpp
index 3d88090..741e938 100644
--- a/system/ulib/fs/pseudo-file.cpp
+++ b/system/ulib/fs/pseudo-file.cpp
@@ -6,6 +6,9 @@
#include <utility>
+#include <fs/vfs.h>
+#include <zircon/device/vfs.h>
+
namespace fs {
PseudoFile::PseudoFile(ReadHandler read_handler, WriteHandler write_handler)
diff --git a/system/ulib/fs/service.cpp b/system/ulib/fs/service.cpp
index 6135311..aca03d9 100644
--- a/system/ulib/fs/service.cpp
+++ b/system/ulib/fs/service.cpp
@@ -6,6 +6,8 @@
#include <utility>
+#include <zircon/device/vfs.h>
+
namespace fs {
Service::Service(Connector connector)
diff --git a/system/ulib/fs/vfs.cpp b/system/ulib/fs/vfs.cpp
index d3de1b1..c066081 100644
--- a/system/ulib/fs/vfs.cpp
+++ b/system/ulib/fs/vfs.cpp
@@ -244,6 +244,14 @@
#define TOKEN_RIGHTS (ZX_RIGHTS_BASIC)
+namespace {
+zx_koid_t GetTokenKoid(const zx::event& token) {
+ zx_info_handle_basic_t info = {};
+ token.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
+ return info.koid;
+}
+} // namespace
+
void Vfs::TokenDiscard(zx::event ios_token) {
fbl::AutoLock lock(&vfs_lock_);
if (ios_token) {
@@ -256,13 +264,12 @@
//
// By cleared the token cookie, any remaining handles to the event will
// be ignored by the filesystem server.
- ios_token.set_cookie(*zx::process::self(), 0);
+ auto rename_request = vnode_tokens_.erase(GetTokenKoid(ios_token));
}
}
zx_status_t Vfs::VnodeToToken(fbl::RefPtr<Vnode> vn, zx::event* ios_token,
zx::event* out) {
- uint64_t vnode_cookie = reinterpret_cast<uint64_t>(vn.get());
zx_status_t r;
fbl::AutoLock lock(&vfs_lock_);
@@ -280,28 +287,22 @@
return r;
} else if ((r = new_ios_token.duplicate(TOKEN_RIGHTS, &new_token) != ZX_OK)) {
return r;
- } else if ((r = new_ios_token.set_cookie(*zx::process::self(), vnode_cookie)) != ZX_OK) {
- return r;
}
+ auto koid = GetTokenKoid(new_ios_token);
+ vnode_tokens_.insert(std::make_unique<VnodeToken>(koid, std::move(vn)));
*ios_token = std::move(new_ios_token);
*out = std::move(new_token);
return ZX_OK;
}
zx_status_t Vfs::TokenToVnode(zx::event token, fbl::RefPtr<Vnode>* out) {
- uint64_t vcookie;
- zx_status_t r;
- if ((r = token.get_cookie(*zx::process::self(), &vcookie)) < 0) {
+ const auto& vnode_token = vnode_tokens_.find(GetTokenKoid(token));
+ if (vnode_token == vnode_tokens_.end()) {
// TODO(smklein): Return a more specific error code for "token not from this server"
return ZX_ERR_INVALID_ARGS;
}
- if (vcookie == 0) {
- // Client closed the channel associated with the token
- return ZX_ERR_INVALID_ARGS;
- }
-
- *out = fbl::RefPtr<fs::Vnode>(reinterpret_cast<fs::Vnode*>(vcookie));
+ *out = vnode_token->get_vnode();
return ZX_OK;
}
diff --git a/system/ulib/fs/vmo-file.cpp b/system/ulib/fs/vmo-file.cpp
index 8c8acc1..33dae23 100644
--- a/system/ulib/fs/vmo-file.cpp
+++ b/system/ulib/fs/vmo-file.cpp
@@ -9,8 +9,10 @@
#include <fbl/algorithm.h>
#include <fbl/auto_lock.h>
+#include <fs/vfs.h>
#include <fuchsia/io/c/fidl.h>
#include <zircon/assert.h>
+#include <zircon/device/vfs.h>
#include <zircon/syscalls.h>
namespace fs {
diff --git a/system/utest/fs-vnode/lazy-dir-tests.cpp b/system/utest/fs-vnode/lazy-dir-tests.cpp
index bcf8034..105d79e 100644
--- a/system/utest/fs-vnode/lazy-dir-tests.cpp
+++ b/system/utest/fs-vnode/lazy-dir-tests.cpp
@@ -5,6 +5,7 @@
#include <fbl/vector.h>
#include <fs/lazy-dir.h>
#include <fs/pseudo-file.h>
+#include <fs/vfs.h>
#include <unittest/unittest.h>
#include <utility>
diff --git a/system/utest/fs-vnode/pseudo-file-tests.cpp b/system/utest/fs-vnode/pseudo-file-tests.cpp
index 6157488..f4a24f4 100644
--- a/system/utest/fs-vnode/pseudo-file-tests.cpp
+++ b/system/utest/fs-vnode/pseudo-file-tests.cpp
@@ -7,6 +7,7 @@
#include <fbl/vector.h>
#include <initializer_list>
#include <unittest/unittest.h>
+#include <zircon/device/vfs.h>
#define EXPECT_FSTR_EQ(expected, actual) \
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(expected.c_str()), \
diff --git a/system/utest/fs-vnode/vmo-file-tests.cpp b/system/utest/fs-vnode/vmo-file-tests.cpp
index 16cd497..b2c11a7 100644
--- a/system/utest/fs-vnode/vmo-file-tests.cpp
+++ b/system/utest/fs-vnode/vmo-file-tests.cpp
@@ -7,6 +7,7 @@
#include <limits.h>
#include <fuchsia/io/c/fidl.h>
+#include <zircon/device/vfs.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>