1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Add a directory by kernel instead of a common root, add qnap-301w and rpi4 kernel 6.1 suppport

This commit is contained in:
Ycarus (Yannick Chabanois) 2023-04-22 08:07:24 +02:00
parent e910436a7a
commit 46837ec4c0
9459 changed files with 362648 additions and 116345 deletions

View file

@ -0,0 +1,38 @@
#
# (C) Copyright 2000-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
#
subdirs-$(CONFIG_CMD_CRAMFS) := cramfs
subdirs-$(CONFIG_CMD_EXT2) += ext2
subdirs-$(CONFIG_CMD_FAT) += fat
subdirs-$(CONFIG_CMD_FDOS) += fdos
subdirs-$(CONFIG_CMD_JFFS2) += jffs2
subdirs-$(CONFIG_CMD_REISER) += reiserfs
subdirs-$(CONFIG_YAFFS2) += yaffs2
subdirs-$(CONFIG_CMD_UBIFS) += ubifs
SUBDIRS := $(subdirs-y)
$(obj).depend all:
@for dir in $(SUBDIRS) ; do \
$(MAKE) -C $$dir $@ ; done

View file

@ -0,0 +1,50 @@
#
# (C) Copyright 2000-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libcramfs.o
AOBJS =
COBJS-$(CONFIG_CMD_CRAMFS) := cramfs.o
COBJS-$(CONFIG_CMD_CRAMFS) += uncompress.o
SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS-y))
#CPPFLAGS +=
all: $(LIB) $(AOBJS)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,346 @@
/*
* cramfs.c
*
* Copyright (C) 1999 Linus Torvalds
*
* Copyright (C) 2000-2002 Transmeta Corporation
*
* Copyright (C) 2003 Kai-Uwe Bloem,
* Auerswald GmbH & Co KG, <linux-development@auerswald.de>
* - adapted from the www.tuxbox.org u-boot tree, added "ls" command
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* Compressed ROM filesystem for Linux.
*
* TODO:
* add support for resolving symbolic links
*/
/*
* These are the VFS interfaces to the compressed ROM filesystem.
* The actual compression is based on zlib, see the other files.
*/
#include <common.h>
#include <malloc.h>
#include <asm/byteorder.h>
#include <linux/stat.h>
#include <jffs2/jffs2.h>
#include <jffs2/load_kernel.h>
#include <cramfs/cramfs_fs.h>
/* These two macros may change in future, to provide better st_ino
semantics. */
#define CRAMINO(x) (CRAMFS_GET_OFFSET(x) ? CRAMFS_GET_OFFSET(x)<<2 : 1)
#define OFFSET(x) ((x)->i_ino)
struct cramfs_super super;
/* CPU address space offset calculation macro, struct part_info offset is
* device address space offset, so we need to shift it by a device start address. */
#if !defined(CONFIG_SYS_NO_FLASH)
extern flash_info_t flash_info[];
#define PART_OFFSET(x) (x->offset + flash_info[x->dev->id->num].start[0])
#else
#define PART_OFFSET(x) (x->offset)
#endif
static int cramfs_read_super (struct part_info *info)
{
unsigned long root_offset;
/* Read the first block and get the superblock from it */
memcpy (&super, (void *) PART_OFFSET(info), sizeof (super));
/* Do sanity checks on the superblock */
if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) {
/* check at 512 byte offset */
memcpy (&super, (void *) PART_OFFSET(info) + 512, sizeof (super));
if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) {
printf ("cramfs: wrong magic\n");
return -1;
}
}
/* flags is reused several times, so swab it once */
super.flags = CRAMFS_32 (super.flags);
super.size = CRAMFS_32 (super.size);
/* get feature flags first */
if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
printf ("cramfs: unsupported filesystem features\n");
return -1;
}
/* Check that the root inode is in a sane state */
if (!S_ISDIR (CRAMFS_16 (super.root.mode))) {
printf ("cramfs: root is not a directory\n");
return -1;
}
root_offset = CRAMFS_GET_OFFSET (&(super.root)) << 2;
if (root_offset == 0) {
printf ("cramfs: empty filesystem");
} else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
((root_offset != sizeof (struct cramfs_super)) &&
(root_offset != 512 + sizeof (struct cramfs_super)))) {
printf ("cramfs: bad root offset %lu\n", root_offset);
return -1;
}
return 0;
}
static unsigned long cramfs_resolve (unsigned long begin, unsigned long offset,
unsigned long size, int raw,
char *filename)
{
unsigned long inodeoffset = 0, nextoffset;
while (inodeoffset < size) {
struct cramfs_inode *inode;
char *name;
int namelen;
inode = (struct cramfs_inode *) (begin + offset +
inodeoffset);
/*
* Namelengths on disk are shifted by two
* and the name padded out to 4-byte boundaries
* with zeroes.
*/
namelen = CRAMFS_GET_NAMELEN (inode) << 2;
name = (char *) inode + sizeof (struct cramfs_inode);
nextoffset =
inodeoffset + sizeof (struct cramfs_inode) + namelen;
for (;;) {
if (!namelen)
return -1;
if (name[namelen - 1])
break;
namelen--;
}
if (!strncmp (filename, name, namelen)) {
char *p = strtok (NULL, "/");
if (raw && (p == NULL || *p == '\0'))
return offset + inodeoffset;
if (S_ISDIR (CRAMFS_16 (inode->mode))) {
return cramfs_resolve (begin,
CRAMFS_GET_OFFSET
(inode) << 2,
CRAMFS_24 (inode->
size), raw,
p);
} else if (S_ISREG (CRAMFS_16 (inode->mode))) {
return offset + inodeoffset;
} else {
printf ("%*.*s: unsupported file type (%x)\n",
namelen, namelen, name,
CRAMFS_16 (inode->mode));
return 0;
}
}
inodeoffset = nextoffset;
}
printf ("can't find corresponding entry\n");
return 0;
}
static int cramfs_uncompress (unsigned long begin, unsigned long offset,
unsigned long loadoffset)
{
struct cramfs_inode *inode = (struct cramfs_inode *) (begin + offset);
unsigned long *block_ptrs = (unsigned long *)
(begin + (CRAMFS_GET_OFFSET (inode) << 2));
unsigned long curr_block = (CRAMFS_GET_OFFSET (inode) +
(((CRAMFS_24 (inode->size)) +
4095) >> 12)) << 2;
int size, total_size = 0;
int i;
cramfs_uncompress_init ();
for (i = 0; i < ((CRAMFS_24 (inode->size) + 4095) >> 12); i++) {
size = cramfs_uncompress_block ((void *) loadoffset,
(void *) (begin + curr_block),
(CRAMFS_32 (block_ptrs[i]) -
curr_block));
if (size < 0)
return size;
loadoffset += size;
total_size += size;
curr_block = CRAMFS_32 (block_ptrs[i]);
}
cramfs_uncompress_exit ();
return total_size;
}
int cramfs_load (char *loadoffset, struct part_info *info, char *filename)
{
unsigned long offset;
if (cramfs_read_super (info))
return -1;
offset = cramfs_resolve (PART_OFFSET(info),
CRAMFS_GET_OFFSET (&(super.root)) << 2,
CRAMFS_24 (super.root.size), 0,
strtok (filename, "/"));
if (offset <= 0)
return offset;
return cramfs_uncompress (PART_OFFSET(info), offset,
(unsigned long) loadoffset);
}
static int cramfs_list_inode (struct part_info *info, unsigned long offset)
{
struct cramfs_inode *inode = (struct cramfs_inode *)
(PART_OFFSET(info) + offset);
char *name, str[20];
int namelen, nextoff;
/*
* Namelengths on disk are shifted by two
* and the name padded out to 4-byte boundaries
* with zeroes.
*/
namelen = CRAMFS_GET_NAMELEN (inode) << 2;
name = (char *) inode + sizeof (struct cramfs_inode);
nextoff = namelen;
for (;;) {
if (!namelen)
return namelen;
if (name[namelen - 1])
break;
namelen--;
}
printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str),
CRAMFS_24 (inode->size), namelen, namelen, name);
if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) {
/* symbolic link.
* Unpack the link target, trusting in the inode's size field.
*/
unsigned long size = CRAMFS_24 (inode->size);
char *link = malloc (size);
if (link != NULL && cramfs_uncompress (PART_OFFSET(info), offset,
(unsigned long) link)
== size)
printf (" -> %*.*s\n", (int) size, (int) size, link);
else
printf (" [Error reading link]\n");
if (link)
free (link);
} else
printf ("\n");
return nextoff;
}
int cramfs_ls (struct part_info *info, char *filename)
{
struct cramfs_inode *inode;
unsigned long inodeoffset = 0, nextoffset;
unsigned long offset, size;
if (cramfs_read_super (info))
return -1;
if (strlen (filename) == 0 || !strcmp (filename, "/")) {
/* Root directory. Use root inode in super block */
offset = CRAMFS_GET_OFFSET (&(super.root)) << 2;
size = CRAMFS_24 (super.root.size);
} else {
/* Resolve the path */
offset = cramfs_resolve (PART_OFFSET(info),
CRAMFS_GET_OFFSET (&(super.root)) <<
2, CRAMFS_24 (super.root.size), 1,
strtok (filename, "/"));
if (offset <= 0)
return offset;
/* Resolving was successful. Examine the inode */
inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset);
if (!S_ISDIR (CRAMFS_16 (inode->mode))) {
/* It's not a directory - list it, and that's that */
return (cramfs_list_inode (info, offset) > 0);
}
/* It's a directory. List files within */
offset = CRAMFS_GET_OFFSET (inode) << 2;
size = CRAMFS_24 (inode->size);
}
/* List the given directory */
while (inodeoffset < size) {
inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset +
inodeoffset);
nextoffset = cramfs_list_inode (info, offset + inodeoffset);
if (nextoffset == 0)
break;
inodeoffset += sizeof (struct cramfs_inode) + nextoffset;
}
return 1;
}
int cramfs_info (struct part_info *info)
{
if (cramfs_read_super (info))
return 0;
printf ("size: 0x%x (%u)\n", super.size, super.size);
if (super.flags != 0) {
printf ("flags:\n");
if (super.flags & CRAMFS_FLAG_FSID_VERSION_2)
printf ("\tFSID version 2\n");
if (super.flags & CRAMFS_FLAG_SORTED_DIRS)
printf ("\tsorted dirs\n");
if (super.flags & CRAMFS_FLAG_HOLES)
printf ("\tholes\n");
if (super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET)
printf ("\tshifted root offset\n");
}
printf ("fsid:\n\tcrc: 0x%x\n\tedition: 0x%x\n",
super.fsid.crc, super.fsid.edition);
printf ("name: %16s\n", super.name);
return 1;
}
int cramfs_check (struct part_info *info)
{
struct cramfs_super *sb;
if (info->dev->id->type != MTD_DEV_TYPE_NOR)
return 0;
sb = (struct cramfs_super *) PART_OFFSET(info);
if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) {
/* check at 512 byte offset */
sb = (struct cramfs_super *) (PART_OFFSET(info) + 512);
if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC))
return 0;
}
return 1;
}

View file

@ -0,0 +1,83 @@
/*
* uncompress.c
*
* Copyright (C) 1999 Linus Torvalds
* Copyright (C) 2000-2002 Transmeta Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* cramfs interfaces to the uncompression library. There's really just
* three entrypoints:
*
* - cramfs_uncompress_init() - called to initialize the thing.
* - cramfs_uncompress_exit() - tell me when you're done
* - cramfs_uncompress_block() - uncompress a block.
*
* NOTE NOTE NOTE! The uncompression is entirely single-threaded. We
* only have one stream, and we'll initialize it only once even if it
* then is used by multiple filesystems.
*/
#include <common.h>
#include <malloc.h>
#include <watchdog.h>
#include <u-boot/zlib.h>
static z_stream stream;
/* Returns length of decompressed data. */
int cramfs_uncompress_block (void *dst, void *src, int srclen)
{
int err;
inflateReset (&stream);
stream.next_in = src;
stream.avail_in = srclen;
stream.next_out = dst;
stream.avail_out = 4096 * 2;
err = inflate (&stream, Z_FINISH);
if (err != Z_STREAM_END)
goto err;
return stream.total_out;
err:
/*printf ("Error %d while decompressing!\n", err); */
/*printf ("%p(%d)->%p\n", src, srclen, dst); */
return -1;
}
int cramfs_uncompress_init (void)
{
int err;
stream.zalloc = gzalloc;
stream.zfree = gzfree;
stream.next_in = 0;
stream.avail_in = 0;
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
stream.outcb = (cb_func) WATCHDOG_RESET;
#else
stream.outcb = Z_NULL;
#endif /* CONFIG_HW_WATCHDOG */
err = inflateInit (&stream);
if (err != Z_OK) {
printf ("Error: inflateInit2() returned %d\n", err);
return -1;
}
return 0;
}
int cramfs_uncompress_exit (void)
{
inflateEnd (&stream);
return 0;
}

View file

@ -0,0 +1,52 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2003
# Pavel Bartusek, Sysgo Real-Time Solutions AG, pba@sysgo.de
#
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libext2fs.o
AOBJS =
COBJS-$(CONFIG_CMD_EXT2) := ext2fs.o dev.o
SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS-y))
#CPPFLAGS +=
all: $(LIB) $(AOBJS)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,131 @@
/*
* (C) Copyright 2004
* esd gmbh <www.esd-electronics.com>
* Reinhard Arlt <reinhard.arlt@esd-electronics.com>
*
* based on code of fs/reiserfs/dev.c by
*
* (C) Copyright 2003 - 2004
* Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common.h>
#include <config.h>
#include <ext2fs.h>
static block_dev_desc_t *ext2fs_block_dev_desc;
static disk_partition_t part_info;
int ext2fs_set_blk_dev(block_dev_desc_t *rbdd, int part)
{
ext2fs_block_dev_desc = rbdd;
if (part == 0) {
/* disk doesn't use partition table */
part_info.start = 0;
part_info.size = rbdd->lba;
part_info.blksz = rbdd->blksz;
} else {
if (get_partition_info
(ext2fs_block_dev_desc, part, &part_info)) {
return 0;
}
}
return part_info.size;
}
int ext2fs_devread(int sector, int byte_offset, int byte_len, char *buf)
{
ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, SECTOR_SIZE);
unsigned sectors;
/*
* Check partition boundaries
*/
if ((sector < 0) ||
((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS)) >=
part_info.size)) {
/* errnum = ERR_OUTSIDE_PART; */
printf(" ** %s read outside partition sector %d\n",
__func__,
sector);
return 0;
}
/*
* Get the read to the beginning of a partition.
*/
sector += byte_offset >> SECTOR_BITS;
byte_offset &= SECTOR_SIZE - 1;
debug(" <%d, %d, %d>\n", sector, byte_offset, byte_len);
if (ext2fs_block_dev_desc == NULL) {
printf(" ** %s Invalid Block Device Descriptor (NULL)\n",
__func__);
return 0;
}
if (byte_offset != 0) {
/* read first part which isn't aligned with start of sector */
if (ext2fs_block_dev_desc->
block_read(ext2fs_block_dev_desc->dev,
part_info.start + sector, 1,
(unsigned long *) sec_buf) != 1) {
printf(" ** %s read error **\n", __func__);
return 0;
}
memcpy(buf, sec_buf + byte_offset,
min(SECTOR_SIZE - byte_offset, byte_len));
buf += min(SECTOR_SIZE - byte_offset, byte_len);
byte_len -= min(SECTOR_SIZE - byte_offset, byte_len);
sector++;
}
/* read sector aligned part */
sectors = byte_len / SECTOR_SIZE;
if (sectors > 0) {
if (ext2fs_block_dev_desc->block_read(
ext2fs_block_dev_desc->dev,
part_info.start + sector,
sectors,
(unsigned long *) buf) != sectors) {
printf(" ** %s read error - block\n", __func__);
return 0;
}
buf += sectors * SECTOR_SIZE;
byte_len -= sectors * SECTOR_SIZE;
sector += sectors;
}
if (byte_len != 0) {
/* read rest of data which are not in whole sector */
if (ext2fs_block_dev_desc->
block_read(ext2fs_block_dev_desc->dev,
part_info.start + sector, 1,
(unsigned long *) sec_buf) != 1) {
printf(" ** %s read error - last part\n", __func__);
return 0;
}
memcpy(buf, sec_buf, byte_len);
}
return 1;
}

View file

@ -0,0 +1,919 @@
/*
* (C) Copyright 2004
* esd gmbh <www.esd-electronics.com>
* Reinhard Arlt <reinhard.arlt@esd-electronics.com>
*
* based on code from grub2 fs/ext2.c and fs/fshelp.c by
*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common.h>
#include <ext2fs.h>
#include <malloc.h>
#include <asm/byteorder.h>
extern int ext2fs_devread (int sector, int byte_offset, int byte_len,
char *buf);
/* Magic value used to identify an ext2 filesystem. */
#define EXT2_MAGIC 0xEF53
/* Amount of indirect blocks in an inode. */
#define INDIRECT_BLOCKS 12
/* Maximum lenght of a pathname. */
#define EXT2_PATH_MAX 4096
/* Maximum nesting of symlinks, used to prevent a loop. */
#define EXT2_MAX_SYMLINKCNT 8
/* Filetype used in directory entry. */
#define FILETYPE_UNKNOWN 0
#define FILETYPE_REG 1
#define FILETYPE_DIRECTORY 2
#define FILETYPE_SYMLINK 7
/* Filetype information as used in inodes. */
#define FILETYPE_INO_MASK 0170000
#define FILETYPE_INO_REG 0100000
#define FILETYPE_INO_DIRECTORY 0040000
#define FILETYPE_INO_SYMLINK 0120000
/* Bits used as offset in sector */
#define DISK_SECTOR_BITS 9
/* Log2 size of ext2 block in 512 blocks. */
#define LOG2_EXT2_BLOCK_SIZE(data) (__le32_to_cpu (data->sblock.log2_block_size) + 1)
/* Log2 size of ext2 block in bytes. */
#define LOG2_BLOCK_SIZE(data) (__le32_to_cpu (data->sblock.log2_block_size) + 10)
/* The size of an ext2 block in bytes. */
#define EXT2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE(data))
/* The ext2 superblock. */
struct ext2_sblock {
uint32_t total_inodes;
uint32_t total_blocks;
uint32_t reserved_blocks;
uint32_t free_blocks;
uint32_t free_inodes;
uint32_t first_data_block;
uint32_t log2_block_size;
uint32_t log2_fragment_size;
uint32_t blocks_per_group;
uint32_t fragments_per_group;
uint32_t inodes_per_group;
uint32_t mtime;
uint32_t utime;
uint16_t mnt_count;
uint16_t max_mnt_count;
uint16_t magic;
uint16_t fs_state;
uint16_t error_handling;
uint16_t minor_revision_level;
uint32_t lastcheck;
uint32_t checkinterval;
uint32_t creator_os;
uint32_t revision_level;
uint16_t uid_reserved;
uint16_t gid_reserved;
uint32_t first_inode;
uint16_t inode_size;
uint16_t block_group_number;
uint32_t feature_compatibility;
uint32_t feature_incompat;
uint32_t feature_ro_compat;
uint32_t unique_id[4];
char volume_name[16];
char last_mounted_on[64];
uint32_t compression_info;
};
/* The ext2 blockgroup. */
struct ext2_block_group {
uint32_t block_id;
uint32_t inode_id;
uint32_t inode_table_id;
uint16_t free_blocks;
uint16_t free_inodes;
uint16_t used_dir_cnt;
uint32_t reserved[3];
};
/* The ext2 inode. */
struct ext2_inode {
uint16_t mode;
uint16_t uid;
uint32_t size;
uint32_t atime;
uint32_t ctime;
uint32_t mtime;
uint32_t dtime;
uint16_t gid;
uint16_t nlinks;
uint32_t blockcnt; /* Blocks of 512 bytes!! */
uint32_t flags;
uint32_t osd1;
union {
struct datablocks {
uint32_t dir_blocks[INDIRECT_BLOCKS];
uint32_t indir_block;
uint32_t double_indir_block;
uint32_t tripple_indir_block;
} blocks;
char symlink[60];
} b;
uint32_t version;
uint32_t acl;
uint32_t dir_acl;
uint32_t fragment_addr;
uint32_t osd2[3];
};
/* The header of an ext2 directory entry. */
struct ext2_dirent {
uint32_t inode;
uint16_t direntlen;
uint8_t namelen;
uint8_t filetype;
};
struct ext2fs_node {
struct ext2_data *data;
struct ext2_inode inode;
int ino;
int inode_read;
};
/* Information about a "mounted" ext2 filesystem. */
struct ext2_data {
struct ext2_sblock sblock;
struct ext2_inode *inode;
struct ext2fs_node diropen;
};
typedef struct ext2fs_node *ext2fs_node_t;
struct ext2_data *ext2fs_root = NULL;
ext2fs_node_t ext2fs_file = NULL;
int symlinknest = 0;
uint32_t *indir1_block = NULL;
int indir1_size = 0;
int indir1_blkno = -1;
uint32_t *indir2_block = NULL;
int indir2_size = 0;
int indir2_blkno = -1;
static unsigned int inode_size;
static int ext2fs_blockgroup
(struct ext2_data *data, int group, struct ext2_block_group *blkgrp) {
unsigned int blkno;
unsigned int blkoff;
unsigned int desc_per_blk;
desc_per_blk = EXT2_BLOCK_SIZE(data) / sizeof(struct ext2_block_group);
blkno = __le32_to_cpu(data->sblock.first_data_block) + 1 +
group / desc_per_blk;
blkoff = (group % desc_per_blk) * sizeof(struct ext2_block_group);
#ifdef DEBUG
printf ("ext2fs read %d group descriptor (blkno %d blkoff %d)\n",
group, blkno, blkoff);
#endif
return (ext2fs_devread (blkno << LOG2_EXT2_BLOCK_SIZE(data),
blkoff, sizeof(struct ext2_block_group), (char *)blkgrp));
}
static int ext2fs_read_inode
(struct ext2_data *data, int ino, struct ext2_inode *inode) {
struct ext2_block_group blkgrp;
struct ext2_sblock *sblock = &data->sblock;
int inodes_per_block;
int status;
unsigned int blkno;
unsigned int blkoff;
#ifdef DEBUG
printf ("ext2fs read inode %d, inode_size %d\n", ino, inode_size);
#endif
/* It is easier to calculate if the first inode is 0. */
ino--;
status = ext2fs_blockgroup (data, ino / __le32_to_cpu
(sblock->inodes_per_group), &blkgrp);
if (status == 0) {
return (0);
}
inodes_per_block = EXT2_BLOCK_SIZE(data) / inode_size;
blkno = __le32_to_cpu (blkgrp.inode_table_id) +
(ino % __le32_to_cpu (sblock->inodes_per_group))
/ inodes_per_block;
blkoff = (ino % inodes_per_block) * inode_size;
#ifdef DEBUG
printf ("ext2fs read inode blkno %d blkoff %d\n", blkno, blkoff);
#endif
/* Read the inode. */
status = ext2fs_devread (blkno << LOG2_EXT2_BLOCK_SIZE (data), blkoff,
sizeof (struct ext2_inode), (char *) inode);
if (status == 0) {
return (0);
}
return (1);
}
void ext2fs_free_node (ext2fs_node_t node, ext2fs_node_t currroot) {
if ((node != &ext2fs_root->diropen) && (node != currroot)) {
free (node);
}
}
static int ext2fs_read_block (ext2fs_node_t node, int fileblock) {
struct ext2_data *data = node->data;
struct ext2_inode *inode = &node->inode;
int blknr;
int blksz = EXT2_BLOCK_SIZE (data);
int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
int status;
/* Direct blocks. */
if (fileblock < INDIRECT_BLOCKS) {
blknr = __le32_to_cpu (inode->b.blocks.dir_blocks[fileblock]);
}
/* Indirect. */
else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4))) {
if (indir1_block == NULL) {
indir1_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN,
blksz);
if (indir1_block == NULL) {
printf ("** ext2fs read block (indir 1) malloc failed. **\n");
return (-1);
}
indir1_size = blksz;
indir1_blkno = -1;
}
if (blksz != indir1_size) {
free (indir1_block);
indir1_block = NULL;
indir1_size = 0;
indir1_blkno = -1;
indir1_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN,
blksz);
if (indir1_block == NULL) {
printf ("** ext2fs read block (indir 1) malloc failed. **\n");
return (-1);
}
indir1_size = blksz;
}
if ((__le32_to_cpu (inode->b.blocks.indir_block) <<
log2_blksz) != indir1_blkno) {
status = ext2fs_devread (__le32_to_cpu(inode->b.blocks.indir_block) << log2_blksz,
0, blksz,
(char *) indir1_block);
if (status == 0) {
printf ("** ext2fs read block (indir 1) failed. **\n");
return (0);
}
indir1_blkno =
__le32_to_cpu (inode->b.blocks.
indir_block) << log2_blksz;
}
blknr = __le32_to_cpu (indir1_block
[fileblock - INDIRECT_BLOCKS]);
}
/* Double indirect. */
else if (fileblock <
(INDIRECT_BLOCKS + (blksz / 4 * (blksz / 4 + 1)))) {
unsigned int perblock = blksz / 4;
unsigned int rblock = fileblock - (INDIRECT_BLOCKS
+ blksz / 4);
if (indir1_block == NULL) {
indir1_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN,
blksz);
if (indir1_block == NULL) {
printf ("** ext2fs read block (indir 2 1) malloc failed. **\n");
return (-1);
}
indir1_size = blksz;
indir1_blkno = -1;
}
if (blksz != indir1_size) {
free (indir1_block);
indir1_block = NULL;
indir1_size = 0;
indir1_blkno = -1;
indir1_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN,
blksz);
if (indir1_block == NULL) {
printf ("** ext2fs read block (indir 2 1) malloc failed. **\n");
return (-1);
}
indir1_size = blksz;
}
if ((__le32_to_cpu (inode->b.blocks.double_indir_block) <<
log2_blksz) != indir1_blkno) {
status = ext2fs_devread (__le32_to_cpu(inode->b.blocks.double_indir_block) << log2_blksz,
0, blksz,
(char *) indir1_block);
if (status == 0) {
printf ("** ext2fs read block (indir 2 1) failed. **\n");
return (-1);
}
indir1_blkno =
__le32_to_cpu (inode->b.blocks.double_indir_block) << log2_blksz;
}
if (indir2_block == NULL) {
indir2_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN,
blksz);
if (indir2_block == NULL) {
printf ("** ext2fs read block (indir 2 2) malloc failed. **\n");
return (-1);
}
indir2_size = blksz;
indir2_blkno = -1;
}
if (blksz != indir2_size) {
free (indir2_block);
indir2_block = NULL;
indir2_size = 0;
indir2_blkno = -1;
indir2_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN,
blksz);
if (indir2_block == NULL) {
printf ("** ext2fs read block (indir 2 2) malloc failed. **\n");
return (-1);
}
indir2_size = blksz;
}
if ((__le32_to_cpu (indir1_block[rblock / perblock]) <<
log2_blksz) != indir2_blkno) {
status = ext2fs_devread (__le32_to_cpu(indir1_block[rblock / perblock]) << log2_blksz,
0, blksz,
(char *) indir2_block);
if (status == 0) {
printf ("** ext2fs read block (indir 2 2) failed. **\n");
return (-1);
}
indir2_blkno =
__le32_to_cpu (indir1_block[rblock / perblock]) << log2_blksz;
}
blknr = __le32_to_cpu (indir2_block[rblock % perblock]);
}
/* Tripple indirect. */
else {
printf ("** ext2fs doesn't support tripple indirect blocks. **\n");
return (-1);
}
#ifdef DEBUG
printf ("ext2fs_read_block %08x\n", blknr);
#endif
return (blknr);
}
int ext2fs_read_file
(ext2fs_node_t node, int pos, unsigned int len, char *buf) {
int i;
int blockcnt;
int log2blocksize = LOG2_EXT2_BLOCK_SIZE (node->data);
int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS);
unsigned int filesize = __le32_to_cpu(node->inode.size);
/* Adjust len so it we can't read past the end of the file. */
if (len > filesize) {
len = filesize;
}
blockcnt = ((len + pos) + blocksize - 1) / blocksize;
for (i = pos / blocksize; i < blockcnt; i++) {
int blknr;
int blockoff = pos % blocksize;
int blockend = blocksize;
int skipfirst = 0;
blknr = ext2fs_read_block (node, i);
if (blknr < 0) {
return (-1);
}
/* Last block. */
if (i == blockcnt - 1) {
blockend = (len + pos) % blocksize;
/* The last portion is exactly blocksize. */
if (!blockend) {
blockend = blocksize;
}
}
/* First block. */
if (i == pos / blocksize) {
skipfirst = blockoff;
blockend -= skipfirst;
}
/* grab middle blocks in one go */
if (i != pos / blocksize && i < blockcnt - 1 && blockcnt > 3) {
int oldblk = blknr;
int blocknxt;
while (i < blockcnt - 1) {
blocknxt = ext2fs_read_block(node, i + 1);
if (blocknxt == (oldblk + 1)) {
oldblk = blocknxt;
i++;
} else {
blocknxt = ext2fs_read_block(node, i);
break;
}
}
if (oldblk == blknr)
blockend = blocksize;
else
blockend = (1 + blocknxt - blknr) * blocksize;
}
blknr = blknr << log2blocksize;
/* If the block number is 0 this block is not stored on disk but
is zero filled instead. */
if (blknr) {
int status;
status = ext2fs_devread (blknr, skipfirst, blockend, buf);
if (status == 0) {
return (-1);
}
} else {
memset (buf, 0, blocksize - skipfirst);
}
buf += blockend - skipfirst;
}
return (len);
}
static int ext2fs_iterate_dir (ext2fs_node_t dir, char *name, ext2fs_node_t * fnode, int *ftype)
{
unsigned int fpos = 0;
int status;
struct ext2fs_node *diro = (struct ext2fs_node *) dir;
#ifdef DEBUG
if (name != NULL)
printf ("Iterate dir %s\n", name);
#endif /* of DEBUG */
if (!diro->inode_read) {
status = ext2fs_read_inode (diro->data, diro->ino,
&diro->inode);
if (status == 0) {
return (0);
}
}
/* Search the file. */
while (fpos < __le32_to_cpu (diro->inode.size)) {
struct ext2_dirent dirent;
status = ext2fs_read_file (diro, fpos,
sizeof (struct ext2_dirent),
(char *) &dirent);
if (status < 1) {
return (0);
}
if (dirent.namelen != 0) {
char filename[dirent.namelen + 1];
ext2fs_node_t fdiro;
int type = FILETYPE_UNKNOWN;
status = ext2fs_read_file (diro,
fpos + sizeof (struct ext2_dirent),
dirent.namelen, filename);
if (status < 1) {
return (0);
}
fdiro = malloc (sizeof (struct ext2fs_node));
if (!fdiro) {
return (0);
}
fdiro->data = diro->data;
fdiro->ino = __le32_to_cpu (dirent.inode);
filename[dirent.namelen] = '\0';
if (dirent.filetype != FILETYPE_UNKNOWN) {
fdiro->inode_read = 0;
if (dirent.filetype == FILETYPE_DIRECTORY) {
type = FILETYPE_DIRECTORY;
} else if (dirent.filetype ==
FILETYPE_SYMLINK) {
type = FILETYPE_SYMLINK;
} else if (dirent.filetype == FILETYPE_REG) {
type = FILETYPE_REG;
}
} else {
/* The filetype can not be read from the dirent, get it from inode */
status = ext2fs_read_inode (diro->data,
__le32_to_cpu(dirent.inode),
&fdiro->inode);
if (status == 0) {
free (fdiro);
return (0);
}
fdiro->inode_read = 1;
if ((__le16_to_cpu (fdiro->inode.mode) &
FILETYPE_INO_MASK) ==
FILETYPE_INO_DIRECTORY) {
type = FILETYPE_DIRECTORY;
} else if ((__le16_to_cpu (fdiro->inode.mode)
& FILETYPE_INO_MASK) ==
FILETYPE_INO_SYMLINK) {
type = FILETYPE_SYMLINK;
} else if ((__le16_to_cpu (fdiro->inode.mode)
& FILETYPE_INO_MASK) ==
FILETYPE_INO_REG) {
type = FILETYPE_REG;
}
}
#ifdef DEBUG
printf ("iterate >%s<\n", filename);
#endif /* of DEBUG */
if ((name != NULL) && (fnode != NULL)
&& (ftype != NULL)) {
if (strcmp (filename, name) == 0) {
*ftype = type;
*fnode = fdiro;
return (1);
}
} else {
if (fdiro->inode_read == 0) {
status = ext2fs_read_inode (diro->data,
__le32_to_cpu (dirent.inode),
&fdiro->inode);
if (status == 0) {
free (fdiro);
return (0);
}
fdiro->inode_read = 1;
}
switch (type) {
case FILETYPE_DIRECTORY:
printf ("<DIR> ");
break;
case FILETYPE_SYMLINK:
printf ("<SYM> ");
break;
case FILETYPE_REG:
printf (" ");
break;
default:
printf ("< ? > ");
break;
}
printf ("%10d %s\n",
__le32_to_cpu (fdiro->inode.size),
filename);
}
free (fdiro);
}
fpos += __le16_to_cpu (dirent.direntlen);
}
return (0);
}
static char *ext2fs_read_symlink (ext2fs_node_t node) {
char *symlink;
struct ext2fs_node *diro = node;
int status;
if (!diro->inode_read) {
status = ext2fs_read_inode (diro->data, diro->ino,
&diro->inode);
if (status == 0) {
return (0);
}
}
symlink = malloc (__le32_to_cpu (diro->inode.size) + 1);
if (!symlink) {
return (0);
}
/* If the filesize of the symlink is bigger than
60 the symlink is stored in a separate block,
otherwise it is stored in the inode. */
if (__le32_to_cpu (diro->inode.size) <= 60) {
strncpy (symlink, diro->inode.b.symlink,
__le32_to_cpu (diro->inode.size));
} else {
status = ext2fs_read_file (diro, 0,
__le32_to_cpu (diro->inode.size),
symlink);
if (status == 0) {
free (symlink);
return (0);
}
}
symlink[__le32_to_cpu (diro->inode.size)] = '\0';
return (symlink);
}
int ext2fs_find_file1
(const char *currpath,
ext2fs_node_t currroot, ext2fs_node_t * currfound, int *foundtype) {
char fpath[strlen (currpath) + 1];
char *name = fpath;
char *next;
int status;
int type = FILETYPE_DIRECTORY;
ext2fs_node_t currnode = currroot;
ext2fs_node_t oldnode = currroot;
strncpy (fpath, currpath, strlen (currpath) + 1);
/* Remove all leading slashes. */
while (*name == '/') {
name++;
}
if (!*name) {
*currfound = currnode;
return (1);
}
for (;;) {
int found;
/* Extract the actual part from the pathname. */
next = strchr (name, '/');
if (next) {
/* Remove all leading slashes. */
while (*next == '/') {
*(next++) = '\0';
}
}
/* At this point it is expected that the current node is a directory, check if this is true. */
if (type != FILETYPE_DIRECTORY) {
ext2fs_free_node (currnode, currroot);
return (0);
}
oldnode = currnode;
/* Iterate over the directory. */
found = ext2fs_iterate_dir (currnode, name, &currnode, &type);
if (found == 0) {
return (0);
}
if (found == -1) {
break;
}
/* Read in the symlink and follow it. */
if (type == FILETYPE_SYMLINK) {
char *symlink;
/* Test if the symlink does not loop. */
if (++symlinknest == 8) {
ext2fs_free_node (currnode, currroot);
ext2fs_free_node (oldnode, currroot);
return (0);
}
symlink = ext2fs_read_symlink (currnode);
ext2fs_free_node (currnode, currroot);
if (!symlink) {
ext2fs_free_node (oldnode, currroot);
return (0);
}
#ifdef DEBUG
printf ("Got symlink >%s<\n", symlink);
#endif /* of DEBUG */
/* The symlink is an absolute path, go back to the root inode. */
if (symlink[0] == '/') {
ext2fs_free_node (oldnode, currroot);
oldnode = &ext2fs_root->diropen;
}
/* Lookup the node the symlink points to. */
status = ext2fs_find_file1 (symlink, oldnode,
&currnode, &type);
free (symlink);
if (status == 0) {
ext2fs_free_node (oldnode, currroot);
return (0);
}
}
ext2fs_free_node (oldnode, currroot);
/* Found the node! */
if (!next || *next == '\0') {
*currfound = currnode;
*foundtype = type;
return (1);
}
name = next;
}
return (-1);
}
int ext2fs_find_file
(const char *path,
ext2fs_node_t rootnode, ext2fs_node_t * foundnode, int expecttype) {
int status;
int foundtype = FILETYPE_DIRECTORY;
symlinknest = 0;
if (!path) {
return (0);
}
status = ext2fs_find_file1 (path, rootnode, foundnode, &foundtype);
if (status == 0) {
return (0);
}
/* Check if the node that was found was of the expected type. */
if ((expecttype == FILETYPE_REG) && (foundtype != expecttype)) {
return (0);
} else if ((expecttype == FILETYPE_DIRECTORY)
&& (foundtype != expecttype)) {
return (0);
}
return (1);
}
int ext2fs_ls (const char *dirname) {
ext2fs_node_t dirnode;
int status;
if (ext2fs_root == NULL) {
return (0);
}
status = ext2fs_find_file (dirname, &ext2fs_root->diropen, &dirnode,
FILETYPE_DIRECTORY);
if (status != 1) {
printf ("** Can not find directory. **\n");
return (1);
}
ext2fs_iterate_dir (dirnode, NULL, NULL, NULL);
ext2fs_free_node (dirnode, &ext2fs_root->diropen);
return (0);
}
int ext2fs_open (const char *filename) {
ext2fs_node_t fdiro = NULL;
int status;
int len;
if (ext2fs_root == NULL) {
return (-1);
}
ext2fs_file = NULL;
status = ext2fs_find_file (filename, &ext2fs_root->diropen, &fdiro,
FILETYPE_REG);
if (status == 0) {
goto fail;
}
if (!fdiro->inode_read) {
status = ext2fs_read_inode (fdiro->data, fdiro->ino,
&fdiro->inode);
if (status == 0) {
goto fail;
}
}
len = __le32_to_cpu (fdiro->inode.size);
ext2fs_file = fdiro;
return (len);
fail:
ext2fs_free_node (fdiro, &ext2fs_root->diropen);
return (-1);
}
int ext2fs_close (void
) {
if ((ext2fs_file != NULL) && (ext2fs_root != NULL)) {
ext2fs_free_node (ext2fs_file, &ext2fs_root->diropen);
ext2fs_file = NULL;
}
if (ext2fs_root != NULL) {
free (ext2fs_root);
ext2fs_root = NULL;
}
if (indir1_block != NULL) {
free (indir1_block);
indir1_block = NULL;
indir1_size = 0;
indir1_blkno = -1;
}
if (indir2_block != NULL) {
free (indir2_block);
indir2_block = NULL;
indir2_size = 0;
indir2_blkno = -1;
}
return (0);
}
int ext2fs_read (char *buf, unsigned len) {
int status;
if (ext2fs_root == NULL) {
return (0);
}
if (ext2fs_file == NULL) {
return (0);
}
status = ext2fs_read_file (ext2fs_file, 0, len, buf);
return (status);
}
int ext2fs_mount (unsigned part_length) {
struct ext2_data *data;
int status;
data = malloc (sizeof (struct ext2_data));
if (!data) {
return (0);
}
/* Read the superblock. */
status = ext2fs_devread (1 * 2, 0, sizeof (struct ext2_sblock),
(char *) &data->sblock);
if (status == 0) {
goto fail;
}
/* Make sure this is an ext2 filesystem. */
if (__le16_to_cpu (data->sblock.magic) != EXT2_MAGIC) {
goto fail;
}
if (__le32_to_cpu(data->sblock.revision_level == 0)) {
inode_size = 128;
} else {
inode_size = __le16_to_cpu(data->sblock.inode_size);
}
#ifdef DEBUG
printf("EXT2 rev %d, inode_size %d\n",
__le32_to_cpu(data->sblock.revision_level), inode_size);
#endif
data->diropen.data = data;
data->diropen.ino = 2;
data->diropen.inode_read = 1;
data->inode = &data->diropen.inode;
status = ext2fs_read_inode (data, 2, data->inode);
if (status == 0) {
goto fail;
}
ext2fs_root = data;
return (1);
fail:
printf ("Failed to mount ext2 filesystem...\n");
free (data);
ext2fs_root = NULL;
return (0);
}

View file

@ -0,0 +1,50 @@
#
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libfat.o
AOBJS =
COBJS-$(CONFIG_CMD_FAT) := fat.o
COBJS-$(CONFIG_FAT_WRITE):= fat_write.o
ifndef CONFIG_SPL_BUILD
COBJS-$(CONFIG_CMD_FAT) += file.o
endif
SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS-y))
all: $(LIB) $(AOBJS)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,200 @@
/*
* file.c
*
* Mini "VFS" by Marcus Sundberg
*
* 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
* 2003-03-10 - kharris@nexus-tech.net - ported to uboot
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <config.h>
#include <malloc.h>
#include <fat.h>
#include <linux/stat.h>
#include <linux/time.h>
/* Supported filesystems */
static const struct filesystem filesystems[] = {
{ file_fat_detectfs, file_fat_ls, file_fat_read, "FAT" },
};
#define NUM_FILESYS (sizeof(filesystems)/sizeof(struct filesystem))
/* The filesystem which was last detected */
static int current_filesystem = FSTYPE_NONE;
/* The current working directory */
#define CWD_LEN 511
char file_cwd[CWD_LEN+1] = "/";
const char *
file_getfsname(int idx)
{
if (idx < 0 || idx >= NUM_FILESYS)
return NULL;
return filesystems[idx].name;
}
static void
pathcpy(char *dest, const char *src)
{
char *origdest = dest;
do {
if (dest-file_cwd >= CWD_LEN) {
*dest = '\0';
return;
}
*(dest) = *(src);
if (*src == '\0') {
if (dest-- != origdest && ISDIRDELIM(*dest)) {
*dest = '\0';
}
return;
}
++dest;
if (ISDIRDELIM(*src))
while (ISDIRDELIM(*src)) src++;
else
src++;
} while (1);
}
int
file_cd(const char *path)
{
if (ISDIRDELIM(*path)) {
while (ISDIRDELIM(*path)) path++;
strncpy(file_cwd+1, path, CWD_LEN-1);
} else {
const char *origpath = path;
char *tmpstr = file_cwd;
int back = 0;
while (*tmpstr != '\0') tmpstr++;
do {
tmpstr--;
} while (ISDIRDELIM(*tmpstr));
while (*path == '.') {
path++;
while (*path == '.') {
path++;
back++;
}
if (*path != '\0' && !ISDIRDELIM(*path)) {
path = origpath;
back = 0;
break;
}
while (ISDIRDELIM(*path)) path++;
origpath = path;
}
while (back--) {
/* Strip off path component */
while (!ISDIRDELIM(*tmpstr)) {
tmpstr--;
}
if (tmpstr == file_cwd) {
/* Incremented again right after the loop. */
tmpstr--;
break;
}
/* Skip delimiters */
while (ISDIRDELIM(*tmpstr)) tmpstr--;
}
tmpstr++;
if (*path == '\0') {
if (tmpstr == file_cwd) {
*tmpstr = '/';
tmpstr++;
}
*tmpstr = '\0';
return 0;
}
*tmpstr = '/';
pathcpy(tmpstr+1, path);
}
return 0;
}
int
file_detectfs(void)
{
int i;
current_filesystem = FSTYPE_NONE;
for (i = 0; i < NUM_FILESYS; i++) {
if (filesystems[i].detect() == 0) {
strcpy(file_cwd, "/");
current_filesystem = i;
break;
}
}
return current_filesystem;
}
int
file_ls(const char *dir)
{
char fullpath[1024];
const char *arg;
if (current_filesystem == FSTYPE_NONE) {
printf("Can't list files without a filesystem!\n");
return -1;
}
if (ISDIRDELIM(*dir)) {
arg = dir;
} else {
sprintf(fullpath, "%s/%s", file_cwd, dir);
arg = fullpath;
}
return filesystems[current_filesystem].ls(arg);
}
long
file_read(const char *filename, void *buffer, unsigned long maxsize)
{
char fullpath[1024];
const char *arg;
if (current_filesystem == FSTYPE_NONE) {
printf("Can't load file without a filesystem!\n");
return -1;
}
if (ISDIRDELIM(*filename)) {
arg = filename;
} else {
sprintf(fullpath, "%s/%s", file_cwd, filename);
arg = fullpath;
}
return filesystems[current_filesystem].read(arg, buffer, maxsize);
}

View file

@ -0,0 +1,54 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2002
# Stäubli Faverges - <www.staubli.com>
# Pierre AUBERT p.aubert@staubli.com
#
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libfdos.o
AOBJS =
COBJS-$(CONFIG_CMD_FDOS) := fat.o vfat.o dev.o fdos.o fs.o subdir.o
SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS-y))
#CPPFLAGS +=
all: $(LIB) $(AOBJS)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,190 @@
/*
* (C) Copyright 2002
* Stäubli Faverges - <www.staubli.com>
* Pierre AUBERT p.aubert@staubli.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <config.h>
#include "dos.h"
#include "fdos.h"
#define NB_HEADS 2
#define NB_TRACKS 80
#define NB_SECTORS 18
static int lastwhere;
/*-----------------------------------------------------------------------------
* dev_open --
*-----------------------------------------------------------------------------
*/
int dev_open (void)
{
lastwhere = 0;
return (0);
}
/*-----------------------------------------------------------------------------
* dev_read -- len and where are sectors number
*-----------------------------------------------------------------------------
*/
int dev_read (void *buffer, int where, int len)
{
PRINTF ("dev_read (len = %d, where = %d)\n", len, where);
/* Si on ne desire pas lire a la position courante, il faut un seek */
if (where != lastwhere) {
if (!fdc_fdos_seek (where)) {
PRINTF ("seek error in dev_read");
lastwhere = -1;
return (-1);
}
}
if (!fdc_fdos_read (buffer, len)) {
PRINTF ("read error\n");
lastwhere = -1;
return (-1);
}
lastwhere = where + len;
return (0);
}
/*-----------------------------------------------------------------------------
* check_dev -- verify the diskette format
*-----------------------------------------------------------------------------
*/
int check_dev (BootSector_t *boot, Fs_t *fs)
{
unsigned int heads, sectors, tracks;
int BootP, Infp0, InfpX, InfTm;
int sect_per_track;
/* Display Boot header */
PRINTF ("Jump to boot code 0x%02x 0x%02x 0x%02x\n",
boot -> jump [0], boot -> jump [1], boot -> jump[2]);
PRINTF ("OEM name & version '%*.*s'\n",
BANNER_LG, BANNER_LG, boot -> banner );
PRINTF ("Bytes per sector hopefully 512 %d\n",
__le16_to_cpu (boot -> secsiz));
PRINTF ("Cluster size in sectors %d\n",
boot -> clsiz);
PRINTF ("Number of reserved (boot) sectors %d\n",
__le16_to_cpu (boot -> nrsvsect));
PRINTF ("Number of FAT tables hopefully 2 %d\n",
boot -> nfat);
PRINTF ("Number of directory slots %d\n",
__le16_to_cpu (boot -> dirents));
PRINTF ("Total sectors on disk %d\n",
__le16_to_cpu (boot -> psect));
PRINTF ("Media descriptor=first byte of FAT %d\n",
boot -> descr);
PRINTF ("Sectors in FAT %d\n",
__le16_to_cpu (boot -> fatlen));
PRINTF ("Sectors/track %d\n",
__le16_to_cpu (boot -> nsect));
PRINTF ("Heads %d\n",
__le16_to_cpu (boot -> nheads));
PRINTF ("number of hidden sectors %d\n",
__le32_to_cpu (boot -> nhs));
PRINTF ("big total sectors %d\n",
__le32_to_cpu (boot -> bigsect));
PRINTF ("physical drive ? %d\n",
boot -> physdrive);
PRINTF ("reserved %d\n",
boot -> reserved);
PRINTF ("dos > 4.0 diskette %d\n",
boot -> dos4);
PRINTF ("serial number %d\n",
__le32_to_cpu (boot -> serial));
PRINTF ("disk label %*.*s\n",
LABEL_LG, LABEL_LG, boot -> label);
PRINTF ("FAT type %8.8s\n",
boot -> fat_type);
PRINTF ("reserved by 2M %d\n",
boot -> res_2m);
PRINTF ("2M checksum (not used) %d\n",
boot -> CheckSum);
PRINTF ("2MF format version %d\n",
boot -> fmt_2mf);
PRINTF ("1 if write track after format %d\n",
boot -> wt);
PRINTF ("data transfer rate on track 0 %d\n",
boot -> rate_0);
PRINTF ("data transfer rate on track<>0 %d\n",
boot -> rate_any);
PRINTF ("offset to boot program %d\n",
__le16_to_cpu (boot -> BootP));
PRINTF ("T1: information for track 0 %d\n",
__le16_to_cpu (boot -> Infp0));
PRINTF ("T2: information for track<>0 %d\n",
__le16_to_cpu (boot -> InfpX));
PRINTF ("T3: track sectors size table %d\n",
__le16_to_cpu (boot -> InfTm));
PRINTF ("Format date 0x%04x\n",
__le16_to_cpu (boot -> DateF));
PRINTF ("Format time 0x%04x\n",
__le16_to_cpu (boot -> TimeF));
/* information is extracted from boot sector */
heads = __le16_to_cpu (boot -> nheads);
sectors = __le16_to_cpu (boot -> nsect);
fs -> tot_sectors = __le32_to_cpu (boot -> bigsect);
if (__le16_to_cpu (boot -> psect) != 0) {
fs -> tot_sectors = __le16_to_cpu (boot -> psect);
}
sect_per_track = heads * sectors;
tracks = (fs -> tot_sectors + sect_per_track - 1) / sect_per_track;
BootP = __le16_to_cpu (boot -> BootP);
Infp0 = __le16_to_cpu (boot -> Infp0);
InfpX = __le16_to_cpu (boot -> InfpX);
InfTm = __le16_to_cpu (boot -> InfTm);
if (boot -> dos4 == EXTENDED_BOOT &&
strncmp( boot->banner,"2M", 2 ) == 0 &&
BootP < SZ_STD_SECTOR &&
Infp0 < SZ_STD_SECTOR &&
InfpX < SZ_STD_SECTOR &&
InfTm < SZ_STD_SECTOR &&
BootP >= InfTm + 2 &&
InfTm >= InfpX &&
InfpX >= Infp0 &&
Infp0 >= 76 ) {
return (-1);
}
if (heads != NB_HEADS ||
tracks != NB_TRACKS ||
sectors != NB_SECTORS ||
__le16_to_cpu (boot -> secsiz) != SZ_STD_SECTOR ||
fs -> tot_sectors == 0 ||
(fs -> tot_sectors % sectors) != 0) {
return (-1);
}
return (0);
}

View file

@ -0,0 +1,175 @@
/*
* (C) Copyright 2002
* Stäubli Faverges - <www.staubli.com>
* Pierre AUBERT p.aubert@staubli.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef _DOS_H_
#define _DOS_H_
/* Definitions for Dos diskettes */
/* General definitions */
#define SZ_STD_SECTOR 512 /* Standard sector size */
#define MDIR_SIZE 32 /* Direntry size */
#define FAT_BITS 12 /* Diskette use 12 bits fat */
#define MAX_PATH 128 /* Max size of the MSDOS PATH */
#define MAX_DIR_SECS 64 /* Taille max d'un repertoire (en */
/* secteurs) */
/* Misc. definitions */
#define DELMARK '\xe5'
#define EXTENDED_BOOT (0x29)
#define MEDIA_STD (0xf0)
#define JUMP_0_1 (0xe9)
#define JUMP_0_2 (0xeb)
/* Boot size is 256 bytes, but we need to read almost a sector, then
assume bootsize is 512 */
#define BOOTSIZE 512
/* Fat definitions for 12 bits fat */
#define FAT12_MAX_NB 4086
#define FAT12_LAST 0x0ff6
#define FAT12_END 0x0fff
/* file attributes */
#define ATTR_READONLY 0x01
#define ATTR_HIDDEN 0x02
#define ATTR_SYSTEM 0x04
#define ATTR_VOLUME 0x08
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20
#define ATTR_VSE 0x0f
/* Name format */
#define EXTCASE 0x10
#define BASECASE 0x8
/* Definition of the boot sector */
#define BANNER_LG 8
#define LABEL_LG 11
typedef struct bootsector
{
unsigned char jump [3]; /* 0 Jump to boot code */
char banner [BANNER_LG]; /* 3 OEM name & version */
unsigned short secsiz; /* 11 Bytes per sector hopefully 512 */
unsigned char clsiz; /* 13 Cluster size in sectors */
unsigned short nrsvsect; /* 14 Number of reserved (boot) sectors */
unsigned char nfat; /* 16 Number of FAT tables hopefully 2 */
unsigned short dirents; /* 17 Number of directory slots */
unsigned short psect; /* 19 Total sectors on disk */
unsigned char descr; /* 21 Media descriptor=first byte of FAT */
unsigned short fatlen; /* 22 Sectors in FAT */
unsigned short nsect; /* 24 Sectors/track */
unsigned short nheads; /* 26 Heads */
unsigned int nhs; /* 28 number of hidden sectors */
unsigned int bigsect; /* 32 big total sectors */
unsigned char physdrive; /* 36 physical drive ? */
unsigned char reserved; /* 37 reserved */
unsigned char dos4; /* 38 dos > 4.0 diskette */
unsigned int serial; /* 39 serial number */
char label [LABEL_LG]; /* 43 disk label */
char fat_type [8]; /* 54 FAT type */
unsigned char res_2m; /* 62 reserved by 2M */
unsigned char CheckSum; /* 63 2M checksum (not used) */
unsigned char fmt_2mf; /* 64 2MF format version */
unsigned char wt; /* 65 1 if write track after format */
unsigned char rate_0; /* 66 data transfer rate on track 0 */
unsigned char rate_any; /* 67 data transfer rate on track<>0 */
unsigned short BootP; /* 68 offset to boot program */
unsigned short Infp0; /* 70 T1: information for track 0 */
unsigned short InfpX; /* 72 T2: information for track<>0 */
unsigned short InfTm; /* 74 T3: track sectors size table */
unsigned short DateF; /* 76 Format date */
unsigned short TimeF; /* 78 Format time */
unsigned char junk [BOOTSIZE - 80]; /* 80 remaining data */
} __attribute__ ((packed)) BootSector_t;
/* Structure d'une entree de repertoire */
typedef struct directory {
char name [8]; /* file name */
char ext [3]; /* file extension */
unsigned char attr; /* attribute byte */
unsigned char Case; /* case of short filename */
unsigned char reserved [9]; /* ?? */
unsigned char time [2]; /* time stamp */
unsigned char date [2]; /* date stamp */
unsigned short start; /* starting cluster number */
unsigned int size; /* size of the file */
} __attribute__ ((packed)) Directory_t;
#define MAX_VFAT_SUBENTRIES 20
#define VSE_NAMELEN 13
#define VSE1SIZE 5
#define VSE2SIZE 6
#define VSE3SIZE 2
#define VBUFSIZE ((MAX_VFAT_SUBENTRIES * VSE_NAMELEN) + 1)
#define MAX_VNAMELEN (255)
#define VSE_PRESENT 0x01
#define VSE_LAST 0x40
#define VSE_MASK 0x1f
/* Flag used by vfat_lookup */
#define DO_OPEN 1
#define ACCEPT_PLAIN 0x20
#define ACCEPT_DIR 0x10
#define ACCEPT_LABEL 0x08
#define SINGLE 2
#define MATCH_ANY 0x40
struct vfat_subentry {
unsigned char id; /* VSE_LAST pour la fin, VSE_MASK */
/* pour un VSE */
char text1 [VSE1SIZE * 2]; /* Caracteres encodes sur 16 bits */
unsigned char attribute; /* 0x0f pour les VFAT */
unsigned char hash1; /* toujours 0 */
unsigned char sum; /* Checksum du nom court */
char text2 [VSE2SIZE * 2]; /* Caracteres encodes sur 16 bits */
unsigned char sector_l; /* 0 pour les VFAT */
unsigned char sector_u; /* 0 pour les VFAT */
char text3 [VSE3SIZE * 2]; /* Caracteres encodes sur 16 bits */
} __attribute__ ((packed)) ;
struct vfat_state {
char name [VBUFSIZE];
int status; /* is now a bit map of 32 bits */
int subentries;
unsigned char sum; /* no need to remember the sum for each */
/* entry, it is the same anyways */
} __attribute__ ((packed)) ;
/* Conversion macros */
#define DOS_YEAR(dir) (((dir)->date[1] >> 1) + 1980)
#define DOS_MONTH(dir) (((((dir)->date[1]&0x1) << 3) + ((dir)->date[0] >> 5)))
#define DOS_DAY(dir) ((dir)->date[0] & 0x1f)
#define DOS_HOUR(dir) ((dir)->time[1] >> 3)
#define DOS_MINUTE(dir) (((((dir)->time[1]&0x7) << 3) + ((dir)->time[0] >> 5)))
#define DOS_SEC(dir) (((dir)->time[0] & 0x1f) * 2)
#endif

View file

@ -0,0 +1,138 @@
/*
* (C) Copyright 2002
* Stäubli Faverges - <www.staubli.com>
* Pierre AUBERT p.aubert@staubli.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <config.h>
#include <malloc.h>
#include "dos.h"
#include "fdos.h"
/*-----------------------------------------------------------------------------
* fat_decode --
*-----------------------------------------------------------------------------
*/
unsigned int fat_decode (Fs_t *fs, unsigned int num)
{
unsigned int start = num * 3 / 2;
unsigned char *address = fs -> fat_buf + start;
if (num < 2 || start + 1 > (fs -> fat_len * SZ_STD_SECTOR))
return 1;
if (num & 1)
return ((address [1] & 0xff) << 4) | ((address [0] & 0xf0 ) >> 4);
else
return ((address [1] & 0xf) << 8) | (address [0] & 0xff );
}
/*-----------------------------------------------------------------------------
* check_fat --
*-----------------------------------------------------------------------------
*/
static int check_fat (Fs_t *fs)
{
int i, f;
/* Cluster verification */
for (i = 3 ; i < fs -> num_clus; i++){
f = fat_decode (fs, i);
if (f < FAT12_LAST && f > fs -> num_clus){
/* Wrong cluster number detected */
return (-1);
}
}
return (0);
}
/*-----------------------------------------------------------------------------
* read_one_fat --
*-----------------------------------------------------------------------------
*/
static int read_one_fat (BootSector_t *boot, Fs_t *fs, int nfat)
{
if (dev_read (fs -> fat_buf,
(fs -> fat_start + nfat * fs -> fat_len),
fs -> fat_len) < 0) {
return (-1);
}
if (fs -> fat_buf [0] || fs -> fat_buf [1] || fs -> fat_buf [2]) {
if ((fs -> fat_buf [0] != boot -> descr &&
(fs -> fat_buf [0] != 0xf9 || boot -> descr != MEDIA_STD)) ||
fs -> fat_buf [0] < MEDIA_STD){
/* Unknown Media */
return (-1);
}
if (fs -> fat_buf [1] != 0xff || fs -> fat_buf [2] != 0xff){
/* FAT doesn't start with good values */
return (-1);
}
}
if (fs -> num_clus >= FAT12_MAX_NB) {
/* Too much clusters */
return (-1);
}
return check_fat (fs);
}
/*-----------------------------------------------------------------------------
* read_fat --
*-----------------------------------------------------------------------------
*/
int read_fat (BootSector_t *boot, Fs_t *fs)
{
unsigned int buflen;
int i;
/* Allocate Fat Buffer */
buflen = fs -> fat_len * SZ_STD_SECTOR;
if (fs -> fat_buf) {
free (fs -> fat_buf);
}
if ((fs -> fat_buf = malloc (buflen)) == NULL) {
return (-1);
}
/* Try to read each Fat */
for (i = 0; i< fs -> nb_fat; i++){
if (read_one_fat (boot, fs, i) == 0) {
/* Fat is OK */
fs -> num_fat = i;
break;
}
}
if (i == fs -> nb_fat){
return (-1);
}
if (fs -> fat_len > (((fs -> num_clus + 2) *
(FAT_BITS / 4) -1 ) / 2 /
SZ_STD_SECTOR + 1)) {
return (-1);
}
return (0);
}

View file

@ -0,0 +1,172 @@
/*
* (C) Copyright 2002
* Stäubli Faverges - <www.staubli.com>
* Pierre AUBERT p.aubert@staubli.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <config.h>
#include <malloc.h>
#include "dos.h"
#include "fdos.h"
const char *month [] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
Fs_t fs;
File_t file;
/*-----------------------------------------------------------------------------
* dos_open --
*-----------------------------------------------------------------------------
*/
int dos_open(char *name)
{
int lg;
int entry;
char *fname;
/* We need to suppress the " char around the name */
if (name [0] == '"') {
name ++;
}
lg = strlen (name);
if (name [lg - 1] == '"') {
name [lg - 1] = '\0';
}
/* Open file system */
if (fs_init (&fs) < 0) {
return -1;
}
/* Init the file descriptor */
file.name = name;
file.fs = &fs;
/* find the subdirectory containing the file */
if (open_subdir (&file) < 0) {
return (-1);
}
fname = basename (name);
/* if we try to open root directory */
if (*fname == '\0') {
file.file = file.subdir;
return (0);
}
/* find the file in the subdir */
entry = 0;
if (vfat_lookup (&file.subdir,
file.fs,
&file.file.dir,
&entry,
0,
fname,
ACCEPT_DIR | ACCEPT_PLAIN | SINGLE | DO_OPEN,
0,
&file.file) != 0) {
/* File not found */
printf ("File not found\n");
return (-1);
}
return 0;
}
/*-----------------------------------------------------------------------------
* dos_read --
*-----------------------------------------------------------------------------
*/
int dos_read (ulong addr)
{
int read = 0, nb;
/* Try to boot a directory ? */
if (file.file.dir.attr & (ATTR_DIRECTORY | ATTR_VOLUME)) {
printf ("Unable to boot %s !!\n", file.name);
return (-1);
}
while (read < file.file.FileSize) {
PRINTF ("read_file (%ld)\n", (file.file.FileSize - read));
nb = read_file (&fs,
&file.file,
(char *)addr + read,
read,
(file.file.FileSize - read));
PRINTF ("read_file -> %d\n", nb);
if (nb < 0) {
printf ("read error\n");
return (-1);
}
read += nb;
}
return (read);
}
/*-----------------------------------------------------------------------------
* dos_dir --
*-----------------------------------------------------------------------------
*/
int dos_dir (void)
{
int entry;
Directory_t dir;
char *name;
if ((file.file.dir.attr & ATTR_DIRECTORY) == 0) {
printf ("%s: not a directory !!\n", file.name);
return (1);
}
entry = 0;
if ((name = malloc (MAX_VNAMELEN + 1)) == NULL) {
PRINTF ("Allcation error\n");
return (1);
}
while (vfat_lookup (&file.file,
file.fs,
&dir,
&entry,
0,
NULL,
ACCEPT_DIR | ACCEPT_PLAIN | MATCH_ANY,
name,
NULL) == 0) {
/* Display file info */
printf ("%3.3s %9d %s %02d %04d %02d:%02d:%02d %s\n",
(dir.attr & ATTR_DIRECTORY) ? "dir" : " ",
__le32_to_cpu (dir.size),
month [DOS_MONTH (&dir) - 1],
DOS_DAY (&dir),
DOS_YEAR (&dir),
DOS_HOUR (&dir),
DOS_MINUTE (&dir),
DOS_SEC (&dir),
name);
}
free (name);
return (0);
}

View file

@ -0,0 +1,116 @@
/*
* (C) Copyright 2002
* Stäubli Faverges - <www.staubli.com>
* Pierre AUBERT p.aubert@staubli.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef _FDOS_H_
#define _FDOS_H_
#undef FDOS_DEBUG
#ifdef FDOS_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
/* Data structure describing media */
typedef struct fs
{
unsigned long tot_sectors;
int cluster_size;
int num_clus;
int fat_start;
int fat_len;
int nb_fat;
int num_fat;
int dir_start;
int dir_len;
unsigned char *fat_buf;
} Fs_t;
/* Data structure describing one file system slot */
typedef struct slot {
int (*map) (struct fs *fs,
struct slot *file,
int where,
int *len);
unsigned long FileSize;
unsigned short int FirstAbsCluNr;
unsigned short int PreviousAbsCluNr;
unsigned short int PreviousRelCluNr;
Directory_t dir;
} Slot_t;
typedef struct file {
char *name;
int Case;
Fs_t *fs;
Slot_t subdir;
Slot_t file;
} File_t;
/* dev.c */
int dev_read (void *buffer, int where, int len);
int dev_open (void);
int check_dev (BootSector_t *boot, Fs_t *fs);
/* fat.c */
unsigned int fat_decode (Fs_t *fs, unsigned int num);
int read_fat (BootSector_t *boot, Fs_t *fs);
/* vfat.c */
int vfat_lookup (Slot_t *dir,
Fs_t *fs,
Directory_t *dirent,
int *entry,
int *vfat_start,
char *filename,
int flags,
char *outname,
Slot_t *file);
/* subdir.c */
char *basename (char *name);
int open_subdir (File_t *desc);
int open_file (Slot_t *file, Directory_t *dir);
int read_file (Fs_t *fs,
Slot_t *file,
char *buf,
int where,
int len);
void init_subdir (void);
/* fs.c */
int fs_init (Fs_t *fs);
#endif

View file

@ -0,0 +1,114 @@
/*
* (C) Copyright 2002
* Stäubli Faverges - <www.staubli.com>
* Pierre AUBERT p.aubert@staubli.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <config.h>
#include <malloc.h>
#include "dos.h"
#include "fdos.h"
/*-----------------------------------------------------------------------------
* fill_fs -- Read info on file system
*-----------------------------------------------------------------------------
*/
static int fill_fs (BootSector_t *boot, Fs_t *fs)
{
fs -> fat_start = __le16_to_cpu (boot -> nrsvsect);
fs -> fat_len = __le16_to_cpu (boot -> fatlen);
fs -> nb_fat = boot -> nfat;
fs -> dir_start = fs -> fat_start + fs -> nb_fat * fs -> fat_len;
fs -> dir_len = __le16_to_cpu (boot -> dirents) * MDIR_SIZE / SZ_STD_SECTOR;
fs -> cluster_size = boot -> clsiz;
fs -> num_clus = (fs -> tot_sectors - fs -> dir_start - fs -> dir_len) / fs -> cluster_size;
return (0);
}
/*-----------------------------------------------------------------------------
* fs_init --
*-----------------------------------------------------------------------------
*/
int fs_init (Fs_t *fs)
{
BootSector_t *boot;
/* Initialize physical device */
if (dev_open () < 0) {
PRINTF ("Unable to initialize the fdc\n");
return (-1);
}
init_subdir ();
/* Allocate space for read the boot sector */
if ((boot = (BootSector_t *)malloc (sizeof (BootSector_t))) == NULL) {
PRINTF ("Unable to allocate space for boot sector\n");
return (-1);
}
/* read boot sector */
if (dev_read (boot, 0, 1)){
PRINTF ("Error during boot sector read\n");
free (boot);
return (-1);
}
/* we verify it'a a DOS diskette */
if (boot -> jump [0] != JUMP_0_1 && boot -> jump [0] != JUMP_0_2) {
PRINTF ("Not a DOS diskette\n");
free (boot);
return (-1);
}
if (boot -> descr < MEDIA_STD) {
/* We handle only recent medias (type F0) */
PRINTF ("unrecognized diskette type\n");
free (boot);
return (-1);
}
if (check_dev (boot, fs) < 0) {
PRINTF ("Bad diskette\n");
free (boot);
return (-1);
}
if (fill_fs (boot, fs) < 0) {
free (boot);
return (-1);
}
/* Read FAT */
if (read_fat (boot, fs) < 0) {
free (boot);
return (-1);
}
free (boot);
return (0);
}

View file

@ -0,0 +1,345 @@
/*
* (C) Copyright 2002
* Stäubli Faverges - <www.staubli.com>
* Pierre AUBERT p.aubert@staubli.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <config.h>
#include <malloc.h>
#include "dos.h"
#include "fdos.h"
static int cache_sect;
static unsigned char cache [SZ_STD_SECTOR];
#define min(x,y) ((x)<(y)?(x):(y))
static int descend (Slot_t *parent,
Fs_t *fs,
char *path);
/*-----------------------------------------------------------------------------
* init_subdir --
*-----------------------------------------------------------------------------
*/
void init_subdir (void)
{
cache_sect = -1;
}
/*-----------------------------------------------------------------------------
* basename --
*-----------------------------------------------------------------------------
*/
char *basename (char *name)
{
register char *cptr;
if (!name || !*name) {
return ("");
}
for (cptr= name; *cptr++; );
while (--cptr >= name) {
if (*cptr == '/') {
return (cptr + 1);
}
}
return(name);
}
/*-----------------------------------------------------------------------------
* root_map --
*-----------------------------------------------------------------------------
*/
static int root_map (Fs_t *fs, Slot_t *file, int where, int *len)
{
*len = min (*len, fs -> dir_len * SZ_STD_SECTOR - where);
if (*len < 0 ) {
*len = 0;
return (-1);
}
return fs -> dir_start * SZ_STD_SECTOR + where;
}
/*-----------------------------------------------------------------------------
* normal_map --
*-----------------------------------------------------------------------------
*/
static int normal_map (Fs_t *fs, Slot_t *file, int where, int *len)
{
int offset;
int NrClu;
unsigned short RelCluNr;
unsigned short CurCluNr;
unsigned short NewCluNr;
unsigned short AbsCluNr;
int clus_size;
clus_size = fs -> cluster_size * SZ_STD_SECTOR;
offset = where % clus_size;
*len = min (*len, file -> FileSize - where);
if (*len < 0 ) {
*len = 0;
return (0);
}
if (file -> FirstAbsCluNr < 2){
*len = 0;
return (0);
}
RelCluNr = where / clus_size;
if (RelCluNr >= file -> PreviousRelCluNr){
CurCluNr = file -> PreviousRelCluNr;
AbsCluNr = file -> PreviousAbsCluNr;
} else {
CurCluNr = 0;
AbsCluNr = file -> FirstAbsCluNr;
}
NrClu = (offset + *len - 1) / clus_size;
while (CurCluNr <= RelCluNr + NrClu) {
if (CurCluNr == RelCluNr){
/* we have reached the beginning of our zone. Save
* coordinates */
file -> PreviousRelCluNr = RelCluNr;
file -> PreviousAbsCluNr = AbsCluNr;
}
NewCluNr = fat_decode (fs, AbsCluNr);
if (NewCluNr == 1 || NewCluNr == 0) {
PRINTF("Fat problem while decoding %d %x\n",
AbsCluNr, NewCluNr);
return (-1);
}
if (CurCluNr == RelCluNr + NrClu) {
break;
}
if (CurCluNr < RelCluNr && NewCluNr == FAT12_END) {
*len = 0;
return 0;
}
if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1)
break;
CurCluNr++;
AbsCluNr = NewCluNr;
}
*len = min (*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
return (((file -> PreviousAbsCluNr - 2) * fs -> cluster_size +
fs -> dir_start + fs -> dir_len) *
SZ_STD_SECTOR + offset);
}
/*-----------------------------------------------------------------------------
* open_subdir -- open the subdir containing the file
*-----------------------------------------------------------------------------
*/
int open_subdir (File_t *desc)
{
char *pathname;
char *tmp, *s, *path;
char terminator;
if ((pathname = (char *)malloc (MAX_PATH)) == NULL) {
return (-1);
}
strcpy (pathname, desc -> name);
/* Suppress file name */
tmp = basename (pathname);
*tmp = '\0';
/* root directory init */
desc -> subdir.FirstAbsCluNr = 0;
desc -> subdir.FileSize = -1;
desc -> subdir.map = root_map;
desc -> subdir.dir.attr = ATTR_DIRECTORY;
tmp = pathname;
for (s = tmp; ; ++s) {
if (*s == '/' || *s == '\0') {
path = tmp;
terminator = *s;
*s = '\0';
if (s != tmp && strcmp (path,".")) {
if (descend (&desc -> subdir, desc -> fs, path) < 0) {
free (pathname);
return (-1);
}
}
if (terminator == 0) {
break;
}
tmp = s + 1;
}
}
free (pathname);
return (0);
}
/*-----------------------------------------------------------------------------
* descend --
*-----------------------------------------------------------------------------
*/
static int descend (Slot_t *parent,
Fs_t *fs,
char *path)
{
int entry;
Slot_t SubDir;
if(path[0] == '\0' || strcmp (path, ".") == 0) {
return (0);
}
entry = 0;
if (vfat_lookup (parent,
fs,
&(SubDir.dir),
&entry,
0,
path,
ACCEPT_DIR | SINGLE | DO_OPEN,
0,
&SubDir) == 0) {
*parent = SubDir;
return (0);
}
if (strcmp(path, "..") == 0) {
parent -> FileSize = -1;
parent -> FirstAbsCluNr = 0;
parent -> map = root_map;
return (0);
}
return (-1);
}
/*-----------------------------------------------------------------------------
* open_file --
*-----------------------------------------------------------------------------
*/
int open_file (Slot_t *file, Directory_t *dir)
{
int first;
unsigned long size;
first = __le16_to_cpu (dir -> start);
if(first == 0 &&
(dir -> attr & ATTR_DIRECTORY) != 0) {
file -> FirstAbsCluNr = 0;
file -> FileSize = -1;
file -> map = root_map;
return (0);
}
if ((dir -> attr & ATTR_DIRECTORY) != 0) {
size = (1UL << 31) - 1;
}
else {
size = __le32_to_cpu (dir -> size);
}
file -> map = normal_map;
file -> FirstAbsCluNr = first;
file -> PreviousRelCluNr = 0xffff;
file -> FileSize = size;
return (0);
}
/*-----------------------------------------------------------------------------
* read_file --
*-----------------------------------------------------------------------------
*/
int read_file (Fs_t *fs,
Slot_t *file,
char *buf,
int where,
int len)
{
int pos;
int read, nb, sect, offset;
pos = file -> map (fs, file, where, &len);
if (pos < 0) {
return -1;
}
if (len == 0) {
return (0);
}
/* Compute sector number */
sect = pos / SZ_STD_SECTOR;
offset = pos % SZ_STD_SECTOR;
read = 0;
if (offset) {
/* Read doesn't start at the sector beginning. We need to use our */
/* cache */
if (sect != cache_sect) {
if (dev_read (cache, sect, 1) < 0) {
return (-1);
}
cache_sect = sect;
}
nb = min (len, SZ_STD_SECTOR - offset);
memcpy (buf, cache + offset, nb);
read += nb;
len -= nb;
sect += 1;
}
if (len > SZ_STD_SECTOR) {
nb = (len - 1) / SZ_STD_SECTOR;
if (dev_read (buf + read, sect, nb) < 0) {
return ((read) ? read : -1);
}
/* update sector position */
sect += nb;
/* Update byte position */
nb *= SZ_STD_SECTOR;
read += nb;
len -= nb;
}
if (len) {
if (sect != cache_sect) {
if (dev_read (cache, sect, 1) < 0) {
return ((read) ? read : -1);
cache_sect = -1;
}
cache_sect = sect;
}
memcpy (buf + read, cache, len);
read += len;
}
return (read);
}

View file

@ -0,0 +1,352 @@
/*
* (C) Copyright 2002
* Stäubli Faverges - <www.staubli.com>
* Pierre AUBERT p.aubert@staubli.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <config.h>
#include <linux/ctype.h>
#include "dos.h"
#include "fdos.h"
static int dir_read (Fs_t *fs,
Slot_t *dir,
Directory_t *dirent,
int num,
struct vfat_state *v);
static int unicode_read (char *in, char *out, int num);
static int match (const char *s, const char *p);
static unsigned char sum_shortname (char *name);
static int check_vfat (struct vfat_state *v, Directory_t *dir);
static char *conv_name (char *name, char *ext, char Case, char *ans);
/*-----------------------------------------------------------------------------
* clear_vfat --
*-----------------------------------------------------------------------------
*/
static void clear_vfat (struct vfat_state *v)
{
v -> subentries = 0;
v -> status = 0;
}
/*-----------------------------------------------------------------------------
* vfat_lookup --
*-----------------------------------------------------------------------------
*/
int vfat_lookup (Slot_t *dir,
Fs_t *fs,
Directory_t *dirent,
int *entry,
int *vfat_start,
char *filename,
int flags,
char *outname,
Slot_t *file)
{
int found;
struct vfat_state vfat;
char newfile [VSE_NAMELEN];
int vfat_present = 0;
if (*entry == -1) {
return -1;
}
found = 0;
clear_vfat (&vfat);
while (1) {
if (dir_read (fs, dir, dirent, *entry, &vfat) < 0) {
if (vfat_start) {
*vfat_start = *entry;
}
break;
}
(*entry)++;
/* Empty slot */
if (dirent -> name[0] == '\0'){
if (vfat_start == 0) {
break;
}
continue;
}
if (dirent -> attr == ATTR_VSE) {
/* VSE entry, continue */
continue;
}
if ( (dirent -> name [0] == DELMARK) ||
((dirent -> attr & ATTR_DIRECTORY) != 0 &&
(flags & ACCEPT_DIR) == 0) ||
((dirent -> attr & ATTR_VOLUME) != 0 &&
(flags & ACCEPT_LABEL) == 0) ||
(((dirent -> attr & (ATTR_DIRECTORY | ATTR_VOLUME)) == 0) &&
(flags & ACCEPT_PLAIN) == 0)) {
clear_vfat (&vfat);
continue;
}
vfat_present = check_vfat (&vfat, dirent);
if (vfat_start) {
*vfat_start = *entry - 1;
if (vfat_present) {
*vfat_start -= vfat.subentries;
}
}
if (dirent -> attr & ATTR_VOLUME) {
strncpy (newfile, dirent -> name, 8);
newfile [8] = '\0';
strncat (newfile, dirent -> ext, 3);
newfile [11] = '\0';
}
else {
conv_name (dirent -> name, dirent -> ext, dirent -> Case, newfile);
}
if (flags & MATCH_ANY) {
found = 1;
break;
}
if ((vfat_present && match (vfat.name, filename)) ||
(match (newfile, filename))) {
found = 1;
break;
}
clear_vfat (&vfat);
}
if (found) {
if ((flags & DO_OPEN) && file) {
if (open_file (file, dirent) < 0) {
return (-1);
}
}
if (outname) {
if (vfat_present) {
strcpy (outname, vfat.name);
}
else {
strcpy (outname, newfile);
}
}
return (0); /* File found */
} else {
*entry = -1;
return -1; /* File not found */
}
}
/*-----------------------------------------------------------------------------
* dir_read -- Read one directory entry
*-----------------------------------------------------------------------------
*/
static int dir_read (Fs_t *fs,
Slot_t *dir,
Directory_t *dirent,
int num,
struct vfat_state *v)
{
/* read the directory entry */
if (read_file (fs,
dir,
(char *)dirent,
num * MDIR_SIZE,
MDIR_SIZE) != MDIR_SIZE) {
return (-1);
}
if (v && (dirent -> attr == ATTR_VSE)) {
struct vfat_subentry *vse;
unsigned char id, last_flag;
char *c;
vse = (struct vfat_subentry *) dirent;
id = vse -> id & VSE_MASK;
last_flag = (vse -> id & VSE_LAST);
if (id > MAX_VFAT_SUBENTRIES) {
/* Invalid VSE entry */
return (-1);
}
/* Decode VSE */
if(v -> sum != vse -> sum) {
clear_vfat (v);
v -> sum = vse -> sum;
}
v -> status |= 1 << (id - 1);
if (last_flag) {
v -> subentries = id;
}
c = &(v -> name [VSE_NAMELEN * (id - 1)]);
c += unicode_read (vse->text1, c, VSE1SIZE);
c += unicode_read (vse->text2, c, VSE2SIZE);
c += unicode_read (vse->text3, c, VSE3SIZE);
if (last_flag) {
*c = '\0'; /* Null terminate long name */
}
}
return (0);
}
/*-----------------------------------------------------------------------------
* unicode_read --
*-----------------------------------------------------------------------------
*/
static int unicode_read (char *in, char *out, int num)
{
int j;
for (j = 0; j < num; ++j) {
if (in [1])
*out = '_';
else
*out = in [0];
out ++;
in += 2;
}
return num;
}
/*-----------------------------------------------------------------------------
* match --
*-----------------------------------------------------------------------------
*/
static int match (const char *s, const char *p)
{
for (; *p != '\0'; ) {
if (toupper (*s) != toupper (*p)) {
return (0);
}
p++;
s++;
}
if (*s != '\0') {
return (0);
}
else {
return (1);
}
}
/*-----------------------------------------------------------------------------
* sum_shortname --
*-----------------------------------------------------------------------------
*/
static unsigned char sum_shortname (char *name)
{
unsigned char sum;
int j;
for (j = sum = 0; j < 11; ++j) {
sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) +
(name [j] ? name [j] : ' ');
}
return (sum);
}
/*-----------------------------------------------------------------------------
* check_vfat --
* Return 1 if long name is valid, 0 else
*-----------------------------------------------------------------------------
*/
static int check_vfat (struct vfat_state *v, Directory_t *dir)
{
char name[12];
if (v -> subentries == 0) {
return 0;
}
strncpy (name, dir -> name, 8);
strncpy (name + 8, dir -> ext, 3);
name [11] = '\0';
if (v -> sum != sum_shortname (name)) {
return 0;
}
if( (v -> status & ((1 << v -> subentries) - 1)) !=
(1 << v -> subentries) - 1) {
return 0;
}
v->name [VSE_NAMELEN * v -> subentries] = 0;
return 1;
}
/*-----------------------------------------------------------------------------
* conv_name --
*-----------------------------------------------------------------------------
*/
static char *conv_name (char *name, char *ext, char Case, char *ans)
{
char tname [9], text [4];
int i;
i = 0;
while (i < 8 && name [i] != ' ' && name [i] != '\0') {
tname [i] = name [i];
i++;
}
tname [i] = '\0';
if (Case & BASECASE) {
for (i = 0; i < 8 && tname [i]; i++) {
tname [i] = tolower (tname [i]);
}
}
i = 0;
while (i < 3 && ext [i] != ' ' && ext [i] != '\0') {
text [i] = ext [i];
i++;
}
text [i] = '\0';
if (Case & EXTCASE){
for (i = 0; i < 3 && text [i]; i++) {
text [i] = tolower (text [i]);
}
}
if (*text) {
strcpy (ans, tname);
strcat (ans, ".");
strcat (ans, text);
}
else {
strcpy(ans, tname);
}
return (ans);
}

View file

@ -0,0 +1,30 @@
The files in this directory and elsewhere which refer to this LICENCE
file are part of JFFS2, the Journalling Flash File System v2.
Copyright © 2001-2007 Red Hat, Inc. and others
JFFS2 is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 or (at your option) any later
version.
JFFS2 is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with JFFS2; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
As a special exception, if other files instantiate templates or use
macros or inline functions from these files, or you compile these
files and link them with other works to produce a work based on these
files, these files do not by themselves cause the resulting work to be
covered by the GNU General Public License. However the source code for
these files must still be made available in accordance with section (3)
of the GNU General Public License.
This exception does not invalidate any other reasons why a work based on
this file might be covered by the GNU General Public License.

View file

@ -0,0 +1,57 @@
#
# (C) Copyright 2000-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libjffs2.o
AOBJS =
ifdef CONFIG_CMD_JFFS2
COBJS-$(CONFIG_JFFS2_LZO) += compr_lzo.o
COBJS-y += compr_rtime.o
COBJS-y += compr_rubin.o
COBJS-y += compr_zlib.o
COBJS-y += jffs2_1pass.o
COBJS-y += mini_inflate.o
endif
COBJS := $(COBJS-y)
SRCS := $(AOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS))
#CPPFLAGS +=
all: $(LIB) $(AOBJS)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,401 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2004 Patrik Kluba,
* University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in the
* jffs2 directory.
*
* $Id: compr_lzo.c,v 1.3 2004/06/23 16:34:39 havasi Exp $
*
*/
/*
LZO1X-1 (and -999) compression module for jffs2
based on the original LZO sources
*/
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
Original copyright notice follows:
lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm
lzo_ptr.h -- low-level pointer constructs
lzo_swd.ch -- sliding window dictionary
lzoconf.h -- configuration for the LZO real-time data compression library
lzo_mchw.ch -- matching functions using a window
minilzo.c -- mini subset of the LZO real-time data compression library
config1x.h -- configuration for the LZO1X algorithm
lzo1x.h -- public interface of the LZO1X compression algorithm
These files are part of the LZO real-time data compression library.
Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
All Rights Reserved.
The LZO library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
The LZO library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the LZO library; see the file COPYING.
If not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Markus F.X.J. Oberhumer
<markus@oberhumer.com>
*/
/*
2004-02-16 pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
Initial release
-removed all 16 bit code
-all sensitive data will be on 4 byte boundary
-removed check parts for library use
-removed all but LZO1X-* compression
*/
#include <config.h>
#include <linux/stddef.h>
#include <jffs2/jffs2.h>
#include <jffs2/compr_rubin.h>
/* Integral types that have *exactly* the same number of bits as a lzo_voidp */
typedef unsigned long lzo_ptr_t;
typedef long lzo_sptr_t;
/* data type definitions */
#define U32 unsigned long
#define S32 signed long
#define I32 long
#define U16 unsigned short
#define S16 signed short
#define I16 short
#define U8 unsigned char
#define S8 signed char
#define I8 char
#define M1_MAX_OFFSET 0x0400
#define M2_MAX_OFFSET 0x0800
#define M3_MAX_OFFSET 0x4000
#define M4_MAX_OFFSET 0xbfff
#define __COPY4(dst,src) * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src)
#define COPY4(dst,src) __COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src))
#define TEST_IP (ip < ip_end)
#define TEST_OP (op <= op_end)
#define NEED_IP(x) \
if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun
#define NEED_OP(x) \
if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun
#define TEST_LOOKBEHIND(m_pos,out) if (m_pos < out) goto lookbehind_overrun
typedef U32 lzo_uint32;
typedef I32 lzo_int32;
typedef U32 lzo_uint;
typedef I32 lzo_int;
typedef int lzo_bool;
#define lzo_byte U8
#define lzo_bytep U8 *
#define lzo_charp char *
#define lzo_voidp void *
#define lzo_shortp short *
#define lzo_ushortp unsigned short *
#define lzo_uint32p lzo_uint32 *
#define lzo_int32p lzo_int32 *
#define lzo_uintp lzo_uint *
#define lzo_intp lzo_int *
#define lzo_voidpp lzo_voidp *
#define lzo_bytepp lzo_bytep *
#define lzo_sizeof_dict_t sizeof(lzo_bytep)
#define LZO_E_OK 0
#define LZO_E_ERROR (-1)
#define LZO_E_OUT_OF_MEMORY (-2) /* not used right now */
#define LZO_E_NOT_COMPRESSIBLE (-3) /* not used right now */
#define LZO_E_INPUT_OVERRUN (-4)
#define LZO_E_OUTPUT_OVERRUN (-5)
#define LZO_E_LOOKBEHIND_OVERRUN (-6)
#define LZO_E_EOF_NOT_FOUND (-7)
#define LZO_E_INPUT_NOT_CONSUMED (-8)
#define PTR(a) ((lzo_ptr_t) (a))
#define PTR_LINEAR(a) PTR(a)
#define PTR_ALIGNED_4(a) ((PTR_LINEAR(a) & 3) == 0)
#define PTR_ALIGNED_8(a) ((PTR_LINEAR(a) & 7) == 0)
#define PTR_ALIGNED2_4(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0)
#define PTR_ALIGNED2_8(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0)
#define PTR_LT(a,b) (PTR(a) < PTR(b))
#define PTR_GE(a,b) (PTR(a) >= PTR(b))
#define PTR_DIFF(a,b) ((lzo_ptrdiff_t) (PTR(a) - PTR(b)))
#define pd(a,b) ((lzo_uint) ((a)-(b)))
typedef ptrdiff_t lzo_ptrdiff_t;
static int
lzo1x_decompress (const lzo_byte * in, lzo_uint in_len,
lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
{
register lzo_byte *op;
register const lzo_byte *ip;
register lzo_uint t;
register const lzo_byte *m_pos;
const lzo_byte *const ip_end = in + in_len;
lzo_byte *const op_end = out + *out_len;
*out_len = 0;
op = out;
ip = in;
if (*ip > 17)
{
t = *ip++ - 17;
if (t < 4)
goto match_next;
NEED_OP (t);
NEED_IP (t + 1);
do
*op++ = *ip++;
while (--t > 0);
goto first_literal_run;
}
while (TEST_IP && TEST_OP)
{
t = *ip++;
if (t >= 16)
goto match;
if (t == 0)
{
NEED_IP (1);
while (*ip == 0)
{
t += 255;
ip++;
NEED_IP (1);
}
t += 15 + *ip++;
}
NEED_OP (t + 3);
NEED_IP (t + 4);
if (PTR_ALIGNED2_4 (op, ip))
{
COPY4 (op, ip);
op += 4;
ip += 4;
if (--t > 0)
{
if (t >= 4)
{
do
{
COPY4 (op, ip);
op += 4;
ip += 4;
t -= 4;
}
while (t >= 4);
if (t > 0)
do
*op++ = *ip++;
while (--t > 0);
}
else
do
*op++ = *ip++;
while (--t > 0);
}
}
else
{
*op++ = *ip++;
*op++ = *ip++;
*op++ = *ip++;
do
*op++ = *ip++;
while (--t > 0);
}
first_literal_run:
t = *ip++;
if (t >= 16)
goto match;
m_pos = op - (1 + M2_MAX_OFFSET);
m_pos -= t >> 2;
m_pos -= *ip++ << 2;
TEST_LOOKBEHIND (m_pos, out);
NEED_OP (3);
*op++ = *m_pos++;
*op++ = *m_pos++;
*op++ = *m_pos;
goto match_done;
while (TEST_IP && TEST_OP)
{
match:
if (t >= 64)
{
m_pos = op - 1;
m_pos -= (t >> 2) & 7;
m_pos -= *ip++ << 3;
t = (t >> 5) - 1;
TEST_LOOKBEHIND (m_pos, out);
NEED_OP (t + 3 - 1);
goto copy_match;
}
else if (t >= 32)
{
t &= 31;
if (t == 0)
{
NEED_IP (1);
while (*ip == 0)
{
t += 255;
ip++;
NEED_IP (1);
}
t += 31 + *ip++;
}
m_pos = op - 1;
m_pos -= (ip[0] >> 2) + (ip[1] << 6);
ip += 2;
}
else if (t >= 16)
{
m_pos = op;
m_pos -= (t & 8) << 11;
t &= 7;
if (t == 0)
{
NEED_IP (1);
while (*ip == 0)
{
t += 255;
ip++;
NEED_IP (1);
}
t += 7 + *ip++;
}
m_pos -= (ip[0] >> 2) + (ip[1] << 6);
ip += 2;
if (m_pos == op)
goto eof_found;
m_pos -= 0x4000;
}
else
{
m_pos = op - 1;
m_pos -= t >> 2;
m_pos -= *ip++ << 2;
TEST_LOOKBEHIND (m_pos, out);
NEED_OP (2);
*op++ = *m_pos++;
*op++ = *m_pos;
goto match_done;
}
TEST_LOOKBEHIND (m_pos, out);
NEED_OP (t + 3 - 1);
if (t >= 2 * 4 - (3 - 1)
&& PTR_ALIGNED2_4 (op, m_pos))
{
COPY4 (op, m_pos);
op += 4;
m_pos += 4;
t -= 4 - (3 - 1);
do
{
COPY4 (op, m_pos);
op += 4;
m_pos += 4;
t -= 4;
}
while (t >= 4);
if (t > 0)
do
*op++ = *m_pos++;
while (--t > 0);
}
else
{
copy_match:
*op++ = *m_pos++;
*op++ = *m_pos++;
do
*op++ = *m_pos++;
while (--t > 0);
}
match_done:
t = ip[-2] & 3;
if (t == 0)
break;
match_next:
NEED_OP (t);
NEED_IP (t + 1);
do
*op++ = *ip++;
while (--t > 0);
t = *ip++;
}
}
*out_len = op - out;
return LZO_E_EOF_NOT_FOUND;
eof_found:
*out_len = op - out;
return (ip == ip_end ? LZO_E_OK :
(ip <
ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
input_overrun:
*out_len = op - out;
return LZO_E_INPUT_OVERRUN;
output_overrun:
*out_len = op - out;
return LZO_E_OUTPUT_OVERRUN;
lookbehind_overrun:
*out_len = op - out;
return LZO_E_LOOKBEHIND_OVERRUN;
}
int lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
u32 srclen, u32 destlen)
{
lzo_uint outlen = destlen;
return lzo1x_decompress (data_in, srclen, cpage_out, &outlen, NULL);
}

View file

@ -0,0 +1,87 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
* The original JFFS, from which the design for JFFS2 was derived,
* was designed and implemented by Axis Communications AB.
*
* The contents of this file are subject to the Red Hat eCos Public
* License Version 1.1 (the "Licence"); you may not use this file
* except in compliance with the Licence. You may obtain a copy of
* the Licence at http://www.redhat.com/
*
* Software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the Licence for the specific language governing rights and
* limitations under the Licence.
*
* The Original Code is JFFS2 - Journalling Flash File System, version 2
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in
* which case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the RHEPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: compr_rtime.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $
*
*
* Very simple lz77-ish encoder.
*
* Theory of operation: Both encoder and decoder have a list of "last
* occurances" for every possible source-value; after sending the
* first source-byte, the second byte indicated the "run" length of
* matches
*
* The algorithm is intended to only send "whole bytes", no bit-messing.
*
*/
#include <config.h>
#include <jffs2/jffs2.h>
void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
u32 srclen, u32 destlen)
{
int positions[256];
int outpos;
int pos;
int i;
outpos = pos = 0;
for (i = 0; i < 256; positions[i++] = 0);
while (outpos<destlen) {
unsigned char value;
int backoffs;
int repeat;
value = data_in[pos++];
cpage_out[outpos++] = value; /* first the verbatim copied byte */
repeat = data_in[pos++];
backoffs = positions[value];
positions[value]=outpos;
if (repeat) {
if (backoffs + repeat >= outpos) {
while(repeat) {
cpage_out[outpos++] = cpage_out[backoffs++];
repeat--;
}
} else {
for (i = 0; i < repeat; i++)
*(cpage_out + outpos + i) = *(cpage_out + backoffs + i);
outpos+=repeat;
}
}
}
}

View file

@ -0,0 +1,122 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
* Heavily modified by Russ Dill <Russ.Dill@asu.edu> in an attempt at
* a little more speed.
*
* The original JFFS, from which the design for JFFS2 was derived,
* was designed and implemented by Axis Communications AB.
*
* The contents of this file are subject to the Red Hat eCos Public
* License Version 1.1 (the "Licence"); you may not use this file
* except in compliance with the Licence. You may obtain a copy of
* the Licence at http://www.redhat.com/
*
* Software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the Licence for the specific language governing rights and
* limitations under the Licence.
*
* The Original Code is JFFS2 - Journalling Flash File System, version 2
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in
* which case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the RHEPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: compr_rubin.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $
*
*/
#include <config.h>
#include <jffs2/jffs2.h>
#include <jffs2/compr_rubin.h>
void rubin_do_decompress(unsigned char *bits, unsigned char *in,
unsigned char *page_out, __u32 destlen)
{
register char *curr = (char *)page_out;
char *end = (char *)(page_out + destlen);
register unsigned long temp;
register unsigned long result;
register unsigned long p;
register unsigned long q;
register unsigned long rec_q;
register unsigned long bit;
register long i0;
unsigned long i;
/* init_pushpull */
temp = *(u32 *) in;
bit = 16;
/* init_rubin */
q = 0;
p = (long) (2 * UPPER_BIT_RUBIN);
/* init_decode */
rec_q = (in[0] << 8) | in[1];
while (curr < end) {
/* in byte */
result = 0;
for (i = 0; i < 8; i++) {
/* decode */
while ((q & UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN)) {
q &= ~UPPER_BIT_RUBIN;
q <<= 1;
p <<= 1;
rec_q &= ~UPPER_BIT_RUBIN;
rec_q <<= 1;
rec_q |= (temp >> (bit++ ^ 7)) & 1;
if (bit > 31) {
u32 *p = (u32 *)in;
bit = 0;
temp = *(++p);
in = (unsigned char *)p;
}
}
i0 = (bits[i] * p) >> 8;
if (i0 <= 0) i0 = 1;
/* if it fails, it fails, we have our crc
if (i0 >= p) i0 = p - 1; */
result >>= 1;
if (rec_q < q + i0) {
/* result |= 0x00; */
p = i0;
} else {
result |= 0x80;
p -= i0;
q += i0;
}
}
*(curr++) = result;
}
}
void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
unsigned long sourcelen, unsigned long dstlen)
{
unsigned char bits[8];
int c;
for (c=0; c<8; c++)
bits[c] = (256 - data_in[c]);
rubin_do_decompress(bits, data_in+8, cpage_out, dstlen);
}

View file

@ -0,0 +1,48 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* The original JFFS, from which the design for JFFS2 was derived,
* was designed and implemented by Axis Communications AB.
*
* The contents of this file are subject to the Red Hat eCos Public
* License Version 1.1 (the "Licence"); you may not use this file
* except in compliance with the Licence. You may obtain a copy of
* the Licence at http://www.redhat.com/
*
* Software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the Licence for the specific language governing rights and
* limitations under the Licence.
*
* The Original Code is JFFS2 - Journalling Flash File System, version 2
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in
* which case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the RHEPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: compr_zlib.c,v 1.2 2002/01/24 22:58:42 rfeany Exp $
*
*/
#include <common.h>
#include <config.h>
#include <jffs2/jffs2.h>
#include <jffs2/mini_inflate.h>
long zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
__u32 srclen, __u32 destlen)
{
return (decompress_block(cpage_out, data_in + 2, (void *) ldr_memcpy));
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,133 @@
#ifndef jffs2_private_h
#define jffs2_private_h
#include <jffs2/jffs2.h>
struct b_node {
struct b_node *next;
};
struct b_inode {
struct b_inode *next;
u32 offset; /* physical offset to beginning of real inode */
u32 version;
u32 ino;
u32 isize;
u32 csize;
};
struct b_dirent {
struct b_dirent *next;
u32 offset; /* physical offset to beginning of real dirent */
u32 version;
u32 pino;
u32 ino;
unsigned int nhash;
unsigned char nsize;
unsigned char type;
};
struct b_list {
struct b_node *listTail;
struct b_node *listHead;
unsigned int listCount;
struct mem_block *listMemBase;
};
struct b_lists {
char *partOffset;
struct b_list dir;
struct b_list frag;
};
struct b_compr_info {
u32 num_frags;
u32 compr_sum;
u32 decompr_sum;
};
struct b_jffs2_info {
struct b_compr_info compr_info[JFFS2_NUM_COMPR];
};
static inline int
hdr_crc(struct jffs2_unknown_node *node)
{
#if 1
u32 crc = crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4);
#else
/* what's the semantics of this? why is this here? */
u32 crc = crc32_no_comp(~0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4);
crc ^= ~0;
#endif
if (node->hdr_crc != crc) {
return 0;
} else {
return 1;
}
}
static inline int
dirent_crc(struct jffs2_raw_dirent *node)
{
if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_dirent) - 8)) {
return 0;
} else {
return 1;
}
}
static inline int
dirent_name_crc(struct jffs2_raw_dirent *node)
{
if (node->name_crc != crc32_no_comp(0, (unsigned char *)&(node->name), node->nsize)) {
return 0;
} else {
return 1;
}
}
static inline int
inode_crc(struct jffs2_raw_inode *node)
{
if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_inode) - 8)) {
return 0;
} else {
return 1;
}
}
/* Borrowed from include/linux/dcache.h */
/* Name hashing routines. Initial hash value */
/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
#define init_name_hash() 0
/* partial hash update function. Assume roughly 4 bits per character */
static inline unsigned long
partial_name_hash(unsigned long c, unsigned long prevhash)
{
return (prevhash + (c << 4) + (c >> 4)) * 11;
}
/*
* Finally: cut down the number of bits to a int value (and try to avoid
* losing bits)
*/
static inline unsigned long end_name_hash(unsigned long hash)
{
return (unsigned int) hash;
}
/* Compute the hash for a name string. */
static inline unsigned int
full_name_hash(const unsigned char *name, unsigned int len)
{
unsigned long hash = init_name_hash();
while (len--)
hash = partial_name_hash(*name++, hash);
return end_name_hash(hash);
}
#endif /* jffs2_private.h */

View file

@ -0,0 +1,101 @@
#ifndef jffs2_private_h
#define jffs2_private_h
#include <jffs2/jffs2.h>
struct b_node {
u32 offset;
struct b_node *next;
enum { CRC_UNKNOWN = 0, CRC_OK, CRC_BAD } datacrc;
};
struct b_list {
struct b_node *listTail;
struct b_node *listHead;
#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
struct b_node *listLast;
int (*listCompare)(struct b_node *new, struct b_node *node);
u32 listLoops;
#endif
u32 listCount;
struct mem_block *listMemBase;
};
struct b_lists {
struct b_list dir;
struct b_list frag;
void *readbuf;
};
struct b_compr_info {
u32 num_frags;
u32 compr_sum;
u32 decompr_sum;
};
struct b_jffs2_info {
struct b_compr_info compr_info[JFFS2_NUM_COMPR];
};
static inline int
hdr_crc(struct jffs2_unknown_node *node)
{
#if 1
u32 crc = crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4);
#else
/* what's the semantics of this? why is this here? */
u32 crc = crc32_no_comp(~0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4);
crc ^= ~0;
#endif
if (node->hdr_crc != crc) {
return 0;
} else {
return 1;
}
}
static inline int
dirent_crc(struct jffs2_raw_dirent *node)
{
if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_dirent) - 8)) {
return 0;
} else {
return 1;
}
}
static inline int
dirent_name_crc(struct jffs2_raw_dirent *node)
{
if (node->name_crc != crc32_no_comp(0, (unsigned char *)&(node->name), node->nsize)) {
return 0;
} else {
return 1;
}
}
static inline int
inode_crc(struct jffs2_raw_inode *node)
{
if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_inode) - 8)) {
return 0;
} else {
return 1;
}
}
static inline int
data_crc(struct jffs2_raw_inode *node)
{
if (node->data_crc != crc32_no_comp(0, (unsigned char *)
((int) &node->node_crc + sizeof (node->node_crc)),
node->csize)) {
return 0;
} else {
return 1;
}
}
#endif /* jffs2_private.h */

View file

@ -0,0 +1,391 @@
/*-------------------------------------------------------------------------
* Filename: mini_inflate.c
* Version: $Id: mini_inflate.c,v 1.3 2002/01/24 22:58:42 rfeany Exp $
* Copyright: Copyright (C) 2001, Russ Dill
* Author: Russ Dill <Russ.Dill@asu.edu>
* Description: Mini inflate implementation (RFC 1951)
*-----------------------------------------------------------------------*/
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <config.h>
#include <jffs2/mini_inflate.h>
/* The order that the code lengths in section 3.2.7 are in */
static unsigned char huffman_order[] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5,
11, 4, 12, 3, 13, 2, 14, 1, 15};
inline void cramfs_memset(int *s, const int c, size n)
{
n--;
for (;n > 0; n--) s[n] = c;
s[0] = c;
}
/* associate a stream with a block of data and reset the stream */
static void init_stream(struct bitstream *stream, unsigned char *data,
void *(*inflate_memcpy)(void *, const void *, size))
{
stream->error = NO_ERROR;
stream->memcpy = inflate_memcpy;
stream->decoded = 0;
stream->data = data;
stream->bit = 0; /* The first bit of the stream is the lsb of the
* first byte */
/* really sorry about all this initialization, think of a better way,
* let me know and it will get cleaned up */
stream->codes.bits = 8;
stream->codes.num_symbols = 19;
stream->codes.lengths = stream->code_lengths;
stream->codes.symbols = stream->code_symbols;
stream->codes.count = stream->code_count;
stream->codes.first = stream->code_first;
stream->codes.pos = stream->code_pos;
stream->lengths.bits = 16;
stream->lengths.num_symbols = 288;
stream->lengths.lengths = stream->length_lengths;
stream->lengths.symbols = stream->length_symbols;
stream->lengths.count = stream->length_count;
stream->lengths.first = stream->length_first;
stream->lengths.pos = stream->length_pos;
stream->distance.bits = 16;
stream->distance.num_symbols = 32;
stream->distance.lengths = stream->distance_lengths;
stream->distance.symbols = stream->distance_symbols;
stream->distance.count = stream->distance_count;
stream->distance.first = stream->distance_first;
stream->distance.pos = stream->distance_pos;
}
/* pull 'bits' bits out of the stream. The last bit pulled it returned as the
* msb. (section 3.1.1)
*/
inline unsigned long pull_bits(struct bitstream *stream,
const unsigned int bits)
{
unsigned long ret;
int i;
ret = 0;
for (i = 0; i < bits; i++) {
ret += ((*(stream->data) >> stream->bit) & 1) << i;
/* if, before incrementing, we are on bit 7,
* go to the lsb of the next byte */
if (stream->bit++ == 7) {
stream->bit = 0;
stream->data++;
}
}
return ret;
}
inline int pull_bit(struct bitstream *stream)
{
int ret = ((*(stream->data) >> stream->bit) & 1);
if (stream->bit++ == 7) {
stream->bit = 0;
stream->data++;
}
return ret;
}
/* discard bits up to the next whole byte */
static void discard_bits(struct bitstream *stream)
{
if (stream->bit != 0) {
stream->bit = 0;
stream->data++;
}
}
/* No decompression, the data is all literals (section 3.2.4) */
static void decompress_none(struct bitstream *stream, unsigned char *dest)
{
unsigned int length;
discard_bits(stream);
length = *(stream->data++);
length += *(stream->data++) << 8;
pull_bits(stream, 16); /* throw away the inverse of the size */
stream->decoded += length;
stream->memcpy(dest, stream->data, length);
stream->data += length;
}
/* Read in a symbol from the stream (section 3.2.2) */
static int read_symbol(struct bitstream *stream, struct huffman_set *set)
{
int bits = 0;
int code = 0;
while (!(set->count[bits] && code < set->first[bits] +
set->count[bits])) {
code = (code << 1) + pull_bit(stream);
if (++bits > set->bits) {
/* error decoding (corrupted data?) */
stream->error = CODE_NOT_FOUND;
return -1;
}
}
return set->symbols[set->pos[bits] + code - set->first[bits]];
}
/* decompress a stream of data encoded with the passed length and distance
* huffman codes */
static void decompress_huffman(struct bitstream *stream, unsigned char *dest)
{
struct huffman_set *lengths = &(stream->lengths);
struct huffman_set *distance = &(stream->distance);
int symbol, length, dist, i;
do {
if ((symbol = read_symbol(stream, lengths)) < 0) return;
if (symbol < 256) {
*(dest++) = symbol; /* symbol is a literal */
stream->decoded++;
} else if (symbol > 256) {
/* Determine the length of the repitition
* (section 3.2.5) */
if (symbol < 265) length = symbol - 254;
else if (symbol == 285) length = 258;
else {
length = pull_bits(stream, (symbol - 261) >> 2);
length += (4 << ((symbol - 261) >> 2)) + 3;
length += ((symbol - 1) % 4) <<
((symbol - 261) >> 2);
}
/* Determine how far back to go */
if ((symbol = read_symbol(stream, distance)) < 0)
return;
if (symbol < 4) dist = symbol + 1;
else {
dist = pull_bits(stream, (symbol - 2) >> 1);
dist += (2 << ((symbol - 2) >> 1)) + 1;
dist += (symbol % 2) << ((symbol - 2) >> 1);
}
stream->decoded += length;
for (i = 0; i < length; i++) {
*dest = dest[-dist];
dest++;
}
}
} while (symbol != 256); /* 256 is the end of the data block */
}
/* Fill the lookup tables (section 3.2.2) */
static void fill_code_tables(struct huffman_set *set)
{
int code = 0, i, length;
/* fill in the first code of each bit length, and the pos pointer */
set->pos[0] = 0;
for (i = 1; i < set->bits; i++) {
code = (code + set->count[i - 1]) << 1;
set->first[i] = code;
set->pos[i] = set->pos[i - 1] + set->count[i - 1];
}
/* Fill in the table of symbols in order of their huffman code */
for (i = 0; i < set->num_symbols; i++) {
if ((length = set->lengths[i]))
set->symbols[set->pos[length]++] = i;
}
/* reset the pos pointer */
for (i = 1; i < set->bits; i++) set->pos[i] -= set->count[i];
}
static void init_code_tables(struct huffman_set *set)
{
cramfs_memset(set->lengths, 0, set->num_symbols);
cramfs_memset(set->count, 0, set->bits);
cramfs_memset(set->first, 0, set->bits);
}
/* read in the huffman codes for dynamic decoding (section 3.2.7) */
static void decompress_dynamic(struct bitstream *stream, unsigned char *dest)
{
/* I tried my best to minimize the memory footprint here, while still
* keeping up performance. I really dislike the _lengths[] tables, but
* I see no way of eliminating them without a sizable performance
* impact. The first struct table keeps track of stats on each bit
* length. The _length table keeps a record of the bit length of each
* symbol. The _symbols table is for looking up symbols by the huffman
* code (the pos element points to the first place in the symbol table
* where that bit length occurs). I also hate the initization of these
* structs, if someone knows how to compact these, lemme know. */
struct huffman_set *codes = &(stream->codes);
struct huffman_set *lengths = &(stream->lengths);
struct huffman_set *distance = &(stream->distance);
int hlit = pull_bits(stream, 5) + 257;
int hdist = pull_bits(stream, 5) + 1;
int hclen = pull_bits(stream, 4) + 4;
int length, curr_code, symbol, i, last_code;
last_code = 0;
init_code_tables(codes);
init_code_tables(lengths);
init_code_tables(distance);
/* fill in the count of each bit length' as well as the lengths
* table */
for (i = 0; i < hclen; i++) {
length = pull_bits(stream, 3);
codes->lengths[huffman_order[i]] = length;
if (length) codes->count[length]++;
}
fill_code_tables(codes);
/* Do the same for the length codes, being carefull of wrap through
* to the distance table */
curr_code = 0;
while (curr_code < hlit) {
if ((symbol = read_symbol(stream, codes)) < 0) return;
if (symbol == 0) {
curr_code++;
last_code = 0;
} else if (symbol < 16) { /* Literal length */
lengths->lengths[curr_code] = last_code = symbol;
lengths->count[symbol]++;
curr_code++;
} else if (symbol == 16) { /* repeat the last symbol 3 - 6
* times */
length = 3 + pull_bits(stream, 2);
for (;length; length--, curr_code++)
if (curr_code < hlit) {
lengths->lengths[curr_code] =
last_code;
lengths->count[last_code]++;
} else { /* wrap to the distance table */
distance->lengths[curr_code - hlit] =
last_code;
distance->count[last_code]++;
}
} else if (symbol == 17) { /* repeat a bit length 0 */
curr_code += 3 + pull_bits(stream, 3);
last_code = 0;
} else { /* same, but more times */
curr_code += 11 + pull_bits(stream, 7);
last_code = 0;
}
}
fill_code_tables(lengths);
/* Fill the distance table, don't need to worry about wrapthrough
* here */
curr_code -= hlit;
while (curr_code < hdist) {
if ((symbol = read_symbol(stream, codes)) < 0) return;
if (symbol == 0) {
curr_code++;
last_code = 0;
} else if (symbol < 16) {
distance->lengths[curr_code] = last_code = symbol;
distance->count[symbol]++;
curr_code++;
} else if (symbol == 16) {
length = 3 + pull_bits(stream, 2);
for (;length; length--, curr_code++) {
distance->lengths[curr_code] =
last_code;
distance->count[last_code]++;
}
} else if (symbol == 17) {
curr_code += 3 + pull_bits(stream, 3);
last_code = 0;
} else {
curr_code += 11 + pull_bits(stream, 7);
last_code = 0;
}
}
fill_code_tables(distance);
decompress_huffman(stream, dest);
}
/* fill in the length and distance huffman codes for fixed encoding
* (section 3.2.6) */
static void decompress_fixed(struct bitstream *stream, unsigned char *dest)
{
/* let gcc fill in the initial values */
struct huffman_set *lengths = &(stream->lengths);
struct huffman_set *distance = &(stream->distance);
cramfs_memset(lengths->count, 0, 16);
cramfs_memset(lengths->first, 0, 16);
cramfs_memset(lengths->lengths, 8, 144);
cramfs_memset(lengths->lengths + 144, 9, 112);
cramfs_memset(lengths->lengths + 256, 7, 24);
cramfs_memset(lengths->lengths + 280, 8, 8);
lengths->count[7] = 24;
lengths->count[8] = 152;
lengths->count[9] = 112;
cramfs_memset(distance->count, 0, 16);
cramfs_memset(distance->first, 0, 16);
cramfs_memset(distance->lengths, 5, 32);
distance->count[5] = 32;
fill_code_tables(lengths);
fill_code_tables(distance);
decompress_huffman(stream, dest);
}
/* returns the number of bytes decoded, < 0 if there was an error. Note that
* this function assumes that the block starts on a byte boundry
* (non-compliant, but I don't see where this would happen). section 3.2.3 */
long decompress_block(unsigned char *dest, unsigned char *source,
void *(*inflate_memcpy)(void *, const void *, size))
{
int bfinal, btype;
struct bitstream stream;
init_stream(&stream, source, inflate_memcpy);
do {
bfinal = pull_bit(&stream);
btype = pull_bits(&stream, 2);
if (btype == NO_COMP) decompress_none(&stream, dest + stream.decoded);
else if (btype == DYNAMIC_COMP)
decompress_dynamic(&stream, dest + stream.decoded);
else if (btype == FIXED_COMP) decompress_fixed(&stream, dest + stream.decoded);
else stream.error = COMP_UNKNOWN;
} while (!bfinal && !stream.error);
#if 0
putstr("decompress_block start\r\n");
putLabeledWord("stream.error = ",stream.error);
putLabeledWord("stream.decoded = ",stream.decoded);
putLabeledWord("dest = ",dest);
putstr("decompress_block end\r\n");
#endif
return stream.error ? -stream.error : stream.decoded;
}

View file

@ -0,0 +1,163 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
* Zoltan Sogor <weth@inf.u-szeged.hu>,
* Patrik Kluba <pajko@halom.u-szeged.hu>,
* University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in this directory.
*
*/
#ifndef JFFS2_SUMMARY_H
#define JFFS2_SUMMARY_H
#define BLK_STATE_ALLFF 0
#define BLK_STATE_CLEAN 1
#define BLK_STATE_PARTDIRTY 2
#define BLK_STATE_CLEANMARKER 3
#define BLK_STATE_ALLDIRTY 4
#define BLK_STATE_BADBLOCK 5
#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))
/* Summary structures used on flash */
struct jffs2_sum_unknown_flash
{
__u16 nodetype; /* node type */
};
struct jffs2_sum_inode_flash
{
__u16 nodetype; /* node type */
__u32 inode; /* inode number */
__u32 version; /* inode version */
__u32 offset; /* offset on jeb */
__u32 totlen; /* record length */
} __attribute__((packed));
struct jffs2_sum_dirent_flash
{
__u16 nodetype; /* == JFFS_NODETYPE_DIRENT */
__u32 totlen; /* record length */
__u32 offset; /* offset on jeb */
__u32 pino; /* parent inode */
__u32 version; /* dirent version */
__u32 ino; /* == zero for unlink */
uint8_t nsize; /* dirent name size */
uint8_t type; /* dirent type */
uint8_t name[0]; /* dirent name */
} __attribute__((packed));
struct jffs2_sum_xattr_flash
{
__u16 nodetype; /* == JFFS2_NODETYPE_XATR */
__u32 xid; /* xattr identifier */
__u32 version; /* version number */
__u32 offset; /* offset on jeb */
__u32 totlen; /* node length */
} __attribute__((packed));
struct jffs2_sum_xref_flash
{
__u16 nodetype; /* == JFFS2_NODETYPE_XREF */
__u32 offset; /* offset on jeb */
} __attribute__((packed));
union jffs2_sum_flash
{
struct jffs2_sum_unknown_flash u;
struct jffs2_sum_inode_flash i;
struct jffs2_sum_dirent_flash d;
struct jffs2_sum_xattr_flash x;
struct jffs2_sum_xref_flash r;
};
/* Summary structures used in the memory */
struct jffs2_sum_unknown_mem
{
union jffs2_sum_mem *next;
__u16 nodetype; /* node type */
};
struct jffs2_sum_inode_mem
{
union jffs2_sum_mem *next;
__u16 nodetype; /* node type */
__u32 inode; /* inode number */
__u32 version; /* inode version */
__u32 offset; /* offset on jeb */
__u32 totlen; /* record length */
} __attribute__((packed));
struct jffs2_sum_dirent_mem
{
union jffs2_sum_mem *next;
__u16 nodetype; /* == JFFS_NODETYPE_DIRENT */
__u32 totlen; /* record length */
__u32 offset; /* ofset on jeb */
__u32 pino; /* parent inode */
__u32 version; /* dirent version */
__u32 ino; /* == zero for unlink */
uint8_t nsize; /* dirent name size */
uint8_t type; /* dirent type */
uint8_t name[0]; /* dirent name */
} __attribute__((packed));
struct jffs2_sum_xattr_mem
{
union jffs2_sum_mem *next;
__u16 nodetype;
__u32 xid;
__u32 version;
__u32 offset;
__u32 totlen;
} __attribute__((packed));
struct jffs2_sum_xref_mem
{
union jffs2_sum_mem *next;
__u16 nodetype;
__u32 offset;
} __attribute__((packed));
union jffs2_sum_mem
{
struct jffs2_sum_unknown_mem u;
struct jffs2_sum_inode_mem i;
struct jffs2_sum_dirent_mem d;
struct jffs2_sum_xattr_mem x;
struct jffs2_sum_xref_mem r;
};
/* Summary related information stored in superblock */
struct jffs2_summary
{
uint32_t sum_size; /* collected summary information for nextblock */
uint32_t sum_num;
uint32_t sum_padded;
union jffs2_sum_mem *sum_list_head;
union jffs2_sum_mem *sum_list_tail;
__u32 *sum_buf; /* buffer for writing out summary */
};
/* Summary marker is stored at the end of every sumarized erase block */
struct jffs2_sum_marker
{
__u32 offset; /* offset of the summary node in the jeb */
__u32 magic; /* == JFFS2_SUM_MAGIC */
};
#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker))
#endif /* JFFS2_SUMMARY_H */

View file

@ -0,0 +1,52 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2003
# Pavel Bartusek, Sysgo Real-Time Solutions AG, pba@sysgo.de
#
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libreiserfs.o
AOBJS =
COBJS-$(CONFIG_CMD_REISER) := reiserfs.o dev.o mode_string.o
SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS-y))
#CPPFLAGS +=
all: $(LIB) $(AOBJS)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,119 @@
/*
* (C) Copyright 2003 - 2004
* Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common.h>
#include <config.h>
#include <reiserfs.h>
#include "reiserfs_private.h"
static block_dev_desc_t *reiserfs_block_dev_desc;
static disk_partition_t part_info;
int reiserfs_set_blk_dev(block_dev_desc_t *rbdd, int part)
{
reiserfs_block_dev_desc = rbdd;
if (part == 0) {
/* disk doesn't use partition table */
part_info.start = 0;
part_info.size = rbdd->lba;
part_info.blksz = rbdd->blksz;
} else {
if (get_partition_info (reiserfs_block_dev_desc, part, &part_info)) {
return 0;
}
}
return (part_info.size);
}
int reiserfs_devread (int sector, int byte_offset, int byte_len, char *buf)
{
char sec_buf[SECTOR_SIZE];
unsigned block_len;
/*
unsigned len = byte_len;
u8 *start = buf;
*/
/*
* Check partition boundaries
*/
if (sector < 0
|| ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS))
>= part_info.size)) {
/* errnum = ERR_OUTSIDE_PART; */
printf (" ** reiserfs_devread() read outside partition\n");
return 0;
}
/*
* Get the read to the beginning of a partition.
*/
sector += byte_offset >> SECTOR_BITS;
byte_offset &= SECTOR_SIZE - 1;
#if defined(DEBUG)
printf (" <%d, %d, %d> ", sector, byte_offset, byte_len);
#endif
if (reiserfs_block_dev_desc == NULL)
return 0;
if (byte_offset != 0) {
/* read first part which isn't aligned with start of sector */
if (reiserfs_block_dev_desc->block_read(reiserfs_block_dev_desc->dev,
part_info.start+sector, 1, (unsigned long *)sec_buf) != 1) {
printf (" ** reiserfs_devread() read error\n");
return 0;
}
memcpy(buf, sec_buf+byte_offset, min(SECTOR_SIZE-byte_offset, byte_len));
buf+=min(SECTOR_SIZE-byte_offset, byte_len);
byte_len-=min(SECTOR_SIZE-byte_offset, byte_len);
sector++;
}
/* read sector aligned part */
block_len = byte_len & ~(SECTOR_SIZE-1);
if (reiserfs_block_dev_desc->block_read(reiserfs_block_dev_desc->dev,
part_info.start+sector, block_len/SECTOR_SIZE, (unsigned long *)buf) !=
block_len/SECTOR_SIZE) {
printf (" ** reiserfs_devread() read error - block\n");
return 0;
}
buf+=block_len;
byte_len-=block_len;
sector+= block_len/SECTOR_SIZE;
if ( byte_len != 0 ) {
/* read rest of data which are not in whole sector */
if (reiserfs_block_dev_desc->block_read(reiserfs_block_dev_desc->dev,
part_info.start+sector, 1, (unsigned long *)sec_buf) != 1) {
printf (" ** reiserfs_devread() read error - last part\n");
return 0;
}
memcpy(buf, sec_buf, byte_len);
}
return 1;
}

View file

@ -0,0 +1,138 @@
/*
* mode_string implementation for busybox
*
* Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* Aug 13, 2003
* Fix a bug reported by junkio@cox.net involving the mode_chars index.
*/
#include <common.h>
#include <linux/stat.h>
#if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \
|| ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \
|| ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \
|| ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 )
#error permission bitflag value assumption(s) violated!
#endif
#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \
|| ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \
|| ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \
|| ( S_IFIFO != 0010000 )
#warning mode type bitflag value assumption(s) violated! falling back to larger version
#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777
#undef mode_t
#define mode_t unsigned short
#endif
static const mode_t mode_flags[] = {
S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID,
S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID,
S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX
};
/* The static const char arrays below are duplicated for the two cases
* because moving them ahead of the mode_flags declaration cause a text
* size increase with the gcc version I'm using. */
/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
* and 'B' types don't appear to be available on linux. So I removed them. */
static const char type_chars[16] = "?pc?d?b?-?l?s???";
/* 0123456789abcdef */
static const char mode_chars[7] = "rwxSTst";
const char *bb_mode_string(int mode)
{
static char buf[12];
char *p = buf;
int i, j, k;
*p = type_chars[ (mode >> 12) & 0xf ];
i = 0;
do {
j = k = 0;
do {
*++p = '-';
if (mode & mode_flags[i+j]) {
*p = mode_chars[j];
k = j;
}
} while (++j < 3);
if (mode & mode_flags[i+j]) {
*p = mode_chars[3 + (k & 2) + ((i&8) >> 3)];
}
i += 4;
} while (i < 12);
/* Note: We don't bother with nul termination because bss initialization
* should have taken care of that for us. If the user scribbled in buf
* memory, they deserve whatever happens. But we'll at least assert. */
if (buf[10] != 0) return NULL;
return buf;
}
#else
/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
* and 'B' types don't appear to be available on linux. So I removed them. */
static const char type_chars[16] = "?pc?d?b?-?l?s???";
/* 0123456789abcdef */
static const char mode_chars[7] = "rwxSTst";
const char *bb_mode_string(int mode)
{
static char buf[12];
char *p = buf;
int i, j, k, m;
*p = type_chars[ (mode >> 12) & 0xf ];
i = 0;
m = 0400;
do {
j = k = 0;
do {
*++p = '-';
if (mode & m) {
*p = mode_chars[j];
k = j;
}
m >>= 1;
} while (++j < 3);
++i;
if (mode & (010000 >> i)) {
*p = mode_chars[3 + (k & 2) + (i == 3)];
}
} while (i < 3);
/* Note: We don't bother with nul termination because bss initialization
* should have taken care of that for us. If the user scribbled in buf
* memory, they deserve whatever happens. But we'll at least assert. */
if (buf[10] != 0) return NULL;
return buf;
}
#endif

View file

@ -0,0 +1,984 @@
/*
* Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
*
* (C) Copyright 2003 - 2004
* Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* An implementation for the ReiserFS filesystem ported from GRUB.
* Some parts of this code (mainly the structures and defines) are
* from the original reiser fs code, as found in the linux kernel.
*/
#include <common.h>
#include <malloc.h>
#include <linux/ctype.h>
#include <linux/time.h>
#include <asm/byteorder.h>
#include <reiserfs.h>
#include "reiserfs_private.h"
#undef REISERDEBUG
/* Some parts of this code (mainly the structures and defines) are
* from the original reiser fs code, as found in the linux kernel.
*/
static char fsys_buf[FSYS_BUFLEN];
static reiserfs_error_t errnum = ERR_NONE;
static int print_possibilities;
static unsigned int filepos, filemax;
static int
substring (const char *s1, const char *s2)
{
while (*s1 == *s2)
{
/* The strings match exactly. */
if (! *(s1++))
return 0;
s2 ++;
}
/* S1 is a substring of S2. */
if (*s1 == 0)
return -1;
/* S1 isn't a substring. */
return 1;
}
static void sd_print_item (struct item_head * ih, char * item)
{
char filetime[30];
time_t ttime;
if (stat_data_v1 (ih)) {
struct stat_data_v1 * sd = (struct stat_data_v1 *)item;
ttime = sd_v1_mtime(sd);
ctime_r(&ttime, filetime);
printf ("%-10s %4hd %6d %6d %9d %24.24s",
bb_mode_string(sd_v1_mode(sd)), sd_v1_nlink(sd),sd_v1_uid(sd), sd_v1_gid(sd),
sd_v1_size(sd), filetime);
} else {
struct stat_data * sd = (struct stat_data *)item;
ttime = sd_v2_mtime(sd);
ctime_r(&ttime, filetime);
printf ("%-10s %4d %6d %6d %9d %24.24s",
bb_mode_string(sd_v2_mode(sd)), sd_v2_nlink(sd),sd_v2_uid(sd),sd_v2_gid(sd),
(__u32) sd_v2_size(sd), filetime);
}
}
static int
journal_read (int block, int len, char *buffer)
{
return reiserfs_devread ((INFO->journal_block + block) << INFO->blocksize_shift,
0, len, buffer);
}
/* Read a block from ReiserFS file system, taking the journal into
* account. If the block nr is in the journal, the block from the
* journal taken.
*/
static int
block_read (unsigned int blockNr, int start, int len, char *buffer)
{
int transactions = INFO->journal_transactions;
int desc_block = INFO->journal_first_desc;
int journal_mask = INFO->journal_block_count - 1;
int translatedNr = blockNr;
__u32 *journal_table = JOURNAL_START;
while (transactions-- > 0)
{
int i = 0;
int j_len;
if (__le32_to_cpu(*journal_table) != 0xffffffff)
{
/* Search for the blockNr in cached journal */
j_len = __le32_to_cpu(*journal_table++);
while (i++ < j_len)
{
if (__le32_to_cpu(*journal_table++) == blockNr)
{
journal_table += j_len - i;
goto found;
}
}
}
else
{
/* This is the end of cached journal marker. The remaining
* transactions are still on disk.
*/
struct reiserfs_journal_desc desc;
struct reiserfs_journal_commit commit;
if (! journal_read (desc_block, sizeof (desc), (char *) &desc))
return 0;
j_len = __le32_to_cpu(desc.j_len);
while (i < j_len && i < JOURNAL_TRANS_HALF)
if (__le32_to_cpu(desc.j_realblock[i++]) == blockNr)
goto found;
if (j_len >= JOURNAL_TRANS_HALF)
{
int commit_block = (desc_block + 1 + j_len) & journal_mask;
if (! journal_read (commit_block,
sizeof (commit), (char *) &commit))
return 0;
while (i < j_len)
if (__le32_to_cpu(commit.j_realblock[i++ - JOURNAL_TRANS_HALF]) == blockNr)
goto found;
}
}
goto not_found;
found:
translatedNr = INFO->journal_block + ((desc_block + i) & journal_mask);
#ifdef REISERDEBUG
printf ("block_read: block %d is mapped to journal block %d.\n",
blockNr, translatedNr - INFO->journal_block);
#endif
/* We must continue the search, as this block may be overwritten
* in later transactions.
*/
not_found:
desc_block = (desc_block + 2 + j_len) & journal_mask;
}
return reiserfs_devread (translatedNr << INFO->blocksize_shift, start, len, buffer);
}
/* Init the journal data structure. We try to cache as much as
* possible in the JOURNAL_START-JOURNAL_END space, but if it is full
* we can still read the rest from the disk on demand.
*
* The first number of valid transactions and the descriptor block of the
* first valid transaction are held in INFO. The transactions are all
* adjacent, but we must take care of the journal wrap around.
*/
static int
journal_init (void)
{
unsigned int block_count = INFO->journal_block_count;
unsigned int desc_block;
unsigned int commit_block;
unsigned int next_trans_id;
struct reiserfs_journal_header header;
struct reiserfs_journal_desc desc;
struct reiserfs_journal_commit commit;
__u32 *journal_table = JOURNAL_START;
journal_read (block_count, sizeof (header), (char *) &header);
desc_block = __le32_to_cpu(header.j_first_unflushed_offset);
if (desc_block >= block_count)
return 0;
INFO->journal_first_desc = desc_block;
next_trans_id = __le32_to_cpu(header.j_last_flush_trans_id) + 1;
#ifdef REISERDEBUG
printf ("journal_init: last flushed %d\n",
__le32_to_cpu(header.j_last_flush_trans_id));
#endif
while (1)
{
journal_read (desc_block, sizeof (desc), (char *) &desc);
if (substring (JOURNAL_DESC_MAGIC, desc.j_magic) > 0
|| __le32_to_cpu(desc.j_trans_id) != next_trans_id
|| __le32_to_cpu(desc.j_mount_id) != __le32_to_cpu(header.j_mount_id))
/* no more valid transactions */
break;
commit_block = (desc_block + __le32_to_cpu(desc.j_len) + 1) & (block_count - 1);
journal_read (commit_block, sizeof (commit), (char *) &commit);
if (__le32_to_cpu(desc.j_trans_id) != commit.j_trans_id
|| __le32_to_cpu(desc.j_len) != __le32_to_cpu(commit.j_len))
/* no more valid transactions */
break;
#ifdef REISERDEBUG
printf ("Found valid transaction %d/%d at %d.\n",
__le32_to_cpu(desc.j_trans_id), __le32_to_cpu(desc.j_mount_id), desc_block);
#endif
next_trans_id++;
if (journal_table < JOURNAL_END)
{
if ((journal_table + 1 + __le32_to_cpu(desc.j_len)) >= JOURNAL_END)
{
/* The table is almost full; mark the end of the cached
* journal.*/
*journal_table = __cpu_to_le32(0xffffffff);
journal_table = JOURNAL_END;
}
else
{
unsigned int i;
/* Cache the length and the realblock numbers in the table.
* The block number of descriptor can easily be computed.
* and need not to be stored here.
*/
/* both are in the little endian format */
*journal_table++ = desc.j_len;
for (i = 0; i < __le32_to_cpu(desc.j_len) && i < JOURNAL_TRANS_HALF; i++)
{
/* both are in the little endian format */
*journal_table++ = desc.j_realblock[i];
#ifdef REISERDEBUG
printf ("block %d is in journal %d.\n",
__le32_to_cpu(desc.j_realblock[i]), desc_block);
#endif
}
for ( ; i < __le32_to_cpu(desc.j_len); i++)
{
/* both are in the little endian format */
*journal_table++ = commit.j_realblock[i-JOURNAL_TRANS_HALF];
#ifdef REISERDEBUG
printf ("block %d is in journal %d.\n",
__le32_to_cpu(commit.j_realblock[i-JOURNAL_TRANS_HALF]),
desc_block);
#endif
}
}
}
desc_block = (commit_block + 1) & (block_count - 1);
}
#ifdef REISERDEBUG
printf ("Transaction %d/%d at %d isn't valid.\n",
__le32_to_cpu(desc.j_trans_id), __le32_to_cpu(desc.j_mount_id), desc_block);
#endif
INFO->journal_transactions
= next_trans_id - __le32_to_cpu(header.j_last_flush_trans_id) - 1;
return errnum == 0;
}
/* check filesystem types and read superblock into memory buffer */
int
reiserfs_mount (unsigned part_length)
{
struct reiserfs_super_block super;
int superblock = REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS;
char *cache;
if (part_length < superblock + (sizeof (super) >> SECTOR_BITS)
|| ! reiserfs_devread (superblock, 0, sizeof (struct reiserfs_super_block),
(char *) &super)
|| (substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) > 0
&& substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0
&& substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0)
|| (/* check that this is not a copy inside the journal log */
sb_journal_block(&super) * sb_blocksize(&super)
<= REISERFS_DISK_OFFSET_IN_BYTES))
{
/* Try old super block position */
superblock = REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS;
if (part_length < superblock + (sizeof (super) >> SECTOR_BITS)
|| ! reiserfs_devread (superblock, 0, sizeof (struct reiserfs_super_block),
(char *) &super))
return 0;
if (substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0
&& substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0)
{
/* pre journaling super block ? */
if (substring (REISERFS_SUPER_MAGIC_STRING,
(char*) ((int) &super + 20)) > 0)
return 0;
set_sb_blocksize(&super, REISERFS_OLD_BLOCKSIZE);
set_sb_journal_block(&super, 0);
set_sb_version(&super, 0);
}
}
/* check the version number. */
if (sb_version(&super) > REISERFS_MAX_SUPPORTED_VERSION)
return 0;
INFO->version = sb_version(&super);
INFO->blocksize = sb_blocksize(&super);
INFO->fullblocksize_shift = log2 (sb_blocksize(&super));
INFO->blocksize_shift = INFO->fullblocksize_shift - SECTOR_BITS;
INFO->cached_slots =
(FSYSREISER_CACHE_SIZE >> INFO->fullblocksize_shift) - 1;
#ifdef REISERDEBUG
printf ("reiserfs_mount: version=%d, blocksize=%d\n",
INFO->version, INFO->blocksize);
#endif /* REISERDEBUG */
/* Clear node cache. */
memset (INFO->blocks, 0, sizeof (INFO->blocks));
if (sb_blocksize(&super) < FSYSREISER_MIN_BLOCKSIZE
|| sb_blocksize(&super) > FSYSREISER_MAX_BLOCKSIZE
|| (SECTOR_SIZE << INFO->blocksize_shift) != sb_blocksize(&super))
return 0;
/* Initialize journal code. If something fails we end with zero
* journal_transactions, so we don't access the journal at all.
*/
INFO->journal_transactions = 0;
if (sb_journal_block(&super) != 0 && super.s_journal_dev == 0)
{
INFO->journal_block = sb_journal_block(&super);
INFO->journal_block_count = sb_journal_size(&super);
if (is_power_of_two (INFO->journal_block_count))
journal_init ();
/* Read in super block again, maybe it is in the journal */
block_read (superblock >> INFO->blocksize_shift,
0, sizeof (struct reiserfs_super_block), (char *) &super);
}
if (! block_read (sb_root_block(&super), 0, INFO->blocksize, (char*) ROOT))
return 0;
cache = ROOT;
INFO->tree_depth = __le16_to_cpu(BLOCKHEAD (cache)->blk_level);
#ifdef REISERDEBUG
printf ("root read_in: block=%d, depth=%d\n",
sb_root_block(&super), INFO->tree_depth);
#endif /* REISERDEBUG */
if (INFO->tree_depth >= MAX_HEIGHT)
return 0;
if (INFO->tree_depth == DISK_LEAF_NODE_LEVEL)
{
/* There is only one node in the whole filesystem,
* which is simultanously leaf and root */
memcpy (LEAF, ROOT, INFO->blocksize);
}
return 1;
}
/***************** TREE ACCESSING METHODS *****************************/
/* I assume you are familiar with the ReiserFS tree, if not go to
* http://www.namesys.com/content_table.html
*
* My tree node cache is organized as following
* 0 ROOT node
* 1 LEAF node (if the ROOT is also a LEAF it is copied here
* 2-n other nodes on current path from bottom to top.
* if there is not enough space in the cache, the top most are
* omitted.
*
* I have only two methods to find a key in the tree:
* search_stat(dir_id, objectid) searches for the stat entry (always
* the first entry) of an object.
* next_key() gets the next key in tree order.
*
* This means, that I can only sequential reads of files are
* efficient, but this really doesn't hurt for grub.
*/
/* Read in the node at the current path and depth into the node cache.
* You must set INFO->blocks[depth] before.
*/
static char *
read_tree_node (unsigned int blockNr, int depth)
{
char* cache = CACHE(depth);
int num_cached = INFO->cached_slots;
if (depth < num_cached)
{
/* This is the cached part of the path. Check if same block is
* needed.
*/
if (blockNr == INFO->blocks[depth])
return cache;
}
else
cache = CACHE(num_cached);
#ifdef REISERDEBUG
printf (" next read_in: block=%d (depth=%d)\n",
blockNr, depth);
#endif /* REISERDEBUG */
if (! block_read (blockNr, 0, INFO->blocksize, cache))
return 0;
/* Make sure it has the right node level */
if (__le16_to_cpu(BLOCKHEAD (cache)->blk_level) != depth)
{
errnum = ERR_FSYS_CORRUPT;
return 0;
}
INFO->blocks[depth] = blockNr;
return cache;
}
/* Get the next key, i.e. the key following the last retrieved key in
* tree order. INFO->current_ih and
* INFO->current_info are adapted accordingly. */
static int
next_key (void)
{
int depth;
struct item_head *ih = INFO->current_ih + 1;
char *cache;
#ifdef REISERDEBUG
printf ("next_key:\n old ih: key %d:%d:%d:%d version:%d\n",
__le32_to_cpu(INFO->current_ih->ih_key.k_dir_id),
__le32_to_cpu(INFO->current_ih->ih_key.k_objectid),
__le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_offset),
__le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_uniqueness),
__le16_to_cpu(INFO->current_ih->ih_version));
#endif /* REISERDEBUG */
if (ih == &ITEMHEAD[__le16_to_cpu(BLOCKHEAD (LEAF)->blk_nr_item)])
{
depth = DISK_LEAF_NODE_LEVEL;
/* The last item, was the last in the leaf node.
* Read in the next block
*/
do
{
if (depth == INFO->tree_depth)
{
/* There are no more keys at all.
* Return a dummy item with MAX_KEY */
ih = (struct item_head *) &BLOCKHEAD (LEAF)->blk_right_delim_key;
goto found;
}
depth++;
#ifdef REISERDEBUG
printf (" depth=%d, i=%d\n", depth, INFO->next_key_nr[depth]);
#endif /* REISERDEBUG */
}
while (INFO->next_key_nr[depth] == 0);
if (depth == INFO->tree_depth)
cache = ROOT;
else if (depth <= INFO->cached_slots)
cache = CACHE (depth);
else
{
cache = read_tree_node (INFO->blocks[depth], depth);
if (! cache)
return 0;
}
do
{
int nr_item = __le16_to_cpu(BLOCKHEAD (cache)->blk_nr_item);
int key_nr = INFO->next_key_nr[depth]++;
#ifdef REISERDEBUG
printf (" depth=%d, i=%d/%d\n", depth, key_nr, nr_item);
#endif /* REISERDEBUG */
if (key_nr == nr_item)
/* This is the last item in this block, set the next_key_nr to 0 */
INFO->next_key_nr[depth] = 0;
cache = read_tree_node (dc_block_number(&(DC (cache)[key_nr])), --depth);
if (! cache)
return 0;
}
while (depth > DISK_LEAF_NODE_LEVEL);
ih = ITEMHEAD;
}
found:
INFO->current_ih = ih;
INFO->current_item = &LEAF[__le16_to_cpu(ih->ih_item_location)];
#ifdef REISERDEBUG
printf (" new ih: key %d:%d:%d:%d version:%d\n",
__le32_to_cpu(INFO->current_ih->ih_key.k_dir_id),
__le32_to_cpu(INFO->current_ih->ih_key.k_objectid),
__le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_offset),
__le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_uniqueness),
__le16_to_cpu(INFO->current_ih->ih_version));
#endif /* REISERDEBUG */
return 1;
}
/* preconditions: reiserfs_mount already executed, therefore
* INFO block is valid
* returns: 0 if error (errnum is set),
* nonzero iff we were able to find the key successfully.
* postconditions: on a nonzero return, the current_ih and
* current_item fields describe the key that equals the
* searched key. INFO->next_key contains the next key after
* the searched key.
* side effects: messes around with the cache.
*/
static int
search_stat (__u32 dir_id, __u32 objectid)
{
char *cache;
int depth;
int nr_item;
int i;
struct item_head *ih;
#ifdef REISERDEBUG
printf ("search_stat:\n key %d:%d:0:0\n", dir_id, objectid);
#endif /* REISERDEBUG */
depth = INFO->tree_depth;
cache = ROOT;
while (depth > DISK_LEAF_NODE_LEVEL)
{
struct key *key;
nr_item = __le16_to_cpu(BLOCKHEAD (cache)->blk_nr_item);
key = KEY (cache);
for (i = 0; i < nr_item; i++)
{
if (__le32_to_cpu(key->k_dir_id) > dir_id
|| (__le32_to_cpu(key->k_dir_id) == dir_id
&& (__le32_to_cpu(key->k_objectid) > objectid
|| (__le32_to_cpu(key->k_objectid) == objectid
&& (__le32_to_cpu(key->u.v1.k_offset)
| __le32_to_cpu(key->u.v1.k_uniqueness)) > 0))))
break;
key++;
}
#ifdef REISERDEBUG
printf (" depth=%d, i=%d/%d\n", depth, i, nr_item);
#endif /* REISERDEBUG */
INFO->next_key_nr[depth] = (i == nr_item) ? 0 : i+1;
cache = read_tree_node (dc_block_number(&(DC (cache)[i])), --depth);
if (! cache)
return 0;
}
/* cache == LEAF */
nr_item = __le16_to_cpu(BLOCKHEAD (LEAF)->blk_nr_item);
ih = ITEMHEAD;
for (i = 0; i < nr_item; i++)
{
if (__le32_to_cpu(ih->ih_key.k_dir_id) == dir_id
&& __le32_to_cpu(ih->ih_key.k_objectid) == objectid
&& __le32_to_cpu(ih->ih_key.u.v1.k_offset) == 0
&& __le32_to_cpu(ih->ih_key.u.v1.k_uniqueness) == 0)
{
#ifdef REISERDEBUG
printf (" depth=%d, i=%d/%d\n", depth, i, nr_item);
#endif /* REISERDEBUG */
INFO->current_ih = ih;
INFO->current_item = &LEAF[__le16_to_cpu(ih->ih_item_location)];
return 1;
}
ih++;
}
errnum = ERR_FSYS_CORRUPT;
return 0;
}
int
reiserfs_read (char *buf, unsigned len)
{
unsigned int blocksize;
unsigned int offset;
unsigned int to_read;
char *prev_buf = buf;
#ifdef REISERDEBUG
printf ("reiserfs_read: filepos=%d len=%d, offset=%Lx\n",
filepos, len, (__u64) IH_KEY_OFFSET (INFO->current_ih) - 1);
#endif /* REISERDEBUG */
if (__le32_to_cpu(INFO->current_ih->ih_key.k_objectid) != INFO->fileinfo.k_objectid
|| IH_KEY_OFFSET (INFO->current_ih) > filepos + 1)
{
search_stat (INFO->fileinfo.k_dir_id, INFO->fileinfo.k_objectid);
goto get_next_key;
}
while (! errnum)
{
if (__le32_to_cpu(INFO->current_ih->ih_key.k_objectid) != INFO->fileinfo.k_objectid) {
break;
}
offset = filepos - IH_KEY_OFFSET (INFO->current_ih) + 1;
blocksize = __le16_to_cpu(INFO->current_ih->ih_item_len);
#ifdef REISERDEBUG
printf (" loop: filepos=%d len=%d, offset=%d blocksize=%d\n",
filepos, len, offset, blocksize);
#endif /* REISERDEBUG */
if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_DIRECT)
&& offset < blocksize)
{
#ifdef REISERDEBUG
printf ("direct_read: offset=%d, blocksize=%d\n",
offset, blocksize);
#endif /* REISERDEBUG */
to_read = blocksize - offset;
if (to_read > len)
to_read = len;
memcpy (buf, INFO->current_item + offset, to_read);
goto update_buf_len;
}
else if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_INDIRECT))
{
blocksize = (blocksize >> 2) << INFO->fullblocksize_shift;
#ifdef REISERDEBUG
printf ("indirect_read: offset=%d, blocksize=%d\n",
offset, blocksize);
#endif /* REISERDEBUG */
while (offset < blocksize)
{
__u32 blocknr = __le32_to_cpu(((__u32 *) INFO->current_item)
[offset >> INFO->fullblocksize_shift]);
int blk_offset = offset & (INFO->blocksize-1);
to_read = INFO->blocksize - blk_offset;
if (to_read > len)
to_read = len;
/* Journal is only for meta data. Data blocks can be read
* directly without using block_read
*/
reiserfs_devread (blocknr << INFO->blocksize_shift,
blk_offset, to_read, buf);
update_buf_len:
len -= to_read;
buf += to_read;
offset += to_read;
filepos += to_read;
if (len == 0)
goto done;
}
}
get_next_key:
next_key ();
}
done:
return errnum ? 0 : buf - prev_buf;
}
/* preconditions: reiserfs_mount already executed, therefore
* INFO block is valid
* returns: 0 if error, nonzero iff we were able to find the file successfully
* postconditions: on a nonzero return, INFO->fileinfo contains the info
* of the file we were trying to look up, filepos is 0 and filemax is
* the size of the file.
*/
static int
reiserfs_dir (char *dirname)
{
struct reiserfs_de_head *de_head;
char *rest, ch;
__u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0;
#ifndef STAGE1_5
int do_possibilities = 0;
#endif /* ! STAGE1_5 */
char linkbuf[PATH_MAX]; /* buffer for following symbolic links */
int link_count = 0;
int mode;
dir_id = REISERFS_ROOT_PARENT_OBJECTID;
objectid = REISERFS_ROOT_OBJECTID;
while (1)
{
#ifdef REISERDEBUG
printf ("dirname=%s\n", dirname);
#endif /* REISERDEBUG */
/* Search for the stat info first. */
if (! search_stat (dir_id, objectid))
return 0;
#ifdef REISERDEBUG
printf ("sd_mode=%x sd_size=%d\n",
stat_data_v1(INFO->current_ih) ? sd_v1_mode((struct stat_data_v1 *) INFO->current_item) :
sd_v2_mode((struct stat_data *) (INFO->current_item)),
stat_data_v1(INFO->current_ih) ? sd_v1_size((struct stat_data_v1 *) INFO->current_item) :
sd_v2_size((struct stat_data *) INFO->current_item)
);
#endif /* REISERDEBUG */
mode = stat_data_v1(INFO->current_ih) ?
sd_v1_mode((struct stat_data_v1 *) INFO->current_item) :
sd_v2_mode((struct stat_data *) INFO->current_item);
/* If we've got a symbolic link, then chase it. */
if (S_ISLNK (mode))
{
unsigned int len;
if (++link_count > MAX_LINK_COUNT)
{
errnum = ERR_SYMLINK_LOOP;
return 0;
}
/* Get the symlink size. */
filemax = stat_data_v1(INFO->current_ih) ?
sd_v1_size((struct stat_data_v1 *) INFO->current_item) :
sd_v2_size((struct stat_data *) INFO->current_item);
/* Find out how long our remaining name is. */
len = 0;
while (dirname[len] && !isspace (dirname[len]))
len++;
if (filemax + len > sizeof (linkbuf) - 1)
{
errnum = ERR_FILELENGTH;
return 0;
}
/* Copy the remaining name to the end of the symlink data.
Note that DIRNAME and LINKBUF may overlap! */
memmove (linkbuf + filemax, dirname, len+1);
INFO->fileinfo.k_dir_id = dir_id;
INFO->fileinfo.k_objectid = objectid;
filepos = 0;
if (! next_key ()
|| reiserfs_read (linkbuf, filemax) != filemax)
{
if (! errnum)
errnum = ERR_FSYS_CORRUPT;
return 0;
}
#ifdef REISERDEBUG
printf ("symlink=%s\n", linkbuf);
#endif /* REISERDEBUG */
dirname = linkbuf;
if (*dirname == '/')
{
/* It's an absolute link, so look it up in root. */
dir_id = REISERFS_ROOT_PARENT_OBJECTID;
objectid = REISERFS_ROOT_OBJECTID;
}
else
{
/* Relative, so look it up in our parent directory. */
dir_id = parent_dir_id;
objectid = parent_objectid;
}
/* Now lookup the new name. */
continue;
}
/* if we have a real file (and we're not just printing possibilities),
then this is where we want to exit */
if (! *dirname || isspace (*dirname))
{
if (! S_ISREG (mode))
{
errnum = ERR_BAD_FILETYPE;
return 0;
}
filepos = 0;
filemax = stat_data_v1(INFO->current_ih) ?
sd_v1_size((struct stat_data_v1 *) INFO->current_item) :
sd_v2_size((struct stat_data *) INFO->current_item);
#if 0
/* If this is a new stat data and size is > 4GB set filemax to
* maximum
*/
if (__le16_to_cpu(INFO->current_ih->ih_version) == ITEM_VERSION_2
&& sd_size_hi((struct stat_data *) INFO->current_item) > 0)
filemax = 0xffffffff;
#endif
INFO->fileinfo.k_dir_id = dir_id;
INFO->fileinfo.k_objectid = objectid;
return next_key ();
}
/* continue with the file/directory name interpretation */
while (*dirname == '/')
dirname++;
if (! S_ISDIR (mode))
{
errnum = ERR_BAD_FILETYPE;
return 0;
}
for (rest = dirname; (ch = *rest) && ! isspace (ch) && ch != '/'; rest++);
*rest = 0;
# ifndef STAGE1_5
if (print_possibilities && ch != '/')
do_possibilities = 1;
# endif /* ! STAGE1_5 */
while (1)
{
char *name_end;
int num_entries;
if (! next_key ())
return 0;
#ifdef REISERDEBUG
printf ("ih: key %d:%d:%d:%d version:%d\n",
__le32_to_cpu(INFO->current_ih->ih_key.k_dir_id),
__le32_to_cpu(INFO->current_ih->ih_key.k_objectid),
__le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_offset),
__le32_to_cpu(INFO->current_ih->ih_key.u.v1.k_uniqueness),
__le16_to_cpu(INFO->current_ih->ih_version));
#endif /* REISERDEBUG */
if (__le32_to_cpu(INFO->current_ih->ih_key.k_objectid) != objectid)
break;
name_end = INFO->current_item + __le16_to_cpu(INFO->current_ih->ih_item_len);
de_head = (struct reiserfs_de_head *) INFO->current_item;
num_entries = __le16_to_cpu(INFO->current_ih->u.ih_entry_count);
while (num_entries > 0)
{
char *filename = INFO->current_item + deh_location(de_head);
char tmp = *name_end;
if ((deh_state(de_head) & DEH_Visible))
{
int cmp;
/* Directory names in ReiserFS are not null
* terminated. We write a temporary 0 behind it.
* NOTE: that this may overwrite the first block in
* the tree cache. That doesn't hurt as long as we
* don't call next_key () in between.
*/
*name_end = 0;
cmp = substring (dirname, filename);
*name_end = tmp;
# ifndef STAGE1_5
if (do_possibilities)
{
if (cmp <= 0)
{
char fn[PATH_MAX];
struct fsys_reiser_info info_save;
if (print_possibilities > 0)
print_possibilities = -print_possibilities;
*name_end = 0;
strcpy(fn, filename);
*name_end = tmp;
/* If NAME is "." or "..", do not count it. */
if (strcmp (fn, ".") != 0 && strcmp (fn, "..") != 0) {
memcpy(&info_save, INFO, sizeof(struct fsys_reiser_info));
search_stat (deh_dir_id(de_head), deh_objectid(de_head));
sd_print_item(INFO->current_ih, INFO->current_item);
printf(" %s\n", fn);
search_stat (dir_id, objectid);
memcpy(INFO, &info_save, sizeof(struct fsys_reiser_info));
}
}
}
else
# endif /* ! STAGE1_5 */
if (cmp == 0)
goto found;
}
/* The beginning of this name marks the end of the next name.
*/
name_end = filename;
de_head++;
num_entries--;
}
}
# ifndef STAGE1_5
if (print_possibilities < 0)
return 1;
# endif /* ! STAGE1_5 */
errnum = ERR_FILE_NOT_FOUND;
*rest = ch;
return 0;
found:
*rest = ch;
dirname = rest;
parent_dir_id = dir_id;
parent_objectid = objectid;
dir_id = deh_dir_id(de_head);
objectid = deh_objectid(de_head);
}
}
/*
* U-Boot interface functions
*/
/*
* List given directory
*
* RETURN: 0 - OK, else grub_error_t errnum
*/
int
reiserfs_ls (char *dirname)
{
char *dir_slash;
int res;
errnum = 0;
dir_slash = malloc(strlen(dirname) + 1);
if (dir_slash == NULL) {
return ERR_NUMBER_OVERFLOW;
}
strcpy(dir_slash, dirname);
/* add "/" to the directory name */
strcat(dir_slash, "/");
print_possibilities = 1;
res = reiserfs_dir (dir_slash);
free(dir_slash);
if (!res || errnum) {
return errnum;
}
return 0;
}
/*
* Open file for reading
*
* RETURN: >0 - OK, size of opened file
* <0 - ERROR -grub_error_t errnum
*/
int
reiserfs_open (char *filename)
{
/* open the file */
errnum = 0;
print_possibilities = 0;
if (!reiserfs_dir (filename) || errnum) {
return -errnum;
}
return filemax;
}

View file

@ -0,0 +1,520 @@
/*
* Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
*
* (C) Copyright 2003 - 2004
* Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* An implementation for the ReiserFS filesystem ported from GRUB.
* Some parts of this code (mainly the structures and defines) are
* from the original reiser fs code, as found in the linux kernel.
*/
#ifndef __BYTE_ORDER
#if defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
#define __BYTE_ORDER __LITTLE_ENDIAN
#elif defined(__BIG_ENDIAN) && !defined(__LITTLE_ENDIAN)
#define __BYTE_ORDER __BIG_ENDIAN
#else
#error "unable to define __BYTE_ORDER"
#endif
#endif /* not __BYTE_ORDER */
#define FSYS_BUFLEN 0x8000
#define FSYS_BUF fsys_buf
/* This is the new super block of a journaling reiserfs system */
struct reiserfs_super_block
{
__u32 s_block_count; /* blocks count */
__u32 s_free_blocks; /* free blocks count */
__u32 s_root_block; /* root block number */
__u32 s_journal_block; /* journal block number */
__u32 s_journal_dev; /* journal device number */
__u32 s_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */
__u32 s_journal_trans_max; /* max number of blocks in a transaction. */
__u32 s_journal_magic; /* random value made on fs creation */
__u32 s_journal_max_batch; /* max number of blocks to batch into a trans */
__u32 s_journal_max_commit_age; /* in seconds, how old can an async commit be */
__u32 s_journal_max_trans_age; /* in seconds, how old can a transaction be */
__u16 s_blocksize; /* block size */
__u16 s_oid_maxsize; /* max size of object id array */
__u16 s_oid_cursize; /* current size of object id array */
__u16 s_state; /* valid or error */
char s_magic[16]; /* reiserfs magic string indicates that file system is reiserfs */
__u16 s_tree_height; /* height of disk tree */
__u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */
__u16 s_version;
char s_unused[128]; /* zero filled by mkreiserfs */
};
#define sb_root_block(sbp) (__le32_to_cpu((sbp)->s_root_block))
#define sb_journal_block(sbp) (__le32_to_cpu((sbp)->s_journal_block))
#define set_sb_journal_block(sbp,v) ((sbp)->s_journal_block = __cpu_to_le32(v))
#define sb_journal_size(sbp) (__le32_to_cpu((sbp)->s_journal_size))
#define sb_blocksize(sbp) (__le16_to_cpu((sbp)->s_blocksize))
#define set_sb_blocksize(sbp,v) ((sbp)->s_blocksize = __cpu_to_le16(v))
#define sb_version(sbp) (__le16_to_cpu((sbp)->s_version))
#define set_sb_version(sbp,v) ((sbp)->s_version = __cpu_to_le16(v))
#define REISERFS_MAX_SUPPORTED_VERSION 2
#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISER3FS_SUPER_MAGIC_STRING "ReIsEr3Fs"
#define MAX_HEIGHT 7
/* must be correct to keep the desc and commit structs at 4k */
#define JOURNAL_TRANS_HALF 1018
/* first block written in a commit. */
struct reiserfs_journal_desc {
__u32 j_trans_id; /* id of commit */
__u32 j_len; /* length of commit. len +1 is the commit block */
__u32 j_mount_id; /* mount id of this trans*/
__u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the first blocks */
char j_magic[12];
};
/* last block written in a commit */
struct reiserfs_journal_commit {
__u32 j_trans_id; /* must match j_trans_id from the desc block */
__u32 j_len; /* ditto */
__u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the last blocks */
char j_digest[16]; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */
};
/* this header block gets written whenever a transaction is considered
fully flushed, and is more recent than the last fully flushed
transaction.
fully flushed means all the log blocks and all the real blocks are
on disk, and this transaction does not need to be replayed.
*/
struct reiserfs_journal_header {
/* id of last fully flushed transaction */
__u32 j_last_flush_trans_id;
/* offset in the log of where to start replay after a crash */
__u32 j_first_unflushed_offset;
/* mount id to detect very old transactions */
__u32 j_mount_id;
};
/* magic string to find desc blocks in the journal */
#define JOURNAL_DESC_MAGIC "ReIsErLB"
/*
* directories use this key as well as old files
*/
struct offset_v1
{
/*
* for regular files this is the offset to the first byte of the
* body, contained in the object-item, as measured from the start of
* the entire body of the object.
*
* for directory entries, k_offset consists of hash derived from
* hashing the name and using few bits (23 or more) of the resulting
* hash, and generation number that allows distinguishing names with
* hash collisions. If number of collisions overflows generation
* number, we return EEXIST. High order bit is 0 always
*/
__u32 k_offset;
__u32 k_uniqueness;
};
struct offset_v2 {
/*
* for regular files this is the offset to the first byte of the
* body, contained in the object-item, as measured from the start of
* the entire body of the object.
*
* for directory entries, k_offset consists of hash derived from
* hashing the name and using few bits (23 or more) of the resulting
* hash, and generation number that allows distinguishing names with
* hash collisions. If number of collisions overflows generation
* number, we return EEXIST. High order bit is 0 always
*/
#if defined(__LITTLE_ENDIAN_BITFIELD)
/* little endian version */
__u64 k_offset:60;
__u64 k_type: 4;
#elif defined(__BIG_ENDIAN_BITFIELD)
/* big endian version */
__u64 k_type: 4;
__u64 k_offset:60;
#else
#error "__LITTLE_ENDIAN_BITFIELD or __BIG_ENDIAN_BITFIELD must be defined"
#endif
} __attribute__ ((__packed__));
#define TYPE_MAXTYPE 3
#define TYPE_ANY 15
#if (__BYTE_ORDER == __BIG_ENDIAN)
typedef union {
struct offset_v2 offset_v2;
__u64 linear;
} __attribute__ ((__packed__)) offset_v2_esafe_overlay;
static inline __u16 offset_v2_k_type( const struct offset_v2 *v2 )
{
offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
tmp.linear = __le64_to_cpu( tmp.linear );
return (tmp.offset_v2.k_type <= TYPE_MAXTYPE)?tmp.offset_v2.k_type:TYPE_ANY;
}
static inline loff_t offset_v2_k_offset( const struct offset_v2 *v2 )
{
offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
tmp.linear = __le64_to_cpu( tmp.linear );
return tmp.offset_v2.k_offset;
}
#elif (__BYTE_ORDER == __LITTLE_ENDIAN)
# define offset_v2_k_type(v2) ((v2)->k_type)
# define offset_v2_k_offset(v2) ((v2)->k_offset)
#else
#error "__BYTE_ORDER must be __LITTLE_ENDIAN or __BIG_ENDIAN"
#endif
struct key
{
/* packing locality: by default parent directory object id */
__u32 k_dir_id;
/* object identifier */
__u32 k_objectid;
/* the offset and node type (old and new form) */
union
{
struct offset_v1 v1;
struct offset_v2 v2;
}
u;
};
#define KEY_SIZE (sizeof (struct key))
/* Header of a disk block. More precisely, header of a formatted leaf
or internal node, and not the header of an unformatted node. */
struct block_head
{
__u16 blk_level; /* Level of a block in the tree. */
__u16 blk_nr_item; /* Number of keys/items in a block. */
__u16 blk_free_space; /* Block free space in bytes. */
struct key blk_right_delim_key; /* Right delimiting key for this block (supported for leaf level nodes
only) */
};
#define BLKH_SIZE (sizeof (struct block_head))
#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */
struct item_head
{
/* Everything in the tree is found by searching for it based on
* its key.*/
struct key ih_key;
union {
/* The free space in the last unformatted node of an
indirect item if this is an indirect item. This
equals 0xFFFF iff this is a direct item or stat data
item. Note that the key, not this field, is used to
determine the item type, and thus which field this
union contains. */
__u16 ih_free_space;
/* Iff this is a directory item, this field equals the
number of directory entries in the directory item. */
__u16 ih_entry_count;
} __attribute__ ((__packed__)) u;
__u16 ih_item_len; /* total size of the item body */
__u16 ih_item_location; /* an offset to the item body
* within the block */
__u16 ih_version; /* 0 for all old items, 2 for new
ones. Highest bit is set by fsck
temporary, cleaned after all
done */
} __attribute__ ((__packed__));
/* size of item header */
#define IH_SIZE (sizeof (struct item_head))
#define ITEM_VERSION_1 0
#define ITEM_VERSION_2 1
#define ih_version(ih) (__le16_to_cpu((ih)->ih_version))
#define IH_KEY_OFFSET(ih) (ih_version(ih) == ITEM_VERSION_1 \
? __le32_to_cpu((ih)->ih_key.u.v1.k_offset) \
: offset_v2_k_offset(&((ih)->ih_key.u.v2)))
#define IH_KEY_ISTYPE(ih, type) (ih_version(ih) == ITEM_VERSION_1 \
? __le32_to_cpu((ih)->ih_key.u.v1.k_uniqueness) == V1_##type \
: offset_v2_k_type(&((ih)->ih_key.u.v2)) == V2_##type)
/***************************************************************************/
/* DISK CHILD */
/***************************************************************************/
/* Disk child pointer: The pointer from an internal node of the tree
to a node that is on disk. */
struct disk_child {
__u32 dc_block_number; /* Disk child's block number. */
__u16 dc_size; /* Disk child's used space. */
__u16 dc_reserved;
};
#define DC_SIZE (sizeof(struct disk_child))
#define dc_block_number(dc_p) (__le32_to_cpu((dc_p)->dc_block_number))
/*
* old stat data is 32 bytes long. We are going to distinguish new one by
* different size
*/
struct stat_data_v1
{
__u16 sd_mode; /* file type, permissions */
__u16 sd_nlink; /* number of hard links */
__u16 sd_uid; /* owner */
__u16 sd_gid; /* group */
__u32 sd_size; /* file size */
__u32 sd_atime; /* time of last access */
__u32 sd_mtime; /* time file was last modified */
__u32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */
union {
__u32 sd_rdev;
__u32 sd_blocks; /* number of blocks file uses */
} __attribute__ ((__packed__)) u;
__u32 sd_first_direct_byte; /* first byte of file which is stored
in a direct item: except that if it
equals 1 it is a symlink and if it
equals ~(__u32)0 there is no
direct item. The existence of this
field really grates on me. Let's
replace it with a macro based on
sd_size and our tail suppression
policy. Someday. -Hans */
} __attribute__ ((__packed__));
#define stat_data_v1(ih) (ih_version(ih) == ITEM_VERSION_1)
#define sd_v1_mode(sdp) ((sdp)->sd_mode)
#define sd_v1_nlink(sdp) (__le16_to_cpu((sdp)->sd_nlink))
#define sd_v1_uid(sdp) (__le16_to_cpu((sdp)->sd_uid))
#define sd_v1_gid(sdp) (__le16_to_cpu((sdp)->sd_gid))
#define sd_v1_size(sdp) (__le32_to_cpu((sdp)->sd_size))
#define sd_v1_mtime(sdp) (__le32_to_cpu((sdp)->sd_mtime))
/* Stat Data on disk (reiserfs version of UFS disk inode minus the
address blocks) */
struct stat_data {
__u16 sd_mode; /* file type, permissions */
__u16 sd_attrs; /* persistent inode flags */
__u32 sd_nlink; /* number of hard links */
__u64 sd_size; /* file size */
__u32 sd_uid; /* owner */
__u32 sd_gid; /* group */
__u32 sd_atime; /* time of last access */
__u32 sd_mtime; /* time file was last modified */
__u32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */
__u32 sd_blocks;
union {
__u32 sd_rdev;
__u32 sd_generation;
/*__u32 sd_first_direct_byte; */
/* first byte of file which is stored in a
direct item: except that if it equals 1
it is a symlink and if it equals
~(__u32)0 there is no direct item. The
existence of this field really grates
on me. Let's replace it with a macro
based on sd_size and our tail
suppression policy? */
} __attribute__ ((__packed__)) u;
} __attribute__ ((__packed__));
#define stat_data_v2(ih) (ih_version(ih) == ITEM_VERSION_2)
#define sd_v2_mode(sdp) (__le16_to_cpu((sdp)->sd_mode))
#define sd_v2_nlink(sdp) (__le32_to_cpu((sdp)->sd_nlink))
#define sd_v2_size(sdp) (__le64_to_cpu((sdp)->sd_size))
#define sd_v2_uid(sdp) (__le32_to_cpu((sdp)->sd_uid))
#define sd_v2_gid(sdp) (__le32_to_cpu((sdp)->sd_gid))
#define sd_v2_mtime(sdp) (__le32_to_cpu((sdp)->sd_mtime))
#define sd_mode(sdp) (__le16_to_cpu((sdp)->sd_mode))
#define sd_size(sdp) (__le32_to_cpu((sdp)->sd_size))
#define sd_size_hi(sdp) (__le32_to_cpu((sdp)->sd_size_hi))
struct reiserfs_de_head
{
__u32 deh_offset; /* third component of the directory entry key */
__u32 deh_dir_id; /* objectid of the parent directory of the
object, that is referenced by directory entry */
__u32 deh_objectid;/* objectid of the object, that is referenced by
directory entry */
__u16 deh_location;/* offset of name in the whole item */
__u16 deh_state; /* whether 1) entry contains stat data (for
future), and 2) whether entry is hidden
(unlinked) */
};
#define DEH_SIZE (sizeof (struct reiserfs_de_head))
#define deh_offset(p_deh) (__le32_to_cpu((p_deh)->deh_offset))
#define deh_dir_id(p_deh) (__le32_to_cpu((p_deh)->deh_dir_id))
#define deh_objectid(p_deh) (__le32_to_cpu((p_deh)->deh_objectid))
#define deh_location(p_deh) (__le16_to_cpu((p_deh)->deh_location))
#define deh_state(p_deh) (__le16_to_cpu((p_deh)->deh_state))
#define DEH_Statdata (1 << 0) /* not used now */
#define DEH_Visible (1 << 2)
#define SD_OFFSET 0
#define SD_UNIQUENESS 0
#define DOT_OFFSET 1
#define DOT_DOT_OFFSET 2
#define DIRENTRY_UNIQUENESS 500
#define V1_TYPE_STAT_DATA 0x0
#define V1_TYPE_DIRECT 0xffffffff
#define V1_TYPE_INDIRECT 0xfffffffe
#define V1_TYPE_DIRECTORY_MAX 0xfffffffd
#define V2_TYPE_STAT_DATA 0
#define V2_TYPE_INDIRECT 1
#define V2_TYPE_DIRECT 2
#define V2_TYPE_DIRENTRY 3
#define REISERFS_ROOT_OBJECTID 2
#define REISERFS_ROOT_PARENT_OBJECTID 1
#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024)
/* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */
#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024)
#define REISERFS_OLD_BLOCKSIZE 4096
#define S_ISREG(mode) (((mode) & 0170000) == 0100000)
#define S_ISDIR(mode) (((mode) & 0170000) == 0040000)
#define S_ISLNK(mode) (((mode) & 0170000) == 0120000)
#define PATH_MAX 1024 /* include/linux/limits.h */
#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */
/* The size of the node cache */
#define FSYSREISER_CACHE_SIZE 24*1024
#define FSYSREISER_MIN_BLOCKSIZE SECTOR_SIZE
#define FSYSREISER_MAX_BLOCKSIZE FSYSREISER_CACHE_SIZE / 3
/* Info about currently opened file */
struct fsys_reiser_fileinfo
{
__u32 k_dir_id;
__u32 k_objectid;
};
/* In memory info about the currently mounted filesystem */
struct fsys_reiser_info
{
/* The last read item head */
struct item_head *current_ih;
/* The last read item */
char *current_item;
/* The information for the currently opened file */
struct fsys_reiser_fileinfo fileinfo;
/* The start of the journal */
__u32 journal_block;
/* The size of the journal */
__u32 journal_block_count;
/* The first valid descriptor block in journal
(relative to journal_block) */
__u32 journal_first_desc;
/* The ReiserFS version. */
__u16 version;
/* The current depth of the reiser tree. */
__u16 tree_depth;
/* SECTOR_SIZE << blocksize_shift == blocksize. */
__u8 blocksize_shift;
/* 1 << full_blocksize_shift == blocksize. */
__u8 fullblocksize_shift;
/* The reiserfs block size (must be a power of 2) */
__u16 blocksize;
/* The number of cached tree nodes */
__u16 cached_slots;
/* The number of valid transactions in journal */
__u16 journal_transactions;
unsigned int blocks[MAX_HEIGHT];
unsigned int next_key_nr[MAX_HEIGHT];
};
/* The cached s+tree blocks in FSYS_BUF, see below
* for a more detailed description.
*/
#define ROOT ((char *) ((int) FSYS_BUF))
#define CACHE(i) (ROOT + ((i) << INFO->fullblocksize_shift))
#define LEAF CACHE (DISK_LEAF_NODE_LEVEL)
#define BLOCKHEAD(cache) ((struct block_head *) cache)
#define ITEMHEAD ((struct item_head *) ((int) LEAF + BLKH_SIZE))
#define KEY(cache) ((struct key *) ((int) cache + BLKH_SIZE))
#define DC(cache) ((struct disk_child *) \
((int) cache + BLKH_SIZE + KEY_SIZE * nr_item))
/* The fsys_reiser_info block.
*/
#define INFO \
((struct fsys_reiser_info *) ((int) FSYS_BUF + FSYSREISER_CACHE_SIZE))
/*
* The journal cache. For each transaction it contains the number of
* blocks followed by the real block numbers of this transaction.
*
* If the block numbers of some transaction won't fit in this space,
* this list is stopped with a 0xffffffff marker and the remaining
* uncommitted transactions aren't cached.
*/
#define JOURNAL_START ((__u32 *) (INFO + 1))
#define JOURNAL_END ((__u32 *) (FSYS_BUF + FSYS_BUFLEN))
static __inline__ unsigned long
log2 (unsigned long word)
{
#ifdef __I386__
__asm__ ("bsfl %1,%0"
: "=r" (word)
: "r" (word));
return word;
#else
int i;
for(i=0; i<(8*sizeof(word)); i++)
if ((1<<i) & word)
return i;
return 0;
#endif
}
static __inline__ int
is_power_of_two (unsigned long word)
{
return (word & -word) == word;
}
extern const char *bb_mode_string(int mode);
extern int reiserfs_devread (int sector, int byte_offset, int byte_len, char *buf);

View file

@ -0,0 +1,52 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2003
# Pavel Bartusek, Sysgo Real-Time Solutions AG, pba@sysgo.de
#
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libubifs.o
COBJS-$(CONFIG_CMD_UBIFS) := ubifs.o io.o super.o sb.o master.o lpt.o
COBJS-$(CONFIG_CMD_UBIFS) += lpt_commit.o scan.o lprops.o
COBJS-$(CONFIG_CMD_UBIFS) += tnc.o tnc_misc.o debug.o crc16.o budget.o
COBJS-$(CONFIG_CMD_UBIFS) += log.o orphan.o recovery.o replay.o
SRCS := $(AOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS-y))
all: $(LIB) $(AOBJS)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,113 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Adrian Hunter
* Artem Bityutskiy (Битюцкий Артём)
*/
/*
* This file implements the budgeting sub-system which is responsible for UBIFS
* space management.
*
* Factors such as compression, wasted space at the ends of LEBs, space in other
* journal heads, the effect of updates on the index, and so on, make it
* impossible to accurately predict the amount of space needed. Consequently
* approximations are used.
*/
#include "ubifs.h"
#include <linux/math64.h>
/**
* ubifs_calc_min_idx_lebs - calculate amount of eraseblocks for the index.
* @c: UBIFS file-system description object
*
* This function calculates and returns the number of eraseblocks which should
* be kept for index usage.
*/
int ubifs_calc_min_idx_lebs(struct ubifs_info *c)
{
int idx_lebs, eff_leb_size = c->leb_size - c->max_idx_node_sz;
long long idx_size;
idx_size = c->old_idx_sz + c->budg_idx_growth + c->budg_uncommitted_idx;
/* And make sure we have thrice the index size of space reserved */
idx_size = idx_size + (idx_size << 1);
/*
* We do not maintain 'old_idx_size' as 'old_idx_lebs'/'old_idx_bytes'
* pair, nor similarly the two variables for the new index size, so we
* have to do this costly 64-bit division on fast-path.
*/
idx_size += eff_leb_size - 1;
idx_lebs = div_u64(idx_size, eff_leb_size);
/*
* The index head is not available for the in-the-gaps method, so add an
* extra LEB to compensate.
*/
idx_lebs += 1;
if (idx_lebs < MIN_INDEX_LEBS)
idx_lebs = MIN_INDEX_LEBS;
return idx_lebs;
}
/**
* ubifs_reported_space - calculate reported free space.
* @c: the UBIFS file-system description object
* @free: amount of free space
*
* This function calculates amount of free space which will be reported to
* user-space. User-space application tend to expect that if the file-system
* (e.g., via the 'statfs()' call) reports that it has N bytes available, they
* are able to write a file of size N. UBIFS attaches node headers to each data
* node and it has to write indexing nodes as well. This introduces additional
* overhead, and UBIFS has to report slightly less free space to meet the above
* expectations.
*
* This function assumes free space is made up of uncompressed data nodes and
* full index nodes (one per data node, tripled because we always allow enough
* space to write the index thrice).
*
* Note, the calculation is pessimistic, which means that most of the time
* UBIFS reports less space than it actually has.
*/
long long ubifs_reported_space(const struct ubifs_info *c, long long free)
{
int divisor, factor, f;
/*
* Reported space size is @free * X, where X is UBIFS block size
* divided by UBIFS block size + all overhead one data block
* introduces. The overhead is the node header + indexing overhead.
*
* Indexing overhead calculations are based on the following formula:
* I = N/(f - 1) + 1, where I - number of indexing nodes, N - number
* of data nodes, f - fanout. Because effective UBIFS fanout is twice
* as less than maximum fanout, we assume that each data node
* introduces 3 * @c->max_idx_node_sz / (@c->fanout/2 - 1) bytes.
* Note, the multiplier 3 is because UBIFS reserves thrice as more space
* for the index.
*/
f = c->fanout > 3 ? c->fanout >> 1 : 2;
factor = UBIFS_BLOCK_SIZE;
divisor = UBIFS_MAX_DATA_NODE_SZ;
divisor += (c->max_idx_node_sz * 3) / (f - 1);
free *= factor;
return div_u64(free, divisor);
}

View file

@ -0,0 +1,60 @@
/*
* crc16.c
*
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
*/
#include <linux/types.h>
#include "crc16.h"
/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
u16 const crc16_table[256] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
/**
* crc16 - compute the CRC-16 for the data buffer
* @crc: previous CRC value
* @buffer: data pointer
* @len: number of bytes in the buffer
*
* Returns the updated CRC value.
*/
u16 crc16(u16 crc, u8 const *buffer, size_t len)
{
while (len--)
crc = crc16_byte(crc, *buffer++);
return crc;
}

View file

@ -0,0 +1,29 @@
/*
* crc16.h - CRC-16 routine
*
* Implements the standard CRC-16:
* Width 16
* Poly 0x8005 (x^16 + x^15 + x^2 + 1)
* Init 0
*
* Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
*
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
*/
#ifndef __CRC16_H
#define __CRC16_H
#include <linux/types.h>
extern u16 const crc16_table[256];
extern u16 crc16(u16 crc, const u8 *buffer, size_t len);
static inline u16 crc16_byte(u16 crc, const u8 data)
{
return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff];
}
#endif /* __CRC16_H */

View file

@ -0,0 +1,156 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
/*
* This file implements most of the debugging stuff which is compiled in only
* when it is enabled. But some debugging check functions are implemented in
* corresponding subsystem, just because they are closely related and utilize
* various local functions of those subsystems.
*/
#define UBIFS_DBG_PRESERVE_UBI
#include "ubifs.h"
#ifdef CONFIG_UBIFS_FS_DEBUG
DEFINE_SPINLOCK(dbg_lock);
static char dbg_key_buf0[128];
static char dbg_key_buf1[128];
unsigned int ubifs_msg_flags = UBIFS_MSG_FLAGS_DEFAULT;
unsigned int ubifs_chk_flags = UBIFS_CHK_FLAGS_DEFAULT;
unsigned int ubifs_tst_flags;
module_param_named(debug_msgs, ubifs_msg_flags, uint, S_IRUGO | S_IWUSR);
module_param_named(debug_chks, ubifs_chk_flags, uint, S_IRUGO | S_IWUSR);
module_param_named(debug_tsts, ubifs_tst_flags, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug_msgs, "Debug message type flags");
MODULE_PARM_DESC(debug_chks, "Debug check flags");
MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
static const char *get_key_type(int type)
{
switch (type) {
case UBIFS_INO_KEY:
return "inode";
case UBIFS_DENT_KEY:
return "direntry";
case UBIFS_XENT_KEY:
return "xentry";
case UBIFS_DATA_KEY:
return "data";
case UBIFS_TRUN_KEY:
return "truncate";
default:
return "unknown/invalid key";
}
}
static void sprintf_key(const struct ubifs_info *c, const union ubifs_key *key,
char *buffer)
{
char *p = buffer;
int type = key_type(c, key);
if (c->key_fmt == UBIFS_SIMPLE_KEY_FMT) {
switch (type) {
case UBIFS_INO_KEY:
sprintf(p, "(%lu, %s)", (unsigned long)key_inum(c, key),
get_key_type(type));
break;
case UBIFS_DENT_KEY:
case UBIFS_XENT_KEY:
sprintf(p, "(%lu, %s, %#08x)",
(unsigned long)key_inum(c, key),
get_key_type(type), key_hash(c, key));
break;
case UBIFS_DATA_KEY:
sprintf(p, "(%lu, %s, %u)",
(unsigned long)key_inum(c, key),
get_key_type(type), key_block(c, key));
break;
case UBIFS_TRUN_KEY:
sprintf(p, "(%lu, %s)",
(unsigned long)key_inum(c, key),
get_key_type(type));
break;
default:
sprintf(p, "(bad key type: %#08x, %#08x)",
key->u32[0], key->u32[1]);
}
} else
sprintf(p, "bad key format %d", c->key_fmt);
}
const char *dbg_key_str0(const struct ubifs_info *c, const union ubifs_key *key)
{
/* dbg_lock must be held */
sprintf_key(c, key, dbg_key_buf0);
return dbg_key_buf0;
}
const char *dbg_key_str1(const struct ubifs_info *c, const union ubifs_key *key)
{
/* dbg_lock must be held */
sprintf_key(c, key, dbg_key_buf1);
return dbg_key_buf1;
}
/**
* ubifs_debugging_init - initialize UBIFS debugging.
* @c: UBIFS file-system description object
*
* This function initializes debugging-related data for the file system.
* Returns zero in case of success and a negative error code in case of
* failure.
*/
int ubifs_debugging_init(struct ubifs_info *c)
{
c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
if (!c->dbg)
return -ENOMEM;
c->dbg->buf = vmalloc(c->leb_size);
if (!c->dbg->buf)
goto out;
return 0;
out:
kfree(c->dbg);
return -ENOMEM;
}
/**
* ubifs_debugging_exit - free debugging data.
* @c: UBIFS file-system description object
*/
void ubifs_debugging_exit(struct ubifs_info *c)
{
vfree(c->dbg->buf);
kfree(c->dbg);
}
#endif /* CONFIG_UBIFS_FS_DEBUG */

View file

@ -0,0 +1,392 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
#ifndef __UBIFS_DEBUG_H__
#define __UBIFS_DEBUG_H__
#ifdef CONFIG_UBIFS_FS_DEBUG
/**
* ubifs_debug_info - per-FS debugging information.
* @buf: a buffer of LEB size, used for various purposes
* @old_zroot: old index root - used by 'dbg_check_old_index()'
* @old_zroot_level: old index root level - used by 'dbg_check_old_index()'
* @old_zroot_sqnum: old index root sqnum - used by 'dbg_check_old_index()'
* @failure_mode: failure mode for recovery testing
* @fail_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls
* @fail_timeout: time in jiffies when delay of failure mode expires
* @fail_cnt: current number of calls to failure mode I/O functions
* @fail_cnt_max: number of calls by which to delay failure mode
* @chk_lpt_sz: used by LPT tree size checker
* @chk_lpt_sz2: used by LPT tree size checker
* @chk_lpt_wastage: used by LPT tree size checker
* @chk_lpt_lebs: used by LPT tree size checker
* @new_nhead_offs: used by LPT tree size checker
* @new_ihead_lnum: used by debugging to check @c->ihead_lnum
* @new_ihead_offs: used by debugging to check @c->ihead_offs
*
* @saved_lst: saved lprops statistics (used by 'dbg_save_space_info()')
* @saved_free: saved free space (used by 'dbg_save_space_info()')
*
* dfs_dir_name: name of debugfs directory containing this file-system's files
* dfs_dir: direntry object of the file-system debugfs directory
* dfs_dump_lprops: "dump lprops" debugfs knob
* dfs_dump_budg: "dump budgeting information" debugfs knob
* dfs_dump_tnc: "dump TNC" debugfs knob
*/
struct ubifs_debug_info {
void *buf;
struct ubifs_zbranch old_zroot;
int old_zroot_level;
unsigned long long old_zroot_sqnum;
int failure_mode;
int fail_delay;
unsigned long fail_timeout;
unsigned int fail_cnt;
unsigned int fail_cnt_max;
long long chk_lpt_sz;
long long chk_lpt_sz2;
long long chk_lpt_wastage;
int chk_lpt_lebs;
int new_nhead_offs;
int new_ihead_lnum;
int new_ihead_offs;
struct ubifs_lp_stats saved_lst;
long long saved_free;
char dfs_dir_name[100];
struct dentry *dfs_dir;
struct dentry *dfs_dump_lprops;
struct dentry *dfs_dump_budg;
struct dentry *dfs_dump_tnc;
};
#define UBIFS_DBG(op) op
#define ubifs_assert(expr) do { \
if (unlikely(!(expr))) { \
printk(KERN_CRIT "UBIFS assert failed in %s at %u (pid %d)\n", \
__func__, __LINE__, 0); \
dbg_dump_stack(); \
} \
} while (0)
#define ubifs_assert_cmt_locked(c) do { \
if (unlikely(down_write_trylock(&(c)->commit_sem))) { \
up_write(&(c)->commit_sem); \
printk(KERN_CRIT "commit lock is not locked!\n"); \
ubifs_assert(0); \
} \
} while (0)
#define dbg_dump_stack() do { \
if (!dbg_failure_mode) \
dump_stack(); \
} while (0)
/* Generic debugging messages */
#define dbg_msg(fmt, ...) do { \
spin_lock(&dbg_lock); \
printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", 0, \
__func__, ##__VA_ARGS__); \
spin_unlock(&dbg_lock); \
} while (0)
#define dbg_do_msg(typ, fmt, ...) do { \
if (ubifs_msg_flags & typ) \
dbg_msg(fmt, ##__VA_ARGS__); \
} while (0)
#define dbg_err(fmt, ...) do { \
spin_lock(&dbg_lock); \
ubifs_err(fmt, ##__VA_ARGS__); \
spin_unlock(&dbg_lock); \
} while (0)
const char *dbg_key_str0(const struct ubifs_info *c,
const union ubifs_key *key);
const char *dbg_key_str1(const struct ubifs_info *c,
const union ubifs_key *key);
/*
* DBGKEY macros require @dbg_lock to be held, which it is in the dbg message
* macros.
*/
#define DBGKEY(key) dbg_key_str0(c, (key))
#define DBGKEY1(key) dbg_key_str1(c, (key))
/* General messages */
#define dbg_gen(fmt, ...) dbg_do_msg(UBIFS_MSG_GEN, fmt, ##__VA_ARGS__)
/* Additional journal messages */
#define dbg_jnl(fmt, ...) dbg_do_msg(UBIFS_MSG_JNL, fmt, ##__VA_ARGS__)
/* Additional TNC messages */
#define dbg_tnc(fmt, ...) dbg_do_msg(UBIFS_MSG_TNC, fmt, ##__VA_ARGS__)
/* Additional lprops messages */
#define dbg_lp(fmt, ...) dbg_do_msg(UBIFS_MSG_LP, fmt, ##__VA_ARGS__)
/* Additional LEB find messages */
#define dbg_find(fmt, ...) dbg_do_msg(UBIFS_MSG_FIND, fmt, ##__VA_ARGS__)
/* Additional mount messages */
#define dbg_mnt(fmt, ...) dbg_do_msg(UBIFS_MSG_MNT, fmt, ##__VA_ARGS__)
/* Additional I/O messages */
#define dbg_io(fmt, ...) dbg_do_msg(UBIFS_MSG_IO, fmt, ##__VA_ARGS__)
/* Additional commit messages */
#define dbg_cmt(fmt, ...) dbg_do_msg(UBIFS_MSG_CMT, fmt, ##__VA_ARGS__)
/* Additional budgeting messages */
#define dbg_budg(fmt, ...) dbg_do_msg(UBIFS_MSG_BUDG, fmt, ##__VA_ARGS__)
/* Additional log messages */
#define dbg_log(fmt, ...) dbg_do_msg(UBIFS_MSG_LOG, fmt, ##__VA_ARGS__)
/* Additional gc messages */
#define dbg_gc(fmt, ...) dbg_do_msg(UBIFS_MSG_GC, fmt, ##__VA_ARGS__)
/* Additional scan messages */
#define dbg_scan(fmt, ...) dbg_do_msg(UBIFS_MSG_SCAN, fmt, ##__VA_ARGS__)
/* Additional recovery messages */
#define dbg_rcvry(fmt, ...) dbg_do_msg(UBIFS_MSG_RCVRY, fmt, ##__VA_ARGS__)
/*
* Debugging message type flags (must match msg_type_names in debug.c).
*
* UBIFS_MSG_GEN: general messages
* UBIFS_MSG_JNL: journal messages
* UBIFS_MSG_MNT: mount messages
* UBIFS_MSG_CMT: commit messages
* UBIFS_MSG_FIND: LEB find messages
* UBIFS_MSG_BUDG: budgeting messages
* UBIFS_MSG_GC: garbage collection messages
* UBIFS_MSG_TNC: TNC messages
* UBIFS_MSG_LP: lprops messages
* UBIFS_MSG_IO: I/O messages
* UBIFS_MSG_LOG: log messages
* UBIFS_MSG_SCAN: scan messages
* UBIFS_MSG_RCVRY: recovery messages
*/
enum {
UBIFS_MSG_GEN = 0x1,
UBIFS_MSG_JNL = 0x2,
UBIFS_MSG_MNT = 0x4,
UBIFS_MSG_CMT = 0x8,
UBIFS_MSG_FIND = 0x10,
UBIFS_MSG_BUDG = 0x20,
UBIFS_MSG_GC = 0x40,
UBIFS_MSG_TNC = 0x80,
UBIFS_MSG_LP = 0x100,
UBIFS_MSG_IO = 0x200,
UBIFS_MSG_LOG = 0x400,
UBIFS_MSG_SCAN = 0x800,
UBIFS_MSG_RCVRY = 0x1000,
};
/* Debugging message type flags for each default debug message level */
#define UBIFS_MSG_LVL_0 0
#define UBIFS_MSG_LVL_1 0x1
#define UBIFS_MSG_LVL_2 0x7f
#define UBIFS_MSG_LVL_3 0xffff
/*
* Debugging check flags (must match chk_names in debug.c).
*
* UBIFS_CHK_GEN: general checks
* UBIFS_CHK_TNC: check TNC
* UBIFS_CHK_IDX_SZ: check index size
* UBIFS_CHK_ORPH: check orphans
* UBIFS_CHK_OLD_IDX: check the old index
* UBIFS_CHK_LPROPS: check lprops
* UBIFS_CHK_FS: check the file-system
*/
enum {
UBIFS_CHK_GEN = 0x1,
UBIFS_CHK_TNC = 0x2,
UBIFS_CHK_IDX_SZ = 0x4,
UBIFS_CHK_ORPH = 0x8,
UBIFS_CHK_OLD_IDX = 0x10,
UBIFS_CHK_LPROPS = 0x20,
UBIFS_CHK_FS = 0x40,
};
/*
* Special testing flags (must match tst_names in debug.c).
*
* UBIFS_TST_FORCE_IN_THE_GAPS: force the use of in-the-gaps method
* UBIFS_TST_RCVRY: failure mode for recovery testing
*/
enum {
UBIFS_TST_FORCE_IN_THE_GAPS = 0x2,
UBIFS_TST_RCVRY = 0x4,
};
#if CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 1
#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_1
#elif CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 2
#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_2
#elif CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 3
#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_3
#else
#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_0
#endif
#ifdef CONFIG_UBIFS_FS_DEBUG_CHKS
#define UBIFS_CHK_FLAGS_DEFAULT 0xffffffff
#else
#define UBIFS_CHK_FLAGS_DEFAULT 0
#endif
#define dbg_ntype(type) ""
#define dbg_cstate(cmt_state) ""
#define dbg_get_key_dump(c, key) ({})
#define dbg_dump_inode(c, inode) ({})
#define dbg_dump_node(c, node) ({})
#define dbg_dump_budget_req(req) ({})
#define dbg_dump_lstats(lst) ({})
#define dbg_dump_budg(c) ({})
#define dbg_dump_lprop(c, lp) ({})
#define dbg_dump_lprops(c) ({})
#define dbg_dump_lpt_info(c) ({})
#define dbg_dump_leb(c, lnum) ({})
#define dbg_dump_znode(c, znode) ({})
#define dbg_dump_heap(c, heap, cat) ({})
#define dbg_dump_pnode(c, pnode, parent, iip) ({})
#define dbg_dump_tnc(c) ({})
#define dbg_dump_index(c) ({})
#define dbg_walk_index(c, leaf_cb, znode_cb, priv) 0
#define dbg_old_index_check_init(c, zroot) 0
#define dbg_check_old_index(c, zroot) 0
#define dbg_check_cats(c) 0
#define dbg_check_ltab(c) 0
#define dbg_chk_lpt_free_spc(c) 0
#define dbg_chk_lpt_sz(c, action, len) 0
#define dbg_check_synced_i_size(inode) 0
#define dbg_check_dir_size(c, dir) 0
#define dbg_check_tnc(c, x) 0
#define dbg_check_idx_size(c, idx_size) 0
#define dbg_check_filesystem(c) 0
#define dbg_check_heap(c, heap, cat, add_pos) ({})
#define dbg_check_lprops(c) 0
#define dbg_check_lpt_nodes(c, cnode, row, col) 0
#define dbg_force_in_the_gaps_enabled 0
#define dbg_force_in_the_gaps() 0
#define dbg_failure_mode 0
#define dbg_failure_mode_registration(c) ({})
#define dbg_failure_mode_deregistration(c) ({})
int ubifs_debugging_init(struct ubifs_info *c);
void ubifs_debugging_exit(struct ubifs_info *c);
#else /* !CONFIG_UBIFS_FS_DEBUG */
#define UBIFS_DBG(op)
/* Use "if (0)" to make compiler check arguments even if debugging is off */
#define ubifs_assert(expr) do { \
if (0 && (expr)) \
printk(KERN_CRIT "UBIFS assert failed in %s at %u (pid %d)\n", \
__func__, __LINE__, 0); \
} while (0)
#define dbg_err(fmt, ...) do { \
if (0) \
ubifs_err(fmt, ##__VA_ARGS__); \
} while (0)
#define dbg_msg(fmt, ...) do { \
if (0) \
printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", \
0, __func__, ##__VA_ARGS__); \
} while (0)
#define dbg_dump_stack()
#define ubifs_assert_cmt_locked(c)
#define dbg_gen(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_jnl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_tnc(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_lp(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_find(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_mnt(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_cmt(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_budg(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_log(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_gc(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_scan(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define dbg_rcvry(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#define DBGKEY(key) ((char *)(key))
#define DBGKEY1(key) ((char *)(key))
#define ubifs_debugging_init(c) 0
#define ubifs_debugging_exit(c) ({})
#define dbg_ntype(type) ""
#define dbg_cstate(cmt_state) ""
#define dbg_get_key_dump(c, key) ({})
#define dbg_dump_inode(c, inode) ({})
#define dbg_dump_node(c, node) ({})
#define dbg_dump_budget_req(req) ({})
#define dbg_dump_lstats(lst) ({})
#define dbg_dump_budg(c) ({})
#define dbg_dump_lprop(c, lp) ({})
#define dbg_dump_lprops(c) ({})
#define dbg_dump_lpt_info(c) ({})
#define dbg_dump_leb(c, lnum) ({})
#define dbg_dump_znode(c, znode) ({})
#define dbg_dump_heap(c, heap, cat) ({})
#define dbg_dump_pnode(c, pnode, parent, iip) ({})
#define dbg_dump_tnc(c) ({})
#define dbg_dump_index(c) ({})
#define dbg_walk_index(c, leaf_cb, znode_cb, priv) 0
#define dbg_old_index_check_init(c, zroot) 0
#define dbg_check_old_index(c, zroot) 0
#define dbg_check_cats(c) 0
#define dbg_check_ltab(c) 0
#define dbg_chk_lpt_free_spc(c) 0
#define dbg_chk_lpt_sz(c, action, len) 0
#define dbg_check_synced_i_size(inode) 0
#define dbg_check_dir_size(c, dir) 0
#define dbg_check_tnc(c, x) 0
#define dbg_check_idx_size(c, idx_size) 0
#define dbg_check_filesystem(c) 0
#define dbg_check_heap(c, heap, cat, add_pos) ({})
#define dbg_check_lprops(c) 0
#define dbg_check_lpt_nodes(c, cnode, row, col) 0
#define dbg_force_in_the_gaps_enabled 0
#define dbg_force_in_the_gaps() 0
#define dbg_failure_mode 0
#define dbg_failure_mode_registration(c) ({})
#define dbg_failure_mode_deregistration(c) ({})
#endif /* !CONFIG_UBIFS_FS_DEBUG */
#endif /* !__UBIFS_DEBUG_H__ */

View file

@ -0,0 +1,316 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
* Copyright (C) 2006, 2007 University of Szeged, Hungary
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
* Zoltan Sogor
*/
/*
* This file implements UBIFS I/O subsystem which provides various I/O-related
* helper functions (reading/writing/checking/validating nodes) and implements
* write-buffering support. Write buffers help to save space which otherwise
* would have been wasted for padding to the nearest minimal I/O unit boundary.
* Instead, data first goes to the write-buffer and is flushed when the
* buffer is full or when it is not used for some time (by timer). This is
* similar to the mechanism is used by JFFS2.
*
* Write-buffers are defined by 'struct ubifs_wbuf' objects and protected by
* mutexes defined inside these objects. Since sometimes upper-level code
* has to lock the write-buffer (e.g. journal space reservation code), many
* functions related to write-buffers have "nolock" suffix which means that the
* caller has to lock the write-buffer before calling this function.
*
* UBIFS stores nodes at 64 bit-aligned addresses. If the node length is not
* aligned, UBIFS starts the next node from the aligned address, and the padded
* bytes may contain any rubbish. In other words, UBIFS does not put padding
* bytes in those small gaps. Common headers of nodes store real node lengths,
* not aligned lengths. Indexing nodes also store real lengths in branches.
*
* UBIFS uses padding when it pads to the next min. I/O unit. In this case it
* uses padding nodes or padding bytes, if the padding node does not fit.
*
* All UBIFS nodes are protected by CRC checksums and UBIFS checks all nodes
* every time they are read from the flash media.
*/
#include "ubifs.h"
/**
* ubifs_ro_mode - switch UBIFS to read read-only mode.
* @c: UBIFS file-system description object
* @err: error code which is the reason of switching to R/O mode
*/
void ubifs_ro_mode(struct ubifs_info *c, int err)
{
if (!c->ro_media) {
c->ro_media = 1;
c->no_chk_data_crc = 0;
ubifs_warn("switched to read-only mode, error %d", err);
dbg_dump_stack();
}
}
/**
* ubifs_check_node - check node.
* @c: UBIFS file-system description object
* @buf: node to check
* @lnum: logical eraseblock number
* @offs: offset within the logical eraseblock
* @quiet: print no messages
* @must_chk_crc: indicates whether to always check the CRC
*
* This function checks node magic number and CRC checksum. This function also
* validates node length to prevent UBIFS from becoming crazy when an attacker
* feeds it a file-system image with incorrect nodes. For example, too large
* node length in the common header could cause UBIFS to read memory outside of
* allocated buffer when checking the CRC checksum.
*
* This function may skip data nodes CRC checking if @c->no_chk_data_crc is
* true, which is controlled by corresponding UBIFS mount option. However, if
* @must_chk_crc is true, then @c->no_chk_data_crc is ignored and CRC is
* checked. Similarly, if @c->always_chk_crc is true, @c->no_chk_data_crc is
* ignored and CRC is checked.
*
* This function returns zero in case of success and %-EUCLEAN in case of bad
* CRC or magic.
*/
int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
int offs, int quiet, int must_chk_crc)
{
int err = -EINVAL, type, node_len;
uint32_t crc, node_crc, magic;
const struct ubifs_ch *ch = buf;
ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
ubifs_assert(!(offs & 7) && offs < c->leb_size);
magic = le32_to_cpu(ch->magic);
if (magic != UBIFS_NODE_MAGIC) {
if (!quiet)
ubifs_err("bad magic %#08x, expected %#08x",
magic, UBIFS_NODE_MAGIC);
err = -EUCLEAN;
goto out;
}
type = ch->node_type;
if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) {
if (!quiet)
ubifs_err("bad node type %d", type);
goto out;
}
node_len = le32_to_cpu(ch->len);
if (node_len + offs > c->leb_size)
goto out_len;
if (c->ranges[type].max_len == 0) {
if (node_len != c->ranges[type].len)
goto out_len;
} else if (node_len < c->ranges[type].min_len ||
node_len > c->ranges[type].max_len)
goto out_len;
if (!must_chk_crc && type == UBIFS_DATA_NODE && !c->always_chk_crc &&
c->no_chk_data_crc)
return 0;
crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8);
node_crc = le32_to_cpu(ch->crc);
if (crc != node_crc) {
if (!quiet)
ubifs_err("bad CRC: calculated %#08x, read %#08x",
crc, node_crc);
err = -EUCLEAN;
goto out;
}
return 0;
out_len:
if (!quiet)
ubifs_err("bad node length %d", node_len);
out:
if (!quiet) {
ubifs_err("bad node at LEB %d:%d", lnum, offs);
dbg_dump_node(c, buf);
dbg_dump_stack();
}
return err;
}
/**
* ubifs_pad - pad flash space.
* @c: UBIFS file-system description object
* @buf: buffer to put padding to
* @pad: how many bytes to pad
*
* The flash media obliges us to write only in chunks of %c->min_io_size and
* when we have to write less data we add padding node to the write-buffer and
* pad it to the next minimal I/O unit's boundary. Padding nodes help when the
* media is being scanned. If the amount of wasted space is not enough to fit a
* padding node which takes %UBIFS_PAD_NODE_SZ bytes, we write padding bytes
* pattern (%UBIFS_PADDING_BYTE).
*
* Padding nodes are also used to fill gaps when the "commit-in-gaps" method is
* used.
*/
void ubifs_pad(const struct ubifs_info *c, void *buf, int pad)
{
uint32_t crc;
ubifs_assert(pad >= 0 && !(pad & 7));
if (pad >= UBIFS_PAD_NODE_SZ) {
struct ubifs_ch *ch = buf;
struct ubifs_pad_node *pad_node = buf;
ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
ch->node_type = UBIFS_PAD_NODE;
ch->group_type = UBIFS_NO_NODE_GROUP;
ch->padding[0] = ch->padding[1] = 0;
ch->sqnum = 0;
ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ);
pad -= UBIFS_PAD_NODE_SZ;
pad_node->pad_len = cpu_to_le32(pad);
crc = crc32(UBIFS_CRC32_INIT, buf + 8, UBIFS_PAD_NODE_SZ - 8);
ch->crc = cpu_to_le32(crc);
memset(buf + UBIFS_PAD_NODE_SZ, 0, pad);
} else if (pad > 0)
/* Too little space, padding node won't fit */
memset(buf, UBIFS_PADDING_BYTE, pad);
}
/**
* next_sqnum - get next sequence number.
* @c: UBIFS file-system description object
*/
static unsigned long long next_sqnum(struct ubifs_info *c)
{
unsigned long long sqnum;
spin_lock(&c->cnt_lock);
sqnum = ++c->max_sqnum;
spin_unlock(&c->cnt_lock);
if (unlikely(sqnum >= SQNUM_WARN_WATERMARK)) {
if (sqnum >= SQNUM_WATERMARK) {
ubifs_err("sequence number overflow %llu, end of life",
sqnum);
ubifs_ro_mode(c, -EINVAL);
}
ubifs_warn("running out of sequence numbers, end of life soon");
}
return sqnum;
}
/**
* ubifs_prepare_node - prepare node to be written to flash.
* @c: UBIFS file-system description object
* @node: the node to pad
* @len: node length
* @pad: if the buffer has to be padded
*
* This function prepares node at @node to be written to the media - it
* calculates node CRC, fills the common header, and adds proper padding up to
* the next minimum I/O unit if @pad is not zero.
*/
void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
{
uint32_t crc;
struct ubifs_ch *ch = node;
unsigned long long sqnum = next_sqnum(c);
ubifs_assert(len >= UBIFS_CH_SZ);
ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
ch->len = cpu_to_le32(len);
ch->group_type = UBIFS_NO_NODE_GROUP;
ch->sqnum = cpu_to_le64(sqnum);
ch->padding[0] = ch->padding[1] = 0;
crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
ch->crc = cpu_to_le32(crc);
if (pad) {
len = ALIGN(len, 8);
pad = ALIGN(len, c->min_io_size) - len;
ubifs_pad(c, node + len, pad);
}
}
/**
* ubifs_read_node - read node.
* @c: UBIFS file-system description object
* @buf: buffer to read to
* @type: node type
* @len: node length (not aligned)
* @lnum: logical eraseblock number
* @offs: offset within the logical eraseblock
*
* This function reads a node of known type and and length, checks it and
* stores in @buf. Returns zero in case of success, %-EUCLEAN if CRC mismatched
* and a negative error code in case of failure.
*/
int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
int lnum, int offs)
{
int err, l;
struct ubifs_ch *ch = buf;
dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len);
ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
ubifs_assert(len >= UBIFS_CH_SZ && offs + len <= c->leb_size);
ubifs_assert(!(offs & 7) && offs < c->leb_size);
ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT);
err = ubi_read(c->ubi, lnum, buf, offs, len);
if (err && err != -EBADMSG) {
ubifs_err("cannot read node %d from LEB %d:%d, error %d",
type, lnum, offs, err);
return err;
}
if (type != ch->node_type) {
ubifs_err("bad node type (%d but expected %d)",
ch->node_type, type);
goto out;
}
err = ubifs_check_node(c, buf, lnum, offs, 0, 0);
if (err) {
ubifs_err("expected node type %d", type);
return err;
}
l = le32_to_cpu(ch->len);
if (l != len) {
ubifs_err("bad node length %d, expected %d", l, len);
goto out;
}
return 0;
out:
ubifs_err("bad node at LEB %d:%d", lnum, offs);
dbg_dump_node(c, buf);
dbg_dump_stack();
return -EINVAL;
}

View file

@ -0,0 +1,557 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
/*
* This header contains various key-related definitions and helper function.
* UBIFS allows several key schemes, so we access key fields only via these
* helpers. At the moment only one key scheme is supported.
*
* Simple key scheme
* ~~~~~~~~~~~~~~~~~
*
* Keys are 64-bits long. First 32-bits are inode number (parent inode number
* in case of direntry key). Next 3 bits are node type. The last 29 bits are
* 4KiB offset in case of inode node, and direntry hash in case of a direntry
* node. We use "r5" hash borrowed from reiserfs.
*/
#ifndef __UBIFS_KEY_H__
#define __UBIFS_KEY_H__
/**
* key_mask_hash - mask a valid hash value.
* @val: value to be masked
*
* We use hash values as offset in directories, so values %0 and %1 are
* reserved for "." and "..". %2 is reserved for "end of readdir" marker. This
* function makes sure the reserved values are not used.
*/
static inline uint32_t key_mask_hash(uint32_t hash)
{
hash &= UBIFS_S_KEY_HASH_MASK;
if (unlikely(hash <= 2))
hash += 3;
return hash;
}
/**
* key_r5_hash - R5 hash function (borrowed from reiserfs).
* @s: direntry name
* @len: name length
*/
static inline uint32_t key_r5_hash(const char *s, int len)
{
uint32_t a = 0;
const signed char *str = (const signed char *)s;
while (*str) {
a += *str << 4;
a += *str >> 4;
a *= 11;
str++;
}
return key_mask_hash(a);
}
/**
* key_test_hash - testing hash function.
* @str: direntry name
* @len: name length
*/
static inline uint32_t key_test_hash(const char *str, int len)
{
uint32_t a = 0;
len = min_t(uint32_t, len, 4);
memcpy(&a, str, len);
return key_mask_hash(a);
}
/**
* ino_key_init - initialize inode key.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: inode number
*/
static inline void ino_key_init(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS;
}
/**
* ino_key_init_flash - initialize on-flash inode key.
* @c: UBIFS file-system description object
* @k: key to initialize
* @inum: inode number
*/
static inline void ino_key_init_flash(const struct ubifs_info *c, void *k,
ino_t inum)
{
union ubifs_key *key = k;
key->j32[0] = cpu_to_le32(inum);
key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS);
memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
}
/**
* lowest_ino_key - get the lowest possible inode key.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: inode number
*/
static inline void lowest_ino_key(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
key->u32[1] = 0;
}
/**
* highest_ino_key - get the highest possible inode key.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: inode number
*/
static inline void highest_ino_key(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
key->u32[1] = 0xffffffff;
}
/**
* dent_key_init - initialize directory entry key.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: parent inode number
* @nm: direntry name and length
*/
static inline void dent_key_init(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum,
const struct qstr *nm)
{
uint32_t hash = c->key_hash(nm->name, nm->len);
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->u32[0] = inum;
key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
}
/**
* dent_key_init_hash - initialize directory entry key without re-calculating
* hash function.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: parent inode number
* @hash: direntry name hash
*/
static inline void dent_key_init_hash(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum,
uint32_t hash)
{
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->u32[0] = inum;
key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
}
/**
* dent_key_init_flash - initialize on-flash directory entry key.
* @c: UBIFS file-system description object
* @k: key to initialize
* @inum: parent inode number
* @nm: direntry name and length
*/
static inline void dent_key_init_flash(const struct ubifs_info *c, void *k,
ino_t inum, const struct qstr *nm)
{
union ubifs_key *key = k;
uint32_t hash = c->key_hash(nm->name, nm->len);
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->j32[0] = cpu_to_le32(inum);
key->j32[1] = cpu_to_le32(hash |
(UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS));
memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
}
/**
* lowest_dent_key - get the lowest possible directory entry key.
* @c: UBIFS file-system description object
* @key: where to store the lowest key
* @inum: parent inode number
*/
static inline void lowest_dent_key(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
key->u32[1] = UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS;
}
/**
* xent_key_init - initialize extended attribute entry key.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: host inode number
* @nm: extended attribute entry name and length
*/
static inline void xent_key_init(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum,
const struct qstr *nm)
{
uint32_t hash = c->key_hash(nm->name, nm->len);
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->u32[0] = inum;
key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
}
/**
* xent_key_init_hash - initialize extended attribute entry key without
* re-calculating hash function.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: host inode number
* @hash: extended attribute entry name hash
*/
static inline void xent_key_init_hash(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum,
uint32_t hash)
{
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->u32[0] = inum;
key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
}
/**
* xent_key_init_flash - initialize on-flash extended attribute entry key.
* @c: UBIFS file-system description object
* @k: key to initialize
* @inum: host inode number
* @nm: extended attribute entry name and length
*/
static inline void xent_key_init_flash(const struct ubifs_info *c, void *k,
ino_t inum, const struct qstr *nm)
{
union ubifs_key *key = k;
uint32_t hash = c->key_hash(nm->name, nm->len);
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->j32[0] = cpu_to_le32(inum);
key->j32[1] = cpu_to_le32(hash |
(UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS));
memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
}
/**
* lowest_xent_key - get the lowest possible extended attribute entry key.
* @c: UBIFS file-system description object
* @key: where to store the lowest key
* @inum: host inode number
*/
static inline void lowest_xent_key(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
key->u32[1] = UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS;
}
/**
* data_key_init - initialize data key.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: inode number
* @block: block number
*/
static inline void data_key_init(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum,
unsigned int block)
{
ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK));
key->u32[0] = inum;
key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS);
}
/**
* data_key_init_flash - initialize on-flash data key.
* @c: UBIFS file-system description object
* @k: key to initialize
* @inum: inode number
* @block: block number
*/
static inline void data_key_init_flash(const struct ubifs_info *c, void *k,
ino_t inum, unsigned int block)
{
union ubifs_key *key = k;
ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK));
key->j32[0] = cpu_to_le32(inum);
key->j32[1] = cpu_to_le32(block |
(UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS));
memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
}
/**
* trun_key_init - initialize truncation node key.
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: inode number
*
* Note, UBIFS does not have truncation keys on the media and this function is
* only used for purposes of replay.
*/
static inline void trun_key_init(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum)
{
key->u32[0] = inum;
key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS;
}
/**
* key_type - get key type.
* @c: UBIFS file-system description object
* @key: key to get type of
*/
static inline int key_type(const struct ubifs_info *c,
const union ubifs_key *key)
{
return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS;
}
/**
* key_type_flash - get type of a on-flash formatted key.
* @c: UBIFS file-system description object
* @k: key to get type of
*/
static inline int key_type_flash(const struct ubifs_info *c, const void *k)
{
const union ubifs_key *key = k;
return le32_to_cpu(key->j32[1]) >> UBIFS_S_KEY_BLOCK_BITS;
}
/**
* key_inum - fetch inode number from key.
* @c: UBIFS file-system description object
* @k: key to fetch inode number from
*/
static inline ino_t key_inum(const struct ubifs_info *c, const void *k)
{
const union ubifs_key *key = k;
return key->u32[0];
}
/**
* key_inum_flash - fetch inode number from an on-flash formatted key.
* @c: UBIFS file-system description object
* @k: key to fetch inode number from
*/
static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k)
{
const union ubifs_key *key = k;
return le32_to_cpu(key->j32[0]);
}
/**
* key_hash - get directory entry hash.
* @c: UBIFS file-system description object
* @key: the key to get hash from
*/
static inline int key_hash(const struct ubifs_info *c,
const union ubifs_key *key)
{
return key->u32[1] & UBIFS_S_KEY_HASH_MASK;
}
/**
* key_hash_flash - get directory entry hash from an on-flash formatted key.
* @c: UBIFS file-system description object
* @k: the key to get hash from
*/
static inline int key_hash_flash(const struct ubifs_info *c, const void *k)
{
const union ubifs_key *key = k;
return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_HASH_MASK;
}
/**
* key_block - get data block number.
* @c: UBIFS file-system description object
* @key: the key to get the block number from
*/
static inline unsigned int key_block(const struct ubifs_info *c,
const union ubifs_key *key)
{
return key->u32[1] & UBIFS_S_KEY_BLOCK_MASK;
}
/**
* key_block_flash - get data block number from an on-flash formatted key.
* @c: UBIFS file-system description object
* @k: the key to get the block number from
*/
static inline unsigned int key_block_flash(const struct ubifs_info *c,
const void *k)
{
const union ubifs_key *key = k;
return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_BLOCK_MASK;
}
/**
* key_read - transform a key to in-memory format.
* @c: UBIFS file-system description object
* @from: the key to transform
* @to: the key to store the result
*/
static inline void key_read(const struct ubifs_info *c, const void *from,
union ubifs_key *to)
{
const union ubifs_key *f = from;
to->u32[0] = le32_to_cpu(f->j32[0]);
to->u32[1] = le32_to_cpu(f->j32[1]);
}
/**
* key_write - transform a key from in-memory format.
* @c: UBIFS file-system description object
* @from: the key to transform
* @to: the key to store the result
*/
static inline void key_write(const struct ubifs_info *c,
const union ubifs_key *from, void *to)
{
union ubifs_key *t = to;
t->j32[0] = cpu_to_le32(from->u32[0]);
t->j32[1] = cpu_to_le32(from->u32[1]);
memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8);
}
/**
* key_write_idx - transform a key from in-memory format for the index.
* @c: UBIFS file-system description object
* @from: the key to transform
* @to: the key to store the result
*/
static inline void key_write_idx(const struct ubifs_info *c,
const union ubifs_key *from, void *to)
{
union ubifs_key *t = to;
t->j32[0] = cpu_to_le32(from->u32[0]);
t->j32[1] = cpu_to_le32(from->u32[1]);
}
/**
* key_copy - copy a key.
* @c: UBIFS file-system description object
* @from: the key to copy from
* @to: the key to copy to
*/
static inline void key_copy(const struct ubifs_info *c,
const union ubifs_key *from, union ubifs_key *to)
{
to->u64[0] = from->u64[0];
}
/**
* keys_cmp - compare keys.
* @c: UBIFS file-system description object
* @key1: the first key to compare
* @key2: the second key to compare
*
* This function compares 2 keys and returns %-1 if @key1 is less than
* @key2, %0 if the keys are equivalent and %1 if @key1 is greater than @key2.
*/
static inline int keys_cmp(const struct ubifs_info *c,
const union ubifs_key *key1,
const union ubifs_key *key2)
{
if (key1->u32[0] < key2->u32[0])
return -1;
if (key1->u32[0] > key2->u32[0])
return 1;
if (key1->u32[1] < key2->u32[1])
return -1;
if (key1->u32[1] > key2->u32[1])
return 1;
return 0;
}
/**
* keys_eq - determine if keys are equivalent.
* @c: UBIFS file-system description object
* @key1: the first key to compare
* @key2: the second key to compare
*
* This function compares 2 keys and returns %1 if @key1 is equal to @key2 and
* %0 if not.
*/
static inline int keys_eq(const struct ubifs_info *c,
const union ubifs_key *key1,
const union ubifs_key *key2)
{
if (key1->u32[0] != key2->u32[0])
return 0;
if (key1->u32[1] != key2->u32[1])
return 0;
return 1;
}
/**
* is_hash_key - is a key vulnerable to hash collisions.
* @c: UBIFS file-system description object
* @key: key
*
* This function returns %1 if @key is a hashed key or %0 otherwise.
*/
static inline int is_hash_key(const struct ubifs_info *c,
const union ubifs_key *key)
{
int type = key_type(c, key);
return type == UBIFS_DENT_KEY || type == UBIFS_XENT_KEY;
}
/**
* key_max_inode_size - get maximum file size allowed by current key format.
* @c: UBIFS file-system description object
*/
static inline unsigned long long key_max_inode_size(const struct ubifs_info *c)
{
switch (c->key_fmt) {
case UBIFS_SIMPLE_KEY_FMT:
return (1ULL << UBIFS_S_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE;
default:
return 0;
}
}
#endif /* !__UBIFS_KEY_H__ */

View file

@ -0,0 +1,104 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
/*
* This file is a part of UBIFS journal implementation and contains various
* functions which manipulate the log. The log is a fixed area on the flash
* which does not contain any data but refers to buds. The log is a part of the
* journal.
*/
#include "ubifs.h"
/**
* ubifs_search_bud - search bud LEB.
* @c: UBIFS file-system description object
* @lnum: logical eraseblock number to search
*
* This function searches bud LEB @lnum. Returns bud description object in case
* of success and %NULL if there is no bud with this LEB number.
*/
struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum)
{
struct rb_node *p;
struct ubifs_bud *bud;
spin_lock(&c->buds_lock);
p = c->buds.rb_node;
while (p) {
bud = rb_entry(p, struct ubifs_bud, rb);
if (lnum < bud->lnum)
p = p->rb_left;
else if (lnum > bud->lnum)
p = p->rb_right;
else {
spin_unlock(&c->buds_lock);
return bud;
}
}
spin_unlock(&c->buds_lock);
return NULL;
}
/**
* ubifs_add_bud - add bud LEB to the tree of buds and its journal head list.
* @c: UBIFS file-system description object
* @bud: the bud to add
*/
void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud)
{
struct rb_node **p, *parent = NULL;
struct ubifs_bud *b;
struct ubifs_jhead *jhead;
spin_lock(&c->buds_lock);
p = &c->buds.rb_node;
while (*p) {
parent = *p;
b = rb_entry(parent, struct ubifs_bud, rb);
ubifs_assert(bud->lnum != b->lnum);
if (bud->lnum < b->lnum)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&bud->rb, parent, p);
rb_insert_color(&bud->rb, &c->buds);
if (c->jheads) {
jhead = &c->jheads[bud->jhead];
list_add_tail(&bud->list, &jhead->buds_list);
} else
ubifs_assert(c->replaying && (c->vfs_sb->s_flags & MS_RDONLY));
/*
* Note, although this is a new bud, we anyway account this space now,
* before any data has been written to it, because this is about to
* guarantee fixed mount time, and this bud will anyway be read and
* scanned.
*/
c->bud_bytes += c->leb_size - bud->start;
dbg_log("LEB %d:%d, jhead %d, bud_bytes %lld", bud->lnum,
bud->start, bud->jhead, c->bud_bytes);
spin_unlock(&c->buds_lock);
}

View file

@ -0,0 +1,842 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Adrian Hunter
* Artem Bityutskiy (Битюцкий Артём)
*/
/*
* This file implements the functions that access LEB properties and their
* categories. LEBs are categorized based on the needs of UBIFS, and the
* categories are stored as either heaps or lists to provide a fast way of
* finding a LEB in a particular category. For example, UBIFS may need to find
* an empty LEB for the journal, or a very dirty LEB for garbage collection.
*/
#include "ubifs.h"
/**
* get_heap_comp_val - get the LEB properties value for heap comparisons.
* @lprops: LEB properties
* @cat: LEB category
*/
static int get_heap_comp_val(struct ubifs_lprops *lprops, int cat)
{
switch (cat) {
case LPROPS_FREE:
return lprops->free;
case LPROPS_DIRTY_IDX:
return lprops->free + lprops->dirty;
default:
return lprops->dirty;
}
}
/**
* move_up_lpt_heap - move a new heap entry up as far as possible.
* @c: UBIFS file-system description object
* @heap: LEB category heap
* @lprops: LEB properties to move
* @cat: LEB category
*
* New entries to a heap are added at the bottom and then moved up until the
* parent's value is greater. In the case of LPT's category heaps, the value
* is either the amount of free space or the amount of dirty space, depending
* on the category.
*/
static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
struct ubifs_lprops *lprops, int cat)
{
int val1, val2, hpos;
hpos = lprops->hpos;
if (!hpos)
return; /* Already top of the heap */
val1 = get_heap_comp_val(lprops, cat);
/* Compare to parent and, if greater, move up the heap */
do {
int ppos = (hpos - 1) / 2;
val2 = get_heap_comp_val(heap->arr[ppos], cat);
if (val2 >= val1)
return;
/* Greater than parent so move up */
heap->arr[ppos]->hpos = hpos;
heap->arr[hpos] = heap->arr[ppos];
heap->arr[ppos] = lprops;
lprops->hpos = ppos;
hpos = ppos;
} while (hpos);
}
/**
* adjust_lpt_heap - move a changed heap entry up or down the heap.
* @c: UBIFS file-system description object
* @heap: LEB category heap
* @lprops: LEB properties to move
* @hpos: heap position of @lprops
* @cat: LEB category
*
* Changed entries in a heap are moved up or down until the parent's value is
* greater. In the case of LPT's category heaps, the value is either the amount
* of free space or the amount of dirty space, depending on the category.
*/
static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
struct ubifs_lprops *lprops, int hpos, int cat)
{
int val1, val2, val3, cpos;
val1 = get_heap_comp_val(lprops, cat);
/* Compare to parent and, if greater than parent, move up the heap */
if (hpos) {
int ppos = (hpos - 1) / 2;
val2 = get_heap_comp_val(heap->arr[ppos], cat);
if (val1 > val2) {
/* Greater than parent so move up */
while (1) {
heap->arr[ppos]->hpos = hpos;
heap->arr[hpos] = heap->arr[ppos];
heap->arr[ppos] = lprops;
lprops->hpos = ppos;
hpos = ppos;
if (!hpos)
return;
ppos = (hpos - 1) / 2;
val2 = get_heap_comp_val(heap->arr[ppos], cat);
if (val1 <= val2)
return;
/* Still greater than parent so keep going */
}
}
}
/* Not greater than parent, so compare to children */
while (1) {
/* Compare to left child */
cpos = hpos * 2 + 1;
if (cpos >= heap->cnt)
return;
val2 = get_heap_comp_val(heap->arr[cpos], cat);
if (val1 < val2) {
/* Less than left child, so promote biggest child */
if (cpos + 1 < heap->cnt) {
val3 = get_heap_comp_val(heap->arr[cpos + 1],
cat);
if (val3 > val2)
cpos += 1; /* Right child is bigger */
}
heap->arr[cpos]->hpos = hpos;
heap->arr[hpos] = heap->arr[cpos];
heap->arr[cpos] = lprops;
lprops->hpos = cpos;
hpos = cpos;
continue;
}
/* Compare to right child */
cpos += 1;
if (cpos >= heap->cnt)
return;
val3 = get_heap_comp_val(heap->arr[cpos], cat);
if (val1 < val3) {
/* Less than right child, so promote right child */
heap->arr[cpos]->hpos = hpos;
heap->arr[hpos] = heap->arr[cpos];
heap->arr[cpos] = lprops;
lprops->hpos = cpos;
hpos = cpos;
continue;
}
return;
}
}
/**
* add_to_lpt_heap - add LEB properties to a LEB category heap.
* @c: UBIFS file-system description object
* @lprops: LEB properties to add
* @cat: LEB category
*
* This function returns %1 if @lprops is added to the heap for LEB category
* @cat, otherwise %0 is returned because the heap is full.
*/
static int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops,
int cat)
{
struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
if (heap->cnt >= heap->max_cnt) {
const int b = LPT_HEAP_SZ / 2 - 1;
int cpos, val1, val2;
/* Compare to some other LEB on the bottom of heap */
/* Pick a position kind of randomly */
cpos = (((size_t)lprops >> 4) & b) + b;
ubifs_assert(cpos >= b);
ubifs_assert(cpos < LPT_HEAP_SZ);
ubifs_assert(cpos < heap->cnt);
val1 = get_heap_comp_val(lprops, cat);
val2 = get_heap_comp_val(heap->arr[cpos], cat);
if (val1 > val2) {
struct ubifs_lprops *lp;
lp = heap->arr[cpos];
lp->flags &= ~LPROPS_CAT_MASK;
lp->flags |= LPROPS_UNCAT;
list_add(&lp->list, &c->uncat_list);
lprops->hpos = cpos;
heap->arr[cpos] = lprops;
move_up_lpt_heap(c, heap, lprops, cat);
dbg_check_heap(c, heap, cat, lprops->hpos);
return 1; /* Added to heap */
}
dbg_check_heap(c, heap, cat, -1);
return 0; /* Not added to heap */
} else {
lprops->hpos = heap->cnt++;
heap->arr[lprops->hpos] = lprops;
move_up_lpt_heap(c, heap, lprops, cat);
dbg_check_heap(c, heap, cat, lprops->hpos);
return 1; /* Added to heap */
}
}
/**
* remove_from_lpt_heap - remove LEB properties from a LEB category heap.
* @c: UBIFS file-system description object
* @lprops: LEB properties to remove
* @cat: LEB category
*/
static void remove_from_lpt_heap(struct ubifs_info *c,
struct ubifs_lprops *lprops, int cat)
{
struct ubifs_lpt_heap *heap;
int hpos = lprops->hpos;
heap = &c->lpt_heap[cat - 1];
ubifs_assert(hpos >= 0 && hpos < heap->cnt);
ubifs_assert(heap->arr[hpos] == lprops);
heap->cnt -= 1;
if (hpos < heap->cnt) {
heap->arr[hpos] = heap->arr[heap->cnt];
heap->arr[hpos]->hpos = hpos;
adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat);
}
dbg_check_heap(c, heap, cat, -1);
}
/**
* lpt_heap_replace - replace lprops in a category heap.
* @c: UBIFS file-system description object
* @old_lprops: LEB properties to replace
* @new_lprops: LEB properties with which to replace
* @cat: LEB category
*
* During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
* and the lprops that the pnode contains. When that happens, references in
* the category heaps to those lprops must be updated to point to the new
* lprops. This function does that.
*/
static void lpt_heap_replace(struct ubifs_info *c,
struct ubifs_lprops *old_lprops,
struct ubifs_lprops *new_lprops, int cat)
{
struct ubifs_lpt_heap *heap;
int hpos = new_lprops->hpos;
heap = &c->lpt_heap[cat - 1];
heap->arr[hpos] = new_lprops;
}
/**
* ubifs_add_to_cat - add LEB properties to a category list or heap.
* @c: UBIFS file-system description object
* @lprops: LEB properties to add
* @cat: LEB category to which to add
*
* LEB properties are categorized to enable fast find operations.
*/
void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
int cat)
{
switch (cat) {
case LPROPS_DIRTY:
case LPROPS_DIRTY_IDX:
case LPROPS_FREE:
if (add_to_lpt_heap(c, lprops, cat))
break;
/* No more room on heap so make it uncategorized */
cat = LPROPS_UNCAT;
/* Fall through */
case LPROPS_UNCAT:
list_add(&lprops->list, &c->uncat_list);
break;
case LPROPS_EMPTY:
list_add(&lprops->list, &c->empty_list);
break;
case LPROPS_FREEABLE:
list_add(&lprops->list, &c->freeable_list);
c->freeable_cnt += 1;
break;
case LPROPS_FRDI_IDX:
list_add(&lprops->list, &c->frdi_idx_list);
break;
default:
ubifs_assert(0);
}
lprops->flags &= ~LPROPS_CAT_MASK;
lprops->flags |= cat;
}
/**
* ubifs_remove_from_cat - remove LEB properties from a category list or heap.
* @c: UBIFS file-system description object
* @lprops: LEB properties to remove
* @cat: LEB category from which to remove
*
* LEB properties are categorized to enable fast find operations.
*/
static void ubifs_remove_from_cat(struct ubifs_info *c,
struct ubifs_lprops *lprops, int cat)
{
switch (cat) {
case LPROPS_DIRTY:
case LPROPS_DIRTY_IDX:
case LPROPS_FREE:
remove_from_lpt_heap(c, lprops, cat);
break;
case LPROPS_FREEABLE:
c->freeable_cnt -= 1;
ubifs_assert(c->freeable_cnt >= 0);
/* Fall through */
case LPROPS_UNCAT:
case LPROPS_EMPTY:
case LPROPS_FRDI_IDX:
ubifs_assert(!list_empty(&lprops->list));
list_del(&lprops->list);
break;
default:
ubifs_assert(0);
}
}
/**
* ubifs_replace_cat - replace lprops in a category list or heap.
* @c: UBIFS file-system description object
* @old_lprops: LEB properties to replace
* @new_lprops: LEB properties with which to replace
*
* During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
* and the lprops that the pnode contains. When that happens, references in
* category lists and heaps must be replaced. This function does that.
*/
void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops,
struct ubifs_lprops *new_lprops)
{
int cat;
cat = new_lprops->flags & LPROPS_CAT_MASK;
switch (cat) {
case LPROPS_DIRTY:
case LPROPS_DIRTY_IDX:
case LPROPS_FREE:
lpt_heap_replace(c, old_lprops, new_lprops, cat);
break;
case LPROPS_UNCAT:
case LPROPS_EMPTY:
case LPROPS_FREEABLE:
case LPROPS_FRDI_IDX:
list_replace(&old_lprops->list, &new_lprops->list);
break;
default:
ubifs_assert(0);
}
}
/**
* ubifs_ensure_cat - ensure LEB properties are categorized.
* @c: UBIFS file-system description object
* @lprops: LEB properties
*
* A LEB may have fallen off of the bottom of a heap, and ended up as
* uncategorized even though it has enough space for us now. If that is the case
* this function will put the LEB back onto a heap.
*/
void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops)
{
int cat = lprops->flags & LPROPS_CAT_MASK;
if (cat != LPROPS_UNCAT)
return;
cat = ubifs_categorize_lprops(c, lprops);
if (cat == LPROPS_UNCAT)
return;
ubifs_remove_from_cat(c, lprops, LPROPS_UNCAT);
ubifs_add_to_cat(c, lprops, cat);
}
/**
* ubifs_categorize_lprops - categorize LEB properties.
* @c: UBIFS file-system description object
* @lprops: LEB properties to categorize
*
* LEB properties are categorized to enable fast find operations. This function
* returns the LEB category to which the LEB properties belong. Note however
* that if the LEB category is stored as a heap and the heap is full, the
* LEB properties may have their category changed to %LPROPS_UNCAT.
*/
int ubifs_categorize_lprops(const struct ubifs_info *c,
const struct ubifs_lprops *lprops)
{
if (lprops->flags & LPROPS_TAKEN)
return LPROPS_UNCAT;
if (lprops->free == c->leb_size) {
ubifs_assert(!(lprops->flags & LPROPS_INDEX));
return LPROPS_EMPTY;
}
if (lprops->free + lprops->dirty == c->leb_size) {
if (lprops->flags & LPROPS_INDEX)
return LPROPS_FRDI_IDX;
else
return LPROPS_FREEABLE;
}
if (lprops->flags & LPROPS_INDEX) {
if (lprops->dirty + lprops->free >= c->min_idx_node_sz)
return LPROPS_DIRTY_IDX;
} else {
if (lprops->dirty >= c->dead_wm &&
lprops->dirty > lprops->free)
return LPROPS_DIRTY;
if (lprops->free > 0)
return LPROPS_FREE;
}
return LPROPS_UNCAT;
}
/**
* change_category - change LEB properties category.
* @c: UBIFS file-system description object
* @lprops: LEB properties to recategorize
*
* LEB properties are categorized to enable fast find operations. When the LEB
* properties change they must be recategorized.
*/
static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
{
int old_cat = lprops->flags & LPROPS_CAT_MASK;
int new_cat = ubifs_categorize_lprops(c, lprops);
if (old_cat == new_cat) {
struct ubifs_lpt_heap *heap = &c->lpt_heap[new_cat - 1];
/* lprops on a heap now must be moved up or down */
if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT)
return; /* Not on a heap */
heap = &c->lpt_heap[new_cat - 1];
adjust_lpt_heap(c, heap, lprops, lprops->hpos, new_cat);
} else {
ubifs_remove_from_cat(c, lprops, old_cat);
ubifs_add_to_cat(c, lprops, new_cat);
}
}
/**
* calc_dark - calculate LEB dark space size.
* @c: the UBIFS file-system description object
* @spc: amount of free and dirty space in the LEB
*
* This function calculates amount of dark space in an LEB which has @spc bytes
* of free and dirty space. Returns the calculations result.
*
* Dark space is the space which is not always usable - it depends on which
* nodes are written in which order. E.g., if an LEB has only 512 free bytes,
* it is dark space, because it cannot fit a large data node. So UBIFS cannot
* count on this LEB and treat these 512 bytes as usable because it is not true
* if, for example, only big chunks of uncompressible data will be written to
* the FS.
*/
static int calc_dark(struct ubifs_info *c, int spc)
{
ubifs_assert(!(spc & 7));
if (spc < c->dark_wm)
return spc;
/*
* If we have slightly more space then the dark space watermark, we can
* anyway safely assume it we'll be able to write a node of the
* smallest size there.
*/
if (spc - c->dark_wm < MIN_WRITE_SZ)
return spc - MIN_WRITE_SZ;
return c->dark_wm;
}
/**
* is_lprops_dirty - determine if LEB properties are dirty.
* @c: the UBIFS file-system description object
* @lprops: LEB properties to test
*/
static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops)
{
struct ubifs_pnode *pnode;
int pos;
pos = (lprops->lnum - c->main_first) & (UBIFS_LPT_FANOUT - 1);
pnode = (struct ubifs_pnode *)container_of(lprops - pos,
struct ubifs_pnode,
lprops[0]);
return !test_bit(COW_ZNODE, &pnode->flags) &&
test_bit(DIRTY_CNODE, &pnode->flags);
}
/**
* ubifs_change_lp - change LEB properties.
* @c: the UBIFS file-system description object
* @lp: LEB properties to change
* @free: new free space amount
* @dirty: new dirty space amount
* @flags: new flags
* @idx_gc_cnt: change to the count of idx_gc list
*
* This function changes LEB properties (@free, @dirty or @flag). However, the
* property which has the %LPROPS_NC value is not changed. Returns a pointer to
* the updated LEB properties on success and a negative error code on failure.
*
* Note, the LEB properties may have had to be copied (due to COW) and
* consequently the pointer returned may not be the same as the pointer
* passed.
*/
const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
const struct ubifs_lprops *lp,
int free, int dirty, int flags,
int idx_gc_cnt)
{
/*
* This is the only function that is allowed to change lprops, so we
* discard the const qualifier.
*/
struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp;
dbg_lp("LEB %d, free %d, dirty %d, flags %d",
lprops->lnum, free, dirty, flags);
ubifs_assert(mutex_is_locked(&c->lp_mutex));
ubifs_assert(c->lst.empty_lebs >= 0 &&
c->lst.empty_lebs <= c->main_lebs);
ubifs_assert(c->freeable_cnt >= 0);
ubifs_assert(c->freeable_cnt <= c->main_lebs);
ubifs_assert(c->lst.taken_empty_lebs >= 0);
ubifs_assert(c->lst.taken_empty_lebs <= c->lst.empty_lebs);
ubifs_assert(!(c->lst.total_free & 7) && !(c->lst.total_dirty & 7));
ubifs_assert(!(c->lst.total_dead & 7) && !(c->lst.total_dark & 7));
ubifs_assert(!(c->lst.total_used & 7));
ubifs_assert(free == LPROPS_NC || free >= 0);
ubifs_assert(dirty == LPROPS_NC || dirty >= 0);
if (!is_lprops_dirty(c, lprops)) {
lprops = ubifs_lpt_lookup_dirty(c, lprops->lnum);
if (IS_ERR(lprops))
return lprops;
} else
ubifs_assert(lprops == ubifs_lpt_lookup_dirty(c, lprops->lnum));
ubifs_assert(!(lprops->free & 7) && !(lprops->dirty & 7));
spin_lock(&c->space_lock);
if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
c->lst.taken_empty_lebs -= 1;
if (!(lprops->flags & LPROPS_INDEX)) {
int old_spc;
old_spc = lprops->free + lprops->dirty;
if (old_spc < c->dead_wm)
c->lst.total_dead -= old_spc;
else
c->lst.total_dark -= calc_dark(c, old_spc);
c->lst.total_used -= c->leb_size - old_spc;
}
if (free != LPROPS_NC) {
free = ALIGN(free, 8);
c->lst.total_free += free - lprops->free;
/* Increase or decrease empty LEBs counter if needed */
if (free == c->leb_size) {
if (lprops->free != c->leb_size)
c->lst.empty_lebs += 1;
} else if (lprops->free == c->leb_size)
c->lst.empty_lebs -= 1;
lprops->free = free;
}
if (dirty != LPROPS_NC) {
dirty = ALIGN(dirty, 8);
c->lst.total_dirty += dirty - lprops->dirty;
lprops->dirty = dirty;
}
if (flags != LPROPS_NC) {
/* Take care about indexing LEBs counter if needed */
if ((lprops->flags & LPROPS_INDEX)) {
if (!(flags & LPROPS_INDEX))
c->lst.idx_lebs -= 1;
} else if (flags & LPROPS_INDEX)
c->lst.idx_lebs += 1;
lprops->flags = flags;
}
if (!(lprops->flags & LPROPS_INDEX)) {
int new_spc;
new_spc = lprops->free + lprops->dirty;
if (new_spc < c->dead_wm)
c->lst.total_dead += new_spc;
else
c->lst.total_dark += calc_dark(c, new_spc);
c->lst.total_used += c->leb_size - new_spc;
}
if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
c->lst.taken_empty_lebs += 1;
change_category(c, lprops);
c->idx_gc_cnt += idx_gc_cnt;
spin_unlock(&c->space_lock);
return lprops;
}
/**
* ubifs_get_lp_stats - get lprops statistics.
* @c: UBIFS file-system description object
* @st: return statistics
*/
void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst)
{
spin_lock(&c->space_lock);
memcpy(lst, &c->lst, sizeof(struct ubifs_lp_stats));
spin_unlock(&c->space_lock);
}
/**
* ubifs_change_one_lp - change LEB properties.
* @c: the UBIFS file-system description object
* @lnum: LEB to change properties for
* @free: amount of free space
* @dirty: amount of dirty space
* @flags_set: flags to set
* @flags_clean: flags to clean
* @idx_gc_cnt: change to the count of idx_gc list
*
* This function changes properties of LEB @lnum. It is a helper wrapper over
* 'ubifs_change_lp()' which hides lprops get/release. The arguments are the
* same as in case of 'ubifs_change_lp()'. Returns zero in case of success and
* a negative error code in case of failure.
*/
int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
int flags_set, int flags_clean, int idx_gc_cnt)
{
int err = 0, flags;
const struct ubifs_lprops *lp;
ubifs_get_lprops(c);
lp = ubifs_lpt_lookup_dirty(c, lnum);
if (IS_ERR(lp)) {
err = PTR_ERR(lp);
goto out;
}
flags = (lp->flags | flags_set) & ~flags_clean;
lp = ubifs_change_lp(c, lp, free, dirty, flags, idx_gc_cnt);
if (IS_ERR(lp))
err = PTR_ERR(lp);
out:
ubifs_release_lprops(c);
return err;
}
/**
* ubifs_update_one_lp - update LEB properties.
* @c: the UBIFS file-system description object
* @lnum: LEB to change properties for
* @free: amount of free space
* @dirty: amount of dirty space to add
* @flags_set: flags to set
* @flags_clean: flags to clean
*
* This function is the same as 'ubifs_change_one_lp()' but @dirty is added to
* current dirty space, not substitutes it.
*/
int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
int flags_set, int flags_clean)
{
int err = 0, flags;
const struct ubifs_lprops *lp;
ubifs_get_lprops(c);
lp = ubifs_lpt_lookup_dirty(c, lnum);
if (IS_ERR(lp)) {
err = PTR_ERR(lp);
goto out;
}
flags = (lp->flags | flags_set) & ~flags_clean;
lp = ubifs_change_lp(c, lp, free, lp->dirty + dirty, flags, 0);
if (IS_ERR(lp))
err = PTR_ERR(lp);
out:
ubifs_release_lprops(c);
return err;
}
/**
* ubifs_read_one_lp - read LEB properties.
* @c: the UBIFS file-system description object
* @lnum: LEB to read properties for
* @lp: where to store read properties
*
* This helper function reads properties of a LEB @lnum and stores them in @lp.
* Returns zero in case of success and a negative error code in case of
* failure.
*/
int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp)
{
int err = 0;
const struct ubifs_lprops *lpp;
ubifs_get_lprops(c);
lpp = ubifs_lpt_lookup(c, lnum);
if (IS_ERR(lpp)) {
err = PTR_ERR(lpp);
goto out;
}
memcpy(lp, lpp, sizeof(struct ubifs_lprops));
out:
ubifs_release_lprops(c);
return err;
}
/**
* ubifs_fast_find_free - try to find a LEB with free space quickly.
* @c: the UBIFS file-system description object
*
* This function returns LEB properties for a LEB with free space or %NULL if
* the function is unable to find a LEB quickly.
*/
const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c)
{
struct ubifs_lprops *lprops;
struct ubifs_lpt_heap *heap;
ubifs_assert(mutex_is_locked(&c->lp_mutex));
heap = &c->lpt_heap[LPROPS_FREE - 1];
if (heap->cnt == 0)
return NULL;
lprops = heap->arr[0];
ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
ubifs_assert(!(lprops->flags & LPROPS_INDEX));
return lprops;
}
/**
* ubifs_fast_find_empty - try to find an empty LEB quickly.
* @c: the UBIFS file-system description object
*
* This function returns LEB properties for an empty LEB or %NULL if the
* function is unable to find an empty LEB quickly.
*/
const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c)
{
struct ubifs_lprops *lprops;
ubifs_assert(mutex_is_locked(&c->lp_mutex));
if (list_empty(&c->empty_list))
return NULL;
lprops = list_entry(c->empty_list.next, struct ubifs_lprops, list);
ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
ubifs_assert(!(lprops->flags & LPROPS_INDEX));
ubifs_assert(lprops->free == c->leb_size);
return lprops;
}
/**
* ubifs_fast_find_freeable - try to find a freeable LEB quickly.
* @c: the UBIFS file-system description object
*
* This function returns LEB properties for a freeable LEB or %NULL if the
* function is unable to find a freeable LEB quickly.
*/
const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c)
{
struct ubifs_lprops *lprops;
ubifs_assert(mutex_is_locked(&c->lp_mutex));
if (list_empty(&c->freeable_list))
return NULL;
lprops = list_entry(c->freeable_list.next, struct ubifs_lprops, list);
ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
ubifs_assert(!(lprops->flags & LPROPS_INDEX));
ubifs_assert(lprops->free + lprops->dirty == c->leb_size);
ubifs_assert(c->freeable_cnt > 0);
return lprops;
}
/**
* ubifs_fast_find_frdi_idx - try to find a freeable index LEB quickly.
* @c: the UBIFS file-system description object
*
* This function returns LEB properties for a freeable index LEB or %NULL if the
* function is unable to find a freeable index LEB quickly.
*/
const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c)
{
struct ubifs_lprops *lprops;
ubifs_assert(mutex_is_locked(&c->lp_mutex));
if (list_empty(&c->frdi_idx_list))
return NULL;
lprops = list_entry(c->frdi_idx_list.next, struct ubifs_lprops, list);
ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
ubifs_assert((lprops->flags & LPROPS_INDEX));
ubifs_assert(lprops->free + lprops->dirty == c->leb_size);
return lprops;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,171 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Adrian Hunter
* Artem Bityutskiy (Битюцкий Артём)
*/
/*
* This file implements commit-related functionality of the LEB properties
* subsystem.
*/
#include "crc16.h"
#include "ubifs.h"
/**
* free_obsolete_cnodes - free obsolete cnodes for commit end.
* @c: UBIFS file-system description object
*/
static void free_obsolete_cnodes(struct ubifs_info *c)
{
struct ubifs_cnode *cnode, *cnext;
cnext = c->lpt_cnext;
if (!cnext)
return;
do {
cnode = cnext;
cnext = cnode->cnext;
if (test_bit(OBSOLETE_CNODE, &cnode->flags))
kfree(cnode);
else
cnode->cnext = NULL;
} while (cnext != c->lpt_cnext);
c->lpt_cnext = NULL;
}
/**
* first_nnode - find the first nnode in memory.
* @c: UBIFS file-system description object
* @hght: height of tree where nnode found is returned here
*
* This function returns a pointer to the nnode found or %NULL if no nnode is
* found. This function is a helper to 'ubifs_lpt_free()'.
*/
static struct ubifs_nnode *first_nnode(struct ubifs_info *c, int *hght)
{
struct ubifs_nnode *nnode;
int h, i, found;
nnode = c->nroot;
*hght = 0;
if (!nnode)
return NULL;
for (h = 1; h < c->lpt_hght; h++) {
found = 0;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
if (nnode->nbranch[i].nnode) {
found = 1;
nnode = nnode->nbranch[i].nnode;
*hght = h;
break;
}
}
if (!found)
break;
}
return nnode;
}
/**
* next_nnode - find the next nnode in memory.
* @c: UBIFS file-system description object
* @nnode: nnode from which to start.
* @hght: height of tree where nnode is, is passed and returned here
*
* This function returns a pointer to the nnode found or %NULL if no nnode is
* found. This function is a helper to 'ubifs_lpt_free()'.
*/
static struct ubifs_nnode *next_nnode(struct ubifs_info *c,
struct ubifs_nnode *nnode, int *hght)
{
struct ubifs_nnode *parent;
int iip, h, i, found;
parent = nnode->parent;
if (!parent)
return NULL;
if (nnode->iip == UBIFS_LPT_FANOUT - 1) {
*hght -= 1;
return parent;
}
for (iip = nnode->iip + 1; iip < UBIFS_LPT_FANOUT; iip++) {
nnode = parent->nbranch[iip].nnode;
if (nnode)
break;
}
if (!nnode) {
*hght -= 1;
return parent;
}
for (h = *hght + 1; h < c->lpt_hght; h++) {
found = 0;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
if (nnode->nbranch[i].nnode) {
found = 1;
nnode = nnode->nbranch[i].nnode;
*hght = h;
break;
}
}
if (!found)
break;
}
return nnode;
}
/**
* ubifs_lpt_free - free resources owned by the LPT.
* @c: UBIFS file-system description object
* @wr_only: free only resources used for writing
*/
void ubifs_lpt_free(struct ubifs_info *c, int wr_only)
{
struct ubifs_nnode *nnode;
int i, hght;
/* Free write-only things first */
free_obsolete_cnodes(c); /* Leftover from a failed commit */
vfree(c->ltab_cmt);
c->ltab_cmt = NULL;
vfree(c->lpt_buf);
c->lpt_buf = NULL;
kfree(c->lsave);
c->lsave = NULL;
if (wr_only)
return;
/* Now free the rest */
nnode = first_nnode(c, &hght);
while (nnode) {
for (i = 0; i < UBIFS_LPT_FANOUT; i++)
kfree(nnode->nbranch[i].nnode);
nnode = next_nnode(c, nnode, &hght);
}
for (i = 0; i < LPROPS_HEAP_CNT; i++)
kfree(c->lpt_heap[i].arr);
kfree(c->dirty_idx.arr);
kfree(c->nroot);
vfree(c->ltab);
kfree(c->lpt_nod_buf);
}

View file

@ -0,0 +1,341 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
/* This file implements reading and writing the master node */
#include "ubifs.h"
/**
* scan_for_master - search the valid master node.
* @c: UBIFS file-system description object
*
* This function scans the master node LEBs and search for the latest master
* node. Returns zero in case of success and a negative error code in case of
* failure.
*/
static int scan_for_master(struct ubifs_info *c)
{
struct ubifs_scan_leb *sleb;
struct ubifs_scan_node *snod;
int lnum, offs = 0, nodes_cnt;
lnum = UBIFS_MST_LNUM;
sleb = ubifs_scan(c, lnum, 0, c->sbuf);
if (IS_ERR(sleb))
return PTR_ERR(sleb);
nodes_cnt = sleb->nodes_cnt;
if (nodes_cnt > 0) {
snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
list);
if (snod->type != UBIFS_MST_NODE)
goto out;
memcpy(c->mst_node, snod->node, snod->len);
offs = snod->offs;
}
ubifs_scan_destroy(sleb);
lnum += 1;
sleb = ubifs_scan(c, lnum, 0, c->sbuf);
if (IS_ERR(sleb))
return PTR_ERR(sleb);
if (sleb->nodes_cnt != nodes_cnt)
goto out;
if (!sleb->nodes_cnt)
goto out;
snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list);
if (snod->type != UBIFS_MST_NODE)
goto out;
if (snod->offs != offs)
goto out;
if (memcmp((void *)c->mst_node + UBIFS_CH_SZ,
(void *)snod->node + UBIFS_CH_SZ,
UBIFS_MST_NODE_SZ - UBIFS_CH_SZ))
goto out;
c->mst_offs = offs;
ubifs_scan_destroy(sleb);
return 0;
out:
ubifs_scan_destroy(sleb);
return -EINVAL;
}
/**
* validate_master - validate master node.
* @c: UBIFS file-system description object
*
* This function validates data which was read from master node. Returns zero
* if the data is all right and %-EINVAL if not.
*/
static int validate_master(const struct ubifs_info *c)
{
long long main_sz;
int err;
if (c->max_sqnum >= SQNUM_WATERMARK) {
err = 1;
goto out;
}
if (c->cmt_no >= c->max_sqnum) {
err = 2;
goto out;
}
if (c->highest_inum >= INUM_WATERMARK) {
err = 3;
goto out;
}
if (c->lhead_lnum < UBIFS_LOG_LNUM ||
c->lhead_lnum >= UBIFS_LOG_LNUM + c->log_lebs ||
c->lhead_offs < 0 || c->lhead_offs >= c->leb_size ||
c->lhead_offs & (c->min_io_size - 1)) {
err = 4;
goto out;
}
if (c->zroot.lnum >= c->leb_cnt || c->zroot.lnum < c->main_first ||
c->zroot.offs >= c->leb_size || c->zroot.offs & 7) {
err = 5;
goto out;
}
if (c->zroot.len < c->ranges[UBIFS_IDX_NODE].min_len ||
c->zroot.len > c->ranges[UBIFS_IDX_NODE].max_len) {
err = 6;
goto out;
}
if (c->gc_lnum >= c->leb_cnt || c->gc_lnum < c->main_first) {
err = 7;
goto out;
}
if (c->ihead_lnum >= c->leb_cnt || c->ihead_lnum < c->main_first ||
c->ihead_offs % c->min_io_size || c->ihead_offs < 0 ||
c->ihead_offs > c->leb_size || c->ihead_offs & 7) {
err = 8;
goto out;
}
main_sz = (long long)c->main_lebs * c->leb_size;
if (c->old_idx_sz & 7 || c->old_idx_sz >= main_sz) {
err = 9;
goto out;
}
if (c->lpt_lnum < c->lpt_first || c->lpt_lnum > c->lpt_last ||
c->lpt_offs < 0 || c->lpt_offs + c->nnode_sz > c->leb_size) {
err = 10;
goto out;
}
if (c->nhead_lnum < c->lpt_first || c->nhead_lnum > c->lpt_last ||
c->nhead_offs < 0 || c->nhead_offs % c->min_io_size ||
c->nhead_offs > c->leb_size) {
err = 11;
goto out;
}
if (c->ltab_lnum < c->lpt_first || c->ltab_lnum > c->lpt_last ||
c->ltab_offs < 0 ||
c->ltab_offs + c->ltab_sz > c->leb_size) {
err = 12;
goto out;
}
if (c->big_lpt && (c->lsave_lnum < c->lpt_first ||
c->lsave_lnum > c->lpt_last || c->lsave_offs < 0 ||
c->lsave_offs + c->lsave_sz > c->leb_size)) {
err = 13;
goto out;
}
if (c->lscan_lnum < c->main_first || c->lscan_lnum >= c->leb_cnt) {
err = 14;
goto out;
}
if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs - 2) {
err = 15;
goto out;
}
if (c->lst.idx_lebs < 0 || c->lst.idx_lebs > c->main_lebs - 1) {
err = 16;
goto out;
}
if (c->lst.total_free < 0 || c->lst.total_free > main_sz ||
c->lst.total_free & 7) {
err = 17;
goto out;
}
if (c->lst.total_dirty < 0 || (c->lst.total_dirty & 7)) {
err = 18;
goto out;
}
if (c->lst.total_used < 0 || (c->lst.total_used & 7)) {
err = 19;
goto out;
}
if (c->lst.total_free + c->lst.total_dirty +
c->lst.total_used > main_sz) {
err = 20;
goto out;
}
if (c->lst.total_dead + c->lst.total_dark +
c->lst.total_used + c->old_idx_sz > main_sz) {
err = 21;
goto out;
}
if (c->lst.total_dead < 0 ||
c->lst.total_dead > c->lst.total_free + c->lst.total_dirty ||
c->lst.total_dead & 7) {
err = 22;
goto out;
}
if (c->lst.total_dark < 0 ||
c->lst.total_dark > c->lst.total_free + c->lst.total_dirty ||
c->lst.total_dark & 7) {
err = 23;
goto out;
}
return 0;
out:
ubifs_err("bad master node at offset %d error %d", c->mst_offs, err);
dbg_dump_node(c, c->mst_node);
return -EINVAL;
}
/**
* ubifs_read_master - read master node.
* @c: UBIFS file-system description object
*
* This function finds and reads the master node during file-system mount. If
* the flash is empty, it creates default master node as well. Returns zero in
* case of success and a negative error code in case of failure.
*/
int ubifs_read_master(struct ubifs_info *c)
{
int err, old_leb_cnt;
c->mst_node = kzalloc(c->mst_node_alsz, GFP_KERNEL);
if (!c->mst_node)
return -ENOMEM;
err = scan_for_master(c);
if (err) {
err = ubifs_recover_master_node(c);
if (err)
/*
* Note, we do not free 'c->mst_node' here because the
* unmount routine will take care of this.
*/
return err;
}
/* Make sure that the recovery flag is clear */
c->mst_node->flags &= cpu_to_le32(~UBIFS_MST_RCVRY);
c->max_sqnum = le64_to_cpu(c->mst_node->ch.sqnum);
c->highest_inum = le64_to_cpu(c->mst_node->highest_inum);
c->cmt_no = le64_to_cpu(c->mst_node->cmt_no);
c->zroot.lnum = le32_to_cpu(c->mst_node->root_lnum);
c->zroot.offs = le32_to_cpu(c->mst_node->root_offs);
c->zroot.len = le32_to_cpu(c->mst_node->root_len);
c->lhead_lnum = le32_to_cpu(c->mst_node->log_lnum);
c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum);
c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum);
c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs);
c->old_idx_sz = le64_to_cpu(c->mst_node->index_size);
c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum);
c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs);
c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum);
c->nhead_offs = le32_to_cpu(c->mst_node->nhead_offs);
c->ltab_lnum = le32_to_cpu(c->mst_node->ltab_lnum);
c->ltab_offs = le32_to_cpu(c->mst_node->ltab_offs);
c->lsave_lnum = le32_to_cpu(c->mst_node->lsave_lnum);
c->lsave_offs = le32_to_cpu(c->mst_node->lsave_offs);
c->lscan_lnum = le32_to_cpu(c->mst_node->lscan_lnum);
c->lst.empty_lebs = le32_to_cpu(c->mst_node->empty_lebs);
c->lst.idx_lebs = le32_to_cpu(c->mst_node->idx_lebs);
old_leb_cnt = le32_to_cpu(c->mst_node->leb_cnt);
c->lst.total_free = le64_to_cpu(c->mst_node->total_free);
c->lst.total_dirty = le64_to_cpu(c->mst_node->total_dirty);
c->lst.total_used = le64_to_cpu(c->mst_node->total_used);
c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead);
c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark);
c->calc_idx_sz = c->old_idx_sz;
if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
c->no_orphs = 1;
if (old_leb_cnt != c->leb_cnt) {
/* The file system has been resized */
int growth = c->leb_cnt - old_leb_cnt;
if (c->leb_cnt < old_leb_cnt ||
c->leb_cnt < UBIFS_MIN_LEB_CNT) {
ubifs_err("bad leb_cnt on master node");
dbg_dump_node(c, c->mst_node);
return -EINVAL;
}
dbg_mnt("Auto resizing (master) from %d LEBs to %d LEBs",
old_leb_cnt, c->leb_cnt);
c->lst.empty_lebs += growth;
c->lst.total_free += growth * (long long)c->leb_size;
c->lst.total_dark += growth * (long long)c->dark_wm;
/*
* Reflect changes back onto the master node. N.B. the master
* node gets written immediately whenever mounting (or
* remounting) in read-write mode, so we do not need to write it
* here.
*/
c->mst_node->leb_cnt = cpu_to_le32(c->leb_cnt);
c->mst_node->empty_lebs = cpu_to_le32(c->lst.empty_lebs);
c->mst_node->total_free = cpu_to_le64(c->lst.total_free);
c->mst_node->total_dark = cpu_to_le64(c->lst.total_dark);
}
err = validate_master(c);
if (err)
return err;
err = dbg_old_index_check_init(c, &c->zroot);
return err;
}

View file

@ -0,0 +1,311 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
/*
* This file contains miscellaneous helper functions.
*/
#ifndef __UBIFS_MISC_H__
#define __UBIFS_MISC_H__
/**
* ubifs_zn_dirty - check if znode is dirty.
* @znode: znode to check
*
* This helper function returns %1 if @znode is dirty and %0 otherwise.
*/
static inline int ubifs_zn_dirty(const struct ubifs_znode *znode)
{
return !!test_bit(DIRTY_ZNODE, &znode->flags);
}
/**
* ubifs_wake_up_bgt - wake up background thread.
* @c: UBIFS file-system description object
*/
static inline void ubifs_wake_up_bgt(struct ubifs_info *c)
{
if (c->bgt && !c->need_bgt) {
c->need_bgt = 1;
wake_up_process(c->bgt);
}
}
/**
* ubifs_tnc_find_child - find next child in znode.
* @znode: znode to search at
* @start: the zbranch index to start at
*
* This helper function looks for znode child starting at index @start. Returns
* the child or %NULL if no children were found.
*/
static inline struct ubifs_znode *
ubifs_tnc_find_child(struct ubifs_znode *znode, int start)
{
while (start < znode->child_cnt) {
if (znode->zbranch[start].znode)
return znode->zbranch[start].znode;
start += 1;
}
return NULL;
}
/**
* ubifs_inode - get UBIFS inode information by VFS 'struct inode' object.
* @inode: the VFS 'struct inode' pointer
*/
static inline struct ubifs_inode *ubifs_inode(const struct inode *inode)
{
return container_of(inode, struct ubifs_inode, vfs_inode);
}
/**
* ubifs_compr_present - check if compressor was compiled in.
* @compr_type: compressor type to check
*
* This function returns %1 of compressor of type @compr_type is present, and
* %0 if not.
*/
static inline int ubifs_compr_present(int compr_type)
{
ubifs_assert(compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT);
return !!ubifs_compressors[compr_type]->capi_name;
}
/**
* ubifs_compr_name - get compressor name string by its type.
* @compr_type: compressor type
*
* This function returns compressor type string.
*/
static inline const char *ubifs_compr_name(int compr_type)
{
ubifs_assert(compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT);
return ubifs_compressors[compr_type]->name;
}
/**
* ubifs_wbuf_sync - synchronize write-buffer.
* @wbuf: write-buffer to synchronize
*
* This is the same as as 'ubifs_wbuf_sync_nolock()' but it does not assume
* that the write-buffer is already locked.
*/
static inline int ubifs_wbuf_sync(struct ubifs_wbuf *wbuf)
{
int err;
mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
err = ubifs_wbuf_sync_nolock(wbuf);
mutex_unlock(&wbuf->io_mutex);
return err;
}
/**
* ubifs_leb_unmap - unmap an LEB.
* @c: UBIFS file-system description object
* @lnum: LEB number to unmap
*
* This function returns %0 on success and a negative error code on failure.
*/
static inline int ubifs_leb_unmap(const struct ubifs_info *c, int lnum)
{
int err;
if (c->ro_media)
return -EROFS;
err = ubi_leb_unmap(c->ubi, lnum);
if (err) {
ubifs_err("unmap LEB %d failed, error %d", lnum, err);
return err;
}
return 0;
}
/**
* ubifs_leb_write - write to a LEB.
* @c: UBIFS file-system description object
* @lnum: LEB number to write
* @buf: buffer to write from
* @offs: offset within LEB to write to
* @len: length to write
* @dtype: data type
*
* This function returns %0 on success and a negative error code on failure.
*/
static inline int ubifs_leb_write(const struct ubifs_info *c, int lnum,
const void *buf, int offs, int len, int dtype)
{
int err;
if (c->ro_media)
return -EROFS;
err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype);
if (err) {
ubifs_err("writing %d bytes at %d:%d, error %d",
len, lnum, offs, err);
return err;
}
return 0;
}
/**
* ubifs_leb_change - atomic LEB change.
* @c: UBIFS file-system description object
* @lnum: LEB number to write
* @buf: buffer to write from
* @len: length to write
* @dtype: data type
*
* This function returns %0 on success and a negative error code on failure.
*/
static inline int ubifs_leb_change(const struct ubifs_info *c, int lnum,
const void *buf, int len, int dtype)
{
int err;
if (c->ro_media)
return -EROFS;
err = ubi_leb_change(c->ubi, lnum, buf, len, dtype);
if (err) {
ubifs_err("changing %d bytes in LEB %d, error %d",
len, lnum, err);
return err;
}
return 0;
}
/**
* ubifs_add_dirt - add dirty space to LEB properties.
* @c: the UBIFS file-system description object
* @lnum: LEB to add dirty space for
* @dirty: dirty space to add
*
* This is a helper function which increased amount of dirty LEB space. Returns
* zero in case of success and a negative error code in case of failure.
*/
static inline int ubifs_add_dirt(struct ubifs_info *c, int lnum, int dirty)
{
return ubifs_update_one_lp(c, lnum, LPROPS_NC, dirty, 0, 0);
}
/**
* ubifs_return_leb - return LEB to lprops.
* @c: the UBIFS file-system description object
* @lnum: LEB to return
*
* This helper function cleans the "taken" flag of a logical eraseblock in the
* lprops. Returns zero in case of success and a negative error code in case of
* failure.
*/
static inline int ubifs_return_leb(struct ubifs_info *c, int lnum)
{
return ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
LPROPS_TAKEN, 0);
}
/**
* ubifs_idx_node_sz - return index node size.
* @c: the UBIFS file-system description object
* @child_cnt: number of children of this index node
*/
static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt)
{
return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt;
}
/**
* ubifs_idx_branch - return pointer to an index branch.
* @c: the UBIFS file-system description object
* @idx: index node
* @bnum: branch number
*/
static inline
struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
const struct ubifs_idx_node *idx,
int bnum)
{
return (struct ubifs_branch *)((void *)idx->branches +
(UBIFS_BRANCH_SZ + c->key_len) * bnum);
}
/**
* ubifs_idx_key - return pointer to an index key.
* @c: the UBIFS file-system description object
* @idx: index node
*/
static inline void *ubifs_idx_key(const struct ubifs_info *c,
const struct ubifs_idx_node *idx)
{
const __u8 *branch = idx->branches;
return (void *)((struct ubifs_branch *)branch)->key;
}
/**
* ubifs_tnc_lookup - look up a file-system node.
* @c: UBIFS file-system description object
* @key: node key to lookup
* @node: the node is returned here
*
* This function look up and reads node with key @key. The caller has to make
* sure the @node buffer is large enough to fit the node. Returns zero in case
* of success, %-ENOENT if the node was not found, and a negative error code in
* case of failure.
*/
static inline int ubifs_tnc_lookup(struct ubifs_info *c,
const union ubifs_key *key, void *node)
{
return ubifs_tnc_locate(c, key, node, NULL, NULL);
}
/**
* ubifs_get_lprops - get reference to LEB properties.
* @c: the UBIFS file-system description object
*
* This function locks lprops. Lprops have to be unlocked by
* 'ubifs_release_lprops()'.
*/
static inline void ubifs_get_lprops(struct ubifs_info *c)
{
mutex_lock(&c->lp_mutex);
}
/**
* ubifs_release_lprops - release lprops lock.
* @c: the UBIFS file-system description object
*
* This function has to be called after each 'ubifs_get_lprops()' call to
* unlock lprops.
*/
static inline void ubifs_release_lprops(struct ubifs_info *c)
{
ubifs_assert(mutex_is_locked(&c->lp_mutex));
ubifs_assert(c->lst.empty_lebs >= 0 &&
c->lst.empty_lebs <= c->main_lebs);
mutex_unlock(&c->lp_mutex);
}
#endif /* __UBIFS_MISC_H__ */

View file

@ -0,0 +1,316 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Author: Adrian Hunter
*/
#include "ubifs.h"
/*
* An orphan is an inode number whose inode node has been committed to the index
* with a link count of zero. That happens when an open file is deleted
* (unlinked) and then a commit is run. In the normal course of events the inode
* would be deleted when the file is closed. However in the case of an unclean
* unmount, orphans need to be accounted for. After an unclean unmount, the
* orphans' inodes must be deleted which means either scanning the entire index
* looking for them, or keeping a list on flash somewhere. This unit implements
* the latter approach.
*
* The orphan area is a fixed number of LEBs situated between the LPT area and
* the main area. The number of orphan area LEBs is specified when the file
* system is created. The minimum number is 1. The size of the orphan area
* should be so that it can hold the maximum number of orphans that are expected
* to ever exist at one time.
*
* The number of orphans that can fit in a LEB is:
*
* (c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64)
*
* For example: a 15872 byte LEB can fit 1980 orphans so 1 LEB may be enough.
*
* Orphans are accumulated in a rb-tree. When an inode's link count drops to
* zero, the inode number is added to the rb-tree. It is removed from the tree
* when the inode is deleted. Any new orphans that are in the orphan tree when
* the commit is run, are written to the orphan area in 1 or more orphan nodes.
* If the orphan area is full, it is consolidated to make space. There is
* always enough space because validation prevents the user from creating more
* than the maximum number of orphans allowed.
*/
/**
* tot_avail_orphs - calculate total space.
* @c: UBIFS file-system description object
*
* This function returns the number of orphans that can be written in half
* the total space. That leaves half the space for adding new orphans.
*/
static int tot_avail_orphs(struct ubifs_info *c)
{
int avail_lebs, avail;
avail_lebs = c->orph_lebs;
avail = avail_lebs *
((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64));
return avail / 2;
}
/**
* ubifs_clear_orphans - erase all LEBs used for orphans.
* @c: UBIFS file-system description object
*
* If recovery is not required, then the orphans from the previous session
* are not needed. This function locates the LEBs used to record
* orphans, and un-maps them.
*/
int ubifs_clear_orphans(struct ubifs_info *c)
{
int lnum, err;
for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
err = ubifs_leb_unmap(c, lnum);
if (err)
return err;
}
c->ohead_lnum = c->orph_first;
c->ohead_offs = 0;
return 0;
}
/**
* insert_dead_orphan - insert an orphan.
* @c: UBIFS file-system description object
* @inum: orphan inode number
*
* This function is a helper to the 'do_kill_orphans()' function. The orphan
* must be kept until the next commit, so it is added to the rb-tree and the
* deletion list.
*/
static int insert_dead_orphan(struct ubifs_info *c, ino_t inum)
{
struct ubifs_orphan *orphan, *o;
struct rb_node **p, *parent = NULL;
orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_KERNEL);
if (!orphan)
return -ENOMEM;
orphan->inum = inum;
p = &c->orph_tree.rb_node;
while (*p) {
parent = *p;
o = rb_entry(parent, struct ubifs_orphan, rb);
if (inum < o->inum)
p = &(*p)->rb_left;
else if (inum > o->inum)
p = &(*p)->rb_right;
else {
/* Already added - no problem */
kfree(orphan);
return 0;
}
}
c->tot_orphans += 1;
rb_link_node(&orphan->rb, parent, p);
rb_insert_color(&orphan->rb, &c->orph_tree);
list_add_tail(&orphan->list, &c->orph_list);
orphan->dnext = c->orph_dnext;
c->orph_dnext = orphan;
dbg_mnt("ino %lu, new %d, tot %d", (unsigned long)inum,
c->new_orphans, c->tot_orphans);
return 0;
}
/**
* do_kill_orphans - remove orphan inodes from the index.
* @c: UBIFS file-system description object
* @sleb: scanned LEB
* @last_cmt_no: cmt_no of last orphan node read is passed and returned here
* @outofdate: whether the LEB is out of date is returned here
* @last_flagged: whether the end orphan node is encountered
*
* This function is a helper to the 'kill_orphans()' function. It goes through
* every orphan node in a LEB and for every inode number recorded, removes
* all keys for that inode from the TNC.
*/
static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
unsigned long long *last_cmt_no, int *outofdate,
int *last_flagged)
{
struct ubifs_scan_node *snod;
struct ubifs_orph_node *orph;
unsigned long long cmt_no;
ino_t inum;
int i, n, err, first = 1;
list_for_each_entry(snod, &sleb->nodes, list) {
if (snod->type != UBIFS_ORPH_NODE) {
ubifs_err("invalid node type %d in orphan area at "
"%d:%d", snod->type, sleb->lnum, snod->offs);
dbg_dump_node(c, snod->node);
return -EINVAL;
}
orph = snod->node;
/* Check commit number */
cmt_no = le64_to_cpu(orph->cmt_no) & LLONG_MAX;
/*
* The commit number on the master node may be less, because
* of a failed commit. If there are several failed commits in a
* row, the commit number written on orphan nodes will continue
* to increase (because the commit number is adjusted here) even
* though the commit number on the master node stays the same
* because the master node has not been re-written.
*/
if (cmt_no > c->cmt_no)
c->cmt_no = cmt_no;
if (cmt_no < *last_cmt_no && *last_flagged) {
/*
* The last orphan node had a higher commit number and
* was flagged as the last written for that commit
* number. That makes this orphan node, out of date.
*/
if (!first) {
ubifs_err("out of order commit number %llu in "
"orphan node at %d:%d",
cmt_no, sleb->lnum, snod->offs);
dbg_dump_node(c, snod->node);
return -EINVAL;
}
dbg_rcvry("out of date LEB %d", sleb->lnum);
*outofdate = 1;
return 0;
}
if (first)
first = 0;
n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
for (i = 0; i < n; i++) {
inum = le64_to_cpu(orph->inos[i]);
dbg_rcvry("deleting orphaned inode %lu",
(unsigned long)inum);
err = ubifs_tnc_remove_ino(c, inum);
if (err)
return err;
err = insert_dead_orphan(c, inum);
if (err)
return err;
}
*last_cmt_no = cmt_no;
if (le64_to_cpu(orph->cmt_no) & (1ULL << 63)) {
dbg_rcvry("last orph node for commit %llu at %d:%d",
cmt_no, sleb->lnum, snod->offs);
*last_flagged = 1;
} else
*last_flagged = 0;
}
return 0;
}
/**
* kill_orphans - remove all orphan inodes from the index.
* @c: UBIFS file-system description object
*
* If recovery is required, then orphan inodes recorded during the previous
* session (which ended with an unclean unmount) must be deleted from the index.
* This is done by updating the TNC, but since the index is not updated until
* the next commit, the LEBs where the orphan information is recorded are not
* erased until the next commit.
*/
static int kill_orphans(struct ubifs_info *c)
{
unsigned long long last_cmt_no = 0;
int lnum, err = 0, outofdate = 0, last_flagged = 0;
c->ohead_lnum = c->orph_first;
c->ohead_offs = 0;
/* Check no-orphans flag and skip this if no orphans */
if (c->no_orphs) {
dbg_rcvry("no orphans");
return 0;
}
/*
* Orph nodes always start at c->orph_first and are written to each
* successive LEB in turn. Generally unused LEBs will have been unmapped
* but may contain out of date orphan nodes if the unmap didn't go
* through. In addition, the last orphan node written for each commit is
* marked (top bit of orph->cmt_no is set to 1). It is possible that
* there are orphan nodes from the next commit (i.e. the commit did not
* complete successfully). In that case, no orphans will have been lost
* due to the way that orphans are written, and any orphans added will
* be valid orphans anyway and so can be deleted.
*/
for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) {
struct ubifs_scan_leb *sleb;
dbg_rcvry("LEB %d", lnum);
sleb = ubifs_scan(c, lnum, 0, c->sbuf);
if (IS_ERR(sleb)) {
sleb = ubifs_recover_leb(c, lnum, 0, c->sbuf, 0);
if (IS_ERR(sleb)) {
err = PTR_ERR(sleb);
break;
}
}
err = do_kill_orphans(c, sleb, &last_cmt_no, &outofdate,
&last_flagged);
if (err || outofdate) {
ubifs_scan_destroy(sleb);
break;
}
if (sleb->endpt) {
c->ohead_lnum = lnum;
c->ohead_offs = sleb->endpt;
}
ubifs_scan_destroy(sleb);
}
return err;
}
/**
* ubifs_mount_orphans - delete orphan inodes and erase LEBs that recorded them.
* @c: UBIFS file-system description object
* @unclean: indicates recovery from unclean unmount
* @read_only: indicates read only mount
*
* This function is called when mounting to erase orphans from the previous
* session. If UBIFS was not unmounted cleanly, then the inodes recorded as
* orphans are deleted.
*/
int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only)
{
int err = 0;
c->max_orphans = tot_avail_orphs(c);
if (!read_only) {
c->orph_buf = vmalloc(c->leb_size);
if (!c->orph_buf)
return -ENOMEM;
}
if (unclean)
err = kill_orphans(c);
else if (!read_only)
err = ubifs_clear_orphans(c);
return err;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,346 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
/*
* This file implements UBIFS superblock. The superblock is stored at the first
* LEB of the volume and is never changed by UBIFS. Only user-space tools may
* change it. The superblock node mostly contains geometry information.
*/
#include "ubifs.h"
/*
* Default journal size in logical eraseblocks as a percent of total
* flash size.
*/
#define DEFAULT_JNL_PERCENT 5
/* Default maximum journal size in bytes */
#define DEFAULT_MAX_JNL (32*1024*1024)
/* Default indexing tree fanout */
#define DEFAULT_FANOUT 8
/* Default number of data journal heads */
#define DEFAULT_JHEADS_CNT 1
/* Default positions of different LEBs in the main area */
#define DEFAULT_IDX_LEB 0
#define DEFAULT_DATA_LEB 1
#define DEFAULT_GC_LEB 2
/* Default number of LEB numbers in LPT's save table */
#define DEFAULT_LSAVE_CNT 256
/* Default reserved pool size as a percent of maximum free space */
#define DEFAULT_RP_PERCENT 5
/* The default maximum size of reserved pool in bytes */
#define DEFAULT_MAX_RP_SIZE (5*1024*1024)
/* Default time granularity in nanoseconds */
#define DEFAULT_TIME_GRAN 1000000000
/**
* validate_sb - validate superblock node.
* @c: UBIFS file-system description object
* @sup: superblock node
*
* This function validates superblock node @sup. Since most of data was read
* from the superblock and stored in @c, the function validates fields in @c
* instead. Returns zero in case of success and %-EINVAL in case of validation
* failure.
*/
static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
{
long long max_bytes;
int err = 1, min_leb_cnt;
if (!c->key_hash) {
err = 2;
goto failed;
}
if (sup->key_fmt != UBIFS_SIMPLE_KEY_FMT) {
err = 3;
goto failed;
}
if (le32_to_cpu(sup->min_io_size) != c->min_io_size) {
ubifs_err("min. I/O unit mismatch: %d in superblock, %d real",
le32_to_cpu(sup->min_io_size), c->min_io_size);
goto failed;
}
if (le32_to_cpu(sup->leb_size) != c->leb_size) {
ubifs_err("LEB size mismatch: %d in superblock, %d real",
le32_to_cpu(sup->leb_size), c->leb_size);
goto failed;
}
if (c->log_lebs < UBIFS_MIN_LOG_LEBS ||
c->lpt_lebs < UBIFS_MIN_LPT_LEBS ||
c->orph_lebs < UBIFS_MIN_ORPH_LEBS ||
c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
err = 4;
goto failed;
}
/*
* Calculate minimum allowed amount of main area LEBs. This is very
* similar to %UBIFS_MIN_LEB_CNT, but we take into account real what we
* have just read from the superblock.
*/
min_leb_cnt = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs;
min_leb_cnt += c->lpt_lebs + c->orph_lebs + c->jhead_cnt + 6;
if (c->leb_cnt < min_leb_cnt || c->leb_cnt > c->vi.size) {
ubifs_err("bad LEB count: %d in superblock, %d on UBI volume, "
"%d minimum required", c->leb_cnt, c->vi.size,
min_leb_cnt);
goto failed;
}
if (c->max_leb_cnt < c->leb_cnt) {
ubifs_err("max. LEB count %d less than LEB count %d",
c->max_leb_cnt, c->leb_cnt);
goto failed;
}
if (c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
err = 7;
goto failed;
}
if (c->max_bud_bytes < (long long)c->leb_size * UBIFS_MIN_BUD_LEBS ||
c->max_bud_bytes > (long long)c->leb_size * c->main_lebs) {
err = 8;
goto failed;
}
if (c->jhead_cnt < NONDATA_JHEADS_CNT + 1 ||
c->jhead_cnt > NONDATA_JHEADS_CNT + UBIFS_MAX_JHEADS) {
err = 9;
goto failed;
}
if (c->fanout < UBIFS_MIN_FANOUT ||
ubifs_idx_node_sz(c, c->fanout) > c->leb_size) {
err = 10;
goto failed;
}
if (c->lsave_cnt < 0 || (c->lsave_cnt > DEFAULT_LSAVE_CNT &&
c->lsave_cnt > c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS -
c->log_lebs - c->lpt_lebs - c->orph_lebs)) {
err = 11;
goto failed;
}
if (UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs +
c->orph_lebs + c->main_lebs != c->leb_cnt) {
err = 12;
goto failed;
}
if (c->default_compr < 0 || c->default_compr >= UBIFS_COMPR_TYPES_CNT) {
err = 13;
goto failed;
}
max_bytes = c->main_lebs * (long long)c->leb_size;
if (c->rp_size < 0 || max_bytes < c->rp_size) {
err = 14;
goto failed;
}
if (le32_to_cpu(sup->time_gran) > 1000000000 ||
le32_to_cpu(sup->time_gran) < 1) {
err = 15;
goto failed;
}
return 0;
failed:
ubifs_err("bad superblock, error %d", err);
dbg_dump_node(c, sup);
return -EINVAL;
}
/**
* ubifs_read_sb_node - read superblock node.
* @c: UBIFS file-system description object
*
* This function returns a pointer to the superblock node or a negative error
* code.
*/
struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
{
struct ubifs_sb_node *sup;
int err;
sup = kmalloc(ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size), GFP_NOFS);
if (!sup)
return ERR_PTR(-ENOMEM);
err = ubifs_read_node(c, sup, UBIFS_SB_NODE, UBIFS_SB_NODE_SZ,
UBIFS_SB_LNUM, 0);
if (err) {
kfree(sup);
return ERR_PTR(err);
}
return sup;
}
/**
* ubifs_read_superblock - read superblock.
* @c: UBIFS file-system description object
*
* This function finds, reads and checks the superblock. If an empty UBI volume
* is being mounted, this function creates default superblock. Returns zero in
* case of success, and a negative error code in case of failure.
*/
int ubifs_read_superblock(struct ubifs_info *c)
{
int err, sup_flags;
struct ubifs_sb_node *sup;
if (c->empty) {
printf("No UBIFS filesystem found!\n");
return -1;
}
sup = ubifs_read_sb_node(c);
if (IS_ERR(sup))
return PTR_ERR(sup);
c->fmt_version = le32_to_cpu(sup->fmt_version);
c->ro_compat_version = le32_to_cpu(sup->ro_compat_version);
/*
* The software supports all previous versions but not future versions,
* due to the unavailability of time-travelling equipment.
*/
if (c->fmt_version > UBIFS_FORMAT_VERSION) {
struct super_block *sb = c->vfs_sb;
int mounting_ro = sb->s_flags & MS_RDONLY;
ubifs_assert(!c->ro_media || mounting_ro);
if (!mounting_ro ||
c->ro_compat_version > UBIFS_RO_COMPAT_VERSION) {
ubifs_err("on-flash format version is w%d/r%d, but "
"software only supports up to version "
"w%d/r%d", c->fmt_version,
c->ro_compat_version, UBIFS_FORMAT_VERSION,
UBIFS_RO_COMPAT_VERSION);
if (c->ro_compat_version <= UBIFS_RO_COMPAT_VERSION) {
ubifs_msg("only R/O mounting is possible");
err = -EROFS;
} else
err = -EINVAL;
goto out;
}
/*
* The FS is mounted R/O, and the media format is
* R/O-compatible with the UBIFS implementation, so we can
* mount.
*/
c->rw_incompat = 1;
}
if (c->fmt_version < 3) {
ubifs_err("on-flash format version %d is not supported",
c->fmt_version);
err = -EINVAL;
goto out;
}
switch (sup->key_hash) {
case UBIFS_KEY_HASH_R5:
c->key_hash = key_r5_hash;
c->key_hash_type = UBIFS_KEY_HASH_R5;
break;
case UBIFS_KEY_HASH_TEST:
c->key_hash = key_test_hash;
c->key_hash_type = UBIFS_KEY_HASH_TEST;
break;
};
c->key_fmt = sup->key_fmt;
switch (c->key_fmt) {
case UBIFS_SIMPLE_KEY_FMT:
c->key_len = UBIFS_SK_LEN;
break;
default:
ubifs_err("unsupported key format");
err = -EINVAL;
goto out;
}
c->leb_cnt = le32_to_cpu(sup->leb_cnt);
c->max_leb_cnt = le32_to_cpu(sup->max_leb_cnt);
c->max_bud_bytes = le64_to_cpu(sup->max_bud_bytes);
c->log_lebs = le32_to_cpu(sup->log_lebs);
c->lpt_lebs = le32_to_cpu(sup->lpt_lebs);
c->orph_lebs = le32_to_cpu(sup->orph_lebs);
c->jhead_cnt = le32_to_cpu(sup->jhead_cnt) + NONDATA_JHEADS_CNT;
c->fanout = le32_to_cpu(sup->fanout);
c->lsave_cnt = le32_to_cpu(sup->lsave_cnt);
c->default_compr = le16_to_cpu(sup->default_compr);
c->rp_size = le64_to_cpu(sup->rp_size);
c->rp_uid = le32_to_cpu(sup->rp_uid);
c->rp_gid = le32_to_cpu(sup->rp_gid);
sup_flags = le32_to_cpu(sup->flags);
c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran);
memcpy(&c->uuid, &sup->uuid, 16);
c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
/* Automatically increase file system size to the maximum size */
c->old_leb_cnt = c->leb_cnt;
if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) {
c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size);
dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs",
c->old_leb_cnt, c->leb_cnt);
}
c->log_bytes = (long long)c->log_lebs * c->leb_size;
c->log_last = UBIFS_LOG_LNUM + c->log_lebs - 1;
c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs;
c->lpt_last = c->lpt_first + c->lpt_lebs - 1;
c->orph_first = c->lpt_last + 1;
c->orph_last = c->orph_first + c->orph_lebs - 1;
c->main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS;
c->main_lebs -= c->log_lebs + c->lpt_lebs + c->orph_lebs;
c->main_first = c->leb_cnt - c->main_lebs;
c->report_rp_size = ubifs_reported_space(c, c->rp_size);
err = validate_sb(c, sup);
out:
kfree(sup);
return err;
}

View file

@ -0,0 +1,362 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Adrian Hunter
* Artem Bityutskiy (Битюцкий Артём)
*/
/*
* This file implements the scan which is a general-purpose function for
* determining what nodes are in an eraseblock. The scan is used to replay the
* journal, to do garbage collection. for the TNC in-the-gaps method, and by
* debugging functions.
*/
#include "ubifs.h"
/**
* scan_padding_bytes - scan for padding bytes.
* @buf: buffer to scan
* @len: length of buffer
*
* This function returns the number of padding bytes on success and
* %SCANNED_GARBAGE on failure.
*/
static int scan_padding_bytes(void *buf, int len)
{
int pad_len = 0, max_pad_len = min_t(int, UBIFS_PAD_NODE_SZ, len);
uint8_t *p = buf;
dbg_scan("not a node");
while (pad_len < max_pad_len && *p++ == UBIFS_PADDING_BYTE)
pad_len += 1;
if (!pad_len || (pad_len & 7))
return SCANNED_GARBAGE;
dbg_scan("%d padding bytes", pad_len);
return pad_len;
}
/**
* ubifs_scan_a_node - scan for a node or padding.
* @c: UBIFS file-system description object
* @buf: buffer to scan
* @len: length of buffer
* @lnum: logical eraseblock number
* @offs: offset within the logical eraseblock
* @quiet: print no messages
*
* This function returns a scanning code to indicate what was scanned.
*/
int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
int offs, int quiet)
{
struct ubifs_ch *ch = buf;
uint32_t magic;
magic = le32_to_cpu(ch->magic);
if (magic == 0xFFFFFFFF) {
dbg_scan("hit empty space");
return SCANNED_EMPTY_SPACE;
}
if (magic != UBIFS_NODE_MAGIC)
return scan_padding_bytes(buf, len);
if (len < UBIFS_CH_SZ)
return SCANNED_GARBAGE;
dbg_scan("scanning %s", dbg_ntype(ch->node_type));
if (ubifs_check_node(c, buf, lnum, offs, quiet, 1))
return SCANNED_A_CORRUPT_NODE;
if (ch->node_type == UBIFS_PAD_NODE) {
struct ubifs_pad_node *pad = buf;
int pad_len = le32_to_cpu(pad->pad_len);
int node_len = le32_to_cpu(ch->len);
/* Validate the padding node */
if (pad_len < 0 ||
offs + node_len + pad_len > c->leb_size) {
if (!quiet) {
ubifs_err("bad pad node at LEB %d:%d",
lnum, offs);
dbg_dump_node(c, pad);
}
return SCANNED_A_BAD_PAD_NODE;
}
/* Make the node pads to 8-byte boundary */
if ((node_len + pad_len) & 7) {
if (!quiet) {
dbg_err("bad padding length %d - %d",
offs, offs + node_len + pad_len);
}
return SCANNED_A_BAD_PAD_NODE;
}
dbg_scan("%d bytes padded, offset now %d",
pad_len, ALIGN(offs + node_len + pad_len, 8));
return node_len + pad_len;
}
return SCANNED_A_NODE;
}
/**
* ubifs_start_scan - create LEB scanning information at start of scan.
* @c: UBIFS file-system description object
* @lnum: logical eraseblock number
* @offs: offset to start at (usually zero)
* @sbuf: scan buffer (must be c->leb_size)
*
* This function returns %0 on success and a negative error code on failure.
*/
struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum,
int offs, void *sbuf)
{
struct ubifs_scan_leb *sleb;
int err;
dbg_scan("scan LEB %d:%d", lnum, offs);
sleb = kzalloc(sizeof(struct ubifs_scan_leb), GFP_NOFS);
if (!sleb)
return ERR_PTR(-ENOMEM);
sleb->lnum = lnum;
INIT_LIST_HEAD(&sleb->nodes);
sleb->buf = sbuf;
err = ubi_read(c->ubi, lnum, sbuf + offs, offs, c->leb_size - offs);
if (err && err != -EBADMSG) {
ubifs_err("cannot read %d bytes from LEB %d:%d,"
" error %d", c->leb_size - offs, lnum, offs, err);
kfree(sleb);
return ERR_PTR(err);
}
if (err == -EBADMSG)
sleb->ecc = 1;
return sleb;
}
/**
* ubifs_end_scan - update LEB scanning information at end of scan.
* @c: UBIFS file-system description object
* @sleb: scanning information
* @lnum: logical eraseblock number
* @offs: offset to start at (usually zero)
*
* This function returns %0 on success and a negative error code on failure.
*/
void ubifs_end_scan(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
int lnum, int offs)
{
lnum = lnum;
dbg_scan("stop scanning LEB %d at offset %d", lnum, offs);
ubifs_assert(offs % c->min_io_size == 0);
sleb->endpt = ALIGN(offs, c->min_io_size);
}
/**
* ubifs_add_snod - add a scanned node to LEB scanning information.
* @c: UBIFS file-system description object
* @sleb: scanning information
* @buf: buffer containing node
* @offs: offset of node on flash
*
* This function returns %0 on success and a negative error code on failure.
*/
int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
void *buf, int offs)
{
struct ubifs_ch *ch = buf;
struct ubifs_ino_node *ino = buf;
struct ubifs_scan_node *snod;
snod = kzalloc(sizeof(struct ubifs_scan_node), GFP_NOFS);
if (!snod)
return -ENOMEM;
snod->sqnum = le64_to_cpu(ch->sqnum);
snod->type = ch->node_type;
snod->offs = offs;
snod->len = le32_to_cpu(ch->len);
snod->node = buf;
switch (ch->node_type) {
case UBIFS_INO_NODE:
case UBIFS_DENT_NODE:
case UBIFS_XENT_NODE:
case UBIFS_DATA_NODE:
case UBIFS_TRUN_NODE:
/*
* The key is in the same place in all keyed
* nodes.
*/
key_read(c, &ino->key, &snod->key);
break;
}
list_add_tail(&snod->list, &sleb->nodes);
sleb->nodes_cnt += 1;
return 0;
}
/**
* ubifs_scanned_corruption - print information after UBIFS scanned corruption.
* @c: UBIFS file-system description object
* @lnum: LEB number of corruption
* @offs: offset of corruption
* @buf: buffer containing corruption
*/
void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
void *buf)
{
int len;
ubifs_err("corrupted data at LEB %d:%d", lnum, offs);
if (dbg_failure_mode)
return;
len = c->leb_size - offs;
if (len > 4096)
len = 4096;
dbg_err("first %d bytes from LEB %d:%d", len, lnum, offs);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1);
}
/**
* ubifs_scan - scan a logical eraseblock.
* @c: UBIFS file-system description object
* @lnum: logical eraseblock number
* @offs: offset to start at (usually zero)
* @sbuf: scan buffer (must be c->leb_size)
*
* This function scans LEB number @lnum and returns complete information about
* its contents. Returns an error code in case of failure.
*/
struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum,
int offs, void *sbuf)
{
void *buf = sbuf + offs;
int err, len = c->leb_size - offs;
struct ubifs_scan_leb *sleb;
sleb = ubifs_start_scan(c, lnum, offs, sbuf);
if (IS_ERR(sleb))
return sleb;
while (len >= 8) {
struct ubifs_ch *ch = buf;
int node_len, ret;
dbg_scan("look at LEB %d:%d (%d bytes left)",
lnum, offs, len);
cond_resched();
ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 0);
if (ret > 0) {
/* Padding bytes or a valid padding node */
offs += ret;
buf += ret;
len -= ret;
continue;
}
if (ret == SCANNED_EMPTY_SPACE)
/* Empty space is checked later */
break;
switch (ret) {
case SCANNED_GARBAGE:
dbg_err("garbage");
goto corrupted;
case SCANNED_A_NODE:
break;
case SCANNED_A_CORRUPT_NODE:
case SCANNED_A_BAD_PAD_NODE:
dbg_err("bad node");
goto corrupted;
default:
dbg_err("unknown");
goto corrupted;
}
err = ubifs_add_snod(c, sleb, buf, offs);
if (err)
goto error;
node_len = ALIGN(le32_to_cpu(ch->len), 8);
offs += node_len;
buf += node_len;
len -= node_len;
}
if (offs % c->min_io_size)
goto corrupted;
ubifs_end_scan(c, sleb, lnum, offs);
for (; len > 4; offs += 4, buf = buf + 4, len -= 4)
if (*(uint32_t *)buf != 0xffffffff)
break;
for (; len; offs++, buf++, len--)
if (*(uint8_t *)buf != 0xff) {
ubifs_err("corrupt empty space at LEB %d:%d",
lnum, offs);
goto corrupted;
}
return sleb;
corrupted:
ubifs_scanned_corruption(c, lnum, offs, buf);
err = -EUCLEAN;
error:
ubifs_err("LEB %d scanning failed", lnum);
ubifs_scan_destroy(sleb);
return ERR_PTR(err);
}
/**
* ubifs_scan_destroy - destroy LEB scanning information.
* @sleb: scanning information to free
*/
void ubifs_scan_destroy(struct ubifs_scan_leb *sleb)
{
struct ubifs_scan_node *node;
struct list_head *head;
head = &sleb->nodes;
while (!list_empty(head)) {
node = list_entry(head->next, struct ubifs_scan_node, list);
list_del(&node->list);
kfree(node);
}
kfree(sleb);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,435 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Adrian Hunter
* Artem Bityutskiy (Битюцкий Артём)
*/
/*
* This file contains miscelanious TNC-related functions shared betweend
* different files. This file does not form any logically separate TNC
* sub-system. The file was created because there is a lot of TNC code and
* putting it all in one file would make that file too big and unreadable.
*/
#include "ubifs.h"
/**
* ubifs_tnc_levelorder_next - next TNC tree element in levelorder traversal.
* @zr: root of the subtree to traverse
* @znode: previous znode
*
* This function implements levelorder TNC traversal. The LNC is ignored.
* Returns the next element or %NULL if @znode is already the last one.
*/
struct ubifs_znode *ubifs_tnc_levelorder_next(struct ubifs_znode *zr,
struct ubifs_znode *znode)
{
int level, iip, level_search = 0;
struct ubifs_znode *zn;
ubifs_assert(zr);
if (unlikely(!znode))
return zr;
if (unlikely(znode == zr)) {
if (znode->level == 0)
return NULL;
return ubifs_tnc_find_child(zr, 0);
}
level = znode->level;
iip = znode->iip;
while (1) {
ubifs_assert(znode->level <= zr->level);
/*
* First walk up until there is a znode with next branch to
* look at.
*/
while (znode->parent != zr && iip >= znode->parent->child_cnt) {
znode = znode->parent;
iip = znode->iip;
}
if (unlikely(znode->parent == zr &&
iip >= znode->parent->child_cnt)) {
/* This level is done, switch to the lower one */
level -= 1;
if (level_search || level < 0)
/*
* We were already looking for znode at lower
* level ('level_search'). As we are here
* again, it just does not exist. Or all levels
* were finished ('level < 0').
*/
return NULL;
level_search = 1;
iip = -1;
znode = ubifs_tnc_find_child(zr, 0);
ubifs_assert(znode);
}
/* Switch to the next index */
zn = ubifs_tnc_find_child(znode->parent, iip + 1);
if (!zn) {
/* No more children to look at, we have walk up */
iip = znode->parent->child_cnt;
continue;
}
/* Walk back down to the level we came from ('level') */
while (zn->level != level) {
znode = zn;
zn = ubifs_tnc_find_child(zn, 0);
if (!zn) {
/*
* This path is not too deep so it does not
* reach 'level'. Try next path.
*/
iip = znode->iip;
break;
}
}
if (zn) {
ubifs_assert(zn->level >= 0);
return zn;
}
}
}
/**
* ubifs_search_zbranch - search znode branch.
* @c: UBIFS file-system description object
* @znode: znode to search in
* @key: key to search for
* @n: znode branch slot number is returned here
*
* This is a helper function which search branch with key @key in @znode using
* binary search. The result of the search may be:
* o exact match, then %1 is returned, and the slot number of the branch is
* stored in @n;
* o no exact match, then %0 is returned and the slot number of the left
* closest branch is returned in @n; the slot if all keys in this znode are
* greater than @key, then %-1 is returned in @n.
*/
int ubifs_search_zbranch(const struct ubifs_info *c,
const struct ubifs_znode *znode,
const union ubifs_key *key, int *n)
{
int beg = 0, end = znode->child_cnt, uninitialized_var(mid);
int uninitialized_var(cmp);
const struct ubifs_zbranch *zbr = &znode->zbranch[0];
ubifs_assert(end > beg);
while (end > beg) {
mid = (beg + end) >> 1;
cmp = keys_cmp(c, key, &zbr[mid].key);
if (cmp > 0)
beg = mid + 1;
else if (cmp < 0)
end = mid;
else {
*n = mid;
return 1;
}
}
*n = end - 1;
/* The insert point is after *n */
ubifs_assert(*n >= -1 && *n < znode->child_cnt);
if (*n == -1)
ubifs_assert(keys_cmp(c, key, &zbr[0].key) < 0);
else
ubifs_assert(keys_cmp(c, key, &zbr[*n].key) > 0);
if (*n + 1 < znode->child_cnt)
ubifs_assert(keys_cmp(c, key, &zbr[*n + 1].key) < 0);
return 0;
}
/**
* ubifs_tnc_postorder_first - find first znode to do postorder tree traversal.
* @znode: znode to start at (root of the sub-tree to traverse)
*
* Find the lowest leftmost znode in a subtree of the TNC tree. The LNC is
* ignored.
*/
struct ubifs_znode *ubifs_tnc_postorder_first(struct ubifs_znode *znode)
{
if (unlikely(!znode))
return NULL;
while (znode->level > 0) {
struct ubifs_znode *child;
child = ubifs_tnc_find_child(znode, 0);
if (!child)
return znode;
znode = child;
}
return znode;
}
/**
* ubifs_tnc_postorder_next - next TNC tree element in postorder traversal.
* @znode: previous znode
*
* This function implements postorder TNC traversal. The LNC is ignored.
* Returns the next element or %NULL if @znode is already the last one.
*/
struct ubifs_znode *ubifs_tnc_postorder_next(struct ubifs_znode *znode)
{
struct ubifs_znode *zn;
ubifs_assert(znode);
if (unlikely(!znode->parent))
return NULL;
/* Switch to the next index in the parent */
zn = ubifs_tnc_find_child(znode->parent, znode->iip + 1);
if (!zn)
/* This is in fact the last child, return parent */
return znode->parent;
/* Go to the first znode in this new subtree */
return ubifs_tnc_postorder_first(zn);
}
/**
* read_znode - read an indexing node from flash and fill znode.
* @c: UBIFS file-system description object
* @lnum: LEB of the indexing node to read
* @offs: node offset
* @len: node length
* @znode: znode to read to
*
* This function reads an indexing node from the flash media and fills znode
* with the read data. Returns zero in case of success and a negative error
* code in case of failure. The read indexing node is validated and if anything
* is wrong with it, this function prints complaint messages and returns
* %-EINVAL.
*/
static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
struct ubifs_znode *znode)
{
int i, err, type, cmp;
struct ubifs_idx_node *idx;
idx = kmalloc(c->max_idx_node_sz, GFP_NOFS);
if (!idx)
return -ENOMEM;
err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
if (err < 0) {
kfree(idx);
return err;
}
znode->child_cnt = le16_to_cpu(idx->child_cnt);
znode->level = le16_to_cpu(idx->level);
dbg_tnc("LEB %d:%d, level %d, %d branch",
lnum, offs, znode->level, znode->child_cnt);
if (znode->child_cnt > c->fanout || znode->level > UBIFS_MAX_LEVELS) {
dbg_err("current fanout %d, branch count %d",
c->fanout, znode->child_cnt);
dbg_err("max levels %d, znode level %d",
UBIFS_MAX_LEVELS, znode->level);
err = 1;
goto out_dump;
}
for (i = 0; i < znode->child_cnt; i++) {
const struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
struct ubifs_zbranch *zbr = &znode->zbranch[i];
key_read(c, &br->key, &zbr->key);
zbr->lnum = le32_to_cpu(br->lnum);
zbr->offs = le32_to_cpu(br->offs);
zbr->len = le32_to_cpu(br->len);
zbr->znode = NULL;
/* Validate branch */
if (zbr->lnum < c->main_first ||
zbr->lnum >= c->leb_cnt || zbr->offs < 0 ||
zbr->offs + zbr->len > c->leb_size || zbr->offs & 7) {
dbg_err("bad branch %d", i);
err = 2;
goto out_dump;
}
switch (key_type(c, &zbr->key)) {
case UBIFS_INO_KEY:
case UBIFS_DATA_KEY:
case UBIFS_DENT_KEY:
case UBIFS_XENT_KEY:
break;
default:
dbg_msg("bad key type at slot %d: %s", i,
DBGKEY(&zbr->key));
err = 3;
goto out_dump;
}
if (znode->level)
continue;
type = key_type(c, &zbr->key);
if (c->ranges[type].max_len == 0) {
if (zbr->len != c->ranges[type].len) {
dbg_err("bad target node (type %d) length (%d)",
type, zbr->len);
dbg_err("have to be %d", c->ranges[type].len);
err = 4;
goto out_dump;
}
} else if (zbr->len < c->ranges[type].min_len ||
zbr->len > c->ranges[type].max_len) {
dbg_err("bad target node (type %d) length (%d)",
type, zbr->len);
dbg_err("have to be in range of %d-%d",
c->ranges[type].min_len,
c->ranges[type].max_len);
err = 5;
goto out_dump;
}
}
/*
* Ensure that the next key is greater or equivalent to the
* previous one.
*/
for (i = 0; i < znode->child_cnt - 1; i++) {
const union ubifs_key *key1, *key2;
key1 = &znode->zbranch[i].key;
key2 = &znode->zbranch[i + 1].key;
cmp = keys_cmp(c, key1, key2);
if (cmp > 0) {
dbg_err("bad key order (keys %d and %d)", i, i + 1);
err = 6;
goto out_dump;
} else if (cmp == 0 && !is_hash_key(c, key1)) {
/* These can only be keys with colliding hash */
dbg_err("keys %d and %d are not hashed but equivalent",
i, i + 1);
err = 7;
goto out_dump;
}
}
kfree(idx);
return 0;
out_dump:
ubifs_err("bad indexing node at LEB %d:%d, error %d", lnum, offs, err);
dbg_dump_node(c, idx);
kfree(idx);
return -EINVAL;
}
/**
* ubifs_load_znode - load znode to TNC cache.
* @c: UBIFS file-system description object
* @zbr: znode branch
* @parent: znode's parent
* @iip: index in parent
*
* This function loads znode pointed to by @zbr into the TNC cache and
* returns pointer to it in case of success and a negative error code in case
* of failure.
*/
struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c,
struct ubifs_zbranch *zbr,
struct ubifs_znode *parent, int iip)
{
int err;
struct ubifs_znode *znode;
ubifs_assert(!zbr->znode);
/*
* A slab cache is not presently used for znodes because the znode size
* depends on the fanout which is stored in the superblock.
*/
znode = kzalloc(c->max_znode_sz, GFP_NOFS);
if (!znode)
return ERR_PTR(-ENOMEM);
err = read_znode(c, zbr->lnum, zbr->offs, zbr->len, znode);
if (err)
goto out;
zbr->znode = znode;
znode->parent = parent;
znode->time = get_seconds();
znode->iip = iip;
return znode;
out:
kfree(znode);
return ERR_PTR(err);
}
/**
* ubifs_tnc_read_node - read a leaf node from the flash media.
* @c: UBIFS file-system description object
* @zbr: key and position of the node
* @node: node is returned here
*
* This function reads a node defined by @zbr from the flash media. Returns
* zero in case of success or a negative negative error code in case of
* failure.
*/
int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
void *node)
{
union ubifs_key key1, *key = &zbr->key;
int err, type = key_type(c, key);
err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum, zbr->offs);
if (err) {
dbg_tnc("key %s", DBGKEY(key));
return err;
}
/* Make sure the key of the read node is correct */
key_read(c, node + UBIFS_KEY_OFFSET, &key1);
if (!keys_eq(c, key, &key1)) {
ubifs_err("bad key in node at LEB %d:%d",
zbr->lnum, zbr->offs);
dbg_tnc("looked for key %s found node's key %s",
DBGKEY(key), DBGKEY1(&key1));
dbg_dump_node(c, node);
return -EINVAL;
}
return 0;
}

View file

@ -0,0 +1,775 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
/*
* This file describes UBIFS on-flash format and contains definitions of all the
* relevant data structures and constants.
*
* All UBIFS on-flash objects are stored in the form of nodes. All nodes start
* with the UBIFS node magic number and have the same common header. Nodes
* always sit at 8-byte aligned positions on the media and node header sizes are
* also 8-byte aligned (except for the indexing node and the padding node).
*/
#ifndef __UBIFS_MEDIA_H__
#define __UBIFS_MEDIA_H__
/* UBIFS node magic number (must not have the padding byte first or last) */
#define UBIFS_NODE_MAGIC 0x06101831
/*
* UBIFS on-flash format version. This version is increased when the on-flash
* format is changing. If this happens, UBIFS is will support older versions as
* well. But older UBIFS code will not support newer formats. Format changes
* will be rare and only when absolutely necessary, e.g. to fix a bug or to add
* a new feature.
*
* UBIFS went into mainline kernel with format version 4. The older formats
* were development formats.
*/
#define UBIFS_FORMAT_VERSION 4
/*
* Read-only compatibility version. If the UBIFS format is changed, older UBIFS
* implementations will not be able to mount newer formats in read-write mode.
* However, depending on the change, it may be possible to mount newer formats
* in R/O mode. This is indicated by the R/O compatibility version which is
* stored in the super-block.
*
* This is needed to support boot-loaders which only need R/O mounting. With
* this flag it is possible to do UBIFS format changes without a need to update
* boot-loaders.
*/
#define UBIFS_RO_COMPAT_VERSION 0
/* Minimum logical eraseblock size in bytes */
#define UBIFS_MIN_LEB_SZ (15*1024)
/* Initial CRC32 value used when calculating CRC checksums */
#define UBIFS_CRC32_INIT 0xFFFFFFFFU
/*
* UBIFS does not try to compress data if its length is less than the below
* constant.
*/
#define UBIFS_MIN_COMPR_LEN 128
/*
* If compressed data length is less than %UBIFS_MIN_COMPRESS_DIFF bytes
* shorter than uncompressed data length, UBIFS prefers to leave this data
* node uncompress, because it'll be read faster.
*/
#define UBIFS_MIN_COMPRESS_DIFF 64
/* Root inode number */
#define UBIFS_ROOT_INO 1
/* Lowest inode number used for regular inodes (not UBIFS-only internal ones) */
#define UBIFS_FIRST_INO 64
/*
* Maximum file name and extended attribute length (must be a multiple of 8,
* minus 1).
*/
#define UBIFS_MAX_NLEN 255
/* Maximum number of data journal heads */
#define UBIFS_MAX_JHEADS 1
/*
* Size of UBIFS data block. Note, UBIFS is not a block oriented file-system,
* which means that it does not treat the underlying media as consisting of
* blocks like in case of hard drives. Do not be confused. UBIFS block is just
* the maximum amount of data which one data node can have or which can be
* attached to an inode node.
*/
#define UBIFS_BLOCK_SIZE 4096
#define UBIFS_BLOCK_SHIFT 12
/* UBIFS padding byte pattern (must not be first or last byte of node magic) */
#define UBIFS_PADDING_BYTE 0xCE
/* Maximum possible key length */
#define UBIFS_MAX_KEY_LEN 16
/* Key length ("simple" format) */
#define UBIFS_SK_LEN 8
/* Minimum index tree fanout */
#define UBIFS_MIN_FANOUT 3
/* Maximum number of levels in UBIFS indexing B-tree */
#define UBIFS_MAX_LEVELS 512
/* Maximum amount of data attached to an inode in bytes */
#define UBIFS_MAX_INO_DATA UBIFS_BLOCK_SIZE
/* LEB Properties Tree fanout (must be power of 2) and fanout shift */
#define UBIFS_LPT_FANOUT 4
#define UBIFS_LPT_FANOUT_SHIFT 2
/* LEB Properties Tree bit field sizes */
#define UBIFS_LPT_CRC_BITS 16
#define UBIFS_LPT_CRC_BYTES 2
#define UBIFS_LPT_TYPE_BITS 4
/* The key is always at the same position in all keyed nodes */
#define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key)
/*
* LEB Properties Tree node types.
*
* UBIFS_LPT_PNODE: LPT leaf node (contains LEB properties)
* UBIFS_LPT_NNODE: LPT internal node
* UBIFS_LPT_LTAB: LPT's own lprops table
* UBIFS_LPT_LSAVE: LPT's save table (big model only)
* UBIFS_LPT_NODE_CNT: count of LPT node types
* UBIFS_LPT_NOT_A_NODE: all ones (15 for 4 bits) is never a valid node type
*/
enum {
UBIFS_LPT_PNODE,
UBIFS_LPT_NNODE,
UBIFS_LPT_LTAB,
UBIFS_LPT_LSAVE,
UBIFS_LPT_NODE_CNT,
UBIFS_LPT_NOT_A_NODE = (1 << UBIFS_LPT_TYPE_BITS) - 1,
};
/*
* UBIFS inode types.
*
* UBIFS_ITYPE_REG: regular file
* UBIFS_ITYPE_DIR: directory
* UBIFS_ITYPE_LNK: soft link
* UBIFS_ITYPE_BLK: block device node
* UBIFS_ITYPE_CHR: character device node
* UBIFS_ITYPE_FIFO: fifo
* UBIFS_ITYPE_SOCK: socket
* UBIFS_ITYPES_CNT: count of supported file types
*/
enum {
UBIFS_ITYPE_REG,
UBIFS_ITYPE_DIR,
UBIFS_ITYPE_LNK,
UBIFS_ITYPE_BLK,
UBIFS_ITYPE_CHR,
UBIFS_ITYPE_FIFO,
UBIFS_ITYPE_SOCK,
UBIFS_ITYPES_CNT,
};
/*
* Supported key hash functions.
*
* UBIFS_KEY_HASH_R5: R5 hash
* UBIFS_KEY_HASH_TEST: test hash which just returns first 4 bytes of the name
*/
enum {
UBIFS_KEY_HASH_R5,
UBIFS_KEY_HASH_TEST,
};
/*
* Supported key formats.
*
* UBIFS_SIMPLE_KEY_FMT: simple key format
*/
enum {
UBIFS_SIMPLE_KEY_FMT,
};
/*
* The simple key format uses 29 bits for storing UBIFS block number and hash
* value.
*/
#define UBIFS_S_KEY_BLOCK_BITS 29
#define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF
#define UBIFS_S_KEY_HASH_BITS UBIFS_S_KEY_BLOCK_BITS
#define UBIFS_S_KEY_HASH_MASK UBIFS_S_KEY_BLOCK_MASK
/*
* Key types.
*
* UBIFS_INO_KEY: inode node key
* UBIFS_DATA_KEY: data node key
* UBIFS_DENT_KEY: directory entry node key
* UBIFS_XENT_KEY: extended attribute entry key
* UBIFS_KEY_TYPES_CNT: number of supported key types
*/
enum {
UBIFS_INO_KEY,
UBIFS_DATA_KEY,
UBIFS_DENT_KEY,
UBIFS_XENT_KEY,
UBIFS_KEY_TYPES_CNT,
};
/* Count of LEBs reserved for the superblock area */
#define UBIFS_SB_LEBS 1
/* Count of LEBs reserved for the master area */
#define UBIFS_MST_LEBS 2
/* First LEB of the superblock area */
#define UBIFS_SB_LNUM 0
/* First LEB of the master area */
#define UBIFS_MST_LNUM (UBIFS_SB_LNUM + UBIFS_SB_LEBS)
/* First LEB of the log area */
#define UBIFS_LOG_LNUM (UBIFS_MST_LNUM + UBIFS_MST_LEBS)
/*
* The below constants define the absolute minimum values for various UBIFS
* media areas. Many of them actually depend of flash geometry and the FS
* configuration (number of journal heads, orphan LEBs, etc). This means that
* the smallest volume size which can be used for UBIFS cannot be pre-defined
* by these constants. The file-system that meets the below limitation will not
* necessarily mount. UBIFS does run-time calculations and validates the FS
* size.
*/
/* Minimum number of logical eraseblocks in the log */
#define UBIFS_MIN_LOG_LEBS 2
/* Minimum number of bud logical eraseblocks (one for each head) */
#define UBIFS_MIN_BUD_LEBS 3
/* Minimum number of journal logical eraseblocks */
#define UBIFS_MIN_JNL_LEBS (UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS)
/* Minimum number of LPT area logical eraseblocks */
#define UBIFS_MIN_LPT_LEBS 2
/* Minimum number of orphan area logical eraseblocks */
#define UBIFS_MIN_ORPH_LEBS 1
/*
* Minimum number of main area logical eraseblocks (buds, 3 for the index, 1
* for GC, 1 for deletions, and at least 1 for committed data).
*/
#define UBIFS_MIN_MAIN_LEBS (UBIFS_MIN_BUD_LEBS + 6)
/* Minimum number of logical eraseblocks */
#define UBIFS_MIN_LEB_CNT (UBIFS_SB_LEBS + UBIFS_MST_LEBS + \
UBIFS_MIN_LOG_LEBS + UBIFS_MIN_LPT_LEBS + \
UBIFS_MIN_ORPH_LEBS + UBIFS_MIN_MAIN_LEBS)
/* Node sizes (N.B. these are guaranteed to be multiples of 8) */
#define UBIFS_CH_SZ sizeof(struct ubifs_ch)
#define UBIFS_INO_NODE_SZ sizeof(struct ubifs_ino_node)
#define UBIFS_DATA_NODE_SZ sizeof(struct ubifs_data_node)
#define UBIFS_DENT_NODE_SZ sizeof(struct ubifs_dent_node)
#define UBIFS_TRUN_NODE_SZ sizeof(struct ubifs_trun_node)
#define UBIFS_PAD_NODE_SZ sizeof(struct ubifs_pad_node)
#define UBIFS_SB_NODE_SZ sizeof(struct ubifs_sb_node)
#define UBIFS_MST_NODE_SZ sizeof(struct ubifs_mst_node)
#define UBIFS_REF_NODE_SZ sizeof(struct ubifs_ref_node)
#define UBIFS_IDX_NODE_SZ sizeof(struct ubifs_idx_node)
#define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node)
#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node)
/* Extended attribute entry nodes are identical to directory entry nodes */
#define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ
/* Only this does not have to be multiple of 8 bytes */
#define UBIFS_BRANCH_SZ sizeof(struct ubifs_branch)
/* Maximum node sizes (N.B. these are guaranteed to be multiples of 8) */
#define UBIFS_MAX_DATA_NODE_SZ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE)
#define UBIFS_MAX_INO_NODE_SZ (UBIFS_INO_NODE_SZ + UBIFS_MAX_INO_DATA)
#define UBIFS_MAX_DENT_NODE_SZ (UBIFS_DENT_NODE_SZ + UBIFS_MAX_NLEN + 1)
#define UBIFS_MAX_XENT_NODE_SZ UBIFS_MAX_DENT_NODE_SZ
/* The largest UBIFS node */
#define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ
/*
* On-flash inode flags.
*
* UBIFS_COMPR_FL: use compression for this inode
* UBIFS_SYNC_FL: I/O on this inode has to be synchronous
* UBIFS_IMMUTABLE_FL: inode is immutable
* UBIFS_APPEND_FL: writes to the inode may only append data
* UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous
* UBIFS_XATTR_FL: this inode is the inode for an extended attribute value
*
* Note, these are on-flash flags which correspond to ioctl flags
* (@FS_COMPR_FL, etc). They have the same values now, but generally, do not
* have to be the same.
*/
enum {
UBIFS_COMPR_FL = 0x01,
UBIFS_SYNC_FL = 0x02,
UBIFS_IMMUTABLE_FL = 0x04,
UBIFS_APPEND_FL = 0x08,
UBIFS_DIRSYNC_FL = 0x10,
UBIFS_XATTR_FL = 0x20,
};
/* Inode flag bits used by UBIFS */
#define UBIFS_FL_MASK 0x0000001F
/*
* UBIFS compression algorithms.
*
* UBIFS_COMPR_NONE: no compression
* UBIFS_COMPR_LZO: LZO compression
* UBIFS_COMPR_ZLIB: ZLIB compression
* UBIFS_COMPR_TYPES_CNT: count of supported compression types
*/
enum {
UBIFS_COMPR_NONE,
UBIFS_COMPR_LZO,
UBIFS_COMPR_ZLIB,
UBIFS_COMPR_TYPES_CNT,
};
/*
* UBIFS node types.
*
* UBIFS_INO_NODE: inode node
* UBIFS_DATA_NODE: data node
* UBIFS_DENT_NODE: directory entry node
* UBIFS_XENT_NODE: extended attribute node
* UBIFS_TRUN_NODE: truncation node
* UBIFS_PAD_NODE: padding node
* UBIFS_SB_NODE: superblock node
* UBIFS_MST_NODE: master node
* UBIFS_REF_NODE: LEB reference node
* UBIFS_IDX_NODE: index node
* UBIFS_CS_NODE: commit start node
* UBIFS_ORPH_NODE: orphan node
* UBIFS_NODE_TYPES_CNT: count of supported node types
*
* Note, we index arrays by these numbers, so keep them low and contiguous.
* Node type constants for inodes, direntries and so on have to be the same as
* corresponding key type constants.
*/
enum {
UBIFS_INO_NODE,
UBIFS_DATA_NODE,
UBIFS_DENT_NODE,
UBIFS_XENT_NODE,
UBIFS_TRUN_NODE,
UBIFS_PAD_NODE,
UBIFS_SB_NODE,
UBIFS_MST_NODE,
UBIFS_REF_NODE,
UBIFS_IDX_NODE,
UBIFS_CS_NODE,
UBIFS_ORPH_NODE,
UBIFS_NODE_TYPES_CNT,
};
/*
* Master node flags.
*
* UBIFS_MST_DIRTY: rebooted uncleanly - master node is dirty
* UBIFS_MST_NO_ORPHS: no orphan inodes present
* UBIFS_MST_RCVRY: written by recovery
*/
enum {
UBIFS_MST_DIRTY = 1,
UBIFS_MST_NO_ORPHS = 2,
UBIFS_MST_RCVRY = 4,
};
/*
* Node group type (used by recovery to recover whole group or none).
*
* UBIFS_NO_NODE_GROUP: this node is not part of a group
* UBIFS_IN_NODE_GROUP: this node is a part of a group
* UBIFS_LAST_OF_NODE_GROUP: this node is the last in a group
*/
enum {
UBIFS_NO_NODE_GROUP = 0,
UBIFS_IN_NODE_GROUP,
UBIFS_LAST_OF_NODE_GROUP,
};
/*
* Superblock flags.
*
* UBIFS_FLG_BIGLPT: if "big" LPT model is used if set
*/
enum {
UBIFS_FLG_BIGLPT = 0x02,
};
/**
* struct ubifs_ch - common header node.
* @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
* @crc: CRC-32 checksum of the node header
* @sqnum: sequence number
* @len: full node length
* @node_type: node type
* @group_type: node group type
* @padding: reserved for future, zeroes
*
* Every UBIFS node starts with this common part. If the node has a key, the
* key always goes next.
*/
struct ubifs_ch {
__le32 magic;
__le32 crc;
__le64 sqnum;
__le32 len;
__u8 node_type;
__u8 group_type;
__u8 padding[2];
} __attribute__ ((packed));
/**
* union ubifs_dev_desc - device node descriptor.
* @new: new type device descriptor
* @huge: huge type device descriptor
*
* This data structure describes major/minor numbers of a device node. In an
* inode is a device node then its data contains an object of this type. UBIFS
* uses standard Linux "new" and "huge" device node encodings.
*/
union ubifs_dev_desc {
__le32 new;
__le64 huge;
} __attribute__ ((packed));
/**
* struct ubifs_ino_node - inode node.
* @ch: common header
* @key: node key
* @creat_sqnum: sequence number at time of creation
* @size: inode size in bytes (amount of uncompressed data)
* @atime_sec: access time seconds
* @ctime_sec: creation time seconds
* @mtime_sec: modification time seconds
* @atime_nsec: access time nanoseconds
* @ctime_nsec: creation time nanoseconds
* @mtime_nsec: modification time nanoseconds
* @nlink: number of hard links
* @uid: owner ID
* @gid: group ID
* @mode: access flags
* @flags: per-inode flags (%UBIFS_COMPR_FL, %UBIFS_SYNC_FL, etc)
* @data_len: inode data length
* @xattr_cnt: count of extended attributes this inode has
* @xattr_size: summarized size of all extended attributes in bytes
* @padding1: reserved for future, zeroes
* @xattr_names: sum of lengths of all extended attribute names belonging to
* this inode
* @compr_type: compression type used for this inode
* @padding2: reserved for future, zeroes
* @data: data attached to the inode
*
* Note, even though inode compression type is defined by @compr_type, some
* nodes of this inode may be compressed with different compressor - this
* happens if compression type is changed while the inode already has data
* nodes. But @compr_type will be use for further writes to the inode.
*
* Note, do not forget to amend 'zero_ino_node_unused()' function when changing
* the padding fields.
*/
struct ubifs_ino_node {
struct ubifs_ch ch;
__u8 key[UBIFS_MAX_KEY_LEN];
__le64 creat_sqnum;
__le64 size;
__le64 atime_sec;
__le64 ctime_sec;
__le64 mtime_sec;
__le32 atime_nsec;
__le32 ctime_nsec;
__le32 mtime_nsec;
__le32 nlink;
__le32 uid;
__le32 gid;
__le32 mode;
__le32 flags;
__le32 data_len;
__le32 xattr_cnt;
__le32 xattr_size;
__u8 padding1[4]; /* Watch 'zero_ino_node_unused()' if changing! */
__le32 xattr_names;
__le16 compr_type;
__u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */
__u8 data[];
} __attribute__ ((packed));
/**
* struct ubifs_dent_node - directory entry node.
* @ch: common header
* @key: node key
* @inum: target inode number
* @padding1: reserved for future, zeroes
* @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc)
* @nlen: name length
* @padding2: reserved for future, zeroes
* @name: zero-terminated name
*
* Note, do not forget to amend 'zero_dent_node_unused()' function when
* changing the padding fields.
*/
struct ubifs_dent_node {
struct ubifs_ch ch;
__u8 key[UBIFS_MAX_KEY_LEN];
__le64 inum;
__u8 padding1;
__u8 type;
__le16 nlen;
__u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
__u8 name[];
} __attribute__ ((packed));
/**
* struct ubifs_data_node - data node.
* @ch: common header
* @key: node key
* @size: uncompressed data size in bytes
* @compr_type: compression type (%UBIFS_COMPR_NONE, %UBIFS_COMPR_LZO, etc)
* @padding: reserved for future, zeroes
* @data: data
*
* Note, do not forget to amend 'zero_data_node_unused()' function when
* changing the padding fields.
*/
struct ubifs_data_node {
struct ubifs_ch ch;
__u8 key[UBIFS_MAX_KEY_LEN];
__le32 size;
__le16 compr_type;
__u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
__u8 data[];
} __attribute__ ((packed));
/**
* struct ubifs_trun_node - truncation node.
* @ch: common header
* @inum: truncated inode number
* @padding: reserved for future, zeroes
* @old_size: size before truncation
* @new_size: size after truncation
*
* This node exists only in the journal and never goes to the main area. Note,
* do not forget to amend 'zero_trun_node_unused()' function when changing the
* padding fields.
*/
struct ubifs_trun_node {
struct ubifs_ch ch;
__le32 inum;
__u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */
__le64 old_size;
__le64 new_size;
} __attribute__ ((packed));
/**
* struct ubifs_pad_node - padding node.
* @ch: common header
* @pad_len: how many bytes after this node are unused (because padded)
* @padding: reserved for future, zeroes
*/
struct ubifs_pad_node {
struct ubifs_ch ch;
__le32 pad_len;
} __attribute__ ((packed));
/**
* struct ubifs_sb_node - superblock node.
* @ch: common header
* @padding: reserved for future, zeroes
* @key_hash: type of hash function used in keys
* @key_fmt: format of the key
* @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc)
* @min_io_size: minimal input/output unit size
* @leb_size: logical eraseblock size in bytes
* @leb_cnt: count of LEBs used by file-system
* @max_leb_cnt: maximum count of LEBs used by file-system
* @max_bud_bytes: maximum amount of data stored in buds
* @log_lebs: log size in logical eraseblocks
* @lpt_lebs: number of LEBs used for lprops table
* @orph_lebs: number of LEBs used for recording orphans
* @jhead_cnt: count of journal heads
* @fanout: tree fanout (max. number of links per indexing node)
* @lsave_cnt: number of LEB numbers in LPT's save table
* @fmt_version: UBIFS on-flash format version
* @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
* @padding1: reserved for future, zeroes
* @rp_uid: reserve pool UID
* @rp_gid: reserve pool GID
* @rp_size: size of the reserved pool in bytes
* @padding2: reserved for future, zeroes
* @time_gran: time granularity in nanoseconds
* @uuid: UUID generated when the file system image was created
* @ro_compat_version: UBIFS R/O compatibility version
*/
struct ubifs_sb_node {
struct ubifs_ch ch;
__u8 padding[2];
__u8 key_hash;
__u8 key_fmt;
__le32 flags;
__le32 min_io_size;
__le32 leb_size;
__le32 leb_cnt;
__le32 max_leb_cnt;
__le64 max_bud_bytes;
__le32 log_lebs;
__le32 lpt_lebs;
__le32 orph_lebs;
__le32 jhead_cnt;
__le32 fanout;
__le32 lsave_cnt;
__le32 fmt_version;
__le16 default_compr;
__u8 padding1[2];
__le32 rp_uid;
__le32 rp_gid;
__le64 rp_size;
__le32 time_gran;
__u8 uuid[16];
__le32 ro_compat_version;
__u8 padding2[3968];
} __attribute__ ((packed));
/**
* struct ubifs_mst_node - master node.
* @ch: common header
* @highest_inum: highest inode number in the committed index
* @cmt_no: commit number
* @flags: various flags (%UBIFS_MST_DIRTY, etc)
* @log_lnum: start of the log
* @root_lnum: LEB number of the root indexing node
* @root_offs: offset within @root_lnum
* @root_len: root indexing node length
* @gc_lnum: LEB reserved for garbage collection (%-1 value means the LEB was
* not reserved and should be reserved on mount)
* @ihead_lnum: LEB number of index head
* @ihead_offs: offset of index head
* @index_size: size of index on flash
* @total_free: total free space in bytes
* @total_dirty: total dirty space in bytes
* @total_used: total used space in bytes (includes only data LEBs)
* @total_dead: total dead space in bytes (includes only data LEBs)
* @total_dark: total dark space in bytes (includes only data LEBs)
* @lpt_lnum: LEB number of LPT root nnode
* @lpt_offs: offset of LPT root nnode
* @nhead_lnum: LEB number of LPT head
* @nhead_offs: offset of LPT head
* @ltab_lnum: LEB number of LPT's own lprops table
* @ltab_offs: offset of LPT's own lprops table
* @lsave_lnum: LEB number of LPT's save table (big model only)
* @lsave_offs: offset of LPT's save table (big model only)
* @lscan_lnum: LEB number of last LPT scan
* @empty_lebs: number of empty logical eraseblocks
* @idx_lebs: number of indexing logical eraseblocks
* @leb_cnt: count of LEBs used by file-system
* @padding: reserved for future, zeroes
*/
struct ubifs_mst_node {
struct ubifs_ch ch;
__le64 highest_inum;
__le64 cmt_no;
__le32 flags;
__le32 log_lnum;
__le32 root_lnum;
__le32 root_offs;
__le32 root_len;
__le32 gc_lnum;
__le32 ihead_lnum;
__le32 ihead_offs;
__le64 index_size;
__le64 total_free;
__le64 total_dirty;
__le64 total_used;
__le64 total_dead;
__le64 total_dark;
__le32 lpt_lnum;
__le32 lpt_offs;
__le32 nhead_lnum;
__le32 nhead_offs;
__le32 ltab_lnum;
__le32 ltab_offs;
__le32 lsave_lnum;
__le32 lsave_offs;
__le32 lscan_lnum;
__le32 empty_lebs;
__le32 idx_lebs;
__le32 leb_cnt;
__u8 padding[344];
} __attribute__ ((packed));
/**
* struct ubifs_ref_node - logical eraseblock reference node.
* @ch: common header
* @lnum: the referred logical eraseblock number
* @offs: start offset in the referred LEB
* @jhead: journal head number
* @padding: reserved for future, zeroes
*/
struct ubifs_ref_node {
struct ubifs_ch ch;
__le32 lnum;
__le32 offs;
__le32 jhead;
__u8 padding[28];
} __attribute__ ((packed));
/**
* struct ubifs_branch - key/reference/length branch
* @lnum: LEB number of the target node
* @offs: offset within @lnum
* @len: target node length
* @key: key
*/
struct ubifs_branch {
__le32 lnum;
__le32 offs;
__le32 len;
__u8 key[];
} __attribute__ ((packed));
/**
* struct ubifs_idx_node - indexing node.
* @ch: common header
* @child_cnt: number of child index nodes
* @level: tree level
* @branches: LEB number / offset / length / key branches
*/
struct ubifs_idx_node {
struct ubifs_ch ch;
__le16 child_cnt;
__le16 level;
__u8 branches[];
} __attribute__ ((packed));
/**
* struct ubifs_cs_node - commit start node.
* @ch: common header
* @cmt_no: commit number
*/
struct ubifs_cs_node {
struct ubifs_ch ch;
__le64 cmt_no;
} __attribute__ ((packed));
/**
* struct ubifs_orph_node - orphan node.
* @ch: common header
* @cmt_no: commit number (also top bit is set on the last node of the commit)
* @inos: inode numbers of orphans
*/
struct ubifs_orph_node {
struct ubifs_ch ch;
__le64 cmt_no;
__le64 inos[];
} __attribute__ ((packed));
#endif /* __UBIFS_MEDIA_H__ */

View file

@ -0,0 +1,752 @@
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* (C) Copyright 2008-2010
* Stefan Roese, DENX Software Engineering, sr@denx.de.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
#include "ubifs.h"
#include <u-boot/zlib.h>
DECLARE_GLOBAL_DATA_PTR;
/* compress.c */
/*
* We need a wrapper for zunzip() because the parameters are
* incompatible with the lzo decompressor.
*/
static int gzip_decompress(const unsigned char *in, size_t in_len,
unsigned char *out, size_t *out_len)
{
unsigned long len = in_len;
return zunzip(out, *out_len, (unsigned char *)in, &len, 0, 0);
}
/* Fake description object for the "none" compressor */
static struct ubifs_compressor none_compr = {
.compr_type = UBIFS_COMPR_NONE,
.name = "no compression",
.capi_name = "",
.decompress = NULL,
};
static struct ubifs_compressor lzo_compr = {
.compr_type = UBIFS_COMPR_LZO,
.name = "LZO",
.capi_name = "lzo",
.decompress = lzo1x_decompress_safe,
};
static struct ubifs_compressor zlib_compr = {
.compr_type = UBIFS_COMPR_ZLIB,
.name = "zlib",
.capi_name = "deflate",
.decompress = gzip_decompress,
};
/* All UBIFS compressors */
struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
/**
* ubifs_decompress - decompress data.
* @in_buf: data to decompress
* @in_len: length of the data to decompress
* @out_buf: output buffer where decompressed data should
* @out_len: output length is returned here
* @compr_type: type of compression
*
* This function decompresses data from buffer @in_buf into buffer @out_buf.
* The length of the uncompressed data is returned in @out_len. This functions
* returns %0 on success or a negative error code on failure.
*/
int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
int *out_len, int compr_type)
{
int err;
struct ubifs_compressor *compr;
if (unlikely(compr_type < 0 || compr_type >= UBIFS_COMPR_TYPES_CNT)) {
ubifs_err("invalid compression type %d", compr_type);
return -EINVAL;
}
compr = ubifs_compressors[compr_type];
if (unlikely(!compr->capi_name)) {
ubifs_err("%s compression is not compiled in", compr->name);
return -EINVAL;
}
if (compr_type == UBIFS_COMPR_NONE) {
memcpy(out_buf, in_buf, in_len);
*out_len = in_len;
return 0;
}
err = compr->decompress(in_buf, in_len, out_buf, (size_t *)out_len);
if (err)
ubifs_err("cannot decompress %d bytes, compressor %s, "
"error %d", in_len, compr->name, err);
return err;
}
/**
* compr_init - initialize a compressor.
* @compr: compressor description object
*
* This function initializes the requested compressor and returns zero in case
* of success or a negative error code in case of failure.
*/
static int __init compr_init(struct ubifs_compressor *compr)
{
ubifs_compressors[compr->compr_type] = compr;
#ifdef CONFIG_NEEDS_MANUAL_RELOC
ubifs_compressors[compr->compr_type]->name += gd->reloc_off;
ubifs_compressors[compr->compr_type]->capi_name += gd->reloc_off;
ubifs_compressors[compr->compr_type]->decompress += gd->reloc_off;
#endif
return 0;
}
/**
* ubifs_compressors_init - initialize UBIFS compressors.
*
* This function initializes the compressor which were compiled in. Returns
* zero in case of success and a negative error code in case of failure.
*/
int __init ubifs_compressors_init(void)
{
int err;
err = compr_init(&lzo_compr);
if (err)
return err;
err = compr_init(&zlib_compr);
if (err)
return err;
err = compr_init(&none_compr);
if (err)
return err;
return 0;
}
/*
* ubifsls...
*/
static int filldir(struct ubifs_info *c, const char *name, int namlen,
u64 ino, unsigned int d_type)
{
struct inode *inode;
char filetime[32];
switch (d_type) {
case UBIFS_ITYPE_REG:
printf("\t");
break;
case UBIFS_ITYPE_DIR:
printf("<DIR>\t");
break;
case UBIFS_ITYPE_LNK:
printf("<LNK>\t");
break;
default:
printf("other\t");
break;
}
inode = ubifs_iget(c->vfs_sb, ino);
if (IS_ERR(inode)) {
printf("%s: Error in ubifs_iget(), ino=%lld ret=%p!\n",
__func__, ino, inode);
return -1;
}
ctime_r((time_t *)&inode->i_mtime, filetime);
printf("%9lld %24.24s ", inode->i_size, filetime);
ubifs_iput(inode);
printf("%s\n", name);
return 0;
}
static int ubifs_printdir(struct file *file, void *dirent)
{
int err, over = 0;
struct qstr nm;
union ubifs_key key;
struct ubifs_dent_node *dent;
struct inode *dir = file->f_path.dentry->d_inode;
struct ubifs_info *c = dir->i_sb->s_fs_info;
dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);
if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2)
/*
* The directory was seek'ed to a senseless position or there
* are no more entries.
*/
return 0;
if (file->f_pos == 1) {
/* Find the first entry in TNC and save it */
lowest_dent_key(c, &key, dir->i_ino);
nm.name = NULL;
dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
goto out;
}
file->f_pos = key_hash_flash(c, &dent->key);
file->private_data = dent;
}
dent = file->private_data;
if (!dent) {
/*
* The directory was seek'ed to and is now readdir'ed.
* Find the entry corresponding to @file->f_pos or the
* closest one.
*/
dent_key_init_hash(c, &key, dir->i_ino, file->f_pos);
nm.name = NULL;
dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
goto out;
}
file->f_pos = key_hash_flash(c, &dent->key);
file->private_data = dent;
}
while (1) {
dbg_gen("feed '%s', ino %llu, new f_pos %#x",
dent->name, (unsigned long long)le64_to_cpu(dent->inum),
key_hash_flash(c, &dent->key));
ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum);
nm.len = le16_to_cpu(dent->nlen);
over = filldir(c, (char *)dent->name, nm.len,
le64_to_cpu(dent->inum), dent->type);
if (over)
return 0;
/* Switch to the next entry */
key_read(c, &dent->key, &key);
nm.name = (char *)dent->name;
dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
goto out;
}
kfree(file->private_data);
file->f_pos = key_hash_flash(c, &dent->key);
file->private_data = dent;
cond_resched();
}
out:
if (err != -ENOENT) {
ubifs_err("cannot find next direntry, error %d", err);
return err;
}
kfree(file->private_data);
file->private_data = NULL;
file->f_pos = 2;
return 0;
}
static int ubifs_finddir(struct super_block *sb, char *dirname,
unsigned long root_inum, unsigned long *inum)
{
int err;
struct qstr nm;
union ubifs_key key;
struct ubifs_dent_node *dent;
struct ubifs_info *c;
struct file *file;
struct dentry *dentry;
struct inode *dir;
file = kzalloc(sizeof(struct file), 0);
dentry = kzalloc(sizeof(struct dentry), 0);
dir = kzalloc(sizeof(struct inode), 0);
if (!file || !dentry || !dir) {
printf("%s: Error, no memory for malloc!\n", __func__);
err = -ENOMEM;
goto out;
}
dir->i_sb = sb;
file->f_path.dentry = dentry;
file->f_path.dentry->d_parent = dentry;
file->f_path.dentry->d_inode = dir;
file->f_path.dentry->d_inode->i_ino = root_inum;
c = sb->s_fs_info;
dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);
/* Find the first entry in TNC and save it */
lowest_dent_key(c, &key, dir->i_ino);
nm.name = NULL;
dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
goto out;
}
file->f_pos = key_hash_flash(c, &dent->key);
file->private_data = dent;
while (1) {
dbg_gen("feed '%s', ino %llu, new f_pos %#x",
dent->name, (unsigned long long)le64_to_cpu(dent->inum),
key_hash_flash(c, &dent->key));
ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum);
nm.len = le16_to_cpu(dent->nlen);
if ((strncmp(dirname, (char *)dent->name, nm.len) == 0) &&
(strlen(dirname) == nm.len)) {
*inum = le64_to_cpu(dent->inum);
return 1;
}
/* Switch to the next entry */
key_read(c, &dent->key, &key);
nm.name = (char *)dent->name;
dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
goto out;
}
kfree(file->private_data);
file->f_pos = key_hash_flash(c, &dent->key);
file->private_data = dent;
cond_resched();
}
out:
if (err != -ENOENT) {
ubifs_err("cannot find next direntry, error %d", err);
return err;
}
if (file->private_data)
kfree(file->private_data);
if (file)
free(file);
if (dentry)
free(dentry);
if (dir)
free(dir);
return 0;
}
static unsigned long ubifs_findfile(struct super_block *sb, char *filename)
{
int ret;
char *next;
char fpath[128];
char symlinkpath[128];
char *name = fpath;
unsigned long root_inum = 1;
unsigned long inum;
int symlink_count = 0; /* Don't allow symlink recursion */
char link_name[64];
strcpy(fpath, filename);
/* Remove all leading slashes */
while (*name == '/')
name++;
/*
* Handle root-direcoty ('/')
*/
inum = root_inum;
if (!name || *name == '\0')
return inum;
for (;;) {
struct inode *inode;
struct ubifs_inode *ui;
/* Extract the actual part from the pathname. */
next = strchr(name, '/');
if (next) {
/* Remove all leading slashes. */
while (*next == '/')
*(next++) = '\0';
}
ret = ubifs_finddir(sb, name, root_inum, &inum);
if (!ret)
return 0;
inode = ubifs_iget(sb, inum);
if (!inode)
return 0;
ui = ubifs_inode(inode);
if ((inode->i_mode & S_IFMT) == S_IFLNK) {
char buf[128];
/* We have some sort of symlink recursion, bail out */
if (symlink_count++ > 8) {
printf("Symlink recursion, aborting\n");
return 0;
}
memcpy(link_name, ui->data, ui->data_len);
link_name[ui->data_len] = '\0';
if (link_name[0] == '/') {
/* Absolute path, redo everything without
* the leading slash */
next = name = link_name + 1;
root_inum = 1;
continue;
}
/* Relative to cur dir */
sprintf(buf, "%s/%s",
link_name, next == NULL ? "" : next);
memcpy(symlinkpath, buf, sizeof(buf));
next = name = symlinkpath;
continue;
}
/*
* Check if directory with this name exists
*/
/* Found the node! */
if (!next || *next == '\0')
return inum;
root_inum = inum;
name = next;
}
return 0;
}
int ubifs_ls(char *filename)
{
struct ubifs_info *c = ubifs_sb->s_fs_info;
struct file *file;
struct dentry *dentry;
struct inode *dir;
void *dirent = NULL;
unsigned long inum;
int ret = 0;
c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
inum = ubifs_findfile(ubifs_sb, filename);
if (!inum) {
ret = -1;
goto out;
}
file = kzalloc(sizeof(struct file), 0);
dentry = kzalloc(sizeof(struct dentry), 0);
dir = kzalloc(sizeof(struct inode), 0);
if (!file || !dentry || !dir) {
printf("%s: Error, no memory for malloc!\n", __func__);
ret = -ENOMEM;
goto out_mem;
}
dir->i_sb = ubifs_sb;
file->f_path.dentry = dentry;
file->f_path.dentry->d_parent = dentry;
file->f_path.dentry->d_inode = dir;
file->f_path.dentry->d_inode->i_ino = inum;
file->f_pos = 1;
file->private_data = NULL;
ubifs_printdir(file, dirent);
out_mem:
if (file)
free(file);
if (dentry)
free(dentry);
if (dir)
free(dir);
out:
ubi_close_volume(c->ubi);
return ret;
}
/*
* ubifsload...
*/
/* file.c */
static inline void *kmap(struct page *page)
{
return page->addr;
}
static int read_block(struct inode *inode, void *addr, unsigned int block,
struct ubifs_data_node *dn)
{
struct ubifs_info *c = inode->i_sb->s_fs_info;
int err, len, out_len;
union ubifs_key key;
unsigned int dlen;
data_key_init(c, &key, inode->i_ino, block);
err = ubifs_tnc_lookup(c, &key, dn);
if (err) {
if (err == -ENOENT)
/* Not found, so it must be a hole */
memset(addr, 0, UBIFS_BLOCK_SIZE);
return err;
}
ubifs_assert(le64_to_cpu(dn->ch.sqnum) > ubifs_inode(inode)->creat_sqnum);
len = le32_to_cpu(dn->size);
if (len <= 0 || len > UBIFS_BLOCK_SIZE)
goto dump;
dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
out_len = UBIFS_BLOCK_SIZE;
err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
le16_to_cpu(dn->compr_type));
if (err || len != out_len)
goto dump;
/*
* Data length can be less than a full block, even for blocks that are
* not the last in the file (e.g., as a result of making a hole and
* appending data). Ensure that the remainder is zeroed out.
*/
if (len < UBIFS_BLOCK_SIZE)
memset(addr + len, 0, UBIFS_BLOCK_SIZE - len);
return 0;
dump:
ubifs_err("bad data node (block %u, inode %lu)",
block, inode->i_ino);
dbg_dump_node(c, dn);
return -EINVAL;
}
static int do_readpage(struct ubifs_info *c, struct inode *inode,
struct page *page, int last_block_size)
{
void *addr;
int err = 0, i;
unsigned int block, beyond;
struct ubifs_data_node *dn;
loff_t i_size = inode->i_size;
dbg_gen("ino %lu, pg %lu, i_size %lld",
inode->i_ino, page->index, i_size);
addr = kmap(page);
block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
beyond = (i_size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
if (block >= beyond) {
/* Reading beyond inode */
memset(addr, 0, PAGE_CACHE_SIZE);
goto out;
}
dn = kmalloc(UBIFS_MAX_DATA_NODE_SZ, GFP_NOFS);
if (!dn)
return -ENOMEM;
i = 0;
while (1) {
int ret;
if (block >= beyond) {
/* Reading beyond inode */
err = -ENOENT;
memset(addr, 0, UBIFS_BLOCK_SIZE);
} else {
/*
* Reading last block? Make sure to not write beyond
* the requested size in the destination buffer.
*/
if (((block + 1) == beyond) || last_block_size) {
void *buff;
int dlen;
/*
* We need to buffer the data locally for the
* last block. This is to not pad the
* destination area to a multiple of
* UBIFS_BLOCK_SIZE.
*/
buff = malloc(UBIFS_BLOCK_SIZE);
if (!buff) {
printf("%s: Error, malloc fails!\n",
__func__);
err = -ENOMEM;
break;
}
/* Read block-size into temp buffer */
ret = read_block(inode, buff, block, dn);
if (ret) {
err = ret;
if (err != -ENOENT) {
free(buff);
break;
}
}
if (last_block_size)
dlen = last_block_size;
else
dlen = le32_to_cpu(dn->size);
/* Now copy required size back to dest */
memcpy(addr, buff, dlen);
free(buff);
} else {
ret = read_block(inode, addr, block, dn);
if (ret) {
err = ret;
if (err != -ENOENT)
break;
}
}
}
if (++i >= UBIFS_BLOCKS_PER_PAGE)
break;
block += 1;
addr += UBIFS_BLOCK_SIZE;
}
if (err) {
if (err == -ENOENT) {
/* Not found, so it must be a hole */
dbg_gen("hole");
goto out_free;
}
ubifs_err("cannot read page %lu of inode %lu, error %d",
page->index, inode->i_ino, err);
goto error;
}
out_free:
kfree(dn);
out:
return 0;
error:
kfree(dn);
return err;
}
int ubifs_load(char *filename, u32 addr, u32 size)
{
struct ubifs_info *c = ubifs_sb->s_fs_info;
unsigned long inum;
struct inode *inode;
struct page page;
int err = 0;
int i;
int count;
int last_block_size = 0;
char buf [10];
c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
/* ubifs_findfile will resolve symlinks, so we know that we get
* the real file here */
inum = ubifs_findfile(ubifs_sb, filename);
if (!inum) {
err = -1;
goto out;
}
/*
* Read file inode
*/
inode = ubifs_iget(ubifs_sb, inum);
if (IS_ERR(inode)) {
printf("%s: Error reading inode %ld!\n", __func__, inum);
err = PTR_ERR(inode);
goto out;
}
/*
* If no size was specified or if size bigger than filesize
* set size to filesize
*/
if ((size == 0) || (size > inode->i_size))
size = inode->i_size;
count = (size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
printf("Loading file '%s' to addr 0x%08x with size %d (0x%08x)...\n",
filename, addr, size, size);
page.addr = (void *)addr;
page.index = 0;
page.inode = inode;
for (i = 0; i < count; i++) {
/*
* Make sure to not read beyond the requested size
*/
if (((i + 1) == count) && (size < inode->i_size))
last_block_size = size - (i * PAGE_SIZE);
err = do_readpage(c, inode, &page, last_block_size);
if (err)
break;
page.addr += PAGE_SIZE;
page.index++;
}
if (err)
printf("Error reading file '%s'\n", filename);
else {
sprintf(buf, "%X", size);
setenv("filesize", buf);
printf("Done\n");
}
ubifs_iput(inode);
out:
ubi_close_volume(c->ubi);
return err;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
# Makefile for YAFFS direct test
#
#
# YAFFS: Yet another Flash File System. A NAND-flash specific file system.
#
# Copyright (C) 2003 Aleph One Ltd.
#
#
# Created by Charles Manning <charles@aleph1.co.uk>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# NB Warning this Makefile does not include header dependencies.
#
# $Id: Makefile,v 1.15 2007/07/18 19:40:38 charles Exp $
#EXTRA_COMPILE_FLAGS = -DYAFFS_IGNORE_TAGS_ECC
include $(TOPDIR)/config.mk
LIB = $(obj)libyaffs2.o
COBJS-$(CONFIG_YAFFS2) := \
yaffscfg.o yaffs_ecc.o yaffsfs.o yaffs_guts.o yaffs_packedtags1.o \
yaffs_tagscompat.o yaffs_packedtags2.o yaffs_tagsvalidity.o \
yaffs_nand.o yaffs_checkptrw.o yaffs_qsort.o yaffs_mtdif.o \
yaffs_mtdif2.o
SRCS := $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS-y))
# -DCONFIG_YAFFS_NO_YAFFS1
CFLAGS += -DCONFIG_YAFFS_DIRECT -DCONFIG_YAFFS_SHORT_NAMES_IN_RAM -DCONFIG_YAFFS_YAFFS2 -DLINUX_VERSION_CODE=0x20622
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,201 @@
Welcome to YAFFS, the first file system developed specifically for NAND flash.
It is now YAFFS2 - original YAFFS (AYFFS1) only supports 512-byte page
NAND and is now deprectated. YAFFS2 supports 512b page in 'YAFFS1
compatibility' mode (CONFIG_YAFFS_YAFFS1) and 2K or larger page NAND
in YAFFS2 mode (CONFIG_YAFFS_YAFFS2).
A note on licencing
-------------------
YAFFS is available under the GPL and via alternative licensing
arrangements with Aleph One. If you're using YAFFS as a Linux kernel
file system then it will be under the GPL. For use in other situations
you should discuss licensing issues with Aleph One.
Terminology
-----------
Page - NAND addressable unit (normally 512b or 2Kbyte size) - can
be read, written, marked bad. Has associated OOB.
Block - Eraseable unit. 64 Pages. (128K on 2K NAND, 32K on 512b NAND)
OOB - 'spare area' of each page for ECC, bad block marked and YAFFS
tags. 16 bytes per 512b - 64 bytes for 2K page size.
Chunk - Basic YAFFS addressable unit. Same size as Page.
Object - YAFFS Object: File, Directory, Link, Device etc.
YAFFS design
------------
YAFFS is a log-structured filesystem. It is designed particularly for
NAND (as opposed to NOR) flash, to be flash-friendly, robust due to
journalling, and to have low RAM and boot time overheads. File data is
stored in 'chunks'. Chunks are the same size as NAND pages. Each page
is marked with file id and chunk number. These marking 'tags' are
stored in the OOB (or 'spare') region of the flash. The chunk number
is determined by dividing the file position by the chunk size. Each
chunk has a number of valid bytes, which equals the page size for all
except the last chunk in a file.
File 'headers' are stored as the first page in a file, marked as a
different type to data pages. The same mechanism is used to store
directories, device files, links etc. The first page describes which
type of object it is.
YAFFS2 never re-writes a page, because the spec of NAND chips does not
allow it. (YAFFS1 used to mark a block 'deleted' in the OOB). Deletion
is managed by moving deleted objects to the special, hidden 'unlinked'
directory. These records are preserved until all the pages containing
the object have been erased (We know when this happen by keeping a
count of chunks remaining on the system for each object - when it
reaches zero the object really is gone).
When data in a file is overwritten, the relevant chunks are replaced
by writing new pages to flash containing the new data but the same
tags.
Pages are also marked with a short (2 bit) serial number that
increments each time the page at this position is incremented. The
reason for this is that if power loss/crash/other act of demonic
forces happens before the replaced page is marked as discarded, it is
possible to have two pages with the same tags. The serial number is
used to arbitrate.
A block containing only discarded pages (termed a dirty block) is an
obvious candidate for garbage collection. Otherwise valid pages can be
copied off a block thus rendering the whole block discarded and ready
for garbage collection.
In theory you don't need to hold the file structure in RAM... you
could just scan the whole flash looking for pages when you need them.
In practice though you'd want better file access times than that! The
mechanism proposed here is to have a list of __u16 page addresses
associated with each file. Since there are 2^18 pages in a 128MB NAND,
a __u16 is insufficient to uniquely identify a page but is does
identify a group of 4 pages - a small enough region to search
exhaustively. This mechanism is clearly expandable to larger NAND
devices - within reason. The RAM overhead with this approach is approx
2 bytes per page - 512kB of RAM for a whole 128MB NAND.
Boot-time scanning to build the file structure lists only requires
one pass reading NAND. If proper shutdowns happen the current RAM
summary of the filesystem status is saved to flash, called
'checkpointing'. This saves re-scanning the flash on startup, and gives
huge boot/mount time savings.
YAFFS regenerates its state by 'replaying the tape' - i.e. by
scanning the chunks in their allocation order (i.e. block sequence ID
order), which is usually different form the media block order. Each
block is still only read once - starting from the end of the media and
working back.
YAFFS tags in YAFFS1 mode:
18-bit Object ID (2^18 files, i.e. > 260,000 files). File id 0- is not
valid and indicates a deleted page. File od 0x3ffff is also not valid.
Synonymous with inode.
2-bit serial number
20-bit Chunk ID within file. Limit of 2^20 chunks/pages per file (i.e.
> 500MB max file size). Chunk ID 0 is the file header for the file.
10-bit counter of the number of bytes used in the page.
12 bit ECC on tags
YAFFS tags in YAFFS2 mode:
4 bytes 32-bit chunk ID
4 bytes 32-bit object ID
2 bytes Number of data bytes in this chunk
4 bytes Sequence number for this block
3 bytes ECC on tags
12 bytes ECC on data (3 bytes per 256 bytes of data)
Page allocation and garbage collection
Pages are allocated sequentially from the currently selected block.
When all the pages in the block are filled, another clean block is
selected for allocation. At least two or three clean blocks are
reserved for garbage collection purposes. If there are insufficient
clean blocks available, then a dirty block ( ie one containing only
discarded pages) is erased to free it up as a clean block. If no dirty
blocks are available, then the dirtiest block is selected for garbage
collection.
Garbage collection is performed by copying the valid data pages into
new data pages thus rendering all the pages in this block dirty and
freeing it up for erasure. I also like the idea of selecting a block
at random some small percentage of the time - thus reducing the chance
of wear differences.
YAFFS is single-threaded. Garbage-collection is done as a parasitic
task of writing data. So each time some data is written, a bit of
pending garbage collection is done. More pages are garbage-collected
when free space is tight.
Flash writing
YAFFS only ever writes each page once, complying with the requirements
of the most restricitve NAND devices.
Wear levelling
This comes as a side-effect of the block-allocation strategy. Data is
always written on the next free block, so they are all used equally.
Blocks containing data that is written but never erased will not get
back into the free list, so wear is levelled over only blocks which
are free or become free, not blocks which never change.
Some helpful info
-----------------
Formatting a YAFFS device is simply done by erasing it.
Making an initial filesystem can be tricky because YAFFS uses the OOB
and thus the bytes that get written depend on the YAFFS data (tags),
and the ECC bytes and bad block markers which are dictated by the
hardware and/or the MTD subsystem. The data layout also depends on the
device page size (512b or 2K). Because YAFFS is only responsible for
some of the OOB data, generating a filesystem offline requires
detailed knowledge of what the other parts (MTD and NAND
driver/hardware) are going to do.
To make a YAFFS filesystem you have 3 options:
1) Boot the system with an empty NAND device mounted as YAFFS and copy
stuff on.
2) Make a filesystem image offline, then boot the system and use
MTDutils to write an image to flash.
3) Make a filesystem image offline and use some tool like a bootloader to
write it to flash.
Option 1 avoids a lot of issues because all the parts
(YAFFS/MTD/hardware) all take care of their own bits and (if you have
put things together properly) it will 'just work'. YAFFS just needs to
know how many bytes of the OOB it can use. However sometimes it is not
practical.
Option 2 lets MTD/hardware take care of the ECC so the filesystem
image just had to know which bytes to use for YAFFS Tags.
Option 3 is hardest as the image creator needs to know exactly what
ECC bytes, endianness and algorithm to use as well as which bytes are
available to YAFFS.
mkyaffs2image creates an image suitable for option 3 for the
particular case of yaffs2 on 2K page NAND with default MTD layout.
mkyaffsimage creates an equivalent image for 512b page NAND (i.e.
yaffs1 format).
Bootloaders
-----------
A bootloader using YAFFS needs to know how MTD is laying out the OOB
so that it can skip bad blocks.
YAFFS Tracing
-------------

View file

@ -0,0 +1,275 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/*
* This file is just holds extra declarations used during development.
* Most of these are from kernel includes placed here so we can use them in
* applications.
*
*/
#ifndef __EXTRAS_H__
#define __EXTRAS_H__
#if defined WIN32
#define __inline__ __inline
#define new newHack
#endif
/* XXX U-BOOT XXX */
#if 1 /* !(defined __KERNEL__) || (defined WIN32) */
/* User space defines */
/* XXX U-BOOT XXX */
#if 0
typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned __u32;
#endif
#include <asm/types.h>
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#define prefetch(x) 1
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static __inline__ void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static __inline__ void list_add_tail(struct list_head *new,
struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_del(struct list_head *prev,
struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static __inline__ void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static __inline__ void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static __inline__ int list_empty(struct list_head *head)
{
return head->next == head;
}
/**
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static __inline__ void list_splice(struct list_head *list,
struct list_head *head)
{
struct list_head *first = list->next;
if (first != list) {
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, prefetch(pos->next))
/**
* list_for_each_safe - iterate over a list safe against removal
* of list entry
* @pos: the &struct list_head to use as a loop counter.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/*
* File types
*/
#define DT_UNKNOWN 0
#define DT_FIFO 1
#define DT_CHR 2
#define DT_DIR 4
#define DT_BLK 6
#define DT_REG 8
#define DT_LNK 10
#define DT_SOCK 12
#define DT_WHT 14
#ifndef WIN32
/* XXX U-BOOT XXX */
#if 0
#include <sys/stat.h>
#else
#include "common.h"
#endif
#endif
/*
* Attribute flags. These should be or-ed together to figure out what
* has been changed!
*/
#define ATTR_MODE 1
#define ATTR_UID 2
#define ATTR_GID 4
#define ATTR_SIZE 8
#define ATTR_ATIME 16
#define ATTR_MTIME 32
#define ATTR_CTIME 64
#define ATTR_ATIME_SET 128
#define ATTR_MTIME_SET 256
#define ATTR_FORCE 512 /* Not a change, but a change it */
#define ATTR_ATTR_FLAG 1024
struct iattr {
unsigned int ia_valid;
unsigned ia_mode;
unsigned ia_uid;
unsigned ia_gid;
unsigned ia_size;
unsigned ia_atime;
unsigned ia_mtime;
unsigned ia_ctime;
unsigned int ia_attr_flags;
};
#define KERN_DEBUG
#else
#ifndef WIN32
#include <linux/types.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/stat.h>
#endif
#endif
#if defined WIN32
#undef new
#endif
#endif

View file

@ -0,0 +1,405 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
#include <malloc.h>
const char *yaffs_checkptrw_c_version =
"$Id: yaffs_checkptrw.c,v 1.14 2007/05/15 20:07:40 charles Exp $";
#include "yaffs_checkptrw.h"
static int yaffs_CheckpointSpaceOk(yaffs_Device *dev)
{
int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
T(YAFFS_TRACE_CHECKPOINT,
(TSTR("checkpt blocks available = %d" TENDSTR),
blocksAvailable));
return (blocksAvailable <= 0) ? 0 : 1;
}
static int yaffs_CheckpointErase(yaffs_Device *dev)
{
int i;
if(!dev->eraseBlockInNAND)
return 0;
T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR),
dev->internalStartBlock,dev->internalEndBlock));
for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){
T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i));
if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){
bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
dev->nErasedBlocks++;
dev->nFreeChunks += dev->nChunksPerBlock;
}
else {
dev->markNANDBlockBad(dev,i);
bi->blockState = YAFFS_BLOCK_STATE_DEAD;
}
}
}
dev->blocksInCheckpoint = 0;
return 1;
}
static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev)
{
int i;
int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
T(YAFFS_TRACE_CHECKPOINT,
(TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR),
dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock));
if(dev->checkpointNextBlock >= 0 &&
dev->checkpointNextBlock <= dev->internalEndBlock &&
blocksAvailable > 0){
for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){
dev->checkpointNextBlock = i + 1;
dev->checkpointCurrentBlock = i;
T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i));
return;
}
}
}
T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR)));
dev->checkpointNextBlock = -1;
dev->checkpointCurrentBlock = -1;
}
static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev)
{
int i;
yaffs_ExtendedTags tags;
T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start: blocks %d next %d" TENDSTR),
dev->blocksInCheckpoint, dev->checkpointNextBlock));
if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks)
for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){
int chunk = i * dev->nChunksPerBlock;
int realignedChunk = chunk - dev->chunkOffset;
dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags);
T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR),
i, tags.objectId,tags.sequenceNumber,tags.eccResult));
if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){
/* Right kind of block */
dev->checkpointNextBlock = tags.objectId;
dev->checkpointCurrentBlock = i;
dev->checkpointBlockList[dev->blocksInCheckpoint] = i;
dev->blocksInCheckpoint++;
T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i));
return;
}
}
T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR)));
dev->checkpointNextBlock = -1;
dev->checkpointCurrentBlock = -1;
}
int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting)
{
/* Got the functions we need? */
if (!dev->writeChunkWithTagsToNAND ||
!dev->readChunkWithTagsFromNAND ||
!dev->eraseBlockInNAND ||
!dev->markNANDBlockBad)
return 0;
if(forWriting && !yaffs_CheckpointSpaceOk(dev))
return 0;
if(!dev->checkpointBuffer)
dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk);
if(!dev->checkpointBuffer)
return 0;
dev->checkpointPageSequence = 0;
dev->checkpointOpenForWrite = forWriting;
dev->checkpointByteCount = 0;
dev->checkpointSum = 0;
dev->checkpointXor = 0;
dev->checkpointCurrentBlock = -1;
dev->checkpointCurrentChunk = -1;
dev->checkpointNextBlock = dev->internalStartBlock;
/* Erase all the blocks in the checkpoint area */
if(forWriting){
memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
dev->checkpointByteOffset = 0;
return yaffs_CheckpointErase(dev);
} else {
int i;
/* Set to a value that will kick off a read */
dev->checkpointByteOffset = dev->nDataBytesPerChunk;
/* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully)
* going to be way more than we need */
dev->blocksInCheckpoint = 0;
dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2;
dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks);
for(i = 0; i < dev->checkpointMaxBlocks; i++)
dev->checkpointBlockList[i] = -1;
}
return 1;
}
int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum)
{
__u32 compositeSum;
compositeSum = (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF);
*sum = compositeSum;
return 1;
}
static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
{
int chunk;
int realignedChunk;
yaffs_ExtendedTags tags;
if(dev->checkpointCurrentBlock < 0){
yaffs_CheckpointFindNextErasedBlock(dev);
dev->checkpointCurrentChunk = 0;
}
if(dev->checkpointCurrentBlock < 0)
return 0;
tags.chunkDeleted = 0;
tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */
tags.chunkId = dev->checkpointPageSequence + 1;
tags.sequenceNumber = YAFFS_SEQUENCE_CHECKPOINT_DATA;
tags.byteCount = dev->nDataBytesPerChunk;
if(dev->checkpointCurrentChunk == 0){
/* First chunk we write for the block? Set block state to
checkpoint */
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock);
bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
dev->blocksInCheckpoint++;
}
chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk;
T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR),
chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId));
realignedChunk = chunk - dev->chunkOffset;
dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags);
dev->checkpointByteOffset = 0;
dev->checkpointPageSequence++;
dev->checkpointCurrentChunk++;
if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){
dev->checkpointCurrentChunk = 0;
dev->checkpointCurrentBlock = -1;
}
memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
return 1;
}
int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes)
{
int i=0;
int ok = 1;
__u8 * dataBytes = (__u8 *)data;
if(!dev->checkpointBuffer)
return 0;
if(!dev->checkpointOpenForWrite)
return -1;
while(i < nBytes && ok) {
dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ;
dev->checkpointSum += *dataBytes;
dev->checkpointXor ^= *dataBytes;
dev->checkpointByteOffset++;
i++;
dataBytes++;
dev->checkpointByteCount++;
if(dev->checkpointByteOffset < 0 ||
dev->checkpointByteOffset >= dev->nDataBytesPerChunk)
ok = yaffs_CheckpointFlushBuffer(dev);
}
return i;
}
int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
{
int i=0;
int ok = 1;
yaffs_ExtendedTags tags;
int chunk;
int realignedChunk;
__u8 *dataBytes = (__u8 *)data;
if(!dev->checkpointBuffer)
return 0;
if(dev->checkpointOpenForWrite)
return -1;
while(i < nBytes && ok) {
if(dev->checkpointByteOffset < 0 ||
dev->checkpointByteOffset >= dev->nDataBytesPerChunk) {
if(dev->checkpointCurrentBlock < 0){
yaffs_CheckpointFindNextCheckpointBlock(dev);
dev->checkpointCurrentChunk = 0;
}
if(dev->checkpointCurrentBlock < 0)
ok = 0;
else {
chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock +
dev->checkpointCurrentChunk;
realignedChunk = chunk - dev->chunkOffset;
/* read in the next chunk */
/* printf("read checkpoint page %d\n",dev->checkpointPage); */
dev->readChunkWithTagsFromNAND(dev, realignedChunk,
dev->checkpointBuffer,
&tags);
if(tags.chunkId != (dev->checkpointPageSequence + 1) ||
tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
ok = 0;
dev->checkpointByteOffset = 0;
dev->checkpointPageSequence++;
dev->checkpointCurrentChunk++;
if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
dev->checkpointCurrentBlock = -1;
}
}
if(ok){
*dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
dev->checkpointSum += *dataBytes;
dev->checkpointXor ^= *dataBytes;
dev->checkpointByteOffset++;
i++;
dataBytes++;
dev->checkpointByteCount++;
}
}
return i;
}
int yaffs_CheckpointClose(yaffs_Device *dev)
{
if(dev->checkpointOpenForWrite){
if(dev->checkpointByteOffset != 0)
yaffs_CheckpointFlushBuffer(dev);
} else {
int i;
for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]);
if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
else {
// Todo this looks odd...
}
}
YFREE(dev->checkpointBlockList);
dev->checkpointBlockList = NULL;
}
dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
dev->nErasedBlocks -= dev->blocksInCheckpoint;
T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR),
dev->checkpointByteCount));
if(dev->checkpointBuffer){
/* free the buffer */
YFREE(dev->checkpointBuffer);
dev->checkpointBuffer = NULL;
return 1;
}
else
return 0;
}
int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
{
/* Erase the first checksum block */
T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR)));
if(!yaffs_CheckpointSpaceOk(dev))
return 0;
return yaffs_CheckpointErase(dev);
}

View file

@ -0,0 +1,34 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_CHECKPTRW_H__
#define __YAFFS_CHECKPTRW_H__
#include "yaffs_guts.h"
int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting);
int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes);
int yaffs_CheckpointRead(yaffs_Device *dev,void *data, int nBytes);
int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum);
int yaffs_CheckpointClose(yaffs_Device *dev);
int yaffs_CheckpointInvalidateStream(yaffs_Device *dev);
#endif

View file

@ -0,0 +1,333 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* This code implements the ECC algorithm used in SmartMedia.
*
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
* The two unused bit are set to 1.
* The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC
* blocks are used on a 512-byte NAND page.
*
*/
/* Table generated by gen-ecc.c
* Using a table means we do not have to calculate p1..p4 and p1'..p4'
* for each byte of data. These are instead provided in a table in bits7..2.
* Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore
* this bytes influence on the line parity.
*/
/* XXX U-BOOT XXX */
#include <common.h>
const char *yaffs_ecc_c_version =
"$Id: yaffs_ecc.c,v 1.9 2007/02/14 01:09:06 wookey Exp $";
#include "yportenv.h"
#include "yaffs_ecc.h"
static const unsigned char column_parity_table[] = {
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
};
/* Count the bits in an unsigned char or a U32 */
static int yaffs_CountBits(unsigned char x)
{
int r = 0;
while (x) {
if (x & 1)
r++;
x >>= 1;
}
return r;
}
static int yaffs_CountBits32(unsigned x)
{
int r = 0;
while (x) {
if (x & 1)
r++;
x >>= 1;
}
return r;
}
/* Calculate the ECC for a 256-byte block of data */
void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc)
{
unsigned int i;
unsigned char col_parity = 0;
unsigned char line_parity = 0;
unsigned char line_parity_prime = 0;
unsigned char t;
unsigned char b;
for (i = 0; i < 256; i++) {
b = column_parity_table[*data++];
col_parity ^= b;
if (b & 0x01) // odd number of bits in the byte
{
line_parity ^= i;
line_parity_prime ^= ~i;
}
}
ecc[2] = (~col_parity) | 0x03;
t = 0;
if (line_parity & 0x80)
t |= 0x80;
if (line_parity_prime & 0x80)
t |= 0x40;
if (line_parity & 0x40)
t |= 0x20;
if (line_parity_prime & 0x40)
t |= 0x10;
if (line_parity & 0x20)
t |= 0x08;
if (line_parity_prime & 0x20)
t |= 0x04;
if (line_parity & 0x10)
t |= 0x02;
if (line_parity_prime & 0x10)
t |= 0x01;
ecc[1] = ~t;
t = 0;
if (line_parity & 0x08)
t |= 0x80;
if (line_parity_prime & 0x08)
t |= 0x40;
if (line_parity & 0x04)
t |= 0x20;
if (line_parity_prime & 0x04)
t |= 0x10;
if (line_parity & 0x02)
t |= 0x08;
if (line_parity_prime & 0x02)
t |= 0x04;
if (line_parity & 0x01)
t |= 0x02;
if (line_parity_prime & 0x01)
t |= 0x01;
ecc[0] = ~t;
#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
// Swap the bytes into the wrong order
t = ecc[0];
ecc[0] = ecc[1];
ecc[1] = t;
#endif
}
/* Correct the ECC on a 256 byte block of data */
int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc,
const unsigned char *test_ecc)
{
unsigned char d0, d1, d2; /* deltas */
d0 = read_ecc[0] ^ test_ecc[0];
d1 = read_ecc[1] ^ test_ecc[1];
d2 = read_ecc[2] ^ test_ecc[2];
if ((d0 | d1 | d2) == 0)
return 0; /* no error */
if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {
/* Single bit (recoverable) error in data */
unsigned byte;
unsigned bit;
#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
// swap the bytes to correct for the wrong order
unsigned char t;
t = d0;
d0 = d1;
d1 = t;
#endif
bit = byte = 0;
if (d1 & 0x80)
byte |= 0x80;
if (d1 & 0x20)
byte |= 0x40;
if (d1 & 0x08)
byte |= 0x20;
if (d1 & 0x02)
byte |= 0x10;
if (d0 & 0x80)
byte |= 0x08;
if (d0 & 0x20)
byte |= 0x04;
if (d0 & 0x08)
byte |= 0x02;
if (d0 & 0x02)
byte |= 0x01;
if (d2 & 0x80)
bit |= 0x04;
if (d2 & 0x20)
bit |= 0x02;
if (d2 & 0x08)
bit |= 0x01;
data[byte] ^= (1 << bit);
return 1; /* Corrected the error */
}
if ((yaffs_CountBits(d0) +
yaffs_CountBits(d1) +
yaffs_CountBits(d2)) == 1) {
/* Reccoverable error in ecc */
read_ecc[0] = test_ecc[0];
read_ecc[1] = test_ecc[1];
read_ecc[2] = test_ecc[2];
return 1; /* Corrected the error */
}
/* Unrecoverable error */
return -1;
}
/*
* ECCxxxOther does ECC calcs on arbitrary n bytes of data
*/
void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes,
yaffs_ECCOther * eccOther)
{
unsigned int i;
unsigned char col_parity = 0;
unsigned line_parity = 0;
unsigned line_parity_prime = 0;
unsigned char b;
for (i = 0; i < nBytes; i++) {
b = column_parity_table[*data++];
col_parity ^= b;
if (b & 0x01) {
/* odd number of bits in the byte */
line_parity ^= i;
line_parity_prime ^= ~i;
}
}
eccOther->colParity = (col_parity >> 2) & 0x3f;
eccOther->lineParity = line_parity;
eccOther->lineParityPrime = line_parity_prime;
}
int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes,
yaffs_ECCOther * read_ecc,
const yaffs_ECCOther * test_ecc)
{
unsigned char cDelta; /* column parity delta */
unsigned lDelta; /* line parity delta */
unsigned lDeltaPrime; /* line parity delta */
unsigned bit;
cDelta = read_ecc->colParity ^ test_ecc->colParity;
lDelta = read_ecc->lineParity ^ test_ecc->lineParity;
lDeltaPrime = read_ecc->lineParityPrime ^ test_ecc->lineParityPrime;
if ((cDelta | lDelta | lDeltaPrime) == 0)
return 0; /* no error */
if (lDelta == ~lDeltaPrime &&
(((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15))
{
/* Single bit (recoverable) error in data */
bit = 0;
if (cDelta & 0x20)
bit |= 0x04;
if (cDelta & 0x08)
bit |= 0x02;
if (cDelta & 0x02)
bit |= 0x01;
if(lDelta >= nBytes)
return -1;
data[lDelta] ^= (1 << bit);
return 1; /* corrected */
}
if ((yaffs_CountBits32(lDelta) + yaffs_CountBits32(lDeltaPrime) +
yaffs_CountBits(cDelta)) == 1) {
/* Reccoverable error in ecc */
*read_ecc = *test_ecc;
return 1; /* corrected */
}
/* Unrecoverable error */
return -1;
}

View file

@ -0,0 +1,44 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/*
* This code implements the ECC algorithm used in SmartMedia.
*
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
* The two unused bit are set to 1.
* The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC
* blocks are used on a 512-byte NAND page.
*
*/
#ifndef __YAFFS_ECC_H__
#define __YAFFS_ECC_H__
typedef struct {
unsigned char colParity;
unsigned lineParity;
unsigned lineParityPrime;
} yaffs_ECCOther;
void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc);
int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc,
const unsigned char *test_ecc);
void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes,
yaffs_ECCOther * ecc);
int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes,
yaffs_ECCOther * read_ecc,
const yaffs_ECCOther * test_ecc);
#endif

View file

@ -0,0 +1,31 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_FLASH_H__
#define __YAFFS_FLASH_H__
#include "yaffs_guts.h"
int yflash_EraseBlockInNAND(yaffs_Device *dev, int blockNumber);
int yflash_WriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare);
int yflash_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_ExtendedTags *tags);
int yflash_ReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
int yflash_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags);
int yflash_EraseBlockInNAND(yaffs_Device *dev, int blockNumber);
int yflash_InitialiseNAND(yaffs_Device *dev);
int yflash_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
int yflash_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,908 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_GUTS_H__
#define __YAFFS_GUTS_H__
#include "devextras.h"
#include "yportenv.h"
#define YAFFS_OK 1
#define YAFFS_FAIL 0
/* Give us a Y=0x59,
* Give us an A=0x41,
* Give us an FF=0xFF
* Give us an S=0x53
* And what have we got...
*/
#define YAFFS_MAGIC 0x5941FF53
#define YAFFS_NTNODES_LEVEL0 16
#define YAFFS_TNODES_LEVEL0_BITS 4
#define YAFFS_TNODES_LEVEL0_MASK 0xf
#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1)
#define YAFFS_TNODES_INTERNAL_MASK 0x7
#define YAFFS_TNODES_MAX_LEVEL 6
#ifndef CONFIG_YAFFS_NO_YAFFS1
#define YAFFS_BYTES_PER_SPARE 16
#define YAFFS_BYTES_PER_CHUNK 512
#define YAFFS_CHUNK_SIZE_SHIFT 9
#define YAFFS_CHUNKS_PER_BLOCK 32
#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
#endif
#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024
#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32
#define YAFFS_MAX_CHUNK_ID 0x000FFFFF
#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF
#define YAFFS_ALLOCATION_NOBJECTS 100
#define YAFFS_ALLOCATION_NTNODES 100
#define YAFFS_ALLOCATION_NLINKS 100
#define YAFFS_NOBJECT_BUCKETS 256
#define YAFFS_OBJECT_SPACE 0x40000
#define YAFFS_CHECKPOINT_VERSION 3
#ifdef CONFIG_YAFFS_UNICODE
#define YAFFS_MAX_NAME_LENGTH 127
#define YAFFS_MAX_ALIAS_LENGTH 79
#else
#define YAFFS_MAX_NAME_LENGTH 255
#define YAFFS_MAX_ALIAS_LENGTH 159
#endif
#define YAFFS_SHORT_NAME_LENGTH 15
/* Some special object ids for pseudo objects */
#define YAFFS_OBJECTID_ROOT 1
#define YAFFS_OBJECTID_LOSTNFOUND 2
#define YAFFS_OBJECTID_UNLINKED 3
#define YAFFS_OBJECTID_DELETED 4
/* Sseudo object ids for checkpointing */
#define YAFFS_OBJECTID_SB_HEADER 0x10
#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20
#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21
/* */
#define YAFFS_MAX_SHORT_OP_CACHES 20
#define YAFFS_N_TEMP_BUFFERS 4
/* We limit the number attempts at sucessfully saving a chunk of data.
* Small-page devices have 32 pages per block; large-page devices have 64.
* Default to something in the order of 5 to 10 blocks worth of chunks.
*/
#define YAFFS_WR_ATTEMPTS (5*64)
/* Sequence numbers are used in YAFFS2 to determine block allocation order.
* The range is limited slightly to help distinguish bad numbers from good.
* This also allows us to perhaps in the future use special numbers for
* special purposes.
* EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years,
* and is a larger number than the lifetime of a 2GB device.
*/
#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000
#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xEFFFFF00
/* ChunkCache is used for short read/write operations.*/
typedef struct {
struct yaffs_ObjectStruct *object;
int chunkId;
int lastUse;
int dirty;
int nBytes; /* Only valid if the cache is dirty */
int locked; /* Can't push out or flush while locked. */
#ifdef CONFIG_YAFFS_YAFFS2
__u8 *data;
#else
__u8 data[YAFFS_BYTES_PER_CHUNK];
#endif
} yaffs_ChunkCache;
/* Tags structures in RAM
* NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise
* the structure size will get blown out.
*/
#ifndef CONFIG_YAFFS_NO_YAFFS1
typedef struct {
unsigned chunkId:20;
unsigned serialNumber:2;
unsigned byteCount:10;
unsigned objectId:18;
unsigned ecc:12;
unsigned unusedStuff:2;
} yaffs_Tags;
typedef union {
yaffs_Tags asTags;
__u8 asBytes[8];
} yaffs_TagsUnion;
#endif
/* Stuff used for extended tags in YAFFS2 */
typedef enum {
YAFFS_ECC_RESULT_UNKNOWN,
YAFFS_ECC_RESULT_NO_ERROR,
YAFFS_ECC_RESULT_FIXED,
YAFFS_ECC_RESULT_UNFIXED
} yaffs_ECCResult;
typedef enum {
YAFFS_OBJECT_TYPE_UNKNOWN,
YAFFS_OBJECT_TYPE_FILE,
YAFFS_OBJECT_TYPE_SYMLINK,
YAFFS_OBJECT_TYPE_DIRECTORY,
YAFFS_OBJECT_TYPE_HARDLINK,
YAFFS_OBJECT_TYPE_SPECIAL
} yaffs_ObjectType;
#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL
typedef struct {
unsigned validMarker0;
unsigned chunkUsed; /* Status of the chunk: used or unused */
unsigned objectId; /* If 0 then this is not part of an object (unused) */
unsigned chunkId; /* If 0 then this is a header, else a data chunk */
unsigned byteCount; /* Only valid for data chunks */
/* The following stuff only has meaning when we read */
yaffs_ECCResult eccResult;
unsigned blockBad;
/* YAFFS 1 stuff */
unsigned chunkDeleted; /* The chunk is marked deleted */
unsigned serialNumber; /* Yaffs1 2-bit serial number */
/* YAFFS2 stuff */
unsigned sequenceNumber; /* The sequence number of this block */
/* Extra info if this is an object header (YAFFS2 only) */
unsigned extraHeaderInfoAvailable; /* There is extra info available if this is not zero */
unsigned extraParentObjectId; /* The parent object */
unsigned extraIsShrinkHeader; /* Is it a shrink header? */
unsigned extraShadows; /* Does this shadow another object? */
yaffs_ObjectType extraObjectType; /* What object type? */
unsigned extraFileLength; /* Length if it is a file */
unsigned extraEquivalentObjectId; /* Equivalent object Id if it is a hard link */
unsigned validMarker1;
} yaffs_ExtendedTags;
/* Spare structure for YAFFS1 */
typedef struct {
__u8 tagByte0;
__u8 tagByte1;
__u8 tagByte2;
__u8 tagByte3;
__u8 pageStatus; /* set to 0 to delete the chunk */
__u8 blockStatus;
__u8 tagByte4;
__u8 tagByte5;
__u8 ecc1[3];
__u8 tagByte6;
__u8 tagByte7;
__u8 ecc2[3];
} yaffs_Spare;
/*Special structure for passing through to mtd */
struct yaffs_NANDSpare {
yaffs_Spare spare;
int eccres1;
int eccres2;
};
/* Block data in RAM */
typedef enum {
YAFFS_BLOCK_STATE_UNKNOWN = 0,
YAFFS_BLOCK_STATE_SCANNING,
YAFFS_BLOCK_STATE_NEEDS_SCANNING,
/* The block might have something on it (ie it is allocating or full, perhaps empty)
* but it needs to be scanned to determine its true state.
* This state is only valid during yaffs_Scan.
* NB We tolerate empty because the pre-scanner might be incapable of deciding
* However, if this state is returned on a YAFFS2 device, then we expect a sequence number
*/
YAFFS_BLOCK_STATE_EMPTY,
/* This block is empty */
YAFFS_BLOCK_STATE_ALLOCATING,
/* This block is partially allocated.
* At least one page holds valid data.
* This is the one currently being used for page
* allocation. Should never be more than one of these
*/
YAFFS_BLOCK_STATE_FULL,
/* All the pages in this block have been allocated.
*/
YAFFS_BLOCK_STATE_DIRTY,
/* All pages have been allocated and deleted.
* Erase me, reuse me.
*/
YAFFS_BLOCK_STATE_CHECKPOINT,
/* This block is assigned to holding checkpoint data.
*/
YAFFS_BLOCK_STATE_COLLECTING,
/* This block is being garbage collected */
YAFFS_BLOCK_STATE_DEAD
/* This block has failed and is not in use */
} yaffs_BlockState;
#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1)
typedef struct {
int softDeletions:10; /* number of soft deleted pages */
int pagesInUse:10; /* number of pages in use */
unsigned blockState:4; /* One of the above block states. NB use unsigned because enum is sometimes an int */
__u32 needsRetiring:1; /* Data has failed on this block, need to get valid data off */
/* and retire the block. */
__u32 skipErasedCheck: 1; /* If this is set we can skip the erased check on this block */
__u32 gcPrioritise: 1; /* An ECC check or blank check has failed on this block.
It should be prioritised for GC */
__u32 chunkErrorStrikes:3; /* How many times we've had ecc etc failures on this block and tried to reuse it */
#ifdef CONFIG_YAFFS_YAFFS2
__u32 hasShrinkHeader:1; /* This block has at least one shrink object header */
__u32 sequenceNumber; /* block sequence number for yaffs2 */
#endif
} yaffs_BlockInfo;
/* -------------------------- Object structure -------------------------------*/
/* This is the object structure as stored on NAND */
typedef struct {
yaffs_ObjectType type;
/* Apply to everything */
int parentObjectId;
__u16 sum__NoLongerUsed; /* checksum of name. No longer used */
YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
/* Thes following apply to directories, files, symlinks - not hard links */
__u32 yst_mode; /* protection */
#ifdef CONFIG_YAFFS_WINCE
__u32 notForWinCE[5];
#else
__u32 yst_uid;
__u32 yst_gid;
__u32 yst_atime;
__u32 yst_mtime;
__u32 yst_ctime;
#endif
/* File size applies to files only */
int fileSize;
/* Equivalent object id applies to hard links only. */
int equivalentObjectId;
/* Alias is for symlinks only. */
YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];
__u32 yst_rdev; /* device stuff for block and char devices (major/min) */
#ifdef CONFIG_YAFFS_WINCE
__u32 win_ctime[2];
__u32 win_atime[2];
__u32 win_mtime[2];
__u32 roomToGrow[4];
#else
__u32 roomToGrow[10];
#endif
int shadowsObject; /* This object header shadows the specified object if > 0 */
/* isShrink applies to object headers written when we shrink the file (ie resize) */
__u32 isShrink;
} yaffs_ObjectHeader;
/*--------------------------- Tnode -------------------------- */
union yaffs_Tnode_union {
#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL + 1];
#else
union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL];
#endif
/* __u16 level0[YAFFS_NTNODES_LEVEL0]; */
};
typedef union yaffs_Tnode_union yaffs_Tnode;
struct yaffs_TnodeList_struct {
struct yaffs_TnodeList_struct *next;
yaffs_Tnode *tnodes;
};
typedef struct yaffs_TnodeList_struct yaffs_TnodeList;
/*------------------------ Object -----------------------------*/
/* An object can be one of:
* - a directory (no data, has children links
* - a regular file (data.... not prunes :->).
* - a symlink [symbolic link] (the alias).
* - a hard link
*/
typedef struct {
__u32 fileSize;
__u32 scannedFileSize;
__u32 shrinkSize;
int topLevel;
yaffs_Tnode *top;
} yaffs_FileStructure;
typedef struct {
struct list_head children; /* list of child links */
} yaffs_DirectoryStructure;
typedef struct {
YCHAR *alias;
} yaffs_SymLinkStructure;
typedef struct {
struct yaffs_ObjectStruct *equivalentObject;
__u32 equivalentObjectId;
} yaffs_HardLinkStructure;
typedef union {
yaffs_FileStructure fileVariant;
yaffs_DirectoryStructure directoryVariant;
yaffs_SymLinkStructure symLinkVariant;
yaffs_HardLinkStructure hardLinkVariant;
} yaffs_ObjectVariant;
struct yaffs_ObjectStruct {
__u8 deleted:1; /* This should only apply to unlinked files. */
__u8 softDeleted:1; /* it has also been soft deleted */
__u8 unlinked:1; /* An unlinked file. The file should be in the unlinked directory.*/
__u8 fake:1; /* A fake object has no presence on NAND. */
__u8 renameAllowed:1; /* Some objects are not allowed to be renamed. */
__u8 unlinkAllowed:1;
__u8 dirty:1; /* the object needs to be written to flash */
__u8 valid:1; /* When the file system is being loaded up, this
* object might be created before the data
* is available (ie. file data records appear before the header).
*/
__u8 lazyLoaded:1; /* This object has been lazy loaded and is missing some detail */
__u8 deferedFree:1; /* For Linux kernel. Object is removed from NAND, but is
* still in the inode cache. Free of object is defered.
* until the inode is released.
*/
__u8 serial; /* serial number of chunk in NAND. Cached here */
__u16 sum; /* sum of the name to speed searching */
struct yaffs_DeviceStruct *myDev; /* The device I'm on */
struct list_head hashLink; /* list of objects in this hash bucket */
struct list_head hardLinks; /* all the equivalent hard linked objects */
/* directory structure stuff */
/* also used for linking up the free list */
struct yaffs_ObjectStruct *parent;
struct list_head siblings;
/* Where's my object header in NAND? */
int chunkId;
int nDataChunks; /* Number of data chunks attached to the file. */
__u32 objectId; /* the object id value */
__u32 yst_mode;
#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
YCHAR shortName[YAFFS_SHORT_NAME_LENGTH + 1];
#endif
/* XXX U-BOOT XXX */
/* #ifndef __KERNEL__ */
__u32 inUse;
/* #endif */
#ifdef CONFIG_YAFFS_WINCE
__u32 win_ctime[2];
__u32 win_mtime[2];
__u32 win_atime[2];
#else
__u32 yst_uid;
__u32 yst_gid;
__u32 yst_atime;
__u32 yst_mtime;
__u32 yst_ctime;
#endif
__u32 yst_rdev;
/* XXX U-BOOT XXX */
/* #ifndef __KERNEL__ */
struct inode *myInode;
/* #endif */
yaffs_ObjectType variantType;
yaffs_ObjectVariant variant;
};
typedef struct yaffs_ObjectStruct yaffs_Object;
struct yaffs_ObjectList_struct {
yaffs_Object *objects;
struct yaffs_ObjectList_struct *next;
};
typedef struct yaffs_ObjectList_struct yaffs_ObjectList;
typedef struct {
struct list_head list;
int count;
} yaffs_ObjectBucket;
/* yaffs_CheckpointObject holds the definition of an object as dumped
* by checkpointing.
*/
typedef struct {
int structType;
__u32 objectId;
__u32 parentId;
int chunkId;
yaffs_ObjectType variantType:3;
__u8 deleted:1;
__u8 softDeleted:1;
__u8 unlinked:1;
__u8 fake:1;
__u8 renameAllowed:1;
__u8 unlinkAllowed:1;
__u8 serial;
int nDataChunks;
__u32 fileSizeOrEquivalentObjectId;
}yaffs_CheckpointObject;
/*--------------------- Temporary buffers ----------------
*
* These are chunk-sized working buffers. Each device has a few
*/
typedef struct {
__u8 *buffer;
int line; /* track from whence this buffer was allocated */
int maxLine;
} yaffs_TempBuffer;
/*----------------- Device ---------------------------------*/
struct yaffs_DeviceStruct {
struct list_head devList;
const char *name;
/* Entry parameters set up way early. Yaffs sets up the rest.*/
int nDataBytesPerChunk; /* Should be a power of 2 >= 512 */
int nChunksPerBlock; /* does not need to be a power of 2 */
int nBytesPerSpare; /* spare area size */
int startBlock; /* Start block we're allowed to use */
int endBlock; /* End block we're allowed to use */
int nReservedBlocks; /* We want this tuneable so that we can reduce */
/* reserved blocks on NOR and RAM. */
/* Stuff used by the shared space checkpointing mechanism */
/* If this value is zero, then this mechanism is disabled */
int nCheckpointReservedBlocks; /* Blocks to reserve for checkpoint data */
int nShortOpCaches; /* If <= 0, then short op caching is disabled, else
* the number of short op caches (don't use too many)
*/
int useHeaderFileSize; /* Flag to determine if we should use file sizes from the header */
int useNANDECC; /* Flag to decide whether or not to use NANDECC */
void *genericDevice; /* Pointer to device context
* On an mtd this holds the mtd pointer.
*/
void *superBlock;
/* NAND access functions (Must be set before calling YAFFS)*/
int (*writeChunkToNAND) (struct yaffs_DeviceStruct * dev,
int chunkInNAND, const __u8 * data,
const yaffs_Spare * spare);
int (*readChunkFromNAND) (struct yaffs_DeviceStruct * dev,
int chunkInNAND, __u8 * data,
yaffs_Spare * spare);
int (*eraseBlockInNAND) (struct yaffs_DeviceStruct * dev,
int blockInNAND);
int (*initialiseNAND) (struct yaffs_DeviceStruct * dev);
#ifdef CONFIG_YAFFS_YAFFS2
int (*writeChunkWithTagsToNAND) (struct yaffs_DeviceStruct * dev,
int chunkInNAND, const __u8 * data,
const yaffs_ExtendedTags * tags);
int (*readChunkWithTagsFromNAND) (struct yaffs_DeviceStruct * dev,
int chunkInNAND, __u8 * data,
yaffs_ExtendedTags * tags);
int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int blockNo);
int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo,
yaffs_BlockState * state, int *sequenceNumber);
#endif
int isYaffs2;
/* The removeObjectCallback function must be supplied by OS flavours that
* need it. The Linux kernel does not use this, but yaffs direct does use
* it to implement the faster readdir
*/
void (*removeObjectCallback)(struct yaffs_ObjectStruct *obj);
/* Callback to mark the superblock dirsty */
void (*markSuperBlockDirty)(void * superblock);
int wideTnodesDisabled; /* Set to disable wide tnodes */
/* End of stuff that must be set before initialisation. */
/* Checkpoint control. Can be set before or after initialisation */
__u8 skipCheckpointRead;
__u8 skipCheckpointWrite;
/* Runtime parameters. Set up by YAFFS. */
__u16 chunkGroupBits; /* 0 for devices <= 32MB. else log2(nchunks) - 16 */
__u16 chunkGroupSize; /* == 2^^chunkGroupBits */
/* Stuff to support wide tnodes */
__u32 tnodeWidth;
__u32 tnodeMask;
/* Stuff to support various file offses to chunk/offset translations */
/* "Crumbs" for nDataBytesPerChunk not being a power of 2 */
__u32 crumbMask;
__u32 crumbShift;
__u32 crumbsPerChunk;
/* Straight shifting for nDataBytesPerChunk being a power of 2 */
__u32 chunkShift;
__u32 chunkMask;
/* XXX U-BOOT XXX */
#if 0
#ifndef __KERNEL__
struct semaphore sem; /* Semaphore for waiting on erasure.*/
struct semaphore grossLock; /* Gross locking semaphore */
void (*putSuperFunc) (struct super_block * sb);
#endif
#endif
__u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer
* at compile time so we have to allocate it.
*/
int isMounted;
int isCheckpointed;
/* Stuff to support block offsetting to support start block zero */
int internalStartBlock;
int internalEndBlock;
int blockOffset;
int chunkOffset;
/* Runtime checkpointing stuff */
int checkpointPageSequence; /* running sequence number of checkpoint pages */
int checkpointByteCount;
int checkpointByteOffset;
__u8 *checkpointBuffer;
int checkpointOpenForWrite;
int blocksInCheckpoint;
int checkpointCurrentChunk;
int checkpointCurrentBlock;
int checkpointNextBlock;
int *checkpointBlockList;
int checkpointMaxBlocks;
__u32 checkpointSum;
__u32 checkpointXor;
/* Block Info */
yaffs_BlockInfo *blockInfo;
__u8 *chunkBits; /* bitmap of chunks in use */
unsigned blockInfoAlt:1; /* was allocated using alternative strategy */
unsigned chunkBitsAlt:1; /* was allocated using alternative strategy */
int chunkBitmapStride; /* Number of bytes of chunkBits per block.
* Must be consistent with nChunksPerBlock.
*/
int nErasedBlocks;
int allocationBlock; /* Current block being allocated off */
__u32 allocationPage;
int allocationBlockFinder; /* Used to search for next allocation block */
/* Runtime state */
int nTnodesCreated;
yaffs_Tnode *freeTnodes;
int nFreeTnodes;
yaffs_TnodeList *allocatedTnodeList;
int isDoingGC;
int nObjectsCreated;
yaffs_Object *freeObjects;
int nFreeObjects;
yaffs_ObjectList *allocatedObjectList;
yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS];
int nFreeChunks;
int currentDirtyChecker; /* Used to find current dirtiest block */
__u32 *gcCleanupList; /* objects to delete at the end of a GC. */
int nonAggressiveSkip; /* GC state/mode */
/* Statistcs */
int nPageWrites;
int nPageReads;
int nBlockErasures;
int nErasureFailures;
int nGCCopies;
int garbageCollections;
int passiveGarbageCollections;
int nRetriedWrites;
int nRetiredBlocks;
int eccFixed;
int eccUnfixed;
int tagsEccFixed;
int tagsEccUnfixed;
int nDeletions;
int nUnmarkedDeletions;
int hasPendingPrioritisedGCs; /* We think this device might have pending prioritised gcs */
/* Special directories */
yaffs_Object *rootDir;
yaffs_Object *lostNFoundDir;
/* Buffer areas for storing data to recover from write failures TODO
* __u8 bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK];
* yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK];
*/
int bufferedBlock; /* Which block is buffered here? */
int doingBufferedBlockRewrite;
yaffs_ChunkCache *srCache;
int srLastUse;
int cacheHits;
/* Stuff for background deletion and unlinked files.*/
yaffs_Object *unlinkedDir; /* Directory where unlinked and deleted files live. */
yaffs_Object *deletedDir; /* Directory where deleted objects are sent to disappear. */
yaffs_Object *unlinkedDeletion; /* Current file being background deleted.*/
int nDeletedFiles; /* Count of files awaiting deletion;*/
int nUnlinkedFiles; /* Count of unlinked files. */
int nBackgroundDeletions; /* Count of background deletions. */
yaffs_TempBuffer tempBuffer[YAFFS_N_TEMP_BUFFERS];
int maxTemp;
int unmanagedTempAllocations;
int unmanagedTempDeallocations;
/* yaffs2 runtime stuff */
unsigned sequenceNumber; /* Sequence number of currently allocating block */
unsigned oldestDirtySequence;
};
typedef struct yaffs_DeviceStruct yaffs_Device;
/* The static layout of bllock usage etc is stored in the super block header */
typedef struct {
int StructType;
int version;
int checkpointStartBlock;
int checkpointEndBlock;
int startBlock;
int endBlock;
int rfu[100];
} yaffs_SuperBlockHeader;
/* The CheckpointDevice structure holds the device information that changes at runtime and
* must be preserved over unmount/mount cycles.
*/
typedef struct {
int structType;
int nErasedBlocks;
int allocationBlock; /* Current block being allocated off */
__u32 allocationPage;
int nFreeChunks;
int nDeletedFiles; /* Count of files awaiting deletion;*/
int nUnlinkedFiles; /* Count of unlinked files. */
int nBackgroundDeletions; /* Count of background deletions. */
/* yaffs2 runtime stuff */
unsigned sequenceNumber; /* Sequence number of currently allocating block */
unsigned oldestDirtySequence;
} yaffs_CheckpointDevice;
typedef struct {
int structType;
__u32 magic;
__u32 version;
__u32 head;
} yaffs_CheckpointValidity;
/* Function to manipulate block info */
static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk)
{
if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),
blk));
YBUG();
}
return &dev->blockInfo[blk - dev->internalStartBlock];
}
/*----------------------- YAFFS Functions -----------------------*/
int yaffs_GutsInitialise(yaffs_Device * dev);
void yaffs_Deinitialise(yaffs_Device * dev);
int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev);
int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName,
yaffs_Object * newDir, const YCHAR * newName);
int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name);
int yaffs_DeleteFile(yaffs_Object * obj);
int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize);
int yaffs_GetObjectFileLength(yaffs_Object * obj);
int yaffs_GetObjectInode(yaffs_Object * obj);
unsigned yaffs_GetObjectType(yaffs_Object * obj);
int yaffs_GetObjectLinkCount(yaffs_Object * obj);
int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr);
int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr);
/* File operations */
int yaffs_ReadDataFromFile(yaffs_Object * obj, __u8 * buffer, loff_t offset,
int nBytes);
int yaffs_WriteDataToFile(yaffs_Object * obj, const __u8 * buffer, loff_t offset,
int nBytes, int writeThrough);
int yaffs_ResizeFile(yaffs_Object * obj, loff_t newSize);
yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name,
__u32 mode, __u32 uid, __u32 gid);
int yaffs_FlushFile(yaffs_Object * obj, int updateTime);
/* Flushing and checkpointing */
void yaffs_FlushEntireDeviceCache(yaffs_Device *dev);
int yaffs_CheckpointSave(yaffs_Device *dev);
int yaffs_CheckpointRestore(yaffs_Device *dev);
/* Directory operations */
yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name,
__u32 mode, __u32 uid, __u32 gid);
yaffs_Object *yaffs_FindObjectByName(yaffs_Object * theDir, const YCHAR * name);
int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir,
int (*fn) (yaffs_Object *));
yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number);
/* Link operations */
yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name,
yaffs_Object * equivalentObject);
yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj);
/* Symlink operations */
yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name,
__u32 mode, __u32 uid, __u32 gid,
const YCHAR * alias);
YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj);
/* Special inodes (fifos, sockets and devices) */
yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name,
__u32 mode, __u32 uid, __u32 gid, __u32 rdev);
/* Special directories */
yaffs_Object *yaffs_Root(yaffs_Device * dev);
yaffs_Object *yaffs_LostNFound(yaffs_Device * dev);
#ifdef CONFIG_YAFFS_WINCE
/* CONFIG_YAFFS_WINCE special stuff */
void yfsd_WinFileTimeNow(__u32 target[2]);
#endif
/* XXX U-BOOT XXX */
#if 0
#ifndef __KERNEL__
void yaffs_HandleDeferedFree(yaffs_Object * obj);
#endif
#endif
/* Debug dump */
int yaffs_DumpObject(yaffs_Object * obj);
void yaffs_GutsTest(yaffs_Device * dev);
/* A few useful functions */
void yaffs_InitialiseTags(yaffs_ExtendedTags * tags);
void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn);
int yaffs_CheckFF(__u8 * buffer, int nBytes);
void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi);
#endif

View file

@ -0,0 +1,25 @@
#ifndef __YAFFS_MALLOC_H__
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/* XXX U-BOOT XXX */
#if 0
#include <stdlib.h>
#endif
void *yaffs_malloc(size_t size);
void yaffs_free(void *ptr);
#endif

View file

@ -0,0 +1,246 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
const char *yaffs_mtdif_c_version =
"$Id: yaffs_mtdif.c,v 1.19 2007/02/14 01:09:06 wookey Exp $";
#include "yportenv.h"
#include "yaffs_mtdif.h"
#include "linux/mtd/mtd.h"
#include "linux/types.h"
#include "linux/time.h"
#include "linux/mtd/nand.h"
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
static struct nand_oobinfo yaffs_oobinfo = {
.useecc = 1,
.eccbytes = 6,
.eccpos = {8, 9, 10, 13, 14, 15}
};
static struct nand_oobinfo yaffs_noeccinfo = {
.useecc = 0,
};
#endif
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob)
{
oob[0] = spare->tagByte0;
oob[1] = spare->tagByte1;
oob[2] = spare->tagByte2;
oob[3] = spare->tagByte3;
oob[4] = spare->tagByte4;
oob[5] = spare->tagByte5 & 0x3f;
oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80;
oob[5] |= spare->pageStatus == 0 ? 0: 0x40;
oob[6] = spare->tagByte6;
oob[7] = spare->tagByte7;
}
static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob)
{
struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare;
spare->tagByte0 = oob[0];
spare->tagByte1 = oob[1];
spare->tagByte2 = oob[2];
spare->tagByte3 = oob[3];
spare->tagByte4 = oob[4];
spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f;
spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y';
spare->pageStatus = oob[5] & 0x40 ? 0xff : 0;
spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xff;
spare->tagByte6 = oob[6];
spare->tagByte7 = oob[7];
spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xff;
nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */
}
#endif
int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND,
const __u8 * data, const yaffs_Spare * spare)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
struct mtd_oob_ops ops;
#endif
size_t dummy;
int retval = 0;
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
__u8 spareAsBytes[8]; /* OOB */
if (data && !spare)
retval = mtd->write(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data);
else if (spare) {
if (dev->useNANDECC) {
translate_spare2oob(spare, spareAsBytes);
ops.mode = MTD_OOB_AUTO;
ops.ooblen = 8; /* temp hack */
} else {
ops.mode = MTD_OOB_RAW;
ops.ooblen = YAFFS_BYTES_PER_SPARE;
}
ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen;
ops.datbuf = (u8 *)data;
ops.ooboffs = 0;
ops.oobbuf = spareAsBytes;
retval = mtd->write_oob(mtd, addr, &ops);
}
#else
__u8 *spareAsBytes = (__u8 *) spare;
if (data && spare) {
if (dev->useNANDECC)
retval =
mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, spareAsBytes,
&yaffs_oobinfo);
else
retval =
mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, spareAsBytes,
&yaffs_noeccinfo);
} else {
if (data)
retval =
mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy,
data);
if (spare)
retval =
mtd->write_oob(mtd, addr, YAFFS_BYTES_PER_SPARE,
&dummy, spareAsBytes);
}
#endif
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data,
yaffs_Spare * spare)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
struct mtd_oob_ops ops;
#endif
size_t dummy;
int retval = 0;
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
__u8 spareAsBytes[8]; /* OOB */
if (data && !spare)
retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data);
else if (spare) {
if (dev->useNANDECC) {
ops.mode = MTD_OOB_AUTO;
ops.ooblen = 8; /* temp hack */
} else {
ops.mode = MTD_OOB_RAW;
ops.ooblen = YAFFS_BYTES_PER_SPARE;
}
ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen;
ops.datbuf = data;
ops.ooboffs = 0;
ops.oobbuf = spareAsBytes;
retval = mtd->read_oob(mtd, addr, &ops);
if (dev->useNANDECC)
translate_oob2spare(spare, spareAsBytes);
}
#else
__u8 *spareAsBytes = (__u8 *) spare;
if (data && spare) {
if (dev->useNANDECC) {
/* Careful, this call adds 2 ints */
/* to the end of the spare data. Calling function */
/* should allocate enough memory for spare, */
/* i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)]. */
retval =
mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, spareAsBytes,
&yaffs_oobinfo);
} else {
retval =
mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, spareAsBytes,
&yaffs_noeccinfo);
}
} else {
if (data)
retval =
mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy,
data);
if (spare)
retval =
mtd->read_oob(mtd, addr, YAFFS_BYTES_PER_SPARE,
&dummy, spareAsBytes);
}
#endif
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
__u32 addr =
((loff_t) blockNumber) * dev->nDataBytesPerChunk
* dev->nChunksPerBlock;
struct erase_info ei;
int retval = 0;
ei.mtd = mtd;
ei.addr = addr;
ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock;
ei.time = 1000;
ei.retries = 2;
ei.callback = NULL;
ei.priv = (u_long) dev;
/* Todo finish off the ei if required */
/* XXX U-BOOT XXX */
#if 0
sema_init(&dev->sem, 0);
#endif
retval = mtd->erase(mtd, &ei);
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd_InitialiseNAND(yaffs_Device * dev)
{
return YAFFS_OK;
}

View file

@ -0,0 +1,27 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_MTDIF_H__
#define __YAFFS_MTDIF_H__
#include "yaffs_guts.h"
int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND,
const __u8 * data, const yaffs_Spare * spare);
int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data,
yaffs_Spare * spare);
int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber);
int nandmtd_InitialiseNAND(yaffs_Device * dev);
#endif

View file

@ -0,0 +1,235 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* mtd interface for YAFFS2 */
/* XXX U-BOOT XXX */
#include <common.h>
#include "asm/errno.h"
const char *yaffs_mtdif2_c_version =
"$Id: yaffs_mtdif2.c,v 1.17 2007/02/14 01:09:06 wookey Exp $";
#include "yportenv.h"
#include "yaffs_mtdif2.h"
#include "linux/mtd/mtd.h"
#include "linux/types.h"
#include "linux/time.h"
#include "yaffs_packedtags2.h"
int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
const __u8 * data,
const yaffs_ExtendedTags * tags)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
struct mtd_oob_ops ops;
#else
size_t dummy;
#endif
int retval = 0;
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
yaffs_PackedTags2 pt;
T(YAFFS_TRACE_MTD,
(TSTR
("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p"
TENDSTR), chunkInNAND, data, tags));
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
if (tags)
yaffs_PackTags2(&pt, tags);
else
BUG(); /* both tags and data should always be present */
if (data) {
ops.mode = MTD_OOB_AUTO;
ops.ooblen = sizeof(pt);
ops.len = dev->nDataBytesPerChunk;
ops.ooboffs = 0;
ops.datbuf = (__u8 *)data;
ops.oobbuf = (void *)&pt;
retval = mtd->write_oob(mtd, addr, &ops);
} else
BUG(); /* both tags and data should always be present */
#else
if (tags) {
yaffs_PackTags2(&pt, tags);
}
if (data && tags) {
if (dev->useNANDECC)
retval =
mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, (__u8 *) & pt, NULL);
else
retval =
mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, (__u8 *) & pt, NULL);
} else {
if (data)
retval =
mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy,
data);
if (tags)
retval =
mtd->write_oob(mtd, addr, mtd->oobsize, &dummy,
(__u8 *) & pt);
}
#endif
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * data, yaffs_ExtendedTags * tags)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
struct mtd_oob_ops ops;
#endif
size_t dummy;
int retval = 0;
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
yaffs_PackedTags2 pt;
T(YAFFS_TRACE_MTD,
(TSTR
("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p"
TENDSTR), chunkInNAND, data, tags));
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
if (data && !tags)
retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data);
else if (tags) {
ops.mode = MTD_OOB_AUTO;
ops.ooblen = sizeof(pt);
ops.len = data ? dev->nDataBytesPerChunk : sizeof(pt);
ops.ooboffs = 0;
ops.datbuf = data;
ops.oobbuf = dev->spareBuffer;
retval = mtd->read_oob(mtd, addr, &ops);
}
#else
if (data && tags) {
if (dev->useNANDECC) {
retval =
mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, dev->spareBuffer,
NULL);
} else {
retval =
mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, dev->spareBuffer,
NULL);
}
} else {
if (data)
retval =
mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy,
data);
if (tags)
retval =
mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,
dev->spareBuffer);
}
#endif
memcpy(&pt, dev->spareBuffer, sizeof(pt));
if (tags)
yaffs_UnpackTags2(tags, &pt);
if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR)
tags->eccResult = YAFFS_ECC_RESULT_UNFIXED;
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
int retval;
T(YAFFS_TRACE_MTD,
(TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo));
retval =
mtd->block_markbad(mtd,
blockNo * dev->nChunksPerBlock *
dev->nDataBytesPerChunk);
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
yaffs_BlockState * state, int *sequenceNumber)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
int retval;
T(YAFFS_TRACE_MTD,
(TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo));
retval =
mtd->block_isbad(mtd,
blockNo * dev->nChunksPerBlock *
dev->nDataBytesPerChunk);
if (retval) {
T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR)));
*state = YAFFS_BLOCK_STATE_DEAD;
*sequenceNumber = 0;
} else {
yaffs_ExtendedTags t;
nandmtd2_ReadChunkWithTagsFromNAND(dev,
blockNo *
dev->nChunksPerBlock, NULL,
&t);
if (t.chunkUsed) {
*sequenceNumber = t.sequenceNumber;
*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
} else {
*sequenceNumber = 0;
*state = YAFFS_BLOCK_STATE_EMPTY;
}
}
T(YAFFS_TRACE_MTD,
(TSTR("block is bad seq %d state %d" TENDSTR), *sequenceNumber,
*state));
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}

View file

@ -0,0 +1,29 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_MTDIF2_H__
#define __YAFFS_MTDIF2_H__
#include "yaffs_guts.h"
int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
const __u8 * data,
const yaffs_ExtendedTags * tags);
int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * data, yaffs_ExtendedTags * tags);
int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
yaffs_BlockState * state, int *sequenceNumber);
#endif

View file

@ -0,0 +1,134 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
const char *yaffs_nand_c_version =
"$Id: yaffs_nand.c,v 1.7 2007/02/14 01:09:06 wookey Exp $";
#include "yaffs_nand.h"
#include "yaffs_tagscompat.h"
#include "yaffs_tagsvalidity.h"
int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * buffer,
yaffs_ExtendedTags * tags)
{
int result;
yaffs_ExtendedTags localTags;
int realignedChunkInNAND = chunkInNAND - dev->chunkOffset;
/* If there are no tags provided, use local tags to get prioritised gc working */
if(!tags)
tags = &localTags;
if (dev->readChunkWithTagsFromNAND)
result = dev->readChunkWithTagsFromNAND(dev, realignedChunkInNAND, buffer,
tags);
else
result = yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,
realignedChunkInNAND,
buffer,
tags);
if(tags &&
tags->eccResult > YAFFS_ECC_RESULT_NO_ERROR){
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, chunkInNAND/dev->nChunksPerBlock);
yaffs_HandleChunkError(dev,bi);
}
return result;
}
int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
int chunkInNAND,
const __u8 * buffer,
yaffs_ExtendedTags * tags)
{
chunkInNAND -= dev->chunkOffset;
if (tags) {
tags->sequenceNumber = dev->sequenceNumber;
tags->chunkUsed = 1;
if (!yaffs_ValidateTags(tags)) {
T(YAFFS_TRACE_ERROR,
(TSTR("Writing uninitialised tags" TENDSTR)));
YBUG();
}
T(YAFFS_TRACE_WRITE,
(TSTR("Writing chunk %d tags %d %d" TENDSTR), chunkInNAND,
tags->objectId, tags->chunkId));
} else {
T(YAFFS_TRACE_ERROR, (TSTR("Writing with no tags" TENDSTR)));
YBUG();
}
if (dev->writeChunkWithTagsToNAND)
return dev->writeChunkWithTagsToNAND(dev, chunkInNAND, buffer,
tags);
else
return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev,
chunkInNAND,
buffer,
tags);
}
int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo)
{
blockNo -= dev->blockOffset;
;
if (dev->markNANDBlockBad)
return dev->markNANDBlockBad(dev, blockNo);
else
return yaffs_TagsCompatabilityMarkNANDBlockBad(dev, blockNo);
}
int yaffs_QueryInitialBlockState(yaffs_Device * dev,
int blockNo,
yaffs_BlockState * state,
int *sequenceNumber)
{
blockNo -= dev->blockOffset;
if (dev->queryNANDBlock)
return dev->queryNANDBlock(dev, blockNo, state, sequenceNumber);
else
return yaffs_TagsCompatabilityQueryNANDBlock(dev, blockNo,
state,
sequenceNumber);
}
int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
int blockInNAND)
{
int result;
blockInNAND -= dev->blockOffset;
dev->nBlockErasures++;
result = dev->eraseBlockInNAND(dev, blockInNAND);
return result;
}
int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
{
return dev->initialiseNAND(dev);
}

View file

@ -0,0 +1,43 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_NAND_H__
#define __YAFFS_NAND_H__
#include "yaffs_guts.h"
int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * buffer,
yaffs_ExtendedTags * tags);
int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
int chunkInNAND,
const __u8 * buffer,
yaffs_ExtendedTags * tags);
int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo);
int yaffs_QueryInitialBlockState(yaffs_Device * dev,
int blockNo,
yaffs_BlockState * state,
int *sequenceNumber);
int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
int blockInNAND);
int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev);
#endif

View file

@ -0,0 +1,39 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/* Interface to emulated NAND functions (2k page size) */
#ifndef __YAFFS_NANDEMUL2K_H__
#define __YAFFS_NANDEMUL2K_H__
#include "yaffs_guts.h"
int nandemul2k_WriteChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
int chunkInNAND, const __u8 * data,
yaffs_ExtendedTags * tags);
int nandemul2k_ReadChunkWithTagsFromNAND(struct yaffs_DeviceStruct *dev,
int chunkInNAND, __u8 * data,
yaffs_ExtendedTags * tags);
int nandemul2k_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
int nandemul2k_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
yaffs_BlockState * state, int *sequenceNumber);
int nandemul2k_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
int blockInNAND);
int nandemul2k_InitialiseNAND(struct yaffs_DeviceStruct *dev);
int nandemul2k_GetBytesPerChunk(void);
int nandemul2k_GetChunksPerBlock(void);
int nandemul2k_GetNumberOfBlocks(void);
#endif

View file

@ -0,0 +1,55 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
#include "yaffs_packedtags1.h"
#include "yportenv.h"
void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t)
{
pt->chunkId = t->chunkId;
pt->serialNumber = t->serialNumber;
pt->byteCount = t->byteCount;
pt->objectId = t->objectId;
pt->ecc = 0;
pt->deleted = (t->chunkDeleted) ? 0 : 1;
pt->unusedStuff = 0;
pt->shouldBeFF = 0xFFFFFFFF;
}
void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt)
{
static const __u8 allFF[] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff };
if (memcmp(allFF, pt, sizeof(yaffs_PackedTags1))) {
t->blockBad = 0;
if (pt->shouldBeFF != 0xFFFFFFFF) {
t->blockBad = 1;
}
t->chunkUsed = 1;
t->objectId = pt->objectId;
t->chunkId = pt->chunkId;
t->byteCount = pt->byteCount;
t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
t->chunkDeleted = (pt->deleted) ? 0 : 1;
t->serialNumber = pt->serialNumber;
} else {
memset(t, 0, sizeof(yaffs_ExtendedTags));
}
}

View file

@ -0,0 +1,37 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */
#ifndef __YAFFS_PACKEDTAGS1_H__
#define __YAFFS_PACKEDTAGS1_H__
#include "yaffs_guts.h"
typedef struct {
unsigned chunkId:20;
unsigned serialNumber:2;
unsigned byteCount:10;
unsigned objectId:18;
unsigned ecc:12;
unsigned deleted:1;
unsigned unusedStuff:1;
unsigned shouldBeFF;
} yaffs_PackedTags1;
void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t);
void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt);
#endif

View file

@ -0,0 +1,185 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
#include "yaffs_packedtags2.h"
#include "yportenv.h"
#include "yaffs_tagsvalidity.h"
/* This code packs a set of extended tags into a binary structure for
* NAND storage
*/
/* Some of the information is "extra" struff which can be packed in to
* speed scanning
* This is defined by having the EXTRA_HEADER_INFO_FLAG set.
*/
/* Extra flags applied to chunkId */
#define EXTRA_HEADER_INFO_FLAG 0x80000000
#define EXTRA_SHRINK_FLAG 0x40000000
#define EXTRA_SHADOWS_FLAG 0x20000000
#define EXTRA_SPARE_FLAGS 0x10000000
#define ALL_EXTRA_FLAGS 0xF0000000
/* Also, the top 4 bits of the object Id are set to the object type. */
#define EXTRA_OBJECT_TYPE_SHIFT (28)
#define EXTRA_OBJECT_TYPE_MASK ((0x0F) << EXTRA_OBJECT_TYPE_SHIFT)
static void yaffs_DumpPackedTags2(const yaffs_PackedTags2 * pt)
{
T(YAFFS_TRACE_MTD,
(TSTR("packed tags obj %d chunk %d byte %d seq %d" TENDSTR),
pt->t.objectId, pt->t.chunkId, pt->t.byteCount,
pt->t.sequenceNumber));
}
static void yaffs_DumpTags2(const yaffs_ExtendedTags * t)
{
T(YAFFS_TRACE_MTD,
(TSTR
("ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte "
"%d del %d ser %d seq %d"
TENDSTR), t->eccResult, t->blockBad, t->chunkUsed, t->objectId,
t->chunkId, t->byteCount, t->chunkDeleted, t->serialNumber,
t->sequenceNumber));
}
void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t)
{
pt->t.chunkId = t->chunkId;
pt->t.sequenceNumber = t->sequenceNumber;
pt->t.byteCount = t->byteCount;
pt->t.objectId = t->objectId;
if (t->chunkId == 0 && t->extraHeaderInfoAvailable) {
/* Store the extra header info instead */
/* We save the parent object in the chunkId */
pt->t.chunkId = EXTRA_HEADER_INFO_FLAG
| t->extraParentObjectId;
if (t->extraIsShrinkHeader) {
pt->t.chunkId |= EXTRA_SHRINK_FLAG;
}
if (t->extraShadows) {
pt->t.chunkId |= EXTRA_SHADOWS_FLAG;
}
pt->t.objectId &= ~EXTRA_OBJECT_TYPE_MASK;
pt->t.objectId |=
(t->extraObjectType << EXTRA_OBJECT_TYPE_SHIFT);
if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) {
pt->t.byteCount = t->extraEquivalentObjectId;
} else if (t->extraObjectType == YAFFS_OBJECT_TYPE_FILE) {
pt->t.byteCount = t->extraFileLength;
} else {
pt->t.byteCount = 0;
}
}
yaffs_DumpPackedTags2(pt);
yaffs_DumpTags2(t);
#ifndef YAFFS_IGNORE_TAGS_ECC
{
yaffs_ECCCalculateOther((unsigned char *)&pt->t,
sizeof(yaffs_PackedTags2TagsPart),
&pt->ecc);
}
#endif
}
void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt)
{
memset(t, 0, sizeof(yaffs_ExtendedTags));
yaffs_InitialiseTags(t);
if (pt->t.sequenceNumber != 0xFFFFFFFF) {
/* Page is in use */
#ifdef YAFFS_IGNORE_TAGS_ECC
{
t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
}
#else
{
yaffs_ECCOther ecc;
int result;
yaffs_ECCCalculateOther((unsigned char *)&pt->t,
sizeof
(yaffs_PackedTags2TagsPart),
&ecc);
result =
yaffs_ECCCorrectOther((unsigned char *)&pt->t,
sizeof
(yaffs_PackedTags2TagsPart),
&pt->ecc, &ecc);
switch(result){
case 0:
t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
break;
case 1:
t->eccResult = YAFFS_ECC_RESULT_FIXED;
break;
case -1:
t->eccResult = YAFFS_ECC_RESULT_UNFIXED;
break;
default:
t->eccResult = YAFFS_ECC_RESULT_UNKNOWN;
}
}
#endif
t->blockBad = 0;
t->chunkUsed = 1;
t->objectId = pt->t.objectId;
t->chunkId = pt->t.chunkId;
t->byteCount = pt->t.byteCount;
t->chunkDeleted = 0;
t->serialNumber = 0;
t->sequenceNumber = pt->t.sequenceNumber;
/* Do extra header info stuff */
if (pt->t.chunkId & EXTRA_HEADER_INFO_FLAG) {
t->chunkId = 0;
t->byteCount = 0;
t->extraHeaderInfoAvailable = 1;
t->extraParentObjectId =
pt->t.chunkId & (~(ALL_EXTRA_FLAGS));
t->extraIsShrinkHeader =
(pt->t.chunkId & EXTRA_SHRINK_FLAG) ? 1 : 0;
t->extraShadows =
(pt->t.chunkId & EXTRA_SHADOWS_FLAG) ? 1 : 0;
t->extraObjectType =
pt->t.objectId >> EXTRA_OBJECT_TYPE_SHIFT;
t->objectId &= ~EXTRA_OBJECT_TYPE_MASK;
if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) {
t->extraEquivalentObjectId = pt->t.byteCount;
} else {
t->extraFileLength = pt->t.byteCount;
}
}
}
yaffs_DumpPackedTags2(pt);
yaffs_DumpTags2(t);
}

View file

@ -0,0 +1,38 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/* This is used to pack YAFFS2 tags, not YAFFS1tags. */
#ifndef __YAFFS_PACKEDTAGS2_H__
#define __YAFFS_PACKEDTAGS2_H__
#include "yaffs_guts.h"
#include "yaffs_ecc.h"
typedef struct {
unsigned sequenceNumber;
unsigned objectId;
unsigned chunkId;
unsigned byteCount;
} yaffs_PackedTags2TagsPart;
typedef struct {
yaffs_PackedTags2TagsPart t;
yaffs_ECCOther ecc;
} yaffs_PackedTags2;
void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t);
void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt);
#endif

View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* XXX U-BOOT XXX */
#include <common.h>
#include "yportenv.h"
//#include <linux/string.h>
/*
* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
*/
#define swapcode(TYPE, parmi, parmj, n) { \
long i = (n) / sizeof (TYPE); \
register TYPE *pi = (TYPE *) (parmi); \
register TYPE *pj = (TYPE *) (parmj); \
do { \
register TYPE t = *pi; \
*pi++ = *pj; \
*pj++ = t; \
} while (--i > 0); \
}
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
static __inline void
swapfunc(char *a, char *b, int n, int swaptype)
{
if (swaptype <= 1)
swapcode(long, a, b, n)
else
swapcode(char, a, b, n)
}
#define swap(a, b) \
if (swaptype == 0) { \
long t = *(long *)(a); \
*(long *)(a) = *(long *)(b); \
*(long *)(b) = t; \
} else \
swapfunc(a, b, es, swaptype)
#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
static __inline char *
med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
{
return cmp(a, b) < 0 ?
(cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
:(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
}
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
void
yaffs_qsort(void *aa, size_t n, size_t es,
int (*cmp)(const void *, const void *))
{
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
int d, r, swaptype, swap_cnt;
register char *a = aa;
loop: SWAPINIT(a, es);
swap_cnt = 0;
if (n < 7) {
for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
pl -= es)
swap(pl, pl - es);
return;
}
pm = (char *)a + (n / 2) * es;
if (n > 7) {
pl = (char *)a;
pn = (char *)a + (n - 1) * es;
if (n > 40) {
d = (n / 8) * es;
pl = med3(pl, pl + d, pl + 2 * d, cmp);
pm = med3(pm - d, pm, pm + d, cmp);
pn = med3(pn - 2 * d, pn - d, pn, cmp);
}
pm = med3(pl, pm, pn, cmp);
}
swap(a, pm);
pa = pb = (char *)a + es;
pc = pd = (char *)a + (n - 1) * es;
for (;;) {
while (pb <= pc && (r = cmp(pb, a)) <= 0) {
if (r == 0) {
swap_cnt = 1;
swap(pa, pb);
pa += es;
}
pb += es;
}
while (pb <= pc && (r = cmp(pc, a)) >= 0) {
if (r == 0) {
swap_cnt = 1;
swap(pc, pd);
pd -= es;
}
pc -= es;
}
if (pb > pc)
break;
swap(pb, pc);
swap_cnt = 1;
pb += es;
pc -= es;
}
if (swap_cnt == 0) { /* Switch to insertion sort */
for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
pl -= es)
swap(pl, pl - es);
return;
}
pn = (char *)a + n * es;
r = min(pa - (char *)a, pb - pa);
vecswap(a, pb - r, r);
r = min((long)(pd - pc), (long)(pn - pd - es));
vecswap(pb, pn - r, r);
if ((r = pb - pa) > es)
yaffs_qsort(a, r / es, es, cmp);
if ((r = pd - pc) > es) {
/* Iterate rather than recurse to save stack space */
a = pn - r;
n = r / es;
goto loop;
}
/* yaffs_qsort(pn - r, r / es, es, cmp);*/
}

View file

@ -0,0 +1,23 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_QSORT_H__
#define __YAFFS_QSORT_H__
extern void yaffs_qsort (void *const base, size_t total_elems, size_t size,
int (*cmp)(const void *, const void *));
#endif

View file

@ -0,0 +1,32 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/*
* yaffs_ramdisk.h: yaffs ram disk component
*/
#ifndef __YAFFS_RAMDISK_H__
#define __YAFFS_RAMDISK_H__
#include "yaffs_guts.h"
int yramdisk_EraseBlockInNAND(yaffs_Device *dev, int blockNumber);
int yramdisk_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_ExtendedTags *tags);
int yramdisk_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags);
int yramdisk_EraseBlockInNAND(yaffs_Device *dev, int blockNumber);
int yramdisk_InitialiseNAND(yaffs_Device *dev);
int yramdisk_MarkNANDBlockBad(yaffs_Device *dev,int blockNumber);
int yramdisk_QueryNANDBlock(yaffs_Device *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber);
#endif

View file

@ -0,0 +1,532 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
#include "yaffs_guts.h"
#include "yaffs_tagscompat.h"
#include "yaffs_ecc.h"
static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND);
#ifdef NOTYET
static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND);
static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
const __u8 * data,
const yaffs_Spare * spare);
static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
const yaffs_Spare * spare);
static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND);
#endif
static const char yaffs_countBitsTable[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
int yaffs_CountBits(__u8 x)
{
int retVal;
retVal = yaffs_countBitsTable[x];
return retVal;
}
/********** Tags ECC calculations *********/
void yaffs_CalcECC(const __u8 * data, yaffs_Spare * spare)
{
yaffs_ECCCalculate(data, spare->ecc1);
yaffs_ECCCalculate(&data[256], spare->ecc2);
}
void yaffs_CalcTagsECC(yaffs_Tags * tags)
{
/* Calculate an ecc */
unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes;
unsigned i, j;
unsigned ecc = 0;
unsigned bit = 0;
tags->ecc = 0;
for (i = 0; i < 8; i++) {
for (j = 1; j & 0xff; j <<= 1) {
bit++;
if (b[i] & j) {
ecc ^= bit;
}
}
}
tags->ecc = ecc;
}
int yaffs_CheckECCOnTags(yaffs_Tags * tags)
{
unsigned ecc = tags->ecc;
yaffs_CalcTagsECC(tags);
ecc ^= tags->ecc;
if (ecc && ecc <= 64) {
/* TODO: Handle the failure better. Retire? */
unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes;
ecc--;
b[ecc / 8] ^= (1 << (ecc & 7));
/* Now recvalc the ecc */
yaffs_CalcTagsECC(tags);
return 1; /* recovered error */
} else if (ecc) {
/* Wierd ecc failure value */
/* TODO Need to do somethiong here */
return -1; /* unrecovered error */
}
return 0;
}
/********** Tags **********/
static void yaffs_LoadTagsIntoSpare(yaffs_Spare * sparePtr,
yaffs_Tags * tagsPtr)
{
yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr;
yaffs_CalcTagsECC(tagsPtr);
sparePtr->tagByte0 = tu->asBytes[0];
sparePtr->tagByte1 = tu->asBytes[1];
sparePtr->tagByte2 = tu->asBytes[2];
sparePtr->tagByte3 = tu->asBytes[3];
sparePtr->tagByte4 = tu->asBytes[4];
sparePtr->tagByte5 = tu->asBytes[5];
sparePtr->tagByte6 = tu->asBytes[6];
sparePtr->tagByte7 = tu->asBytes[7];
}
static void yaffs_GetTagsFromSpare(yaffs_Device * dev, yaffs_Spare * sparePtr,
yaffs_TagsUnion *tu)
{
int result;
tu->asBytes[0] = sparePtr->tagByte0;
tu->asBytes[1] = sparePtr->tagByte1;
tu->asBytes[2] = sparePtr->tagByte2;
tu->asBytes[3] = sparePtr->tagByte3;
tu->asBytes[4] = sparePtr->tagByte4;
tu->asBytes[5] = sparePtr->tagByte5;
tu->asBytes[6] = sparePtr->tagByte6;
tu->asBytes[7] = sparePtr->tagByte7;
result = yaffs_CheckECCOnTags(&tu->asTags);
if (result > 0) {
dev->tagsEccFixed++;
} else if (result < 0) {
dev->tagsEccUnfixed++;
}
}
static void yaffs_SpareInitialise(yaffs_Spare * spare)
{
memset(spare, 0xFF, sizeof(yaffs_Spare));
}
static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,
int chunkInNAND, const __u8 * data,
yaffs_Spare * spare)
{
if (chunkInNAND < dev->startBlock * dev->nChunksPerBlock) {
T(YAFFS_TRACE_ERROR,
(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),
chunkInNAND));
return YAFFS_FAIL;
}
dev->nPageWrites++;
return dev->writeChunkToNAND(dev, chunkInNAND, data, spare);
}
static int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
int chunkInNAND,
__u8 * data,
yaffs_Spare * spare,
yaffs_ECCResult * eccResult,
int doErrorCorrection)
{
int retVal;
yaffs_Spare localSpare;
dev->nPageReads++;
if (!spare && data) {
/* If we don't have a real spare, then we use a local one. */
/* Need this for the calculation of the ecc */
spare = &localSpare;
}
if (!dev->useNANDECC) {
retVal = dev->readChunkFromNAND(dev, chunkInNAND, data, spare);
if (data && doErrorCorrection) {
/* Do ECC correction */
/* Todo handle any errors */
int eccResult1, eccResult2;
__u8 calcEcc[3];
yaffs_ECCCalculate(data, calcEcc);
eccResult1 =
yaffs_ECCCorrect(data, spare->ecc1, calcEcc);
yaffs_ECCCalculate(&data[256], calcEcc);
eccResult2 =
yaffs_ECCCorrect(&data[256], spare->ecc2, calcEcc);
if (eccResult1 > 0) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>>yaffs ecc error fix performed on chunk %d:0"
TENDSTR), chunkInNAND));
dev->eccFixed++;
} else if (eccResult1 < 0) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>>yaffs ecc error unfixed on chunk %d:0"
TENDSTR), chunkInNAND));
dev->eccUnfixed++;
}
if (eccResult2 > 0) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>>yaffs ecc error fix performed on chunk %d:1"
TENDSTR), chunkInNAND));
dev->eccFixed++;
} else if (eccResult2 < 0) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>>yaffs ecc error unfixed on chunk %d:1"
TENDSTR), chunkInNAND));
dev->eccUnfixed++;
}
if (eccResult1 || eccResult2) {
/* We had a data problem on this page */
yaffs_HandleReadDataError(dev, chunkInNAND);
}
if (eccResult1 < 0 || eccResult2 < 0)
*eccResult = YAFFS_ECC_RESULT_UNFIXED;
else if (eccResult1 > 0 || eccResult2 > 0)
*eccResult = YAFFS_ECC_RESULT_FIXED;
else
*eccResult = YAFFS_ECC_RESULT_NO_ERROR;
}
} else {
/* Must allocate enough memory for spare+2*sizeof(int) */
/* for ecc results from device. */
struct yaffs_NANDSpare nspare;
retVal =
dev->readChunkFromNAND(dev, chunkInNAND, data,
(yaffs_Spare *) & nspare);
memcpy(spare, &nspare, sizeof(yaffs_Spare));
if (data && doErrorCorrection) {
if (nspare.eccres1 > 0) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>>mtd ecc error fix performed on chunk %d:0"
TENDSTR), chunkInNAND));
} else if (nspare.eccres1 < 0) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>>mtd ecc error unfixed on chunk %d:0"
TENDSTR), chunkInNAND));
}
if (nspare.eccres2 > 0) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>>mtd ecc error fix performed on chunk %d:1"
TENDSTR), chunkInNAND));
} else if (nspare.eccres2 < 0) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>>mtd ecc error unfixed on chunk %d:1"
TENDSTR), chunkInNAND));
}
if (nspare.eccres1 || nspare.eccres2) {
/* We had a data problem on this page */
yaffs_HandleReadDataError(dev, chunkInNAND);
}
if (nspare.eccres1 < 0 || nspare.eccres2 < 0)
*eccResult = YAFFS_ECC_RESULT_UNFIXED;
else if (nspare.eccres1 > 0 || nspare.eccres2 > 0)
*eccResult = YAFFS_ECC_RESULT_FIXED;
else
*eccResult = YAFFS_ECC_RESULT_NO_ERROR;
}
}
return retVal;
}
#ifdef NOTYET
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
int chunkInNAND)
{
static int init = 0;
static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
static __u8 data[YAFFS_BYTES_PER_CHUNK];
/* Might as well always allocate the larger size for */
/* dev->useNANDECC == true; */
static __u8 spare[sizeof(struct yaffs_NANDSpare)];
dev->readChunkFromNAND(dev, chunkInNAND, data, (yaffs_Spare *) spare);
if (!init) {
memset(cmpbuf, 0xff, YAFFS_BYTES_PER_CHUNK);
init = 1;
}
if (memcmp(cmpbuf, data, YAFFS_BYTES_PER_CHUNK))
return YAFFS_FAIL;
if (memcmp(cmpbuf, spare, 16))
return YAFFS_FAIL;
return YAFFS_OK;
}
#endif
/*
* Functions for robustisizing
*/
static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND)
{
int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
/* Mark the block for retirement */
yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1;
T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
(TSTR("**>>Block %d marked for retirement" TENDSTR), blockInNAND));
/* TODO:
* Just do a garbage collection on the affected block
* then retire the block
* NB recursion
*/
}
#ifdef NOTYET
static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND)
{
}
static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
const __u8 * data,
const yaffs_Spare * spare)
{
}
static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
const yaffs_Spare * spare)
{
}
static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND)
{
int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
/* Mark the block for retirement */
yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1;
/* Delete the chunk */
yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
}
static int yaffs_VerifyCompare(const __u8 * d0, const __u8 * d1,
const yaffs_Spare * s0, const yaffs_Spare * s1)
{
if (memcmp(d0, d1, YAFFS_BYTES_PER_CHUNK) != 0 ||
s0->tagByte0 != s1->tagByte0 ||
s0->tagByte1 != s1->tagByte1 ||
s0->tagByte2 != s1->tagByte2 ||
s0->tagByte3 != s1->tagByte3 ||
s0->tagByte4 != s1->tagByte4 ||
s0->tagByte5 != s1->tagByte5 ||
s0->tagByte6 != s1->tagByte6 ||
s0->tagByte7 != s1->tagByte7 ||
s0->ecc1[0] != s1->ecc1[0] ||
s0->ecc1[1] != s1->ecc1[1] ||
s0->ecc1[2] != s1->ecc1[2] ||
s0->ecc2[0] != s1->ecc2[0] ||
s0->ecc2[1] != s1->ecc2[1] || s0->ecc2[2] != s1->ecc2[2]) {
return 0;
}
return 1;
}
#endif /* NOTYET */
int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev,
int chunkInNAND,
const __u8 * data,
const yaffs_ExtendedTags *
eTags)
{
yaffs_Spare spare;
yaffs_Tags tags;
yaffs_SpareInitialise(&spare);
if (eTags->chunkDeleted) {
spare.pageStatus = 0;
} else {
tags.objectId = eTags->objectId;
tags.chunkId = eTags->chunkId;
tags.byteCount = eTags->byteCount;
tags.serialNumber = eTags->serialNumber;
if (!dev->useNANDECC && data) {
yaffs_CalcECC(data, &spare);
}
yaffs_LoadTagsIntoSpare(&spare, &tags);
}
return yaffs_WriteChunkToNAND(dev, chunkInNAND, data, &spare);
}
int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev,
int chunkInNAND,
__u8 * data,
yaffs_ExtendedTags * eTags)
{
yaffs_Spare spare;
yaffs_TagsUnion tags;
yaffs_ECCResult eccResult;
static yaffs_Spare spareFF;
static int init;
if (!init) {
memset(&spareFF, 0xFF, sizeof(spareFF));
init = 1;
}
if (yaffs_ReadChunkFromNAND
(dev, chunkInNAND, data, &spare, &eccResult, 1)) {
/* eTags may be NULL */
if (eTags) {
int deleted =
(yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0;
eTags->chunkDeleted = deleted;
eTags->eccResult = eccResult;
eTags->blockBad = 0; /* We're reading it */
/* therefore it is not a bad block */
eTags->chunkUsed =
(memcmp(&spareFF, &spare, sizeof(spareFF)) !=
0) ? 1 : 0;
if (eTags->chunkUsed) {
yaffs_GetTagsFromSpare(dev, &spare, &tags);
eTags->objectId = tags.asTags.objectId;
eTags->chunkId = tags.asTags.chunkId;
eTags->byteCount = tags.asTags.byteCount;
eTags->serialNumber = tags.asTags.serialNumber;
}
}
return YAFFS_OK;
} else {
return YAFFS_FAIL;
}
}
int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev,
int blockInNAND)
{
yaffs_Spare spare;
memset(&spare, 0xff, sizeof(yaffs_Spare));
spare.blockStatus = 'Y';
yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL,
&spare);
yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1,
NULL, &spare);
return YAFFS_OK;
}
int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev,
int blockNo, yaffs_BlockState *
state,
int *sequenceNumber)
{
yaffs_Spare spare0, spare1;
static yaffs_Spare spareFF;
static int init;
yaffs_ECCResult dummy;
if (!init) {
memset(&spareFF, 0xFF, sizeof(spareFF));
init = 1;
}
*sequenceNumber = 0;
yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock, NULL,
&spare0, &dummy, 1);
yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock + 1, NULL,
&spare1, &dummy, 1);
if (yaffs_CountBits(spare0.blockStatus & spare1.blockStatus) < 7)
*state = YAFFS_BLOCK_STATE_DEAD;
else if (memcmp(&spareFF, &spare0, sizeof(spareFF)) == 0)
*state = YAFFS_BLOCK_STATE_EMPTY;
else
*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
return YAFFS_OK;
}

View file

@ -0,0 +1,40 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_TAGSCOMPAT_H__
#define __YAFFS_TAGSCOMPAT_H__
#include "yaffs_guts.h"
int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev,
int chunkInNAND,
const __u8 * data,
const yaffs_ExtendedTags *
tags);
int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev,
int chunkInNAND,
__u8 * data,
yaffs_ExtendedTags *
tags);
int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev,
int blockNo);
int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev,
int blockNo, yaffs_BlockState *
state, int *sequenceNumber);
void yaffs_CalcTagsECC(yaffs_Tags * tags);
int yaffs_CheckECCOnTags(yaffs_Tags * tags);
int yaffs_CountBits(__u8 byte);
#endif

View file

@ -0,0 +1,31 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
#include "yaffs_tagsvalidity.h"
void yaffs_InitialiseTags(yaffs_ExtendedTags * tags)
{
memset(tags, 0, sizeof(yaffs_ExtendedTags));
tags->validMarker0 = 0xAAAAAAAA;
tags->validMarker1 = 0x55555555;
}
int yaffs_ValidateTags(yaffs_ExtendedTags * tags)
{
return (tags->validMarker0 == 0xAAAAAAAA &&
tags->validMarker1 == 0x55555555);
}

View file

@ -0,0 +1,24 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFS_TAGS_VALIDITY_H__
#define __YAFFS_TAGS_VALIDITY_H__
#include "yaffs_guts.h"
void yaffs_InitialiseTags(yaffs_ExtendedTags * tags);
int yaffs_ValidateTags(yaffs_ExtendedTags * tags);
#endif

View file

@ -0,0 +1,420 @@
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* yaffscfg.c The configuration for the "direct" use of yaffs.
*
* This file is intended to be modified to your requirements.
* There is no need to redistribute this file.
*/
/* XXX U-BOOT XXX */
#include <common.h>
#include <config.h>
#include "nand.h"
#include "yaffscfg.h"
#include "yaffsfs.h"
#include "yaffs_packedtags2.h"
#include "yaffs_mtdif.h"
#include "yaffs_mtdif2.h"
#if 0
#include <errno.h>
#else
#include "malloc.h"
#endif
unsigned yaffs_traceMask = 0x0; /* Disable logging */
static int yaffs_errno = 0;
void yaffsfs_SetError(int err)
{
//Do whatever to set error
yaffs_errno = err;
}
int yaffsfs_GetError(void)
{
return yaffs_errno;
}
void yaffsfs_Lock(void)
{
}
void yaffsfs_Unlock(void)
{
}
__u32 yaffsfs_CurrentTime(void)
{
return 0;
}
void *yaffs_malloc(size_t size)
{
return malloc(size);
}
void yaffs_free(void *ptr)
{
free(ptr);
}
void yaffsfs_LocalInitialisation(void)
{
// Define locking semaphore.
}
// Configuration for:
// /ram 2MB ramdisk
// /boot 2MB boot disk (flash)
// /flash 14MB flash disk (flash)
// NB Though /boot and /flash occupy the same physical device they
// are still disticnt "yaffs_Devices. You may think of these as "partitions"
// using non-overlapping areas in the same device.
//
#include "yaffs_ramdisk.h"
#include "yaffs_flashif.h"
static int isMounted = 0;
#define MOUNT_POINT "/flash"
extern nand_info_t nand_info[];
/* XXX U-BOOT XXX */
#if 0
static yaffs_Device ramDev;
static yaffs_Device bootDev;
static yaffs_Device flashDev;
#endif
static yaffsfs_DeviceConfiguration yaffsfs_config[] = {
/* XXX U-BOOT XXX */
#if 0
{ "/ram", &ramDev},
{ "/boot", &bootDev},
{ "/flash", &flashDev},
#else
{ MOUNT_POINT, 0},
#endif
{(void *)0,(void *)0}
};
int yaffs_StartUp(void)
{
struct mtd_info *mtd = &nand_info[0];
int yaffsVersion = 2;
int nBlocks;
yaffs_Device *flashDev = calloc(1, sizeof(yaffs_Device));
yaffsfs_config[0].dev = flashDev;
/* store the mtd device for later use */
flashDev->genericDevice = mtd;
// Stuff to configure YAFFS
// Stuff to initialise anything special (eg lock semaphore).
yaffsfs_LocalInitialisation();
// Set up devices
/* XXX U-BOOT XXX */
#if 0
// /ram
ramDev.nBytesPerChunk = 512;
ramDev.nChunksPerBlock = 32;
ramDev.nReservedBlocks = 2; // Set this smaller for RAM
ramDev.startBlock = 1; // Can't use block 0
ramDev.endBlock = 127; // Last block in 2MB.
ramDev.useNANDECC = 1;
ramDev.nShortOpCaches = 0; // Disable caching on this device.
ramDev.genericDevice = (void *) 0; // Used to identify the device in fstat.
ramDev.writeChunkWithTagsToNAND = yramdisk_WriteChunkWithTagsToNAND;
ramDev.readChunkWithTagsFromNAND = yramdisk_ReadChunkWithTagsFromNAND;
ramDev.eraseBlockInNAND = yramdisk_EraseBlockInNAND;
ramDev.initialiseNAND = yramdisk_InitialiseNAND;
// /boot
bootDev.nBytesPerChunk = 612;
bootDev.nChunksPerBlock = 32;
bootDev.nReservedBlocks = 5;
bootDev.startBlock = 1; // Can't use block 0
bootDev.endBlock = 127; // Last block in 2MB.
bootDev.useNANDECC = 0; // use YAFFS's ECC
bootDev.nShortOpCaches = 10; // Use caches
bootDev.genericDevice = (void *) 1; // Used to identify the device in fstat.
bootDev.writeChunkToNAND = yflash_WriteChunkToNAND;
bootDev.readChunkFromNAND = yflash_ReadChunkFromNAND;
bootDev.eraseBlockInNAND = yflash_EraseBlockInNAND;
bootDev.initialiseNAND = yflash_InitialiseNAND;
#endif
// /flash
flashDev->nReservedBlocks = 5;
// flashDev->nShortOpCaches = (options.no_cache) ? 0 : 10;
flashDev->nShortOpCaches = 10; // Use caches
flashDev->useNANDECC = 0; // do not use YAFFS's ECC
if (yaffsVersion == 2)
{
flashDev->writeChunkWithTagsToNAND = nandmtd2_WriteChunkWithTagsToNAND;
flashDev->readChunkWithTagsFromNAND = nandmtd2_ReadChunkWithTagsFromNAND;
flashDev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;
flashDev->queryNANDBlock = nandmtd2_QueryNANDBlock;
flashDev->spareBuffer = YMALLOC(mtd->oobsize);
flashDev->isYaffs2 = 1;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
flashDev->nDataBytesPerChunk = mtd->writesize;
flashDev->nChunksPerBlock = mtd->erasesize / mtd->writesize;
#else
flashDev->nDataBytesPerChunk = mtd->oobblock;
flashDev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
#endif
nBlocks = mtd->size / mtd->erasesize;
flashDev->nCheckpointReservedBlocks = 10;
flashDev->startBlock = 0;
flashDev->endBlock = nBlocks - 1;
}
else
{
flashDev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
flashDev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
flashDev->isYaffs2 = 0;
nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
flashDev->startBlock = 320;
flashDev->endBlock = nBlocks - 1;
flashDev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
flashDev->nDataBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
}
/* ... and common functions */
flashDev->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
flashDev->initialiseNAND = nandmtd_InitialiseNAND;
yaffs_initialise(yaffsfs_config);
return 0;
}
void make_a_file(char *yaffsName,char bval,int sizeOfFile)
{
int outh;
int i;
unsigned char buffer[100];
outh = yaffs_open(yaffsName, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
if (outh < 0)
{
printf("Error opening file: %d\n", outh);
return;
}
memset(buffer,bval,100);
do{
i = sizeOfFile;
if(i > 100) i = 100;
sizeOfFile -= i;
yaffs_write(outh,buffer,i);
} while (sizeOfFile > 0);
yaffs_close(outh);
}
void read_a_file(char *fn)
{
int h;
int i = 0;
unsigned char b;
h = yaffs_open(fn, O_RDWR,0);
if(h<0)
{
printf("File not found\n");
return;
}
while(yaffs_read(h,&b,1)> 0)
{
printf("%02x ",b);
i++;
if(i > 32)
{
printf("\n");
i = 0;;
}
}
printf("\n");
yaffs_close(h);
}
void cmd_yaffs_mount(char *mp)
{
yaffs_StartUp();
int retval = yaffs_mount(mp);
if( retval != -1)
isMounted = 1;
else
printf("Error mounting %s, return value: %d\n", mp, yaffsfs_GetError());
}
static void checkMount(void)
{
if( !isMounted )
{
cmd_yaffs_mount(MOUNT_POINT);
}
}
void cmd_yaffs_umount(char *mp)
{
checkMount();
if( yaffs_unmount(mp) == -1)
printf("Error umounting %s, return value: %d\n", mp, yaffsfs_GetError());
}
void cmd_yaffs_write_file(char *yaffsName,char bval,int sizeOfFile)
{
checkMount();
make_a_file(yaffsName,bval,sizeOfFile);
}
void cmd_yaffs_read_file(char *fn)
{
checkMount();
read_a_file(fn);
}
void cmd_yaffs_mread_file(char *fn, char *addr)
{
int h;
struct yaffs_stat s;
checkMount();
yaffs_stat(fn,&s);
printf ("Copy %s to 0x%p... ", fn, addr);
h = yaffs_open(fn, O_RDWR,0);
if(h<0)
{
printf("File not found\n");
return;
}
yaffs_read(h,addr,(int)s.st_size);
printf("\t[DONE]\n");
yaffs_close(h);
}
void cmd_yaffs_mwrite_file(char *fn, char *addr, int size)
{
int outh;
checkMount();
outh = yaffs_open(fn, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
if (outh < 0)
{
printf("Error opening file: %d\n", outh);
}
yaffs_write(outh,addr,size);
yaffs_close(outh);
}
void cmd_yaffs_ls(const char *mountpt, int longlist)
{
int i;
yaffs_DIR *d;
yaffs_dirent *de;
struct yaffs_stat stat;
char tempstr[255];
checkMount();
d = yaffs_opendir(mountpt);
if(!d)
{
printf("opendir failed\n");
}
else
{
for(i = 0; (de = yaffs_readdir(d)) != NULL; i++)
{
if (longlist)
{
sprintf(tempstr, "%s/%s", mountpt, de->d_name);
yaffs_stat(tempstr, &stat);
printf("%-25s\t%7ld\n",de->d_name, stat.st_size);
}
else
{
printf("%s\n",de->d_name);
}
}
}
}
void cmd_yaffs_mkdir(const char *dir)
{
checkMount();
int retval = yaffs_mkdir(dir, 0);
if ( retval < 0)
printf("yaffs_mkdir returning error: %d\n", retval);
}
void cmd_yaffs_rmdir(const char *dir)
{
checkMount();
int retval = yaffs_rmdir(dir);
if ( retval < 0)
printf("yaffs_rmdir returning error: %d\n", retval);
}
void cmd_yaffs_rm(const char *path)
{
checkMount();
int retval = yaffs_unlink(path);
if ( retval < 0)
printf("yaffs_unlink returning error: %d\n", retval);
}
void cmd_yaffs_mv(const char *oldPath, const char *newPath)
{
checkMount();
int retval = yaffs_rename(newPath, oldPath);
if ( retval < 0)
printf("yaffs_unlink returning error: %d\n", retval);
}

View file

@ -0,0 +1,45 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/*
* Header file for using yaffs in an application via
* a direct interface.
*/
#ifndef __YAFFSCFG_H__
#define __YAFFSCFG_H__
#include "devextras.h"
#define YAFFSFS_N_HANDLES 200
typedef struct {
const char *prefix;
struct yaffs_DeviceStruct *dev;
} yaffsfs_DeviceConfiguration;
void yaffsfs_Lock(void);
void yaffsfs_Unlock(void);
__u32 yaffsfs_CurrentTime(void);
void yaffsfs_SetError(int err);
int yaffsfs_GetError(void);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,231 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/*
* Header file for using yaffs in an application via
* a direct interface.
*/
#ifndef __YAFFSFS_H__
#define __YAFFSFS_H__
#include "yaffscfg.h"
#include "yportenv.h"
//typedef long off_t;
//typedef long dev_t;
//typedef unsigned long mode_t;
#ifndef NAME_MAX
#define NAME_MAX 256
#endif
#ifndef O_RDONLY
#define O_RDONLY 00
#endif
#ifndef O_WRONLY
#define O_WRONLY 01
#endif
#ifndef O_RDWR
#define O_RDWR 02
#endif
#ifndef O_CREAT
#define O_CREAT 0100
#endif
#ifndef O_EXCL
#define O_EXCL 0200
#endif
#ifndef O_TRUNC
#define O_TRUNC 01000
#endif
#ifndef O_APPEND
#define O_APPEND 02000
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
#ifndef SEEK_END
#define SEEK_END 2
#endif
#ifndef EBUSY
#define EBUSY 16
#endif
#ifndef ENODEV
#define ENODEV 19
#endif
#ifndef EINVAL
#define EINVAL 22
#endif
#ifndef EBADF
#define EBADF 9
#endif
#ifndef EACCESS
#define EACCESS 13
#endif
#ifndef EXDEV
#define EXDEV 18
#endif
#ifndef ENOENT
#define ENOENT 2
#endif
#ifndef ENOSPC
#define ENOSPC 28
#endif
#ifndef ENOTEMPTY
#define ENOTEMPTY 39
#endif
#ifndef ENOMEM
#define ENOMEM 12
#endif
#ifndef EEXIST
#define EEXIST 17
#endif
#ifndef ENOTDIR
#define ENOTDIR 20
#endif
#ifndef EISDIR
#define EISDIR 21
#endif
// Mode flags
#ifndef S_IFMT
#define S_IFMT 0170000
#endif
#ifndef S_IFLNK
#define S_IFLNK 0120000
#endif
#ifndef S_IFDIR
#define S_IFDIR 0040000
#endif
#ifndef S_IFREG
#define S_IFREG 0100000
#endif
#ifndef S_IREAD
#define S_IREAD 0000400
#endif
#ifndef S_IWRITE
#define S_IWRITE 0000200
#endif
struct yaffs_dirent{
long d_ino; /* inode number */
off_t d_off; /* offset to this dirent */
unsigned short d_reclen; /* length of this d_name */
char d_name [NAME_MAX+1]; /* file name (null-terminated) */
unsigned d_dont_use; /* debug pointer, not for public consumption */
};
typedef struct yaffs_dirent yaffs_dirent;
typedef struct __opaque yaffs_DIR;
struct yaffs_stat{
int st_dev; /* device */
int st_ino; /* inode */
mode_t st_mode; /* protection */
int st_nlink; /* number of hard links */
int st_uid; /* user ID of owner */
int st_gid; /* group ID of owner */
unsigned st_rdev; /* device type (if inode device) */
off_t st_size; /* total size, in bytes */
unsigned long st_blksize; /* blocksize for filesystem I/O */
unsigned long st_blocks; /* number of blocks allocated */
unsigned long yst_atime; /* time of last access */
unsigned long yst_mtime; /* time of last modification */
unsigned long yst_ctime; /* time of last change */
};
int yaffs_open(const char *path, int oflag, int mode) ;
int yaffs_read(int fd, void *buf, unsigned int nbyte) ;
int yaffs_write(int fd, const void *buf, unsigned int nbyte) ;
int yaffs_close(int fd) ;
off_t yaffs_lseek(int fd, off_t offset, int whence) ;
int yaffs_truncate(int fd, off_t newSize);
int yaffs_unlink(const char *path) ;
int yaffs_rename(const char *oldPath, const char *newPath) ;
int yaffs_stat(const char *path, struct yaffs_stat *buf) ;
int yaffs_lstat(const char *path, struct yaffs_stat *buf) ;
int yaffs_fstat(int fd, struct yaffs_stat *buf) ;
int yaffs_chmod(const char *path, mode_t mode);
int yaffs_fchmod(int fd, mode_t mode);
int yaffs_mkdir(const char *path, mode_t mode) ;
int yaffs_rmdir(const char *path) ;
yaffs_DIR *yaffs_opendir(const char *dirname) ;
struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp) ;
void yaffs_rewinddir(yaffs_DIR *dirp) ;
int yaffs_closedir(yaffs_DIR *dirp) ;
int yaffs_mount(const char *path) ;
int yaffs_unmount(const char *path) ;
int yaffs_symlink(const char *oldpath, const char *newpath);
int yaffs_readlink(const char *path, char *buf, int bufsiz);
int yaffs_link(const char *oldpath, const char *newpath);
int yaffs_mknod(const char *pathname, mode_t mode, dev_t dev);
loff_t yaffs_freespace(const char *path);
void yaffs_initialise(yaffsfs_DeviceConfiguration *configList);
int yaffs_StartUp(void);
#endif

View file

@ -0,0 +1,21 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YAFFSINTERFACE_H__
#define __YAFFSINTERFACE_H__
int yaffs_Initialise(unsigned nBlocks);
#endif

View file

@ -0,0 +1,92 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
/*
* ydirectenv.h: Environment wrappers for YAFFS direct.
*/
#ifndef __YDIRECTENV_H__
#define __YDIRECTENV_H__
/* Direct interface */
#include "devextras.h"
/* XXX U-BOOT XXX */
#if 0
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "assert.h"
#endif
#include "yaffs_malloc.h"
/* XXX U-BOOT XXX */
#if 0
#define YBUG() assert(1)
#endif
#define YCHAR char
#define YUCHAR unsigned char
#define _Y(x) x
#define yaffs_strcpy(a,b) strcpy(a,b)
#define yaffs_strncpy(a,b,c) strncpy(a,b,c)
#define yaffs_strncmp(a,b,c) strncmp(a,b,c)
#define yaffs_strlen(s) strlen(s)
#define yaffs_sprintf sprintf
#define yaffs_toupper(a) toupper(a)
#ifdef NO_Y_INLINE
#define Y_INLINE
#else
#define Y_INLINE inline
#endif
#define YMALLOC(x) yaffs_malloc(x)
#define YFREE(x) free(x)
#define YMALLOC_ALT(x) yaffs_malloc(x)
#define YFREE_ALT(x) free(x)
#define YMALLOC_DMA(x) yaffs_malloc(x)
#define YYIELD() do {} while(0)
//#define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s))
//#define YALERT(s) YINFO(s)
#define TENDSTR "\n"
#define TSTR(x) x
#define TOUT(p) printf p
#define YAFFS_LOSTNFOUND_NAME "lost+found"
#define YAFFS_LOSTNFOUND_PREFIX "obj"
//#define YPRINTF(x) printf x
#include "yaffscfg.h"
#define Y_CURRENT_TIME yaffsfs_CurrentTime()
#define Y_TIME_CONVERT(x) x
#define YAFFS_ROOT_MODE 0666
#define YAFFS_LOSTNFOUND_MODE 0666
#define yaffs_SumCompare(x,y) ((x) == (y))
#define yaffs_strcmp(a,b) strcmp(a,b)
#endif

View file

@ -0,0 +1,193 @@
/*
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1 as
* published by the Free Software Foundation.
*
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
*/
#ifndef __YPORTENV_H__
#define __YPORTENV_H__
/* XXX U-BOOT XXX */
#ifndef CONFIG_YAFFS_DIRECT
#define CONFIG_YAFFS_DIRECT
#endif
#if defined CONFIG_YAFFS_WINCE
#include "ywinceenv.h"
/* XXX U-BOOT XXX */
#elif 0 /* defined __KERNEL__ */
#include "moduleconfig.h"
/* Linux kernel */
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
#include <linux/config.h>
#endif
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#define YCHAR char
#define YUCHAR unsigned char
#define _Y(x) x
#define yaffs_strcpy(a,b) strcpy(a,b)
#define yaffs_strncpy(a,b,c) strncpy(a,b,c)
#define yaffs_strncmp(a,b,c) strncmp(a,b,c)
#define yaffs_strlen(s) strlen(s)
#define yaffs_sprintf sprintf
#define yaffs_toupper(a) toupper(a)
#define Y_INLINE inline
#define YAFFS_LOSTNFOUND_NAME "lost+found"
#define YAFFS_LOSTNFOUND_PREFIX "obj"
/* #define YPRINTF(x) printk x */
#define YMALLOC(x) kmalloc(x,GFP_KERNEL)
#define YFREE(x) kfree(x)
#define YMALLOC_ALT(x) vmalloc(x)
#define YFREE_ALT(x) vfree(x)
#define YMALLOC_DMA(x) YMALLOC(x)
// KR - added for use in scan so processes aren't blocked indefinitely.
#define YYIELD() schedule()
#define YAFFS_ROOT_MODE 0666
#define YAFFS_LOSTNFOUND_MODE 0666
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
#define Y_CURRENT_TIME CURRENT_TIME.tv_sec
#define Y_TIME_CONVERT(x) (x).tv_sec
#else
#define Y_CURRENT_TIME CURRENT_TIME
#define Y_TIME_CONVERT(x) (x)
#endif
#define yaffs_SumCompare(x,y) ((x) == (y))
#define yaffs_strcmp(a,b) strcmp(a,b)
#define TENDSTR "\n"
#define TSTR(x) KERN_WARNING x
#define TOUT(p) printk p
#define yaffs_trace(mask, fmt, args...) \
do { if ((mask) & (yaffs_traceMask|YAFFS_TRACE_ERROR)) \
printk(KERN_WARNING "yaffs: " fmt, ## args); \
} while (0)
#define compile_time_assertion(assertion) \
({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; })
#elif defined CONFIG_YAFFS_DIRECT
/* Direct interface */
#include "ydirectenv.h"
#elif defined CONFIG_YAFFS_UTIL
/* Stuff for YAFFS utilities */
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "devextras.h"
#define YMALLOC(x) malloc(x)
#define YFREE(x) free(x)
#define YMALLOC_ALT(x) malloc(x)
#define YFREE_ALT(x) free(x)
#define YCHAR char
#define YUCHAR unsigned char
#define _Y(x) x
#define yaffs_strcpy(a,b) strcpy(a,b)
#define yaffs_strncpy(a,b,c) strncpy(a,b,c)
#define yaffs_strlen(s) strlen(s)
#define yaffs_sprintf sprintf
#define yaffs_toupper(a) toupper(a)
#define Y_INLINE inline
/* #define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) */
/* #define YALERT(s) YINFO(s) */
#define TENDSTR "\n"
#define TSTR(x) x
#define TOUT(p) printf p
#define YAFFS_LOSTNFOUND_NAME "lost+found"
#define YAFFS_LOSTNFOUND_PREFIX "obj"
/* #define YPRINTF(x) printf x */
#define YAFFS_ROOT_MODE 0666
#define YAFFS_LOSTNFOUND_MODE 0666
#define yaffs_SumCompare(x,y) ((x) == (y))
#define yaffs_strcmp(a,b) strcmp(a,b)
#else
/* Should have specified a configuration type */
#error Unknown configuration
#endif
/* see yaffs_fs.c */
extern unsigned int yaffs_traceMask;
extern unsigned int yaffs_wr_attempts;
/*
* Tracing flags.
* The flags masked in YAFFS_TRACE_ALWAYS are always traced.
*/
#define YAFFS_TRACE_OS 0x00000002
#define YAFFS_TRACE_ALLOCATE 0x00000004
#define YAFFS_TRACE_SCAN 0x00000008
#define YAFFS_TRACE_BAD_BLOCKS 0x00000010
#define YAFFS_TRACE_ERASE 0x00000020
#define YAFFS_TRACE_GC 0x00000040
#define YAFFS_TRACE_WRITE 0x00000080
#define YAFFS_TRACE_TRACING 0x00000100
#define YAFFS_TRACE_DELETION 0x00000200
#define YAFFS_TRACE_BUFFERS 0x00000400
#define YAFFS_TRACE_NANDACCESS 0x00000800
#define YAFFS_TRACE_GC_DETAIL 0x00001000
#define YAFFS_TRACE_SCAN_DEBUG 0x00002000
#define YAFFS_TRACE_MTD 0x00004000
#define YAFFS_TRACE_CHECKPOINT 0x00008000
#define YAFFS_TRACE_VERIFY 0x00010000
#define YAFFS_TRACE_VERIFY_NAND 0x00020000
#define YAFFS_TRACE_VERIFY_FULL 0x00040000
#define YAFFS_TRACE_VERIFY_ALL 0x000F0000
#define YAFFS_TRACE_ERROR 0x40000000
#define YAFFS_TRACE_BUG 0x80000000
#define YAFFS_TRACE_ALWAYS 0xF0000000
#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ALWAYS)) TOUT(p);} while(0)
#ifndef CONFIG_YAFFS_WINCE
#define YBUG() T(YAFFS_TRACE_BUG,(TSTR("==>> yaffs bug: " __FILE__ " %d" TENDSTR),__LINE__))
#endif
#endif