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