Whamcloud - gitweb
Many files:
[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 #define _LARGEFILE_SOURCE
15 #define _LARGEFILE64_SOURCE
16
17 #include <stdio.h>
18 #include <string.h>
19 #if HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22 #if HAVE_ERRNO_H
23 #include <errno.h>
24 #endif
25 #include <fcntl.h>
26 #include <time.h>
27 #if HAVE_SYS_STAT_H
28 #include <sys/stat.h>
29 #endif
30 #if HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33
34 #include "ext2_fs.h"
35 #include "ext2fs.h"
36
37 /*
38  * For checking structure magic numbers...
39  */
40
41 #define EXT2_CHECK_MAGIC(struct, code) \
42           if ((struct)->magic != (code)) return (code)
43
44 struct unix_cache {
45         char            *buf;
46         unsigned long   block;
47         int             access_time;
48         int             dirty:1;
49         int             in_use:1;
50 };
51
52 #define CACHE_SIZE 8
53 #define WRITE_VIA_CACHE_SIZE 4  /* Must be smaller than CACHE_SIZE */
54
55 struct unix_private_data {
56         int     magic;
57         int     dev;
58         int     flags;
59         int     access_time;
60         struct unix_cache cache[CACHE_SIZE];
61 };
62
63 static errcode_t unix_open(const char *name, int flags, io_channel *channel);
64 static errcode_t unix_close(io_channel channel);
65 static errcode_t unix_set_blksize(io_channel channel, int blksize);
66 static errcode_t unix_read_blk(io_channel channel, unsigned long block,
67                                int count, void *data);
68 static errcode_t unix_write_blk(io_channel channel, unsigned long block,
69                                 int count, const void *data);
70 static errcode_t unix_flush(io_channel channel);
71 static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
72                                 int size, const void *data);
73
74 static struct struct_io_manager struct_unix_manager = {
75         EXT2_ET_MAGIC_IO_MANAGER,
76         "Unix I/O Manager",
77         unix_open,
78         unix_close,
79         unix_set_blksize,
80         unix_read_blk,
81         unix_write_blk,
82         unix_flush,
83         unix_write_byte
84 };
85
86 io_manager unix_io_manager = &struct_unix_manager;
87
88 /*
89  * Here are the raw I/O functions
90  */
91 static errcode_t raw_read_blk(io_channel channel,
92                               struct unix_private_data *data,
93                               unsigned long block,
94                               int count, void *buf)
95 {
96         errcode_t       retval;
97         size_t          size;
98         ext2_loff_t     location;
99         int             actual = 0;
100
101         size = (count < 0) ? -count : count * channel->block_size;
102         location = (ext2_loff_t) block * channel->block_size;
103         if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
104                 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
105                 goto error_out;
106         }
107         actual = read(data->dev, buf, size);
108         if (actual != size) {
109                 if (actual < 0)
110                         actual = 0;
111                 retval = EXT2_ET_SHORT_READ;
112                 goto error_out;
113         }
114         return 0;
115         
116 error_out:
117         memset((char *) buf+actual, 0, size-actual);
118         if (channel->read_error)
119                 retval = (channel->read_error)(channel, block, count, buf,
120                                                size, actual, retval);
121         return retval;
122 }
123
124 static errcode_t raw_write_blk(io_channel channel,
125                                struct unix_private_data *data,
126                                unsigned long block,
127                                int count, const void *buf)
128 {
129         size_t          size;
130         ext2_loff_t     location;
131         int             actual = 0;
132         errcode_t       retval;
133
134         if (count == 1)
135                 size = channel->block_size;
136         else {
137                 if (count < 0)
138                         size = -count;
139                 else
140                         size = count * channel->block_size;
141         }
142
143         location = (ext2_loff_t) block * channel->block_size;
144         if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
145                 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
146                 goto error_out;
147         }
148         
149         actual = write(data->dev, buf, size);
150         if (actual != size) {
151                 retval = EXT2_ET_SHORT_WRITE;
152                 goto error_out;
153         }
154         return 0;
155         
156 error_out:
157         if (channel->write_error)
158                 retval = (channel->write_error)(channel, block, count, buf,
159                                                 size, actual, retval);
160         return retval;
161 }
162
163
164 /*
165  * Here we implement the cache functions
166  */
167
168 /* Allocate the cache buffers */
169 static errcode_t alloc_cache(io_channel channel,
170                              struct unix_private_data *data)
171 {
172         errcode_t               retval;
173         struct unix_cache       *cache;
174         int                     i;
175         
176         data->access_time = 0;
177         for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
178                 cache->block = 0;
179                 cache->access_time = 0;
180                 cache->dirty = 0;
181                 cache->in_use = 0;
182                 if ((retval = ext2fs_get_mem(channel->block_size,
183                                              (void **) &cache->buf)))
184                         return retval;
185         }
186         return 0;
187 }
188
189 /* Free the cache buffers */
190 static void free_cache(io_channel channel, 
191                        struct unix_private_data *data)
192 {
193         struct unix_cache       *cache;
194         int                     i;
195         
196         data->access_time = 0;
197         for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
198                 cache->block = 0;
199                 cache->access_time = 0;
200                 cache->dirty = 0;
201                 cache->in_use = 0;
202                 if (cache->buf)
203                         ext2fs_free_mem((void **) &cache->buf);
204                 cache->buf = 0;
205         }
206 }
207
208 /*
209  * Try to find a block in the cache.  If get_cache is non-zero, then
210  * if the block isn't in the cache, evict the oldest block in the
211  * cache and create a new cache entry for the requested block.
212  */
213 static struct unix_cache *find_cached_block(io_channel channel,
214                                             struct unix_private_data *data,
215                                             unsigned long block,
216                                             int get_cache)
217 {
218         struct unix_cache       *cache, *unused_cache, *oldest_cache;
219         int                     i;
220         
221         unused_cache = oldest_cache = 0;
222         for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
223                 if (!cache->in_use) {
224                         unused_cache = cache;
225                         continue;
226                 }
227                 if (cache->block == block) {
228                         cache->access_time = ++data->access_time;
229                         return cache;
230                 }
231                 if (!oldest_cache ||
232                     (cache->access_time < oldest_cache->access_time))
233                         oldest_cache = cache;
234         }
235         if (!get_cache)
236                 return 0;
237         
238         /*
239          * Try to allocate cache slot.
240          */
241         if (unused_cache)
242                 cache = unused_cache;
243         else {
244                 cache = oldest_cache;
245                 if (cache->dirty)
246                         raw_write_blk(channel, data,
247                                       cache->block, 1, cache->buf);
248         }
249         cache->in_use = 1;
250         cache->block = block;
251         cache->access_time = ++data->access_time;
252         return cache;
253 }
254
255 /*
256  * Flush all of the blocks in the cache
257  */
258 static errcode_t flush_cached_blocks(io_channel channel,
259                                      struct unix_private_data *data,
260                                      int invalidate)
261
262 {
263         struct unix_cache       *cache;
264         errcode_t               retval, retval2;
265         int                     i;
266         
267         retval2 = 0;
268         for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
269                 if (!cache->in_use)
270                         continue;
271                 
272                 if (invalidate)
273                         cache->in_use = 0;
274                 
275                 if (!cache->dirty)
276                         continue;
277                 
278                 retval = raw_write_blk(channel, data,
279                                        cache->block, 1, cache->buf);
280                 if (retval)
281                         retval2 = retval;
282                 else
283                         cache->dirty = 0;
284         }
285         return retval2;
286 }
287
288
289
290 static errcode_t unix_open(const char *name, int flags, io_channel *channel)
291 {
292         io_channel      io = NULL;
293         struct unix_private_data *data = NULL;
294         errcode_t       retval;
295         int             open_flags;
296
297         if (name == 0)
298                 return EXT2_ET_BAD_DEVICE_NAME;
299         retval = ext2fs_get_mem(sizeof(struct struct_io_channel),
300                                 (void **) &io);
301         if (retval)
302                 return retval;
303         memset(io, 0, sizeof(struct struct_io_channel));
304         io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
305         retval = ext2fs_get_mem(sizeof(struct unix_private_data),
306                                 (void **) &data);
307         if (retval)
308                 goto cleanup;
309
310         io->manager = unix_io_manager;
311         retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name);
312         if (retval)
313                 goto cleanup;
314
315         strcpy(io->name, name);
316         io->private_data = data;
317         io->block_size = 1024;
318         io->read_error = 0;
319         io->write_error = 0;
320         io->refcount = 1;
321
322         memset(data, 0, sizeof(struct unix_private_data));
323         data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
324
325         if ((retval = alloc_cache(io, data)))
326                 goto cleanup;
327         
328         open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
329 #ifdef HAVE_OPEN64
330         data->dev = open64(name, open_flags);
331 #else
332         data->dev = open(name, open_flags);
333 #endif
334         if (data->dev < 0) {
335                 retval = errno;
336                 goto cleanup;
337         }
338         *channel = io;
339         return 0;
340
341 cleanup:
342         if (data) {
343                 free_cache(io, data);
344                 ext2fs_free_mem((void **) &data);
345         }
346         if (io)
347                 ext2fs_free_mem((void **) &io);
348         return retval;
349 }
350
351 static errcode_t unix_close(io_channel channel)
352 {
353         struct unix_private_data *data;
354         errcode_t       retval = 0;
355
356         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
357         data = (struct unix_private_data *) channel->private_data;
358         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
359
360         if (--channel->refcount > 0)
361                 return 0;
362
363         retval = flush_cached_blocks(channel, data, 0);
364
365         if (close(data->dev) < 0)
366                 retval = errno;
367         free_cache(channel, data);
368         if (channel->private_data)
369                 ext2fs_free_mem((void **) &channel->private_data);
370         if (channel->name)
371                 ext2fs_free_mem((void **) &channel->name);
372         ext2fs_free_mem((void **) &channel);
373         return retval;
374 }
375
376 static errcode_t unix_set_blksize(io_channel channel, int blksize)
377 {
378         struct unix_private_data *data;
379         errcode_t               retval;
380
381         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
382         data = (struct unix_private_data *) channel->private_data;
383         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
384
385         if (channel->block_size != blksize) {
386                 if ((retval = flush_cached_blocks(channel, data, 0)))
387                         return retval;
388                 
389                 channel->block_size = blksize;
390                 free_cache(channel, data);
391                 if ((retval = alloc_cache(channel, data)))
392                         return retval;
393         }
394         return 0;
395 }
396
397
398 static errcode_t unix_read_blk(io_channel channel, unsigned long block,
399                                int count, void *buf)
400 {
401         struct unix_private_data *data;
402         struct unix_cache *cache;
403         errcode_t       retval;
404         char            *cp;
405         int             i, j;
406
407         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
408         data = (struct unix_private_data *) channel->private_data;
409         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
410
411         /*
412          * If we're doing an odd-sized read, flush out the cache and
413          * then do a direct read.
414          */
415         if (count < 0) {
416                 if ((retval = flush_cached_blocks(channel, data, 0)))
417                         return retval;
418                 return raw_read_blk(channel, data, block, count, buf);
419         }
420
421         cp = buf;
422         while (count > 0) {
423                 /* If it's in the cache, use it! */
424                 if ((cache = find_cached_block(channel, data, block, 0))) {
425 #ifdef DEBUG
426                         printf("Using cached block %d\n", block);
427 #endif
428                         memcpy(cp, cache->buf, channel->block_size);
429                         count--;
430                         block++;
431                         cp += channel->block_size;
432                         continue;
433                 }
434                 /*
435                  * Find the number of uncached blocks so we can do a
436                  * single read request
437                  */
438                 for (i=1; i < count; i++)
439                         if (find_cached_block(channel, data, block+i, 0))
440                                 break;
441 #ifdef DEBUG
442                 printf("Reading %d blocks starting at %d\n", i, block);
443 #endif
444                 if ((retval = raw_read_blk(channel, data, block, i, cp)))
445                         return retval;
446                 
447                 /* Save the results in the cache */
448                 for (j=0; j < i; j++) {
449                         count--;
450                         cache = find_cached_block(channel, data, block++, 1);
451                         if (cache)
452                                 memcpy(cache->buf, cp, channel->block_size);
453                         cp += channel->block_size;
454                 }
455         }
456         return 0;
457 }
458
459 static errcode_t unix_write_blk(io_channel channel, unsigned long block,
460                                 int count, const void *buf)
461 {
462         struct unix_private_data *data;
463         struct unix_cache *cache;
464         errcode_t       retval = 0, retval2;
465         const char      *cp;
466         int             writethrough;
467
468         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
469         data = (struct unix_private_data *) channel->private_data;
470         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
471
472         /*
473          * If we're doing an odd-sized write or a very large write,
474          * flush out the cache completely and then do a direct write.
475          */
476         if (count < 0 || count > WRITE_VIA_CACHE_SIZE) {
477                 if ((retval = flush_cached_blocks(channel, data, 1)))
478                         return retval;
479                 return raw_write_blk(channel, data, block, count, buf);
480         }
481
482         /*
483          * For a moderate-sized multi-block write, first force a write
484          * if we're in write-through cache mode, and then fill the
485          * cache with the blocks.
486          */
487         writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
488         if (writethrough)
489                 retval = raw_write_blk(channel, data, block, count, buf);
490         
491         cp = buf;
492         while (count > 0) {
493                 cache = find_cached_block(channel, data, block, 1);
494                 if (!cache) {
495                         /*
496                          * Oh shit, we couldn't get cache descriptor.
497                          * Force the write directly.
498                          */
499                         if ((retval2 = raw_write_blk(channel, data, block,
500                                                 1, cp)))
501                                 retval = retval2;
502                 } else {
503                         memcpy(cache->buf, cp, channel->block_size);
504                         cache->dirty = !writethrough;
505                 }
506                 count--;
507                 block++;
508                 cp += channel->block_size;
509         }
510         return retval;
511 }
512
513 static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
514                                  int size, const void *buf)
515 {
516         struct unix_private_data *data;
517         errcode_t       retval = 0;
518         size_t          actual;
519
520         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
521         data = (struct unix_private_data *) channel->private_data;
522         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
523
524         /*
525          * Flush out the cache completely
526          */
527         if ((retval = flush_cached_blocks(channel, data, 1)))
528                 return retval;
529
530         if (lseek(data->dev, offset, SEEK_SET) < 0)
531                 return errno;
532         
533         actual = write(data->dev, buf, size);
534         if (actual != size)
535                 return EXT2_ET_SHORT_WRITE;
536
537         return 0;
538 }
539
540 /*
541  * Flush data buffers to disk.  
542  */
543 static errcode_t unix_flush(io_channel channel)
544 {
545         struct unix_private_data *data;
546         errcode_t retval = 0;
547         
548         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
549         data = (struct unix_private_data *) channel->private_data;
550         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
551
552         retval = flush_cached_blocks(channel, data, 0);
553         fsync(data->dev);
554         return retval;
555 }
556