Whamcloud - gitweb
libext2fs: fix unix_io's Direct I/O support
[tools/e2fsprogs.git] / lib / ext2fs / unix_io.c
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 Library
14  * General Public License, version 2.
15  * %End-Header%
16  */
17
18 #if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
19 #define _XOPEN_SOURCE 600
20 #define _DARWIN_C_SOURCE
21 #define _FILE_OFFSET_BITS 64
22 #ifndef _LARGEFILE_SOURCE
23 #define _LARGEFILE_SOURCE
24 #endif
25 #ifndef _LARGEFILE64_SOURCE
26 #define _LARGEFILE64_SOURCE
27 #endif
28 #ifndef _GNU_SOURCE
29 #define _GNU_SOURCE
30 #endif
31 #endif
32
33 #include "config.h"
34 #include <stdio.h>
35 #include <string.h>
36 #if HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #if HAVE_ERRNO_H
40 #include <errno.h>
41 #endif
42 #include <fcntl.h>
43 #include <time.h>
44 #ifdef __linux__
45 #include <sys/utsname.h>
46 #endif
47 #if HAVE_SYS_TYPES_H
48 #include <sys/types.h>
49 #endif
50 #ifdef HAVE_SYS_IOCTL_H
51 #include <sys/ioctl.h>
52 #endif
53 #ifdef HAVE_SYS_MOUNT_H
54 #include <sys/mount.h>
55 #endif
56 #ifdef HAVE_SYS_PRCTL_H
57 #include <sys/prctl.h>
58 #else
59 #define PR_GET_DUMPABLE 3
60 #endif
61 #if HAVE_SYS_STAT_H
62 #include <sys/stat.h>
63 #endif
64 #if HAVE_SYS_RESOURCE_H
65 #include <sys/resource.h>
66 #endif
67 #if HAVE_LINUX_FALLOC_H
68 #include <linux/falloc.h>
69 #endif
70 #ifdef HAVE_PTHREAD
71 #include <pthread.h>
72 #endif
73
74 #if defined(__linux__) && defined(_IO) && !defined(BLKROGET)
75 #define BLKROGET   _IO(0x12, 94) /* Get read-only status (0 = read_write).  */
76 #endif
77
78 #undef ALIGN_DEBUG
79
80 #include "ext2_fs.h"
81 #include "ext2fs.h"
82 #include "ext2fsP.h"
83
84 /*
85  * For checking structure magic numbers...
86  */
87
88 #define EXT2_CHECK_MAGIC(struct, code) \
89           if ((struct)->magic != (code)) return (code)
90
91 struct unix_cache {
92         char                    *buf;
93         unsigned long long      block;
94         int                     access_time;
95         unsigned                dirty:1;
96         unsigned                in_use:1;
97 };
98
99 #define CACHE_SIZE 8
100 #define WRITE_DIRECT_SIZE 4     /* Must be smaller than CACHE_SIZE */
101 #define READ_DIRECT_SIZE 4      /* Should be smaller than CACHE_SIZE */
102
103 struct unix_private_data {
104         int     magic;
105         int     dev;
106         int     flags;
107         int     align;
108         int     access_time;
109         ext2_loff_t offset;
110         struct unix_cache cache[CACHE_SIZE];
111         void    *bounce;
112         struct struct_io_stats io_stats;
113 #ifdef HAVE_PTHREAD
114         pthread_mutex_t cache_mutex;
115         pthread_mutex_t bounce_mutex;
116         pthread_mutex_t stats_mutex;
117 #endif
118 };
119
120 #define IS_ALIGNED(n, align) ((((uintptr_t) n) & \
121                                ((uintptr_t) ((align)-1))) == 0)
122
123 typedef enum lock_kind {
124         CACHE_MTX, BOUNCE_MTX, STATS_MTX
125 } kind_t;
126
127 #ifdef HAVE_PTHREAD
128 static inline pthread_mutex_t *get_mutex(struct unix_private_data *data,
129                                          kind_t kind)
130 {
131         if (data->flags & IO_FLAG_THREADS) {
132                 switch (kind) {
133                 case CACHE_MTX:
134                         return &data->cache_mutex;
135                 case BOUNCE_MTX:
136                         return &data->bounce_mutex;
137                 case STATS_MTX:
138                         return &data->stats_mutex;
139                 }
140         }
141         return NULL;
142 }
143 #endif
144
145 static inline void mutex_lock(struct unix_private_data *data, kind_t kind)
146 {
147 #ifdef HAVE_PTHREAD
148         pthread_mutex_t *mtx = get_mutex(data,kind);
149
150         if (mtx)
151                 pthread_mutex_lock(mtx);
152 #endif
153 }
154
155 static inline void mutex_unlock(struct unix_private_data *data, kind_t kind)
156 {
157 #ifdef HAVE_PTHREAD
158         pthread_mutex_t *mtx = get_mutex(data,kind);
159
160         if (mtx)
161                 pthread_mutex_unlock(mtx);
162 #endif
163 }
164
165 static errcode_t unix_get_stats(io_channel channel, io_stats *stats)
166 {
167         errcode_t       retval = 0;
168
169         struct unix_private_data *data;
170
171         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
172         data = (struct unix_private_data *) channel->private_data;
173         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
174
175         if (stats) {
176                 mutex_lock(data, STATS_MTX);
177                 *stats = &data->io_stats;
178                 mutex_unlock(data, STATS_MTX);
179         }
180
181         return retval;
182 }
183
184 static char *safe_getenv(const char *arg)
185 {
186         if ((getuid() != geteuid()) || (getgid() != getegid()))
187                 return NULL;
188 #ifdef HAVE_PRCTL
189         if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
190                 return NULL;
191 #else
192 #if (defined(linux) && defined(SYS_prctl))
193         if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
194                 return NULL;
195 #endif
196 #endif
197
198 #if defined(HAVE_SECURE_GETENV)
199         return secure_getenv(arg);
200 #elif defined(HAVE___SECURE_GETENV)
201         return __secure_getenv(arg);
202 #else
203         return getenv(arg);
204 #endif
205 }
206
207 /*
208  * Here are the raw I/O functions
209  */
210 static errcode_t raw_read_blk(io_channel channel,
211                               struct unix_private_data *data,
212                               unsigned long long block,
213                               int count, void *bufv)
214 {
215         errcode_t       retval;
216         ssize_t         size;
217         ext2_loff_t     location;
218         int             actual = 0;
219         unsigned char   *buf = bufv;
220         ssize_t         really_read = 0;
221         unsigned long long aligned_blk;
222         int             align_size, offset;
223
224         size = (count < 0) ? -count : (ext2_loff_t) count * channel->block_size;
225         mutex_lock(data, STATS_MTX);
226         data->io_stats.bytes_read += size;
227         mutex_unlock(data, STATS_MTX);
228         location = ((ext2_loff_t) block * channel->block_size) + data->offset;
229
230         if (data->flags & IO_FLAG_FORCE_BOUNCE) {
231                 if (ext2fs_llseek(data->dev, location, SEEK_SET) < 0) {
232                         retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
233                         goto error_out;
234                 }
235                 goto bounce_read;
236         }
237
238 #ifdef HAVE_PREAD64
239         /* Try an aligned pread */
240         if ((channel->align == 0) ||
241             (IS_ALIGNED(buf, channel->align) &&
242              IS_ALIGNED(location, channel->align) &&
243              IS_ALIGNED(size, channel->align))) {
244                 actual = pread64(data->dev, buf, size, location);
245                 if (actual == size)
246                         return 0;
247                 actual = 0;
248         }
249 #elif HAVE_PREAD
250         /* Try an aligned pread */
251         if ((sizeof(off_t) >= sizeof(ext2_loff_t)) &&
252             ((channel->align == 0) ||
253              (IS_ALIGNED(buf, channel->align) &&
254               IS_ALIGNED(location, channel->align) &&
255               IS_ALIGNED(size, channel->align)))) {
256                 actual = pread(data->dev, buf, size, location);
257                 if (actual == size)
258                         return 0;
259                 actual = 0;
260         }
261 #endif /* HAVE_PREAD */
262
263         if (ext2fs_llseek(data->dev, location, SEEK_SET) < 0) {
264                 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
265                 goto error_out;
266         }
267         if ((channel->align == 0) ||
268             (IS_ALIGNED(buf, channel->align) &&
269              IS_ALIGNED(location, channel->align) &&
270              IS_ALIGNED(size, channel->align))) {
271                 actual = read(data->dev, buf, size);
272                 if (actual != size) {
273                 short_read:
274                         if (actual < 0) {
275                                 retval = errno;
276                                 actual = 0;
277                         } else
278                                 retval = EXT2_ET_SHORT_READ;
279                         goto error_out;
280                 }
281                 return 0;
282         }
283
284 #ifdef ALIGN_DEBUG
285         printf("raw_read_blk: O_DIRECT fallback: %p %lu\n", buf,
286                (unsigned long) size);
287 #endif
288
289         /*
290          * The buffer or size which we're trying to read isn't aligned
291          * to the O_DIRECT rules, so we need to do this the hard way...
292          */
293 bounce_read:
294         if ((channel->block_size > channel->align) &&
295             (channel->block_size % channel->align) == 0)
296                 align_size = channel->block_size;
297         else
298                 align_size = channel->align;
299         aligned_blk = location / align_size;
300         offset = location % align_size;
301
302         if (ext2fs_llseek(data->dev, aligned_blk * align_size, SEEK_SET) < 0) {
303                 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
304                 goto error_out;
305         }
306         while (size > 0) {
307                 mutex_lock(data, BOUNCE_MTX);
308                 actual = read(data->dev, data->bounce, align_size);
309                 if (actual != align_size) {
310                         mutex_unlock(data, BOUNCE_MTX);
311                         actual = really_read;
312                         buf -= really_read;
313                         size += really_read;
314                         goto short_read;
315                 }
316                 if ((actual + offset) > align_size)
317                         actual = align_size - offset;
318                 if (actual > size)
319                         actual = size;
320                 memcpy(buf, data->bounce + offset, actual);
321
322                 really_read += actual;
323                 size -= actual;
324                 buf += actual;
325                 offset = 0;
326                 aligned_blk++;
327                 mutex_unlock(data, BOUNCE_MTX);
328         }
329         return 0;
330
331 error_out:
332         if (actual >= 0 && actual < size)
333                 memset((char *) buf+actual, 0, size-actual);
334         if (channel->read_error)
335                 retval = (channel->read_error)(channel, block, count, buf,
336                                                size, actual, retval);
337         return retval;
338 }
339
340 static errcode_t raw_write_blk(io_channel channel,
341                                struct unix_private_data *data,
342                                unsigned long long block,
343                                int count, const void *bufv)
344 {
345         ssize_t         size;
346         ext2_loff_t     location;
347         int             actual = 0;
348         errcode_t       retval;
349         const unsigned char *buf = bufv;
350         unsigned long long aligned_blk;
351         int             align_size, offset;
352
353         if (count == 1)
354                 size = channel->block_size;
355         else {
356                 if (count < 0)
357                         size = -count;
358                 else
359                         size = (ext2_loff_t) count * channel->block_size;
360         }
361         mutex_lock(data, STATS_MTX);
362         data->io_stats.bytes_written += size;
363         mutex_unlock(data, STATS_MTX);
364
365         location = ((ext2_loff_t) block * channel->block_size) + data->offset;
366
367         if (data->flags & IO_FLAG_FORCE_BOUNCE) {
368                 if (ext2fs_llseek(data->dev, location, SEEK_SET) < 0) {
369                         retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
370                         goto error_out;
371                 }
372                 goto bounce_write;
373         }
374
375 #ifdef HAVE_PWRITE64
376         /* Try an aligned pwrite */
377         if ((channel->align == 0) ||
378             (IS_ALIGNED(buf, channel->align) &&
379              IS_ALIGNED(location, channel->align) &&
380              IS_ALIGNED(size, channel->align))) {
381                 actual = pwrite64(data->dev, buf, size, location);
382                 if (actual == size)
383                         return 0;
384         }
385 #elif HAVE_PWRITE
386         /* Try an aligned pwrite */
387         if ((sizeof(off_t) >= sizeof(ext2_loff_t)) &&
388             ((channel->align == 0) ||
389              (IS_ALIGNED(buf, channel->align) &&
390               IS_ALIGNED(location, channel->align) &&
391               IS_ALIGNED(size, channel->align)))) {
392                 actual = pwrite(data->dev, buf, size, location);
393                 if (actual == size)
394                         return 0;
395         }
396 #endif /* HAVE_PWRITE */
397
398         if (ext2fs_llseek(data->dev, location, SEEK_SET) < 0) {
399                 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
400                 goto error_out;
401         }
402
403         if ((channel->align == 0) ||
404             (IS_ALIGNED(buf, channel->align) &&
405              IS_ALIGNED(location, channel->align) &&
406              IS_ALIGNED(size, channel->align))) {
407                 actual = write(data->dev, buf, size);
408                 if (actual < 0) {
409                         retval = errno;
410                         goto error_out;
411                 }
412                 if (actual != size) {
413                 short_write:
414                         retval = EXT2_ET_SHORT_WRITE;
415                         goto error_out;
416                 }
417                 return 0;
418         }
419
420 #ifdef ALIGN_DEBUG
421         printf("raw_write_blk: O_DIRECT fallback: %p %lu\n", buf,
422                (unsigned long) size);
423 #endif
424         /*
425          * The buffer or size which we're trying to write isn't aligned
426          * to the O_DIRECT rules, so we need to do this the hard way...
427          */
428 bounce_write:
429         if ((channel->block_size > channel->align) &&
430             (channel->block_size % channel->align) == 0)
431                 align_size = channel->block_size;
432         else
433                 align_size = channel->align;
434         aligned_blk = location / align_size;
435         offset = location % align_size;
436
437         while (size > 0) {
438                 int actual_w;
439
440                 mutex_lock(data, BOUNCE_MTX);
441                 if (size < align_size || offset) {
442                         if (ext2fs_llseek(data->dev, aligned_blk * align_size,
443                                           SEEK_SET) < 0) {
444                                 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
445                                 goto error_out;
446                         }
447                         actual = read(data->dev, data->bounce,
448                                       align_size);
449                         if (actual != align_size) {
450                                 if (actual < 0) {
451                                         mutex_unlock(data, BOUNCE_MTX);
452                                         retval = errno;
453                                         goto error_out;
454                                 }
455                                 memset((char *) data->bounce + actual, 0,
456                                        align_size - actual);
457                         }
458                 }
459                 actual = size;
460                 if ((actual + offset) > align_size)
461                         actual = align_size - offset;
462                 if (actual > size)
463                         actual = size;
464                 memcpy(((char *)data->bounce) + offset, buf, actual);
465                 if (ext2fs_llseek(data->dev, aligned_blk * align_size, SEEK_SET) < 0) {
466                         retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
467                         goto error_out;
468                 }
469                 actual_w = write(data->dev, data->bounce, align_size);
470                 mutex_unlock(data, BOUNCE_MTX);
471                 if (actual_w < 0) {
472                         retval = errno;
473                         goto error_out;
474                 }
475                 if (actual_w != align_size)
476                         goto short_write;
477                 size -= actual;
478                 buf += actual;
479                 location += actual;
480                 aligned_blk++;
481                 offset = 0;
482         }
483         return 0;
484
485 error_out:
486         if (channel->write_error)
487                 retval = (channel->write_error)(channel, block, count, buf,
488                                                 size, actual, retval);
489         return retval;
490 }
491
492
493 /*
494  * Here we implement the cache functions
495  */
496
497 /* Allocate the cache buffers */
498 static errcode_t alloc_cache(io_channel channel,
499                              struct unix_private_data *data)
500 {
501         errcode_t               retval;
502         struct unix_cache       *cache;
503         int                     i;
504
505         data->access_time = 0;
506         for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
507                 cache->block = 0;
508                 cache->access_time = 0;
509                 cache->dirty = 0;
510                 cache->in_use = 0;
511                 if (cache->buf)
512                         ext2fs_free_mem(&cache->buf);
513                 retval = io_channel_alloc_buf(channel, 0, &cache->buf);
514                 if (retval)
515                         return retval;
516         }
517         if (channel->align || data->flags & IO_FLAG_FORCE_BOUNCE) {
518                 if (data->bounce)
519                         ext2fs_free_mem(&data->bounce);
520                 retval = io_channel_alloc_buf(channel, 0, &data->bounce);
521         }
522         return retval;
523 }
524
525 /* Free the cache buffers */
526 static void free_cache(struct unix_private_data *data)
527 {
528         struct unix_cache       *cache;
529         int                     i;
530
531         data->access_time = 0;
532         for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
533                 cache->block = 0;
534                 cache->access_time = 0;
535                 cache->dirty = 0;
536                 cache->in_use = 0;
537                 if (cache->buf)
538                         ext2fs_free_mem(&cache->buf);
539         }
540         if (data->bounce)
541                 ext2fs_free_mem(&data->bounce);
542 }
543
544 #ifndef NO_IO_CACHE
545 /*
546  * Try to find a block in the cache.  If the block is not found, and
547  * eldest is a non-zero pointer, then fill in eldest with the cache
548  * entry to that should be reused.
549  */
550 static struct unix_cache *find_cached_block(struct unix_private_data *data,
551                                             unsigned long long block,
552                                             struct unix_cache **eldest)
553 {
554         struct unix_cache       *cache, *unused_cache, *oldest_cache;
555         int                     i;
556
557         unused_cache = oldest_cache = 0;
558         for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
559                 if (!cache->in_use) {
560                         if (!unused_cache)
561                                 unused_cache = cache;
562                         continue;
563                 }
564                 if (cache->block == block) {
565                         cache->access_time = ++data->access_time;
566                         return cache;
567                 }
568                 if (!oldest_cache ||
569                     (cache->access_time < oldest_cache->access_time))
570                         oldest_cache = cache;
571         }
572         if (eldest)
573                 *eldest = (unused_cache) ? unused_cache : oldest_cache;
574         return 0;
575 }
576
577 /*
578  * Reuse a particular cache entry for another block.
579  */
580 static void reuse_cache(io_channel channel, struct unix_private_data *data,
581                  struct unix_cache *cache, unsigned long long block)
582 {
583         if (cache->dirty && cache->in_use)
584                 raw_write_blk(channel, data, cache->block, 1, cache->buf);
585
586         cache->in_use = 1;
587         cache->dirty = 0;
588         cache->block = block;
589         cache->access_time = ++data->access_time;
590 }
591
592 #define FLUSH_INVALIDATE        0x01
593 #define FLUSH_NOLOCK            0x02
594
595 /*
596  * Flush all of the blocks in the cache
597  */
598 static errcode_t flush_cached_blocks(io_channel channel,
599                                      struct unix_private_data *data,
600                                      int flags)
601 {
602         struct unix_cache       *cache;
603         errcode_t               retval, retval2;
604         int                     i;
605
606         retval2 = 0;
607         if ((flags & FLUSH_NOLOCK) == 0)
608                 mutex_lock(data, CACHE_MTX);
609         for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
610                 if (!cache->in_use)
611                         continue;
612
613                 if (flags & FLUSH_INVALIDATE)
614                         cache->in_use = 0;
615
616                 if (!cache->dirty)
617                         continue;
618
619                 retval = raw_write_blk(channel, data,
620                                        cache->block, 1, cache->buf);
621                 if (retval)
622                         retval2 = retval;
623                 else
624                         cache->dirty = 0;
625         }
626         if ((flags & FLUSH_NOLOCK) == 0)
627                 mutex_unlock(data, CACHE_MTX);
628         return retval2;
629 }
630 #endif /* NO_IO_CACHE */
631
632 #ifdef __linux__
633 #ifndef BLKDISCARDZEROES
634 #define BLKDISCARDZEROES _IO(0x12,124)
635 #endif
636 #endif
637
638 int ext2fs_open_file(const char *pathname, int flags, mode_t mode)
639 {
640         if (mode)
641 #if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
642                 return open64(pathname, flags, mode);
643         else
644                 return open64(pathname, flags);
645 #else
646                 return open(pathname, flags, mode);
647         else
648                 return open(pathname, flags);
649 #endif
650 }
651
652 int ext2fs_stat(const char *path, ext2fs_struct_stat *buf)
653 {
654 #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
655         return stat64(path, buf);
656 #else
657         return stat(path, buf);
658 #endif
659 }
660
661 int ext2fs_fstat(int fd, ext2fs_struct_stat *buf)
662 {
663 #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
664         return fstat64(fd, buf);
665 #else
666         return fstat(fd, buf);
667 #endif
668 }
669
670
671 static errcode_t unix_open_channel(const char *name, int fd,
672                                    int flags, io_channel *channel,
673                                    io_manager io_mgr)
674 {
675         io_channel      io = NULL;
676         struct unix_private_data *data = NULL;
677         errcode_t       retval;
678         ext2fs_struct_stat st;
679 #ifdef __linux__
680         struct          utsname ut;
681 #endif
682
683         if (safe_getenv("UNIX_IO_FORCE_BOUNCE"))
684                 flags |= IO_FLAG_FORCE_BOUNCE;
685
686 #ifdef __linux__
687         /*
688          * We need to make sure any previous errors in the block
689          * device are thrown away, sigh.
690          */
691         (void) fsync(fd);
692 #endif
693
694         retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
695         if (retval)
696                 goto cleanup;
697         memset(io, 0, sizeof(struct struct_io_channel));
698         io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
699         retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data);
700         if (retval)
701                 goto cleanup;
702
703         io->manager = io_mgr;
704         retval = ext2fs_get_mem(strlen(name)+1, &io->name);
705         if (retval)
706                 goto cleanup;
707
708         strcpy(io->name, name);
709         io->private_data = data;
710         io->block_size = 1024;
711         io->read_error = 0;
712         io->write_error = 0;
713         io->refcount = 1;
714         io->flags = 0;
715
716         memset(data, 0, sizeof(struct unix_private_data));
717         data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
718         data->io_stats.num_fields = 2;
719         data->flags = flags;
720         data->dev = fd;
721
722 #if defined(O_DIRECT)
723         if (flags & IO_FLAG_DIRECT_IO)
724                 io->align = ext2fs_get_dio_alignment(data->dev);
725 #elif defined(F_NOCACHE)
726         if (flags & IO_FLAG_DIRECT_IO)
727                 io->align = 4096;
728 #endif
729
730         /*
731          * If the device is really a block device, then set the
732          * appropriate flag, otherwise we can set DISCARD_ZEROES flag
733          * because we are going to use punch hole instead of discard
734          * and if it succeed, subsequent read from sparse area returns
735          * zero.
736          */
737         if (ext2fs_fstat(data->dev, &st) == 0) {
738                 if (ext2fsP_is_disk_device(st.st_mode))
739                         io->flags |= CHANNEL_FLAGS_BLOCK_DEVICE;
740                 else
741                         io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
742         }
743
744 #ifdef BLKDISCARDZEROES
745         {
746                 int zeroes = 0;
747                 if (ioctl(data->dev, BLKDISCARDZEROES, &zeroes) == 0 &&
748                     zeroes)
749                         io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
750         }
751 #endif
752
753 #if defined(__CYGWIN__)
754         /*
755          * Some operating systems require that the buffers be aligned,
756          * regardless of O_DIRECT
757          */
758         if (!io->align)
759                 io->align = 512;
760 #endif
761
762 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
763         if (io->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
764                 int dio_align = ext2fs_get_dio_alignment(fd);
765
766                 if (io->align < dio_align)
767                         io->align = dio_align;
768         }
769 #endif
770
771         if ((retval = alloc_cache(io, data)))
772                 goto cleanup;
773
774 #ifdef BLKROGET
775         if (flags & IO_FLAG_RW) {
776                 int error;
777                 int readonly = 0;
778
779                 /* Is the block device actually writable? */
780                 error = ioctl(data->dev, BLKROGET, &readonly);
781                 if (!error && readonly) {
782                         retval = EPERM;
783                         goto cleanup;
784                 }
785         }
786 #endif
787
788 #ifdef __linux__
789 #undef RLIM_INFINITY
790 #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4)))
791 #define RLIM_INFINITY   ((unsigned long)(~0UL>>1))
792 #else
793 #define RLIM_INFINITY  (~0UL)
794 #endif
795         /*
796          * Work around a bug in 2.4.10-2.4.18 kernels where writes to
797          * block devices are wrongly getting hit by the filesize
798          * limit.  This workaround isn't perfect, since it won't work
799          * if glibc wasn't built against 2.2 header files.  (Sigh.)
800          *
801          */
802         if ((flags & IO_FLAG_RW) &&
803             (uname(&ut) == 0) &&
804             ((ut.release[0] == '2') && (ut.release[1] == '.') &&
805              (ut.release[2] == '4') && (ut.release[3] == '.') &&
806              (ut.release[4] == '1') && (ut.release[5] >= '0') &&
807              (ut.release[5] < '8')) &&
808             (ext2fs_fstat(data->dev, &st) == 0) &&
809             (ext2fsP_is_disk_device(st.st_mode))) {
810                 struct rlimit   rlim;
811
812                 rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY;
813                 setrlimit(RLIMIT_FSIZE, &rlim);
814                 getrlimit(RLIMIT_FSIZE, &rlim);
815                 if (((unsigned long) rlim.rlim_cur) <
816                     ((unsigned long) rlim.rlim_max)) {
817                         rlim.rlim_cur = rlim.rlim_max;
818                         setrlimit(RLIMIT_FSIZE, &rlim);
819                 }
820         }
821 #endif
822 #ifdef HAVE_PTHREAD
823         if (flags & IO_FLAG_THREADS) {
824                 io->flags |= CHANNEL_FLAGS_THREADS;
825                 retval = pthread_mutex_init(&data->cache_mutex, NULL);
826                 if (retval)
827                         goto cleanup;
828                 retval = pthread_mutex_init(&data->bounce_mutex, NULL);
829                 if (retval) {
830                         pthread_mutex_destroy(&data->cache_mutex);
831                         goto cleanup;
832                 }
833                 retval = pthread_mutex_init(&data->stats_mutex, NULL);
834                 if (retval) {
835                         pthread_mutex_destroy(&data->cache_mutex);
836                         pthread_mutex_destroy(&data->bounce_mutex);
837                         goto cleanup;
838                 }
839         }
840 #endif
841         *channel = io;
842         return 0;
843
844 cleanup:
845         if (data) {
846                 if (data->dev >= 0)
847                         close(data->dev);
848                 free_cache(data);
849                 ext2fs_free_mem(&data);
850         }
851         if (io) {
852                 if (io->name) {
853                         ext2fs_free_mem(&io->name);
854                 }
855                 ext2fs_free_mem(&io);
856         }
857         return retval;
858 }
859
860 static errcode_t unixfd_open(const char *str_fd, int flags,
861                              io_channel *channel)
862 {
863         int fd;
864         int fd_flags;
865
866         fd = atoi(str_fd);
867 #if defined(HAVE_FCNTL)
868         fd_flags = fcntl(fd, F_GETFD);
869         if (fd_flags == -1)
870                 return EBADF;
871
872         flags = 0;
873         if (fd_flags & O_RDWR)
874                 flags |= IO_FLAG_RW;
875         if (fd_flags & O_EXCL)
876                 flags |= IO_FLAG_EXCLUSIVE;
877 #if defined(O_DIRECT)
878         if (fd_flags & O_DIRECT)
879                 flags |= IO_FLAG_DIRECT_IO;
880 #endif
881 #endif  /* HAVE_FCNTL */
882
883         return unix_open_channel(str_fd, fd, flags, channel, unixfd_io_manager);
884 }
885
886 static errcode_t unix_open(const char *name, int flags,
887                            io_channel *channel)
888 {
889         int fd = -1;
890         int open_flags;
891
892         if (name == 0)
893                 return EXT2_ET_BAD_DEVICE_NAME;
894
895         open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
896         if (flags & IO_FLAG_EXCLUSIVE)
897                 open_flags |= O_EXCL;
898 #if defined(O_DIRECT)
899         if (flags & IO_FLAG_DIRECT_IO)
900                 open_flags |= O_DIRECT;
901 #endif
902         fd = ext2fs_open_file(name, open_flags, 0);
903         if (fd < 0)
904                 return errno;
905 #if defined(F_NOCACHE) && !defined(IO_DIRECT)
906         if (flags & IO_FLAG_DIRECT_IO) {
907                 if (fcntl(fd, F_NOCACHE, 1) < 0)
908                         return errno;
909         }
910 #endif
911         return unix_open_channel(name, fd, flags, channel, unix_io_manager);
912 }
913
914 static errcode_t unix_close(io_channel channel)
915 {
916         struct unix_private_data *data;
917         errcode_t       retval = 0;
918
919         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
920         data = (struct unix_private_data *) channel->private_data;
921         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
922
923         if (--channel->refcount > 0)
924                 return 0;
925
926 #ifndef NO_IO_CACHE
927         retval = flush_cached_blocks(channel, data, 0);
928 #endif
929
930         if (close(data->dev) < 0)
931                 retval = errno;
932         free_cache(data);
933 #ifdef HAVE_PTHREAD
934         if (data->flags & IO_FLAG_THREADS) {
935                 pthread_mutex_destroy(&data->cache_mutex);
936                 pthread_mutex_destroy(&data->bounce_mutex);
937                 pthread_mutex_destroy(&data->stats_mutex);
938         }
939 #endif
940
941         ext2fs_free_mem(&channel->private_data);
942         if (channel->name)
943                 ext2fs_free_mem(&channel->name);
944         ext2fs_free_mem(&channel);
945         return retval;
946 }
947
948 static errcode_t unix_set_blksize(io_channel channel, int blksize)
949 {
950         struct unix_private_data *data;
951         errcode_t               retval = 0;
952
953         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
954         data = (struct unix_private_data *) channel->private_data;
955         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
956
957         if (channel->block_size != blksize) {
958                 mutex_lock(data, CACHE_MTX);
959                 mutex_lock(data, BOUNCE_MTX);
960 #ifndef NO_IO_CACHE
961                 if ((retval = flush_cached_blocks(channel, data, FLUSH_NOLOCK)))
962                         return retval;
963 #endif
964
965                 channel->block_size = blksize;
966                 free_cache(data);
967                 retval = alloc_cache(channel, data);
968                 mutex_unlock(data, BOUNCE_MTX);
969                 mutex_unlock(data, CACHE_MTX);
970         }
971         return retval;
972 }
973
974 static errcode_t unix_read_blk64(io_channel channel, unsigned long long block,
975                                int count, void *buf)
976 {
977         struct unix_private_data *data;
978         struct unix_cache *cache, *reuse[READ_DIRECT_SIZE];
979         errcode_t       retval = 0;
980         char            *cp;
981         int             i, j;
982
983         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
984         data = (struct unix_private_data *) channel->private_data;
985         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
986
987 #ifdef NO_IO_CACHE
988         return raw_read_blk(channel, data, block, count, buf);
989 #else
990         if (data->flags & IO_FLAG_NOCACHE)
991                 return raw_read_blk(channel, data, block, count, buf);
992         /*
993          * If we're doing an odd-sized read or a very large read,
994          * flush out the cache and then do a direct read.
995          */
996         if (count < 0 || count > WRITE_DIRECT_SIZE) {
997                 if ((retval = flush_cached_blocks(channel, data, 0)))
998                         return retval;
999                 return raw_read_blk(channel, data, block, count, buf);
1000         }
1001
1002         cp = buf;
1003         mutex_lock(data, CACHE_MTX);
1004         while (count > 0) {
1005                 /* If it's in the cache, use it! */
1006                 if ((cache = find_cached_block(data, block, &reuse[0]))) {
1007 #ifdef DEBUG
1008                         printf("Using cached block %lu\n", block);
1009 #endif
1010                         memcpy(cp, cache->buf, channel->block_size);
1011                         count--;
1012                         block++;
1013                         cp += channel->block_size;
1014                         continue;
1015                 }
1016                 if (count == 1) {
1017                         /*
1018                          * Special case where we read directly into the
1019                          * cache buffer; important in the O_DIRECT case
1020                          */
1021                         cache = reuse[0];
1022                         reuse_cache(channel, data, cache, block);
1023                         if ((retval = raw_read_blk(channel, data, block, 1,
1024                                                    cache->buf))) {
1025                                 cache->in_use = 0;
1026                                 break;
1027                         }
1028                         memcpy(cp, cache->buf, channel->block_size);
1029                         retval = 0;
1030                         break;
1031                 }
1032
1033                 /*
1034                  * Find the number of uncached blocks so we can do a
1035                  * single read request
1036                  */
1037                 for (i=1; i < count; i++)
1038                         if (find_cached_block(data, block+i, &reuse[i]))
1039                                 break;
1040 #ifdef DEBUG
1041                 printf("Reading %d blocks starting at %lu\n", i, block);
1042 #endif
1043                 if ((retval = raw_read_blk(channel, data, block, i, cp)))
1044                         break;
1045
1046                 /* Save the results in the cache */
1047                 for (j=0; j < i; j++) {
1048                         count--;
1049                         cache = reuse[j];
1050                         reuse_cache(channel, data, cache, block++);
1051                         memcpy(cache->buf, cp, channel->block_size);
1052                         cp += channel->block_size;
1053                 }
1054         }
1055         mutex_unlock(data, CACHE_MTX);
1056         return retval;
1057 #endif /* NO_IO_CACHE */
1058 }
1059
1060 static errcode_t unix_read_blk(io_channel channel, unsigned long block,
1061                                int count, void *buf)
1062 {
1063         return unix_read_blk64(channel, block, count, buf);
1064 }
1065
1066 static errcode_t unix_write_blk64(io_channel channel, unsigned long long block,
1067                                 int count, const void *buf)
1068 {
1069         struct unix_private_data *data;
1070         struct unix_cache *cache, *reuse;
1071         errcode_t       retval = 0;
1072         const char      *cp;
1073         int             writethrough;
1074
1075         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1076         data = (struct unix_private_data *) channel->private_data;
1077         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
1078
1079 #ifdef NO_IO_CACHE
1080         return raw_write_blk(channel, data, block, count, buf);
1081 #else
1082         if (data->flags & IO_FLAG_NOCACHE)
1083                 return raw_write_blk(channel, data, block, count, buf);
1084         /*
1085          * If we're doing an odd-sized write or a very large write,
1086          * flush out the cache completely and then do a direct write.
1087          */
1088         if (count < 0 || count > WRITE_DIRECT_SIZE) {
1089                 if ((retval = flush_cached_blocks(channel, data,
1090                                                   FLUSH_INVALIDATE)))
1091                         return retval;
1092                 return raw_write_blk(channel, data, block, count, buf);
1093         }
1094
1095         /*
1096          * For a moderate-sized multi-block write, first force a write
1097          * if we're in write-through cache mode, and then fill the
1098          * cache with the blocks.
1099          */
1100         writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
1101         if (writethrough)
1102                 retval = raw_write_blk(channel, data, block, count, buf);
1103
1104         cp = buf;
1105         mutex_lock(data, CACHE_MTX);
1106         while (count > 0) {
1107                 cache = find_cached_block(data, block, &reuse);
1108                 if (!cache) {
1109                         cache = reuse;
1110                         reuse_cache(channel, data, cache, block);
1111                 }
1112                 if (cache->buf != cp)
1113                         memcpy(cache->buf, cp, channel->block_size);
1114                 cache->dirty = !writethrough;
1115                 count--;
1116                 block++;
1117                 cp += channel->block_size;
1118         }
1119         mutex_unlock(data, CACHE_MTX);
1120         return retval;
1121 #endif /* NO_IO_CACHE */
1122 }
1123
1124 static errcode_t unix_cache_readahead(io_channel channel,
1125                                       unsigned long long block,
1126                                       unsigned long long count)
1127 {
1128 #ifdef POSIX_FADV_WILLNEED
1129         struct unix_private_data *data;
1130
1131         data = (struct unix_private_data *)channel->private_data;
1132         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
1133         return posix_fadvise(data->dev,
1134                              (ext2_loff_t)block * channel->block_size + data->offset,
1135                              (ext2_loff_t)count * channel->block_size,
1136                              POSIX_FADV_WILLNEED);
1137 #else
1138         return EXT2_ET_OP_NOT_SUPPORTED;
1139 #endif
1140 }
1141
1142 static errcode_t unix_write_blk(io_channel channel, unsigned long block,
1143                                 int count, const void *buf)
1144 {
1145         return unix_write_blk64(channel, block, count, buf);
1146 }
1147
1148 static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
1149                                  int size, const void *buf)
1150 {
1151         struct unix_private_data *data;
1152         errcode_t       retval = 0;
1153         ssize_t         actual;
1154
1155         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1156         data = (struct unix_private_data *) channel->private_data;
1157         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
1158
1159         if (channel->align != 0) {
1160 #ifdef ALIGN_DEBUG
1161                 printf("unix_write_byte: O_DIRECT fallback\n");
1162 #endif
1163                 return EXT2_ET_UNIMPLEMENTED;
1164         }
1165
1166 #ifndef NO_IO_CACHE
1167         /*
1168          * Flush out the cache completely
1169          */
1170         if ((retval = flush_cached_blocks(channel, data, FLUSH_INVALIDATE)))
1171                 return retval;
1172 #endif
1173
1174         if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0)
1175                 return errno;
1176
1177         actual = write(data->dev, buf, size);
1178         if (actual < 0)
1179                 return errno;
1180         if (actual != size)
1181                 return EXT2_ET_SHORT_WRITE;
1182
1183         return 0;
1184 }
1185
1186 /*
1187  * Flush data buffers to disk.
1188  */
1189 static errcode_t unix_flush(io_channel channel)
1190 {
1191         struct unix_private_data *data;
1192         errcode_t retval = 0;
1193
1194         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1195         data = (struct unix_private_data *) channel->private_data;
1196         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
1197
1198 #ifndef NO_IO_CACHE
1199         retval = flush_cached_blocks(channel, data, 0);
1200 #endif
1201 #ifdef HAVE_FSYNC
1202         if (!retval && fsync(data->dev) != 0)
1203                 return errno;
1204 #endif
1205         return retval;
1206 }
1207
1208 static errcode_t unix_set_option(io_channel channel, const char *option,
1209                                  const char *arg)
1210 {
1211         struct unix_private_data *data;
1212         unsigned long long tmp;
1213         errcode_t retval;
1214         char *end;
1215
1216         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1217         data = (struct unix_private_data *) channel->private_data;
1218         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
1219
1220         if (!strcmp(option, "offset")) {
1221                 if (!arg)
1222                         return EXT2_ET_INVALID_ARGUMENT;
1223
1224                 tmp = strtoull(arg, &end, 0);
1225                 if (*end)
1226                         return EXT2_ET_INVALID_ARGUMENT;
1227                 data->offset = tmp;
1228                 if (data->offset < 0)
1229                         return EXT2_ET_INVALID_ARGUMENT;
1230                 return 0;
1231         }
1232         if (!strcmp(option, "cache")) {
1233                 if (!arg)
1234                         return EXT2_ET_INVALID_ARGUMENT;
1235                 if (!strcmp(arg, "on")) {
1236                         data->flags &= ~IO_FLAG_NOCACHE;
1237                         return 0;
1238                 }
1239                 if (!strcmp(arg, "off")) {
1240                         retval = flush_cached_blocks(channel, data, 0);
1241                         data->flags |= IO_FLAG_NOCACHE;
1242                         return retval;
1243                 }
1244                 return EXT2_ET_INVALID_ARGUMENT;
1245         }
1246         return EXT2_ET_INVALID_ARGUMENT;
1247 }
1248
1249 #if defined(__linux__) && !defined(BLKDISCARD)
1250 #define BLKDISCARD              _IO(0x12,119)
1251 #endif
1252
1253 static errcode_t unix_discard(io_channel channel, unsigned long long block,
1254                               unsigned long long count)
1255 {
1256         struct unix_private_data *data;
1257         int             ret;
1258
1259         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1260         data = (struct unix_private_data *) channel->private_data;
1261         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
1262
1263         if (channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
1264 #ifdef BLKDISCARD
1265                 __u64 range[2];
1266
1267                 range[0] = (__u64)(block) * channel->block_size + data->offset;
1268                 range[1] = (__u64)(count) * channel->block_size;
1269
1270                 ret = ioctl(data->dev, BLKDISCARD, &range);
1271 #else
1272                 goto unimplemented;
1273 #endif
1274         } else {
1275 #if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
1276                 /*
1277                  * If we are not on block device, try to use punch hole
1278                  * to reclaim free space.
1279                  */
1280                 ret = fallocate(data->dev,
1281                                 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
1282                                 (off_t)(block) * channel->block_size + data->offset,
1283                                 (off_t)(count) * channel->block_size);
1284 #else
1285                 goto unimplemented;
1286 #endif
1287         }
1288         if (ret < 0) {
1289                 if (errno == EOPNOTSUPP)
1290                         goto unimplemented;
1291                 return errno;
1292         }
1293         return 0;
1294 unimplemented:
1295         return EXT2_ET_UNIMPLEMENTED;
1296 }
1297
1298 /*
1299  * If we know about ZERO_RANGE, try that before we try PUNCH_HOLE because
1300  * ZERO_RANGE doesn't unmap preallocated blocks.  We prefer fallocate because
1301  * it always invalidates page cache, and libext2fs requires that reads after
1302  * ZERO_RANGE return zeroes.
1303  */
1304 static int __unix_zeroout(int fd, off_t offset, off_t len)
1305 {
1306         int ret = -1;
1307
1308 #if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_ZERO_RANGE)
1309         ret = fallocate(fd, FALLOC_FL_ZERO_RANGE, offset, len);
1310         if (ret == 0)
1311                 return 0;
1312 #endif
1313 #if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)
1314         ret = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
1315                         offset,  len);
1316         if (ret == 0)
1317                 return 0;
1318 #endif
1319         errno = EOPNOTSUPP;
1320         return ret;
1321 }
1322
1323 /* parameters might not be used if OS doesn't support zeroout */
1324 #if __GNUC_PREREQ (4, 6)
1325 #pragma GCC diagnostic push
1326 #pragma GCC diagnostic ignored "-Wunused-parameter"
1327 #endif
1328 static errcode_t unix_zeroout(io_channel channel, unsigned long long block,
1329                               unsigned long long count)
1330 {
1331         struct unix_private_data *data;
1332         int             ret;
1333
1334         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1335         data = (struct unix_private_data *) channel->private_data;
1336         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
1337
1338         if (safe_getenv("UNIX_IO_NOZEROOUT"))
1339                 goto unimplemented;
1340
1341         if (!(channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE)) {
1342                 /* Regular file, try to use truncate/punch/zero. */
1343                 struct stat statbuf;
1344
1345                 if (count == 0)
1346                         return 0;
1347                 /*
1348                  * If we're trying to zero a range past the end of the file,
1349                  * extend the file size, then truncate everything.
1350                  */
1351                 ret = fstat(data->dev, &statbuf);
1352                 if (ret)
1353                         goto err;
1354                 if ((unsigned long long) statbuf.st_size <
1355                         (block + count) * channel->block_size + data->offset) {
1356                         ret = ftruncate(data->dev,
1357                                         (block + count) * channel->block_size + data->offset);
1358                         if (ret)
1359                                 goto err;
1360                 }
1361         }
1362
1363         ret = __unix_zeroout(data->dev,
1364                         (off_t)(block) * channel->block_size + data->offset,
1365                         (off_t)(count) * channel->block_size);
1366 err:
1367         if (ret < 0) {
1368                 if (errno == EOPNOTSUPP)
1369                         goto unimplemented;
1370                 return errno;
1371         }
1372         return 0;
1373 unimplemented:
1374         return EXT2_ET_UNIMPLEMENTED;
1375 }
1376 #if __GNUC_PREREQ (4, 6)
1377 #pragma GCC diagnostic pop
1378 #endif
1379
1380 static struct struct_io_manager struct_unix_manager = {
1381         .magic          = EXT2_ET_MAGIC_IO_MANAGER,
1382         .name           = "Unix I/O Manager",
1383         .open           = unix_open,
1384         .close          = unix_close,
1385         .set_blksize    = unix_set_blksize,
1386         .read_blk       = unix_read_blk,
1387         .write_blk      = unix_write_blk,
1388         .flush          = unix_flush,
1389         .write_byte     = unix_write_byte,
1390         .set_option     = unix_set_option,
1391         .get_stats      = unix_get_stats,
1392         .read_blk64     = unix_read_blk64,
1393         .write_blk64    = unix_write_blk64,
1394         .discard        = unix_discard,
1395         .cache_readahead        = unix_cache_readahead,
1396         .zeroout        = unix_zeroout,
1397 };
1398
1399 io_manager unix_io_manager = &struct_unix_manager;
1400
1401 static struct struct_io_manager struct_unixfd_manager = {
1402         .magic          = EXT2_ET_MAGIC_IO_MANAGER,
1403         .name           = "Unix fd I/O Manager",
1404         .open           = unixfd_open,
1405         .close          = unix_close,
1406         .set_blksize    = unix_set_blksize,
1407         .read_blk       = unix_read_blk,
1408         .write_blk      = unix_write_blk,
1409         .flush          = unix_flush,
1410         .write_byte     = unix_write_byte,
1411         .set_option     = unix_set_option,
1412         .get_stats      = unix_get_stats,
1413         .read_blk64     = unix_read_blk64,
1414         .write_blk64    = unix_write_blk64,
1415         .discard        = unix_discard,
1416         .cache_readahead        = unix_cache_readahead,
1417         .zeroout        = unix_zeroout,
1418 };
1419
1420 io_manager unixfd_io_manager = &struct_unixfd_manager;