2 * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com>
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.
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.
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
21 #include <sys/dirent.h>
27 #include <klibc/compiler.h>
31 #include "xfs_types.h"
36 #include "xfs_dinode.h"
38 #include "xfs_readdir.h"
40 static inline int xfs_fmt_local_readdir(struct file
*file
,
41 struct dirent
*dirent
,
44 return xfs_readdir_dir2_local(file
, dirent
, core
);
47 static inline int xfs_fmt_extents_readdir(struct file
*file
,
48 struct dirent
*dirent
,
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
)) {
56 return xfs_readdir_dir2_leaf(file
, dirent
, core
);
59 return xfs_readdir_dir2_node(file
, dirent
, core
);
63 static int xfs_readdir(struct file
*file
, struct dirent
*dirent
)
65 struct fs_info
*fs
= file
->fs
;
67 struct inode
*inode
= file
->inode
;
69 xfs_debug("file %p dirent %p");
71 core
= xfs_dinode_get_core(fs
, inode
->ino
);
73 xfs_error("Failed to get dinode from disk (ino %llx)", inode
->ino
);
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
);
85 static uint32_t xfs_getfssec(struct file
*file
, char *buf
, int sectors
,
88 return generic_getfssec(file
, buf
, sectors
, have_more
);
91 static int xfs_next_extent(struct inode
*inode
, uint32_t lstart
)
93 struct fs_info
*fs
= inode
->fs
;
94 xfs_dinode_t
*core
= NULL
;
97 xfs_bmdr_block_t
*rblock
;
100 xfs_btree_block_t
*blk
;
107 xfs_debug("inode %p lstart %lu", inode
, lstart
);
109 core
= xfs_dinode_get_core(fs
, inode
->ino
);
111 xfs_error("Failed to get dinode from disk (ino %llx)", inode
->ino
);
115 /* The data fork contains the file's data extents */
116 if (XFS_PVT(inode
)->i_cur_extent
== be32_to_cpu(core
->di_nextents
))
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
++);
123 bno
= fsblock_to_bytes(fs
, rec
.br_startblock
) >> BLOCK_SHIFT(fs
);
125 XFS_PVT(inode
)->i_offset
= rec
.br_startoff
;
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
);
140 blk
= (xfs_btree_block_t
*)get_cache(fs
->fs_dev
, bno
);
141 if (be16_to_cpu(blk
->bb_level
) == 0)
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
);
149 /* Find the right extent among threaded leaves */
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));
156 bno
= fsblock_to_bytes(fs
, rec
.br_startblock
)
159 XFS_PVT(inode
)->i_offset
= rec
.br_startoff
;
161 inode
->next_extent
.pstart
= bno
<< BLOCK_SHIFT(fs
)
163 inode
->next_extent
.len
= ((rec
.br_blockcount
165 + SECTOR_SIZE(fs
) - 1)
171 bno
= fsblock_to_bytes(fs
, nextbno
) >> BLOCK_SHIFT(fs
);
172 blk
= (xfs_btree_block_t
*)get_cache(fs
->fs_dev
, bno
);
182 static inline struct inode
*xfs_fmt_local_find_entry(const char *dname
,
183 struct inode
*parent
,
186 return xfs_dir2_local_find_entry(dname
, parent
, core
);
189 static inline struct inode
*xfs_fmt_extents_find_entry(const char *dname
,
190 struct inode
*parent
,
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
)) {
198 return xfs_dir2_leaf_find_entry(dname
, parent
, core
);
201 return xfs_dir2_node_find_entry(dname
, parent
, core
);
205 static inline struct inode
*xfs_fmt_btree_find_entry(const char *dname
,
206 struct inode
*parent
,
209 return xfs_dir2_node_find_entry(dname
, parent
, core
);
212 static struct inode
*xfs_iget(const char *dname
, struct inode
*parent
)
214 struct fs_info
*fs
= parent
->fs
;
215 xfs_dinode_t
*core
= NULL
;
216 struct inode
*inode
= NULL
;
218 xfs_debug("dname %s parent %p parent ino %lu", dname
, parent
, parent
->ino
);
220 core
= xfs_dinode_get_core(fs
, parent
->ino
);
222 xfs_error("Failed to get dinode from disk (ino 0x%llx)", parent
->ino
);
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
);
235 xfs_debug("Entry not found!");
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;
253 static int xfs_readlink(struct inode
*inode
, char *buf
)
255 struct fs_info
*fs
= inode
->fs
;
262 xfs_debug("inode %p buf %p", inode
, buf
);
264 core
= xfs_dinode_get_core(fs
, inode
->ino
);
266 xfs_error("Failed to get dinode from disk (ino 0x%llx)", inode
->ino
);
270 pathlen
= be64_to_cpu(core
->di_size
);
274 if (pathlen
< 0 || pathlen
> MAXPATHLEN
) {
275 xfs_error("inode (%llu) bad symlink length (%d)",
276 inode
->ino
, pathlen
);
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
);
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.
292 memcpy(buf
, dir_buf
, pathlen
);
299 static struct inode
*xfs_iget_root(struct fs_info
*fs
)
301 xfs_dinode_t
*core
= NULL
;
302 struct inode
*inode
= xfs_new_inode(fs
);
304 xfs_debug("Looking for the root inode...");
306 core
= xfs_dinode_get_core(fs
, XFS_INFO(fs
)->rootino
);
308 xfs_error("Inode core's magic number does not match!");
309 xfs_debug("magic number 0x%04x", be16_to_cpu(core
->di_magic
));
313 fill_xfs_inode_pvt(fs
, inode
, XFS_INFO(fs
)->rootino
);
315 xfs_debug("Root inode has been found!");
317 if ((be16_to_cpu(core
->di_mode
) & S_IFMT
) != S_IFDIR
) {
318 xfs_error("root inode is not a directory ?! No makes sense...");
322 inode
->ino
= XFS_INFO(fs
)->rootino
;
323 inode
->mode
= DT_DIR
;
324 inode
->size
= be64_to_cpu(core
->di_size
);
334 static inline int xfs_read_superblock(struct fs_info
*fs
, xfs_sb_t
*sb
)
336 struct disk
*disk
= fs
->fs_dev
->disk
;
338 if (!disk
->rdwr_sectors(disk
, sb
, XFS_SB_DADDR
, 1, false))
344 static struct xfs_fs_info
*xfs_new_sb_info(xfs_sb_t
*sb
)
346 struct xfs_fs_info
*info
;
348 info
= malloc(sizeof *info
);
350 malloc_error("xfs_fs_info structure");
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
;
368 static int xfs_fs_init(struct fs_info
*fs
)
370 struct disk
*disk
= fs
->fs_dev
->disk
;
372 struct xfs_fs_info
*info
;
374 xfs_debug("fs %p", fs
);
376 SECTOR_SHIFT(fs
) = disk
->sector_shift
;
377 SECTOR_SIZE(fs
) = 1 << SECTOR_SHIFT(fs
);
379 if (xfs_read_superblock(fs
, &sb
)) {
380 xfs_error("Superblock read failed");
384 if (!xfs_is_valid_magicnum(&sb
)) {
385 xfs_error("Invalid superblock");
389 xfs_debug("magicnum 0x%lX", be32_to_cpu(sb
.sb_magicnum
));
391 info
= xfs_new_sb_info(&sb
);
393 xfs_error("Failed to fill in filesystem-specific info structure");
399 xfs_debug("block_shift %u blocksize 0x%lX (%lu)", info
->block_shift
,
400 info
->blocksize
, info
->blocksize
);
402 xfs_debug("rootino 0x%llX (%llu)", info
->rootino
, info
->rootino
);
404 BLOCK_SHIFT(fs
) = info
->block_shift
;
405 BLOCK_SIZE(fs
) = info
->blocksize
;
407 cache_init(fs
->fs_dev
, BLOCK_SHIFT(fs
));
409 XFS_INFO(fs
)->dirleafblk
= xfs_dir2_db_to_da(fs
, XFS_DIR2_LEAF_FIRSTDB(fs
));
411 return BLOCK_SHIFT(fs
);
417 const struct fs_ops xfs_fs_ops
= {
419 .fs_flags
= FS_USEMEM
| FS_THISIND
,
420 .fs_init
= xfs_fs_init
,
421 .iget_root
= xfs_iget_root
,
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
,
429 .next_extent
= xfs_next_extent
,
430 .readlink
= xfs_readlink
,