source: mainline/uspace/srv/bd/file_bd/file_bd.c@ 6c01702

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6c01702 was 6c01702, checked in by Jiri Svoboda <jiri@…>, 15 years ago

Flush stream when writing blocks in file_bd. Also check fseek() return values.

  • Property mode set to 100644
File size: 5.9 KB
Line 
1/*
2 * Copyright (c) 2009 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup bd
30 * @{
31 */
32
33/**
34 * @file
35 * @brief File-backed block device driver
36 *
37 * Allows accessing a file as a block device. Useful for, e.g., mounting
38 * a disk image.
39 */
40
41#include <stdio.h>
42#include <unistd.h>
43#include <ipc/ipc.h>
44#include <ipc/bd.h>
45#include <async.h>
46#include <as.h>
47#include <fibril_synch.h>
48#include <devmap.h>
49#include <sys/types.h>
50#include <errno.h>
51#include <bool.h>
52#include <task.h>
53#include <macros.h>
54
55#define NAME "file_bd"
56
57static const size_t block_size = 512;
58static bn_t num_blocks;
59static FILE *img;
60
61static dev_handle_t dev_handle;
62static fibril_mutex_t dev_lock;
63
64static int file_bd_init(const char *fname);
65static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
66static int file_bd_read_blocks(uint64_t ba, size_t cnt, void *buf);
67static int file_bd_write_blocks(uint64_t ba, size_t cnt, const void *buf);
68
69int main(int argc, char **argv)
70{
71 int rc;
72
73 printf(NAME ": File-backed block device driver\n");
74
75 if (argc != 3) {
76 printf("Expected two arguments (image name, device name).\n");
77 return -1;
78 }
79
80 if (file_bd_init(argv[1]) != EOK)
81 return -1;
82
83 rc = devmap_device_register(argv[2], &dev_handle);
84 if (rc != EOK) {
85 devmap_hangup_phone(DEVMAP_DRIVER);
86 printf(NAME ": Unable to register device %s.\n",
87 argv[2]);
88 return rc;
89 }
90
91 printf(NAME ": Accepting connections\n");
92 task_retval(0);
93 async_manager();
94
95 /* Not reached */
96 return 0;
97}
98
99static int file_bd_init(const char *fname)
100{
101 int rc;
102 long img_size;
103
104 rc = devmap_driver_register(NAME, file_bd_connection);
105 if (rc < 0) {
106 printf(NAME ": Unable to register driver.\n");
107 return rc;
108 }
109
110 img = fopen(fname, "rb+");
111 if (img == NULL)
112 return EINVAL;
113
114 if (fseek(img, 0, SEEK_END) != 0) {
115 fclose(img);
116 return EIO;
117 }
118
119 img_size = ftell(img);
120 if (img_size < 0) {
121 fclose(img);
122 return EIO;
123 }
124
125 num_blocks = img_size / block_size;
126
127 fibril_mutex_initialize(&dev_lock);
128
129 return EOK;
130}
131
132static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall)
133{
134 void *fs_va = NULL;
135 ipc_callid_t callid;
136 ipc_call_t call;
137 ipcarg_t method;
138 size_t comm_size;
139 int flags;
140 int retval;
141 uint64_t ba;
142 size_t cnt;
143
144 /* Answer the IPC_M_CONNECT_ME_TO call. */
145 ipc_answer_0(iid, EOK);
146
147 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
148 ipc_answer_0(callid, EHANGUP);
149 return;
150 }
151
152 fs_va = as_get_mappable_page(comm_size);
153 if (fs_va == NULL) {
154 ipc_answer_0(callid, EHANGUP);
155 return;
156 }
157
158 (void) async_share_out_finalize(callid, fs_va);
159
160 while (1) {
161 callid = async_get_call(&call);
162 method = IPC_GET_METHOD(call);
163 switch (method) {
164 case IPC_M_PHONE_HUNGUP:
165 /* The other side has hung up. */
166 ipc_answer_0(callid, EOK);
167 return;
168 case BD_READ_BLOCKS:
169 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
170 IPC_GET_ARG2(call));
171 cnt = IPC_GET_ARG3(call);
172 if (cnt * block_size > comm_size) {
173 retval = ELIMIT;
174 break;
175 }
176 retval = file_bd_read_blocks(ba, cnt, fs_va);
177 break;
178 case BD_WRITE_BLOCKS:
179 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
180 IPC_GET_ARG2(call));
181 cnt = IPC_GET_ARG3(call);
182 if (cnt * block_size > comm_size) {
183 retval = ELIMIT;
184 break;
185 }
186 retval = file_bd_write_blocks(ba, cnt, fs_va);
187 break;
188 case BD_GET_BLOCK_SIZE:
189 ipc_answer_1(callid, EOK, block_size);
190 continue;
191 case BD_GET_NUM_BLOCKS:
192 ipc_answer_2(callid, EOK, LOWER32(num_blocks),
193 UPPER32(num_blocks));
194 continue;
195 default:
196 retval = EINVAL;
197 break;
198 }
199 ipc_answer_0(callid, retval);
200 }
201}
202
203/** Read blocks from the device. */
204static int file_bd_read_blocks(uint64_t ba, size_t cnt, void *buf)
205{
206 size_t n_rd;
207 int rc;
208
209 fibril_mutex_lock(&dev_lock);
210
211 clearerr(img);
212 rc = fseek(img, ba * block_size, SEEK_SET);
213 if (rc < 0) {
214 fibril_mutex_unlock(&dev_lock);
215 return EIO;
216 }
217
218 n_rd = fread(buf, block_size, cnt, img);
219
220 if (ferror(img)) {
221 fibril_mutex_unlock(&dev_lock);
222 return EIO; /* Read error */
223 }
224
225 fibril_mutex_unlock(&dev_lock);
226
227 if (n_rd < cnt)
228 return EINVAL; /* Read beyond end of device */
229
230 return EOK;
231}
232
233/** Write blocks to the device. */
234static int file_bd_write_blocks(uint64_t ba, size_t cnt, const void *buf)
235{
236 size_t n_wr;
237 int rc;
238
239 fibril_mutex_lock(&dev_lock);
240
241 clearerr(img);
242 rc = fseek(img, ba * block_size, SEEK_SET);
243 if (rc < 0) {
244 fibril_mutex_unlock(&dev_lock);
245 return EIO;
246 }
247
248 n_wr = fwrite(buf, block_size, cnt, img);
249
250 if (ferror(img) || n_wr < cnt) {
251 fibril_mutex_unlock(&dev_lock);
252 return EIO; /* Write error */
253 }
254
255 if (fflush(img) != 0) {
256 fibril_mutex_unlock(&dev_lock);
257 return EIO;
258 }
259
260 fibril_mutex_unlock(&dev_lock);
261
262 return EOK;
263}
264
265/**
266 * @}
267 */
Note: See TracBrowser for help on using the repository browser.