]> git.proxmox.com Git - grub2.git/blob - fs/cpio.c
c087b4f90e1d9773e2f7740328b8d756e3ab4ec1
[grub2.git] / fs / cpio.c
1 /* cpio.c - cpio and tar filesystem. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc.
5 *
6 * This program 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.
10 *
11 * This program 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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/file.h>
21 #include <grub/mm.h>
22 #include <grub/misc.h>
23 #include <grub/disk.h>
24 #include <grub/dl.h>
25
26 #ifndef MODE_USTAR
27 /* cpio support */
28 #define MAGIC_BCPIO 070707
29 struct head
30 {
31 grub_uint16_t magic;
32 grub_uint16_t dev;
33 grub_uint16_t ino;
34 grub_uint16_t mode;
35 grub_uint16_t uid;
36 grub_uint16_t gid;
37 grub_uint16_t nlink;
38 grub_uint16_t rdev;
39 grub_uint16_t mtime_1;
40 grub_uint16_t mtime_2;
41 grub_uint16_t namesize;
42 grub_uint16_t filesize_1;
43 grub_uint16_t filesize_2;
44 } __attribute__ ((packed));
45 #else
46 /* tar support */
47 #define MAGIC_USTAR "ustar"
48 struct head
49 {
50 char name[100];
51 char mode[8];
52 char uid[8];
53 char gid[8];
54 char size[12];
55 char mtime[12];
56 char chksum[8];
57 char typeflag;
58 char linkname[100];
59 char magic[6];
60 char version[2];
61 char uname[32];
62 char gname[32];
63 char devmajor[8];
64 char devminor[8];
65 char prefix[155];
66 } __attribute__ ((packed));
67 #endif
68
69 struct grub_cpio_data
70 {
71 grub_disk_t disk;
72 grub_uint32_t hofs;
73 grub_uint32_t dofs;
74 grub_uint32_t size;
75 };
76
77 static grub_dl_t my_mod;
78
79 static grub_err_t
80 grub_cpio_find_file (struct grub_cpio_data *data, char **name,
81 grub_uint32_t * ofs)
82 {
83 #ifndef MODE_USTAR
84 struct head hd;
85
86 if (grub_disk_read
87 (data->disk, 0, data->hofs, sizeof (hd), &hd))
88 return grub_errno;
89
90 if (hd.magic != MAGIC_BCPIO)
91 return grub_error (GRUB_ERR_BAD_FS, "invalid cpio archive");
92
93 data->size = (((grub_uint32_t) hd.filesize_1) << 16) + hd.filesize_2;
94
95 if (hd.namesize & 1)
96 hd.namesize++;
97
98 if ((*name = grub_malloc (hd.namesize)) == NULL)
99 return grub_errno;
100
101 if (grub_disk_read (data->disk, 0, data->hofs + sizeof (hd),
102 hd.namesize, *name))
103 {
104 grub_free (*name);
105 return grub_errno;
106 }
107
108 if (data->size == 0 && hd.mode == 0 && hd.namesize == 11 + 1
109 && ! grub_memcmp(*name, "TRAILER!!!", 11))
110 {
111 *ofs = 0;
112 return GRUB_ERR_NONE;
113 }
114
115 data->dofs = data->hofs + sizeof (hd) + hd.namesize;
116 *ofs = data->dofs + data->size;
117 if (data->size & 1)
118 (*ofs)++;
119 #else
120 struct head hd;
121
122 if (grub_disk_read
123 (data->disk, 0, data->hofs, sizeof (hd), &hd))
124 return grub_errno;
125
126 if (!hd.name[0])
127 {
128 *ofs = 0;
129 return GRUB_ERR_NONE;
130 }
131
132 if (grub_memcmp (hd.magic, MAGIC_USTAR, sizeof (MAGIC_USTAR) - 1))
133 return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive");
134
135 if ((*name = grub_strdup (hd.name)) == NULL)
136 return grub_errno;
137
138 data->size = grub_strtoul (hd.size, NULL, 8);
139 data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE;
140 *ofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) &
141 ~(GRUB_DISK_SECTOR_SIZE - 1));
142 #endif
143 return GRUB_ERR_NONE;
144 }
145
146 static struct grub_cpio_data *
147 grub_cpio_mount (grub_disk_t disk)
148 {
149 struct head hd;
150 struct grub_cpio_data *data;
151
152 if (grub_disk_read (disk, 0, 0, sizeof (hd), &hd))
153 goto fail;
154
155 #ifndef MODE_USTAR
156 if (hd.magic != MAGIC_BCPIO)
157 #else
158 if (grub_memcmp (hd.magic, MAGIC_USTAR,
159 sizeof (MAGIC_USTAR) - 1))
160 #endif
161 goto fail;
162
163 data = (struct grub_cpio_data *) grub_malloc (sizeof (*data));
164 if (!data)
165 goto fail;
166
167 data->disk = disk;
168
169 return data;
170
171 fail:
172 grub_error (GRUB_ERR_BAD_FS, "not a "
173 #ifdef MODE_USTAR
174 "tar"
175 #else
176 "cpio"
177 #endif
178 " filesystem");
179 return 0;
180 }
181
182 static grub_err_t
183 grub_cpio_dir (grub_device_t device, const char *path,
184 int (*hook) (const char *filename,
185 const struct grub_dirhook_info *info))
186 {
187 struct grub_cpio_data *data;
188 grub_uint32_t ofs;
189 char *prev, *name;
190 const char *np;
191 int len;
192
193 grub_dl_ref (my_mod);
194
195 prev = 0;
196
197 data = grub_cpio_mount (device->disk);
198 if (!data)
199 goto fail;
200
201 np = path + 1;
202 len = grub_strlen (path) - 1;
203
204 data->hofs = 0;
205 while (1)
206 {
207 if (grub_cpio_find_file (data, &name, &ofs))
208 goto fail;
209
210 if (!ofs)
211 break;
212
213 if (grub_memcmp (np, name, len) == 0)
214 {
215 char *p, *n;
216
217 n = name + len;
218 if (*n == '/')
219 n++;
220
221 p = grub_strchr (name + len, '/');
222 if (p)
223 *p = 0;
224
225 if ((!prev) || (grub_strcmp (prev, name) != 0))
226 {
227 struct grub_dirhook_info info;
228 grub_memset (&info, 0, sizeof (info));
229 info.dir = (p != NULL);
230
231 hook (name + len, &info);
232 if (prev)
233 grub_free (prev);
234 prev = name;
235 }
236 else
237 grub_free (name);
238 }
239 data->hofs = ofs;
240 }
241
242 fail:
243
244 if (prev)
245 grub_free (prev);
246
247 if (data)
248 grub_free (data);
249
250 grub_dl_unref (my_mod);
251
252 return grub_errno;
253 }
254
255 static grub_err_t
256 grub_cpio_open (grub_file_t file, const char *name)
257 {
258 struct grub_cpio_data *data;
259 grub_uint32_t ofs;
260 char *fn;
261 int i, j;
262
263 grub_dl_ref (my_mod);
264
265 data = grub_cpio_mount (file->device->disk);
266 if (!data)
267 goto fail;
268
269 data->hofs = 0;
270 while (1)
271 {
272 if (grub_cpio_find_file (data, &fn, &ofs))
273 goto fail;
274
275 if (!ofs)
276 {
277 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
278 break;
279 }
280
281 /* Compare NAME and FN by hand in order to cope with duplicate
282 slashes. */
283 i = 0;
284 j = 0;
285 while (name[i] == '/')
286 i++;
287 while (1)
288 {
289 if (name[i] != fn[j])
290 goto no_match;
291
292 if (name[i] == '\0')
293 break;
294
295 while (name[i] == '/' && name[i+1] == '/')
296 i++;
297
298 i++;
299 j++;
300 }
301
302 if (name[i] != fn[j])
303 goto no_match;
304
305 file->data = data;
306 file->size = data->size;
307 grub_free (fn);
308
309 return GRUB_ERR_NONE;
310
311 no_match:
312
313 grub_free (fn);
314 data->hofs = ofs;
315 }
316
317 fail:
318
319 if (data)
320 grub_free (data);
321
322 grub_dl_unref (my_mod);
323
324 return grub_errno;
325 }
326
327 static grub_ssize_t
328 grub_cpio_read (grub_file_t file, char *buf, grub_size_t len)
329 {
330 struct grub_cpio_data *data;
331
332 data = file->data;
333 return (grub_disk_read (data->disk, 0, data->dofs + file->offset,
334 len, buf)) ? -1 : (grub_ssize_t) len;
335 }
336
337 static grub_err_t
338 grub_cpio_close (grub_file_t file)
339 {
340 grub_free (file->data);
341
342 grub_dl_unref (my_mod);
343
344 return grub_errno;
345 }
346
347 static struct grub_fs grub_cpio_fs = {
348 #ifdef MODE_USTAR
349 .name = "tarfs",
350 #else
351 .name = "cpiofs",
352 #endif
353 .dir = grub_cpio_dir,
354 .open = grub_cpio_open,
355 .read = grub_cpio_read,
356 .close = grub_cpio_close,
357 };
358
359 #ifdef MODE_USTAR
360 GRUB_MOD_INIT (tar)
361 #else
362 GRUB_MOD_INIT (cpio)
363 #endif
364 {
365 grub_fs_register (&grub_cpio_fs);
366 my_mod = mod;
367 }
368
369 #ifdef MODE_USTAR
370 GRUB_MOD_FINI (tar)
371 #else
372 GRUB_MOD_FINI (cpio)
373 #endif
374 {
375 grub_fs_unregister (&grub_cpio_fs);
376 }