blob: 5126583e7a0a0102f88854be256ffc631e4596a6 [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 *
6 * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
7 * under the terms of the GNU Public License.
8 */
9
10#include <stdio.h>
11#include <string.h>
12#include <unistd.h>
13#include <stdlib.h>
14#include <fcntl.h>
15#include <time.h>
16#include <sys/stat.h>
17#include <sys/types.h>
18
19#include "et/com_err.h"
20#include "ext2_err.h"
21#include "io.h"
22
Theodore Ts'of3db3561997-04-26 13:34:30 +000023/*
24 * For checking structure magic numbers...
25 */
26
27#define EXT2_CHECK_MAGIC(struct, code) \
28 if ((struct)->magic != (code)) return (code)
29
Theodore Ts'o3839e651997-04-26 13:21:57 +000030struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000031 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000032 int dev;
33 int flags;
34 char *buf;
35 int buf_block_nr;
36};
37
38static errcode_t unix_open(const char *name, int flags, io_channel *channel);
39static errcode_t unix_close(io_channel channel);
40static errcode_t unix_set_blksize(io_channel channel, int blksize);
41static errcode_t unix_read_blk(io_channel channel, unsigned long block,
42 int count, void *data);
43static errcode_t unix_write_blk(io_channel channel, unsigned long block,
44 int count, const void *data);
45static errcode_t unix_flush(io_channel channel);
46
Theodore Ts'of3db3561997-04-26 13:34:30 +000047static struct struct_io_manager struct_unix_manager = {
48 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000049 "Unix I/O Manager",
50 unix_open,
51 unix_close,
52 unix_set_blksize,
53 unix_read_blk,
54 unix_write_blk,
55 unix_flush
56};
57
58io_manager unix_io_manager = &struct_unix_manager;
59
60static errcode_t unix_open(const char *name, int flags, io_channel *channel)
61{
62 io_channel io = NULL;
63 struct unix_private_data *data = NULL;
64 errcode_t retval;
65
66 io = (io_channel) malloc(sizeof(struct struct_io_channel));
67 if (!io)
68 return ENOMEM;
Theodore Ts'of3db3561997-04-26 13:34:30 +000069 memset(io, 0, sizeof(struct struct_io_channel));
70 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000071 data = (struct unix_private_data *)
72 malloc(sizeof(struct unix_private_data));
73 if (!data) {
74 retval = ENOMEM;
75 goto cleanup;
76 }
77 io->manager = unix_io_manager;
78 io->name = malloc(strlen(name)+1);
79 if (!io->name) {
80 retval = ENOMEM;
81 goto cleanup;
82 }
83 strcpy(io->name, name);
84 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +000085 io->block_size = 1024;
86 io->read_error = 0;
87 io->write_error = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000088
89 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +000090 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000091 data->buf = malloc(io->block_size);
92 data->buf_block_nr = -1;
93 if (!data->buf) {
94 retval = ENOMEM;
95 goto cleanup;
96 }
97 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
98 if (data->dev < 0) {
99 retval = errno;
100 goto cleanup;
101 }
102 *channel = io;
103 return 0;
104
105cleanup:
106 if (io)
107 free(io);
108 if (data) {
109 if (data->buf)
110 free(data->buf);
111 free(data);
112 }
113 return retval;
114}
115
116static errcode_t unix_close(io_channel channel)
117{
118 struct unix_private_data *data;
119 errcode_t retval = 0;
120
Theodore Ts'of3db3561997-04-26 13:34:30 +0000121 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000122 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000123 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
124
Theodore Ts'o3839e651997-04-26 13:21:57 +0000125 if (close(data->dev) < 0)
126 retval = errno;
127 if (data->buf)
128 free(data->buf);
129 if (channel->private_data)
130 free(channel->private_data);
131 if (channel->name)
132 free(channel->name);
133 free(channel);
134 return retval;
135}
136
137static errcode_t unix_set_blksize(io_channel channel, int blksize)
138{
139 struct unix_private_data *data;
140
Theodore Ts'of3db3561997-04-26 13:34:30 +0000141 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000142 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000143 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
144
Theodore Ts'o3839e651997-04-26 13:21:57 +0000145 if (channel->block_size != blksize) {
146 channel->block_size = blksize;
147 free(data->buf);
148 data->buf = malloc(blksize);
149 if (!data->buf)
150 return ENOMEM;
151 data->buf_block_nr = -1;
152 }
153 return 0;
154}
155
156
157static errcode_t unix_read_blk(io_channel channel, unsigned long block,
158 int count, void *buf)
159{
160 struct unix_private_data *data;
161 errcode_t retval;
162 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000163 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000164 int actual = 0;
165
Theodore Ts'of3db3561997-04-26 13:34:30 +0000166 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000167 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000168 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000169
170 /*
171 * If it's in the cache, use it!
172 */
173 if ((count == 1) && (block == data->buf_block_nr)) {
174 memcpy(buf, data->buf, channel->block_size);
175 return 0;
176 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000177#if 0
178 printf("read_block %lu (%d)\n", block, count);
179#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000180 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000181 location = (ext2_loff_t) block * channel->block_size;
182 if (ext2_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000183 retval = errno;
184 goto error_out;
185 }
186 actual = read(data->dev, buf, size);
187 if (actual != size) {
188 if (actual < 0)
189 actual = 0;
190 retval = EXT2_ET_SHORT_READ;
191 goto error_out;
192 }
193 if (count == 1) {
194 data->buf_block_nr = block;
195 memcpy(data->buf, buf, size); /* Update the cache */
196 }
197 return 0;
198
199error_out:
200 memset((char *) buf+actual, 0, size-actual);
201 if (channel->read_error)
202 retval = (channel->read_error)(channel, block, count, buf,
203 size, actual, retval);
204 return retval;
205}
206
207static errcode_t unix_write_blk(io_channel channel, unsigned long block,
208 int count, const void *buf)
209{
210 struct unix_private_data *data;
211 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000212 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000213 int actual = 0;
214 errcode_t retval;
215
Theodore Ts'of3db3561997-04-26 13:34:30 +0000216 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000217 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000218 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000219
220 if (count == 1)
221 size = channel->block_size;
222 else {
223 data->buf_block_nr = -1; /* Invalidate the cache */
224 if (count < 0)
225 size = -count;
226 else
227 size = count * channel->block_size;
228 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000229
230 location = (ext2_loff_t) block * channel->block_size;
231 if (ext2_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000232 retval = errno;
233 goto error_out;
234 }
235
236 actual = write(data->dev, buf, size);
237 if (actual != size) {
238 retval = EXT2_ET_SHORT_WRITE;
239 goto error_out;
240 }
241
242 if ((count == 1) && (block == data->buf_block_nr))
243 memcpy(data->buf, buf, size); /* Update the cache */
244
245 return 0;
246
247error_out:
248 if (channel->write_error)
249 retval = (channel->write_error)(channel, block, count, buf,
250 size, actual, retval);
251 return retval;
252}
253
254/*
255 * Flush data buffers to disk. Since we are currently using a
256 * write-through cache, this is a no-op.
257 */
258static errcode_t unix_flush(io_channel channel)
259{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000260 struct unix_private_data *data;
261
262 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
263 data = (struct unix_private_data *) channel->private_data;
264 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
265
Theodore Ts'o3839e651997-04-26 13:21:57 +0000266 return 0;
267}
268