blob: 0baae381ae1f8e74905ae9444e01afb120f5209c [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>
22#include <sys/stat.h>
23#include <sys/types.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000024#if HAVE_ERRNO_H
25#include <errno.h>
26#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000027
28#include "et/com_err.h"
Theodore Ts'o50e1e101997-04-26 13:58:21 +000029#include "ext2fs/ext2_err.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000030#include "io.h"
31
Theodore Ts'of3db3561997-04-26 13:34:30 +000032/*
33 * For checking structure magic numbers...
34 */
35
36#define EXT2_CHECK_MAGIC(struct, code) \
37 if ((struct)->magic != (code)) return (code)
38
Theodore Ts'o3839e651997-04-26 13:21:57 +000039struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000040 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000041 int dev;
42 int flags;
43 char *buf;
44 int buf_block_nr;
45};
46
47static errcode_t unix_open(const char *name, int flags, io_channel *channel);
48static errcode_t unix_close(io_channel channel);
49static errcode_t unix_set_blksize(io_channel channel, int blksize);
50static errcode_t unix_read_blk(io_channel channel, unsigned long block,
51 int count, void *data);
52static errcode_t unix_write_blk(io_channel channel, unsigned long block,
53 int count, const void *data);
54static errcode_t unix_flush(io_channel channel);
55
Theodore Ts'of3db3561997-04-26 13:34:30 +000056static struct struct_io_manager struct_unix_manager = {
57 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000058 "Unix I/O Manager",
59 unix_open,
60 unix_close,
61 unix_set_blksize,
62 unix_read_blk,
63 unix_write_blk,
64 unix_flush
65};
66
67io_manager unix_io_manager = &struct_unix_manager;
68
69static errcode_t unix_open(const char *name, int flags, io_channel *channel)
70{
71 io_channel io = NULL;
72 struct unix_private_data *data = NULL;
73 errcode_t retval;
74
Theodore Ts'o50e1e101997-04-26 13:58:21 +000075 if (name == 0)
76 return EXT2_ET_BAD_DEVICE_NAME;
Theodore Ts'o3839e651997-04-26 13:21:57 +000077 io = (io_channel) malloc(sizeof(struct struct_io_channel));
78 if (!io)
79 return ENOMEM;
Theodore Ts'of3db3561997-04-26 13:34:30 +000080 memset(io, 0, sizeof(struct struct_io_channel));
81 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000082 data = (struct unix_private_data *)
83 malloc(sizeof(struct unix_private_data));
84 if (!data) {
85 retval = ENOMEM;
86 goto cleanup;
87 }
88 io->manager = unix_io_manager;
89 io->name = malloc(strlen(name)+1);
90 if (!io->name) {
91 retval = ENOMEM;
92 goto cleanup;
93 }
94 strcpy(io->name, name);
95 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +000096 io->block_size = 1024;
97 io->read_error = 0;
98 io->write_error = 0;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +000099 io->refcount = 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000100
101 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000102 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000103 data->buf = malloc(io->block_size);
104 data->buf_block_nr = -1;
105 if (!data->buf) {
106 retval = ENOMEM;
107 goto cleanup;
108 }
109 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
110 if (data->dev < 0) {
111 retval = errno;
112 goto cleanup;
113 }
114 *channel = io;
115 return 0;
116
117cleanup:
118 if (io)
119 free(io);
120 if (data) {
121 if (data->buf)
122 free(data->buf);
123 free(data);
124 }
125 return retval;
126}
127
128static errcode_t unix_close(io_channel channel)
129{
130 struct unix_private_data *data;
131 errcode_t retval = 0;
132
Theodore Ts'of3db3561997-04-26 13:34:30 +0000133 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000134 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000135 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000136
137 if (--channel->refcount > 0)
138 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000139
Theodore Ts'o3839e651997-04-26 13:21:57 +0000140 if (close(data->dev) < 0)
141 retval = errno;
142 if (data->buf)
143 free(data->buf);
144 if (channel->private_data)
145 free(channel->private_data);
146 if (channel->name)
147 free(channel->name);
148 free(channel);
149 return retval;
150}
151
152static errcode_t unix_set_blksize(io_channel channel, int blksize)
153{
154 struct unix_private_data *data;
155
Theodore Ts'of3db3561997-04-26 13:34:30 +0000156 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000157 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000158 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
159
Theodore Ts'o3839e651997-04-26 13:21:57 +0000160 if (channel->block_size != blksize) {
161 channel->block_size = blksize;
162 free(data->buf);
163 data->buf = malloc(blksize);
164 if (!data->buf)
165 return ENOMEM;
166 data->buf_block_nr = -1;
167 }
168 return 0;
169}
170
171
172static errcode_t unix_read_blk(io_channel channel, unsigned long block,
173 int count, void *buf)
174{
175 struct unix_private_data *data;
176 errcode_t retval;
177 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000178 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000179 int actual = 0;
180
Theodore Ts'of3db3561997-04-26 13:34:30 +0000181 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000182 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000183 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000184
185 /*
186 * If it's in the cache, use it!
187 */
188 if ((count == 1) && (block == data->buf_block_nr)) {
189 memcpy(buf, data->buf, channel->block_size);
190 return 0;
191 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000192#if 0
193 printf("read_block %lu (%d)\n", block, count);
194#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000195 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000196 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000197 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'oa1230b11997-07-02 02:41:59 +0000198 retval = errno ? errno : EXT2_IO_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000199 goto error_out;
200 }
201 actual = read(data->dev, buf, size);
202 if (actual != size) {
203 if (actual < 0)
204 actual = 0;
205 retval = EXT2_ET_SHORT_READ;
206 goto error_out;
207 }
208 if (count == 1) {
209 data->buf_block_nr = block;
210 memcpy(data->buf, buf, size); /* Update the cache */
211 }
212 return 0;
213
214error_out:
215 memset((char *) buf+actual, 0, size-actual);
216 if (channel->read_error)
217 retval = (channel->read_error)(channel, block, count, buf,
218 size, actual, retval);
219 return retval;
220}
221
222static errcode_t unix_write_blk(io_channel channel, unsigned long block,
223 int count, const void *buf)
224{
225 struct unix_private_data *data;
226 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000227 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000228 int actual = 0;
229 errcode_t retval;
230
Theodore Ts'of3db3561997-04-26 13:34:30 +0000231 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000232 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000233 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000234
235 if (count == 1)
236 size = channel->block_size;
237 else {
238 data->buf_block_nr = -1; /* Invalidate the cache */
239 if (count < 0)
240 size = -count;
241 else
242 size = count * channel->block_size;
243 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000244
245 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000246 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'oa1230b11997-07-02 02:41:59 +0000247 retval = errno ? errno : EXT2_IO_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000248 goto error_out;
249 }
250
251 actual = write(data->dev, buf, size);
252 if (actual != size) {
253 retval = EXT2_ET_SHORT_WRITE;
254 goto error_out;
255 }
256
257 if ((count == 1) && (block == data->buf_block_nr))
258 memcpy(data->buf, buf, size); /* Update the cache */
259
260 return 0;
261
262error_out:
263 if (channel->write_error)
264 retval = (channel->write_error)(channel, block, count, buf,
265 size, actual, retval);
266 return retval;
267}
268
269/*
Theodore Ts'o36f21431997-06-14 07:25:40 +0000270 * Flush data buffers to disk.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000271 */
272static errcode_t unix_flush(io_channel channel)
273{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000274 struct unix_private_data *data;
275
276 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
277 data = (struct unix_private_data *) channel->private_data;
278 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
279
Theodore Ts'o36f21431997-06-14 07:25:40 +0000280 fsync(data->dev);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000281 return 0;
282}
283