Whamcloud - gitweb
f1d37b52ca0d5da69675ffb429ec433d804a81fa
[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, 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%
12  */
13
14 #include <stdio.h>
15 #include <string.h>
16 #if HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #include <stdlib.h>
20 #include <fcntl.h>
21 #include <time.h>
22 #if HAVE_SYS_STAT_H
23 #include <sys/stat.h>
24 #endif
25 #if HAVE_SYS_TYPES_H
26 #include <sys/types.h>
27 #endif
28
29 #include "et/com_err.h"
30 #include "ext2fs/ext2_err.h"
31 #include "ext2fs/ext2_io.h"
32
33 /*
34  * For checking structure magic numbers...
35  */
36
37 #define EXT2_CHECK_MAGIC(struct, code) \
38           if ((struct)->magic != (code)) return (code)
39   
40 struct unix_private_data {
41         int     magic;
42         int     dev;
43         int     flags;
44         char    *buf;
45         int     buf_block_nr;
46 };
47
48 static errcode_t unix_open(const char *name, int flags, io_channel *channel);
49 static errcode_t unix_close(io_channel channel);
50 static errcode_t unix_set_blksize(io_channel channel, int blksize);
51 static errcode_t unix_read_blk(io_channel channel, unsigned long block,
52                                int count, void *data);
53 static errcode_t unix_write_blk(io_channel channel, unsigned long block,
54                                 int count, const void *data);
55 static errcode_t unix_flush(io_channel channel);
56
57 static struct struct_io_manager struct_unix_manager = {
58         EXT2_ET_MAGIC_IO_MANAGER,
59         "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
68 io_manager unix_io_manager = &struct_unix_manager;
69
70 static 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
76         if (name == 0)
77                 return EXT2_ET_BAD_DEVICE_NAME;
78         io = (io_channel) malloc(sizeof(struct struct_io_channel));
79         if (!io)
80                 return EXT2_NO_MEMORY;
81         memset(io, 0, sizeof(struct struct_io_channel));
82         io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
83         data = (struct unix_private_data *)
84                 malloc(sizeof(struct unix_private_data));
85         if (!data) {
86                 retval = EXT2_NO_MEMORY;
87                 goto cleanup;
88         }
89         io->manager = unix_io_manager;
90         io->name = malloc(strlen(name)+1);
91         if (!io->name) {
92                 retval = EXT2_NO_MEMORY;
93                 goto cleanup;
94         }
95         strcpy(io->name, name);
96         io->private_data = data;
97         io->block_size = 1024;
98         io->read_error = 0;
99         io->write_error = 0;
100         io->refcount = 1;
101
102         memset(data, 0, sizeof(struct unix_private_data));
103         data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
104         data->buf = malloc(io->block_size);
105         data->buf_block_nr = -1;
106         if (!data->buf) {
107                 retval = EXT2_NO_MEMORY;
108                 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
118 cleanup:
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
129 static errcode_t unix_close(io_channel channel)
130 {
131         struct unix_private_data *data;
132         errcode_t       retval = 0;
133
134         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
135         data = (struct unix_private_data *) channel->private_data;
136         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
137
138         if (--channel->refcount > 0)
139                 return 0;
140         
141         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
153 static errcode_t unix_set_blksize(io_channel channel, int blksize)
154 {
155         struct unix_private_data *data;
156
157         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
158         data = (struct unix_private_data *) channel->private_data;
159         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
160
161         if (channel->block_size != blksize) {
162                 channel->block_size = blksize;
163                 free(data->buf);
164                 data->buf = malloc(blksize);
165                 if (!data->buf)
166                         return EXT2_NO_MEMORY;
167                 data->buf_block_nr = -1;
168         }
169         return 0;
170 }
171
172
173 static 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;
179         ext2_loff_t     location;
180         int             actual = 0;
181
182         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
183         data = (struct unix_private_data *) channel->private_data;
184         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
185
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         }
193 #if 0
194         printf("read_block %lu (%d)\n", block, count);
195 #endif
196         size = (count < 0) ? -count : count * channel->block_size;
197         location = (ext2_loff_t) block * channel->block_size;
198         if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
199                 retval = errno ? errno : EXT2_IO_LLSEEK_FAILED;
200                 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         
215 error_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
223 static 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;
228         ext2_loff_t     location;
229         int             actual = 0;
230         errcode_t       retval;
231
232         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
233         data = (struct unix_private_data *) channel->private_data;
234         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
235
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         } 
245
246         location = (ext2_loff_t) block * channel->block_size;
247         if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
248                 retval = errno ? errno : EXT2_IO_LLSEEK_FAILED;
249                 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         
263 error_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 /*
271  * Flush data buffers to disk.  
272  */
273 static errcode_t unix_flush(io_channel channel)
274 {
275         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         
281         fsync(data->dev);
282         return 0;
283 }
284