1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <assert.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <linux/ublock.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/types.h>
25 #include <ublock/ublock.h>
26
27 #define CONTROL_FILE "/dev/ublockctl"
28
29 struct ublock_ctx {
30 struct ublock_ops *ops;
31
32 uint32_t index;
33 uint64_t size;
34 uint32_t max_buf;
35
36 char *in_buf;
37 char *out_buf;
38
39 int flags;
40 int fd;
41 int fails;
42 };
43
44 #define CTX_INITED 0x1
45 #define CTX_READY 0x2
46 #define CTX_RUNNING 0x4
47
48 #define MAX_BUF 65536
49
50 #define MAX_FAILURES 10
51
ublock_succeed(struct ublock_ctx * ub_ctx)52 static inline void ublock_succeed(struct ublock_ctx *ub_ctx)
53 {
54 assert(ub_ctx);
55
56 ub_ctx->fails = 0;
57 }
58
ublock_fail(struct ublock_ctx * ub_ctx)59 static void ublock_fail(struct ublock_ctx *ub_ctx)
60 {
61 assert(ub_ctx);
62
63 ub_ctx->fails++;
64 if (ub_ctx->fails > MAX_FAILURES)
65 ublock_stop(ub_ctx);
66 }
67
ublock_handle_init(struct ublock_ctx * ub_ctx,const void * in,size_t in_len,void * out,size_t * out_len)68 static int ublock_handle_init(struct ublock_ctx *ub_ctx,
69 const void *in, size_t in_len,
70 void *out, size_t *out_len)
71 {
72 const struct ublock_init_in *in_h;
73 struct ublock_init_out *out_h;
74
75 assert(ub_ctx);
76 assert(in);
77 assert(out);
78
79 if (in_len != sizeof(*in_h))
80 return -EPROTO;
81
82 in_h = (const struct ublock_init_in *)in;
83
84 if (in_h->version != UBLOCK_VERSION)
85 return -EPROTO;
86
87 out_h = (struct ublock_init_out *)out;
88 out_h->version = UBLOCK_VERSION;
89 out_h->size = ub_ctx->size;
90 if (in_h->max_buf < MAX_BUF)
91 ub_ctx->max_buf = in_h->max_buf;
92 else
93 ub_ctx->max_buf = MAX_BUF;
94 out_h->max_buf = ub_ctx->max_buf;
95
96 *out_len = sizeof(*out_h);
97
98 ub_ctx->index = in_h->index;
99 ub_ctx->flags |= CTX_INITED;
100
101 return 0;
102 }
103
ublock_handle_ready(struct ublock_ctx * ub_ctx,const void * in,size_t in_len,void * out,size_t * out_len)104 static int ublock_handle_ready(struct ublock_ctx *ub_ctx,
105 const void *in, size_t in_len,
106 void *out, size_t *out_len)
107 {
108 struct ublock_ready_out *out_h;
109
110 assert(ub_ctx);
111 assert(in);
112 assert(out);
113
114 if (in_len != sizeof(struct ublock_ready_in))
115 return -EPROTO;
116
117 *out_len = sizeof(struct ublock_ready_out);
118
119 ub_ctx->flags |= CTX_READY;
120
121 return 0;
122 }
123
ublock_handle_read(struct ublock_ctx * ub_ctx,const void * in,size_t in_len,void * out,size_t * out_len)124 static int ublock_handle_read(struct ublock_ctx *ub_ctx,
125 const void *in, size_t in_len,
126 void *out, size_t *out_len)
127 {
128 const struct ublock_read_in *in_h;
129 struct ublock_read_out *out_h;
130 char *out_buf;
131
132 assert(ub_ctx);
133 assert(in);
134 assert(out);
135
136 if (in_len != sizeof(*in_h))
137 return -EPROTO;
138
139 in_h = (const struct ublock_read_in *)in;
140
141 out_h = (struct ublock_read_out *)out;
142 out_buf = (char *)(out_h + 1);
143
144 out_h->status = (ub_ctx->ops->read)(out_buf, in_h->length, in_h->offset);
145
146 if (out_h->status >= 0)
147 *out_len = sizeof(*out_h) + in_h->length;
148 else
149 *out_len = sizeof(*out_h);
150
151 return 0;
152 }
153
ublock_handle_write(struct ublock_ctx * ub_ctx,const void * in,size_t in_len,void * out,size_t * out_len)154 static int ublock_handle_write(struct ublock_ctx *ub_ctx,
155 const void *in, size_t in_len,
156 void *out, size_t *out_len)
157 {
158 const struct ublock_write_in *in_h;
159 const char *in_buf;
160 struct ublock_write_out *out_h;
161
162 assert(ub_ctx);
163 assert(in);
164 assert(out);
165
166 if (in_len < sizeof(*in_h))
167 return -EPROTO;
168
169 in_h = (const struct ublock_write_in *)in;
170 in_buf = (const char*)(in_h + 1);
171
172 out_h = (struct ublock_write_out *)out;
173 *out_len = sizeof(*out_h);
174
175 out_h->status = (ub_ctx->ops->write)(in_buf, in_h->length, in_h->offset);
176
177 return 0;
178 }
179
ublock_handle_request(struct ublock_ctx * ub_ctx,const void * in,size_t in_len,void * out,size_t * out_len)180 static int ublock_handle_request(struct ublock_ctx *ub_ctx,
181 const void *in, size_t in_len,
182 void *out, size_t *out_len)
183 {
184 const struct ublock_in_header *in_h;
185 const void *in_buf;
186 size_t in_buf_len;
187
188 struct ublock_out_header *out_h;
189 void *out_buf;
190 size_t out_buf_len;
191
192 int result;
193 int (*handle_fn)(struct ublock_ctx *, const void *, size_t, void *, size_t *);
194
195 assert(ub_ctx);
196 assert(in);
197 assert(out);
198
199 if (in_len < sizeof(*in_h))
200 return -EPROTO;
201
202 in_h = (const struct ublock_in_header *)in;
203 in_buf = in_h + 1;
204 in_buf_len = in_len - sizeof(*in_h);
205
206 out_h = (struct ublock_out_header *)out;
207 out_buf = out_h + 1;
208
209 switch (in_h->opcode) {
210 case UBLOCK_INIT_IN:
211 out_h->opcode = UBLOCK_INIT_OUT;
212 handle_fn = &ublock_handle_init;
213 break;
214 case UBLOCK_READY_IN:
215 out_h->opcode = UBLOCK_READY_OUT;
216 handle_fn = &ublock_handle_ready;
217 break;
218 case UBLOCK_READ_IN:
219 out_h->opcode = UBLOCK_READ_OUT;
220 handle_fn = &ublock_handle_read;
221 break;
222 case UBLOCK_WRITE_IN:
223 out_h->opcode = UBLOCK_WRITE_OUT;
224 handle_fn = &ublock_handle_write;
225 break;
226 default:
227 return -EPROTO;
228 }
229
230 out_h->seq = in_h->seq;
231 result = (handle_fn)(ub_ctx, in_buf, in_buf_len, out_buf, &out_buf_len);
232 *out_len = sizeof(*out_h) + out_buf_len;
233
234 return result;
235 }
236
ublock_do_request(struct ublock_ctx * ub_ctx,void * in_buf,size_t in_size,void * out_buf,size_t out_size)237 static int ublock_do_request(struct ublock_ctx *ub_ctx,
238 void *in_buf, size_t in_size,
239 void *out_buf, size_t out_size)
240 {
241 size_t out_len;
242 ssize_t in_len, out_wrote;
243 int result;
244
245 assert(ub_ctx);
246 assert(in_buf);
247 assert(out_buf);
248
249 in_len = read(ub_ctx->fd, in_buf, in_size);
250 if (in_len < 0)
251 return -EPROTO;
252
253 result = ublock_handle_request(ub_ctx, in_buf, in_len, out_buf, &out_len);
254
255 assert(out_len <= out_size);
256
257 out_wrote = write(ub_ctx->fd, out_buf, out_len);
258
259 if (out_wrote < out_len)
260 return -EPROTO;
261
262 if (result)
263 ublock_fail(ub_ctx);
264 else
265 ublock_succeed(ub_ctx);
266
267 return result;
268 }
269
270 #define EXIT_READY 1
271 #define EXIT_STOPPED 2
272 #define EXIT_FAIL 4
273
ublock_loop(struct ublock_ctx * ub_ctx,int exit_cond)274 static int ublock_loop(struct ublock_ctx *ub_ctx, int exit_cond)
275 {
276 size_t in_len, out_len;
277 int result;
278
279 result = 0;
280 while (((exit_cond & EXIT_READY) && (!(ub_ctx->flags & CTX_READY))) ||
281 ((exit_cond & EXIT_STOPPED) && (ub_ctx->flags & CTX_RUNNING)) ||
282 ((exit_cond & EXIT_FAIL) && (result))) {
283 result = ublock_do_request(ub_ctx,
284 ub_ctx->in_buf, ub_ctx->max_buf,
285 ub_ctx->out_buf, ub_ctx->max_buf);
286 if (result)
287 return result;
288 }
289
290 return 0;
291 }
292
ublock_run(struct ublock_ctx * ub_ctx)293 int ublock_run(struct ublock_ctx *ub_ctx)
294 {
295 if (!ub_ctx)
296 return -EFAULT;
297
298 if (!(ub_ctx->flags & CTX_INITED))
299 return -EINVAL;
300
301 ub_ctx->flags |= CTX_RUNNING;
302 ublock_loop(ub_ctx, EXIT_STOPPED);
303
304 return 0;
305 }
306
ublock_stop(struct ublock_ctx * ub_ctx)307 void ublock_stop(struct ublock_ctx *ub_ctx)
308 {
309 if (!ub_ctx)
310 return;
311
312 if (!(ub_ctx->flags & CTX_INITED))
313 return;
314
315 ub_ctx->flags &= ~CTX_RUNNING;
316 }
317
ublock_index(struct ublock_ctx * ub_ctx)318 int ublock_index(struct ublock_ctx *ub_ctx)
319 {
320 if (!ub_ctx)
321 return -EFAULT;
322
323 if (!(ub_ctx->flags & CTX_INITED))
324 return -EINVAL;
325
326 return ub_ctx->index;
327 }
328
ublock_init_buf_size(void)329 static inline size_t ublock_init_buf_size(void)
330 {
331 size_t in_size = sizeof(struct ublock_in_header) +
332 sizeof(struct ublock_init_in);
333 size_t out_size = sizeof(struct ublock_out_header) +
334 sizeof(struct ublock_init_out);
335
336 return (in_size > out_size) ? in_size : out_size;
337 }
338
ublock_init(struct ublock_ctx ** ub_ctx_out,struct ublock_ops * ops,uint64_t dev_size)339 int ublock_init(struct ublock_ctx **ub_ctx_out, struct ublock_ops *ops,
340 uint64_t dev_size)
341 {
342 struct ublock_ctx *ub_ctx;
343 char *in_buf, *out_buf;
344 size_t size;
345 int result;
346
347 if (!ub_ctx_out || !ops)
348 return -EFAULT;
349
350 in_buf = out_buf = NULL;
351
352 ub_ctx = malloc(sizeof(struct ublock_ctx));
353 if (!ub_ctx) {
354 result = -ENOMEM;
355 goto error;
356 }
357
358 size = ublock_init_buf_size();
359 in_buf = malloc(size);
360 out_buf = malloc(size);
361 if (!(in_buf && out_buf)) {
362 result = -ENOMEM;
363 goto error;
364 }
365
366 ub_ctx->ops = ops;
367 ub_ctx->size = dev_size;
368 ub_ctx->max_buf = 0;
369 ub_ctx->flags = 0;
370
371 ub_ctx->fd = open(CONTROL_FILE, O_RDWR);
372 if (ub_ctx->fd < 0) {
373 result = -ENOENT;
374 goto error;
375 }
376
377 result = ublock_do_request(ub_ctx, in_buf, size, out_buf, size);
378 if (result) {
379 result = -EPROTO;
380 goto error;
381 }
382 if (!ub_ctx->flags & CTX_INITED) {
383 result = -EPROTO;
384 goto error;
385 }
386
387 free(in_buf);
388 in_buf = NULL;
389 free(out_buf);
390 out_buf = NULL;
391
392 ub_ctx->in_buf = malloc(ub_ctx->max_buf);
393 ub_ctx->out_buf = malloc(ub_ctx->max_buf);
394 if (!(ub_ctx->in_buf && ub_ctx->out_buf)) {
395 result = -ENOMEM;
396 goto error;
397 }
398
399 ublock_loop(ub_ctx, EXIT_READY);
400
401 *ub_ctx_out = ub_ctx;
402
403 return 0;
404
405 error:
406 if (ub_ctx) {
407 if (ub_ctx->in_buf)
408 free(ub_ctx->in_buf);
409 if (ub_ctx->out_buf)
410 free(ub_ctx->out_buf);
411 if (ub_ctx->fd)
412 close(ub_ctx->fd);
413 free(ub_ctx);
414 }
415 if (in_buf)
416 free(in_buf);
417 if (out_buf)
418 free(out_buf);
419
420 return result;
421 }
422
ublock_destroy(struct ublock_ctx * ub_ctx)423 void ublock_destroy(struct ublock_ctx *ub_ctx)
424 {
425 if (!ub_ctx)
426 return;
427
428 close(ub_ctx->fd);
429 free(ub_ctx);
430 }
431