Whamcloud - gitweb
1137870eddddc7aee98d79439a22bb60ee1de0d0
[tools/e2fsprogs.git] / lib / ext2fs / unix_io.c
1 /*
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
23 struct unix_private_data {
24         int     dev;
25         int     flags;
26         char    *buf;
27         int     buf_block_nr;
28 };
29
30 static errcode_t unix_open(const char *name, int flags, io_channel *channel);
31 static errcode_t unix_close(io_channel channel);
32 static errcode_t unix_set_blksize(io_channel channel, int blksize);
33 static errcode_t unix_read_blk(io_channel channel, unsigned long block,
34                                int count, void *data);
35 static errcode_t unix_write_blk(io_channel channel, unsigned long block,
36                                 int count, const void *data);
37 static errcode_t unix_flush(io_channel channel);
38
39 struct 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
49 io_manager unix_io_manager = &struct_unix_manager;
50
51 static 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
91 cleanup:
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
102 static 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
120 static 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
137 static 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         
173 error_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
181 static 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         
218 error_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  */
229 static errcode_t unix_flush(io_channel channel)
230 {
231         return 0;
232 }
233