blob: 5083771f747c7b10eb015cfa3e6c0e6a11a1527b [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'o3839e651997-04-26 13:21:57 +000097
98 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +000099 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000100 data->buf = malloc(io->block_size);
101 data->buf_block_nr = -1;
102 if (!data->buf) {
103 retval = ENOMEM;
104 goto cleanup;
105 }
106 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
107 if (data->dev < 0) {
108 retval = errno;
109 goto cleanup;
110 }
111 *channel = io;
112 return 0;
113
114cleanup:
115 if (io)
116 free(io);
117 if (data) {
118 if (data->buf)
119 free(data->buf);
120 free(data);
121 }
122 return retval;
123}
124
125static errcode_t unix_close(io_channel channel)
126{
127 struct unix_private_data *data;
128 errcode_t retval = 0;
129
Theodore Ts'of3db3561997-04-26 13:34:30 +0000130 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000131 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000132 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
133
Theodore Ts'o3839e651997-04-26 13:21:57 +0000134 if (close(data->dev) < 0)
135 retval = errno;
136 if (data->buf)
137 free(data->buf);
138 if (channel->private_data)
139 free(channel->private_data);
140 if (channel->name)
141 free(channel->name);
142 free(channel);
143 return retval;
144}
145
146static errcode_t unix_set_blksize(io_channel channel, int blksize)
147{
148 struct unix_private_data *data;
149
Theodore Ts'of3db3561997-04-26 13:34:30 +0000150 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000151 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000152 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
153
Theodore Ts'o3839e651997-04-26 13:21:57 +0000154 if (channel->block_size != blksize) {
155 channel->block_size = blksize;
156 free(data->buf);
157 data->buf = malloc(blksize);
158 if (!data->buf)
159 return ENOMEM;
160 data->buf_block_nr = -1;
161 }
162 return 0;
163}
164
165
166static errcode_t unix_read_blk(io_channel channel, unsigned long block,
167 int count, void *buf)
168{
169 struct unix_private_data *data;
170 errcode_t retval;
171 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000172 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000173 int actual = 0;
174
Theodore Ts'of3db3561997-04-26 13:34:30 +0000175 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000176 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000177 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000178
179 /*
180 * If it's in the cache, use it!
181 */
182 if ((count == 1) && (block == data->buf_block_nr)) {
183 memcpy(buf, data->buf, channel->block_size);
184 return 0;
185 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000186#if 0
187 printf("read_block %lu (%d)\n", block, count);
188#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000189 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000190 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000191 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000192 retval = errno;
193 goto error_out;
194 }
195 actual = read(data->dev, buf, size);
196 if (actual != size) {
197 if (actual < 0)
198 actual = 0;
199 retval = EXT2_ET_SHORT_READ;
200 goto error_out;
201 }
202 if (count == 1) {
203 data->buf_block_nr = block;
204 memcpy(data->buf, buf, size); /* Update the cache */
205 }
206 return 0;
207
208error_out:
209 memset((char *) buf+actual, 0, size-actual);
210 if (channel->read_error)
211 retval = (channel->read_error)(channel, block, count, buf,
212 size, actual, retval);
213 return retval;
214}
215
216static errcode_t unix_write_blk(io_channel channel, unsigned long block,
217 int count, const void *buf)
218{
219 struct unix_private_data *data;
220 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000221 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000222 int actual = 0;
223 errcode_t retval;
224
Theodore Ts'of3db3561997-04-26 13:34:30 +0000225 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000226 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000227 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000228
229 if (count == 1)
230 size = channel->block_size;
231 else {
232 data->buf_block_nr = -1; /* Invalidate the cache */
233 if (count < 0)
234 size = -count;
235 else
236 size = count * channel->block_size;
237 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000238
239 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000240 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000241 retval = errno;
242 goto error_out;
243 }
244
245 actual = write(data->dev, buf, size);
246 if (actual != size) {
247 retval = EXT2_ET_SHORT_WRITE;
248 goto error_out;
249 }
250
251 if ((count == 1) && (block == data->buf_block_nr))
252 memcpy(data->buf, buf, size); /* Update the cache */
253
254 return 0;
255
256error_out:
257 if (channel->write_error)
258 retval = (channel->write_error)(channel, block, count, buf,
259 size, actual, retval);
260 return retval;
261}
262
263/*
264 * Flush data buffers to disk. Since we are currently using a
265 * write-through cache, this is a no-op.
266 */
267static errcode_t unix_flush(io_channel channel)
268{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000269 struct unix_private_data *data;
270
271 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
272 data = (struct unix_private_data *) channel->private_data;
273 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
274
Theodore Ts'o3839e651997-04-26 13:21:57 +0000275 return 0;
276}
277