diff options
author | Mike Frysinger <vapier@gentoo.org> | 2005-05-09 22:10:42 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2005-05-09 22:10:42 +0000 |
commit | 1fd98e039d146dcff02a5350f509cabca65fd29c (patch) | |
tree | 1564707d41a6271bb44cf3fa5b88b9cc70fedd35 /e2fsprogs/ext2fs/unix_io.c | |
parent | b32011943a0764872ca1ea17f13b53176ace8e69 (diff) | |
download | busybox-w32-1fd98e039d146dcff02a5350f509cabca65fd29c.tar.gz busybox-w32-1fd98e039d146dcff02a5350f509cabca65fd29c.tar.bz2 busybox-w32-1fd98e039d146dcff02a5350f509cabca65fd29c.zip |
import ext2fs lib to prep for new e2fsprogs
Diffstat (limited to 'e2fsprogs/ext2fs/unix_io.c')
-rw-r--r-- | e2fsprogs/ext2fs/unix_io.c | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/e2fsprogs/ext2fs/unix_io.c b/e2fsprogs/ext2fs/unix_io.c new file mode 100644 index 000000000..5bc7a6abe --- /dev/null +++ b/e2fsprogs/ext2fs/unix_io.c | |||
@@ -0,0 +1,707 @@ | |||
1 | /* | ||
2 | * unix_io.c --- This is the Unix (well, really POSIX) implementation | ||
3 | * of the I/O manager. | ||
4 | * | ||
5 | * Implements a one-block write-through cache. | ||
6 | * | ||
7 | * Includes support for Windows NT support under Cygwin. | ||
8 | * | ||
9 | * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, | ||
10 | * 2002 by Theodore Ts'o. | ||
11 | * | ||
12 | * %Begin-Header% | ||
13 | * This file may be redistributed under the terms of the GNU Public | ||
14 | * License. | ||
15 | * %End-Header% | ||
16 | */ | ||
17 | |||
18 | #include <stdio.h> | ||
19 | #include <string.h> | ||
20 | #if HAVE_UNISTD_H | ||
21 | #include <unistd.h> | ||
22 | #endif | ||
23 | #if HAVE_ERRNO_H | ||
24 | #include <errno.h> | ||
25 | #endif | ||
26 | #include <fcntl.h> | ||
27 | #include <time.h> | ||
28 | #ifdef __linux__ | ||
29 | #include <sys/utsname.h> | ||
30 | #endif | ||
31 | #if HAVE_SYS_STAT_H | ||
32 | #include <sys/stat.h> | ||
33 | #endif | ||
34 | #if HAVE_SYS_TYPES_H | ||
35 | #include <sys/types.h> | ||
36 | #endif | ||
37 | #if HAVE_SYS_RESOURCE_H | ||
38 | #include <sys/resource.h> | ||
39 | #endif | ||
40 | |||
41 | #include "ext2_fs.h" | ||
42 | #include "ext2fs.h" | ||
43 | |||
44 | /* | ||
45 | * For checking structure magic numbers... | ||
46 | */ | ||
47 | |||
48 | #define EXT2_CHECK_MAGIC(struct, code) \ | ||
49 | if ((struct)->magic != (code)) return (code) | ||
50 | |||
51 | struct unix_cache { | ||
52 | char *buf; | ||
53 | unsigned long block; | ||
54 | int access_time; | ||
55 | unsigned dirty:1; | ||
56 | unsigned in_use:1; | ||
57 | }; | ||
58 | |||
59 | #define CACHE_SIZE 8 | ||
60 | #define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */ | ||
61 | #define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */ | ||
62 | |||
63 | struct unix_private_data { | ||
64 | int magic; | ||
65 | int dev; | ||
66 | int flags; | ||
67 | int access_time; | ||
68 | ext2_loff_t offset; | ||
69 | struct unix_cache cache[CACHE_SIZE]; | ||
70 | }; | ||
71 | |||
72 | static errcode_t unix_open(const char *name, int flags, io_channel *channel); | ||
73 | static errcode_t unix_close(io_channel channel); | ||
74 | static errcode_t unix_set_blksize(io_channel channel, int blksize); | ||
75 | static errcode_t unix_read_blk(io_channel channel, unsigned long block, | ||
76 | int count, void *data); | ||
77 | static errcode_t unix_write_blk(io_channel channel, unsigned long block, | ||
78 | int count, const void *data); | ||
79 | static errcode_t unix_flush(io_channel channel); | ||
80 | static errcode_t unix_write_byte(io_channel channel, unsigned long offset, | ||
81 | int size, const void *data); | ||
82 | static errcode_t unix_set_option(io_channel channel, const char *option, | ||
83 | const char *arg); | ||
84 | |||
85 | static void reuse_cache(io_channel channel, struct unix_private_data *data, | ||
86 | struct unix_cache *cache, unsigned long block); | ||
87 | |||
88 | /* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel | ||
89 | * does not know buffered block devices - everything is raw. */ | ||
90 | #if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | ||
91 | #define NEED_BOUNCE_BUFFER | ||
92 | #else | ||
93 | #undef NEED_BOUNCE_BUFFER | ||
94 | #endif | ||
95 | |||
96 | static struct struct_io_manager struct_unix_manager = { | ||
97 | EXT2_ET_MAGIC_IO_MANAGER, | ||
98 | "Unix I/O Manager", | ||
99 | unix_open, | ||
100 | unix_close, | ||
101 | unix_set_blksize, | ||
102 | unix_read_blk, | ||
103 | unix_write_blk, | ||
104 | unix_flush, | ||
105 | #ifdef NEED_BOUNCE_BUFFER | ||
106 | 0, | ||
107 | #else | ||
108 | unix_write_byte, | ||
109 | #endif | ||
110 | unix_set_option | ||
111 | }; | ||
112 | |||
113 | io_manager unix_io_manager = &struct_unix_manager; | ||
114 | |||
115 | /* | ||
116 | * Here are the raw I/O functions | ||
117 | */ | ||
118 | #ifndef NEED_BOUNCE_BUFFER | ||
119 | static errcode_t raw_read_blk(io_channel channel, | ||
120 | struct unix_private_data *data, | ||
121 | unsigned long block, | ||
122 | int count, void *buf) | ||
123 | { | ||
124 | errcode_t retval; | ||
125 | ssize_t size; | ||
126 | ext2_loff_t location; | ||
127 | int actual = 0; | ||
128 | |||
129 | size = (count < 0) ? -count : count * channel->block_size; | ||
130 | location = ((ext2_loff_t) block * channel->block_size) + data->offset; | ||
131 | if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { | ||
132 | retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; | ||
133 | goto error_out; | ||
134 | } | ||
135 | actual = read(data->dev, buf, size); | ||
136 | if (actual != size) { | ||
137 | if (actual < 0) | ||
138 | actual = 0; | ||
139 | retval = EXT2_ET_SHORT_READ; | ||
140 | goto error_out; | ||
141 | } | ||
142 | return 0; | ||
143 | |||
144 | error_out: | ||
145 | memset((char *) buf+actual, 0, size-actual); | ||
146 | if (channel->read_error) | ||
147 | retval = (channel->read_error)(channel, block, count, buf, | ||
148 | size, actual, retval); | ||
149 | return retval; | ||
150 | } | ||
151 | #else /* NEED_BOUNCE_BUFFER */ | ||
152 | /* | ||
153 | * Windows and FreeBSD block devices only allow sector alignment IO in offset and size | ||
154 | */ | ||
155 | static errcode_t raw_read_blk(io_channel channel, | ||
156 | struct unix_private_data *data, | ||
157 | unsigned long block, | ||
158 | int count, void *buf) | ||
159 | { | ||
160 | errcode_t retval; | ||
161 | size_t size, alignsize, fragment; | ||
162 | ext2_loff_t location; | ||
163 | int total = 0, actual; | ||
164 | #define BLOCKALIGN 512 | ||
165 | char sector[BLOCKALIGN]; | ||
166 | |||
167 | size = (count < 0) ? -count : count * channel->block_size; | ||
168 | location = ((ext2_loff_t) block * channel->block_size) + data->offset; | ||
169 | #ifdef DEBUG | ||
170 | printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n", | ||
171 | count, size, block, channel->block_size, location); | ||
172 | #endif | ||
173 | if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { | ||
174 | retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; | ||
175 | goto error_out; | ||
176 | } | ||
177 | fragment = size % BLOCKALIGN; | ||
178 | alignsize = size - fragment; | ||
179 | if (alignsize) { | ||
180 | actual = read(data->dev, buf, alignsize); | ||
181 | if (actual != alignsize) | ||
182 | goto short_read; | ||
183 | } | ||
184 | if (fragment) { | ||
185 | actual = read(data->dev, sector, BLOCKALIGN); | ||
186 | if (actual != BLOCKALIGN) | ||
187 | goto short_read; | ||
188 | memcpy(buf+alignsize, sector, fragment); | ||
189 | } | ||
190 | return 0; | ||
191 | |||
192 | short_read: | ||
193 | if (actual>0) | ||
194 | total += actual; | ||
195 | retval = EXT2_ET_SHORT_READ; | ||
196 | |||
197 | error_out: | ||
198 | memset((char *) buf+total, 0, size-actual); | ||
199 | if (channel->read_error) | ||
200 | retval = (channel->read_error)(channel, block, count, buf, | ||
201 | size, actual, retval); | ||
202 | return retval; | ||
203 | } | ||
204 | #endif | ||
205 | |||
206 | static errcode_t raw_write_blk(io_channel channel, | ||
207 | struct unix_private_data *data, | ||
208 | unsigned long block, | ||
209 | int count, const void *buf) | ||
210 | { | ||
211 | ssize_t size; | ||
212 | ext2_loff_t location; | ||
213 | int actual = 0; | ||
214 | errcode_t retval; | ||
215 | |||
216 | if (count == 1) | ||
217 | size = channel->block_size; | ||
218 | else { | ||
219 | if (count < 0) | ||
220 | size = -count; | ||
221 | else | ||
222 | size = count * channel->block_size; | ||
223 | } | ||
224 | |||
225 | location = ((ext2_loff_t) block * channel->block_size) + data->offset; | ||
226 | if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { | ||
227 | retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; | ||
228 | goto error_out; | ||
229 | } | ||
230 | |||
231 | actual = write(data->dev, buf, size); | ||
232 | if (actual != size) { | ||
233 | retval = EXT2_ET_SHORT_WRITE; | ||
234 | goto error_out; | ||
235 | } | ||
236 | return 0; | ||
237 | |||
238 | error_out: | ||
239 | if (channel->write_error) | ||
240 | retval = (channel->write_error)(channel, block, count, buf, | ||
241 | size, actual, retval); | ||
242 | return retval; | ||
243 | } | ||
244 | |||
245 | |||
246 | /* | ||
247 | * Here we implement the cache functions | ||
248 | */ | ||
249 | |||
250 | /* Allocate the cache buffers */ | ||
251 | static errcode_t alloc_cache(io_channel channel, | ||
252 | struct unix_private_data *data) | ||
253 | { | ||
254 | errcode_t retval; | ||
255 | struct unix_cache *cache; | ||
256 | int i; | ||
257 | |||
258 | data->access_time = 0; | ||
259 | for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { | ||
260 | cache->block = 0; | ||
261 | cache->access_time = 0; | ||
262 | cache->dirty = 0; | ||
263 | cache->in_use = 0; | ||
264 | if ((retval = ext2fs_get_mem(channel->block_size, | ||
265 | &cache->buf))) | ||
266 | return retval; | ||
267 | } | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | /* Free the cache buffers */ | ||
272 | static void free_cache(struct unix_private_data *data) | ||
273 | { | ||
274 | struct unix_cache *cache; | ||
275 | int i; | ||
276 | |||
277 | data->access_time = 0; | ||
278 | for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { | ||
279 | cache->block = 0; | ||
280 | cache->access_time = 0; | ||
281 | cache->dirty = 0; | ||
282 | cache->in_use = 0; | ||
283 | if (cache->buf) | ||
284 | ext2fs_free_mem(&cache->buf); | ||
285 | cache->buf = 0; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | #ifndef NO_IO_CACHE | ||
290 | /* | ||
291 | * Try to find a block in the cache. If the block is not found, and | ||
292 | * eldest is a non-zero pointer, then fill in eldest with the cache | ||
293 | * entry to that should be reused. | ||
294 | */ | ||
295 | static struct unix_cache *find_cached_block(struct unix_private_data *data, | ||
296 | unsigned long block, | ||
297 | struct unix_cache **eldest) | ||
298 | { | ||
299 | struct unix_cache *cache, *unused_cache, *oldest_cache; | ||
300 | int i; | ||
301 | |||
302 | unused_cache = oldest_cache = 0; | ||
303 | for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { | ||
304 | if (!cache->in_use) { | ||
305 | if (!unused_cache) | ||
306 | unused_cache = cache; | ||
307 | continue; | ||
308 | } | ||
309 | if (cache->block == block) { | ||
310 | cache->access_time = ++data->access_time; | ||
311 | return cache; | ||
312 | } | ||
313 | if (!oldest_cache || | ||
314 | (cache->access_time < oldest_cache->access_time)) | ||
315 | oldest_cache = cache; | ||
316 | } | ||
317 | if (eldest) | ||
318 | *eldest = (unused_cache) ? unused_cache : oldest_cache; | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * Reuse a particular cache entry for another block. | ||
324 | */ | ||
325 | static void reuse_cache(io_channel channel, struct unix_private_data *data, | ||
326 | struct unix_cache *cache, unsigned long block) | ||
327 | { | ||
328 | if (cache->dirty && cache->in_use) | ||
329 | raw_write_blk(channel, data, cache->block, 1, cache->buf); | ||
330 | |||
331 | cache->in_use = 1; | ||
332 | cache->dirty = 0; | ||
333 | cache->block = block; | ||
334 | cache->access_time = ++data->access_time; | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * Flush all of the blocks in the cache | ||
339 | */ | ||
340 | static errcode_t flush_cached_blocks(io_channel channel, | ||
341 | struct unix_private_data *data, | ||
342 | int invalidate) | ||
343 | |||
344 | { | ||
345 | struct unix_cache *cache; | ||
346 | errcode_t retval, retval2; | ||
347 | int i; | ||
348 | |||
349 | retval2 = 0; | ||
350 | for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { | ||
351 | if (!cache->in_use) | ||
352 | continue; | ||
353 | |||
354 | if (invalidate) | ||
355 | cache->in_use = 0; | ||
356 | |||
357 | if (!cache->dirty) | ||
358 | continue; | ||
359 | |||
360 | retval = raw_write_blk(channel, data, | ||
361 | cache->block, 1, cache->buf); | ||
362 | if (retval) | ||
363 | retval2 = retval; | ||
364 | else | ||
365 | cache->dirty = 0; | ||
366 | } | ||
367 | return retval2; | ||
368 | } | ||
369 | #endif /* NO_IO_CACHE */ | ||
370 | |||
371 | static errcode_t unix_open(const char *name, int flags, io_channel *channel) | ||
372 | { | ||
373 | io_channel io = NULL; | ||
374 | struct unix_private_data *data = NULL; | ||
375 | errcode_t retval; | ||
376 | int open_flags; | ||
377 | struct stat st; | ||
378 | #ifdef __linux__ | ||
379 | struct utsname ut; | ||
380 | #endif | ||
381 | |||
382 | if (name == 0) | ||
383 | return EXT2_ET_BAD_DEVICE_NAME; | ||
384 | retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); | ||
385 | if (retval) | ||
386 | return retval; | ||
387 | memset(io, 0, sizeof(struct struct_io_channel)); | ||
388 | io->magic = EXT2_ET_MAGIC_IO_CHANNEL; | ||
389 | retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); | ||
390 | if (retval) | ||
391 | goto cleanup; | ||
392 | |||
393 | io->manager = unix_io_manager; | ||
394 | retval = ext2fs_get_mem(strlen(name)+1, &io->name); | ||
395 | if (retval) | ||
396 | goto cleanup; | ||
397 | |||
398 | strcpy(io->name, name); | ||
399 | io->private_data = data; | ||
400 | io->block_size = 1024; | ||
401 | io->read_error = 0; | ||
402 | io->write_error = 0; | ||
403 | io->refcount = 1; | ||
404 | |||
405 | memset(data, 0, sizeof(struct unix_private_data)); | ||
406 | data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; | ||
407 | |||
408 | if ((retval = alloc_cache(io, data))) | ||
409 | goto cleanup; | ||
410 | |||
411 | open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; | ||
412 | #ifdef CONFIG_LFS | ||
413 | data->dev = open64(io->name, open_flags); | ||
414 | #else | ||
415 | data->dev = open(io->name, open_flags); | ||
416 | #endif | ||
417 | if (data->dev < 0) { | ||
418 | retval = errno; | ||
419 | goto cleanup; | ||
420 | } | ||
421 | |||
422 | #ifdef __linux__ | ||
423 | #undef RLIM_INFINITY | ||
424 | #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) | ||
425 | #define RLIM_INFINITY ((unsigned long)(~0UL>>1)) | ||
426 | #else | ||
427 | #define RLIM_INFINITY (~0UL) | ||
428 | #endif | ||
429 | /* | ||
430 | * Work around a bug in 2.4.10-2.4.18 kernels where writes to | ||
431 | * block devices are wrongly getting hit by the filesize | ||
432 | * limit. This workaround isn't perfect, since it won't work | ||
433 | * if glibc wasn't built against 2.2 header files. (Sigh.) | ||
434 | * | ||
435 | */ | ||
436 | if ((flags & IO_FLAG_RW) && | ||
437 | (uname(&ut) == 0) && | ||
438 | ((ut.release[0] == '2') && (ut.release[1] == '.') && | ||
439 | (ut.release[2] == '4') && (ut.release[3] == '.') && | ||
440 | (ut.release[4] == '1') && (ut.release[5] >= '0') && | ||
441 | (ut.release[5] < '8')) && | ||
442 | (fstat(data->dev, &st) == 0) && | ||
443 | (S_ISBLK(st.st_mode))) { | ||
444 | struct rlimit rlim; | ||
445 | |||
446 | rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; | ||
447 | setrlimit(RLIMIT_FSIZE, &rlim); | ||
448 | getrlimit(RLIMIT_FSIZE, &rlim); | ||
449 | if (((unsigned long) rlim.rlim_cur) < | ||
450 | ((unsigned long) rlim.rlim_max)) { | ||
451 | rlim.rlim_cur = rlim.rlim_max; | ||
452 | setrlimit(RLIMIT_FSIZE, &rlim); | ||
453 | } | ||
454 | } | ||
455 | #endif | ||
456 | *channel = io; | ||
457 | return 0; | ||
458 | |||
459 | cleanup: | ||
460 | if (data) { | ||
461 | free_cache(data); | ||
462 | ext2fs_free_mem(&data); | ||
463 | } | ||
464 | if (io) | ||
465 | ext2fs_free_mem(&io); | ||
466 | return retval; | ||
467 | } | ||
468 | |||
469 | static errcode_t unix_close(io_channel channel) | ||
470 | { | ||
471 | struct unix_private_data *data; | ||
472 | errcode_t retval = 0; | ||
473 | |||
474 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); | ||
475 | data = (struct unix_private_data *) channel->private_data; | ||
476 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); | ||
477 | |||
478 | if (--channel->refcount > 0) | ||
479 | return 0; | ||
480 | |||
481 | #ifndef NO_IO_CACHE | ||
482 | retval = flush_cached_blocks(channel, data, 0); | ||
483 | #endif | ||
484 | |||
485 | if (close(data->dev) < 0) | ||
486 | retval = errno; | ||
487 | free_cache(data); | ||
488 | |||
489 | ext2fs_free_mem(&channel->private_data); | ||
490 | if (channel->name) | ||
491 | ext2fs_free_mem(&channel->name); | ||
492 | ext2fs_free_mem(&channel); | ||
493 | return retval; | ||
494 | } | ||
495 | |||
496 | static errcode_t unix_set_blksize(io_channel channel, int blksize) | ||
497 | { | ||
498 | struct unix_private_data *data; | ||
499 | errcode_t retval; | ||
500 | |||
501 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); | ||
502 | data = (struct unix_private_data *) channel->private_data; | ||
503 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); | ||
504 | |||
505 | if (channel->block_size != blksize) { | ||
506 | #ifndef NO_IO_CACHE | ||
507 | if ((retval = flush_cached_blocks(channel, data, 0))) | ||
508 | return retval; | ||
509 | #endif | ||
510 | |||
511 | channel->block_size = blksize; | ||
512 | free_cache(data); | ||
513 | if ((retval = alloc_cache(channel, data))) | ||
514 | return retval; | ||
515 | } | ||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | |||
520 | static errcode_t unix_read_blk(io_channel channel, unsigned long block, | ||
521 | int count, void *buf) | ||
522 | { | ||
523 | struct unix_private_data *data; | ||
524 | struct unix_cache *cache, *reuse[READ_DIRECT_SIZE]; | ||
525 | errcode_t retval; | ||
526 | char *cp; | ||
527 | int i, j; | ||
528 | |||
529 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); | ||
530 | data = (struct unix_private_data *) channel->private_data; | ||
531 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); | ||
532 | |||
533 | #ifdef NO_IO_CACHE | ||
534 | return raw_read_blk(channel, data, block, count, buf); | ||
535 | #else | ||
536 | /* | ||
537 | * If we're doing an odd-sized read or a very large read, | ||
538 | * flush out the cache and then do a direct read. | ||
539 | */ | ||
540 | if (count < 0 || count > WRITE_DIRECT_SIZE) { | ||
541 | if ((retval = flush_cached_blocks(channel, data, 0))) | ||
542 | return retval; | ||
543 | return raw_read_blk(channel, data, block, count, buf); | ||
544 | } | ||
545 | |||
546 | cp = buf; | ||
547 | while (count > 0) { | ||
548 | /* If it's in the cache, use it! */ | ||
549 | if ((cache = find_cached_block(data, block, &reuse[0]))) { | ||
550 | #ifdef DEBUG | ||
551 | printf("Using cached block %d\n", block); | ||
552 | #endif | ||
553 | memcpy(cp, cache->buf, channel->block_size); | ||
554 | count--; | ||
555 | block++; | ||
556 | cp += channel->block_size; | ||
557 | continue; | ||
558 | } | ||
559 | /* | ||
560 | * Find the number of uncached blocks so we can do a | ||
561 | * single read request | ||
562 | */ | ||
563 | for (i=1; i < count; i++) | ||
564 | if (find_cached_block(data, block+i, &reuse[i])) | ||
565 | break; | ||
566 | #ifdef DEBUG | ||
567 | printf("Reading %d blocks starting at %d\n", i, block); | ||
568 | #endif | ||
569 | if ((retval = raw_read_blk(channel, data, block, i, cp))) | ||
570 | return retval; | ||
571 | |||
572 | /* Save the results in the cache */ | ||
573 | for (j=0; j < i; j++) { | ||
574 | count--; | ||
575 | cache = reuse[j]; | ||
576 | reuse_cache(channel, data, cache, block++); | ||
577 | memcpy(cache->buf, cp, channel->block_size); | ||
578 | cp += channel->block_size; | ||
579 | } | ||
580 | } | ||
581 | return 0; | ||
582 | #endif /* NO_IO_CACHE */ | ||
583 | } | ||
584 | |||
585 | static errcode_t unix_write_blk(io_channel channel, unsigned long block, | ||
586 | int count, const void *buf) | ||
587 | { | ||
588 | struct unix_private_data *data; | ||
589 | struct unix_cache *cache, *reuse; | ||
590 | errcode_t retval = 0; | ||
591 | const char *cp; | ||
592 | int writethrough; | ||
593 | |||
594 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); | ||
595 | data = (struct unix_private_data *) channel->private_data; | ||
596 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); | ||
597 | |||
598 | #ifdef NO_IO_CACHE | ||
599 | return raw_write_blk(channel, data, block, count, buf); | ||
600 | #else | ||
601 | /* | ||
602 | * If we're doing an odd-sized write or a very large write, | ||
603 | * flush out the cache completely and then do a direct write. | ||
604 | */ | ||
605 | if (count < 0 || count > WRITE_DIRECT_SIZE) { | ||
606 | if ((retval = flush_cached_blocks(channel, data, 1))) | ||
607 | return retval; | ||
608 | return raw_write_blk(channel, data, block, count, buf); | ||
609 | } | ||
610 | |||
611 | /* | ||
612 | * For a moderate-sized multi-block write, first force a write | ||
613 | * if we're in write-through cache mode, and then fill the | ||
614 | * cache with the blocks. | ||
615 | */ | ||
616 | writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH; | ||
617 | if (writethrough) | ||
618 | retval = raw_write_blk(channel, data, block, count, buf); | ||
619 | |||
620 | cp = buf; | ||
621 | while (count > 0) { | ||
622 | cache = find_cached_block(data, block, &reuse); | ||
623 | if (!cache) { | ||
624 | cache = reuse; | ||
625 | reuse_cache(channel, data, cache, block); | ||
626 | } | ||
627 | memcpy(cache->buf, cp, channel->block_size); | ||
628 | cache->dirty = !writethrough; | ||
629 | count--; | ||
630 | block++; | ||
631 | cp += channel->block_size; | ||
632 | } | ||
633 | return retval; | ||
634 | #endif /* NO_IO_CACHE */ | ||
635 | } | ||
636 | |||
637 | static errcode_t unix_write_byte(io_channel channel, unsigned long offset, | ||
638 | int size, const void *buf) | ||
639 | { | ||
640 | struct unix_private_data *data; | ||
641 | errcode_t retval = 0; | ||
642 | ssize_t actual; | ||
643 | |||
644 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); | ||
645 | data = (struct unix_private_data *) channel->private_data; | ||
646 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); | ||
647 | |||
648 | #ifndef NO_IO_CACHE | ||
649 | /* | ||
650 | * Flush out the cache completely | ||
651 | */ | ||
652 | if ((retval = flush_cached_blocks(channel, data, 1))) | ||
653 | return retval; | ||
654 | #endif | ||
655 | |||
656 | if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0) | ||
657 | return errno; | ||
658 | |||
659 | actual = write(data->dev, buf, size); | ||
660 | if (actual != size) | ||
661 | return EXT2_ET_SHORT_WRITE; | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | /* | ||
667 | * Flush data buffers to disk. | ||
668 | */ | ||
669 | static errcode_t unix_flush(io_channel channel) | ||
670 | { | ||
671 | struct unix_private_data *data; | ||
672 | errcode_t retval = 0; | ||
673 | |||
674 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); | ||
675 | data = (struct unix_private_data *) channel->private_data; | ||
676 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); | ||
677 | |||
678 | #ifndef NO_IO_CACHE | ||
679 | retval = flush_cached_blocks(channel, data, 0); | ||
680 | #endif | ||
681 | fsync(data->dev); | ||
682 | return retval; | ||
683 | } | ||
684 | |||
685 | static errcode_t unix_set_option(io_channel channel, const char *option, | ||
686 | const char *arg) | ||
687 | { | ||
688 | struct unix_private_data *data; | ||
689 | unsigned long tmp; | ||
690 | char *end; | ||
691 | |||
692 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); | ||
693 | data = (struct unix_private_data *) channel->private_data; | ||
694 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); | ||
695 | |||
696 | if (!strcmp(option, "offset")) { | ||
697 | if (!arg) | ||
698 | return EXT2_ET_INVALID_ARGUMENT; | ||
699 | |||
700 | tmp = strtoul(arg, &end, 0); | ||
701 | if (*end) | ||
702 | return EXT2_ET_INVALID_ARGUMENT; | ||
703 | data->offset = tmp; | ||
704 | return 0; | ||
705 | } | ||
706 | return EXT2_ET_INVALID_ARGUMENT; | ||
707 | } | ||