1 // Copyright 2016 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 <assert.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include <fbl/alloc_checker.h>
12 #include <fbl/ref_ptr.h>
13 #include <fbl/unique_ptr.h>
14 #include <fs/trace.h>
15 #include <zircon/device/device.h>
16 
17 #include <minfs/format.h>
18 
19 #include "minfs-private.h"
20 #include <utility>
21 
22 namespace minfs {
23 
Readblk(blk_t bno,void * data)24 zx_status_t Bcache::Readblk(blk_t bno, void* data) {
25     off_t off = static_cast<off_t>(bno) * kMinfsBlockSize;
26     assert(off / kMinfsBlockSize == bno); // Overflow
27 #ifndef __Fuchsia__
28     off += offset_;
29 #endif
30     if (lseek(fd_.get(), off, SEEK_SET) < 0) {
31         FS_TRACE_ERROR("minfs: cannot seek to block %u\n", bno);
32         return ZX_ERR_IO;
33     }
34     if (read(fd_.get(), data, kMinfsBlockSize) != kMinfsBlockSize) {
35         FS_TRACE_ERROR("minfs: cannot read block %u\n", bno);
36         return ZX_ERR_IO;
37     }
38     return ZX_OK;
39 }
40 
Writeblk(blk_t bno,const void * data)41 zx_status_t Bcache::Writeblk(blk_t bno, const void* data) {
42     off_t off = static_cast<off_t>(bno) * kMinfsBlockSize;
43     assert(off / kMinfsBlockSize == bno); // Overflow
44 #ifndef __Fuchsia__
45     off += offset_;
46 #endif
47     if (lseek(fd_.get(), off, SEEK_SET) < 0) {
48         FS_TRACE_ERROR("minfs: cannot seek to block %u\n", bno);
49         return ZX_ERR_IO;
50     }
51     if (write(fd_.get(), data, kMinfsBlockSize) != kMinfsBlockSize) {
52         FS_TRACE_ERROR("minfs: cannot write block %u\n", bno);
53         return ZX_ERR_IO;
54     }
55     return ZX_OK;
56 }
57 
Sync()58 int Bcache::Sync() {
59     fs::WriteTxn sync_txn(this);
60     sync_txn.EnqueueFlush();
61     return sync_txn.Transact();
62 }
63 
Create(fbl::unique_ptr<Bcache> * out,fbl::unique_fd fd,uint32_t blockmax)64 zx_status_t Bcache::Create(fbl::unique_ptr<Bcache>* out, fbl::unique_fd fd, uint32_t blockmax) {
65     fbl::AllocChecker ac;
66     fbl::unique_ptr<Bcache> bc(new (&ac) Bcache(std::move(fd), blockmax));
67     if (!ac.check()) {
68         return ZX_ERR_NO_MEMORY;
69     }
70 #ifdef __Fuchsia__
71     zx::fifo fifo;
72     ssize_t r;
73 
74     if ((r = ioctl_block_get_info(bc->fd_.get(), &bc->info_)) < 0) {
75         FS_TRACE_ERROR("minfs: Cannot acquire block device information: %" PRId64 "\n", r);
76         return static_cast<zx_status_t>(r);
77     } else if (kMinfsBlockSize % bc->info_.block_size != 0) {
78         FS_TRACE_ERROR("minfs: minfs Block size not multiple of underlying block size\n");
79         return ZX_ERR_BAD_STATE;
80     } else if ((r = ioctl_block_get_fifos(bc->fd_.get(), fifo.reset_and_get_address())) < 0) {
81         FS_TRACE_ERROR("minfs: Cannot acquire block device fifo: %" PRId64 "\n", r);
82         return static_cast<zx_status_t>(r);
83     }
84     zx_status_t status;
85     if ((status = block_client::Client::Create(std::move(fifo), &bc->fifo_client_)) != ZX_OK) {
86         return status;
87     }
88 #endif
89 
90     *out = std::move(bc);
91     return ZX_OK;
92 }
93 
94 #ifdef __Fuchsia__
GetDevicePath(size_t buffer_len,char * out_name,size_t * out_len)95 zx_status_t Bcache::GetDevicePath(size_t buffer_len, char* out_name, size_t* out_len) {
96     ssize_t r = ioctl_device_get_topo_path(fd_.get(), out_name, buffer_len);
97     if (r < 0) {
98         return static_cast<zx_status_t>(r);
99     }
100     *out_len = r;
101     return ZX_OK;
102 
103 }
104 
AttachVmo(const zx::vmo & vmo,vmoid_t * out) const105 zx_status_t Bcache::AttachVmo(const zx::vmo& vmo, vmoid_t* out) const {
106     zx::vmo xfer_vmo;
107     zx_status_t status = vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &xfer_vmo);
108     if (status != ZX_OK) {
109         return status;
110     }
111     zx_handle_t raw_vmo = xfer_vmo.release();
112     ssize_t r = ioctl_block_attach_vmo(fd_.get(), &raw_vmo, out);
113     if (r < 0) {
114         return static_cast<zx_status_t>(r);
115     }
116     return ZX_OK;
117 }
118 #endif
119 
Bcache(fbl::unique_fd fd,uint32_t blockmax)120 Bcache::Bcache(fbl::unique_fd fd, uint32_t blockmax) :
121     fd_(std::move(fd)), blockmax_(blockmax) {}
122 
~Bcache()123 Bcache::~Bcache() {
124 #ifdef __Fuchsia__
125     if (fd_) {
126         ioctl_block_fifo_close(fd_.get());
127     }
128 #endif
129 }
130 
131 #ifndef __Fuchsia__
SetOffset(off_t offset)132 zx_status_t Bcache::SetOffset(off_t offset) {
133     if (offset_ || extent_lengths_.size() > 0) {
134         return ZX_ERR_ALREADY_BOUND;
135     }
136     offset_ = offset;
137     return ZX_OK;
138 }
139 
SetSparse(off_t offset,const fbl::Vector<size_t> & extent_lengths)140 zx_status_t Bcache::SetSparse(off_t offset, const fbl::Vector<size_t>& extent_lengths) {
141     if (offset_ || extent_lengths_.size() > 0) {
142         return ZX_ERR_ALREADY_BOUND;
143     }
144 
145     ZX_ASSERT(extent_lengths.size() == kExtentCount);
146 
147     fbl::AllocChecker ac;
148     extent_lengths_.reset(new (&ac) size_t[kExtentCount], kExtentCount);
149 
150     if (!ac.check()) {
151         return ZX_ERR_NO_MEMORY;
152     }
153 
154     for (size_t i = 0; i < extent_lengths.size(); i++) {
155         extent_lengths_[i] = extent_lengths[i];
156     }
157 
158     offset_ = offset;
159     return ZX_OK;
160 }
161 
162 // This is used by the ioctl wrappers in zircon/device/device.h. It's not
163 // called by host tools, so just satisfy the linker with a stub.
fdio_ioctl(int fd,int op,const void * in_buf,size_t in_len,void * out_buf,size_t out_len)164 ssize_t fdio_ioctl(int fd, int op, const void* in_buf, size_t in_len, void* out_buf, size_t out_len) {
165     return -1;
166 }
167 #endif
168 
169 } // namespace minfs
170