blob: c46602e83af7f04f18b8de0ede5969bceaf556e4 [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'oc4e749a1998-02-20 05:33:14 +000019#if HAVE_ERRNO_H
20#include <errno.h>
21#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000022#include <fcntl.h>
23#include <time.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000024#if HAVE_SYS_STAT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000025#include <sys/stat.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000026#endif
27#if HAVE_SYS_TYPES_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000028#include <sys/types.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000029#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000030
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000031#if EXT2_FLAT_INCLUDES
32#include "ext2_fs.h"
33#else
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000034#include <linux/ext2_fs.h>
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000035#endif
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000036
37#include "ext2fs.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000038
Theodore Ts'of3db3561997-04-26 13:34:30 +000039/*
40 * For checking structure magic numbers...
41 */
42
43#define EXT2_CHECK_MAGIC(struct, code) \
44 if ((struct)->magic != (code)) return (code)
45
Theodore Ts'o3839e651997-04-26 13:21:57 +000046struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000047 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000048 int dev;
49 int flags;
50 char *buf;
51 int buf_block_nr;
52};
53
54static errcode_t unix_open(const char *name, int flags, io_channel *channel);
55static errcode_t unix_close(io_channel channel);
56static errcode_t unix_set_blksize(io_channel channel, int blksize);
57static errcode_t unix_read_blk(io_channel channel, unsigned long block,
58 int count, void *data);
59static errcode_t unix_write_blk(io_channel channel, unsigned long block,
60 int count, const void *data);
61static errcode_t unix_flush(io_channel channel);
62
Theodore Ts'of3db3561997-04-26 13:34:30 +000063static struct struct_io_manager struct_unix_manager = {
64 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000065 "Unix I/O Manager",
66 unix_open,
67 unix_close,
68 unix_set_blksize,
69 unix_read_blk,
70 unix_write_blk,
71 unix_flush
72};
73
74io_manager unix_io_manager = &struct_unix_manager;
75
76static errcode_t unix_open(const char *name, int flags, io_channel *channel)
77{
78 io_channel io = NULL;
79 struct unix_private_data *data = NULL;
80 errcode_t retval;
81
Theodore Ts'o50e1e101997-04-26 13:58:21 +000082 if (name == 0)
83 return EXT2_ET_BAD_DEVICE_NAME;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000084 retval = ext2fs_get_mem(sizeof(struct struct_io_channel),
85 (void **) &io);
86 if (retval)
87 return retval;
Theodore Ts'of3db3561997-04-26 13:34:30 +000088 memset(io, 0, sizeof(struct struct_io_channel));
89 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000090 retval = ext2fs_get_mem(sizeof(struct unix_private_data),
91 (void **) &data);
92 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +000093 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000094
Theodore Ts'o3839e651997-04-26 13:21:57 +000095 io->manager = unix_io_manager;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000096 retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name);
97 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +000098 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000099
Theodore Ts'o3839e651997-04-26 13:21:57 +0000100 strcpy(io->name, name);
101 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000102 io->block_size = 1024;
103 io->read_error = 0;
104 io->write_error = 0;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000105 io->refcount = 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000106
107 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000108 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000109 retval = ext2fs_get_mem(io->block_size, (void **) &data->buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000110 data->buf_block_nr = -1;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000111 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000112 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000113
Theodore Ts'o3839e651997-04-26 13:21:57 +0000114 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
115 if (data->dev < 0) {
116 retval = errno;
117 goto cleanup;
118 }
119 *channel = io;
120 return 0;
121
122cleanup:
123 if (io)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000124 ext2fs_free_mem((void **) &io);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000125 if (data) {
126 if (data->buf)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000127 ext2fs_free_mem((void **) &data->buf);
128 ext2fs_free_mem((void **) &data);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000129 }
130 return retval;
131}
132
133static errcode_t unix_close(io_channel channel)
134{
135 struct unix_private_data *data;
136 errcode_t retval = 0;
137
Theodore Ts'of3db3561997-04-26 13:34:30 +0000138 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000139 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000140 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000141
142 if (--channel->refcount > 0)
143 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000144
Theodore Ts'o3839e651997-04-26 13:21:57 +0000145 if (close(data->dev) < 0)
146 retval = errno;
147 if (data->buf)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000148 ext2fs_free_mem((void **) &data->buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000149 if (channel->private_data)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000150 ext2fs_free_mem((void **) &channel->private_data);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000151 if (channel->name)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000152 ext2fs_free_mem((void **) &channel->name);
153 ext2fs_free_mem((void **) &channel);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000154 return retval;
155}
156
157static errcode_t unix_set_blksize(io_channel channel, int blksize)
158{
159 struct unix_private_data *data;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000160 errcode_t retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000161
Theodore Ts'of3db3561997-04-26 13:34:30 +0000162 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000163 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000164 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
165
Theodore Ts'o3839e651997-04-26 13:21:57 +0000166 if (channel->block_size != blksize) {
167 channel->block_size = blksize;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000168 ext2fs_free_mem((void **) &data->buf);
169 retval = ext2fs_get_mem(blksize, (void **) &data->buf);
170 if (retval)
171 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000172 data->buf_block_nr = -1;
173 }
174 return 0;
175}
176
177
178static errcode_t unix_read_blk(io_channel channel, unsigned long block,
179 int count, void *buf)
180{
181 struct unix_private_data *data;
182 errcode_t retval;
183 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000184 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000185 int actual = 0;
186
Theodore Ts'of3db3561997-04-26 13:34:30 +0000187 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000188 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000189 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000190
191 /*
192 * If it's in the cache, use it!
193 */
194 if ((count == 1) && (block == data->buf_block_nr)) {
195 memcpy(buf, data->buf, channel->block_size);
196 return 0;
197 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000198#if 0
199 printf("read_block %lu (%d)\n", block, count);
200#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000201 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000202 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000203 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o5be8dc21997-12-01 18:24:10 +0000204 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000205 goto error_out;
206 }
207 actual = read(data->dev, buf, size);
208 if (actual != size) {
209 if (actual < 0)
210 actual = 0;
211 retval = EXT2_ET_SHORT_READ;
212 goto error_out;
213 }
214 if (count == 1) {
215 data->buf_block_nr = block;
216 memcpy(data->buf, buf, size); /* Update the cache */
217 }
218 return 0;
219
220error_out:
221 memset((char *) buf+actual, 0, size-actual);
222 if (channel->read_error)
223 retval = (channel->read_error)(channel, block, count, buf,
224 size, actual, retval);
225 return retval;
226}
227
228static errcode_t unix_write_blk(io_channel channel, unsigned long block,
229 int count, const void *buf)
230{
231 struct unix_private_data *data;
232 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000233 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000234 int actual = 0;
235 errcode_t retval;
236
Theodore Ts'of3db3561997-04-26 13:34:30 +0000237 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000238 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000239 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000240
241 if (count == 1)
242 size = channel->block_size;
243 else {
244 data->buf_block_nr = -1; /* Invalidate the cache */
245 if (count < 0)
246 size = -count;
247 else
248 size = count * channel->block_size;
249 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000250
251 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000252 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o5be8dc21997-12-01 18:24:10 +0000253 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000254 goto error_out;
255 }
256
257 actual = write(data->dev, buf, size);
258 if (actual != size) {
259 retval = EXT2_ET_SHORT_WRITE;
260 goto error_out;
261 }
262
263 if ((count == 1) && (block == data->buf_block_nr))
264 memcpy(data->buf, buf, size); /* Update the cache */
265
266 return 0;
267
268error_out:
269 if (channel->write_error)
270 retval = (channel->write_error)(channel, block, count, buf,
271 size, actual, retval);
272 return retval;
273}
274
275/*
Theodore Ts'o36f21431997-06-14 07:25:40 +0000276 * Flush data buffers to disk.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000277 */
278static errcode_t unix_flush(io_channel channel)
279{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000280 struct unix_private_data *data;
281
282 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
283 data = (struct unix_private_data *) channel->private_data;
284 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
285
Theodore Ts'o36f21431997-06-14 07:25:40 +0000286 fsync(data->dev);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000287 return 0;
288}
289