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
, int size
)
49 grub_bufio_t bufio
= 0;
51 file
= (grub_file_t
) grub_malloc (sizeof (*file
));
56 size
= GRUB_BUFIO_DEF_SIZE
;
57 else if (size
> GRUB_BUFIO_MAX_SIZE
)
58 size
= GRUB_BUFIO_MAX_SIZE
;
60 if ((size
< 0) || ((unsigned) size
> io
->size
))
61 size
= ((io
->size
> GRUB_BUFIO_MAX_SIZE
) ? GRUB_BUFIO_MAX_SIZE
:
64 bufio
= grub_malloc (sizeof (struct grub_bufio
) + size
);
72 bufio
->block_size
= size
;
73 bufio
->buffer_len
= 0;
76 file
->device
= io
->device
;
78 file
->size
= io
->size
;
81 file
->fs
= &grub_bufio_fs
;
82 file
->not_easily_seekable
= io
->not_easily_seekable
;
88 grub_buffile_open (const char *name
, int size
)
92 io
= grub_file_open (name
);
96 file
= grub_bufio_open (io
, size
);
107 grub_bufio_read (grub_file_t file
, char *buf
, grub_size_t len
)
111 grub_bufio_t bufio
= file
->data
;
112 grub_ssize_t really_read
;
114 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
115 file
->size
= bufio
->file
->size
;
117 /* First part: use whatever we already have in the buffer. */
118 if ((file
->offset
>= bufio
->buffer_at
) &&
119 (file
->offset
< bufio
->buffer_at
+ bufio
->buffer_len
))
124 pos
= file
->offset
- bufio
->buffer_at
;
125 n
= bufio
->buffer_len
- pos
;
129 grub_memcpy (buf
, &bufio
->buffer
[pos
], n
);
138 /* Need to read some more. */
139 next_buf
= (file
->offset
+ res
+ len
- 1) & ~((grub_off_t
) bufio
->block_size
- 1);
140 /* Now read between file->offset + res and bufio->buffer_at. */
141 if (file
->offset
+ res
< next_buf
)
143 grub_size_t read_now
;
144 read_now
= next_buf
- (file
->offset
+ res
);
145 grub_file_seek (bufio
->file
, file
->offset
+ res
);
146 really_read
= grub_file_read (bufio
->file
, buf
, read_now
);
149 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
150 file
->size
= bufio
->file
->size
;
155 /* Partial read. File ended unexpectedly. Save the last chunk in buffer.
157 if (really_read
!= (grub_ssize_t
) read_now
)
159 bufio
->buffer_len
= really_read
;
160 if (bufio
->buffer_len
> bufio
->block_size
)
161 bufio
->buffer_len
= bufio
->block_size
;
162 bufio
->buffer_at
= file
->offset
+ res
- bufio
->buffer_len
;
163 grub_memcpy (&bufio
->buffer
[0], buf
- bufio
->buffer_len
,
169 /* Read into buffer. */
170 grub_file_seek (bufio
->file
, next_buf
);
171 really_read
= grub_file_read (bufio
->file
, bufio
->buffer
,
175 bufio
->buffer_at
= next_buf
;
176 bufio
->buffer_len
= really_read
;
178 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
179 file
->size
= bufio
->file
->size
;
181 if (len
> bufio
->buffer_len
)
182 len
= bufio
->buffer_len
;
183 grub_memcpy (buf
, &bufio
->buffer
[file
->offset
+ res
- next_buf
], len
);
190 grub_bufio_close (grub_file_t file
)
192 grub_bufio_t bufio
= file
->data
;
194 grub_file_close (bufio
->file
);
202 static struct grub_fs grub_bufio_fs
=
207 .read
= grub_bufio_read
,
208 .close
= grub_bufio_close
,