| // 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. |
| |
| #include <fcntl.h> |
| |
| #include <atomic> |
| #include <new> |
| |
| #include <fbl/ref_ptr.h> |
| #include <lib/fdio/namespace.h> |
| #include <lib/zxio/null.h> |
| #include <zircon/types.h> |
| #include <zircon/device/vfs.h> |
| |
| #include "../private.h" |
| #include "local-filesystem.h" |
| #include "local-vnode.h" |
| |
| namespace fdio_internal { |
| namespace { |
| |
| // The directory represents a local directory (either / or |
| // some directory between / and a mount point), so it has |
| // to emulate directory behavior. |
| struct LocalConnection { |
| zxio_t io; |
| |
| // Although these are raw pointers for C compatibility, they are |
| // actually strong references to both the namespace and vnode object. |
| // |
| // On close, they must be destroyed. |
| const fdio_namespace* fs; |
| const LocalVnode* vn; |
| |
| // Readdir sequence number. |
| std::atomic<int32_t> seq; |
| }; |
| |
| static_assert(offsetof(LocalConnection, io) == 0, |
| "LocalConnection must be castable to zxio_t"); |
| |
| static_assert(sizeof(LocalConnection) <= sizeof(zxio_storage_t), |
| "LocalConnection must fit inside zxio_storage_t."); |
| |
| LocalConnection* fdio_get_zxio_dir(fdio_t* io) { |
| return reinterpret_cast<LocalConnection*>(fdio_get_zxio(io)); |
| } |
| |
| zx_status_t zxio_dir_close(fdio_t* io) { |
| LocalConnection* dir = fdio_get_zxio_dir(io); |
| // Reclaim a strong reference to |fs| which was leaked during |
| // |CreateLocalConnection()| |
| __UNUSED auto fs = fbl::internal::MakeRefPtrNoAdopt<const fdio_namespace>(dir->fs); |
| __UNUSED auto vn = fbl::internal::MakeRefPtrNoAdopt<const LocalVnode>(dir->vn); |
| dir->fs = nullptr; |
| dir->vn = nullptr; |
| return ZX_OK; |
| } |
| |
| // Expects a canonical path (no ..) with no leading |
| // slash and no trailing slash |
| zx_status_t zxio_dir_open(fdio_t* io, const char* path, uint32_t flags, |
| uint32_t mode, fdio_t** out) { |
| LocalConnection* dir = fdio_get_zxio_dir(io); |
| |
| return dir->fs->Open(fbl::WrapRefPtr(dir->vn), path, flags, mode, out); |
| } |
| |
| zx_status_t zxio_dir_get_attr(fdio_t* io, fuchsia_io_NodeAttributes* attr) { |
| memset(attr, 0, sizeof(*attr)); |
| attr->mode = V_TYPE_DIR | V_IRUSR; |
| attr->id = fuchsia_io_INO_UNKNOWN; |
| attr->link_count = 1; |
| return ZX_OK; |
| } |
| |
| zx_status_t zxio_dir_rewind(fdio_t* io) { |
| LocalConnection* dir = fdio_get_zxio_dir(io); |
| dir->seq.store(0); |
| return ZX_OK; |
| } |
| |
| zx_status_t zxio_dir_readdir(fdio_t* io, void* ptr, size_t max, size_t* out_actual) { |
| LocalConnection* dir = fdio_get_zxio_dir(io); |
| int n = dir->seq.fetch_add(1); |
| if (n != 0) { |
| *out_actual = 0; |
| return ZX_OK; |
| } |
| return dir->fs->Readdir(*dir->vn, ptr, max, out_actual); |
| } |
| |
| zx_status_t zxio_dir_unlink(fdio_t* io, const char* path, size_t len) { |
| return ZX_ERR_UNAVAILABLE; |
| } |
| |
| constexpr fdio_ops_t kLocalConnectionOps = []() { |
| fdio_ops_t ops = {}; |
| ops.get_attr = zxio_dir_get_attr; |
| ops.close = zxio_dir_close; |
| ops.open = zxio_dir_open; |
| ops.clone = fdio_default_clone; |
| ops.ioctl = fdio_default_ioctl; |
| ops.wait_begin = fdio_default_wait_begin; |
| ops.wait_end = fdio_default_wait_end; |
| ops.unwrap = fdio_default_unwrap; |
| ops.posix_ioctl = fdio_default_posix_ioctl; |
| ops.get_vmo = fdio_default_get_vmo; |
| ops.get_token = fdio_default_get_token; |
| ops.set_attr = fdio_default_set_attr; |
| ops.readdir = zxio_dir_readdir; |
| ops.rewind = zxio_dir_rewind; |
| ops.unlink = zxio_dir_unlink; |
| ops.truncate = fdio_default_truncate; |
| ops.rename = fdio_default_rename; |
| ops.link = fdio_default_link; |
| ops.get_flags = fdio_default_get_flags; |
| ops.set_flags = fdio_default_set_flags; |
| ops.recvfrom = fdio_default_recvfrom; |
| ops.sendto = fdio_default_sendto; |
| ops.recvmsg = fdio_default_recvmsg; |
| ops.sendmsg = fdio_default_sendmsg; |
| ops.shutdown = fdio_default_shutdown; |
| return ops; |
| }(); |
| |
| } // namespace |
| |
| fdio_t* CreateLocalConnection(fbl::RefPtr<const fdio_namespace> fs, |
| fbl::RefPtr<const LocalVnode> vn) { |
| fdio_t* io = fdio_alloc(&kLocalConnectionOps); |
| if (io == nullptr) { |
| return nullptr; |
| } |
| // Invoke placement new on the new LocalConnection. Since the object is trivially |
| // destructible, we can avoid invoking the destructor. |
| static_assert(std::is_trivially_destructible<LocalConnection>::value, |
| "LocalConnection must have trivial destructor"); |
| char* storage = reinterpret_cast<char*>(fdio_get_zxio_dir(io)); |
| LocalConnection* dir = new (storage) LocalConnection(); |
| zxio_null_init(&(fdio_get_zxio_storage(io)->io)); |
| |
| // Leak a strong reference to |this| which will be reclaimed |
| // in |zxio_dir_close()|. |
| dir->fs = fs.leak_ref(); |
| dir->vn = vn.leak_ref(); |
| return io; |
| } |
| |
| } // namespace fdio_internal |