summaryrefslogtreecommitdiffstats
path: root/fs/ubifs/ubifs.c
diff options
context:
space:
mode:
authorAlexander Stein <alexander.stein@systec-electronic.com>2016-03-17 11:00:29 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2016-04-08 13:33:18 +0200
commita63059d753680ce249942e2e6c4eba56c4840542 (patch)
tree5d6d708d353e9f90968a9e945f6eda100279b2c5 /fs/ubifs/ubifs.c
parent84abb49171ac2f7c7f52842a88ab7ebb31b015f6 (diff)
downloadbarebox-a63059d753680ce249942e2e6c4eba56c4840542.tar.gz
barebox-a63059d753680ce249942e2e6c4eba56c4840542.tar.xz
ubifs: update implementation from u-boot v2016.03
This updated code supports recovering from unclean unmounts when write buffer size is larger than 8. Linux uses takes information into consideration when checking for writes. The following list shows the changes for each file done compared to u-boot v2016.03 code. Makefile * Add gc.o, do not include header as in u-boot budget.c: * Replace __UBOOT__ with __BAREBOX__ debug.c: * Replace __UBOOT__ with __BAREBOX__ * Remove #include <linux/compat.h> * Replace current->pid with 0 debug.h * Replace __UBOOT__ with __BAREBOX__ * Add "0 &&" to ubifs_assert condition check U-Boot essentially disabled pr_crit output and dump_stack() is defined to do { } while (0), so ubifs_assert does nothing despite condition. gc.c: * Replace __UBOOT__ with __BAREBOX__ io.c: * Replace __UBOOT__ with __BAREBOX__ * Remove #include <linux/compat.h> key.h: * No changes log.c: * No changes lprops.c: * Replace __UBOOT__ with __BAREBOX__ lpt.c * Replace __UBOOT__ with __BAREBOX__ * Remove #include <linux/compat.h> * Remove #include <ubi_uboot.h> lpt_commit.c * Replace __UBOOT__ with __BAREBOX__ * Remove #include <linux/compat.h> master.c * Replace __UBOOT__ with __BAREBOX__ * Remove #include <linux/compat.h> * Remove #include <ubi_uboot.h> misc.h * Replace __UBOOT__ with __BAREBOX__ * Add #ifndef __BAREBOX__ around unneeded ubifs_current_time orphan.c * No changes recovery.c * Replace __UBOOT__ with __BAREBOX__ replay.c * Replace __UBOOT__ with __BAREBOX__ * Remove #include <linux/compat.h> sb.c * Replace __UBOOT__ with __BAREBOX__ * Remove #include <linux/compat.h> * Remove #include <ubi_uboot.h> * rp_uid and rp_gid are still uid_t, not kuid_t, so remove .val scan.c * Replace __UBOOT__ with __BAREBOX__ super.c: * Replaced current->pid with 0 * Replaced __UBOOT__ with __BAREBOX__ * Removed #include <ubi_uboot.h> * Removed #include <memalign.h> * Added #include <init.h> * Replaced malloc_cache_aligned with kzalloc * Replaced I_LOCK with I_SYNC * i_uid and i_gid are still uid_t, not kuid_t, so remove .val * Replaced string "U-Boot" with "Barebox" * Add #ifndef __BAREBOX__ around open_ubi, sb_test, sb_set, ubifs_mount, kill_ubifs_super, ubifs_fs_type * Do not call ubi_open_volume or ubi_close_volume in ubifs_fill_super Those will already be handled in ubifs_probe * Replaced uboot_ubifs_mount with new ubifs_get_super implementation using existing functions alloc_super, alloc_ubifs_info tnc.c * Replace __UBOOT__ with __BAREBOX__ * Remove #include <linux/compat.h> tnc_misc.c * Replace __UBOOT__ with __BAREBOX__ ubifs-media.h * Replace __UBOOT__ with __BAREBOX__ ubifs.c: * Replaced __UBOOT__ with __BAREBOX__ * Adjusted header includes * Removed DECLARE_GLOBAL_DATA_PTR * Add static struct z_stream_s variable * Add struct ubifs_priv delcaration * Replaced zunzip with deflate_decompress call * Add ifdef for disabling ZLIB and/or LZO support * Replaced malloc_cache_aligned with kzalloc * Added #ifndef __BAREBOX__ around filldir, ubifs_printdir, ubifs_set_blk_dev, ubifs_ls, ubifs_exists, ubifs_size, do_readpage, ubifs_read, ubifs_close, ubifs_load * Make parameter filename const char* in ubifs_findfile * Add UBIFS fs_driver_d implementation * Add zlib_decomp_init, ubifs_init ubifs.h: * Replaced __UBOOT__ with __BAREBOX__ * Adjusted header includes * Added old #define crc32 * Added helper around kmem* * Added XATTR_LIST_MAX define * Added #ifndef __BAREBOX__ around current_fs_time struct inode, struct super_block, file_system_type, dentry and friends which barebox already has * Added #ifndef __BAREBOX__ around write_inode in super_operations to avoid declaration of writeback_control * Change rp_uid and rp_gid to uid_t and gui_t * Added struct device_d to struct ubifs_info * Added forward declaration of ubifs_get_super linux/fs.h: * Make struct inode's i_nlink a union containing a non-const int __i_nlink while making i_nlink a const int linux/fs.h: * Add s_remove_count linux/fs.h: * Convert struct super_block.s_instances and struct file_system_type.fs_supers to hlist_head Signed-off-by: Alexander Stein <alexander.stein@systec-electronic.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'fs/ubifs/ubifs.c')
-rw-r--r--fs/ubifs/ubifs.c828
1 files changed, 750 insertions, 78 deletions
diff --git a/fs/ubifs/ubifs.c b/fs/ubifs/ubifs.c
index ad74a0be18..f07ad5e7f9 100644
--- a/fs/ubifs/ubifs.c
+++ b/fs/ubifs/ubifs.c
@@ -6,84 +6,165 @@
* (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
+ *
+ * SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
-#include <malloc.h>
-#include <driver.h>
#include <init.h>
-#include <malloc.h>
#include <fs.h>
-#include <command.h>
-#include <errno.h>
#include <linux/stat.h>
-#include <linux/ctype.h>
#include <linux/zlib.h>
-#include <xfuncs.h>
-#include <fcntl.h>
#include <linux/mtd/mtd.h>
#include "ubifs.h"
+#include <linux/err.h>
+
struct ubifs_priv {
struct cdev *cdev;
struct ubi_volume_desc *ubi;
struct super_block *sb;
};
-struct z_stream_s ubifs_zlib_stream;
+static struct z_stream_s ubifs_zlib_stream;
+
/* compress.c */
-#ifdef CONFIG_ZLIB
-static int ubifs_deflate_decompress(const u8 *src, unsigned int slen, u8 *dst,
- unsigned int *dlen)
+/*
+ * We need a wrapper for zunzip() because the parameters are
+ * incompatible with the lzo decompressor.
+ */
+#if defined(CONFIG_ZLIB)
+static int gzip_decompress(const unsigned char *in, size_t in_len,
+ unsigned char *out, size_t *out_len)
{
- return deflate_decompress(&ubifs_zlib_stream, src, slen, dst, dlen);
+ return deflate_decompress(&ubifs_zlib_stream, in, in_len, out, out_len);
}
#endif
-/* All UBIFS compressors */
-struct ubifs_compressor ubifs_compressors[UBIFS_COMPR_TYPES_CNT] = {
- [UBIFS_COMPR_NONE] = {
- .compr_type = UBIFS_COMPR_NONE,
- .name = "no compression",
- .capi_name = "",
- .decompress = NULL,
- },
- [UBIFS_COMPR_LZO] = {
- .compr_type = UBIFS_COMPR_LZO,
- .name = "LZO",
+/* Fake description object for the "none" compressor */
+static struct ubifs_compressor none_compr = {
+ .compr_type = UBIFS_COMPR_NONE,
+ .name = "none",
+ .capi_name = "",
+ .decompress = NULL,
+};
+
+static struct ubifs_compressor lzo_compr = {
+ .compr_type = UBIFS_COMPR_LZO,
+#ifndef __BAREBOX__
+ .comp_mutex = &lzo_mutex,
+#endif
+ .name = "lzo",
#ifdef CONFIG_LZO_DECOMPRESS
- .capi_name = "lzo",
- .decompress = lzo1x_decompress_safe,
+ .capi_name = "lzo",
+ .decompress = lzo1x_decompress_safe,
+#endif
+};
+
+static struct ubifs_compressor zlib_compr = {
+ .compr_type = UBIFS_COMPR_ZLIB,
+#ifndef __BAREBOX__
+ .comp_mutex = &deflate_mutex,
+ .decomp_mutex = &inflate_mutex,
#endif
- },
- [UBIFS_COMPR_ZLIB] = {
- .compr_type = UBIFS_COMPR_ZLIB,
- .name = "zlib",
+ .name = "zlib",
#ifdef CONFIG_ZLIB
- .capi_name = "deflate",
- .decompress = ubifs_deflate_decompress,
+ .capi_name = "deflate",
+ .decompress = gzip_decompress,
#endif
- },
};
+/* All UBIFS compressors */
+struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
+
+
+#ifdef __BAREBOX__
+/* from mm/util.c */
+
+/**
+ * kmemdup - duplicate region of memory
+ *
+ * @src: memory region to duplicate
+ * @len: memory region length
+ * @gfp: GFP mask to use
+ */
+void *kmemdup(const void *src, size_t len, gfp_t gfp)
+{
+ void *p;
+
+ p = kmalloc(len, gfp);
+ if (p)
+ memcpy(p, src, len);
+ return p;
+}
+
+struct crypto_comp {
+ int compressor;
+};
+
+static inline struct crypto_comp
+*crypto_alloc_comp(const char *alg_name, u32 type, u32 mask)
+{
+ struct ubifs_compressor *comp;
+ struct crypto_comp *ptr;
+ int i = 0;
+
+ ptr = kzalloc(sizeof(struct crypto_comp), 0);
+ while (i < UBIFS_COMPR_TYPES_CNT) {
+ comp = ubifs_compressors[i];
+ if (!comp) {
+ i++;
+ continue;
+ }
+ if (strncmp(alg_name, comp->capi_name, strlen(alg_name)) == 0) {
+ ptr->compressor = i;
+ return ptr;
+ }
+ i++;
+ }
+ if (i >= UBIFS_COMPR_TYPES_CNT) {
+ dbg_gen("invalid compression type %s", alg_name);
+ free (ptr);
+ return NULL;
+ }
+ return ptr;
+}
+static inline int
+crypto_comp_decompress(const struct ubifs_info *c, struct crypto_comp *tfm,
+ const u8 *src, unsigned int slen, u8 *dst,
+ unsigned int *dlen)
+{
+ struct ubifs_compressor *compr = ubifs_compressors[tfm->compressor];
+ int err;
+
+ if (compr->compr_type == UBIFS_COMPR_NONE) {
+ memcpy(dst, src, slen);
+ *dlen = slen;
+ return 0;
+ }
+
+ err = compr->decompress(src, slen, dst, (size_t *)dlen);
+ if (err)
+ ubifs_err(c, "cannot decompress %d bytes, compressor %s, "
+ "error %d", slen, compr->name, err);
+
+ return err;
+
+ return 0;
+}
+
+/* from shrinker.c */
+
+/* Global clean znode counter (for all mounted UBIFS instances) */
+atomic_long_t ubifs_clean_zn_cnt;
+
+#endif
+
/**
* ubifs_decompress - decompress data.
* @in_buf: data to decompress
@@ -96,21 +177,21 @@ struct ubifs_compressor ubifs_compressors[UBIFS_COMPR_TYPES_CNT] = {
* The length of the uncompressed data is returned in @out_len. This functions
* returns %0 on success or a negative error code on failure.
*/
-static int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
- int *out_len, int compr_type)
+int ubifs_decompress(const struct ubifs_info *c, 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);
+ ubifs_err(c, "invalid compression type %d", compr_type);
return -EINVAL;
}
- compr = &ubifs_compressors[compr_type];
+ compr = ubifs_compressors[compr_type];
if (unlikely(!compr->capi_name)) {
- ubifs_err("%s compression is not compiled in", compr->name);
+ ubifs_err(c, "%s compression is not compiled in", compr->name);
return -EINVAL;
}
@@ -120,18 +201,207 @@ static int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
return 0;
}
- err = compr->decompress(in_buf, in_len, out_buf, (size_t *)out_len);
+ if (compr->decomp_mutex)
+ mutex_lock(compr->decomp_mutex);
+ err = crypto_comp_decompress(c, compr->cc, in_buf, in_len, out_buf,
+ (unsigned int *)out_len);
+ if (compr->decomp_mutex)
+ mutex_unlock(compr->decomp_mutex);
if (err)
- ubifs_err("cannot decompress %d bytes, compressor %s, "
- "error %d", in_len, compr->name, err);
+ ubifs_err(c, "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
+
+ if (compr->capi_name) {
+ compr->cc = crypto_alloc_comp(compr->capi_name, 0, 0);
+ if (IS_ERR(compr->cc)) {
+ dbg_gen("cannot initialize compressor %s,"
+ " error %ld", compr->name,
+ PTR_ERR(compr->cc));
+ return PTR_ERR(compr->cc);
+ }
+ }
+
+ 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...
*/
+#ifndef __BAREBOX__
+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);
+#ifndef __BAREBOX__
+ ubifs_iput(inode);
+#endif
+
+ 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(c, "cannot find next direntry, error %d", err);
+ return err;
+ }
+
+ kfree(file->private_data);
+ file->private_data = NULL;
+ file->f_pos = 2;
+ return 0;
+}
+#endif
+
static int ubifs_finddir(struct super_block *sb, char *dirname,
unsigned long root_inum, unsigned long *inum)
{
@@ -206,26 +476,32 @@ static int ubifs_finddir(struct super_block *sb, char *dirname,
out:
if (err != -ENOENT)
- ubifs_err("cannot find next direntry, error %d", err);
+ dbg_gen("cannot find next direntry, error %d", err);
out_free:
- kfree(file->private_data);
- free(file);
- free(dentry);
- free(dir);
+ if (file->private_data)
+ kfree(file->private_data);
+ if (file)
+ free(file);
+ if (dentry)
+ free(dentry);
+ if (dir)
+ free(dir);
return ret;
}
-static struct inode *ubifs_findfile(struct super_block *sb, const char *filename)
+static unsigned long ubifs_findfile(struct super_block *sb, const char *filename)
{
int ret;
char *next;
char fpath[128];
+ char symlinkpath[128];
char *name = fpath;
unsigned long root_inum = 1;
unsigned long inum;
- struct inode *inode = 0;
+ int symlink_count = 0; /* Don't allow symlink recursion */
+ char link_name[64];
strcpy(fpath, filename);
@@ -238,9 +514,12 @@ static struct inode *ubifs_findfile(struct super_block *sb, const char *filename
*/
inum = root_inum;
if (!name || *name == '\0')
- return ubifs_iget(sb, 1);
+ return inum;
for (;;) {
+ struct inode *inode;
+ struct ubifs_inode *ui;
+
/* Extract the actual part from the pathname. */
next = strchr(name, '/');
if (next) {
@@ -248,13 +527,41 @@ static struct inode *ubifs_findfile(struct super_block *sb, const char *filename
while (*next == '/')
*(next++) = '\0';
}
+
ret = ubifs_finddir(sb, name, root_inum, &inum);
if (!ret)
- break;
-
+ return 0;
inode = ubifs_iget(sb, inum);
+
if (!inode)
- break;
+ 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
@@ -262,16 +569,125 @@ static struct inode *ubifs_findfile(struct super_block *sb, const char *filename
/* Found the node! */
if (!next || *next == '\0')
- return inode;
+ return inum;
root_inum = inum;
name = next;
+ }
+
+ return 0;
+}
+
+#ifndef __BAREBOX__
+int ubifs_set_blk_dev(block_dev_desc_t *rbdd, disk_partition_t *info)
+{
+ if (rbdd) {
+ debug("UBIFS cannot be used with normal block devices\n");
+ return -1;
+ }
+
+ /*
+ * Should never happen since get_device_and_partition() already checks
+ * this, but better safe then sorry.
+ */
+ if (!ubifs_is_mounted()) {
+ debug("UBIFS not mounted, use ubifsmount to mount volume first!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int ubifs_ls(const 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, (char *)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;
+}
- ubifs_iput(inode);
+int ubifs_exists(const char *filename)
+{
+ struct ubifs_info *c = ubifs_sb->s_fs_info;
+ unsigned long inum;
+
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
+ inum = ubifs_findfile(ubifs_sb, (char *)filename);
+ ubi_close_volume(c->ubi);
+
+ return inum != 0;
+}
+
+int ubifs_size(const char *filename, loff_t *size)
+{
+ struct ubifs_info *c = ubifs_sb->s_fs_info;
+ unsigned long inum;
+ struct inode *inode;
+ int err = 0;
+
+ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY);
+
+ inum = ubifs_findfile(ubifs_sb, (char *)filename);
+ if (!inum) {
+ err = -1;
+ goto out;
+ }
+
+ 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;
}
- return NULL;
+ *size = inode->i_size;
+
+ ubifs_iput(inode);
+out:
+ ubi_close_volume(c->ubi);
+ return err;
}
+#endif
/*
* ubifsload...
@@ -279,6 +695,11 @@ static struct inode *ubifs_findfile(struct super_block *sb, const char *filename
/* 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)
{
@@ -304,7 +725,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
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,
+ err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len,
le16_to_cpu(dn->compr_type));
if (err || len != out_len)
goto dump;
@@ -320,12 +741,247 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
return 0;
dump:
- ubifs_err("bad data node (block %u, inode %lu)",
+ ubifs_err(c, "bad data node (block %u, inode %lu)",
block, inode->i_ino);
- dbg_dump_node(c, dn);
+ ubifs_dump_node(c, dn);
return -EINVAL;
}
+#ifndef __BAREBOX__
+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 = kzalloc(UBIFS_BLOCK_SIZE, 0);
+ 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(c, "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_read(const char *filename, void *buf, loff_t offset,
+ loff_t size, loff_t *actread)
+{
+ 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;
+
+ *actread = 0;
+
+ if (offset & (PAGE_SIZE - 1)) {
+ printf("ubifs: Error offset must be a multple of %d\n",
+ PAGE_SIZE);
+ return -1;
+ }
+
+ 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, (char *)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 (offset > inode->i_size) {
+ printf("ubifs: Error offset (%lld) > file-size (%lld)\n",
+ offset, size);
+ err = -1;
+ goto put_inode;
+ }
+
+ /*
+ * If no size was specified or if size bigger than filesize
+ * set size to filesize
+ */
+ if ((size == 0) || (size > (inode->i_size - offset)))
+ size = inode->i_size - offset;
+
+ count = (size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT;
+
+ page.addr = buf;
+ page.index = offset / PAGE_SIZE;
+ 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);
+ *actread = i * PAGE_SIZE;
+ } else {
+ *actread = size;
+ }
+
+put_inode:
+ ubifs_iput(inode);
+
+out:
+ ubi_close_volume(c->ubi);
+ return err;
+}
+
+void ubifs_close(void)
+{
+}
+
+/* Compat wrappers for common/cmd_ubifs.c */
+int ubifs_load(char *filename, u32 addr, u32 size)
+{
+ loff_t actread;
+ int err;
+
+ printf("Loading file '%s' to addr 0x%08x...\n", filename, addr);
+
+ err = ubifs_read(filename, (void *)addr, 0, size, &actread);
+ if (err == 0) {
+ setenv_hex("filesize", actread);
+ printf("Done\n");
+ }
+
+ return err;
+}
+#endif
+
+void uboot_ubifs_umount(void)
+{
+ if (ubifs_sb) {
+ printf("Unmounting UBIFS volume %s!\n",
+ ((struct ubifs_info *)(ubifs_sb->s_fs_info))->vi.name);
+ ubifs_umount(ubifs_sb->s_fs_info);
+ ubifs_sb = NULL;
+ }
+}
+
+
+
+
struct ubifs_file {
struct inode *inode;
void *buf;
@@ -338,8 +994,13 @@ static int ubifs_open(struct device_d *dev, FILE *file, const char *filename)
struct ubifs_priv *priv = dev->priv;
struct inode *inode;
struct ubifs_file *uf;
+ unsigned long inum;
- inode = ubifs_findfile(priv->sb, filename);
+ inum = ubifs_findfile(priv->sb, filename);
+ if (!inum)
+ return -ENOENT;
+
+ inode = ubifs_iget(priv->sb, inum);
if (!inode)
return -ENOENT;
@@ -460,11 +1121,13 @@ static DIR *ubifs_opendir(struct device_d *dev, const char *pathname)
unsigned long inum;
struct ubifs_info *c = priv->sb->s_fs_info;
- inode = ubifs_findfile(priv->sb, pathname);
- if (!inode)
+ inum = ubifs_findfile(priv->sb, pathname);
+ if (!inum)
return NULL;
- inum = inode->i_ino;
+ inode = ubifs_iget(priv->sb, inum);
+ if (!inode)
+ return NULL;
ubifs_iput(inode);
@@ -536,8 +1199,13 @@ static int ubifs_stat(struct device_d *dev, const char *filename, struct stat *s
{
struct ubifs_priv *priv = dev->priv;
struct inode *inode;
+ unsigned long inum;
+
+ inum = ubifs_findfile(priv->sb, filename);
+ if (!inum)
+ return -ENOENT;
- inode = ubifs_findfile(priv->sb, filename);
+ inode = ubifs_iget(priv->sb, inum);
if (!inode)
return -ENOENT;
@@ -570,9 +1238,13 @@ static int ubifs_readlink(struct device_d *dev, const char *pathname, char *buf,
struct inode *inode;
char *symlink;
int len;
+ unsigned long inum;
- inode = ubifs_findfile(priv->sb, pathname);
+ inum = ubifs_findfile(priv->sb, pathname);
+ if (!inum)
+ return -ENOENT;
+ inode = ubifs_iget(priv->sb, inum);
if (!inode)
return -ENOENT;
@@ -585,7 +1257,7 @@ static int ubifs_readlink(struct device_d *dev, const char *pathname, char *buf,
return 0;
}
-static void ubifs_set_rootarg(struct ubifs_priv *priv, struct fs_device_d *fsdev)
+void ubifs_set_rootarg(struct ubifs_priv *priv, struct fs_device_d *fsdev)
{
struct ubi_volume_info vi = {};
struct ubi_device_info di = {};