blob: 1137870eddddc7aee98d79439a22bb60ee1de0d0 [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 *
6 * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
7 * under the terms of the GNU Public License.
8 */
9
10#include <stdio.h>
11#include <string.h>
12#include <unistd.h>
13#include <stdlib.h>
14#include <fcntl.h>
15#include <time.h>
16#include <sys/stat.h>
17#include <sys/types.h>
18
19#include "et/com_err.h"
20#include "ext2_err.h"
21#include "io.h"
22
23struct unix_private_data {
24 int dev;
25 int flags;
26 char *buf;
27 int buf_block_nr;
28};
29
30static errcode_t unix_open(const char *name, int flags, io_channel *channel);
31static errcode_t unix_close(io_channel channel);
32static errcode_t unix_set_blksize(io_channel channel, int blksize);
33static errcode_t unix_read_blk(io_channel channel, unsigned long block,
34 int count, void *data);
35static errcode_t unix_write_blk(io_channel channel, unsigned long block,
36 int count, const void *data);
37static errcode_t unix_flush(io_channel channel);
38
39struct struct_io_manager struct_unix_manager = {
40 "Unix I/O Manager",
41 unix_open,
42 unix_close,
43 unix_set_blksize,
44 unix_read_blk,
45 unix_write_blk,
46 unix_flush
47};
48
49io_manager unix_io_manager = &struct_unix_manager;
50
51static errcode_t unix_open(const char *name, int flags, io_channel *channel)
52{
53 io_channel io = NULL;
54 struct unix_private_data *data = NULL;
55 errcode_t retval;
56
57 io = (io_channel) malloc(sizeof(struct struct_io_channel));
58 if (!io)
59 return ENOMEM;
60 data = (struct unix_private_data *)
61 malloc(sizeof(struct unix_private_data));
62 if (!data) {
63 retval = ENOMEM;
64 goto cleanup;
65 }
66 io->manager = unix_io_manager;
67 io->name = malloc(strlen(name)+1);
68 if (!io->name) {
69 retval = ENOMEM;
70 goto cleanup;
71 }
72 strcpy(io->name, name);
73 io->private_data = data;
74
75 memset(data, 0, sizeof(struct unix_private_data));
76 io->block_size = 1024;
77 data->buf = malloc(io->block_size);
78 data->buf_block_nr = -1;
79 if (!data->buf) {
80 retval = ENOMEM;
81 goto cleanup;
82 }
83 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
84 if (data->dev < 0) {
85 retval = errno;
86 goto cleanup;
87 }
88 *channel = io;
89 return 0;
90
91cleanup:
92 if (io)
93 free(io);
94 if (data) {
95 if (data->buf)
96 free(data->buf);
97 free(data);
98 }
99 return retval;
100}
101
102static errcode_t unix_close(io_channel channel)
103{
104 struct unix_private_data *data;
105 errcode_t retval = 0;
106
107 data = (struct unix_private_data *) channel->private_data;
108 if (close(data->dev) < 0)
109 retval = errno;
110 if (data->buf)
111 free(data->buf);
112 if (channel->private_data)
113 free(channel->private_data);
114 if (channel->name)
115 free(channel->name);
116 free(channel);
117 return retval;
118}
119
120static errcode_t unix_set_blksize(io_channel channel, int blksize)
121{
122 struct unix_private_data *data;
123
124 data = (struct unix_private_data *) channel->private_data;
125 if (channel->block_size != blksize) {
126 channel->block_size = blksize;
127 free(data->buf);
128 data->buf = malloc(blksize);
129 if (!data->buf)
130 return ENOMEM;
131 data->buf_block_nr = -1;
132 }
133 return 0;
134}
135
136
137static errcode_t unix_read_blk(io_channel channel, unsigned long block,
138 int count, void *buf)
139{
140 struct unix_private_data *data;
141 errcode_t retval;
142 size_t size;
143 int actual = 0;
144
145 data = (struct unix_private_data *) channel->private_data;
146
147 /*
148 * If it's in the cache, use it!
149 */
150 if ((count == 1) && (block == data->buf_block_nr)) {
151 memcpy(buf, data->buf, channel->block_size);
152 return 0;
153 }
154 size = (count < 0) ? -count : count * channel->block_size;
155 if (lseek(data->dev, block * channel->block_size, SEEK_SET) !=
156 block * channel->block_size) {
157 retval = errno;
158 goto error_out;
159 }
160 actual = read(data->dev, buf, size);
161 if (actual != size) {
162 if (actual < 0)
163 actual = 0;
164 retval = EXT2_ET_SHORT_READ;
165 goto error_out;
166 }
167 if (count == 1) {
168 data->buf_block_nr = block;
169 memcpy(data->buf, buf, size); /* Update the cache */
170 }
171 return 0;
172
173error_out:
174 memset((char *) buf+actual, 0, size-actual);
175 if (channel->read_error)
176 retval = (channel->read_error)(channel, block, count, buf,
177 size, actual, retval);
178 return retval;
179}
180
181static errcode_t unix_write_blk(io_channel channel, unsigned long block,
182 int count, const void *buf)
183{
184 struct unix_private_data *data;
185 size_t size;
186 int actual = 0;
187 errcode_t retval;
188
189 data = (struct unix_private_data *) channel->private_data;
190
191 if (count == 1)
192 size = channel->block_size;
193 else {
194 data->buf_block_nr = -1; /* Invalidate the cache */
195 if (count < 0)
196 size = -count;
197 else
198 size = count * channel->block_size;
199 }
200
201 if (lseek(data->dev, block * channel->block_size, SEEK_SET) !=
202 block * channel->block_size) {
203 retval = errno;
204 goto error_out;
205 }
206
207 actual = write(data->dev, buf, size);
208 if (actual != size) {
209 retval = EXT2_ET_SHORT_WRITE;
210 goto error_out;
211 }
212
213 if ((count == 1) && (block == data->buf_block_nr))
214 memcpy(data->buf, buf, size); /* Update the cache */
215
216 return 0;
217
218error_out:
219 if (channel->write_error)
220 retval = (channel->write_error)(channel, block, count, buf,
221 size, actual, retval);
222 return retval;
223}
224
225/*
226 * Flush data buffers to disk. Since we are currently using a
227 * write-through cache, this is a no-op.
228 */
229static errcode_t unix_flush(io_channel channel)
230{
231 return 0;
232}
233