unix_io.c revision c180ac86533bcbfb1560bd4aa01464785a760f70
1/*
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 *
6 * 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%
12 */
13
14#define _LARGEFILE_SOURCE
15#define _LARGEFILE64_SOURCE
16
17#include <stdio.h>
18#include <string.h>
19#if HAVE_UNISTD_H
20#include <unistd.h>
21#endif
22#if HAVE_ERRNO_H
23#include <errno.h>
24#endif
25#include <fcntl.h>
26#include <time.h>
27#if HAVE_SYS_STAT_H
28#include <sys/stat.h>
29#endif
30#if HAVE_SYS_TYPES_H
31#include <sys/types.h>
32#endif
33
34#if EXT2_FLAT_INCLUDES
35#include "ext2_fs.h"
36#else
37#include <linux/ext2_fs.h>
38#endif
39
40#include "ext2fs.h"
41
42/*
43 * For checking structure magic numbers...
44 */
45
46#define EXT2_CHECK_MAGIC(struct, code) \
47	  if ((struct)->magic != (code)) return (code)
48
49struct unix_cache {
50	char		*buf;
51	unsigned long	block;
52	int		access_time;
53	int		dirty:1;
54	int		in_use:1;
55};
56
57#define CACHE_SIZE 8
58#define WRITE_VIA_CACHE_SIZE 4	/* Must be smaller than CACHE_SIZE */
59
60struct unix_private_data {
61	int	magic;
62	int	dev;
63	int	flags;
64	int	access_time;
65	struct unix_cache cache[CACHE_SIZE];
66};
67
68static errcode_t unix_open(const char *name, int flags, io_channel *channel);
69static errcode_t unix_close(io_channel channel);
70static errcode_t unix_set_blksize(io_channel channel, int blksize);
71static errcode_t unix_read_blk(io_channel channel, unsigned long block,
72			       int count, void *data);
73static errcode_t unix_write_blk(io_channel channel, unsigned long block,
74				int count, const void *data);
75static errcode_t unix_flush(io_channel channel);
76static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
77				int size, const void *data);
78
79static struct struct_io_manager struct_unix_manager = {
80	EXT2_ET_MAGIC_IO_MANAGER,
81	"Unix I/O Manager",
82	unix_open,
83	unix_close,
84	unix_set_blksize,
85	unix_read_blk,
86	unix_write_blk,
87	unix_flush,
88	unix_write_byte
89};
90
91io_manager unix_io_manager = &struct_unix_manager;
92
93/*
94 * Here are the raw I/O functions
95 */
96static errcode_t raw_read_blk(io_channel channel,
97			      struct unix_private_data *data,
98			      unsigned long block,
99			      int count, void *buf)
100{
101	errcode_t	retval;
102	size_t		size;
103	ext2_loff_t	location;
104	int		actual = 0;
105
106	size = (count < 0) ? -count : count * channel->block_size;
107	location = (ext2_loff_t) block * channel->block_size;
108	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
109		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
110		goto error_out;
111	}
112	actual = read(data->dev, buf, size);
113	if (actual != size) {
114		if (actual < 0)
115			actual = 0;
116		retval = EXT2_ET_SHORT_READ;
117		goto error_out;
118	}
119	return 0;
120
121error_out:
122	memset((char *) buf+actual, 0, size-actual);
123	if (channel->read_error)
124		retval = (channel->read_error)(channel, block, count, buf,
125					       size, actual, retval);
126	return retval;
127}
128
129static errcode_t raw_write_blk(io_channel channel,
130			       struct unix_private_data *data,
131			       unsigned long block,
132			       int count, const void *buf)
133{
134	size_t		size;
135	ext2_loff_t	location;
136	int		actual = 0;
137	errcode_t	retval;
138
139	if (count == 1)
140		size = channel->block_size;
141	else {
142		if (count < 0)
143			size = -count;
144		else
145			size = count * channel->block_size;
146	}
147
148	location = (ext2_loff_t) block * channel->block_size;
149	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
150		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
151		goto error_out;
152	}
153
154	actual = write(data->dev, buf, size);
155	if (actual != size) {
156		retval = EXT2_ET_SHORT_WRITE;
157		goto error_out;
158	}
159	return 0;
160
161error_out:
162	if (channel->write_error)
163		retval = (channel->write_error)(channel, block, count, buf,
164						size, actual, retval);
165	return retval;
166}
167
168
169/*
170 * Here we implement the cache functions
171 */
172
173/* Allocate the cache buffers */
174static errcode_t alloc_cache(io_channel channel,
175			     struct unix_private_data *data)
176{
177	errcode_t		retval;
178	struct unix_cache	*cache;
179	int			i;
180
181	data->access_time = 0;
182	for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
183		cache->block = 0;
184		cache->access_time = 0;
185		cache->dirty = 0;
186		cache->in_use = 0;
187		if ((retval = ext2fs_get_mem(channel->block_size,
188					     (void **) &cache->buf)))
189			return retval;
190	}
191	return 0;
192}
193
194/* Free the cache buffers */
195static void free_cache(io_channel channel,
196		       struct unix_private_data *data)
197{
198	struct unix_cache	*cache;
199	int			i;
200
201	data->access_time = 0;
202	for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
203		cache->block = 0;
204		cache->access_time = 0;
205		cache->dirty = 0;
206		cache->in_use = 0;
207		if (cache->buf)
208			ext2fs_free_mem((void **) &cache->buf);
209		cache->buf = 0;
210	}
211}
212
213/*
214 * Try to find a block in the cache.  If get_cache is non-zero, then
215 * if the block isn't in the cache, evict the oldest block in the
216 * cache and create a new cache entry for the requested block.
217 */
218struct unix_cache *find_cached_block(io_channel channel,
219				     struct unix_private_data *data,
220				     unsigned long block,
221				     int get_cache)
222{
223	struct unix_cache	*cache, *free_cache, *oldest_cache;
224	int			i;
225
226	free_cache = oldest_cache = 0;
227	for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
228		if (!cache->in_use) {
229			free_cache = cache;
230			continue;
231		}
232		if (cache->block == block) {
233			cache->access_time = ++data->access_time;
234			return cache;
235		}
236		if (!oldest_cache ||
237		    (cache->access_time < oldest_cache->access_time))
238			oldest_cache = cache;
239	}
240	if (!get_cache)
241		return 0;
242
243	/*
244	 * Try to allocate cache slot.
245	 */
246	if (free_cache)
247		cache = free_cache;
248	else {
249		cache = oldest_cache;
250		if (cache->dirty)
251			raw_write_blk(channel, data,
252				      cache->block, 1, cache->buf);
253	}
254	cache->in_use = 1;
255	cache->block = block;
256	cache->access_time = ++data->access_time;
257	return cache;
258}
259
260/*
261 * Flush all of the blocks in the cache
262 */
263static errcode_t flush_cached_blocks(io_channel channel,
264				     struct unix_private_data *data,
265				     int invalidate)
266
267{
268	struct unix_cache	*cache;
269	errcode_t		retval, retval2;
270	int			i;
271
272	retval2 = 0;
273	for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
274		if (!cache->in_use)
275			continue;
276
277		if (invalidate)
278			cache->in_use = 0;
279
280		if (!cache->dirty)
281			continue;
282
283		retval = raw_write_blk(channel, data,
284				       cache->block, 1, cache->buf);
285		if (retval)
286			retval2 = retval;
287		else
288			cache->dirty = 0;
289	}
290	return retval2;
291}
292
293
294
295static errcode_t unix_open(const char *name, int flags, io_channel *channel)
296{
297	io_channel	io = NULL;
298	struct unix_private_data *data = NULL;
299	errcode_t	retval;
300	int		open_flags;
301
302	if (name == 0)
303		return EXT2_ET_BAD_DEVICE_NAME;
304	retval = ext2fs_get_mem(sizeof(struct struct_io_channel),
305				(void **) &io);
306	if (retval)
307		return retval;
308	memset(io, 0, sizeof(struct struct_io_channel));
309	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
310	retval = ext2fs_get_mem(sizeof(struct unix_private_data),
311				(void **) &data);
312	if (retval)
313		goto cleanup;
314
315	io->manager = unix_io_manager;
316	retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name);
317	if (retval)
318		goto cleanup;
319
320	strcpy(io->name, name);
321	io->private_data = data;
322	io->block_size = 1024;
323	io->read_error = 0;
324	io->write_error = 0;
325	io->refcount = 1;
326
327	memset(data, 0, sizeof(struct unix_private_data));
328	data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
329
330	if ((retval = alloc_cache(io, data)))
331		goto cleanup;
332
333	open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
334#ifdef HAVE_OPEN64
335	data->dev = open64(name, open_flags);
336#else
337	data->dev = open(name, open_flags);
338#endif
339	if (data->dev < 0) {
340		retval = errno;
341		goto cleanup;
342	}
343	*channel = io;
344	return 0;
345
346cleanup:
347	if (data) {
348		free_cache(io, data);
349		ext2fs_free_mem((void **) &data);
350	}
351	if (io)
352		ext2fs_free_mem((void **) &io);
353	return retval;
354}
355
356static errcode_t unix_close(io_channel channel)
357{
358	struct unix_private_data *data;
359	errcode_t	retval = 0;
360
361	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
362	data = (struct unix_private_data *) channel->private_data;
363	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
364
365	if (--channel->refcount > 0)
366		return 0;
367
368	retval = flush_cached_blocks(channel, data, 0);
369
370	if (close(data->dev) < 0)
371		retval = errno;
372	free_cache(channel, data);
373	if (channel->private_data)
374		ext2fs_free_mem((void **) &channel->private_data);
375	if (channel->name)
376		ext2fs_free_mem((void **) &channel->name);
377	ext2fs_free_mem((void **) &channel);
378	return retval;
379}
380
381static errcode_t unix_set_blksize(io_channel channel, int blksize)
382{
383	struct unix_private_data *data;
384	errcode_t		retval;
385
386	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
387	data = (struct unix_private_data *) channel->private_data;
388	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
389
390	if (channel->block_size != blksize) {
391		if ((retval = flush_cached_blocks(channel, data, 0)))
392			return retval;
393
394		channel->block_size = blksize;
395		free_cache(channel, data);
396		if ((retval = alloc_cache(channel, data)))
397			return retval;
398	}
399	return 0;
400}
401
402
403static errcode_t unix_read_blk(io_channel channel, unsigned long block,
404			       int count, void *buf)
405{
406	struct unix_private_data *data;
407	struct unix_cache *cache;
408	errcode_t	retval;
409	int		i, j;
410
411	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
412	data = (struct unix_private_data *) channel->private_data;
413	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
414
415	/*
416	 * If we're doing an odd-sized read, flush out the cache and
417	 * then do a direct read.
418	 */
419	if (count < 0) {
420		if ((retval = flush_cached_blocks(channel, data, 0)))
421			return retval;
422		return raw_read_blk(channel, data, block, count, buf);
423	}
424
425	while (count > 0) {
426		/* If it's in the cache, use it! */
427		if ((cache = find_cached_block(channel, data, block, 0))) {
428#ifdef DEBUG
429			printf("Using cached block %d\n", block);
430#endif
431			memcpy(buf, cache->buf, channel->block_size);
432			count--;
433			block++;
434			buf += channel->block_size;
435			continue;
436		}
437		/*
438		 * Find the number of uncached blocks so we can do a
439		 * single read request
440		 */
441		for (i=1; i < count; i++)
442			if (find_cached_block(channel, data, block+i, 0))
443				break;
444#ifdef DEBUG
445		printf("Reading %d blocks starting at %d\n", i, block);
446#endif
447		if ((retval = raw_read_blk(channel, data, block, i, buf)))
448			return retval;
449
450		/* Save the results in the cache */
451		for (j=0; j < i; j++) {
452			count--;
453			cache = find_cached_block(channel, data, block++, 1);
454			if (cache)
455				memcpy(cache->buf, buf, channel->block_size);
456			buf += channel->block_size;
457		}
458	}
459	return 0;
460}
461
462static errcode_t unix_write_blk(io_channel channel, unsigned long block,
463				int count, const void *buf)
464{
465	struct unix_private_data *data;
466	struct unix_cache *cache;
467	errcode_t	retval = 0, retval2;
468	char		*cp;
469	int		i, writethrough;
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	/*
476	 * If we're doing an odd-sized write or a very large write,
477	 * flush out the cache completely and then do a direct write.
478	 */
479	if (count < 0 || count > WRITE_VIA_CACHE_SIZE) {
480		if ((retval = flush_cached_blocks(channel, data, 1)))
481			return retval;
482		return raw_write_blk(channel, data, block, count, buf);
483	}
484
485	/*
486	 * For a moderate-sized multi-block write, first force a write
487	 * if we're in write-through cache mode, and then fill the
488	 * cache with the blocks.
489	 */
490	writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
491	if (writethrough)
492		retval = raw_write_blk(channel, data, block, count, buf);
493
494	while (count > 0) {
495		cache = find_cached_block(channel, data, block, 1);
496		if (!cache) {
497			/*
498			 * Oh shit, we couldn't get cache descriptor.
499			 * Force the write directly.
500			 */
501			if ((retval2 = raw_write_blk(channel, data, block,
502						1, buf)))
503				retval = retval2;
504		} else {
505			memcpy(cache->buf, buf, channel->block_size);
506			cache->dirty = !writethrough;
507		}
508		count--;
509		block++;
510		buf += channel->block_size;
511	}
512	return retval;
513}
514
515static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
516				 int size, const void *buf)
517{
518	struct unix_private_data *data;
519	struct unix_cache *cache;
520	errcode_t	retval = 0, retval2;
521	char		*cp;
522	int		i, writethrough;
523	size_t		actual;
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	/*
530	 * Flush out the cache completely
531	 */
532	if ((retval = flush_cached_blocks(channel, data, 1)))
533		return retval;
534
535	if (lseek(data->dev, offset, SEEK_SET) < 0)
536		return errno;
537
538	actual = write(data->dev, buf, size);
539	if (actual != size)
540		return EXT2_ET_SHORT_WRITE;
541
542	return 0;
543}
544
545/*
546 * Flush data buffers to disk.
547 */
548static errcode_t unix_flush(io_channel channel)
549{
550	struct unix_private_data *data;
551	errcode_t retval = 0;
552
553	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
554	data = (struct unix_private_data *) channel->private_data;
555	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
556
557	retval = flush_cached_blocks(channel, data, 0);
558	fsync(data->dev);
559	return retval;
560}
561
562