Whamcloud - gitweb
ChangeLog, openfs.c:
[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 #if HAVE_ERRNO_H
20 #include <errno.h>
21 #endif
22 #include <fcntl.h>
23 #include <time.h>
24 #if HAVE_SYS_STAT_H
25 #include <sys/stat.h>
26 #endif
27 #if HAVE_SYS_TYPES_H
28 #include <sys/types.h>
29 #endif
30
31 #if EXT2_FLAT_INCLUDES
32 #include "ext2_fs.h"
33 #else
34 #include <linux/ext2_fs.h>
35 #endif
36
37 #include "ext2fs.h"
38
39 /*
40  * For checking structure magic numbers...
41  */
42
43 #define EXT2_CHECK_MAGIC(struct, code) \
44           if ((struct)->magic != (code)) return (code)
45   
46 struct unix_private_data {
47         int     magic;
48         int     dev;
49         int     flags;
50         char    *buf;
51         int     buf_block_nr;
52 };
53
54 static errcode_t unix_open(const char *name, int flags, io_channel *channel);
55 static errcode_t unix_close(io_channel channel);
56 static errcode_t unix_set_blksize(io_channel channel, int blksize);
57 static errcode_t unix_read_blk(io_channel channel, unsigned long block,
58                                int count, void *data);
59 static errcode_t unix_write_blk(io_channel channel, unsigned long block,
60                                 int count, const void *data);
61 static errcode_t unix_flush(io_channel channel);
62
63 static struct struct_io_manager struct_unix_manager = {
64         EXT2_ET_MAGIC_IO_MANAGER,
65         "Unix I/O Manager",
66         unix_open,
67         unix_close,
68         unix_set_blksize,
69         unix_read_blk,
70         unix_write_blk,
71         unix_flush
72 };
73
74 io_manager unix_io_manager = &struct_unix_manager;
75
76 static errcode_t unix_open(const char *name, int flags, io_channel *channel)
77 {
78         io_channel      io = NULL;
79         struct unix_private_data *data = NULL;
80         errcode_t       retval;
81
82         if (name == 0)
83                 return EXT2_ET_BAD_DEVICE_NAME;
84         retval = ext2fs_get_mem(sizeof(struct struct_io_channel),
85                                 (void **) &io);
86         if (retval)
87                 return retval;
88         memset(io, 0, sizeof(struct struct_io_channel));
89         io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
90         retval = ext2fs_get_mem(sizeof(struct unix_private_data),
91                                 (void **) &data);
92         if (retval)
93                 goto cleanup;
94
95         io->manager = unix_io_manager;
96         retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name);
97         if (retval)
98                 goto cleanup;
99
100         strcpy(io->name, name);
101         io->private_data = data;
102         io->block_size = 1024;
103         io->read_error = 0;
104         io->write_error = 0;
105         io->refcount = 1;
106
107         memset(data, 0, sizeof(struct unix_private_data));
108         data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
109         retval = ext2fs_get_mem(io->block_size, (void **) &data->buf);
110         data->buf_block_nr = -1;
111         if (retval)
112                 goto cleanup;
113
114         data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY);
115         if (data->dev < 0) {
116                 retval = errno;
117                 goto cleanup;
118         }
119         *channel = io;
120         return 0;
121
122 cleanup:
123         if (io)
124                 ext2fs_free_mem((void **) &io);
125         if (data) {
126                 if (data->buf)
127                         ext2fs_free_mem((void **) &data->buf);
128                 ext2fs_free_mem((void **) &data);
129         }
130         return retval;
131 }
132
133 static errcode_t unix_close(io_channel channel)
134 {
135         struct unix_private_data *data;
136         errcode_t       retval = 0;
137
138         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
139         data = (struct unix_private_data *) channel->private_data;
140         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
141
142         if (--channel->refcount > 0)
143                 return 0;
144         
145         if (close(data->dev) < 0)
146                 retval = errno;
147         if (data->buf)
148                 ext2fs_free_mem((void **) &data->buf);
149         if (channel->private_data)
150                 ext2fs_free_mem((void **) &channel->private_data);
151         if (channel->name)
152                 ext2fs_free_mem((void **) &channel->name);
153         ext2fs_free_mem((void **) &channel);
154         return retval;
155 }
156
157 static errcode_t unix_set_blksize(io_channel channel, int blksize)
158 {
159         struct unix_private_data *data;
160         errcode_t               retval;
161
162         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
163         data = (struct unix_private_data *) channel->private_data;
164         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
165
166         if (channel->block_size != blksize) {
167                 channel->block_size = blksize;
168                 ext2fs_free_mem((void **) &data->buf);
169                 retval = ext2fs_get_mem(blksize, (void **) &data->buf);
170                 if (retval)
171                         return retval;
172                 data->buf_block_nr = -1;
173         }
174         return 0;
175 }
176
177
178 static errcode_t unix_read_blk(io_channel channel, unsigned long block,
179                                int count, void *buf)
180 {
181         struct unix_private_data *data;
182         errcode_t       retval;
183         size_t          size;
184         ext2_loff_t     location;
185         int             actual = 0;
186
187         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
188         data = (struct unix_private_data *) channel->private_data;
189         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
190
191         /*
192          * If it's in the cache, use it!
193          */
194         if ((count == 1) && (block == data->buf_block_nr)) {
195                 memcpy(buf, data->buf, channel->block_size);
196                 return 0;
197         }
198 #if 0
199         printf("read_block %lu (%d)\n", block, count);
200 #endif
201         size = (count < 0) ? -count : count * channel->block_size;
202         location = (ext2_loff_t) block * channel->block_size;
203         if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
204                 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
205                 goto error_out;
206         }
207         actual = read(data->dev, buf, size);
208         if (actual != size) {
209                 if (actual < 0)
210                         actual = 0;
211                 retval = EXT2_ET_SHORT_READ;
212                 goto error_out;
213         }
214         if (count == 1) {
215                 data->buf_block_nr = block;
216                 memcpy(data->buf, buf, size);   /* Update the cache */
217         }
218         return 0;
219         
220 error_out:
221         memset((char *) buf+actual, 0, size-actual);
222         if (channel->read_error)
223                 retval = (channel->read_error)(channel, block, count, buf,
224                                                size, actual, retval);
225         return retval;
226 }
227
228 static errcode_t unix_write_blk(io_channel channel, unsigned long block,
229                                 int count, const void *buf)
230 {
231         struct unix_private_data *data;
232         size_t          size;
233         ext2_loff_t     location;
234         int             actual = 0;
235         errcode_t       retval;
236
237         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
238         data = (struct unix_private_data *) channel->private_data;
239         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
240
241         if (count == 1)
242                 size = channel->block_size;
243         else {
244                 data->buf_block_nr = -1;        /* Invalidate the cache */
245                 if (count < 0)
246                         size = -count;
247                 else
248                         size = count * channel->block_size;
249         } 
250
251         location = (ext2_loff_t) block * channel->block_size;
252         if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
253                 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
254                 goto error_out;
255         }
256         
257         actual = write(data->dev, buf, size);
258         if (actual != size) {
259                 retval = EXT2_ET_SHORT_WRITE;
260                 goto error_out;
261         }
262
263         if ((count == 1) && (block == data->buf_block_nr))
264                 memcpy(data->buf, buf, size); /* Update the cache */
265         
266         return 0;
267         
268 error_out:
269         if (channel->write_error)
270                 retval = (channel->write_error)(channel, block, count, buf,
271                                                 size, actual, retval);
272         return retval;
273 }
274
275 /*
276  * Flush data buffers to disk.  
277  */
278 static errcode_t unix_flush(io_channel channel)
279 {
280         struct unix_private_data *data;
281         
282         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
283         data = (struct unix_private_data *) channel->private_data;
284         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
285         
286         fsync(data->dev);
287         return 0;
288 }
289