2009-11-21 Samuel Thibault <samuel.thibault@ens-lyon.org>
[grub2.git] / fs / cpio.c
blob3f3a3d1a0e6557620d065b8a484b7edb801db6b7
1 /* cpio.c - cpio and tar filesystem. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2007,2008 Free Software Foundation, Inc.
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.
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.
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/>.
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>
26 #ifndef MODE_USTAR
27 /* cpio support */
28 #define MAGIC_BCPIO 070707
29 struct head
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
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
69 struct grub_cpio_data
71 grub_disk_t disk;
72 grub_uint32_t hofs;
73 grub_uint32_t dofs;
74 grub_uint32_t size;
77 static grub_dl_t my_mod;
79 static grub_err_t
80 grub_cpio_find_file (struct grub_cpio_data *data, char **name,
81 grub_uint32_t * ofs)
83 #ifndef MODE_USTAR
84 struct head hd;
86 if (grub_disk_read
87 (data->disk, 0, data->hofs, sizeof (hd), &hd))
88 return grub_errno;
90 if (hd.magic != MAGIC_BCPIO)
91 return grub_error (GRUB_ERR_BAD_FS, "Invalid cpio archive");
93 data->size = (((grub_uint32_t) hd.filesize_1) << 16) + hd.filesize_2;
95 if (hd.namesize & 1)
96 hd.namesize++;
98 if ((*name = grub_malloc (hd.namesize)) == NULL)
99 return grub_errno;
101 if (grub_disk_read (data->disk, 0, data->hofs + sizeof (hd),
102 hd.namesize, *name))
104 grub_free (*name);
105 return grub_errno;
108 if (data->size == 0 && hd.mode == 0 && hd.namesize == 11 + 1
109 && ! grub_memcmp(*name, "TRAILER!!!", 11))
111 *ofs = 0;
112 return GRUB_ERR_NONE;
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;
122 if (grub_disk_read
123 (data->disk, 0, data->hofs, sizeof (hd), &hd))
124 return grub_errno;
126 if (!hd.name[0])
128 *ofs = 0;
129 return GRUB_ERR_NONE;
132 if (grub_memcmp (hd.magic, MAGIC_USTAR, sizeof (MAGIC_USTAR) - 1))
133 return grub_error (GRUB_ERR_BAD_FS, "Invalid tar archive");
135 if ((*name = grub_strdup (hd.name)) == NULL)
136 return grub_errno;
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;
146 static struct grub_cpio_data *
147 grub_cpio_mount (grub_disk_t disk)
149 struct head hd;
150 struct grub_cpio_data *data;
152 if (grub_disk_read (disk, 0, 0, sizeof (hd), &hd))
153 goto fail;
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;
163 data = (struct grub_cpio_data *) grub_malloc (sizeof (*data));
164 if (!data)
165 goto fail;
167 data->disk = disk;
169 return data;
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;
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))
187 struct grub_cpio_data *data;
188 grub_uint32_t ofs;
189 char *prev, *name;
190 const char *np;
191 int len;
193 grub_dl_ref (my_mod);
195 prev = 0;
197 data = grub_cpio_mount (device->disk);
198 if (!data)
199 goto fail;
201 np = path + 1;
202 len = grub_strlen (path) - 1;
204 data->hofs = 0;
205 while (1)
207 if (grub_cpio_find_file (data, &name, &ofs))
208 goto fail;
210 if (!ofs)
211 break;
213 if (grub_memcmp (np, name, len) == 0)
215 char *p, *n;
217 n = name + len;
218 if (*n == '/')
219 n++;
221 p = grub_strchr (name + len, '/');
222 if (p)
223 *p = 0;
225 if ((!prev) || (grub_strcmp (prev, name) != 0))
227 struct grub_dirhook_info info;
228 grub_memset (&info, 0, sizeof (info));
229 info.dir = (p != NULL);
231 hook (name + len, &info);
232 if (prev)
233 grub_free (prev);
234 prev = name;
236 else
237 grub_free (name);
239 data->hofs = ofs;
242 fail:
244 if (prev)
245 grub_free (prev);
247 if (data)
248 grub_free (data);
250 grub_dl_unref (my_mod);
252 return grub_errno;
255 static grub_err_t
256 grub_cpio_open (grub_file_t file, const char *name)
258 struct grub_cpio_data *data;
259 grub_uint32_t ofs;
260 char *fn;
261 int i, j;
263 grub_dl_ref (my_mod);
265 data = grub_cpio_mount (file->device->disk);
266 if (!data)
267 goto fail;
269 data->hofs = 0;
270 while (1)
272 if (grub_cpio_find_file (data, &fn, &ofs))
273 goto fail;
275 if (!ofs)
277 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
278 break;
281 /* Compare NAME and FN by hand in order to cope with duplicate
282 slashes. */
283 i = 1;
284 j = 0;
285 while (1)
287 if (name[i] != fn[j])
288 goto no_match;
290 if (name[i] == '\0')
291 break;
293 if (name[i] == '/' && name[i+1] == '/')
294 i++;
296 i++;
297 j++;
300 file->data = data;
301 file->size = data->size;
302 grub_free (fn);
304 return GRUB_ERR_NONE;
306 no_match:
308 grub_free (fn);
309 data->hofs = ofs;
312 fail:
314 if (data)
315 grub_free (data);
317 grub_dl_unref (my_mod);
319 return grub_errno;
322 static grub_ssize_t
323 grub_cpio_read (grub_file_t file, char *buf, grub_size_t len)
325 struct grub_cpio_data *data;
327 data = file->data;
328 return (grub_disk_read (data->disk, 0, data->dofs + file->offset,
329 len, buf)) ? -1 : (grub_ssize_t) len;
332 static grub_err_t
333 grub_cpio_close (grub_file_t file)
335 grub_free (file->data);
337 grub_dl_unref (my_mod);
339 return grub_errno;
342 static struct grub_fs grub_cpio_fs = {
343 #ifdef MODE_USTAR
344 .name = "tarfs",
345 #else
346 .name = "cpiofs",
347 #endif
348 .dir = grub_cpio_dir,
349 .open = grub_cpio_open,
350 .read = grub_cpio_read,
351 .close = grub_cpio_close,
354 #ifdef MODE_USTAR
355 GRUB_MOD_INIT (tar)
356 #else
357 GRUB_MOD_INIT (cpio)
358 #endif
360 grub_fs_register (&grub_cpio_fs);
361 my_mod = mod;
364 #ifdef MODE_USTAR
365 GRUB_MOD_FINI (tar)
366 #else
367 GRUB_MOD_FINI (cpio)
368 #endif
370 grub_fs_unregister (&grub_cpio_fs);