xfs.c 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163
  1. /* xfs.c - XFS. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
  5. *
  6. * GRUB 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. * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/err.h>
  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. #include <grub/types.h>
  26. #include <grub/fshelp.h>
  27. GRUB_MOD_LICENSE ("GPLv3+");
  28. #define XFS_INODE_EXTENTS 9
  29. #define XFS_INODE_FORMAT_INO 1
  30. #define XFS_INODE_FORMAT_EXT 2
  31. #define XFS_INODE_FORMAT_BTREE 3
  32. /* Superblock version field flags */
  33. #define XFS_SB_VERSION_NUMBITS 0x000f
  34. #define XFS_SB_VERSION_ATTRBIT 0x0010
  35. #define XFS_SB_VERSION_NLINKBIT 0x0020
  36. #define XFS_SB_VERSION_QUOTABIT 0x0040
  37. #define XFS_SB_VERSION_ALIGNBIT 0x0080
  38. #define XFS_SB_VERSION_DALIGNBIT 0x0100
  39. #define XFS_SB_VERSION_LOGV2BIT 0x0400
  40. #define XFS_SB_VERSION_SECTORBIT 0x0800
  41. #define XFS_SB_VERSION_EXTFLGBIT 0x1000
  42. #define XFS_SB_VERSION_DIRV2BIT 0x2000
  43. #define XFS_SB_VERSION_MOREBITSBIT 0x8000
  44. #define XFS_SB_VERSION_BITS_SUPPORTED \
  45. (XFS_SB_VERSION_NUMBITS | \
  46. XFS_SB_VERSION_ATTRBIT | \
  47. XFS_SB_VERSION_NLINKBIT | \
  48. XFS_SB_VERSION_QUOTABIT | \
  49. XFS_SB_VERSION_ALIGNBIT | \
  50. XFS_SB_VERSION_DALIGNBIT | \
  51. XFS_SB_VERSION_LOGV2BIT | \
  52. XFS_SB_VERSION_SECTORBIT | \
  53. XFS_SB_VERSION_EXTFLGBIT | \
  54. XFS_SB_VERSION_DIRV2BIT | \
  55. XFS_SB_VERSION_MOREBITSBIT)
  56. /* Recognized xfs format versions */
  57. #define XFS_SB_VERSION_4 4 /* Good old XFS filesystem */
  58. #define XFS_SB_VERSION_5 5 /* CRC enabled filesystem */
  59. /* features2 field flags */
  60. #define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
  61. #define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
  62. #define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32-bit project ids */
  63. #define XFS_SB_VERSION2_FTYPE 0x00000200 /* inode type in dir */
  64. #define XFS_SB_VERSION2_BITS_SUPPORTED \
  65. (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
  66. XFS_SB_VERSION2_ATTR2BIT | \
  67. XFS_SB_VERSION2_PROJID32BIT | \
  68. XFS_SB_VERSION2_FTYPE)
  69. /* incompat feature flags */
  70. #define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
  71. #define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */
  72. #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */
  73. /*
  74. * Directory entries with ftype are explicitly handled by GRUB code.
  75. *
  76. * We do not currently read the inode btrees, so it is safe to read filesystems
  77. * with the XFS_SB_FEAT_INCOMPAT_SPINODES feature.
  78. *
  79. * We do not currently verify metadata UUID, so it is safe to read filesystems
  80. * with the XFS_SB_FEAT_INCOMPAT_META_UUID feature.
  81. */
  82. #define XFS_SB_FEAT_INCOMPAT_SUPPORTED \
  83. (XFS_SB_FEAT_INCOMPAT_FTYPE | \
  84. XFS_SB_FEAT_INCOMPAT_SPINODES | \
  85. XFS_SB_FEAT_INCOMPAT_META_UUID)
  86. struct grub_xfs_sblock
  87. {
  88. grub_uint8_t magic[4];
  89. grub_uint32_t bsize;
  90. grub_uint8_t unused1[24];
  91. grub_uint16_t uuid[8];
  92. grub_uint8_t unused2[8];
  93. grub_uint64_t rootino;
  94. grub_uint8_t unused3[20];
  95. grub_uint32_t agsize;
  96. grub_uint8_t unused4[12];
  97. grub_uint16_t version;
  98. grub_uint8_t unused5[6];
  99. grub_uint8_t label[12];
  100. grub_uint8_t log2_bsize;
  101. grub_uint8_t log2_sect;
  102. grub_uint8_t log2_inode;
  103. grub_uint8_t log2_inop;
  104. grub_uint8_t log2_agblk;
  105. grub_uint8_t unused6[67];
  106. grub_uint8_t log2_dirblk;
  107. grub_uint8_t unused7[7];
  108. grub_uint32_t features2;
  109. grub_uint8_t unused8[4];
  110. grub_uint32_t sb_features_compat;
  111. grub_uint32_t sb_features_ro_compat;
  112. grub_uint32_t sb_features_incompat;
  113. grub_uint32_t sb_features_log_incompat;
  114. } GRUB_PACKED;
  115. struct grub_xfs_dir_header
  116. {
  117. grub_uint8_t count;
  118. grub_uint8_t largeino;
  119. union
  120. {
  121. grub_uint32_t i4;
  122. grub_uint64_t i8;
  123. } GRUB_PACKED parent;
  124. } GRUB_PACKED;
  125. /* Structure for directory entry inlined in the inode */
  126. struct grub_xfs_dir_entry
  127. {
  128. grub_uint8_t len;
  129. grub_uint16_t offset;
  130. char name[1];
  131. /* Inode number follows, 32 / 64 bits. */
  132. } GRUB_PACKED;
  133. /* Structure for directory entry in a block */
  134. struct grub_xfs_dir2_entry
  135. {
  136. grub_uint64_t inode;
  137. grub_uint8_t len;
  138. } GRUB_PACKED;
  139. struct grub_xfs_extent
  140. {
  141. /* This should be a bitfield but bietfields are unportable, so just have
  142. a raw array and functions extracting useful info from it.
  143. */
  144. grub_uint32_t raw[4];
  145. } GRUB_PACKED;
  146. struct grub_xfs_btree_node
  147. {
  148. grub_uint8_t magic[4];
  149. grub_uint16_t level;
  150. grub_uint16_t numrecs;
  151. grub_uint64_t left;
  152. grub_uint64_t right;
  153. /* In V5 here follow crc, uuid, etc. */
  154. /* Then follow keys and block pointers */
  155. } GRUB_PACKED;
  156. struct grub_xfs_btree_root
  157. {
  158. grub_uint16_t level;
  159. grub_uint16_t numrecs;
  160. grub_uint64_t keys[1];
  161. } GRUB_PACKED;
  162. struct grub_xfs_time
  163. {
  164. grub_uint32_t sec;
  165. grub_uint32_t nanosec;
  166. } GRUB_PACKED;
  167. struct grub_xfs_inode
  168. {
  169. grub_uint8_t magic[2];
  170. grub_uint16_t mode;
  171. grub_uint8_t version;
  172. grub_uint8_t format;
  173. grub_uint8_t unused2[26];
  174. struct grub_xfs_time atime;
  175. struct grub_xfs_time mtime;
  176. struct grub_xfs_time ctime;
  177. grub_uint64_t size;
  178. grub_uint64_t nblocks;
  179. grub_uint32_t extsize;
  180. grub_uint32_t nextents;
  181. grub_uint16_t unused3;
  182. grub_uint8_t fork_offset;
  183. grub_uint8_t unused4[17];
  184. } GRUB_PACKED;
  185. #define XFS_V2_INODE_SIZE sizeof(struct grub_xfs_inode)
  186. #define XFS_V3_INODE_SIZE (XFS_V2_INODE_SIZE + 76)
  187. struct grub_xfs_dirblock_tail
  188. {
  189. grub_uint32_t leaf_count;
  190. grub_uint32_t leaf_stale;
  191. } GRUB_PACKED;
  192. struct grub_fshelp_node
  193. {
  194. struct grub_xfs_data *data;
  195. grub_uint64_t ino;
  196. int inode_read;
  197. struct grub_xfs_inode inode;
  198. };
  199. struct grub_xfs_data
  200. {
  201. struct grub_xfs_sblock sblock;
  202. grub_disk_t disk;
  203. int pos;
  204. int bsize;
  205. grub_uint32_t agsize;
  206. unsigned int hasftype:1;
  207. unsigned int hascrc:1;
  208. struct grub_fshelp_node diropen;
  209. };
  210. static grub_dl_t my_mod;
  211. static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
  212. {
  213. return (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
  214. grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5);
  215. }
  216. static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
  217. {
  218. if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
  219. grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5) &&
  220. data->sblock.sb_features_incompat & grub_cpu_to_be32_compile_time(XFS_SB_FEAT_INCOMPAT_FTYPE))
  221. return 1;
  222. if (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
  223. data->sblock.features2 & grub_cpu_to_be32_compile_time(XFS_SB_VERSION2_FTYPE))
  224. return 1;
  225. return 0;
  226. }
  227. static int grub_xfs_sb_valid(struct grub_xfs_data *data)
  228. {
  229. grub_dprintf("xfs", "Validating superblock\n");
  230. if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)
  231. || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS
  232. || ((int) data->sblock.log2_bsize
  233. + (int) data->sblock.log2_dirblk) >= 27)
  234. {
  235. grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
  236. return 0;
  237. }
  238. if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
  239. grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5))
  240. {
  241. grub_dprintf("xfs", "XFS v5 superblock detected\n");
  242. if (data->sblock.sb_features_incompat &
  243. grub_cpu_to_be32_compile_time(~XFS_SB_FEAT_INCOMPAT_SUPPORTED))
  244. {
  245. grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported "
  246. "incompatible features");
  247. return 0;
  248. }
  249. return 1;
  250. }
  251. else if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
  252. grub_cpu_to_be16_compile_time(XFS_SB_VERSION_4))
  253. {
  254. grub_dprintf("xfs", "XFS v4 superblock detected\n");
  255. if (!(data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT)))
  256. {
  257. grub_error (GRUB_ERR_BAD_FS, "XFS filesystem without V2 directories "
  258. "is unsupported");
  259. return 0;
  260. }
  261. if (data->sblock.version & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION_BITS_SUPPORTED) ||
  262. (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
  263. data->sblock.features2 & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION2_BITS_SUPPORTED)))
  264. {
  265. grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported version "
  266. "bits");
  267. return 0;
  268. }
  269. return 1;
  270. }
  271. return 0;
  272. }
  273. /* Filetype information as used in inodes. */
  274. #define FILETYPE_INO_MASK 0170000
  275. #define FILETYPE_INO_REG 0100000
  276. #define FILETYPE_INO_DIRECTORY 0040000
  277. #define FILETYPE_INO_SYMLINK 0120000
  278. static inline int
  279. GRUB_XFS_INO_AGBITS(struct grub_xfs_data *data)
  280. {
  281. return ((data)->sblock.log2_agblk + (data)->sblock.log2_inop);
  282. }
  283. static inline grub_uint64_t
  284. GRUB_XFS_INO_INOINAG (struct grub_xfs_data *data,
  285. grub_uint64_t ino)
  286. {
  287. return (ino & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1));
  288. }
  289. static inline grub_uint64_t
  290. GRUB_XFS_INO_AG (struct grub_xfs_data *data,
  291. grub_uint64_t ino)
  292. {
  293. return (ino >> GRUB_XFS_INO_AGBITS (data));
  294. }
  295. static inline grub_disk_addr_t
  296. GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data *data, grub_disk_addr_t fsb)
  297. {
  298. return ((fsb >> data->sblock.log2_agblk) * data->agsize
  299. + (fsb & ((1LL << data->sblock.log2_agblk) - 1)));
  300. }
  301. static inline grub_uint64_t
  302. GRUB_XFS_EXTENT_OFFSET (struct grub_xfs_extent *exts, int ex)
  303. {
  304. return ((grub_be_to_cpu32 (exts[ex].raw[0]) & ~(1 << 31)) << 23
  305. | grub_be_to_cpu32 (exts[ex].raw[1]) >> 9);
  306. }
  307. static inline grub_uint64_t
  308. GRUB_XFS_EXTENT_BLOCK (struct grub_xfs_extent *exts, int ex)
  309. {
  310. return ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex].raw[1])
  311. & (0x1ff)) << 43
  312. | (grub_uint64_t) grub_be_to_cpu32 (exts[ex].raw[2]) << 11
  313. | grub_be_to_cpu32 (exts[ex].raw[3]) >> 21);
  314. }
  315. static inline grub_uint64_t
  316. GRUB_XFS_EXTENT_SIZE (struct grub_xfs_extent *exts, int ex)
  317. {
  318. return (grub_be_to_cpu32 (exts[ex].raw[3]) & ((1 << 21) - 1));
  319. }
  320. static inline grub_uint64_t
  321. grub_xfs_inode_block (struct grub_xfs_data *data,
  322. grub_uint64_t ino)
  323. {
  324. long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino);
  325. long long ag = GRUB_XFS_INO_AG (data, ino);
  326. long long block;
  327. block = (inoinag >> data->sblock.log2_inop) + ag * data->agsize;
  328. block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS);
  329. return block;
  330. }
  331. static inline int
  332. grub_xfs_inode_offset (struct grub_xfs_data *data,
  333. grub_uint64_t ino)
  334. {
  335. int inoag = GRUB_XFS_INO_INOINAG (data, ino);
  336. return ((inoag & ((1 << data->sblock.log2_inop) - 1)) <<
  337. data->sblock.log2_inode);
  338. }
  339. static inline grub_size_t
  340. grub_xfs_inode_size(struct grub_xfs_data *data)
  341. {
  342. return (grub_size_t)1 << data->sblock.log2_inode;
  343. }
  344. /*
  345. * Returns size occupied by XFS inode stored in memory - we store struct
  346. * grub_fshelp_node there but on disk inode size may be actually larger than
  347. * struct grub_xfs_inode so we need to account for that so that we can read
  348. * from disk directly into in-memory structure.
  349. */
  350. static inline grub_size_t
  351. grub_xfs_fshelp_size(struct grub_xfs_data *data)
  352. {
  353. return sizeof (struct grub_fshelp_node) - sizeof (struct grub_xfs_inode)
  354. + grub_xfs_inode_size(data);
  355. }
  356. /* This should return void * but XFS code is error-prone with alignment, so
  357. return char to retain cast-align.
  358. */
  359. static char *
  360. grub_xfs_inode_data(struct grub_xfs_inode *inode)
  361. {
  362. if (inode->version <= 2)
  363. return ((char *)inode) + XFS_V2_INODE_SIZE;
  364. return ((char *)inode) + XFS_V3_INODE_SIZE;
  365. }
  366. static struct grub_xfs_dir_entry *
  367. grub_xfs_inline_de(struct grub_xfs_dir_header *head)
  368. {
  369. /*
  370. With small inode numbers the header is 4 bytes smaller because of
  371. smaller parent pointer
  372. */
  373. return (struct grub_xfs_dir_entry *)
  374. (((char *) head) + sizeof(struct grub_xfs_dir_header) -
  375. (head->largeino ? 0 : sizeof(grub_uint32_t)));
  376. }
  377. static grub_uint8_t *
  378. grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
  379. struct grub_xfs_dir_entry *de)
  380. {
  381. return ((grub_uint8_t *)(de + 1)) + de->len - 1 + (data->hasftype ? 1 : 0);
  382. }
  383. static struct grub_xfs_dir_entry *
  384. grub_xfs_inline_next_de(struct grub_xfs_data *data,
  385. struct grub_xfs_dir_header *head,
  386. struct grub_xfs_dir_entry *de)
  387. {
  388. char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
  389. p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
  390. if (data->hasftype)
  391. p++;
  392. return (struct grub_xfs_dir_entry *)p;
  393. }
  394. static struct grub_xfs_dirblock_tail *
  395. grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
  396. {
  397. int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
  398. return (struct grub_xfs_dirblock_tail *)
  399. ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
  400. }
  401. static struct grub_xfs_dir2_entry *
  402. grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
  403. {
  404. if (data->hascrc)
  405. return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
  406. return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
  407. }
  408. static struct grub_xfs_dir2_entry *
  409. grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
  410. {
  411. int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
  412. if (data->hasftype)
  413. size++; /* File type */
  414. return (struct grub_xfs_dir2_entry *)(((char *)de) + ALIGN_UP(size, 8));
  415. }
  416. /* This should return void * but XFS code is error-prone with alignment, so
  417. return char to retain cast-align.
  418. */
  419. static char *
  420. grub_xfs_btree_keys(struct grub_xfs_data *data,
  421. struct grub_xfs_btree_node *leaf)
  422. {
  423. char *keys = (char *)(leaf + 1);
  424. if (data->hascrc)
  425. keys += 48; /* skip crc, uuid, ... */
  426. return keys;
  427. }
  428. static grub_err_t
  429. grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
  430. struct grub_xfs_inode *inode)
  431. {
  432. grub_uint64_t block = grub_xfs_inode_block (data, ino);
  433. int offset = grub_xfs_inode_offset (data, ino);
  434. grub_dprintf("xfs", "Reading inode (%"PRIuGRUB_UINT64_T") - %"PRIuGRUB_UINT64_T", %d\n",
  435. ino, block, offset);
  436. /* Read the inode. */
  437. if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
  438. inode))
  439. return grub_errno;
  440. if (grub_strncmp ((char *) inode->magic, "IN", 2))
  441. return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode");
  442. return 0;
  443. }
  444. static grub_uint64_t
  445. get_fsb (const void *keys, int idx)
  446. {
  447. const char *p = (const char *) keys + sizeof(grub_uint64_t) * idx;
  448. return grub_be_to_cpu64 (grub_get_unaligned64 (p));
  449. }
  450. static grub_disk_addr_t
  451. grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
  452. {
  453. struct grub_xfs_btree_node *leaf = 0;
  454. int ex, nrec;
  455. struct grub_xfs_extent *exts;
  456. grub_uint64_t ret = 0;
  457. if (node->inode.format == XFS_INODE_FORMAT_BTREE)
  458. {
  459. struct grub_xfs_btree_root *root;
  460. const char *keys;
  461. int recoffset;
  462. leaf = grub_malloc (node->data->bsize);
  463. if (leaf == 0)
  464. return 0;
  465. root = (struct grub_xfs_btree_root *) grub_xfs_inode_data(&node->inode);
  466. nrec = grub_be_to_cpu16 (root->numrecs);
  467. keys = (char *) &root->keys[0];
  468. if (node->inode.fork_offset)
  469. recoffset = (node->inode.fork_offset - 1) / 2;
  470. else
  471. recoffset = (grub_xfs_inode_size(node->data)
  472. - ((char *) keys - (char *) &node->inode))
  473. / (2 * sizeof (grub_uint64_t));
  474. do
  475. {
  476. int i;
  477. for (i = 0; i < nrec; i++)
  478. {
  479. if (fileblock < get_fsb(keys, i))
  480. break;
  481. }
  482. /* Sparse block. */
  483. if (i == 0)
  484. {
  485. grub_free (leaf);
  486. return 0;
  487. }
  488. if (grub_disk_read (node->data->disk,
  489. GRUB_XFS_FSB_TO_BLOCK (node->data, get_fsb (keys, i - 1 + recoffset)) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS),
  490. 0, node->data->bsize, leaf))
  491. return 0;
  492. if ((!node->data->hascrc &&
  493. grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
  494. (node->data->hascrc &&
  495. grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
  496. {
  497. grub_free (leaf);
  498. grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
  499. return 0;
  500. }
  501. nrec = grub_be_to_cpu16 (leaf->numrecs);
  502. keys = grub_xfs_btree_keys(node->data, leaf);
  503. recoffset = ((node->data->bsize - ((char *) keys
  504. - (char *) leaf))
  505. / (2 * sizeof (grub_uint64_t)));
  506. }
  507. while (leaf->level);
  508. exts = (struct grub_xfs_extent *) keys;
  509. }
  510. else if (node->inode.format == XFS_INODE_FORMAT_EXT)
  511. {
  512. nrec = grub_be_to_cpu32 (node->inode.nextents);
  513. exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode);
  514. }
  515. else
  516. {
  517. grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  518. "XFS does not support inode format %d yet",
  519. node->inode.format);
  520. return 0;
  521. }
  522. /* Iterate over each extent to figure out which extent has
  523. the block we are looking for. */
  524. for (ex = 0; ex < nrec; ex++)
  525. {
  526. grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex);
  527. grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex);
  528. grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex);
  529. /* Sparse block. */
  530. if (fileblock < offset)
  531. break;
  532. else if (fileblock < offset + size)
  533. {
  534. ret = (fileblock - offset + start);
  535. break;
  536. }
  537. }
  538. grub_free (leaf);
  539. return GRUB_XFS_FSB_TO_BLOCK(node->data, ret);
  540. }
  541. /* Read LEN bytes from the file described by DATA starting with byte
  542. POS. Return the amount of read bytes in READ. */
  543. static grub_ssize_t
  544. grub_xfs_read_file (grub_fshelp_node_t node,
  545. grub_disk_read_hook_t read_hook, void *read_hook_data,
  546. grub_off_t pos, grub_size_t len, char *buf, grub_uint32_t header_size)
  547. {
  548. return grub_fshelp_read_file (node->data->disk, node,
  549. read_hook, read_hook_data,
  550. pos, len, buf, grub_xfs_read_block,
  551. grub_be_to_cpu64 (node->inode.size) + header_size,
  552. node->data->sblock.log2_bsize
  553. - GRUB_DISK_SECTOR_BITS, 0);
  554. }
  555. static char *
  556. grub_xfs_read_symlink (grub_fshelp_node_t node)
  557. {
  558. grub_ssize_t size = grub_be_to_cpu64 (node->inode.size);
  559. if (size < 0)
  560. {
  561. grub_error (GRUB_ERR_BAD_FS, "invalid symlink");
  562. return 0;
  563. }
  564. switch (node->inode.format)
  565. {
  566. case XFS_INODE_FORMAT_INO:
  567. return grub_strndup (grub_xfs_inode_data(&node->inode), size);
  568. case XFS_INODE_FORMAT_EXT:
  569. {
  570. char *symlink;
  571. grub_ssize_t numread;
  572. int off = 0;
  573. if (node->data->hascrc)
  574. off = 56;
  575. symlink = grub_malloc (size + 1);
  576. if (!symlink)
  577. return 0;
  578. node->inode.size = grub_be_to_cpu64 (size + off);
  579. numread = grub_xfs_read_file (node, 0, 0, off, size, symlink, off);
  580. if (numread != size)
  581. {
  582. grub_free (symlink);
  583. return 0;
  584. }
  585. symlink[size] = '\0';
  586. return symlink;
  587. }
  588. }
  589. return 0;
  590. }
  591. static enum grub_fshelp_filetype
  592. grub_xfs_mode_to_filetype (grub_uint16_t mode)
  593. {
  594. if ((grub_be_to_cpu16 (mode)
  595. & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
  596. return GRUB_FSHELP_DIR;
  597. else if ((grub_be_to_cpu16 (mode)
  598. & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
  599. return GRUB_FSHELP_SYMLINK;
  600. else if ((grub_be_to_cpu16 (mode)
  601. & FILETYPE_INO_MASK) == FILETYPE_INO_REG)
  602. return GRUB_FSHELP_REG;
  603. return GRUB_FSHELP_UNKNOWN;
  604. }
  605. /* Context for grub_xfs_iterate_dir. */
  606. struct grub_xfs_iterate_dir_ctx
  607. {
  608. grub_fshelp_iterate_dir_hook_t hook;
  609. void *hook_data;
  610. struct grub_fshelp_node *diro;
  611. };
  612. /* Helper for grub_xfs_iterate_dir. */
  613. static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename,
  614. struct grub_xfs_iterate_dir_ctx *ctx)
  615. {
  616. struct grub_fshelp_node *fdiro;
  617. grub_err_t err;
  618. fdiro = grub_malloc (grub_xfs_fshelp_size(ctx->diro->data) + 1);
  619. if (!fdiro)
  620. {
  621. grub_print_error ();
  622. return 0;
  623. }
  624. /* The inode should be read, otherwise the filetype can
  625. not be determined. */
  626. fdiro->ino = ino;
  627. fdiro->inode_read = 1;
  628. fdiro->data = ctx->diro->data;
  629. err = grub_xfs_read_inode (ctx->diro->data, ino, &fdiro->inode);
  630. if (err)
  631. {
  632. grub_print_error ();
  633. return 0;
  634. }
  635. return ctx->hook (filename, grub_xfs_mode_to_filetype (fdiro->inode.mode),
  636. fdiro, ctx->hook_data);
  637. }
  638. static int
  639. grub_xfs_iterate_dir (grub_fshelp_node_t dir,
  640. grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
  641. {
  642. struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
  643. struct grub_xfs_iterate_dir_ctx ctx = {
  644. .hook = hook,
  645. .hook_data = hook_data,
  646. .diro = diro
  647. };
  648. switch (diro->inode.format)
  649. {
  650. case XFS_INODE_FORMAT_INO:
  651. {
  652. struct grub_xfs_dir_header *head = (struct grub_xfs_dir_header *) grub_xfs_inode_data(&diro->inode);
  653. struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
  654. int smallino = !head->largeino;
  655. int i;
  656. grub_uint64_t parent;
  657. /* If small inode numbers are used to pack the direntry, the
  658. parent inode number is small too. */
  659. if (smallino)
  660. parent = grub_be_to_cpu32 (head->parent.i4);
  661. else
  662. parent = grub_be_to_cpu64 (head->parent.i8);
  663. /* Synthesize the direntries for `.' and `..'. */
  664. if (iterate_dir_call_hook (diro->ino, ".", &ctx))
  665. return 1;
  666. if (iterate_dir_call_hook (parent, "..", &ctx))
  667. return 1;
  668. for (i = 0; i < head->count; i++)
  669. {
  670. grub_uint64_t ino;
  671. grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
  672. grub_uint8_t c;
  673. /* inopos might be unaligned. */
  674. if (smallino)
  675. ino = (((grub_uint32_t) inopos[0]) << 24)
  676. | (((grub_uint32_t) inopos[1]) << 16)
  677. | (((grub_uint32_t) inopos[2]) << 8)
  678. | (((grub_uint32_t) inopos[3]) << 0);
  679. else
  680. ino = (((grub_uint64_t) inopos[0]) << 56)
  681. | (((grub_uint64_t) inopos[1]) << 48)
  682. | (((grub_uint64_t) inopos[2]) << 40)
  683. | (((grub_uint64_t) inopos[3]) << 32)
  684. | (((grub_uint64_t) inopos[4]) << 24)
  685. | (((grub_uint64_t) inopos[5]) << 16)
  686. | (((grub_uint64_t) inopos[6]) << 8)
  687. | (((grub_uint64_t) inopos[7]) << 0);
  688. c = de->name[de->len];
  689. de->name[de->len] = '\0';
  690. if (iterate_dir_call_hook (ino, de->name, &ctx))
  691. {
  692. de->name[de->len] = c;
  693. return 1;
  694. }
  695. de->name[de->len] = c;
  696. de = grub_xfs_inline_next_de(dir->data, head, de);
  697. }
  698. break;
  699. }
  700. case XFS_INODE_FORMAT_BTREE:
  701. case XFS_INODE_FORMAT_EXT:
  702. {
  703. grub_ssize_t numread;
  704. char *dirblock;
  705. grub_uint64_t blk;
  706. int dirblk_size, dirblk_log2;
  707. dirblk_log2 = (dir->data->sblock.log2_bsize
  708. + dir->data->sblock.log2_dirblk);
  709. dirblk_size = 1 << dirblk_log2;
  710. dirblock = grub_malloc (dirblk_size);
  711. if (! dirblock)
  712. return 0;
  713. /* Iterate over every block the directory has. */
  714. for (blk = 0;
  715. blk < (grub_be_to_cpu64 (dir->inode.size)
  716. >> dirblk_log2);
  717. blk++)
  718. {
  719. struct grub_xfs_dir2_entry *direntry =
  720. grub_xfs_first_de(dir->data, dirblock);
  721. int entries;
  722. struct grub_xfs_dirblock_tail *tail =
  723. grub_xfs_dir_tail(dir->data, dirblock);
  724. numread = grub_xfs_read_file (dir, 0, 0,
  725. blk << dirblk_log2,
  726. dirblk_size, dirblock, 0);
  727. if (numread != dirblk_size)
  728. return 0;
  729. entries = (grub_be_to_cpu32 (tail->leaf_count)
  730. - grub_be_to_cpu32 (tail->leaf_stale));
  731. if (!entries)
  732. continue;
  733. /* Iterate over all entries within this block. */
  734. while ((char *)direntry < (char *)tail)
  735. {
  736. grub_uint8_t *freetag;
  737. char *filename;
  738. freetag = (grub_uint8_t *) direntry;
  739. if (grub_get_unaligned16 (freetag) == 0XFFFF)
  740. {
  741. grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
  742. /* This entry is not used, go to the next one. */
  743. direntry = (struct grub_xfs_dir2_entry *)
  744. (((char *)direntry) +
  745. grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
  746. continue;
  747. }
  748. filename = (char *)(direntry + 1);
  749. /* The byte after the filename is for the filetype, padding, or
  750. tag, which is not used by GRUB. So it can be overwritten. */
  751. filename[direntry->len] = '\0';
  752. if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode),
  753. filename, &ctx))
  754. {
  755. grub_free (dirblock);
  756. return 1;
  757. }
  758. /* Check if last direntry in this block is
  759. reached. */
  760. entries--;
  761. if (!entries)
  762. break;
  763. /* Select the next directory entry. */
  764. direntry = grub_xfs_next_de(dir->data, direntry);
  765. }
  766. }
  767. grub_free (dirblock);
  768. break;
  769. }
  770. default:
  771. grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  772. "XFS does not support inode format %d yet",
  773. diro->inode.format);
  774. }
  775. return 0;
  776. }
  777. static struct grub_xfs_data *
  778. grub_xfs_mount (grub_disk_t disk)
  779. {
  780. struct grub_xfs_data *data = 0;
  781. data = grub_zalloc (sizeof (struct grub_xfs_data));
  782. if (!data)
  783. return 0;
  784. grub_dprintf("xfs", "Reading sb\n");
  785. /* Read the superblock. */
  786. if (grub_disk_read (disk, 0, 0,
  787. sizeof (struct grub_xfs_sblock), &data->sblock))
  788. goto fail;
  789. if (!grub_xfs_sb_valid(data))
  790. goto fail;
  791. data = grub_realloc (data,
  792. sizeof (struct grub_xfs_data)
  793. - sizeof (struct grub_xfs_inode)
  794. + grub_xfs_inode_size(data) + 1);
  795. if (! data)
  796. goto fail;
  797. data->diropen.data = data;
  798. data->diropen.ino = grub_be_to_cpu64(data->sblock.rootino);
  799. data->diropen.inode_read = 1;
  800. data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
  801. data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
  802. data->hasftype = grub_xfs_sb_hasftype(data);
  803. data->hascrc = grub_xfs_sb_hascrc(data);
  804. data->disk = disk;
  805. data->pos = 0;
  806. grub_dprintf("xfs", "Reading root ino %"PRIuGRUB_UINT64_T"\n",
  807. grub_cpu_to_be64(data->sblock.rootino));
  808. grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
  809. return data;
  810. fail:
  811. if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
  812. grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem");
  813. grub_free (data);
  814. return 0;
  815. }
  816. /* Context for grub_xfs_dir. */
  817. struct grub_xfs_dir_ctx
  818. {
  819. grub_fs_dir_hook_t hook;
  820. void *hook_data;
  821. };
  822. /* Helper for grub_xfs_dir. */
  823. static int
  824. grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
  825. grub_fshelp_node_t node, void *data)
  826. {
  827. struct grub_xfs_dir_ctx *ctx = data;
  828. struct grub_dirhook_info info;
  829. grub_memset (&info, 0, sizeof (info));
  830. if (node->inode_read)
  831. {
  832. info.mtimeset = 1;
  833. info.mtime = grub_be_to_cpu32 (node->inode.mtime.sec);
  834. }
  835. info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
  836. grub_free (node);
  837. return ctx->hook (filename, &info, ctx->hook_data);
  838. }
  839. static grub_err_t
  840. grub_xfs_dir (grub_device_t device, const char *path,
  841. grub_fs_dir_hook_t hook, void *hook_data)
  842. {
  843. struct grub_xfs_dir_ctx ctx = { hook, hook_data };
  844. struct grub_xfs_data *data = 0;
  845. struct grub_fshelp_node *fdiro = 0;
  846. grub_dl_ref (my_mod);
  847. data = grub_xfs_mount (device->disk);
  848. if (!data)
  849. goto mount_fail;
  850. grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir,
  851. grub_xfs_read_symlink, GRUB_FSHELP_DIR);
  852. if (grub_errno)
  853. goto fail;
  854. grub_xfs_iterate_dir (fdiro, grub_xfs_dir_iter, &ctx);
  855. fail:
  856. if (fdiro != &data->diropen)
  857. grub_free (fdiro);
  858. grub_free (data);
  859. mount_fail:
  860. grub_dl_unref (my_mod);
  861. return grub_errno;
  862. }
  863. /* Open a file named NAME and initialize FILE. */
  864. static grub_err_t
  865. grub_xfs_open (struct grub_file *file, const char *name)
  866. {
  867. struct grub_xfs_data *data;
  868. struct grub_fshelp_node *fdiro = 0;
  869. grub_dl_ref (my_mod);
  870. data = grub_xfs_mount (file->device->disk);
  871. if (!data)
  872. goto mount_fail;
  873. grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir,
  874. grub_xfs_read_symlink, GRUB_FSHELP_REG);
  875. if (grub_errno)
  876. goto fail;
  877. if (!fdiro->inode_read)
  878. {
  879. grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode);
  880. if (grub_errno)
  881. goto fail;
  882. }
  883. if (fdiro != &data->diropen)
  884. {
  885. grub_memcpy (&data->diropen, fdiro, grub_xfs_fshelp_size(data));
  886. grub_free (fdiro);
  887. }
  888. file->size = grub_be_to_cpu64 (data->diropen.inode.size);
  889. file->data = data;
  890. file->offset = 0;
  891. return 0;
  892. fail:
  893. if (fdiro != &data->diropen)
  894. grub_free (fdiro);
  895. grub_free (data);
  896. mount_fail:
  897. grub_dl_unref (my_mod);
  898. return grub_errno;
  899. }
  900. static grub_ssize_t
  901. grub_xfs_read (grub_file_t file, char *buf, grub_size_t len)
  902. {
  903. struct grub_xfs_data *data =
  904. (struct grub_xfs_data *) file->data;
  905. return grub_xfs_read_file (&data->diropen,
  906. file->read_hook, file->read_hook_data,
  907. file->offset, len, buf, 0);
  908. }
  909. static grub_err_t
  910. grub_xfs_close (grub_file_t file)
  911. {
  912. grub_free (file->data);
  913. grub_dl_unref (my_mod);
  914. return GRUB_ERR_NONE;
  915. }
  916. static grub_err_t
  917. grub_xfs_label (grub_device_t device, char **label)
  918. {
  919. struct grub_xfs_data *data;
  920. grub_disk_t disk = device->disk;
  921. grub_dl_ref (my_mod);
  922. data = grub_xfs_mount (disk);
  923. if (data)
  924. *label = grub_strndup ((char *) (data->sblock.label), 12);
  925. else
  926. *label = 0;
  927. grub_dl_unref (my_mod);
  928. grub_free (data);
  929. return grub_errno;
  930. }
  931. static grub_err_t
  932. grub_xfs_uuid (grub_device_t device, char **uuid)
  933. {
  934. struct grub_xfs_data *data;
  935. grub_disk_t disk = device->disk;
  936. grub_dl_ref (my_mod);
  937. data = grub_xfs_mount (disk);
  938. if (data)
  939. {
  940. *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
  941. grub_be_to_cpu16 (data->sblock.uuid[0]),
  942. grub_be_to_cpu16 (data->sblock.uuid[1]),
  943. grub_be_to_cpu16 (data->sblock.uuid[2]),
  944. grub_be_to_cpu16 (data->sblock.uuid[3]),
  945. grub_be_to_cpu16 (data->sblock.uuid[4]),
  946. grub_be_to_cpu16 (data->sblock.uuid[5]),
  947. grub_be_to_cpu16 (data->sblock.uuid[6]),
  948. grub_be_to_cpu16 (data->sblock.uuid[7]));
  949. }
  950. else
  951. *uuid = NULL;
  952. grub_dl_unref (my_mod);
  953. grub_free (data);
  954. return grub_errno;
  955. }
  956. static struct grub_fs grub_xfs_fs =
  957. {
  958. .name = "xfs",
  959. .fs_dir = grub_xfs_dir,
  960. .fs_open = grub_xfs_open,
  961. .fs_read = grub_xfs_read,
  962. .fs_close = grub_xfs_close,
  963. .fs_label = grub_xfs_label,
  964. .fs_uuid = grub_xfs_uuid,
  965. #ifdef GRUB_UTIL
  966. .reserved_first_sector = 0,
  967. .blocklist_install = 1,
  968. #endif
  969. .next = 0
  970. };
  971. GRUB_MOD_INIT(xfs)
  972. {
  973. grub_fs_register (&grub_xfs_fs);
  974. my_mod = mod;
  975. }
  976. GRUB_MOD_FINI(xfs)
  977. {
  978. grub_fs_unregister (&grub_xfs_fs);
  979. }