blob: b17ed7fe22026e17149d88cf22cd573c2a553b7f [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>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000016#if HAVE_UNISTD_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000017#include <unistd.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000018#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000019#include <stdlib.h>
20#include <fcntl.h>
21#include <time.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000022#if HAVE_SYS_STAT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000023#include <sys/stat.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000024#endif
25#if HAVE_SYS_TYPES_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000026#include <sys/types.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000027#endif
Theodore Ts'o50e1e101997-04-26 13:58:21 +000028#if HAVE_ERRNO_H
29#include <errno.h>
30#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000031
32#include "et/com_err.h"
Theodore Ts'o50e1e101997-04-26 13:58:21 +000033#include "ext2fs/ext2_err.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000034#include "io.h"
35
Theodore Ts'of3db3561997-04-26 13:34:30 +000036/*
37 * For checking structure magic numbers...
38 */
39
40#define EXT2_CHECK_MAGIC(struct, code) \
41 if ((struct)->magic != (code)) return (code)
42
Theodore Ts'o3839e651997-04-26 13:21:57 +000043struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000044 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000045 int dev;
46 int flags;
47 char *buf;
48 int buf_block_nr;
49};
50
51static errcode_t unix_open(const char *name, int flags, io_channel *channel);
52static errcode_t unix_close(io_channel channel);
53static errcode_t unix_set_blksize(io_channel channel, int blksize);
54static errcode_t unix_read_blk(io_channel channel, unsigned long block,
55 int count, void *data);
56static errcode_t unix_write_blk(io_channel channel, unsigned long block,
57 int count, const void *data);
58static errcode_t unix_flush(io_channel channel);
59
Theodore Ts'of3db3561997-04-26 13:34:30 +000060static struct struct_io_manager struct_unix_manager = {
61 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000062 "Unix I/O Manager",
63 unix_open,
64 unix_close,
65 unix_set_blksize,
66 unix_read_blk,
67 unix_write_blk,
68 unix_flush
69};
70
71io_manager unix_io_manager = &struct_unix_manager;
72
73static errcode_t unix_open(const char *name, int flags, io_channel *channel)
74{
75 io_channel io = NULL;
76 struct unix_private_data *data = NULL;
77 errcode_t retval;
78
Theodore Ts'o50e1e101997-04-26 13:58:21 +000079 if (name == 0)
80 return EXT2_ET_BAD_DEVICE_NAME;
Theodore Ts'o3839e651997-04-26 13:21:57 +000081 io = (io_channel) malloc(sizeof(struct struct_io_channel));
82 if (!io)
83 return ENOMEM;
Theodore Ts'of3db3561997-04-26 13:34:30 +000084 memset(io, 0, sizeof(struct struct_io_channel));
85 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000086 data = (struct unix_private_data *)
87 malloc(sizeof(struct unix_private_data));
88 if (!data) {
89 retval = ENOMEM;
90 goto cleanup;
91 }
92 io->manager = unix_io_manager;
93 io->name = malloc(strlen(name)+1);
94 if (!io->name) {
95 retval = ENOMEM;
96 goto cleanup;
97 }
98 strcpy(io->name, name);
99 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000100 io->block_size = 1024;
101 io->read_error = 0;
102 io->write_error = 0;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000103 io->refcount = 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000104
105 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000106 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000107 data->buf = malloc(io->block_size);
108 data->buf_block_nr = -1;
109 if (!data->buf) {
110 retval = ENOMEM;
111 goto cleanup;
112 }
113 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
114 if (data->dev < 0) {
115 retval = errno;
116 goto cleanup;
117 }
118 *channel = io;
119 return 0;
120
121cleanup:
122 if (io)
123 free(io);
124 if (data) {
125 if (data->buf)
126 free(data->buf);
127 free(data);
128 }
129 return retval;
130}
131
132static errcode_t unix_close(io_channel channel)
133{
134 struct unix_private_data *data;
135 errcode_t retval = 0;
136
Theodore Ts'of3db3561997-04-26 13:34:30 +0000137 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000138 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000139 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000140
141 if (--channel->refcount > 0)
142 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000143
Theodore Ts'o3839e651997-04-26 13:21:57 +0000144 if (close(data->dev) < 0)
145 retval = errno;
146 if (data->buf)
147 free(data->buf);
148 if (channel->private_data)
149 free(channel->private_data);
150 if (channel->name)
151 free(channel->name);
152 free(channel);
153 return retval;
154}
155
156static errcode_t unix_set_blksize(io_channel channel, int blksize)
157{
158 struct unix_private_data *data;
159
Theodore Ts'of3db3561997-04-26 13:34:30 +0000160 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000161 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000162 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
163
Theodore Ts'o3839e651997-04-26 13:21:57 +0000164 if (channel->block_size != blksize) {
165 channel->block_size = blksize;
166 free(data->buf);
167 data->buf = malloc(blksize);
168 if (!data->buf)
169 return ENOMEM;
170 data->buf_block_nr = -1;
171 }
172 return 0;
173}
174
175
176static errcode_t unix_read_blk(io_channel channel, unsigned long block,
177 int count, void *buf)
178{
179 struct unix_private_data *data;
180 errcode_t retval;
181 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000182 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000183 int actual = 0;
184
Theodore Ts'of3db3561997-04-26 13:34:30 +0000185 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000186 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000187 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000188
189 /*
190 * If it's in the cache, use it!
191 */
192 if ((count == 1) && (block == data->buf_block_nr)) {
193 memcpy(buf, data->buf, channel->block_size);
194 return 0;
195 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000196#if 0
197 printf("read_block %lu (%d)\n", block, count);
198#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000199 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000200 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000201 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'oa1230b11997-07-02 02:41:59 +0000202 retval = errno ? errno : EXT2_IO_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000203 goto error_out;
204 }
205 actual = read(data->dev, buf, size);
206 if (actual != size) {
207 if (actual < 0)
208 actual = 0;
209 retval = EXT2_ET_SHORT_READ;
210 goto error_out;
211 }
212 if (count == 1) {
213 data->buf_block_nr = block;
214 memcpy(data->buf, buf, size); /* Update the cache */
215 }
216 return 0;
217
218error_out:
219 memset((char *) buf+actual, 0, size-actual);
220 if (channel->read_error)
221 retval = (channel->read_error)(channel, block, count, buf,
222 size, actual, retval);
223 return retval;
224}
225
226static errcode_t unix_write_blk(io_channel channel, unsigned long block,
227 int count, const void *buf)
228{
229 struct unix_private_data *data;
230 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000231 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000232 int actual = 0;
233 errcode_t retval;
234
Theodore Ts'of3db3561997-04-26 13:34:30 +0000235 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000236 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000237 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000238
239 if (count == 1)
240 size = channel->block_size;
241 else {
242 data->buf_block_nr = -1; /* Invalidate the cache */
243 if (count < 0)
244 size = -count;
245 else
246 size = count * channel->block_size;
247 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000248
249 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000250 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'oa1230b11997-07-02 02:41:59 +0000251 retval = errno ? errno : EXT2_IO_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000252 goto error_out;
253 }
254
255 actual = write(data->dev, buf, size);
256 if (actual != size) {
257 retval = EXT2_ET_SHORT_WRITE;
258 goto error_out;
259 }
260
261 if ((count == 1) && (block == data->buf_block_nr))
262 memcpy(data->buf, buf, size); /* Update the cache */
263
264 return 0;
265
266error_out:
267 if (channel->write_error)
268 retval = (channel->write_error)(channel, block, count, buf,
269 size, actual, retval);
270 return retval;
271}
272
273/*
Theodore Ts'o36f21431997-06-14 07:25:40 +0000274 * Flush data buffers to disk.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000275 */
276static errcode_t unix_flush(io_channel channel)
277{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000278 struct unix_private_data *data;
279
280 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
281 data = (struct unix_private_data *) channel->private_data;
282 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
283
Theodore Ts'o36f21431997-06-14 07:25:40 +0000284 fsync(data->dev);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000285 return 0;
286}
287