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 <inttypes.h>
6#include <fcntl.h>
7#include <limits.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/stat.h>
11
12#include <fbl/algorithm.h>
13#include <fbl/alloc_checker.h>
14#include <fbl/atomic.h>
15#include <fbl/ref_ptr.h>
16#include <fbl/unique_ptr.h>
17#include <lib/fdio/vfs.h>
18#include <fs/vfs.h>
19#include <lib/memfs/cpp/vnode.h>
20#include <zircon/device/vfs.h>
21
22#include "dnode.h"
23
24namespace memfs {
25namespace {
26
27bool WindowMatchesVMO(zx_handle_t vmo, zx_off_t offset, zx_off_t length) {
28    if (offset != 0)
29        return false;
30    uint64_t size;
31    if (zx_vmo_get_size(vmo, &size) < 0)
32        return false;
33    return size == length;
34}
35
36}  // namespace
37
38VnodeVmo::VnodeVmo(Vfs* vfs, zx_handle_t vmo, zx_off_t offset, zx_off_t length)
39    : VnodeMemfs(vfs), vmo_(vmo), offset_(offset), length_(length), have_local_clone_(false) {}
40
41VnodeVmo::~VnodeVmo() {
42    if (have_local_clone_) {
43        zx_handle_close(vmo_);
44    }
45}
46
47zx_status_t VnodeVmo::ValidateFlags(uint32_t flags) {
48    if (flags & ZX_FS_FLAG_DIRECTORY) {
49        return ZX_ERR_NOT_DIR;
50    }
51    if (flags & ZX_FS_RIGHT_WRITABLE) {
52        return ZX_ERR_ACCESS_DENIED;
53    }
54    return ZX_OK;
55}
56
57zx_status_t VnodeVmo::Serve(fs::Vfs* vfs, zx::channel channel, uint32_t flags) {
58    return ZX_OK;
59}
60
61zx_status_t VnodeVmo::GetHandles(uint32_t flags, zx_handle_t* hnd, uint32_t* type,
62                                 zxrio_node_info_t* extra) {
63    zx_off_t* off = &extra->vmofile.offset;
64    zx_off_t* len = &extra->vmofile.length;
65    zx_status_t status;
66    if (!have_local_clone_ && !WindowMatchesVMO(vmo_, offset_, length_)) {
67        status = zx_vmo_clone(vmo_, ZX_VMO_CLONE_COPY_ON_WRITE, offset_, length_, &vmo_);
68        if (status < 0)
69            return status;
70        offset_ = 0;
71        have_local_clone_ = true;
72    }
73
74    zx_info_handle_basic_t info;
75    status = zx_object_get_info(vmo_, ZX_INFO_HANDLE_BASIC,
76                                &info, sizeof(info), NULL, NULL);
77    if (status != ZX_OK)
78        return status;
79
80    // Drop write rights.
81    zx_handle_t vmo;
82    status = zx_handle_duplicate(
83        vmo_,
84        ZX_RIGHT_READ | ZX_RIGHT_MAP |
85        ZX_RIGHTS_BASIC | ZX_RIGHT_GET_PROPERTY |
86        (info.rights & ZX_RIGHT_EXECUTE), // Preserve exec if present.
87        &vmo);
88    if (status < 0)
89        return status;
90
91    *off = offset_;
92    *len = length_;
93    *hnd = vmo;
94    *type = fuchsia_io_NodeInfoTag_vmofile;
95    return ZX_OK;
96}
97
98zx_status_t VnodeVmo::Read(void* data, size_t len, size_t off, size_t* out_actual) {
99    if (off > length_) {
100        *out_actual = 0;
101        return ZX_OK;
102    }
103    size_t rlen = length_ - off;
104    if (len > rlen) {
105        len = rlen;
106    }
107    zx_status_t status = zx_vmo_read(vmo_, data, offset_ + off, len);
108    if (status == ZX_OK) {
109        *out_actual = len;
110    }
111    return status;
112}
113
114zx_status_t VnodeVmo::Getattr(vnattr_t* attr) {
115    memset(attr, 0, sizeof(vnattr_t));
116    attr->inode = ino_;
117    attr->mode = V_TYPE_FILE | V_IRUSR;
118    attr->size = length_;
119    attr->blksize = kMemfsBlksize;
120    attr->blkcount = fbl::round_up(attr->size, kMemfsBlksize) / VNATTR_BLKSIZE;
121    attr->nlink = link_count_;
122    attr->create_time = create_time_;
123    attr->modify_time = modify_time_;
124    return ZX_OK;
125}
126
127} // namespace memfs
128