unix_io.c revision c4e749abd8451f02418fe552b2af14f226f7bd1e
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#include <stdio.h>
15#include <string.h>
16#if HAVE_UNISTD_H
17#include <unistd.h>
18#endif
19#if HAVE_ERRNO_H
20#include <errno.h>
21#endif
22#include <fcntl.h>
23#include <time.h>
24#if HAVE_SYS_STAT_H
25#include <sys/stat.h>
26#endif
27#if HAVE_SYS_TYPES_H
28#include <sys/types.h>
29#endif
30
31#if EXT2_FLAT_INCLUDES
32#include "ext2_fs.h"
33#else
34#include <linux/ext2_fs.h>
35#endif
36
37#include "ext2fs.h"
38
39/*
40 * For checking structure magic numbers...
41 */
42
43#define EXT2_CHECK_MAGIC(struct, code) \
44	  if ((struct)->magic != (code)) return (code)
45
46struct unix_private_data {
47	int	magic;
48	int	dev;
49	int	flags;
50	char	*buf;
51	int	buf_block_nr;
52};
53
54static errcode_t unix_open(const char *name, int flags, io_channel *channel);
55static errcode_t unix_close(io_channel channel);
56static errcode_t unix_set_blksize(io_channel channel, int blksize);
57static errcode_t unix_read_blk(io_channel channel, unsigned long block,
58			       int count, void *data);
59static errcode_t unix_write_blk(io_channel channel, unsigned long block,
60				int count, const void *data);
61static errcode_t unix_flush(io_channel channel);
62
63static struct struct_io_manager struct_unix_manager = {
64	EXT2_ET_MAGIC_IO_MANAGER,
65	"Unix I/O Manager",
66	unix_open,
67	unix_close,
68	unix_set_blksize,
69	unix_read_blk,
70	unix_write_blk,
71	unix_flush
72};
73
74io_manager unix_io_manager = &struct_unix_manager;
75
76static errcode_t unix_open(const char *name, int flags, io_channel *channel)
77{
78	io_channel	io = NULL;
79	struct unix_private_data *data = NULL;
80	errcode_t	retval;
81
82	if (name == 0)
83		return EXT2_ET_BAD_DEVICE_NAME;
84	retval = ext2fs_get_mem(sizeof(struct struct_io_channel),
85				(void **) &io);
86	if (retval)
87		return retval;
88	memset(io, 0, sizeof(struct struct_io_channel));
89	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
90	retval = ext2fs_get_mem(sizeof(struct unix_private_data),
91				(void **) &data);
92	if (retval)
93		goto cleanup;
94
95	io->manager = unix_io_manager;
96	retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name);
97	if (retval)
98		goto cleanup;
99
100	strcpy(io->name, name);
101	io->private_data = data;
102	io->block_size = 1024;
103	io->read_error = 0;
104	io->write_error = 0;
105	io->refcount = 1;
106
107	memset(data, 0, sizeof(struct unix_private_data));
108	data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
109	retval = ext2fs_get_mem(io->block_size, (void **) &data->buf);
110	data->buf_block_nr = -1;
111	if (retval)
112		goto cleanup;
113
114	data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
115	if (data->dev < 0) {
116		retval = errno;
117		goto cleanup;
118	}
119	*channel = io;
120	return 0;
121
122cleanup:
123	if (io)
124		ext2fs_free_mem((void **) &io);
125	if (data) {
126		if (data->buf)
127			ext2fs_free_mem((void **) &data->buf);
128		ext2fs_free_mem((void **) &data);
129	}
130	return retval;
131}
132
133static errcode_t unix_close(io_channel channel)
134{
135	struct unix_private_data *data;
136	errcode_t	retval = 0;
137
138	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
139	data = (struct unix_private_data *) channel->private_data;
140	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
141
142	if (--channel->refcount > 0)
143		return 0;
144
145	if (close(data->dev) < 0)
146		retval = errno;
147	if (data->buf)
148		ext2fs_free_mem((void **) &data->buf);
149	if (channel->private_data)
150		ext2fs_free_mem((void **) &channel->private_data);
151	if (channel->name)
152		ext2fs_free_mem((void **) &channel->name);
153	ext2fs_free_mem((void **) &channel);
154	return retval;
155}
156
157static errcode_t unix_set_blksize(io_channel channel, int blksize)
158{
159	struct unix_private_data *data;
160	errcode_t		retval;
161
162	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
163	data = (struct unix_private_data *) channel->private_data;
164	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
165
166	if (channel->block_size != blksize) {
167		channel->block_size = blksize;
168		ext2fs_free_mem((void **) &data->buf);
169		retval = ext2fs_get_mem(blksize, (void **) &data->buf);
170		if (retval)
171			return retval;
172		data->buf_block_nr = -1;
173	}
174	return 0;
175}
176
177
178static errcode_t unix_read_blk(io_channel channel, unsigned long block,
179			       int count, void *buf)
180{
181	struct unix_private_data *data;
182	errcode_t	retval;
183	size_t		size;
184	ext2_loff_t	location;
185	int		actual = 0;
186
187	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
188	data = (struct unix_private_data *) channel->private_data;
189	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
190
191	/*
192	 * If it's in the cache, use it!
193	 */
194	if ((count == 1) && (block == data->buf_block_nr)) {
195		memcpy(buf, data->buf, channel->block_size);
196		return 0;
197	}
198#if 0
199	printf("read_block %lu (%d)\n", block, count);
200#endif
201	size = (count < 0) ? -count : count * channel->block_size;
202	location = (ext2_loff_t) block * channel->block_size;
203	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
204		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
205		goto error_out;
206	}
207	actual = read(data->dev, buf, size);
208	if (actual != size) {
209		if (actual < 0)
210			actual = 0;
211		retval = EXT2_ET_SHORT_READ;
212		goto error_out;
213	}
214	if (count == 1) {
215		data->buf_block_nr = block;
216		memcpy(data->buf, buf, size);	/* Update the cache */
217	}
218	return 0;
219
220error_out:
221	memset((char *) buf+actual, 0, size-actual);
222	if (channel->read_error)
223		retval = (channel->read_error)(channel, block, count, buf,
224					       size, actual, retval);
225	return retval;
226}
227
228static errcode_t unix_write_blk(io_channel channel, unsigned long block,
229				int count, const void *buf)
230{
231	struct unix_private_data *data;
232	size_t		size;
233	ext2_loff_t	location;
234	int		actual = 0;
235	errcode_t	retval;
236
237	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
238	data = (struct unix_private_data *) channel->private_data;
239	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
240
241	if (count == 1)
242		size = channel->block_size;
243	else {
244		data->buf_block_nr = -1; 	/* Invalidate the cache */
245		if (count < 0)
246			size = -count;
247		else
248			size = count * channel->block_size;
249	}
250
251	location = (ext2_loff_t) block * channel->block_size;
252	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
253		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
254		goto error_out;
255	}
256
257	actual = write(data->dev, buf, size);
258	if (actual != size) {
259		retval = EXT2_ET_SHORT_WRITE;
260		goto error_out;
261	}
262
263	if ((count == 1) && (block == data->buf_block_nr))
264		memcpy(data->buf, buf, size); /* Update the cache */
265
266	return 0;
267
268error_out:
269	if (channel->write_error)
270		retval = (channel->write_error)(channel, block, count, buf,
271						size, actual, retval);
272	return retval;
273}
274
275/*
276 * Flush data buffers to disk.
277 */
278static errcode_t unix_flush(io_channel channel)
279{
280	struct unix_private_data *data;
281
282	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
283	data = (struct unix_private_data *) channel->private_data;
284	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
285
286	fsync(data->dev);
287	return 0;
288}
289
290