• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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