blob: ef00902d4f42e1e4da451208c5e0ac9c6cd0f47f [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
14#include <stdio.h>
15#include <string.h>
16#include <unistd.h>
17#include <stdlib.h>
18#include <fcntl.h>
19#include <time.h>
20#include <sys/stat.h>
21#include <sys/types.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000022#if HAVE_ERRNO_H
23#include <errno.h>
24#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000025
26#include "et/com_err.h"
Theodore Ts'o50e1e101997-04-26 13:58:21 +000027#include "ext2fs/ext2_err.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000028#include "io.h"
29
Theodore Ts'of3db3561997-04-26 13:34:30 +000030/*
31 * For checking structure magic numbers...
32 */
33
34#define EXT2_CHECK_MAGIC(struct, code) \
35 if ((struct)->magic != (code)) return (code)
36
Theodore Ts'o3839e651997-04-26 13:21:57 +000037struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000038 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000039 int dev;
40 int flags;
41 char *buf;
42 int buf_block_nr;
43};
44
45static errcode_t unix_open(const char *name, int flags, io_channel *channel);
46static errcode_t unix_close(io_channel channel);
47static errcode_t unix_set_blksize(io_channel channel, int blksize);
48static errcode_t unix_read_blk(io_channel channel, unsigned long block,
49 int count, void *data);
50static errcode_t unix_write_blk(io_channel channel, unsigned long block,
51 int count, const void *data);
52static errcode_t unix_flush(io_channel channel);
53
Theodore Ts'of3db3561997-04-26 13:34:30 +000054static struct struct_io_manager struct_unix_manager = {
55 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000056 "Unix I/O Manager",
57 unix_open,
58 unix_close,
59 unix_set_blksize,
60 unix_read_blk,
61 unix_write_blk,
62 unix_flush
63};
64
65io_manager unix_io_manager = &struct_unix_manager;
66
67static errcode_t unix_open(const char *name, int flags, io_channel *channel)
68{
69 io_channel io = NULL;
70 struct unix_private_data *data = NULL;
71 errcode_t retval;
72
Theodore Ts'o50e1e101997-04-26 13:58:21 +000073 if (name == 0)
74 return EXT2_ET_BAD_DEVICE_NAME;
Theodore Ts'o3839e651997-04-26 13:21:57 +000075 io = (io_channel) malloc(sizeof(struct struct_io_channel));
76 if (!io)
77 return ENOMEM;
Theodore Ts'of3db3561997-04-26 13:34:30 +000078 memset(io, 0, sizeof(struct struct_io_channel));
79 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000080 data = (struct unix_private_data *)
81 malloc(sizeof(struct unix_private_data));
82 if (!data) {
83 retval = ENOMEM;
84 goto cleanup;
85 }
86 io->manager = unix_io_manager;
87 io->name = malloc(strlen(name)+1);
88 if (!io->name) {
89 retval = ENOMEM;
90 goto cleanup;
91 }
92 strcpy(io->name, name);
93 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +000094 io->block_size = 1024;
95 io->read_error = 0;
96 io->write_error = 0;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000097 io->refcount = 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +000098
99 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000100 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000101 data->buf = malloc(io->block_size);
102 data->buf_block_nr = -1;
103 if (!data->buf) {
104 retval = ENOMEM;
105 goto cleanup;
106 }
107 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
108 if (data->dev < 0) {
109 retval = errno;
110 goto cleanup;
111 }
112 *channel = io;
113 return 0;
114
115cleanup:
116 if (io)
117 free(io);
118 if (data) {
119 if (data->buf)
120 free(data->buf);
121 free(data);
122 }
123 return retval;
124}
125
126static errcode_t unix_close(io_channel channel)
127{
128 struct unix_private_data *data;
129 errcode_t retval = 0;
130
Theodore Ts'of3db3561997-04-26 13:34:30 +0000131 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000132 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000133 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000134
135 if (--channel->refcount > 0)
136 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000137
Theodore Ts'o3839e651997-04-26 13:21:57 +0000138 if (close(data->dev) < 0)
139 retval = errno;
140 if (data->buf)
141 free(data->buf);
142 if (channel->private_data)
143 free(channel->private_data);
144 if (channel->name)
145 free(channel->name);
146 free(channel);
147 return retval;
148}
149
150static errcode_t unix_set_blksize(io_channel channel, int blksize)
151{
152 struct unix_private_data *data;
153
Theodore Ts'of3db3561997-04-26 13:34:30 +0000154 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000155 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000156 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
157
Theodore Ts'o3839e651997-04-26 13:21:57 +0000158 if (channel->block_size != blksize) {
159 channel->block_size = blksize;
160 free(data->buf);
161 data->buf = malloc(blksize);
162 if (!data->buf)
163 return ENOMEM;
164 data->buf_block_nr = -1;
165 }
166 return 0;
167}
168
169
170static errcode_t unix_read_blk(io_channel channel, unsigned long block,
171 int count, void *buf)
172{
173 struct unix_private_data *data;
174 errcode_t retval;
175 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000176 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000177 int actual = 0;
178
Theodore Ts'of3db3561997-04-26 13:34:30 +0000179 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000180 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000181 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000182
183 /*
184 * If it's in the cache, use it!
185 */
186 if ((count == 1) && (block == data->buf_block_nr)) {
187 memcpy(buf, data->buf, channel->block_size);
188 return 0;
189 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000190#if 0
191 printf("read_block %lu (%d)\n", block, count);
192#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000193 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000194 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000195 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000196 retval = errno;
197 goto error_out;
198 }
199 actual = read(data->dev, buf, size);
200 if (actual != size) {
201 if (actual < 0)
202 actual = 0;
203 retval = EXT2_ET_SHORT_READ;
204 goto error_out;
205 }
206 if (count == 1) {
207 data->buf_block_nr = block;
208 memcpy(data->buf, buf, size); /* Update the cache */
209 }
210 return 0;
211
212error_out:
213 memset((char *) buf+actual, 0, size-actual);
214 if (channel->read_error)
215 retval = (channel->read_error)(channel, block, count, buf,
216 size, actual, retval);
217 return retval;
218}
219
220static errcode_t unix_write_blk(io_channel channel, unsigned long block,
221 int count, const void *buf)
222{
223 struct unix_private_data *data;
224 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000225 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000226 int actual = 0;
227 errcode_t retval;
228
Theodore Ts'of3db3561997-04-26 13:34:30 +0000229 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000230 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000231 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000232
233 if (count == 1)
234 size = channel->block_size;
235 else {
236 data->buf_block_nr = -1; /* Invalidate the cache */
237 if (count < 0)
238 size = -count;
239 else
240 size = count * channel->block_size;
241 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000242
243 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000244 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000245 retval = errno;
246 goto error_out;
247 }
248
249 actual = write(data->dev, buf, size);
250 if (actual != size) {
251 retval = EXT2_ET_SHORT_WRITE;
252 goto error_out;
253 }
254
255 if ((count == 1) && (block == data->buf_block_nr))
256 memcpy(data->buf, buf, size); /* Update the cache */
257
258 return 0;
259
260error_out:
261 if (channel->write_error)
262 retval = (channel->write_error)(channel, block, count, buf,
263 size, actual, retval);
264 return retval;
265}
266
267/*
268 * Flush data buffers to disk. Since we are currently using a
269 * write-through cache, this is a no-op.
270 */
271static errcode_t unix_flush(io_channel channel)
272{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000273 struct unix_private_data *data;
274
275 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
276 data = (struct unix_private_data *) channel->private_data;
277 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
278
Theodore Ts'o3839e651997-04-26 13:21:57 +0000279 return 0;
280}
281