1 | /* |
---|
2 | * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com> |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU General Public License as |
---|
6 | * published by the Free Software Foundation. |
---|
7 | * |
---|
8 | * This program is distributed in the hope that it would be useful, |
---|
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
11 | * GNU General Public License for more details. |
---|
12 | * |
---|
13 | * You should have received a copy of the GNU General Public License |
---|
14 | * along with this program; if not, write the Free Software Foundation, |
---|
15 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
16 | */ |
---|
17 | |
---|
18 | #include <dprintf.h> |
---|
19 | #include <stdio.h> |
---|
20 | #include <string.h> |
---|
21 | #include <sys/dirent.h> |
---|
22 | #include <cache.h> |
---|
23 | #include <core.h> |
---|
24 | #include <disk.h> |
---|
25 | #include <fs.h> |
---|
26 | #include <ilog2.h> |
---|
27 | #include <klibc/compiler.h> |
---|
28 | #include <ctype.h> |
---|
29 | |
---|
30 | #include "codepage.h" |
---|
31 | #include "xfs_types.h" |
---|
32 | #include "xfs_sb.h" |
---|
33 | #include "xfs_ag.h" |
---|
34 | #include "misc.h" |
---|
35 | #include "xfs.h" |
---|
36 | #include "xfs_dinode.h" |
---|
37 | #include "xfs_dir2.h" |
---|
38 | #include "xfs_readdir.h" |
---|
39 | |
---|
40 | static inline int xfs_fmt_local_readdir(struct file *file, |
---|
41 | struct dirent *dirent, |
---|
42 | xfs_dinode_t *core) |
---|
43 | { |
---|
44 | return xfs_readdir_dir2_local(file, dirent, core); |
---|
45 | } |
---|
46 | |
---|
47 | static inline int xfs_fmt_extents_readdir(struct file *file, |
---|
48 | struct dirent *dirent, |
---|
49 | xfs_dinode_t *core) |
---|
50 | { |
---|
51 | if (be32_to_cpu(core->di_nextents) <= 1) { |
---|
52 | /* Single-block Directories */ |
---|
53 | return xfs_readdir_dir2_block(file, dirent, core); |
---|
54 | } else if (xfs_dir2_isleaf(file->fs, core)) { |
---|
55 | /* Leaf Directory */ |
---|
56 | return xfs_readdir_dir2_leaf(file, dirent, core); |
---|
57 | } else { |
---|
58 | /* Node Directory */ |
---|
59 | return xfs_readdir_dir2_node(file, dirent, core); |
---|
60 | } |
---|
61 | } |
---|
62 | |
---|
63 | static int xfs_readdir(struct file *file, struct dirent *dirent) |
---|
64 | { |
---|
65 | struct fs_info *fs = file->fs; |
---|
66 | xfs_dinode_t *core; |
---|
67 | struct inode *inode = file->inode; |
---|
68 | |
---|
69 | xfs_debug("file %p dirent %p"); |
---|
70 | |
---|
71 | core = xfs_dinode_get_core(fs, inode->ino); |
---|
72 | if (!core) { |
---|
73 | xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino); |
---|
74 | return -1; |
---|
75 | } |
---|
76 | |
---|
77 | if (core->di_format == XFS_DINODE_FMT_LOCAL) |
---|
78 | return xfs_fmt_local_readdir(file, dirent, core); |
---|
79 | else if (core->di_format == XFS_DINODE_FMT_EXTENTS) |
---|
80 | return xfs_fmt_extents_readdir(file, dirent, core); |
---|
81 | |
---|
82 | return -1; |
---|
83 | } |
---|
84 | |
---|
85 | static uint32_t xfs_getfssec(struct file *file, char *buf, int sectors, |
---|
86 | bool *have_more) |
---|
87 | { |
---|
88 | return generic_getfssec(file, buf, sectors, have_more); |
---|
89 | } |
---|
90 | |
---|
91 | static int xfs_next_extent(struct inode *inode, uint32_t lstart) |
---|
92 | { |
---|
93 | struct fs_info *fs = inode->fs; |
---|
94 | xfs_dinode_t *core = NULL; |
---|
95 | xfs_bmbt_irec_t rec; |
---|
96 | block_t bno; |
---|
97 | xfs_bmdr_block_t *rblock; |
---|
98 | int fsize; |
---|
99 | xfs_bmbt_ptr_t *pp; |
---|
100 | xfs_btree_block_t *blk; |
---|
101 | uint16_t nextents; |
---|
102 | block_t nextbno; |
---|
103 | uint32_t index; |
---|
104 | |
---|
105 | (void)lstart; |
---|
106 | |
---|
107 | xfs_debug("inode %p lstart %lu", inode, lstart); |
---|
108 | |
---|
109 | core = xfs_dinode_get_core(fs, inode->ino); |
---|
110 | if (!core) { |
---|
111 | xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino); |
---|
112 | goto out; |
---|
113 | } |
---|
114 | |
---|
115 | /* The data fork contains the file's data extents */ |
---|
116 | if (XFS_PVT(inode)->i_cur_extent == be32_to_cpu(core->di_nextents)) |
---|
117 | goto out; |
---|
118 | |
---|
119 | if (core->di_format == XFS_DINODE_FMT_EXTENTS) { |
---|
120 | bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] + |
---|
121 | XFS_PVT(inode)->i_cur_extent++); |
---|
122 | |
---|
123 | bno = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs); |
---|
124 | |
---|
125 | XFS_PVT(inode)->i_offset = rec.br_startoff; |
---|
126 | |
---|
127 | inode->next_extent.pstart = bno << BLOCK_SHIFT(fs) >> SECTOR_SHIFT(fs); |
---|
128 | inode->next_extent.len = ((rec.br_blockcount << BLOCK_SHIFT(fs)) + |
---|
129 | SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs); |
---|
130 | } else if (core->di_format == XFS_DINODE_FMT_BTREE) { |
---|
131 | xfs_debug("XFS_DINODE_FMT_BTREE"); |
---|
132 | index = XFS_PVT(inode)->i_cur_extent++; |
---|
133 | rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0]; |
---|
134 | fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK); |
---|
135 | pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0)); |
---|
136 | bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs); |
---|
137 | |
---|
138 | /* Find the leaf */ |
---|
139 | for (;;) { |
---|
140 | blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); |
---|
141 | if (be16_to_cpu(blk->bb_level) == 0) |
---|
142 | break; |
---|
143 | |
---|
144 | pp = XFS_BMBT_PTR_ADDR(fs, blk, 1, |
---|
145 | xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0)); |
---|
146 | bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs); |
---|
147 | } |
---|
148 | |
---|
149 | /* Find the right extent among threaded leaves */ |
---|
150 | for (;;) { |
---|
151 | nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib); |
---|
152 | nextents = be16_to_cpu(blk->bb_numrecs); |
---|
153 | if (nextents - index > 0) { |
---|
154 | bmbt_irec_get(&rec, XFS_BMDR_REC_ADDR(blk, index + 1)); |
---|
155 | |
---|
156 | bno = fsblock_to_bytes(fs, rec.br_startblock) |
---|
157 | >> BLOCK_SHIFT(fs); |
---|
158 | |
---|
159 | XFS_PVT(inode)->i_offset = rec.br_startoff; |
---|
160 | |
---|
161 | inode->next_extent.pstart = bno << BLOCK_SHIFT(fs) |
---|
162 | >> SECTOR_SHIFT(fs); |
---|
163 | inode->next_extent.len = ((rec.br_blockcount |
---|
164 | << BLOCK_SHIFT(fs)) |
---|
165 | + SECTOR_SIZE(fs) - 1) |
---|
166 | >> SECTOR_SHIFT(fs); |
---|
167 | break; |
---|
168 | } |
---|
169 | |
---|
170 | index -= nextents; |
---|
171 | bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs); |
---|
172 | blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); |
---|
173 | } |
---|
174 | } |
---|
175 | |
---|
176 | return 0; |
---|
177 | |
---|
178 | out: |
---|
179 | return -1; |
---|
180 | } |
---|
181 | |
---|
182 | static inline struct inode *xfs_fmt_local_find_entry(const char *dname, |
---|
183 | struct inode *parent, |
---|
184 | xfs_dinode_t *core) |
---|
185 | { |
---|
186 | return xfs_dir2_local_find_entry(dname, parent, core); |
---|
187 | } |
---|
188 | |
---|
189 | static inline struct inode *xfs_fmt_extents_find_entry(const char *dname, |
---|
190 | struct inode *parent, |
---|
191 | xfs_dinode_t *core) |
---|
192 | { |
---|
193 | if (be32_to_cpu(core->di_nextents) <= 1) { |
---|
194 | /* Single-block Directories */ |
---|
195 | return xfs_dir2_block_find_entry(dname, parent, core); |
---|
196 | } else if (xfs_dir2_isleaf(parent->fs, core)) { |
---|
197 | /* Leaf Directory */ |
---|
198 | return xfs_dir2_leaf_find_entry(dname, parent, core); |
---|
199 | } else { |
---|
200 | /* Node Directory */ |
---|
201 | return xfs_dir2_node_find_entry(dname, parent, core); |
---|
202 | } |
---|
203 | } |
---|
204 | |
---|
205 | static inline struct inode *xfs_fmt_btree_find_entry(const char *dname, |
---|
206 | struct inode *parent, |
---|
207 | xfs_dinode_t *core) |
---|
208 | { |
---|
209 | return xfs_dir2_node_find_entry(dname, parent, core); |
---|
210 | } |
---|
211 | |
---|
212 | static struct inode *xfs_iget(const char *dname, struct inode *parent) |
---|
213 | { |
---|
214 | struct fs_info *fs = parent->fs; |
---|
215 | xfs_dinode_t *core = NULL; |
---|
216 | struct inode *inode = NULL; |
---|
217 | |
---|
218 | xfs_debug("dname %s parent %p parent ino %lu", dname, parent, parent->ino); |
---|
219 | |
---|
220 | core = xfs_dinode_get_core(fs, parent->ino); |
---|
221 | if (!core) { |
---|
222 | xfs_error("Failed to get dinode from disk (ino 0x%llx)", parent->ino); |
---|
223 | goto out; |
---|
224 | } |
---|
225 | |
---|
226 | if (core->di_format == XFS_DINODE_FMT_LOCAL) { |
---|
227 | inode = xfs_fmt_local_find_entry(dname, parent, core); |
---|
228 | } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) { |
---|
229 | inode = xfs_fmt_extents_find_entry(dname, parent, core); |
---|
230 | } else if (core->di_format == XFS_DINODE_FMT_BTREE) { |
---|
231 | inode = xfs_fmt_btree_find_entry(dname, parent, core); |
---|
232 | } |
---|
233 | |
---|
234 | if (!inode) { |
---|
235 | xfs_debug("Entry not found!"); |
---|
236 | goto out; |
---|
237 | } |
---|
238 | |
---|
239 | if (inode->mode == DT_REG) { |
---|
240 | XFS_PVT(inode)->i_offset = 0; |
---|
241 | XFS_PVT(inode)->i_cur_extent = 0; |
---|
242 | } else if (inode->mode == DT_DIR) { |
---|
243 | XFS_PVT(inode)->i_btree_offset = 0; |
---|
244 | XFS_PVT(inode)->i_leaf_ent_offset = 0; |
---|
245 | } |
---|
246 | |
---|
247 | return inode; |
---|
248 | |
---|
249 | out: |
---|
250 | return NULL; |
---|
251 | } |
---|
252 | |
---|
253 | static int xfs_readlink(struct inode *inode, char *buf) |
---|
254 | { |
---|
255 | struct fs_info *fs = inode->fs; |
---|
256 | xfs_dinode_t *core; |
---|
257 | int pathlen = -1; |
---|
258 | xfs_bmbt_irec_t rec; |
---|
259 | block_t db; |
---|
260 | const char *dir_buf; |
---|
261 | |
---|
262 | xfs_debug("inode %p buf %p", inode, buf); |
---|
263 | |
---|
264 | core = xfs_dinode_get_core(fs, inode->ino); |
---|
265 | if (!core) { |
---|
266 | xfs_error("Failed to get dinode from disk (ino 0x%llx)", inode->ino); |
---|
267 | goto out; |
---|
268 | } |
---|
269 | |
---|
270 | pathlen = be64_to_cpu(core->di_size); |
---|
271 | if (!pathlen) |
---|
272 | goto out; |
---|
273 | |
---|
274 | if (pathlen < 0 || pathlen > MAXPATHLEN) { |
---|
275 | xfs_error("inode (%llu) bad symlink length (%d)", |
---|
276 | inode->ino, pathlen); |
---|
277 | goto out; |
---|
278 | } |
---|
279 | |
---|
280 | if (core->di_format == XFS_DINODE_FMT_LOCAL) { |
---|
281 | memcpy(buf, (char *)&core->di_literal_area[0], pathlen); |
---|
282 | } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) { |
---|
283 | bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0]); |
---|
284 | db = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs); |
---|
285 | dir_buf = xfs_dir2_dirblks_get_cached(fs, db, rec.br_blockcount); |
---|
286 | |
---|
287 | /* |
---|
288 | * Syslinux only supports filesystem block size larger than or equal to |
---|
289 | * 4 KiB. Thus, one directory block is far enough to hold the maximum |
---|
290 | * symbolic link file content, which is only 1024 bytes long. |
---|
291 | */ |
---|
292 | memcpy(buf, dir_buf, pathlen); |
---|
293 | } |
---|
294 | |
---|
295 | out: |
---|
296 | return pathlen; |
---|
297 | } |
---|
298 | |
---|
299 | static struct inode *xfs_iget_root(struct fs_info *fs) |
---|
300 | { |
---|
301 | xfs_dinode_t *core = NULL; |
---|
302 | struct inode *inode = xfs_new_inode(fs); |
---|
303 | |
---|
304 | xfs_debug("Looking for the root inode..."); |
---|
305 | |
---|
306 | core = xfs_dinode_get_core(fs, XFS_INFO(fs)->rootino); |
---|
307 | if (!core) { |
---|
308 | xfs_error("Inode core's magic number does not match!"); |
---|
309 | xfs_debug("magic number 0x%04x", be16_to_cpu(core->di_magic)); |
---|
310 | goto out; |
---|
311 | } |
---|
312 | |
---|
313 | fill_xfs_inode_pvt(fs, inode, XFS_INFO(fs)->rootino); |
---|
314 | |
---|
315 | xfs_debug("Root inode has been found!"); |
---|
316 | |
---|
317 | if ((be16_to_cpu(core->di_mode) & S_IFMT) != S_IFDIR) { |
---|
318 | xfs_error("root inode is not a directory ?! No makes sense..."); |
---|
319 | goto out; |
---|
320 | } |
---|
321 | |
---|
322 | inode->ino = XFS_INFO(fs)->rootino; |
---|
323 | inode->mode = DT_DIR; |
---|
324 | inode->size = be64_to_cpu(core->di_size); |
---|
325 | |
---|
326 | return inode; |
---|
327 | |
---|
328 | out: |
---|
329 | free(inode); |
---|
330 | |
---|
331 | return NULL; |
---|
332 | } |
---|
333 | |
---|
334 | static inline int xfs_read_superblock(struct fs_info *fs, xfs_sb_t *sb) |
---|
335 | { |
---|
336 | struct disk *disk = fs->fs_dev->disk; |
---|
337 | |
---|
338 | if (!disk->rdwr_sectors(disk, sb, XFS_SB_DADDR, 1, false)) |
---|
339 | return -1; |
---|
340 | |
---|
341 | return 0; |
---|
342 | } |
---|
343 | |
---|
344 | static struct xfs_fs_info *xfs_new_sb_info(xfs_sb_t *sb) |
---|
345 | { |
---|
346 | struct xfs_fs_info *info; |
---|
347 | |
---|
348 | info = malloc(sizeof *info); |
---|
349 | if (!info) |
---|
350 | malloc_error("xfs_fs_info structure"); |
---|
351 | |
---|
352 | info->blocksize = be32_to_cpu(sb->sb_blocksize); |
---|
353 | info->block_shift = sb->sb_blocklog; |
---|
354 | info->dirblksize = 1 << (sb->sb_blocklog + sb->sb_dirblklog); |
---|
355 | info->dirblklog = sb->sb_dirblklog; |
---|
356 | info->inopb_shift = sb->sb_inopblog; |
---|
357 | info->agblk_shift = sb->sb_agblklog; |
---|
358 | info->rootino = be64_to_cpu(sb->sb_rootino); |
---|
359 | info->agblocks = be32_to_cpu(sb->sb_agblocks); |
---|
360 | info->agblocks_shift = sb->sb_agblklog; |
---|
361 | info->agcount = be32_to_cpu(sb->sb_agcount); |
---|
362 | info->inodesize = be16_to_cpu(sb->sb_inodesize); |
---|
363 | info->inode_shift = sb->sb_inodelog; |
---|
364 | |
---|
365 | return info; |
---|
366 | } |
---|
367 | |
---|
368 | static int xfs_fs_init(struct fs_info *fs) |
---|
369 | { |
---|
370 | struct disk *disk = fs->fs_dev->disk; |
---|
371 | xfs_sb_t sb; |
---|
372 | struct xfs_fs_info *info; |
---|
373 | |
---|
374 | xfs_debug("fs %p", fs); |
---|
375 | |
---|
376 | SECTOR_SHIFT(fs) = disk->sector_shift; |
---|
377 | SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs); |
---|
378 | |
---|
379 | if (xfs_read_superblock(fs, &sb)) { |
---|
380 | xfs_error("Superblock read failed"); |
---|
381 | goto out; |
---|
382 | } |
---|
383 | |
---|
384 | if (!xfs_is_valid_magicnum(&sb)) { |
---|
385 | xfs_error("Invalid superblock"); |
---|
386 | goto out; |
---|
387 | } |
---|
388 | |
---|
389 | xfs_debug("magicnum 0x%lX", be32_to_cpu(sb.sb_magicnum)); |
---|
390 | |
---|
391 | info = xfs_new_sb_info(&sb); |
---|
392 | if (!info) { |
---|
393 | xfs_error("Failed to fill in filesystem-specific info structure"); |
---|
394 | goto out; |
---|
395 | } |
---|
396 | |
---|
397 | fs->fs_info = info; |
---|
398 | |
---|
399 | xfs_debug("block_shift %u blocksize 0x%lX (%lu)", info->block_shift, |
---|
400 | info->blocksize, info->blocksize); |
---|
401 | |
---|
402 | xfs_debug("rootino 0x%llX (%llu)", info->rootino, info->rootino); |
---|
403 | |
---|
404 | BLOCK_SHIFT(fs) = info->block_shift; |
---|
405 | BLOCK_SIZE(fs) = info->blocksize; |
---|
406 | |
---|
407 | cache_init(fs->fs_dev, BLOCK_SHIFT(fs)); |
---|
408 | |
---|
409 | XFS_INFO(fs)->dirleafblk = xfs_dir2_db_to_da(fs, XFS_DIR2_LEAF_FIRSTDB(fs)); |
---|
410 | |
---|
411 | return BLOCK_SHIFT(fs); |
---|
412 | |
---|
413 | out: |
---|
414 | return -1; |
---|
415 | } |
---|
416 | |
---|
417 | const struct fs_ops xfs_fs_ops = { |
---|
418 | .fs_name = "xfs", |
---|
419 | .fs_flags = FS_USEMEM | FS_THISIND, |
---|
420 | .fs_init = xfs_fs_init, |
---|
421 | .iget_root = xfs_iget_root, |
---|
422 | .searchdir = NULL, |
---|
423 | .getfssec = xfs_getfssec, |
---|
424 | .open_config = generic_open_config, |
---|
425 | .close_file = generic_close_file, |
---|
426 | .mangle_name = generic_mangle_name, |
---|
427 | .readdir = xfs_readdir, |
---|
428 | .iget = xfs_iget, |
---|
429 | .next_extent = xfs_next_extent, |
---|
430 | .readlink = xfs_readlink, |
---|
431 | .fs_uuid = NULL, |
---|
432 | }; |
---|