1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <fs/vmo-file.h> 6 7#include <limits.h> 8#include <string.h> 9 10#include <fbl/algorithm.h> 11#include <fbl/auto_lock.h> 12#include <fuchsia/io/c/fidl.h> 13#include <zircon/assert.h> 14#include <zircon/syscalls.h> 15 16namespace fs { 17namespace { 18constexpr uint64_t kVmoFileBlksize = PAGE_SIZE; 19 20zx_rights_t GetVmoRightsForAccessMode(uint32_t flags) { 21 zx_rights_t rights = ZX_RIGHTS_BASIC | ZX_RIGHT_MAP; 22 if (flags & ZX_FS_RIGHT_READABLE) { 23 rights |= ZX_RIGHT_READ; 24 } 25 if (flags & ZX_FS_RIGHT_WRITABLE) { 26 rights |= ZX_RIGHT_WRITE; 27 } 28 if ((flags & ZX_FS_RIGHT_READABLE) & !(flags & ZX_FS_RIGHT_WRITABLE)) { 29 rights |= ZX_RIGHT_EXECUTE; 30 } 31 return rights; 32} 33 34} // namespace 35 36VmoFile::VmoFile(const zx::vmo& unowned_vmo, 37 size_t offset, 38 size_t length, 39 bool writable, 40 VmoSharing vmo_sharing) 41 : vmo_handle_(unowned_vmo.get()), 42 offset_(offset), length_(length), writable_(writable), vmo_sharing_(vmo_sharing) { 43 ZX_DEBUG_ASSERT(vmo_handle_ != ZX_HANDLE_INVALID); 44} 45 46VmoFile::~VmoFile() {} 47 48zx_status_t VmoFile::ValidateFlags(uint32_t flags) { 49 if (flags & ZX_FS_FLAG_DIRECTORY) { 50 return ZX_ERR_NOT_DIR; 51 } 52 if (IsWritable(flags) && !writable_) { 53 return ZX_ERR_ACCESS_DENIED; 54 } 55 return ZX_OK; 56} 57 58zx_status_t VmoFile::Getattr(vnattr_t* attr) { 59 memset(attr, 0, sizeof(vnattr_t)); 60 attr->mode = V_TYPE_FILE | V_IRUSR; 61 if (writable_) { 62 attr->mode |= V_IWUSR; 63 } 64 attr->size = length_; 65 attr->blksize = kVmoFileBlksize; 66 attr->blkcount = fbl::round_up(attr->size, kVmoFileBlksize) / VNATTR_BLKSIZE; 67 attr->nlink = 1; 68 return ZX_OK; 69} 70 71zx_status_t VmoFile::Read(void* data, size_t length, size_t offset, size_t* out_actual) { 72 if (length == 0u || offset >= length_) { 73 *out_actual = 0u; 74 return ZX_OK; 75 } 76 77 size_t remaining_length = length_ - offset; 78 if (length > remaining_length) { 79 length = remaining_length; 80 } 81 zx_status_t status = zx_vmo_read(vmo_handle_, data, offset_ + offset, length); 82 if (status != ZX_OK) { 83 return status; 84 } 85 *out_actual = length; 86 return ZX_OK; 87} 88 89zx_status_t VmoFile::Write(const void* data, size_t length, size_t offset, size_t* out_actual) { 90 ZX_DEBUG_ASSERT(writable_); // checked by the VFS 91 92 if (length == 0u) { 93 *out_actual = 0u; 94 return ZX_OK; 95 } 96 if (offset >= length_) { 97 return ZX_ERR_NO_SPACE; 98 } 99 100 size_t remaining_length = length_ - offset; 101 if (length > remaining_length) { 102 length = remaining_length; 103 } 104 zx_status_t status = zx_vmo_write(vmo_handle_, data, offset_ + offset, length); 105 if (status == ZX_OK) { 106 *out_actual = length; 107 } 108 return status; 109} 110 111zx_status_t VmoFile::GetHandles(uint32_t flags, zx_handle_t* hnd, uint32_t* type, 112 zxrio_node_info_t* extra) { 113 ZX_DEBUG_ASSERT(!IsWritable(flags) || writable_); // checked by the VFS 114 115 zx::vmo vmo; 116 size_t offset; 117 zx_status_t status = AcquireVmo(GetVmoRightsForAccessMode(flags), &vmo, &offset); 118 if (status != ZX_OK) { 119 return status; 120 } 121 122 *hnd = vmo.release(); 123 *type = fuchsia_io_NodeInfoTag_vmofile; 124 extra->vmofile.offset = offset; 125 extra->vmofile.length = length_; 126 return ZX_OK; 127} 128 129zx_status_t VmoFile::AcquireVmo(zx_rights_t rights, zx::vmo* out_vmo, size_t* out_offset) { 130 ZX_DEBUG_ASSERT(!(rights & ZX_RIGHT_WRITE) || writable_); // checked by the VFS 131 132 switch (vmo_sharing_) { 133 case VmoSharing::NONE: 134 return ZX_ERR_NOT_SUPPORTED; 135 case VmoSharing::DUPLICATE: 136 return DuplicateVmo(rights, out_vmo, out_offset); 137 case VmoSharing::CLONE_COW: 138 return CloneVmo(rights, out_vmo, out_offset); 139 } 140 __UNREACHABLE; 141} 142 143zx_status_t VmoFile::DuplicateVmo(zx_rights_t rights, zx::vmo* out_vmo, size_t* out_offset) { 144 zx_status_t status = zx_handle_duplicate(vmo_handle_, rights, out_vmo->reset_and_get_address()); 145 if (status != ZX_OK) 146 return status; 147 148 *out_offset = offset_; 149 return ZX_OK; 150} 151 152zx_status_t VmoFile::CloneVmo(zx_rights_t rights, zx::vmo* out_vmo, size_t* out_offset) { 153 size_t clone_offset = fbl::round_down(offset_, static_cast<size_t>(PAGE_SIZE)); 154 size_t clone_length = fbl::round_up(offset_ + length_, static_cast<size_t>(PAGE_SIZE)) - 155 clone_offset; 156 157 if (!(rights & ZX_RIGHT_WRITE)) { 158 // Use a shared clone for read-only content. 159 // TODO(ZX-1154): Replace the mutex with fbl::call_once() once that's implemented. 160 // The shared clone is only initialized at most once so using a mutex is excessive. 161 fbl::AutoLock lock(&mutex_); 162 zx_status_t status; 163 if (!shared_clone_) { 164 status = zx_vmo_clone(vmo_handle_, ZX_VMO_CLONE_COPY_ON_WRITE, 165 clone_offset, clone_length, 166 shared_clone_.reset_and_get_address()); 167 if (status != ZX_OK) 168 return status; 169 } 170 171 status = shared_clone_.duplicate(rights, out_vmo); 172 if (status != ZX_OK) 173 return status; 174 } else { 175 // Use separate clone for each client with writable COW access. 176 zx::vmo private_clone; 177 zx_status_t status = zx_vmo_clone(vmo_handle_, ZX_VMO_CLONE_COPY_ON_WRITE, 178 clone_offset, clone_length, 179 private_clone.reset_and_get_address()); 180 if (status != ZX_OK) 181 return status; 182 183 status = private_clone.replace(rights, out_vmo); 184 if (status != ZX_OK) 185 return status; 186 } 187 188 *out_offset = offset_ - clone_offset; 189 return ZX_OK; 190} 191 192} // namespace fs 193