• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc.,
17  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 #include <dprintf.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <sys/dirent.h>
24 #include <cache.h>
25 #include <disk.h>
26 #include <fs.h>
27 #include <minmax.h>
28 #include "core.h"
29 #include "ufs.h"
30 
31 /*
32  * Read the super block and check magic fields based on
33  * passed paramaters.
34  */
35 static bool
do_checksb(struct ufs_super_block * sb,struct disk * disk,const uint32_t sblock_off,const uint32_t ufs_smagic)36 do_checksb(struct ufs_super_block *sb, struct disk *disk,
37 	    const uint32_t sblock_off, const uint32_t ufs_smagic)
38 {
39     uint32_t lba;
40     static uint32_t count;
41 
42     /* How many sectors are needed to fill sb struct */
43     if (!count)
44 	count = sizeof *sb >> disk->sector_shift;
45     /* Get lba address based on sector size of disk */
46     lba = sblock_off >> (disk->sector_shift);
47     /* Read super block */
48     disk->rdwr_sectors(disk, sb, lba, count, 0);
49 
50     if (sb->magic == ufs_smagic)
51 	return true;
52 
53     return false;
54 }
55 
56 /*
57  * Go through all possible ufs superblock offsets.
58  * TODO: Add UFS support to removable media (sb offset: 0).
59  */
60 static int
ufs_checksb(struct ufs_super_block * sb,struct disk * disk)61 ufs_checksb(struct ufs_super_block *sb, struct disk *disk)
62 {
63     /* Check for UFS1 sb */
64     if (do_checksb(sb, disk, UFS1_SBLOCK_OFFSET, UFS1_SUPER_MAGIC))
65 	return UFS1;
66     /* Check for UFS2 sb */
67     if (do_checksb(sb, disk, UFS2_SBLOCK_OFFSET, UFS2_SUPER_MAGIC))
68 	return UFS2;
69     /* UFS2 may also exist in 256k-, but this isn't the default */
70     if (do_checksb(sb, disk, UFS2_SBLOCK2_OFFSET, UFS2_SUPER_MAGIC))
71 	return UFS2_PIGGY;
72 
73     return NONE;
74 }
75 
76 /*
77  * lblock stands for linear block address,
78  * whereas pblock is the actual blk ptr to get data from.
79  *
80  * UFS1/2 use frag addrs rather than blk ones, then
81  * the offset into the block must be calculated.
82  */
83 static const void *
ufs_get_cache(struct inode * inode,block_t lblock)84 ufs_get_cache(struct inode *inode, block_t lblock)
85 {
86     const void *data;
87     struct fs_info *fs = inode->fs;
88     struct ufs_sb_info *sb = UFS_SB(inode->fs);
89     uint64_t frag_addr, frag_offset;
90     uint32_t frag_shift;
91     block_t pblock;
92 
93     frag_addr = ufs_bmap(inode, lblock, NULL);
94     if (!frag_addr)
95 	return NULL;
96 
97     frag_shift = fs->block_shift - sb->c_blk_frag_shift;
98     /* Get fragment byte address */
99     frag_offset = frag_addr << frag_shift;
100     /* Convert frag addr to blk addr */
101     pblock = frag_to_blk(fs, frag_addr);
102     /* Read the blk */
103     data = get_cache(fs->fs_dev, pblock);
104 
105     /* Return offset into block */
106     return data + (frag_offset & (fs->block_size - 1));
107 }
108 
109 /*
110  * Based on fs/ext2/ext2.c
111  * find a dir entry, return it if found, or return NULL.
112  */
113 static const struct ufs_dir_entry *
ufs_find_entry(struct fs_info * fs,struct inode * inode,const char * dname)114 ufs_find_entry(struct fs_info *fs, struct inode *inode, const char *dname)
115 {
116     const struct ufs_dir_entry *dir;
117     const char *data;
118     int32_t i, offset, maxoffset;
119     block_t index = 0;
120 
121     ufs_debug("ufs_find_entry: dname: %s ", dname);
122     for (i = 0; i < inode->size; i += fs->block_size) {
123 	data = ufs_get_cache(inode, index++);
124 	offset = 0;
125 	maxoffset = min(inode->size-i, fs->block_size);
126 
127 	/* The smallest possible size is 9 bytes */
128 	while (offset < maxoffset-8) {
129 	    dir = (const struct ufs_dir_entry *)(data + offset);
130 	    if (dir->dir_entry_len > maxoffset - offset)
131 		break;
132 
133 	    /*
134 	     * Name fields are variable-length and null terminated,
135 	     * then it's possible to use strcmp directly.
136 	     */
137 	    if (dir->inode_value && !strcmp(dname, (const char *)dir->name)) {
138 		ufs_debug("(found)\n");
139 		return dir;
140 	    }
141 	    offset += dir->dir_entry_len;
142 	}
143     }
144     ufs_debug("(not found)\n");
145     return NULL;
146 }
147 
148 /*
149  * Get either UFS1/2 inode structures.
150  */
151 static const void *
ufs_get_inode(struct fs_info * fs,int inr)152 ufs_get_inode(struct fs_info *fs, int inr)
153 {
154     const char *data;
155     uint32_t group, inode_offset, inode_table;
156     uint32_t block_num, block_off;
157 
158     /* Get cylinder group nr. */
159     group = inr / UFS_SB(fs)->inodes_per_cg;
160     /*
161      * Ensuring group will not exceed the range 0:groups_count-1.
162      * By the way, this should *never* happen.
163      * Unless the (on-disk) fs structure is corrupted!
164      */
165     if (group >= UFS_SB(fs)->groups_count) {
166 	printf("ufs_get_inode: "
167 		"group(%d) exceeded the avail. range (0:%d)\n",
168 		group, UFS_SB(fs)->groups_count - 1);
169 	return NULL;
170     }
171 
172     /* Offset into inode table of the cylinder group */
173     inode_offset = inr % UFS_SB(fs)->inodes_per_cg;
174     /* Get inode table blk addr respective to cylinder group */
175     inode_table = (group * UFS_SB(fs)->blocks_per_cg) +
176 	UFS_SB(fs)->off_inode_tbl;
177     /* Calculating staggering offset (UFS1 only!) */
178     if (UFS_SB(fs)->fs_type == UFS1)
179 	inode_table += UFS_SB(fs)->ufs1.delta_value *
180 	    (group & UFS_SB(fs)->ufs1.cycle_mask);
181 
182     /* Get blk nr and offset into the blk */
183     block_num = inode_table + inode_offset / UFS_SB(fs)->inodes_per_block;
184     block_off = inode_offset % UFS_SB(fs)->inodes_per_block;
185 
186     /*
187      * Read the blk from the blk addr previously computed;
188      * Calc the inode struct offset into the read block.
189      */
190     data = get_cache(fs->fs_dev, block_num);
191     return data + block_off * UFS_SB(fs)->inode_size;
192 }
193 
194 static struct inode *
ufs1_iget_by_inr(struct fs_info * fs,uint32_t inr)195 ufs1_iget_by_inr(struct fs_info *fs, uint32_t inr)
196 {
197     const struct ufs1_inode *ufs_inode;
198     struct inode *inode;
199     uint64_t *dest;
200     uint32_t *source;
201     int i;
202 
203     ufs_inode = (struct ufs1_inode *) ufs_get_inode(fs, inr);
204     if (!ufs_inode)
205 	return NULL;
206 
207     if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt))))
208 	return NULL;
209 
210     /* UFS1 doesn't support neither creation nor deletion times */
211     inode->refcnt  = ufs_inode->link_count;
212     inode->mode    = IFTODT(ufs_inode->file_mode);
213     inode->size    = ufs_inode->size;
214     inode->atime   = ufs_inode->a_time;
215     inode->mtime   = ufs_inode->m_time;
216     inode->blocks  = ufs_inode->blocks_held;
217     inode->flags   = ufs_inode->flags;
218 
219     /*
220      * Copy and extend blk pointers to 64 bits, so avoid
221      * having two structures for inode private.
222      */
223     dest = (uint64_t *) inode->pvt;
224     source = (uint32_t *) ufs_inode->direct_blk_ptr;
225     for (i = 0; i < UFS_NBLOCKS; i++)
226 	dest[i] = ((uint64_t) source[i]) & 0xFFFFFFFF;
227 
228     return inode;
229 }
230 
231 static struct inode *
ufs2_iget_by_inr(struct fs_info * fs,uint32_t inr)232 ufs2_iget_by_inr(struct fs_info *fs, uint32_t inr)
233 {
234     const struct ufs2_inode *ufs_inode;
235     struct inode *inode;
236 
237     ufs_inode = (struct ufs2_inode *) ufs_get_inode(fs, inr);
238     if (!ufs_inode)
239 	return NULL;
240 
241     if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt))))
242 	return NULL;
243 
244     /* UFS2 doesn't support deletion time */
245     inode->refcnt  = ufs_inode->link_count;
246     inode->mode    = IFTODT(ufs_inode->file_mode);
247     inode->size    = ufs_inode->size;
248     inode->atime   = ufs_inode->a_time;
249     inode->ctime   = ufs_inode->creat_time;
250     inode->mtime   = ufs_inode->m_time;
251     inode->blocks  = ufs_inode->bytes_held >> fs->block_shift;
252     inode->flags   = ufs_inode->flags;
253     memcpy(inode->pvt, ufs_inode->direct_blk_ptr,
254 	   sizeof(uint64_t) * UFS_NBLOCKS);
255 
256     return inode;
257 }
258 
259 /*
260  * Both ufs_iget_root and ufs_iget callback based on ufs type.
261  */
262 static struct inode *
ufs_iget_root(struct fs_info * fs)263 ufs_iget_root(struct fs_info *fs)
264 {
265     return UFS_SB(fs)->ufs_iget_by_inr(fs, UFS_ROOT_INODE);
266 }
267 
268 static struct inode *
ufs_iget(const char * dname,struct inode * parent)269 ufs_iget(const char *dname, struct inode *parent)
270 {
271     const struct ufs_dir_entry *dir;
272     struct fs_info *fs = parent->fs;
273 
274     dir = ufs_find_entry(fs, parent, dname);
275     if (!dir)
276 	return NULL;
277 
278     return UFS_SB(fs)->ufs_iget_by_inr(fs, dir->inode_value);
279 }
280 
ufs1_read_blkaddrs(struct inode * inode,char * buf)281 static void ufs1_read_blkaddrs(struct inode *inode, char *buf)
282 {
283     uint32_t dest[UFS_NBLOCKS];
284     const uint64_t *source = (uint64_t *) (inode->pvt);
285     int i;
286 
287     /* Convert ufs_inode_pvt uint64_t fields into uint32_t
288      * Upper-half part of ufs1 private blk addrs are always supposed to be
289      * zero (it's previosuly extended by us), thus data isn't being lost. */
290     for (i = 0; i < UFS_NBLOCKS; i++) {
291         if ((source[i] >> 32) != 0) {
292             /* This should never happen, but will not prevent anything
293              * from working. */
294             ufs_debug("ufs1: inode->pvt[%d]: warning!\n", i);
295         }
296 
297         dest[i] = (uint32_t)(source[i] & 0xFFFFFFFF);
298     }
299     memcpy(buf, (const char *) dest, inode->size);
300 }
301 
ufs2_read_blkaddrs(struct inode * inode,char * buf)302 static void ufs2_read_blkaddrs(struct inode *inode, char *buf)
303 {
304     memcpy(buf, (const char *) (inode->pvt), inode->size);
305 }
306 
307 /*
308  * Taken from ext2/ext2.c.
309  * Read the entire contents of an inode into a memory buffer
310  */
cache_get_file(struct inode * inode,void * buf,size_t bytes)311 static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
312 {
313     struct fs_info *fs = inode->fs;
314     size_t block_size = BLOCK_SIZE(fs);
315     uint32_t index = 0;         /* Logical block number */
316     size_t chunk;
317     const char *data;
318     char *p = buf;
319 
320     if (inode->size > bytes)
321         bytes = inode->size;
322 
323     while (bytes) {
324         chunk = min(bytes, block_size);
325         data = ufs_get_cache(inode, index++);
326         memcpy(p, data, chunk);
327 
328         bytes -= chunk;
329         p += chunk;
330     }
331 
332     return 0;
333 }
334 
ufs_readlink(struct inode * inode,char * buf)335 static int ufs_readlink(struct inode *inode, char *buf)
336 {
337     struct fs_info *fs = inode->fs;
338     uint32_t i_symlink_limit;
339 
340     if (inode->size > BLOCK_SIZE(fs))
341         return -1;              /* Error! */
342 
343     // TODO: use UFS_SB(fs)->maxlen_isymlink instead.
344     i_symlink_limit = ((UFS_SB(fs)->fs_type == UFS1) ?
345         sizeof(uint32_t) : sizeof(uint64_t)) * UFS_NBLOCKS;
346     ufs_debug("UFS_SB(fs)->maxlen_isymlink=%d", UFS_SB(fs)->maxlen_isymlink);
347 
348     if (inode->size <= i_symlink_limit)
349         UFS_SB(fs)->ufs_read_blkaddrs(inode, buf);
350     else
351         cache_get_file(inode, buf, inode->size);
352 
353     return inode->size;
354 }
355 
get_inode_mode(uint8_t type)356 static inline enum dir_type_flags get_inode_mode(uint8_t type)
357 {
358     switch(type) {
359         case UFS_DTYPE_FIFO: return DT_FIFO;
360         case UFS_DTYPE_CHARDEV: return DT_CHR;
361         case UFS_DTYPE_DIR: return DT_DIR;
362         case UFS_DTYPE_BLOCK: return DT_BLK;
363         case UFS_DTYPE_RFILE: return DT_REG;
364         case UFS_DTYPE_SYMLINK: return DT_LNK;
365         case UFS_DTYPE_SOCKET: return DT_SOCK;
366         case UFS_DTYPE_WHITEOUT: return DT_WHT;
367         default: return DT_UNKNOWN;
368     }
369 }
370 
371 /*
372  * Read one directory entry at a time
373  */
ufs_readdir(struct file * file,struct dirent * dirent)374 static int ufs_readdir(struct file *file, struct dirent *dirent)
375 {
376     struct fs_info *fs = file->fs;
377     struct inode *inode = file->inode;
378     const struct ufs_dir_entry *dir;
379     const char *data;
380     block_t index = file->offset >> fs->block_shift;
381 
382     if (file->offset >= inode->size)
383 	return -1;		/* End of file */
384 
385     data = ufs_get_cache(inode, index);
386     dir = (const struct ufs_dir_entry *)
387 	(data + (file->offset & (BLOCK_SIZE(fs) - 1)));
388 
389     dirent->d_ino = dir->inode_value;
390     dirent->d_off = file->offset;
391     dirent->d_reclen = offsetof(struct dirent, d_name) + dir->name_length + 1;
392     dirent->d_type = get_inode_mode(dir->file_type & 0x0F);
393     memcpy(dirent->d_name, dir->name, dir->name_length);
394     dirent->d_name[dir->name_length] = '\0';
395 
396     file->offset += dir->dir_entry_len;  /* Update for next reading */
397 
398     return 0;
399 }
400 
401 static inline struct ufs_sb_info *
set_ufs_info(struct ufs_super_block * sb,int ufs_type)402 set_ufs_info(struct ufs_super_block *sb, int ufs_type)
403 {
404     struct ufs_sb_info *sbi;
405 
406     sbi = malloc(sizeof *sbi);
407     if (!sbi)
408 	malloc_error("ufs_sb_info structure");
409 
410     /* Setting up UFS-dependent info */
411     if (ufs_type == UFS1) {
412 	sbi->inode_size = sizeof (struct ufs1_inode);
413 	sbi->groups_count = sb->ufs1.nr_frags / sb->frags_per_cg;
414 	sbi->ufs1.delta_value = sb->ufs1.delta_value;
415 	sbi->ufs1.cycle_mask = sb->ufs1.cycle_mask;
416 	sbi->ufs_iget_by_inr = ufs1_iget_by_inr;
417         sbi->ufs_read_blkaddrs = ufs1_read_blkaddrs;
418 	sbi->addr_shift = UFS1_ADDR_SHIFT;
419     } else { // UFS2 or UFS2_PIGGY
420 	sbi->inode_size = sizeof (struct ufs2_inode);
421 	sbi->groups_count = sb->ufs2.nr_frags / sb->frags_per_cg;
422 	sbi->ufs_iget_by_inr = ufs2_iget_by_inr;
423         sbi->ufs_read_blkaddrs = ufs2_read_blkaddrs;
424 	sbi->addr_shift = UFS2_ADDR_SHIFT;
425     }
426     sbi->inodes_per_block = sb->block_size / sbi->inode_size;
427     sbi->inodes_per_cg = sb->inodes_per_cg;
428     sbi->blocks_per_cg = sb->frags_per_cg >> sb->c_blk_frag_shift;
429     sbi->off_inode_tbl = sb->off_inode_tbl >> sb->c_blk_frag_shift;
430     sbi->c_blk_frag_shift = sb->c_blk_frag_shift;
431     sbi->maxlen_isymlink = sb->maxlen_isymlink;
432     sbi->fs_type = ufs_type;
433 
434     return sbi;
435 }
436 
437 /*
438  * Init the fs metadata and return block size
439  */
ufs_fs_init(struct fs_info * fs)440 static int ufs_fs_init(struct fs_info *fs)
441 {
442     struct disk *disk = fs->fs_dev->disk;
443     struct ufs_super_block sb;
444     struct cache *cs;
445 
446     int ufs_type = ufs_checksb(&sb, disk);
447     if (ufs_type == NONE)
448 	return -1;
449 
450     ufs_debug("%s SB FOUND!\n", ufs_type == UFS1 ? "UFS1" : "UFS2");
451     ufs_debug("Block size: %u\n", sb.block_size);
452 
453     fs->fs_info = (struct ufs_sb_info *) set_ufs_info(&sb, ufs_type);
454     fs->sector_shift = disk->sector_shift;
455     fs->sector_size  = disk->sector_size;
456     fs->block_shift  = sb.block_shift;
457     fs->block_size   = sb.block_size;
458 
459     /* Initialize the cache, and force a clean on block zero */
460     cache_init(fs->fs_dev, sb.block_shift);
461     cs = _get_cache_block(fs->fs_dev, 0);
462     memset(cs->data, 0, fs->block_size);
463     cache_lock_block(cs);
464 
465     /* For debug purposes */
466     //ufs_checking(fs);
467 
468     //return -1;
469     return fs->block_shift;
470 }
471 
472 const struct fs_ops ufs_fs_ops = {
473     .fs_name        = "ufs",
474     .fs_flags       = FS_USEMEM | FS_THISIND,
475     .fs_init        = ufs_fs_init,
476     .searchdir      = NULL,
477     .getfssec       = generic_getfssec,
478     .close_file     = generic_close_file,
479     .mangle_name    = generic_mangle_name,
480     .open_config    = generic_open_config,
481     .readlink	    = ufs_readlink,
482     .readdir        = ufs_readdir,
483     .iget_root      = ufs_iget_root,
484     .iget           = ufs_iget,
485     .next_extent    = ufs_next_extent,
486 };
487