source: branches/stable/mindi-busybox/e2fsprogs/ext2fs/unix_io.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 16.9 KB
Line 
1/*
2 * unix_io.c --- This is the Unix (well, really POSIX) implementation
3 *  of the I/O manager.
4 *
5 * Implements a one-block write-through cache.
6 *
7 * Includes support for Windows NT support under Cygwin.
8 *
9 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
10 *  2002 by Theodore Ts'o.
11 *
12 * %Begin-Header%
13 * This file may be redistributed under the terms of the GNU Public
14 * License.
15 * %End-Header%
16 */
17
18#include <stdio.h>
19#include <string.h>
20#if HAVE_UNISTD_H
21#include <unistd.h>
22#endif
23#if HAVE_ERRNO_H
24#include <errno.h>
25#endif
26#include <fcntl.h>
27#include <time.h>
28#ifdef __linux__
29#include <sys/utsname.h>
30#endif
31#if HAVE_SYS_STAT_H
32#include <sys/stat.h>
33#endif
34#if HAVE_SYS_TYPES_H
35#include <sys/types.h>
36#endif
37#include <sys/resource.h>
38
39#include "ext2_fs.h"
40#include "ext2fs.h"
41
42/*
43 * For checking structure magic numbers...
44 */
45
46#define EXT2_CHECK_MAGIC(struct, code) \
47      if ((struct)->magic != (code)) return (code)
48
49struct unix_cache {
50    char        *buf;
51    unsigned long   block;
52    int     access_time;
53    unsigned    dirty:1;
54    unsigned    in_use:1;
55};
56
57#define CACHE_SIZE 8
58#define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */
59#define READ_DIRECT_SIZE 4  /* Should be smaller than CACHE_SIZE */
60
61struct unix_private_data {
62    int magic;
63    int dev;
64    int flags;
65    int access_time;
66    ext2_loff_t offset;
67    struct unix_cache cache[CACHE_SIZE];
68};
69
70static errcode_t unix_open(const char *name, int flags, io_channel *channel);
71static errcode_t unix_close(io_channel channel);
72static errcode_t unix_set_blksize(io_channel channel, int blksize);
73static errcode_t unix_read_blk(io_channel channel, unsigned long block,
74                   int count, void *data);
75static errcode_t unix_write_blk(io_channel channel, unsigned long block,
76                int count, const void *data);
77static errcode_t unix_flush(io_channel channel);
78static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
79                int size, const void *data);
80static errcode_t unix_set_option(io_channel channel, const char *option,
81                 const char *arg);
82
83static void reuse_cache(io_channel channel, struct unix_private_data *data,
84         struct unix_cache *cache, unsigned long block);
85
86/* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel
87 * does not know buffered block devices - everything is raw. */
88#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
89#define NEED_BOUNCE_BUFFER
90#else
91#undef NEED_BOUNCE_BUFFER
92#endif
93
94static struct struct_io_manager struct_unix_manager = {
95    EXT2_ET_MAGIC_IO_MANAGER,
96    "Unix I/O Manager",
97    unix_open,
98    unix_close,
99    unix_set_blksize,
100    unix_read_blk,
101    unix_write_blk,
102    unix_flush,
103#ifdef NEED_BOUNCE_BUFFER
104    0,
105#else
106    unix_write_byte,
107#endif
108    unix_set_option
109};
110
111io_manager unix_io_manager = &struct_unix_manager;
112
113/*
114 * Here are the raw I/O functions
115 */
116#ifndef NEED_BOUNCE_BUFFER
117static errcode_t raw_read_blk(io_channel channel,
118                  struct unix_private_data *data,
119                  unsigned long block,
120                  int count, void *buf)
121{
122    errcode_t   retval;
123    ssize_t     size;
124    ext2_loff_t location;
125    int     actual = 0;
126
127    size = (count < 0) ? -count : count * channel->block_size;
128    location = ((ext2_loff_t) block * channel->block_size) + data->offset;
129    if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
130        retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
131        goto error_out;
132    }
133    actual = read(data->dev, buf, size);
134    if (actual != size) {
135        if (actual < 0)
136            actual = 0;
137        retval = EXT2_ET_SHORT_READ;
138        goto error_out;
139    }
140    return 0;
141
142error_out:
143    memset((char *) buf+actual, 0, size-actual);
144    if (channel->read_error)
145        retval = (channel->read_error)(channel, block, count, buf,
146                           size, actual, retval);
147    return retval;
148}
149#else /* NEED_BOUNCE_BUFFER */
150/*
151 * Windows and FreeBSD block devices only allow sector alignment IO in offset and size
152 */
153static errcode_t raw_read_blk(io_channel channel,
154                  struct unix_private_data *data,
155                  unsigned long block,
156                  int count, void *buf)
157{
158    errcode_t   retval;
159    size_t      size, alignsize, fragment;
160    ext2_loff_t location;
161    int     total = 0, actual;
162#define BLOCKALIGN 512
163    char        sector[BLOCKALIGN];
164
165    size = (count < 0) ? -count : count * channel->block_size;
166    location = ((ext2_loff_t) block * channel->block_size) + data->offset;
167#ifdef DEBUG
168    printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n",
169            count, size, block, channel->block_size, location);
170#endif
171    if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
172        retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
173        goto error_out;
174    }
175    fragment = size % BLOCKALIGN;
176    alignsize = size - fragment;
177    if (alignsize) {
178        actual = read(data->dev, buf, alignsize);
179        if (actual != alignsize)
180            goto short_read;
181    }
182    if (fragment) {
183        actual = read(data->dev, sector, BLOCKALIGN);
184        if (actual != BLOCKALIGN)
185            goto short_read;
186        memcpy(buf+alignsize, sector, fragment);
187    }
188    return 0;
189
190short_read:
191    if (actual>0)
192        total += actual;
193    retval = EXT2_ET_SHORT_READ;
194
195error_out:
196    memset((char *) buf+total, 0, size-actual);
197    if (channel->read_error)
198        retval = (channel->read_error)(channel, block, count, buf,
199                           size, actual, retval);
200    return retval;
201}
202#endif
203
204static errcode_t raw_write_blk(io_channel channel,
205                   struct unix_private_data *data,
206                   unsigned long block,
207                   int count, const void *buf)
208{
209    ssize_t     size;
210    ext2_loff_t location;
211    int     actual = 0;
212    errcode_t   retval;
213
214    if (count == 1)
215        size = channel->block_size;
216    else {
217        if (count < 0)
218            size = -count;
219        else
220            size = count * channel->block_size;
221    }
222
223    location = ((ext2_loff_t) block * channel->block_size) + data->offset;
224    if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
225        retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
226        goto error_out;
227    }
228
229    actual = write(data->dev, buf, size);
230    if (actual != size) {
231        retval = EXT2_ET_SHORT_WRITE;
232        goto error_out;
233    }
234    return 0;
235
236error_out:
237    if (channel->write_error)
238        retval = (channel->write_error)(channel, block, count, buf,
239                        size, actual, retval);
240    return retval;
241}
242
243
244/*
245 * Here we implement the cache functions
246 */
247
248/* Allocate the cache buffers */
249static errcode_t alloc_cache(io_channel channel,
250                 struct unix_private_data *data)
251{
252    errcode_t       retval;
253    struct unix_cache   *cache;
254    int         i;
255
256    data->access_time = 0;
257    for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
258        cache->block = 0;
259        cache->access_time = 0;
260        cache->dirty = 0;
261        cache->in_use = 0;
262        if ((retval = ext2fs_get_mem(channel->block_size,
263                         &cache->buf)))
264            return retval;
265    }
266    return 0;
267}
268
269/* Free the cache buffers */
270static void free_cache(struct unix_private_data *data)
271{
272    struct unix_cache   *cache;
273    int         i;
274
275    data->access_time = 0;
276    for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
277        cache->block = 0;
278        cache->access_time = 0;
279        cache->dirty = 0;
280        cache->in_use = 0;
281        ext2fs_free_mem(&cache->buf);
282        cache->buf = 0;
283    }
284}
285
286#ifndef NO_IO_CACHE
287/*
288 * Try to find a block in the cache.  If the block is not found, and
289 * eldest is a non-zero pointer, then fill in eldest with the cache
290 * entry to that should be reused.
291 */
292static struct unix_cache *find_cached_block(struct unix_private_data *data,
293                        unsigned long block,
294                        struct unix_cache **eldest)
295{
296    struct unix_cache   *cache, *unused_cache, *oldest_cache;
297    int         i;
298
299    unused_cache = oldest_cache = 0;
300    for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
301        if (!cache->in_use) {
302            if (!unused_cache)
303                unused_cache = cache;
304            continue;
305        }
306        if (cache->block == block) {
307            cache->access_time = ++data->access_time;
308            return cache;
309        }
310        if (!oldest_cache ||
311            (cache->access_time < oldest_cache->access_time))
312            oldest_cache = cache;
313    }
314    if (eldest)
315        *eldest = (unused_cache) ? unused_cache : oldest_cache;
316    return 0;
317}
318
319/*
320 * Reuse a particular cache entry for another block.
321 */
322static void reuse_cache(io_channel channel, struct unix_private_data *data,
323         struct unix_cache *cache, unsigned long block)
324{
325    if (cache->dirty && cache->in_use)
326        raw_write_blk(channel, data, cache->block, 1, cache->buf);
327
328    cache->in_use = 1;
329    cache->dirty = 0;
330    cache->block = block;
331    cache->access_time = ++data->access_time;
332}
333
334/*
335 * Flush all of the blocks in the cache
336 */
337static errcode_t flush_cached_blocks(io_channel channel,
338                     struct unix_private_data *data,
339                     int invalidate)
340
341{
342    struct unix_cache   *cache;
343    errcode_t       retval, retval2;
344    int         i;
345
346    retval2 = 0;
347    for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
348        if (!cache->in_use)
349            continue;
350
351        if (invalidate)
352            cache->in_use = 0;
353
354        if (!cache->dirty)
355            continue;
356
357        retval = raw_write_blk(channel, data,
358                       cache->block, 1, cache->buf);
359        if (retval)
360            retval2 = retval;
361        else
362            cache->dirty = 0;
363    }
364    return retval2;
365}
366#endif /* NO_IO_CACHE */
367
368static errcode_t unix_open(const char *name, int flags, io_channel *channel)
369{
370    io_channel  io = NULL;
371    struct unix_private_data *data = NULL;
372    errcode_t   retval;
373    int     open_flags;
374    struct stat st;
375#ifdef __linux__
376    struct      utsname ut;
377#endif
378
379    if (name == 0)
380        return EXT2_ET_BAD_DEVICE_NAME;
381    retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
382    if (retval)
383        return retval;
384    memset(io, 0, sizeof(struct struct_io_channel));
385    io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
386    retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data);
387    if (retval)
388        goto cleanup;
389
390    io->manager = unix_io_manager;
391    retval = ext2fs_get_mem(strlen(name)+1, &io->name);
392    if (retval)
393        goto cleanup;
394
395    strcpy(io->name, name);
396    io->private_data = data;
397    io->block_size = 1024;
398    io->read_error = 0;
399    io->write_error = 0;
400    io->refcount = 1;
401
402    memset(data, 0, sizeof(struct unix_private_data));
403    data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
404
405    if ((retval = alloc_cache(io, data)))
406        goto cleanup;
407
408    open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
409#ifdef CONFIG_LFS
410    data->dev = open64(io->name, open_flags);
411#else
412    data->dev = open(io->name, open_flags);
413#endif
414    if (data->dev < 0) {
415        retval = errno;
416        goto cleanup;
417    }
418
419#ifdef __linux__
420#undef RLIM_INFINITY
421#if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4)))
422#define RLIM_INFINITY   ((unsigned long)(~0UL>>1))
423#else
424#define RLIM_INFINITY  (~0UL)
425#endif
426    /*
427     * Work around a bug in 2.4.10-2.4.18 kernels where writes to
428     * block devices are wrongly getting hit by the filesize
429     * limit.  This workaround isn't perfect, since it won't work
430     * if glibc wasn't built against 2.2 header files.  (Sigh.)
431     *
432     */
433    if ((flags & IO_FLAG_RW) &&
434        (uname(&ut) == 0) &&
435        ((ut.release[0] == '2') && (ut.release[1] == '.') &&
436         (ut.release[2] == '4') && (ut.release[3] == '.') &&
437         (ut.release[4] == '1') && (ut.release[5] >= '0') &&
438         (ut.release[5] < '8')) &&
439        (fstat(data->dev, &st) == 0) &&
440        (S_ISBLK(st.st_mode))) {
441        struct rlimit   rlim;
442
443        rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY;
444        setrlimit(RLIMIT_FSIZE, &rlim);
445        getrlimit(RLIMIT_FSIZE, &rlim);
446        if (((unsigned long) rlim.rlim_cur) <
447            ((unsigned long) rlim.rlim_max)) {
448            rlim.rlim_cur = rlim.rlim_max;
449            setrlimit(RLIMIT_FSIZE, &rlim);
450        }
451    }
452#endif
453    *channel = io;
454    return 0;
455
456cleanup:
457    if (data) {
458        free_cache(data);
459        ext2fs_free_mem(&data);
460    }
461    ext2fs_free_mem(&io);
462    return retval;
463}
464
465static errcode_t unix_close(io_channel channel)
466{
467    struct unix_private_data *data;
468    errcode_t   retval = 0;
469
470    EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
471    data = (struct unix_private_data *) channel->private_data;
472    EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
473
474    if (--channel->refcount > 0)
475        return 0;
476
477#ifndef NO_IO_CACHE
478    retval = flush_cached_blocks(channel, data, 0);
479#endif
480
481    if (close(data->dev) < 0)
482        retval = errno;
483    free_cache(data);
484
485    ext2fs_free_mem(&channel->private_data);
486    ext2fs_free_mem(&channel->name);
487    ext2fs_free_mem(&channel);
488    return retval;
489}
490
491static errcode_t unix_set_blksize(io_channel channel, int blksize)
492{
493    struct unix_private_data *data;
494    errcode_t       retval;
495
496    EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
497    data = (struct unix_private_data *) channel->private_data;
498    EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
499
500    if (channel->block_size != blksize) {
501#ifndef NO_IO_CACHE
502        if ((retval = flush_cached_blocks(channel, data, 0)))
503            return retval;
504#endif
505
506        channel->block_size = blksize;
507        free_cache(data);
508        if ((retval = alloc_cache(channel, data)))
509            return retval;
510    }
511    return 0;
512}
513
514
515static errcode_t unix_read_blk(io_channel channel, unsigned long block,
516                   int count, void *buf)
517{
518    struct unix_private_data *data;
519    struct unix_cache *cache, *reuse[READ_DIRECT_SIZE];
520    errcode_t   retval;
521    char        *cp;
522    int     i, j;
523
524    EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
525    data = (struct unix_private_data *) channel->private_data;
526    EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
527
528#ifdef NO_IO_CACHE
529    return raw_read_blk(channel, data, block, count, buf);
530#else
531    /*
532     * If we're doing an odd-sized read or a very large read,
533     * flush out the cache and then do a direct read.
534     */
535    if (count < 0 || count > WRITE_DIRECT_SIZE) {
536        if ((retval = flush_cached_blocks(channel, data, 0)))
537            return retval;
538        return raw_read_blk(channel, data, block, count, buf);
539    }
540
541    cp = buf;
542    while (count > 0) {
543        /* If it's in the cache, use it! */
544        if ((cache = find_cached_block(data, block, &reuse[0]))) {
545#ifdef DEBUG
546            printf("Using cached block %d\n", block);
547#endif
548            memcpy(cp, cache->buf, channel->block_size);
549            count--;
550            block++;
551            cp += channel->block_size;
552            continue;
553        }
554        /*
555         * Find the number of uncached blocks so we can do a
556         * single read request
557         */
558        for (i=1; i < count; i++)
559            if (find_cached_block(data, block+i, &reuse[i]))
560                break;
561#ifdef DEBUG
562        printf("Reading %d blocks starting at %d\n", i, block);
563#endif
564        if ((retval = raw_read_blk(channel, data, block, i, cp)))
565            return retval;
566
567        /* Save the results in the cache */
568        for (j=0; j < i; j++) {
569            count--;
570            cache = reuse[j];
571            reuse_cache(channel, data, cache, block++);
572            memcpy(cache->buf, cp, channel->block_size);
573            cp += channel->block_size;
574        }
575    }
576    return 0;
577#endif /* NO_IO_CACHE */
578}
579
580static errcode_t unix_write_blk(io_channel channel, unsigned long block,
581                int count, const void *buf)
582{
583    struct unix_private_data *data;
584    struct unix_cache *cache, *reuse;
585    errcode_t   retval = 0;
586    const char  *cp;
587    int     writethrough;
588
589    EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
590    data = (struct unix_private_data *) channel->private_data;
591    EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
592
593#ifdef NO_IO_CACHE
594    return raw_write_blk(channel, data, block, count, buf);
595#else
596    /*
597     * If we're doing an odd-sized write or a very large write,
598     * flush out the cache completely and then do a direct write.
599     */
600    if (count < 0 || count > WRITE_DIRECT_SIZE) {
601        if ((retval = flush_cached_blocks(channel, data, 1)))
602            return retval;
603        return raw_write_blk(channel, data, block, count, buf);
604    }
605
606    /*
607     * For a moderate-sized multi-block write, first force a write
608     * if we're in write-through cache mode, and then fill the
609     * cache with the blocks.
610     */
611    writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
612    if (writethrough)
613        retval = raw_write_blk(channel, data, block, count, buf);
614
615    cp = buf;
616    while (count > 0) {
617        cache = find_cached_block(data, block, &reuse);
618        if (!cache) {
619            cache = reuse;
620            reuse_cache(channel, data, cache, block);
621        }
622        memcpy(cache->buf, cp, channel->block_size);
623        cache->dirty = !writethrough;
624        count--;
625        block++;
626        cp += channel->block_size;
627    }
628    return retval;
629#endif /* NO_IO_CACHE */
630}
631
632static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
633                 int size, const void *buf)
634{
635    struct unix_private_data *data;
636    errcode_t   retval = 0;
637    ssize_t     actual;
638
639    EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
640    data = (struct unix_private_data *) channel->private_data;
641    EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
642
643#ifndef NO_IO_CACHE
644    /*
645     * Flush out the cache completely
646     */
647    if ((retval = flush_cached_blocks(channel, data, 1)))
648        return retval;
649#endif
650
651    if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0)
652        return errno;
653
654    actual = write(data->dev, buf, size);
655    if (actual != size)
656        return EXT2_ET_SHORT_WRITE;
657
658    return 0;
659}
660
661/*
662 * Flush data buffers to disk.
663 */
664static errcode_t unix_flush(io_channel channel)
665{
666    struct unix_private_data *data;
667    errcode_t retval = 0;
668
669    EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
670    data = (struct unix_private_data *) channel->private_data;
671    EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
672
673#ifndef NO_IO_CACHE
674    retval = flush_cached_blocks(channel, data, 0);
675#endif
676    fsync(data->dev);
677    return retval;
678}
679
680static errcode_t unix_set_option(io_channel channel, const char *option,
681                 const char *arg)
682{
683    struct unix_private_data *data;
684    unsigned long tmp;
685    char *end;
686
687    EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
688    data = (struct unix_private_data *) channel->private_data;
689    EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
690
691    if (!strcmp(option, "offset")) {
692        if (!arg)
693            return EXT2_ET_INVALID_ARGUMENT;
694
695        tmp = strtoul(arg, &end, 0);
696        if (*end)
697            return EXT2_ET_INVALID_ARGUMENT;
698        data->offset = tmp;
699        return 0;
700    }
701    return EXT2_ET_INVALID_ARGUMENT;
702}
Note: See TracBrowser for help on using the repository browser.