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:
parent
e910436a7a
commit
46837ec4c0
9459 changed files with 362648 additions and 116345 deletions
38
common/package/utils/sysupgrade-helper/src/fs/Makefile
Normal file
38
common/package/utils/sysupgrade-helper/src/fs/Makefile
Normal 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
|
|
@ -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
|
||||
|
||||
#########################################################################
|
346
common/package/utils/sysupgrade-helper/src/fs/cramfs/cramfs.c
Normal file
346
common/package/utils/sysupgrade-helper/src/fs/cramfs/cramfs.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
52
common/package/utils/sysupgrade-helper/src/fs/ext2/Makefile
Normal file
52
common/package/utils/sysupgrade-helper/src/fs/ext2/Makefile
Normal 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
|
||||
|
||||
#########################################################################
|
131
common/package/utils/sysupgrade-helper/src/fs/ext2/dev.c
Normal file
131
common/package/utils/sysupgrade-helper/src/fs/ext2/dev.c
Normal 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;
|
||||
}
|
919
common/package/utils/sysupgrade-helper/src/fs/ext2/ext2fs.c
Normal file
919
common/package/utils/sysupgrade-helper/src/fs/ext2/ext2fs.c
Normal 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);
|
||||
}
|
50
common/package/utils/sysupgrade-helper/src/fs/fat/Makefile
Normal file
50
common/package/utils/sysupgrade-helper/src/fs/fat/Makefile
Normal 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
|
||||
|
||||
#########################################################################
|
1189
common/package/utils/sysupgrade-helper/src/fs/fat/fat.c
Normal file
1189
common/package/utils/sysupgrade-helper/src/fs/fat/fat.c
Normal file
File diff suppressed because it is too large
Load diff
1098
common/package/utils/sysupgrade-helper/src/fs/fat/fat_write.c
Normal file
1098
common/package/utils/sysupgrade-helper/src/fs/fat/fat_write.c
Normal file
File diff suppressed because it is too large
Load diff
200
common/package/utils/sysupgrade-helper/src/fs/fat/file.c
Normal file
200
common/package/utils/sysupgrade-helper/src/fs/fat/file.c
Normal 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);
|
||||
}
|
54
common/package/utils/sysupgrade-helper/src/fs/fdos/Makefile
Normal file
54
common/package/utils/sysupgrade-helper/src/fs/fdos/Makefile
Normal 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
|
||||
|
||||
#########################################################################
|
190
common/package/utils/sysupgrade-helper/src/fs/fdos/dev.c
Normal file
190
common/package/utils/sysupgrade-helper/src/fs/fdos/dev.c
Normal 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);
|
||||
}
|
175
common/package/utils/sysupgrade-helper/src/fs/fdos/dos.h
Normal file
175
common/package/utils/sysupgrade-helper/src/fs/fdos/dos.h
Normal 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
|
138
common/package/utils/sysupgrade-helper/src/fs/fdos/fat.c
Normal file
138
common/package/utils/sysupgrade-helper/src/fs/fdos/fat.c
Normal 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);
|
||||
}
|
172
common/package/utils/sysupgrade-helper/src/fs/fdos/fdos.c
Normal file
172
common/package/utils/sysupgrade-helper/src/fs/fdos/fdos.c
Normal 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);
|
||||
}
|
116
common/package/utils/sysupgrade-helper/src/fs/fdos/fdos.h
Normal file
116
common/package/utils/sysupgrade-helper/src/fs/fdos/fdos.h
Normal 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
|
114
common/package/utils/sysupgrade-helper/src/fs/fdos/fs.c
Normal file
114
common/package/utils/sysupgrade-helper/src/fs/fdos/fs.c
Normal 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);
|
||||
}
|
345
common/package/utils/sysupgrade-helper/src/fs/fdos/subdir.c
Normal file
345
common/package/utils/sysupgrade-helper/src/fs/fdos/subdir.c
Normal 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);
|
||||
}
|
352
common/package/utils/sysupgrade-helper/src/fs/fdos/vfat.c
Normal file
352
common/package/utils/sysupgrade-helper/src/fs/fdos/vfat.c
Normal 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);
|
||||
}
|
30
common/package/utils/sysupgrade-helper/src/fs/jffs2/LICENCE
Normal file
30
common/package/utils/sysupgrade-helper/src/fs/jffs2/LICENCE
Normal 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.
|
||||
|
57
common/package/utils/sysupgrade-helper/src/fs/jffs2/Makefile
Normal file
57
common/package/utils/sysupgrade-helper/src/fs/jffs2/Makefile
Normal 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
|
||||
|
||||
#########################################################################
|
401
common/package/utils/sysupgrade-helper/src/fs/jffs2/compr_lzo.c
Normal file
401
common/package/utils/sysupgrade-helper/src/fs/jffs2/compr_lzo.c
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
|
||||
}
|
1863
common/package/utils/sysupgrade-helper/src/fs/jffs2/jffs2_1pass.c
Normal file
1863
common/package/utils/sysupgrade-helper/src/fs/jffs2/jffs2_1pass.c
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
163
common/package/utils/sysupgrade-helper/src/fs/jffs2/summary.h
Normal file
163
common/package/utils/sysupgrade-helper/src/fs/jffs2/summary.h
Normal 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 */
|
|
@ -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
|
||||
|
||||
#########################################################################
|
119
common/package/utils/sysupgrade-helper/src/fs/reiserfs/dev.c
Normal file
119
common/package/utils/sysupgrade-helper/src/fs/reiserfs/dev.c
Normal 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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
52
common/package/utils/sysupgrade-helper/src/fs/ubifs/Makefile
Normal file
52
common/package/utils/sysupgrade-helper/src/fs/ubifs/Makefile
Normal 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
|
||||
|
||||
#########################################################################
|
113
common/package/utils/sysupgrade-helper/src/fs/ubifs/budget.c
Normal file
113
common/package/utils/sysupgrade-helper/src/fs/ubifs/budget.c
Normal 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);
|
||||
}
|
60
common/package/utils/sysupgrade-helper/src/fs/ubifs/crc16.c
Normal file
60
common/package/utils/sysupgrade-helper/src/fs/ubifs/crc16.c
Normal 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;
|
||||
}
|
29
common/package/utils/sysupgrade-helper/src/fs/ubifs/crc16.h
Normal file
29
common/package/utils/sysupgrade-helper/src/fs/ubifs/crc16.h
Normal 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 */
|
156
common/package/utils/sysupgrade-helper/src/fs/ubifs/debug.c
Normal file
156
common/package/utils/sysupgrade-helper/src/fs/ubifs/debug.c
Normal 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 */
|
392
common/package/utils/sysupgrade-helper/src/fs/ubifs/debug.h
Normal file
392
common/package/utils/sysupgrade-helper/src/fs/ubifs/debug.h
Normal 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__ */
|
316
common/package/utils/sysupgrade-helper/src/fs/ubifs/io.c
Normal file
316
common/package/utils/sysupgrade-helper/src/fs/ubifs/io.c
Normal 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;
|
||||
}
|
557
common/package/utils/sysupgrade-helper/src/fs/ubifs/key.h
Normal file
557
common/package/utils/sysupgrade-helper/src/fs/ubifs/key.h
Normal 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__ */
|
104
common/package/utils/sysupgrade-helper/src/fs/ubifs/log.c
Normal file
104
common/package/utils/sysupgrade-helper/src/fs/ubifs/log.c
Normal 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);
|
||||
}
|
842
common/package/utils/sysupgrade-helper/src/fs/ubifs/lprops.c
Normal file
842
common/package/utils/sysupgrade-helper/src/fs/ubifs/lprops.c
Normal 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;
|
||||
}
|
1105
common/package/utils/sysupgrade-helper/src/fs/ubifs/lpt.c
Normal file
1105
common/package/utils/sysupgrade-helper/src/fs/ubifs/lpt.c
Normal file
File diff suppressed because it is too large
Load diff
171
common/package/utils/sysupgrade-helper/src/fs/ubifs/lpt_commit.c
Normal file
171
common/package/utils/sysupgrade-helper/src/fs/ubifs/lpt_commit.c
Normal 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);
|
||||
}
|
341
common/package/utils/sysupgrade-helper/src/fs/ubifs/master.c
Normal file
341
common/package/utils/sysupgrade-helper/src/fs/ubifs/master.c
Normal 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;
|
||||
}
|
311
common/package/utils/sysupgrade-helper/src/fs/ubifs/misc.h
Normal file
311
common/package/utils/sysupgrade-helper/src/fs/ubifs/misc.h
Normal 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__ */
|
316
common/package/utils/sysupgrade-helper/src/fs/ubifs/orphan.c
Normal file
316
common/package/utils/sysupgrade-helper/src/fs/ubifs/orphan.c
Normal 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;
|
||||
}
|
1225
common/package/utils/sysupgrade-helper/src/fs/ubifs/recovery.c
Normal file
1225
common/package/utils/sysupgrade-helper/src/fs/ubifs/recovery.c
Normal file
File diff suppressed because it is too large
Load diff
1070
common/package/utils/sysupgrade-helper/src/fs/ubifs/replay.c
Normal file
1070
common/package/utils/sysupgrade-helper/src/fs/ubifs/replay.c
Normal file
File diff suppressed because it is too large
Load diff
346
common/package/utils/sysupgrade-helper/src/fs/ubifs/sb.c
Normal file
346
common/package/utils/sysupgrade-helper/src/fs/ubifs/sb.c
Normal 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;
|
||||
}
|
362
common/package/utils/sysupgrade-helper/src/fs/ubifs/scan.c
Normal file
362
common/package/utils/sysupgrade-helper/src/fs/ubifs/scan.c
Normal 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);
|
||||
}
|
1202
common/package/utils/sysupgrade-helper/src/fs/ubifs/super.c
Normal file
1202
common/package/utils/sysupgrade-helper/src/fs/ubifs/super.c
Normal file
File diff suppressed because it is too large
Load diff
2767
common/package/utils/sysupgrade-helper/src/fs/ubifs/tnc.c
Normal file
2767
common/package/utils/sysupgrade-helper/src/fs/ubifs/tnc.c
Normal file
File diff suppressed because it is too large
Load diff
435
common/package/utils/sysupgrade-helper/src/fs/ubifs/tnc_misc.c
Normal file
435
common/package/utils/sysupgrade-helper/src/fs/ubifs/tnc_misc.c
Normal 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;
|
||||
}
|
|
@ -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__ */
|
752
common/package/utils/sysupgrade-helper/src/fs/ubifs/ubifs.c
Normal file
752
common/package/utils/sysupgrade-helper/src/fs/ubifs/ubifs.c
Normal 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;
|
||||
}
|
2143
common/package/utils/sysupgrade-helper/src/fs/ubifs/ubifs.h
Normal file
2143
common/package/utils/sysupgrade-helper/src/fs/ubifs/ubifs.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
||||
#########################################################################
|
|
@ -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
|
||||
-------------
|
275
common/package/utils/sysupgrade-helper/src/fs/yaffs2/devextras.h
Normal file
275
common/package/utils/sysupgrade-helper/src/fs/yaffs2/devextras.h
Normal 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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
333
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffs_ecc.c
Normal file
333
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffs_ecc.c
Normal 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;
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
7329
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffs_guts.c
Normal file
7329
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffs_guts.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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));
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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);*/
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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
|
420
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffscfg.c
Normal file
420
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffscfg.c
Normal 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);
|
||||
}
|
|
@ -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
|
1510
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffsfs.c
Normal file
1510
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffsfs.c
Normal file
File diff suppressed because it is too large
Load diff
231
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffsfs.h
Normal file
231
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yaffsfs.h
Normal 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
|
|
@ -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
|
|
@ -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
|
193
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yportenv.h
Normal file
193
common/package/utils/sysupgrade-helper/src/fs/yaffs2/yportenv.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue