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