source: bootcd/isolinux/syslinux-6.03/core/fs/xfs/xfs.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 12.1 KB
Line 
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
40static 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
47static 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
63static 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
85static 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
91static 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
178out:
179    return -1;
180}
181
182static 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
189static 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
205static 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
212static 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
249out:
250    return NULL;
251}
252
253static 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
295out:
296    return pathlen;
297}
298
299static 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
328out:
329    free(inode);
330
331    return NULL;
332}
333
334static 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
344static 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
368static 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
413out:
414    return -1;
415}
416
417const 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};
Note: See TracBrowser for help on using the repository browser.