cpio.c 7.5 KB

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