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