blob: f1d37b52ca0d5da69675ffb429ec433d804a81fa [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'o3839e651997-04-26 13:21:57 +000028
29#include "et/com_err.h"
Theodore Ts'o50e1e101997-04-26 13:58:21 +000030#include "ext2fs/ext2_err.h"
Theodore Ts'od40259f1997-10-20 00:44:26 +000031#include "ext2fs/ext2_io.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000032
Theodore Ts'of3db3561997-04-26 13:34:30 +000033/*
34 * For checking structure magic numbers...
35 */
36
37#define EXT2_CHECK_MAGIC(struct, code) \
38 if ((struct)->magic != (code)) return (code)
39
Theodore Ts'o3839e651997-04-26 13:21:57 +000040struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000041 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000042 int dev;
43 int flags;
44 char *buf;
45 int buf_block_nr;
46};
47
48static errcode_t unix_open(const char *name, int flags, io_channel *channel);
49static errcode_t unix_close(io_channel channel);
50static errcode_t unix_set_blksize(io_channel channel, int blksize);
51static errcode_t unix_read_blk(io_channel channel, unsigned long block,
52 int count, void *data);
53static errcode_t unix_write_blk(io_channel channel, unsigned long block,
54 int count, const void *data);
55static errcode_t unix_flush(io_channel channel);
56
Theodore Ts'of3db3561997-04-26 13:34:30 +000057static struct struct_io_manager struct_unix_manager = {
58 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000059 "Unix I/O Manager",
60 unix_open,
61 unix_close,
62 unix_set_blksize,
63 unix_read_blk,
64 unix_write_blk,
65 unix_flush
66};
67
68io_manager unix_io_manager = &struct_unix_manager;
69
70static errcode_t unix_open(const char *name, int flags, io_channel *channel)
71{
72 io_channel io = NULL;
73 struct unix_private_data *data = NULL;
74 errcode_t retval;
75
Theodore Ts'o50e1e101997-04-26 13:58:21 +000076 if (name == 0)
77 return EXT2_ET_BAD_DEVICE_NAME;
Theodore Ts'o3839e651997-04-26 13:21:57 +000078 io = (io_channel) malloc(sizeof(struct struct_io_channel));
79 if (!io)
Theodore Ts'oc555aeb1997-10-25 04:16:53 +000080 return EXT2_NO_MEMORY;
Theodore Ts'of3db3561997-04-26 13:34:30 +000081 memset(io, 0, sizeof(struct struct_io_channel));
82 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000083 data = (struct unix_private_data *)
84 malloc(sizeof(struct unix_private_data));
85 if (!data) {
Theodore Ts'oc555aeb1997-10-25 04:16:53 +000086 retval = EXT2_NO_MEMORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +000087 goto cleanup;
88 }
89 io->manager = unix_io_manager;
90 io->name = malloc(strlen(name)+1);
91 if (!io->name) {
Theodore Ts'oc555aeb1997-10-25 04:16:53 +000092 retval = EXT2_NO_MEMORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +000093 goto cleanup;
94 }
95 strcpy(io->name, name);
96 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +000097 io->block_size = 1024;
98 io->read_error = 0;
99 io->write_error = 0;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000100 io->refcount = 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000101
102 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000103 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000104 data->buf = malloc(io->block_size);
105 data->buf_block_nr = -1;
106 if (!data->buf) {
Theodore Ts'oc555aeb1997-10-25 04:16:53 +0000107 retval = EXT2_NO_MEMORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000108 goto cleanup;
109 }
110 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
111 if (data->dev < 0) {
112 retval = errno;
113 goto cleanup;
114 }
115 *channel = io;
116 return 0;
117
118cleanup:
119 if (io)
120 free(io);
121 if (data) {
122 if (data->buf)
123 free(data->buf);
124 free(data);
125 }
126 return retval;
127}
128
129static errcode_t unix_close(io_channel channel)
130{
131 struct unix_private_data *data;
132 errcode_t retval = 0;
133
Theodore Ts'of3db3561997-04-26 13:34:30 +0000134 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000135 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000136 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000137
138 if (--channel->refcount > 0)
139 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000140
Theodore Ts'o3839e651997-04-26 13:21:57 +0000141 if (close(data->dev) < 0)
142 retval = errno;
143 if (data->buf)
144 free(data->buf);
145 if (channel->private_data)
146 free(channel->private_data);
147 if (channel->name)
148 free(channel->name);
149 free(channel);
150 return retval;
151}
152
153static errcode_t unix_set_blksize(io_channel channel, int blksize)
154{
155 struct unix_private_data *data;
156
Theodore Ts'of3db3561997-04-26 13:34:30 +0000157 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000158 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000159 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
160
Theodore Ts'o3839e651997-04-26 13:21:57 +0000161 if (channel->block_size != blksize) {
162 channel->block_size = blksize;
163 free(data->buf);
164 data->buf = malloc(blksize);
165 if (!data->buf)
Theodore Ts'oc555aeb1997-10-25 04:16:53 +0000166 return EXT2_NO_MEMORY;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000167 data->buf_block_nr = -1;
168 }
169 return 0;
170}
171
172
173static errcode_t unix_read_blk(io_channel channel, unsigned long block,
174 int count, void *buf)
175{
176 struct unix_private_data *data;
177 errcode_t retval;
178 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000179 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000180 int actual = 0;
181
Theodore Ts'of3db3561997-04-26 13:34:30 +0000182 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000183 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000184 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000185
186 /*
187 * If it's in the cache, use it!
188 */
189 if ((count == 1) && (block == data->buf_block_nr)) {
190 memcpy(buf, data->buf, channel->block_size);
191 return 0;
192 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000193#if 0
194 printf("read_block %lu (%d)\n", block, count);
195#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000196 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000197 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000198 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'oa1230b11997-07-02 02:41:59 +0000199 retval = errno ? errno : EXT2_IO_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000200 goto error_out;
201 }
202 actual = read(data->dev, buf, size);
203 if (actual != size) {
204 if (actual < 0)
205 actual = 0;
206 retval = EXT2_ET_SHORT_READ;
207 goto error_out;
208 }
209 if (count == 1) {
210 data->buf_block_nr = block;
211 memcpy(data->buf, buf, size); /* Update the cache */
212 }
213 return 0;
214
215error_out:
216 memset((char *) buf+actual, 0, size-actual);
217 if (channel->read_error)
218 retval = (channel->read_error)(channel, block, count, buf,
219 size, actual, retval);
220 return retval;
221}
222
223static errcode_t unix_write_blk(io_channel channel, unsigned long block,
224 int count, const void *buf)
225{
226 struct unix_private_data *data;
227 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000228 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000229 int actual = 0;
230 errcode_t retval;
231
Theodore Ts'of3db3561997-04-26 13:34:30 +0000232 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000233 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000234 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000235
236 if (count == 1)
237 size = channel->block_size;
238 else {
239 data->buf_block_nr = -1; /* Invalidate the cache */
240 if (count < 0)
241 size = -count;
242 else
243 size = count * channel->block_size;
244 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000245
246 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000247 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'oa1230b11997-07-02 02:41:59 +0000248 retval = errno ? errno : EXT2_IO_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000249 goto error_out;
250 }
251
252 actual = write(data->dev, buf, size);
253 if (actual != size) {
254 retval = EXT2_ET_SHORT_WRITE;
255 goto error_out;
256 }
257
258 if ((count == 1) && (block == data->buf_block_nr))
259 memcpy(data->buf, buf, size); /* Update the cache */
260
261 return 0;
262
263error_out:
264 if (channel->write_error)
265 retval = (channel->write_error)(channel, block, count, buf,
266 size, actual, retval);
267 return retval;
268}
269
270/*
Theodore Ts'o36f21431997-06-14 07:25:40 +0000271 * Flush data buffers to disk.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000272 */
273static errcode_t unix_flush(io_channel channel)
274{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000275 struct unix_private_data *data;
276
277 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
278 data = (struct unix_private_data *) channel->private_data;
279 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
280
Theodore Ts'o36f21431997-06-14 07:25:40 +0000281 fsync(data->dev);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000282 return 0;
283}
284