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_zalloc (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_zalloc (sizeof (struct grub_bufio
) + size
);
72 bufio
->block_size
= size
;
74 file
->device
= io
->device
;
75 file
->size
= io
->size
;
77 file
->fs
= &grub_bufio_fs
;
78 file
->not_easily_seekable
= io
->not_easily_seekable
;
84 grub_buffile_open (const char *name
, int size
)
88 io
= grub_file_open (name
);
92 file
= grub_bufio_open (io
, size
);
103 grub_bufio_read (grub_file_t file
, char *buf
, grub_size_t len
)
107 grub_bufio_t bufio
= file
->data
;
108 grub_ssize_t really_read
;
110 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
111 file
->size
= bufio
->file
->size
;
113 /* First part: use whatever we already have in the buffer. */
114 if ((file
->offset
>= bufio
->buffer_at
) &&
115 (file
->offset
< bufio
->buffer_at
+ bufio
->buffer_len
))
120 pos
= file
->offset
- bufio
->buffer_at
;
121 n
= bufio
->buffer_len
- pos
;
125 grub_memcpy (buf
, &bufio
->buffer
[pos
], n
);
134 /* Need to read some more. */
135 next_buf
= (file
->offset
+ res
+ len
- 1) & ~((grub_off_t
) bufio
->block_size
- 1);
136 /* Now read between file->offset + res and bufio->buffer_at. */
137 if (file
->offset
+ res
< next_buf
)
139 grub_size_t read_now
;
140 read_now
= next_buf
- (file
->offset
+ res
);
141 grub_file_seek (bufio
->file
, file
->offset
+ res
);
142 really_read
= grub_file_read (bufio
->file
, buf
, read_now
);
145 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
146 file
->size
= bufio
->file
->size
;
151 /* Partial read. File ended unexpectedly. Save the last chunk in buffer.
153 if (really_read
!= (grub_ssize_t
) read_now
)
155 bufio
->buffer_len
= really_read
;
156 if (bufio
->buffer_len
> bufio
->block_size
)
157 bufio
->buffer_len
= bufio
->block_size
;
158 bufio
->buffer_at
= file
->offset
+ res
- bufio
->buffer_len
;
159 grub_memcpy (&bufio
->buffer
[0], buf
- bufio
->buffer_len
,
165 /* Read into buffer. */
166 grub_file_seek (bufio
->file
, next_buf
);
167 really_read
= grub_file_read (bufio
->file
, bufio
->buffer
,
171 bufio
->buffer_at
= next_buf
;
172 bufio
->buffer_len
= really_read
;
174 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
175 file
->size
= bufio
->file
->size
;
177 if (len
> bufio
->buffer_len
)
178 len
= bufio
->buffer_len
;
179 grub_memcpy (buf
, &bufio
->buffer
[file
->offset
+ res
- next_buf
], len
);
186 grub_bufio_close (grub_file_t file
)
188 grub_bufio_t bufio
= file
->data
;
190 grub_file_close (bufio
->file
);
198 static struct grub_fs grub_bufio_fs
=
203 .read
= grub_bufio_read
,
204 .close
= grub_bufio_close
,