blob: 0f993e491544bef14af703276ae490efce6f3e61 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * unix_io.c --- This is the Unix I/O interface to the I/O manager.
3 *
4 * Implements a one-block write-through cache.
5 *
Theodore Ts'o19c78dc1997-04-29 16:17:09 +00006 * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
7 *
8 * %Begin-Header%
9 * This file may be redistributed under the terms of the GNU Public
10 * License.
11 * %End-Header%
Theodore Ts'o3839e651997-04-26 13:21:57 +000012 */
13
Theodore Ts'odc5f68c2000-05-25 23:31:54 +000014#define _LARGEFILE_SOURCE
15#define _LARGEFILE64_SOURCE
16
Theodore Ts'o3839e651997-04-26 13:21:57 +000017#include <stdio.h>
18#include <string.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000019#if HAVE_UNISTD_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000020#include <unistd.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000021#endif
Theodore Ts'oc4e749a1998-02-20 05:33:14 +000022#if HAVE_ERRNO_H
23#include <errno.h>
24#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000025#include <fcntl.h>
26#include <time.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000027#if HAVE_SYS_STAT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000028#include <sys/stat.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000029#endif
30#if HAVE_SYS_TYPES_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000031#include <sys/types.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000032#endif
Theodore Ts'o8880e752001-11-26 21:05:36 -050033#include <sys/resource.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000034
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000035#include "ext2_fs.h"
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000036#include "ext2fs.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000037
Theodore Ts'of3db3561997-04-26 13:34:30 +000038/*
39 * For checking structure magic numbers...
40 */
41
42#define EXT2_CHECK_MAGIC(struct, code) \
43 if ((struct)->magic != (code)) return (code)
Theodore Ts'oadfc8c62000-10-18 19:22:24 +000044
45struct unix_cache {
46 char *buf;
47 unsigned long block;
48 int access_time;
49 int dirty:1;
50 int in_use:1;
51};
52
53#define CACHE_SIZE 8
54#define WRITE_VIA_CACHE_SIZE 4 /* Must be smaller than CACHE_SIZE */
55
Theodore Ts'o3839e651997-04-26 13:21:57 +000056struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000057 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000058 int dev;
59 int flags;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +000060 int access_time;
61 struct unix_cache cache[CACHE_SIZE];
Theodore Ts'o3839e651997-04-26 13:21:57 +000062};
63
64static errcode_t unix_open(const char *name, int flags, io_channel *channel);
65static errcode_t unix_close(io_channel channel);
66static errcode_t unix_set_blksize(io_channel channel, int blksize);
67static errcode_t unix_read_blk(io_channel channel, unsigned long block,
68 int count, void *data);
69static errcode_t unix_write_blk(io_channel channel, unsigned long block,
70 int count, const void *data);
71static errcode_t unix_flush(io_channel channel);
Theodore Ts'oc180ac82000-10-26 20:24:43 +000072static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
73 int size, const void *data);
Theodore Ts'o3839e651997-04-26 13:21:57 +000074
Theodore Ts'of3db3561997-04-26 13:34:30 +000075static struct struct_io_manager struct_unix_manager = {
76 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000077 "Unix I/O Manager",
78 unix_open,
79 unix_close,
80 unix_set_blksize,
81 unix_read_blk,
82 unix_write_blk,
Theodore Ts'oc180ac82000-10-26 20:24:43 +000083 unix_flush,
84 unix_write_byte
Theodore Ts'o3839e651997-04-26 13:21:57 +000085};
86
87io_manager unix_io_manager = &struct_unix_manager;
88
Theodore Ts'oadfc8c62000-10-18 19:22:24 +000089/*
90 * Here are the raw I/O functions
91 */
92static errcode_t raw_read_blk(io_channel channel,
93 struct unix_private_data *data,
94 unsigned long block,
95 int count, void *buf)
96{
97 errcode_t retval;
98 size_t size;
99 ext2_loff_t location;
100 int actual = 0;
101
102 size = (count < 0) ? -count : count * channel->block_size;
103 location = (ext2_loff_t) block * channel->block_size;
104 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
105 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
106 goto error_out;
107 }
108 actual = read(data->dev, buf, size);
109 if (actual != size) {
110 if (actual < 0)
111 actual = 0;
112 retval = EXT2_ET_SHORT_READ;
113 goto error_out;
114 }
115 return 0;
116
117error_out:
118 memset((char *) buf+actual, 0, size-actual);
119 if (channel->read_error)
120 retval = (channel->read_error)(channel, block, count, buf,
121 size, actual, retval);
122 return retval;
123}
124
125static errcode_t raw_write_blk(io_channel channel,
126 struct unix_private_data *data,
127 unsigned long block,
128 int count, const void *buf)
129{
130 size_t size;
131 ext2_loff_t location;
132 int actual = 0;
133 errcode_t retval;
134
135 if (count == 1)
136 size = channel->block_size;
137 else {
138 if (count < 0)
139 size = -count;
140 else
141 size = count * channel->block_size;
142 }
143
144 location = (ext2_loff_t) block * channel->block_size;
145 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
146 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
147 goto error_out;
148 }
149
150 actual = write(data->dev, buf, size);
151 if (actual != size) {
152 retval = EXT2_ET_SHORT_WRITE;
153 goto error_out;
154 }
155 return 0;
156
157error_out:
158 if (channel->write_error)
159 retval = (channel->write_error)(channel, block, count, buf,
160 size, actual, retval);
161 return retval;
162}
163
164
165/*
166 * Here we implement the cache functions
167 */
168
169/* Allocate the cache buffers */
170static errcode_t alloc_cache(io_channel channel,
171 struct unix_private_data *data)
172{
173 errcode_t retval;
174 struct unix_cache *cache;
175 int i;
176
177 data->access_time = 0;
178 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
179 cache->block = 0;
180 cache->access_time = 0;
181 cache->dirty = 0;
182 cache->in_use = 0;
183 if ((retval = ext2fs_get_mem(channel->block_size,
184 (void **) &cache->buf)))
185 return retval;
186 }
187 return 0;
188}
189
190/* Free the cache buffers */
191static void free_cache(io_channel channel,
192 struct unix_private_data *data)
193{
194 struct unix_cache *cache;
195 int i;
196
197 data->access_time = 0;
198 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
199 cache->block = 0;
200 cache->access_time = 0;
201 cache->dirty = 0;
202 cache->in_use = 0;
203 if (cache->buf)
204 ext2fs_free_mem((void **) &cache->buf);
205 cache->buf = 0;
206 }
207}
208
209/*
210 * Try to find a block in the cache. If get_cache is non-zero, then
211 * if the block isn't in the cache, evict the oldest block in the
212 * cache and create a new cache entry for the requested block.
213 */
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000214static struct unix_cache *find_cached_block(io_channel channel,
215 struct unix_private_data *data,
216 unsigned long block,
217 int get_cache)
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000218{
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000219 struct unix_cache *cache, *unused_cache, *oldest_cache;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000220 int i;
221
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000222 unused_cache = oldest_cache = 0;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000223 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
224 if (!cache->in_use) {
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000225 unused_cache = cache;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000226 continue;
227 }
228 if (cache->block == block) {
229 cache->access_time = ++data->access_time;
230 return cache;
231 }
232 if (!oldest_cache ||
233 (cache->access_time < oldest_cache->access_time))
234 oldest_cache = cache;
235 }
236 if (!get_cache)
237 return 0;
238
239 /*
240 * Try to allocate cache slot.
241 */
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000242 if (unused_cache)
243 cache = unused_cache;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000244 else {
245 cache = oldest_cache;
246 if (cache->dirty)
247 raw_write_blk(channel, data,
248 cache->block, 1, cache->buf);
249 }
250 cache->in_use = 1;
251 cache->block = block;
252 cache->access_time = ++data->access_time;
253 return cache;
254}
255
256/*
257 * Flush all of the blocks in the cache
258 */
259static errcode_t flush_cached_blocks(io_channel channel,
260 struct unix_private_data *data,
261 int invalidate)
262
263{
264 struct unix_cache *cache;
265 errcode_t retval, retval2;
266 int i;
267
268 retval2 = 0;
269 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
270 if (!cache->in_use)
271 continue;
272
273 if (invalidate)
274 cache->in_use = 0;
275
276 if (!cache->dirty)
277 continue;
278
279 retval = raw_write_blk(channel, data,
280 cache->block, 1, cache->buf);
281 if (retval)
282 retval2 = retval;
283 else
284 cache->dirty = 0;
285 }
286 return retval2;
287}
288
289
290
Theodore Ts'o3839e651997-04-26 13:21:57 +0000291static errcode_t unix_open(const char *name, int flags, io_channel *channel)
292{
293 io_channel io = NULL;
294 struct unix_private_data *data = NULL;
295 errcode_t retval;
Theodore Ts'odc5f68c2000-05-25 23:31:54 +0000296 int open_flags;
Theodore Ts'o8880e752001-11-26 21:05:36 -0500297 struct stat st;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000298
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000299 if (name == 0)
300 return EXT2_ET_BAD_DEVICE_NAME;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000301 retval = ext2fs_get_mem(sizeof(struct struct_io_channel),
302 (void **) &io);
303 if (retval)
304 return retval;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000305 memset(io, 0, sizeof(struct struct_io_channel));
306 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000307 retval = ext2fs_get_mem(sizeof(struct unix_private_data),
308 (void **) &data);
309 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000310 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000311
Theodore Ts'o3839e651997-04-26 13:21:57 +0000312 io->manager = unix_io_manager;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000313 retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name);
314 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000315 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000316
Theodore Ts'o3839e651997-04-26 13:21:57 +0000317 strcpy(io->name, name);
318 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000319 io->block_size = 1024;
320 io->read_error = 0;
321 io->write_error = 0;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000322 io->refcount = 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000323
324 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000325 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000326
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000327 if ((retval = alloc_cache(io, data)))
328 goto cleanup;
329
Theodore Ts'odc5f68c2000-05-25 23:31:54 +0000330 open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
331#ifdef HAVE_OPEN64
332 data->dev = open64(name, open_flags);
333#else
334 data->dev = open(name, open_flags);
335#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000336 if (data->dev < 0) {
337 retval = errno;
338 goto cleanup;
339 }
Theodore Ts'o8880e752001-11-26 21:05:36 -0500340 /*
341 * Work around a bug in 2.4.10+ kernels where writes to block
342 * devices are wrongly getting hit by the filesize limit.
343 */
344 if ((flags & IO_FLAG_RW) &&
345 (fstat(data->dev, &st) == 0) &&
346 (S_ISBLK(st.st_mode))) {
347 struct rlimit rlim;
348
Theodore Ts'obd278802001-12-03 05:47:32 +0100349 rlim.rlim_cur = rlim.rlim_max = ((unsigned long)(~0UL));
Theodore Ts'o8880e752001-11-26 21:05:36 -0500350 setrlimit(RLIMIT_FSIZE, &rlim);
351 getrlimit(RLIMIT_FSIZE, &rlim);
Theodore Ts'obd278802001-12-03 05:47:32 +0100352 if (((unsigned long) rlim.rlim_cur) <
353 ((unsigned long) rlim.rlim_max)) {
Theodore Ts'o8880e752001-11-26 21:05:36 -0500354 rlim.rlim_cur = rlim.rlim_max;
355 setrlimit(RLIMIT_FSIZE, &rlim);
356 }
357 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000358 *channel = io;
359 return 0;
360
361cleanup:
Theodore Ts'o3839e651997-04-26 13:21:57 +0000362 if (data) {
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000363 free_cache(io, data);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000364 ext2fs_free_mem((void **) &data);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000365 }
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000366 if (io)
367 ext2fs_free_mem((void **) &io);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000368 return retval;
369}
370
371static errcode_t unix_close(io_channel channel)
372{
373 struct unix_private_data *data;
374 errcode_t retval = 0;
375
Theodore Ts'of3db3561997-04-26 13:34:30 +0000376 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000377 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000378 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000379
380 if (--channel->refcount > 0)
381 return 0;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000382
383 retval = flush_cached_blocks(channel, data, 0);
384
Theodore Ts'o3839e651997-04-26 13:21:57 +0000385 if (close(data->dev) < 0)
386 retval = errno;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000387 free_cache(channel, data);
Theodore Ts'of12e2852002-02-20 01:06:25 -0500388
389 ext2fs_free_mem((void **) &channel->private_data);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000390 if (channel->name)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000391 ext2fs_free_mem((void **) &channel->name);
392 ext2fs_free_mem((void **) &channel);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000393 return retval;
394}
395
396static errcode_t unix_set_blksize(io_channel channel, int blksize)
397{
398 struct unix_private_data *data;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000399 errcode_t retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000400
Theodore Ts'of3db3561997-04-26 13:34:30 +0000401 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000402 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000403 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
404
Theodore Ts'o3839e651997-04-26 13:21:57 +0000405 if (channel->block_size != blksize) {
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000406 if ((retval = flush_cached_blocks(channel, data, 0)))
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000407 return retval;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000408
409 channel->block_size = blksize;
410 free_cache(channel, data);
411 if ((retval = alloc_cache(channel, data)))
412 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000413 }
414 return 0;
415}
416
417
418static errcode_t unix_read_blk(io_channel channel, unsigned long block,
419 int count, void *buf)
420{
421 struct unix_private_data *data;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000422 struct unix_cache *cache;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000423 errcode_t retval;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000424 char *cp;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000425 int i, j;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000426
Theodore Ts'of3db3561997-04-26 13:34:30 +0000427 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000428 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000429 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000430
431 /*
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000432 * If we're doing an odd-sized read, flush out the cache and
433 * then do a direct read.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000434 */
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000435 if (count < 0) {
436 if ((retval = flush_cached_blocks(channel, data, 0)))
437 return retval;
438 return raw_read_blk(channel, data, block, count, buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000439 }
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000440
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000441 cp = buf;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000442 while (count > 0) {
443 /* If it's in the cache, use it! */
444 if ((cache = find_cached_block(channel, data, block, 0))) {
445#ifdef DEBUG
446 printf("Using cached block %d\n", block);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000447#endif
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000448 memcpy(cp, cache->buf, channel->block_size);
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000449 count--;
450 block++;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000451 cp += channel->block_size;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000452 continue;
453 }
454 /*
455 * Find the number of uncached blocks so we can do a
456 * single read request
457 */
458 for (i=1; i < count; i++)
459 if (find_cached_block(channel, data, block+i, 0))
460 break;
461#ifdef DEBUG
462 printf("Reading %d blocks starting at %d\n", i, block);
463#endif
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000464 if ((retval = raw_read_blk(channel, data, block, i, cp)))
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000465 return retval;
466
467 /* Save the results in the cache */
468 for (j=0; j < i; j++) {
469 count--;
470 cache = find_cached_block(channel, data, block++, 1);
471 if (cache)
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000472 memcpy(cache->buf, cp, channel->block_size);
473 cp += channel->block_size;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000474 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000475 }
476 return 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000477}
478
479static errcode_t unix_write_blk(io_channel channel, unsigned long block,
480 int count, const void *buf)
481{
482 struct unix_private_data *data;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000483 struct unix_cache *cache;
484 errcode_t retval = 0, retval2;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000485 const char *cp;
486 int writethrough;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000487
Theodore Ts'of3db3561997-04-26 13:34:30 +0000488 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000489 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000490 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000491
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000492 /*
493 * If we're doing an odd-sized write or a very large write,
494 * flush out the cache completely and then do a direct write.
495 */
496 if (count < 0 || count > WRITE_VIA_CACHE_SIZE) {
497 if ((retval = flush_cached_blocks(channel, data, 1)))
498 return retval;
499 return raw_write_blk(channel, data, block, count, buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000500 }
501
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000502 /*
503 * For a moderate-sized multi-block write, first force a write
504 * if we're in write-through cache mode, and then fill the
505 * cache with the blocks.
506 */
507 writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
508 if (writethrough)
509 retval = raw_write_blk(channel, data, block, count, buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000510
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000511 cp = buf;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000512 while (count > 0) {
513 cache = find_cached_block(channel, data, block, 1);
514 if (!cache) {
515 /*
516 * Oh shit, we couldn't get cache descriptor.
517 * Force the write directly.
518 */
519 if ((retval2 = raw_write_blk(channel, data, block,
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000520 1, cp)))
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000521 retval = retval2;
522 } else {
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000523 memcpy(cache->buf, cp, channel->block_size);
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000524 cache->dirty = !writethrough;
525 }
526 count--;
527 block++;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000528 cp += channel->block_size;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000529 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000530 return retval;
531}
532
Theodore Ts'oc180ac82000-10-26 20:24:43 +0000533static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
534 int size, const void *buf)
535{
536 struct unix_private_data *data;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000537 errcode_t retval = 0;
Theodore Ts'oc180ac82000-10-26 20:24:43 +0000538 size_t actual;
539
540 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
541 data = (struct unix_private_data *) channel->private_data;
542 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
543
544 /*
545 * Flush out the cache completely
546 */
547 if ((retval = flush_cached_blocks(channel, data, 1)))
548 return retval;
549
550 if (lseek(data->dev, offset, SEEK_SET) < 0)
551 return errno;
552
553 actual = write(data->dev, buf, size);
554 if (actual != size)
555 return EXT2_ET_SHORT_WRITE;
556
557 return 0;
558}
559
Theodore Ts'o3839e651997-04-26 13:21:57 +0000560/*
Theodore Ts'o36f21431997-06-14 07:25:40 +0000561 * Flush data buffers to disk.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000562 */
563static errcode_t unix_flush(io_channel channel)
564{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000565 struct unix_private_data *data;
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000566 errcode_t retval = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000567
568 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
569 data = (struct unix_private_data *) channel->private_data;
570 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000571
572 retval = flush_cached_blocks(channel, data, 0);
Theodore Ts'o36f21431997-06-14 07:25:40 +0000573 fsync(data->dev);
Theodore Ts'oadfc8c62000-10-18 19:22:24 +0000574 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000575}
576