• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
xfs_fmt_local_readdir(struct file * file,struct dirent * dirent,xfs_dinode_t * core)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 
xfs_fmt_extents_readdir(struct file * file,struct dirent * dirent,xfs_dinode_t * core)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 
xfs_readdir(struct file * file,struct dirent * dirent)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 
xfs_getfssec(struct file * file,char * buf,int sectors,bool * have_more)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 
xfs_next_extent(struct inode * inode,uint32_t lstart)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 
xfs_fmt_local_find_entry(const char * dname,struct inode * parent,xfs_dinode_t * core)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 
xfs_fmt_extents_find_entry(const char * dname,struct inode * parent,xfs_dinode_t * core)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 
xfs_fmt_btree_find_entry(const char * dname,struct inode * parent,xfs_dinode_t * core)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 
xfs_iget(const char * dname,struct inode * parent)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 
xfs_readlink(struct inode * inode,char * buf)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 
xfs_iget_root(struct fs_info * fs)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 
xfs_read_superblock(struct fs_info * fs,xfs_sb_t * sb)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 
xfs_new_sb_info(xfs_sb_t * sb)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 
xfs_fs_init(struct fs_info * fs)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 };
433