blob: e0cc4db44106291addbb152f4588fedd5ae48616 [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
Theodore Ts'odc5f68c2000-05-25 23:31:54 +000014#define _LARGEFILE_SOURCE
15#define _LARGEFILE64_SOURCE
16
Theodore Ts'o3839e651997-04-26 13:21:57 +000017#include <stdio.h>
18#include <string.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000019#if HAVE_UNISTD_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000020#include <unistd.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000021#endif
Theodore Ts'oc4e749a1998-02-20 05:33:14 +000022#if HAVE_ERRNO_H
23#include <errno.h>
24#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000025#include <fcntl.h>
26#include <time.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000027#if HAVE_SYS_STAT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000028#include <sys/stat.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000029#endif
30#if HAVE_SYS_TYPES_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000031#include <sys/types.h>
Theodore Ts'o1d2ff461997-10-19 23:00:21 +000032#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000033
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000034#if EXT2_FLAT_INCLUDES
35#include "ext2_fs.h"
36#else
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000037#include <linux/ext2_fs.h>
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000038#endif
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000039
40#include "ext2fs.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000041
Theodore Ts'of3db3561997-04-26 13:34:30 +000042/*
43 * For checking structure magic numbers...
44 */
45
46#define EXT2_CHECK_MAGIC(struct, code) \
47 if ((struct)->magic != (code)) return (code)
48
Theodore Ts'o3839e651997-04-26 13:21:57 +000049struct unix_private_data {
Theodore Ts'of3db3561997-04-26 13:34:30 +000050 int magic;
Theodore Ts'o3839e651997-04-26 13:21:57 +000051 int dev;
52 int flags;
53 char *buf;
54 int buf_block_nr;
55};
56
57static errcode_t unix_open(const char *name, int flags, io_channel *channel);
58static errcode_t unix_close(io_channel channel);
59static errcode_t unix_set_blksize(io_channel channel, int blksize);
60static errcode_t unix_read_blk(io_channel channel, unsigned long block,
61 int count, void *data);
62static errcode_t unix_write_blk(io_channel channel, unsigned long block,
63 int count, const void *data);
64static errcode_t unix_flush(io_channel channel);
65
Theodore Ts'of3db3561997-04-26 13:34:30 +000066static struct struct_io_manager struct_unix_manager = {
67 EXT2_ET_MAGIC_IO_MANAGER,
Theodore Ts'o3839e651997-04-26 13:21:57 +000068 "Unix I/O Manager",
69 unix_open,
70 unix_close,
71 unix_set_blksize,
72 unix_read_blk,
73 unix_write_blk,
74 unix_flush
75};
76
77io_manager unix_io_manager = &struct_unix_manager;
78
79static errcode_t unix_open(const char *name, int flags, io_channel *channel)
80{
81 io_channel io = NULL;
82 struct unix_private_data *data = NULL;
83 errcode_t retval;
Theodore Ts'odc5f68c2000-05-25 23:31:54 +000084 int open_flags;
Theodore Ts'o3839e651997-04-26 13:21:57 +000085
Theodore Ts'o50e1e101997-04-26 13:58:21 +000086 if (name == 0)
87 return EXT2_ET_BAD_DEVICE_NAME;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000088 retval = ext2fs_get_mem(sizeof(struct struct_io_channel),
89 (void **) &io);
90 if (retval)
91 return retval;
Theodore Ts'of3db3561997-04-26 13:34:30 +000092 memset(io, 0, sizeof(struct struct_io_channel));
93 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000094 retval = ext2fs_get_mem(sizeof(struct unix_private_data),
95 (void **) &data);
96 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +000097 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000098
Theodore Ts'o3839e651997-04-26 13:21:57 +000099 io->manager = unix_io_manager;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000100 retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name);
101 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000102 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000103
Theodore Ts'o3839e651997-04-26 13:21:57 +0000104 strcpy(io->name, name);
105 io->private_data = data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000106 io->block_size = 1024;
107 io->read_error = 0;
108 io->write_error = 0;
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000109 io->refcount = 1;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000110
111 memset(data, 0, sizeof(struct unix_private_data));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000112 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000113 retval = ext2fs_get_mem(io->block_size, (void **) &data->buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000114 data->buf_block_nr = -1;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000115 if (retval)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000116 goto cleanup;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000117
Theodore Ts'odc5f68c2000-05-25 23:31:54 +0000118 open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
119#ifdef HAVE_OPEN64
120 data->dev = open64(name, open_flags);
121#else
122 data->dev = open(name, open_flags);
123#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000124 if (data->dev < 0) {
125 retval = errno;
126 goto cleanup;
127 }
128 *channel = io;
129 return 0;
130
131cleanup:
132 if (io)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000133 ext2fs_free_mem((void **) &io);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000134 if (data) {
135 if (data->buf)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000136 ext2fs_free_mem((void **) &data->buf);
137 ext2fs_free_mem((void **) &data);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000138 }
139 return retval;
140}
141
142static errcode_t unix_close(io_channel channel)
143{
144 struct unix_private_data *data;
145 errcode_t retval = 0;
146
Theodore Ts'of3db3561997-04-26 13:34:30 +0000147 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000148 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000149 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'oa29f4d31997-04-29 21:26:48 +0000150
151 if (--channel->refcount > 0)
152 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000153
Theodore Ts'o3839e651997-04-26 13:21:57 +0000154 if (close(data->dev) < 0)
155 retval = errno;
156 if (data->buf)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000157 ext2fs_free_mem((void **) &data->buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000158 if (channel->private_data)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000159 ext2fs_free_mem((void **) &channel->private_data);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000160 if (channel->name)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000161 ext2fs_free_mem((void **) &channel->name);
162 ext2fs_free_mem((void **) &channel);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000163 return retval;
164}
165
166static errcode_t unix_set_blksize(io_channel channel, int blksize)
167{
168 struct unix_private_data *data;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000169 errcode_t retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000170
Theodore Ts'of3db3561997-04-26 13:34:30 +0000171 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000172 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000173 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
174
Theodore Ts'o3839e651997-04-26 13:21:57 +0000175 if (channel->block_size != blksize) {
176 channel->block_size = blksize;
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000177 ext2fs_free_mem((void **) &data->buf);
178 retval = ext2fs_get_mem(blksize, (void **) &data->buf);
179 if (retval)
180 return retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000181 data->buf_block_nr = -1;
182 }
183 return 0;
184}
185
186
187static errcode_t unix_read_blk(io_channel channel, unsigned long block,
188 int count, void *buf)
189{
190 struct unix_private_data *data;
191 errcode_t retval;
192 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000193 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000194 int actual = 0;
195
Theodore Ts'of3db3561997-04-26 13:34:30 +0000196 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000197 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000198 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000199
200 /*
201 * If it's in the cache, use it!
202 */
203 if ((count == 1) && (block == data->buf_block_nr)) {
204 memcpy(buf, data->buf, channel->block_size);
205 return 0;
206 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000207#if 0
208 printf("read_block %lu (%d)\n", block, count);
209#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000210 size = (count < 0) ? -count : count * channel->block_size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000211 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000212 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o5be8dc21997-12-01 18:24:10 +0000213 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000214 goto error_out;
215 }
216 actual = read(data->dev, buf, size);
217 if (actual != size) {
218 if (actual < 0)
219 actual = 0;
220 retval = EXT2_ET_SHORT_READ;
221 goto error_out;
222 }
223 if (count == 1) {
224 data->buf_block_nr = block;
225 memcpy(data->buf, buf, size); /* Update the cache */
226 }
227 return 0;
228
229error_out:
230 memset((char *) buf+actual, 0, size-actual);
231 if (channel->read_error)
232 retval = (channel->read_error)(channel, block, count, buf,
233 size, actual, retval);
234 return retval;
235}
236
237static errcode_t unix_write_blk(io_channel channel, unsigned long block,
238 int count, const void *buf)
239{
240 struct unix_private_data *data;
241 size_t size;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000242 ext2_loff_t location;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000243 int actual = 0;
244 errcode_t retval;
245
Theodore Ts'of3db3561997-04-26 13:34:30 +0000246 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000247 data = (struct unix_private_data *) channel->private_data;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000248 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000249
250 if (count == 1)
251 size = channel->block_size;
252 else {
253 data->buf_block_nr = -1; /* Invalidate the cache */
254 if (count < 0)
255 size = -count;
256 else
257 size = count * channel->block_size;
258 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000259
260 location = (ext2_loff_t) block * channel->block_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000261 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
Theodore Ts'o5be8dc21997-12-01 18:24:10 +0000262 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000263 goto error_out;
264 }
265
266 actual = write(data->dev, buf, size);
267 if (actual != size) {
268 retval = EXT2_ET_SHORT_WRITE;
269 goto error_out;
270 }
271
272 if ((count == 1) && (block == data->buf_block_nr))
273 memcpy(data->buf, buf, size); /* Update the cache */
274
275 return 0;
276
277error_out:
278 if (channel->write_error)
279 retval = (channel->write_error)(channel, block, count, buf,
280 size, actual, retval);
281 return retval;
282}
283
284/*
Theodore Ts'o36f21431997-06-14 07:25:40 +0000285 * Flush data buffers to disk.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000286 */
287static errcode_t unix_flush(io_channel channel)
288{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000289 struct unix_private_data *data;
290
291 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
292 data = (struct unix_private_data *) channel->private_data;
293 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
294
Theodore Ts'o36f21431997-06-14 07:25:40 +0000295 fsync(data->dev);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000296 return 0;
297}
298