1 /*
2 * BURG - Brand-new Universal loadeR from GRUB
3 * Copyright 2010 Bean Lee - All Rights Reserved
4 *
5 * BURG is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * BURG is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with BURG. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <grub/fs.h>
20 #include <grub/mm.h>
21 #include <grub/disk.h>
22 #include <grub/file.h>
23 #include <grub/misc.h>
24 #include <grub/fbfs.h>
25
26 struct grub_fb_data
27 {
28 grub_uint32_t ofs;
29 grub_uint32_t pri_size;
30 struct fbm_file *ptr;
31 char fb_list[0];
32 };
33
34 static struct grub_fb_data *
grub_fbfs_mount(grub_disk_t disk)35 grub_fbfs_mount (grub_disk_t disk)
36 {
37 struct fb_mbr *m;
38 struct fb_data *d;
39 char buf[512];
40 struct grub_fb_data *data;
41 int boot_base, boot_size, list_used, pri_size, ofs, i;
42 char *fb_list, *p1, *p2;
43 grub_uint32_t *grub32;
44
45 if (grub_disk_read (disk, 0, 0, sizeof (buf), buf))
46 goto fail;
47
48 m = (struct fb_mbr *) buf;
49 d = (struct fb_data *) buf;
50 grub32 = (grub_uint32_t *)&buf;
51 if (*grub32 == FB_AR_MAGIC_LONG)
52 {
53 ofs = 0;
54 boot_base = 0;
55 boot_size = 0;
56 pri_size = 0;
57 }
58 else
59 {
60 if ((m->fb_magic != FB_MAGIC_LONG) || (m->end_magic != 0xaa55))
61 goto fail;
62
63 ofs = m->lba;
64 boot_base = m->boot_base;
65
66 if (grub_disk_read (disk, boot_base + 1 - ofs, 0, sizeof (buf), buf))
67 goto fail;
68
69 boot_size = d->boot_size;
70 pri_size = d->pri_size;
71 }
72
73 if ((d->ver_major != FB_VER_MAJOR) || (d->ver_minor != FB_VER_MINOR))
74 goto fail;
75
76 list_used = d->list_used;
77 data = grub_malloc (sizeof (*data) + (list_used << 9));
78 if (! data)
79 goto fail;
80
81 fb_list = data->fb_list;
82 if (grub_disk_read (disk, boot_base + 1 + boot_size - ofs, 0,
83 (list_used << 9), fb_list))
84 {
85 grub_free (data);
86 goto fail;
87 }
88
89 p1 = p2 = fb_list;
90 for (i = 0; i < list_used - 1; i++)
91 {
92 p1 += 510;
93 p2 += 512;
94 grub_memcpy (p1, p2, 510);
95 }
96
97 data->ofs = ofs;
98 data->pri_size = pri_size;
99 return data;
100
101 fail:
102 grub_error (GRUB_ERR_BAD_FS, "not a fb filesystem");
103 return 0;
104 }
105
106 static grub_err_t
grub_fbfs_dir(grub_device_t device,const char * path,int (* hook)(const char * filename,const struct grub_dirhook_info * info,void * closure),void * closure)107 grub_fbfs_dir (grub_device_t device, const char *path,
108 int (*hook) (const char *filename,
109 const struct grub_dirhook_info *info,
110 void *closure),
111 void *closure)
112 {
113 struct grub_dirhook_info info;
114 struct fbm_file *p;
115 char *fn;
116 int len, ofs;
117 struct grub_fb_data *data;
118
119 data = grub_fbfs_mount (device->disk);
120 if (! data)
121 return grub_errno;
122 if (! hook)
123 return GRUB_ERR_NONE;
124
125 while (*path == '/')
126 path++;
127 len = grub_strlen (path);
128 fn = grub_strrchr (path, '/');
129 ofs = (fn) ? (fn + 1 - path) : 0;
130
131 grub_memset (&info, 0, sizeof (info));
132 info.mtimeset = 1;
133 p = (struct fbm_file *) data->fb_list;
134 while (p->size)
135 {
136 info.mtime = grub_le_to_cpu32 (p->data_time);
137 if ((! grub_memcmp (path, p->name, len)) &&
138 (hook (p->name + ofs, &info, closure)))
139 break;
140
141 p = (struct fbm_file *) ((char *) p + p->size + 2);
142 }
143
144 grub_free (data);
145 return GRUB_ERR_NONE;
146 }
147
148 static grub_err_t
grub_fbfs_open(struct grub_file * file,const char * name)149 grub_fbfs_open (struct grub_file *file, const char *name)
150 {
151 struct fbm_file *p;
152 struct grub_fb_data *data;
153
154 data = grub_fbfs_mount (file->device->disk);
155 if (! data)
156 return grub_errno;
157
158 while (*name == '/')
159 name++;
160
161 p = (struct fbm_file *) data->fb_list;
162 while (p->size)
163 {
164 if (! grub_strcasecmp (name, p->name))
165 {
166 file->data = data;
167 data->ptr = p;
168 file->size = p->data_size;
169 return GRUB_ERR_NONE;
170 }
171
172 p = (struct fbm_file *) ((char *) p + p->size + 2);
173 }
174
175 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
176 }
177
178 static grub_ssize_t
grub_fbfs_read(grub_file_t file,char * buf,grub_size_t len)179 grub_fbfs_read (grub_file_t file, char *buf, grub_size_t len)
180 {
181 struct fbm_file *p;
182 grub_disk_t disk;
183 grub_uint32_t sector;
184 grub_size_t saved_len, ofs;
185 struct grub_fb_data *data;
186
187 disk = file->device->disk;
188 disk->read_hook = file->read_hook;
189 disk->closure = file->closure;
190
191 data = file->data;
192 p = data->ptr;
193 if (p->data_start >= data->pri_size)
194 {
195 grub_err_t err;
196
197 err = grub_disk_read_ex (disk, p->data_start - data->ofs,
198 file->offset, len, buf, file->flags);
199 disk->read_hook = 0;
200 return (err) ? -1 : (grub_ssize_t) len;
201 }
202
203 sector = p->data_start + ((grub_uint32_t) file->offset / 510) - data->ofs;
204 ofs = ((grub_uint32_t) file->offset % 510);
205 saved_len = len;
206 while (len)
207 {
208 int n;
209
210 n = len;
211 if (ofs + n > 510)
212 n = 510 - ofs;
213 if (grub_disk_read (disk, sector, ofs, n, buf))
214 {
215 saved_len = -1;
216 break;
217 }
218 sector++;
219 if (buf)
220 buf += n;
221 len -= n;
222 ofs = 0;
223 }
224
225 disk->read_hook = 0;
226 return saved_len;
227 }
228
229 static grub_err_t
grub_fbfs_close(grub_file_t file)230 grub_fbfs_close (grub_file_t file)
231 {
232 grub_free (file->data);
233 return GRUB_ERR_NONE;
234 }
235
236 static grub_err_t
grub_fbfs_label(grub_device_t device,char ** label)237 grub_fbfs_label (grub_device_t device ,
238 char **label)
239 {
240 *label = 0;
241 return GRUB_ERR_NONE;
242 }
243
244 struct grub_fs grub_fb_fs =
245 {
246 .name = "fbfs",
247 .dir = grub_fbfs_dir,
248 .open = grub_fbfs_open,
249 .read = grub_fbfs_read,
250 .close = grub_fbfs_close,
251 .label = grub_fbfs_label,
252 .next = 0
253 };
254
255