1 /* bufio.c - buffered io access */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21 #include <grub/types.h>
23 #include <grub/misc.h>
25 #include <grub/bufio.h>
28 GRUB_MOD_LICENSE ("GPLv3+");
30 #define GRUB_BUFIO_DEF_SIZE 8192
31 #define GRUB_BUFIO_MAX_SIZE 1048576
36 grub_size_t block_size
;
37 grub_size_t buffer_len
;
41 typedef struct grub_bufio
*grub_bufio_t
;
43 static struct grub_fs grub_bufio_fs
;
46 grub_bufio_open (grub_file_t io
, grub_size_t size
)
49 grub_bufio_t bufio
= 0;
51 file
= (grub_file_t
) grub_zalloc (sizeof (*file
));
56 size
= GRUB_BUFIO_DEF_SIZE
;
57 else if (size
> GRUB_BUFIO_MAX_SIZE
)
58 size
= GRUB_BUFIO_MAX_SIZE
;
61 size
= ((io
->size
> GRUB_BUFIO_MAX_SIZE
) ? GRUB_BUFIO_MAX_SIZE
:
65 * Round up size to power of 2 which the binary math to
66 * calculate next_buf in grub_bufio_read() requires.
68 while (size
& (size
- 1))
69 size
= (size
| (size
- 1)) + 1;
71 bufio
= grub_zalloc (sizeof (struct grub_bufio
) + size
);
79 bufio
->block_size
= size
;
81 file
->device
= io
->device
;
82 file
->size
= io
->size
;
84 file
->fs
= &grub_bufio_fs
;
85 file
->not_easily_seekable
= io
->not_easily_seekable
;
91 grub_buffile_open (const char *name
, enum grub_file_type type
, grub_size_t size
)
95 io
= grub_file_open (name
, type
);
99 file
= grub_bufio_open (io
, size
);
102 grub_file_close (io
);
110 grub_bufio_read (grub_file_t file
, char *buf
, grub_size_t len
)
114 grub_bufio_t bufio
= file
->data
;
115 grub_ssize_t really_read
;
117 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
118 file
->size
= bufio
->file
->size
;
120 /* First part: use whatever we already have in the buffer. */
121 if ((file
->offset
>= bufio
->buffer_at
) &&
122 (file
->offset
< bufio
->buffer_at
+ bufio
->buffer_len
))
127 pos
= file
->offset
- bufio
->buffer_at
;
128 n
= bufio
->buffer_len
- pos
;
132 grub_memcpy (buf
, &bufio
->buffer
[pos
], n
);
141 /* Need to read some more. */
142 next_buf
= (file
->offset
+ res
+ len
- 1) & ~((grub_off_t
) bufio
->block_size
- 1);
143 /* Now read between file->offset + res and bufio->buffer_at. */
144 if (file
->offset
+ res
< next_buf
)
146 grub_size_t read_now
;
147 read_now
= next_buf
- (file
->offset
+ res
);
148 grub_file_seek (bufio
->file
, file
->offset
+ res
);
149 really_read
= grub_file_read (bufio
->file
, buf
, read_now
);
152 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
153 file
->size
= bufio
->file
->size
;
158 /* Partial read. File ended unexpectedly. Save the last chunk in buffer.
160 if (really_read
!= (grub_ssize_t
) read_now
)
162 bufio
->buffer_len
= really_read
;
163 if (bufio
->buffer_len
> bufio
->block_size
)
164 bufio
->buffer_len
= bufio
->block_size
;
165 bufio
->buffer_at
= file
->offset
+ res
- bufio
->buffer_len
;
166 grub_memcpy (&bufio
->buffer
[0], buf
- bufio
->buffer_len
,
172 /* Read into buffer. */
173 grub_file_seek (bufio
->file
, next_buf
);
174 really_read
= grub_file_read (bufio
->file
, bufio
->buffer
,
178 bufio
->buffer_at
= next_buf
;
179 bufio
->buffer_len
= really_read
;
181 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
182 file
->size
= bufio
->file
->size
;
184 if (len
> bufio
->buffer_len
)
185 len
= bufio
->buffer_len
;
186 grub_memcpy (buf
, &bufio
->buffer
[file
->offset
+ res
- next_buf
], len
);
193 grub_bufio_close (grub_file_t file
)
195 grub_bufio_t bufio
= file
->data
;
197 grub_file_close (bufio
->file
);
205 static struct grub_fs grub_bufio_fs
=
210 .fs_read
= grub_bufio_read
,
211 .fs_close
= grub_bufio_close
,