Home | History | Annotate | Download | only in libublock
      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 
     52 static inline void ublock_succeed(struct ublock_ctx *ub_ctx)
     53 {
     54 	assert(ub_ctx);
     55 
     56 	ub_ctx->fails = 0;
     57 }
     58 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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