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