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 */
35static bool
36do_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 */
60static int
61ufs_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 */
83static const void *
84ufs_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 */
113static const struct ufs_dir_entry *
114ufs_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 */
151static const void *
152ufs_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
194static struct inode *
195ufs1_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
231static struct inode *
232ufs2_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 */
262static struct inode *
263ufs_iget_root(struct fs_info *fs)
264{
265    return UFS_SB(fs)->ufs_iget_by_inr(fs, UFS_ROOT_INODE);
266}
267
268static struct inode *
269ufs_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
281static 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
302static 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 */
311static 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
335static 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
356static 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 */
374static 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
401static inline struct ufs_sb_info *
402set_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 */
440static 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
472const 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