blob: 4639d4fd639a5dc179fc6b9d68be6e658ce0335d [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 <fcntl.h>
20#include <time.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000021#if HAVE_SYS_STAT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000022#include <sys/stat.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000023#endif
24#if HAVE_SYS_TYPES_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000025#include <sys/types.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000026#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000027
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000028#if EXT2_FLAT_INCLUDES
29#include "ext2_fs.h"
30#else
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000031#include <linux/ext2_fs.h>
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000032#endif
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000033
34#include "ext2fs.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000035
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'o7b4e4531997-10-26 03:41:24 +000081 retval = ext2fs_get_mem(sizeof(struct struct_io_channel),
82 (void **) &io);
83 if (retval)
84 return retval;
Theodore Ts'of3db3561997-04-26 13:34:30 +000085 memset(io, 0, sizeof(struct struct_io_channel));
86 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000087 retval = ext2fs_get_mem(sizeof(struct unix_private_data),
88 (void **) &data);
89 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +000090 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000091
Theodore Ts'o3839e651997-04-26 13:21:57 +000092 io->manager = unix_io_manager;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000093 retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name);
94 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +000095 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000096
Theodore Ts'o3839e651997-04-26 13:21:57 +000097 strcpy(io->name, name);
98 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +000099 io->block_size = 1024;
100 io->read_error = 0;
101 io->write_error = 0;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000102 io->refcount = 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000103
104 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000105 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000106 retval = ext2fs_get_mem(io->block_size, (void **) &data->buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000107 data->buf_block_nr = -1;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000108 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000109 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000110
Theodore Ts'o3839e651997-04-26 13:21:57 +0000111 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
112 if (data->dev < 0) {
113 retval = errno;
114 goto cleanup;
115 }
116 *channel = io;
117 return 0;
118
119cleanup:
120 if (io)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000121 ext2fs_free_mem((void **) &io);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000122 if (data) {
123 if (data->buf)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000124 ext2fs_free_mem((void **) &data->buf);
125 ext2fs_free_mem((void **) &data);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000126 }
127 return retval;
128}
129
130static errcode_t unix_close(io_channel channel)
131{
132 struct unix_private_data *data;
133 errcode_t retval = 0;
134
Theodore Ts'of3db3561997-04-26 13:34:30 +0000135 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000136 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000137 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000138
139 if (--channel->refcount > 0)
140 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000141
Theodore Ts'o3839e651997-04-26 13:21:57 +0000142 if (close(data->dev) < 0)
143 retval = errno;
144 if (data->buf)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000145 ext2fs_free_mem((void **) &data->buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000146 if (channel->private_data)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000147 ext2fs_free_mem((void **) &channel->private_data);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000148 if (channel->name)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000149 ext2fs_free_mem((void **) &channel->name);
150 ext2fs_free_mem((void **) &channel);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000151 return retval;
152}
153
154static errcode_t unix_set_blksize(io_channel channel, int blksize)
155{
156 struct unix_private_data *data;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000157 errcode_t retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000158
Theodore Ts'of3db3561997-04-26 13:34:30 +0000159 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000160 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000161 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
162
Theodore Ts'o3839e651997-04-26 13:21:57 +0000163 if (channel->block_size != blksize) {
164 channel->block_size = blksize;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000165 ext2fs_free_mem((void **) &data->buf);
166 retval = ext2fs_get_mem(blksize, (void **) &data->buf);
167 if (retval)
168 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000169 data->buf_block_nr = -1;
170 }
171 return 0;
172}
173
174
175static errcode_t unix_read_blk(io_channel channel, unsigned long block,
176 int count, void *buf)
177{
178 struct unix_private_data *data;
179 errcode_t retval;
180 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000181 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000182 int actual = 0;
183
Theodore Ts'of3db3561997-04-26 13:34:30 +0000184 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000185 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000186 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000187
188 /*
189 * If it's in the cache, use it!
190 */
191 if ((count == 1) && (block == data->buf_block_nr)) {
192 memcpy(buf, data->buf, channel->block_size);
193 return 0;
194 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000195#if 0
196 printf("read_block %lu (%d)\n", block, count);
197#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000198 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000199 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000200 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o5be8dc21997-12-01 18:24:10 +0000201 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000202 goto error_out;
203 }
204 actual = read(data->dev, buf, size);
205 if (actual != size) {
206 if (actual < 0)
207 actual = 0;
208 retval = EXT2_ET_SHORT_READ;
209 goto error_out;
210 }
211 if (count == 1) {
212 data->buf_block_nr = block;
213 memcpy(data->buf, buf, size); /* Update the cache */
214 }
215 return 0;
216
217error_out:
218 memset((char *) buf+actual, 0, size-actual);
219 if (channel->read_error)
220 retval = (channel->read_error)(channel, block, count, buf,
221 size, actual, retval);
222 return retval;
223}
224
225static errcode_t unix_write_blk(io_channel channel, unsigned long block,
226 int count, const void *buf)
227{
228 struct unix_private_data *data;
229 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000230 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000231 int actual = 0;
232 errcode_t retval;
233
Theodore Ts'of3db3561997-04-26 13:34:30 +0000234 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000235 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000236 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000237
238 if (count == 1)
239 size = channel->block_size;
240 else {
241 data->buf_block_nr = -1; /* Invalidate the cache */
242 if (count < 0)
243 size = -count;
244 else
245 size = count * channel->block_size;
246 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000247
248 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000249 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o5be8dc21997-12-01 18:24:10 +0000250 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000251 goto error_out;
252 }
253
254 actual = write(data->dev, buf, size);
255 if (actual != size) {
256 retval = EXT2_ET_SHORT_WRITE;
257 goto error_out;
258 }
259
260 if ((count == 1) && (block == data->buf_block_nr))
261 memcpy(data->buf, buf, size); /* Update the cache */
262
263 return 0;
264
265error_out:
266 if (channel->write_error)
267 retval = (channel->write_error)(channel, block, count, buf,
268 size, actual, retval);
269 return retval;
270}
271
272/*
Theodore Ts'o36f21431997-06-14 07:25:40 +0000273 * Flush data buffers to disk.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000274 */
275static errcode_t unix_flush(io_channel channel)
276{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000277 struct unix_private_data *data;
278
279 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
280 data = (struct unix_private_data *) channel->private_data;
281 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
282
Theodore Ts'o36f21431997-06-14 07:25:40 +0000283 fsync(data->dev);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000284 return 0;
285}
286