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