blob: b99551504a44f309afe0bb38266a3183726c96db [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>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000018#if HAVE_ERRNO_H
19#include <errno.h>
20#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000021
22#include "et/com_err.h"
Theodore Ts'o50e1e101997-04-26 13:58:21 +000023#include "ext2fs/ext2_err.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000024#include "io.h"
25
Theodore Ts'of3db3561997-04-26 13:34:30 +000026/*
27 * For checking structure magic numbers...
28 */
29
30#define EXT2_CHECK_MAGIC(struct, code) \
31 if ((struct)->magic != (code)) return (code)
32
Theodore Ts'o3839e651997-04-26 13:21:57 +000033struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000034 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000035 int dev;
36 int flags;
37 char *buf;
38 int buf_block_nr;
39};
40
41static errcode_t unix_open(const char *name, int flags, io_channel *channel);
42static errcode_t unix_close(io_channel channel);
43static errcode_t unix_set_blksize(io_channel channel, int blksize);
44static errcode_t unix_read_blk(io_channel channel, unsigned long block,
45 int count, void *data);
46static errcode_t unix_write_blk(io_channel channel, unsigned long block,
47 int count, const void *data);
48static errcode_t unix_flush(io_channel channel);
49
Theodore Ts'of3db3561997-04-26 13:34:30 +000050static struct struct_io_manager struct_unix_manager = {
51 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000052 "Unix I/O Manager",
53 unix_open,
54 unix_close,
55 unix_set_blksize,
56 unix_read_blk,
57 unix_write_blk,
58 unix_flush
59};
60
61io_manager unix_io_manager = &struct_unix_manager;
62
63static errcode_t unix_open(const char *name, int flags, io_channel *channel)
64{
65 io_channel io = NULL;
66 struct unix_private_data *data = NULL;
67 errcode_t retval;
68
Theodore Ts'o50e1e101997-04-26 13:58:21 +000069 if (name == 0)
70 return EXT2_ET_BAD_DEVICE_NAME;
Theodore Ts'o3839e651997-04-26 13:21:57 +000071 io = (io_channel) malloc(sizeof(struct struct_io_channel));
72 if (!io)
73 return ENOMEM;
Theodore Ts'of3db3561997-04-26 13:34:30 +000074 memset(io, 0, sizeof(struct struct_io_channel));
75 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000076 data = (struct unix_private_data *)
77 malloc(sizeof(struct unix_private_data));
78 if (!data) {
79 retval = ENOMEM;
80 goto cleanup;
81 }
82 io->manager = unix_io_manager;
83 io->name = malloc(strlen(name)+1);
84 if (!io->name) {
85 retval = ENOMEM;
86 goto cleanup;
87 }
88 strcpy(io->name, name);
89 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +000090 io->block_size = 1024;
91 io->read_error = 0;
92 io->write_error = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000093
94 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +000095 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000096 data->buf = malloc(io->block_size);
97 data->buf_block_nr = -1;
98 if (!data->buf) {
99 retval = ENOMEM;
100 goto cleanup;
101 }
102 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
103 if (data->dev < 0) {
104 retval = errno;
105 goto cleanup;
106 }
107 *channel = io;
108 return 0;
109
110cleanup:
111 if (io)
112 free(io);
113 if (data) {
114 if (data->buf)
115 free(data->buf);
116 free(data);
117 }
118 return retval;
119}
120
121static errcode_t unix_close(io_channel channel)
122{
123 struct unix_private_data *data;
124 errcode_t retval = 0;
125
Theodore Ts'of3db3561997-04-26 13:34:30 +0000126 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000127 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000128 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
129
Theodore Ts'o3839e651997-04-26 13:21:57 +0000130 if (close(data->dev) < 0)
131 retval = errno;
132 if (data->buf)
133 free(data->buf);
134 if (channel->private_data)
135 free(channel->private_data);
136 if (channel->name)
137 free(channel->name);
138 free(channel);
139 return retval;
140}
141
142static errcode_t unix_set_blksize(io_channel channel, int blksize)
143{
144 struct unix_private_data *data;
145
Theodore Ts'of3db3561997-04-26 13:34:30 +0000146 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000147 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000148 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
149
Theodore Ts'o3839e651997-04-26 13:21:57 +0000150 if (channel->block_size != blksize) {
151 channel->block_size = blksize;
152 free(data->buf);
153 data->buf = malloc(blksize);
154 if (!data->buf)
155 return ENOMEM;
156 data->buf_block_nr = -1;
157 }
158 return 0;
159}
160
161
162static errcode_t unix_read_blk(io_channel channel, unsigned long block,
163 int count, void *buf)
164{
165 struct unix_private_data *data;
166 errcode_t retval;
167 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000168 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000169 int actual = 0;
170
Theodore Ts'of3db3561997-04-26 13:34:30 +0000171 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000172 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000173 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000174
175 /*
176 * If it's in the cache, use it!
177 */
178 if ((count == 1) && (block == data->buf_block_nr)) {
179 memcpy(buf, data->buf, channel->block_size);
180 return 0;
181 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000182#if 0
183 printf("read_block %lu (%d)\n", block, count);
184#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000185 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000186 location = (ext2_loff_t) block * channel->block_size;
187 if (ext2_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000188 retval = errno;
189 goto error_out;
190 }
191 actual = read(data->dev, buf, size);
192 if (actual != size) {
193 if (actual < 0)
194 actual = 0;
195 retval = EXT2_ET_SHORT_READ;
196 goto error_out;
197 }
198 if (count == 1) {
199 data->buf_block_nr = block;
200 memcpy(data->buf, buf, size); /* Update the cache */
201 }
202 return 0;
203
204error_out:
205 memset((char *) buf+actual, 0, size-actual);
206 if (channel->read_error)
207 retval = (channel->read_error)(channel, block, count, buf,
208 size, actual, retval);
209 return retval;
210}
211
212static errcode_t unix_write_blk(io_channel channel, unsigned long block,
213 int count, const void *buf)
214{
215 struct unix_private_data *data;
216 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000217 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000218 int actual = 0;
219 errcode_t retval;
220
Theodore Ts'of3db3561997-04-26 13:34:30 +0000221 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000222 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000223 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000224
225 if (count == 1)
226 size = channel->block_size;
227 else {
228 data->buf_block_nr = -1; /* Invalidate the cache */
229 if (count < 0)
230 size = -count;
231 else
232 size = count * channel->block_size;
233 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000234
235 location = (ext2_loff_t) block * channel->block_size;
236 if (ext2_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000237 retval = errno;
238 goto error_out;
239 }
240
241 actual = write(data->dev, buf, size);
242 if (actual != size) {
243 retval = EXT2_ET_SHORT_WRITE;
244 goto error_out;
245 }
246
247 if ((count == 1) && (block == data->buf_block_nr))
248 memcpy(data->buf, buf, size); /* Update the cache */
249
250 return 0;
251
252error_out:
253 if (channel->write_error)
254 retval = (channel->write_error)(channel, block, count, buf,
255 size, actual, retval);
256 return retval;
257}
258
259/*
260 * Flush data buffers to disk. Since we are currently using a
261 * write-through cache, this is a no-op.
262 */
263static errcode_t unix_flush(io_channel channel)
264{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000265 struct unix_private_data *data;
266
267 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
268 data = (struct unix_private_data *) channel->private_data;
269 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
270
Theodore Ts'o3839e651997-04-26 13:21:57 +0000271 return 0;
272}
273