The U-Boot UBIFS implementation is largely a direct copy from the current Linux version (2.6.29-rc6). As already done in the UBI version we have an "abstraction layer" to redefine or remove some OS calls (e.g. mutex_lock() ...). This makes it possible to use the original Linux code with very little changes. And by this we can better update to later Linux versions. I removed some of the Linux features that are not used in the U-Boot version (e.g. garbage-collection, write support). Signed-off-by: Stefan Roese <sr@denx.de> CC: Artem Bityutskiy <dedekind@infradead.org> CC: Adrian Hunter <ext-Adrian.Hunter@nokia.com>master
parent
b1b4e89a0f
commit
9eefe2a2b3
@ -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.a
|
||||
|
||||
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) |
||||
$(AR) $(ARFLAGS) $@ $(OBJS)
|
||||
|
||||
#########################################################################
|
||||
|
||||
# defines $(obj).depend target
|
||||
include $(SRCTREE)/rules.mk |
||||
|
||||
sinclude $(obj).depend |
||||
|
||||
#########################################################################
|
@ -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); |
||||
} |
@ -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; |
||||
} |
@ -0,0 +1,30 @@ |
||||
/*
|
||||
* 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 */ |
||||
|
@ -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 */ |
@ -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__ */ |
@ -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; |
||||
} |
@ -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__ */ |
@ -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); |
||||
} |
@ -0,0 +1,842 @@ |
||||
/*
|
||||
* This file is part of UBIFS. |
||||
* |
||||
* Copyright (C) 2006-2008 Nokia Corporation. |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License version 2 as published by |
||||
* the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, but WITHOUT |
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
||||
* more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with |
||||
* this program; if not, write to the Free Software Foundation, Inc., 51 |
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
* |
||||
* Authors: Adrian Hunter |
||||
* Artem Bityutskiy (Битюцкий Артём) |
||||
*/ |
||||
|
||||
/*
|
||||
* This file implements the functions that access LEB properties and their |
||||
* categories. LEBs are categorized based on the needs of UBIFS, and the |
||||
* categories are stored as either heaps or lists to provide a fast way of |
||||
* finding a LEB in a particular category. For example, UBIFS may need to find |
||||
* an empty LEB for the journal, or a very dirty LEB for garbage collection. |
||||
*/ |
||||
|
||||
#include "ubifs.h" |
||||
|
||||
/**
|
||||
* get_heap_comp_val - get the LEB properties value for heap comparisons. |
||||
* @lprops: LEB properties |
||||
* @cat: LEB category |
||||
*/ |
||||
static int get_heap_comp_val(struct ubifs_lprops *lprops, int cat) |
||||
{ |
||||
switch (cat) { |
||||
case LPROPS_FREE: |
||||
return lprops->free; |
||||
case LPROPS_DIRTY_IDX: |
||||
return lprops->free + lprops->dirty; |
||||
default: |
||||
return lprops->dirty; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* move_up_lpt_heap - move a new heap entry up as far as possible. |
||||
* @c: UBIFS file-system description object |
||||
* @heap: LEB category heap |
||||
* @lprops: LEB properties to move |
||||
* @cat: LEB category |
||||
* |
||||
* New entries to a heap are added at the bottom and then moved up until the |
||||
* parent's value is greater. In the case of LPT's category heaps, the value |
||||
* is either the amount of free space or the amount of dirty space, depending |
||||
* on the category. |
||||
*/ |
||||
static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, |
||||
struct ubifs_lprops *lprops, int cat) |
||||
{ |
||||
int val1, val2, hpos; |
||||
|
||||
hpos = lprops->hpos; |
||||
if (!hpos) |
||||
return; /* Already top of the heap */ |
||||
val1 = get_heap_comp_val(lprops, cat); |
||||
/* Compare to parent and, if greater, move up the heap */ |
||||
do { |
||||
int ppos = (hpos - 1) / 2; |
||||
|
||||
val2 = get_heap_comp_val(heap->arr[ppos], cat); |
||||
if (val2 >= val1) |
||||
return; |
||||
/* Greater than parent so move up */ |
||||
heap->arr[ppos]->hpos = hpos; |
||||
heap->arr[hpos] = heap->arr[ppos]; |
||||
heap->arr[ppos] = lprops; |
||||
lprops->hpos = ppos; |
||||
hpos = ppos; |
||||
} while (hpos); |
||||
} |
||||
|
||||
/**
|
||||
* adjust_lpt_heap - move a changed heap entry up or down the heap. |
||||
* @c: UBIFS file-system description object |
||||
* @heap: LEB category heap |
||||
* @lprops: LEB properties to move |
||||
* @hpos: heap position of @lprops |
||||
* @cat: LEB category |
||||
* |
||||
* Changed entries in a heap are moved up or down until the parent's value is |
||||
* greater. In the case of LPT's category heaps, the value is either the amount |
||||
* of free space or the amount of dirty space, depending on the category. |
||||
*/ |
||||
static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, |
||||
struct ubifs_lprops *lprops, int hpos, int cat) |
||||
{ |
||||
int val1, val2, val3, cpos; |
||||
|
||||
val1 = get_heap_comp_val(lprops, cat); |
||||
/* Compare to parent and, if greater than parent, move up the heap */ |
||||
if (hpos) { |
||||
int ppos = (hpos - 1) / 2; |
||||
|
||||
val2 = get_heap_comp_val(heap->arr[ppos], cat); |
||||
if (val1 > val2) { |
||||
/* Greater than parent so move up */ |
||||
while (1) { |
||||
heap->arr[ppos]->hpos = hpos; |
||||
heap->arr[hpos] = heap->arr[ppos]; |
||||
heap->arr[ppos] = lprops; |
||||
lprops->hpos = ppos; |
||||
hpos = ppos; |
||||
if (!hpos) |
||||
return; |
||||
ppos = (hpos - 1) / 2; |
||||
val2 = get_heap_comp_val(heap->arr[ppos], cat); |
||||
if (val1 <= val2) |
||||
return; |
||||
/* Still greater than parent so keep going */ |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Not greater than parent, so compare to children */ |
||||
while (1) { |
||||
/* Compare to left child */ |
||||
cpos = hpos * 2 + 1; |
||||
if (cpos >= heap->cnt) |
||||
return; |
||||
val2 = get_heap_comp_val(heap->arr[cpos], cat); |
||||
if (val1 < val2) { |
||||
/* Less than left child, so promote biggest child */ |
||||
if (cpos + 1 < heap->cnt) { |
||||
val3 = get_heap_comp_val(heap->arr[cpos + 1], |
||||
cat); |
||||
if (val3 > val2) |
||||
cpos += 1; /* Right child is bigger */ |
||||
} |
||||
heap->arr[cpos]->hpos = hpos; |
||||
heap->arr[hpos] = heap->arr[cpos]; |
||||
heap->arr[cpos] = lprops; |
||||
lprops->hpos = cpos; |
||||
hpos = cpos; |
||||
continue; |
||||
} |
||||
/* Compare to right child */ |
||||
cpos += 1; |
||||
if (cpos >= heap->cnt) |
||||
return; |
||||
val3 = get_heap_comp_val(heap->arr[cpos], cat); |
||||
if (val1 < val3) { |
||||
/* Less than right child, so promote right child */ |
||||
heap->arr[cpos]->hpos = hpos; |
||||
heap->arr[hpos] = heap->arr[cpos]; |
||||
heap->arr[cpos] = lprops; |
||||
lprops->hpos = cpos; |
||||
hpos = cpos; |
||||
continue; |
||||
} |
||||
return; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* add_to_lpt_heap - add LEB properties to a LEB category heap. |
||||
* @c: UBIFS file-system description object |
||||
* @lprops: LEB properties to add |
||||
* @cat: LEB category |
||||
* |
||||
* This function returns %1 if @lprops is added to the heap for LEB category |
||||
* @cat, otherwise %0 is returned because the heap is full. |
||||
*/ |
||||
static int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops, |
||||
int cat) |
||||
{ |
||||
struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1]; |
||||
|
||||
if (heap->cnt >= heap->max_cnt) { |
||||
const int b = LPT_HEAP_SZ / 2 - 1; |
||||
int cpos, val1, val2; |
||||
|
||||
/* Compare to some other LEB on the bottom of heap */ |
||||
/* Pick a position kind of randomly */ |
||||
cpos = (((size_t)lprops >> 4) & b) + b; |
||||
ubifs_assert(cpos >= b); |
||||
ubifs_assert(cpos < LPT_HEAP_SZ); |
||||
ubifs_assert(cpos < heap->cnt); |
||||
|
||||
val1 = get_heap_comp_val(lprops, cat); |
||||
val2 = get_heap_comp_val(heap->arr[cpos], cat); |
||||
if (val1 > val2) { |
||||
struct ubifs_lprops *lp; |
||||
|
||||
lp = heap->arr[cpos]; |
||||
lp->flags &= ~LPROPS_CAT_MASK; |
||||
lp->flags |= LPROPS_UNCAT; |
||||
list_add(&lp->list, &c->uncat_list); |
||||
lprops->hpos = cpos; |
||||
heap->arr[cpos] = lprops; |
||||
move_up_lpt_heap(c, heap, lprops, cat); |
||||
dbg_check_heap(c, heap, cat, lprops->hpos); |
||||
return 1; /* Added to heap */ |
||||
} |
||||
dbg_check_heap(c, heap, cat, -1); |
||||
return 0; /* Not added to heap */ |
||||
} else { |
||||
lprops->hpos = heap->cnt++; |
||||
heap->arr[lprops->hpos] = lprops; |
||||
move_up_lpt_heap(c, heap, lprops, cat); |
||||
dbg_check_heap(c, heap, cat, lprops->hpos); |
||||
return 1; /* Added to heap */ |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* remove_from_lpt_heap - remove LEB properties from a LEB category heap. |
||||
* @c: UBIFS file-system description object |
||||
* @lprops: LEB properties to remove |
||||
* @cat: LEB category |
||||
*/ |
||||
static void remove_from_lpt_heap(struct ubifs_info *c, |
||||
struct ubifs_lprops *lprops, int cat) |
||||
{ |
||||
struct ubifs_lpt_heap *heap; |
||||
int hpos = lprops->hpos; |
||||
|
||||
heap = &c->lpt_heap[cat - 1]; |
||||
ubifs_assert(hpos >= 0 && hpos < heap->cnt); |
||||
ubifs_assert(heap->arr[hpos] == lprops); |
||||
heap->cnt -= 1; |
||||
if (hpos < heap->cnt) { |
||||
heap->arr[hpos] = heap->arr[heap->cnt]; |
||||
heap->arr[hpos]->hpos = hpos; |
||||
adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat); |
||||
} |
||||
dbg_check_heap(c, heap, cat, -1); |
||||
} |
||||
|
||||
/**
|
||||
* lpt_heap_replace - replace lprops in a category heap. |
||||
* @c: UBIFS file-system description object |
||||
* @old_lprops: LEB properties to replace |
||||
* @new_lprops: LEB properties with which to replace |
||||
* @cat: LEB category |
||||
* |
||||
* During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode) |
||||
* and the lprops that the pnode contains. When that happens, references in |
||||
* the category heaps to those lprops must be updated to point to the new |
||||
* lprops. This function does that. |
||||
*/ |
||||
static void lpt_heap_replace(struct ubifs_info *c, |
||||
struct ubifs_lprops *old_lprops, |
||||
struct ubifs_lprops *new_lprops, int cat) |
||||
{ |
||||
struct ubifs_lpt_heap *heap; |
||||
int hpos = new_lprops->hpos; |
||||
|
||||
heap = &c->lpt_heap[cat - 1]; |
||||
heap->arr[hpos] = new_lprops; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_add_to_cat - add LEB properties to a category list or heap. |
||||
* @c: UBIFS file-system description object |
||||
* @lprops: LEB properties to add |
||||
* @cat: LEB category to which to add |
||||
* |
||||
* LEB properties are categorized to enable fast find operations. |
||||
*/ |
||||
void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops, |
||||
int cat) |
||||
{ |
||||
switch (cat) { |
||||
case LPROPS_DIRTY: |
||||
case LPROPS_DIRTY_IDX: |
||||
case LPROPS_FREE: |
||||
if (add_to_lpt_heap(c, lprops, cat)) |
||||
break; |
||||
/* No more room on heap so make it uncategorized */ |
||||
cat = LPROPS_UNCAT; |
||||
/* Fall through */ |
||||
case LPROPS_UNCAT: |
||||
list_add(&lprops->list, &c->uncat_list); |
||||
break; |
||||
case LPROPS_EMPTY: |
||||
list_add(&lprops->list, &c->empty_list); |
||||
break; |
||||
case LPROPS_FREEABLE: |
||||
list_add(&lprops->list, &c->freeable_list); |
||||
c->freeable_cnt += 1; |
||||
break; |
||||
case LPROPS_FRDI_IDX: |
||||
list_add(&lprops->list, &c->frdi_idx_list); |
||||
break; |
||||
default: |
||||
ubifs_assert(0); |
||||
} |
||||
lprops->flags &= ~LPROPS_CAT_MASK; |
||||
lprops->flags |= cat; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_remove_from_cat - remove LEB properties from a category list or heap. |
||||
* @c: UBIFS file-system description object |
||||
* @lprops: LEB properties to remove |
||||
* @cat: LEB category from which to remove |
||||
* |
||||
* LEB properties are categorized to enable fast find operations. |
||||
*/ |
||||
static void ubifs_remove_from_cat(struct ubifs_info *c, |
||||
struct ubifs_lprops *lprops, int cat) |
||||
{ |
||||
switch (cat) { |
||||
case LPROPS_DIRTY: |
||||
case LPROPS_DIRTY_IDX: |
||||
case LPROPS_FREE: |
||||
remove_from_lpt_heap(c, lprops, cat); |
||||
break; |
||||
case LPROPS_FREEABLE: |
||||
c->freeable_cnt -= 1; |
||||
ubifs_assert(c->freeable_cnt >= 0); |
||||
/* Fall through */ |
||||
case LPROPS_UNCAT: |
||||
case LPROPS_EMPTY: |
||||
case LPROPS_FRDI_IDX: |
||||
ubifs_assert(!list_empty(&lprops->list)); |
||||
list_del(&lprops->list); |
||||
break; |
||||
default: |
||||
ubifs_assert(0); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_replace_cat - replace lprops in a category list or heap. |
||||
* @c: UBIFS file-system description object |
||||
* @old_lprops: LEB properties to replace |
||||
* @new_lprops: LEB properties with which to replace |
||||
* |
||||
* During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode) |
||||
* and the lprops that the pnode contains. When that happens, references in |
||||
* category lists and heaps must be replaced. This function does that. |
||||
*/ |
||||
void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops, |
||||
struct ubifs_lprops *new_lprops) |
||||
{ |
||||
int cat; |
||||
|
||||
cat = new_lprops->flags & LPROPS_CAT_MASK; |
||||
switch (cat) { |
||||
case LPROPS_DIRTY: |
||||
case LPROPS_DIRTY_IDX: |
||||
case LPROPS_FREE: |
||||
lpt_heap_replace(c, old_lprops, new_lprops, cat); |
||||
break; |
||||
case LPROPS_UNCAT: |
||||
case LPROPS_EMPTY: |
||||
case LPROPS_FREEABLE: |
||||
case LPROPS_FRDI_IDX: |
||||
list_replace(&old_lprops->list, &new_lprops->list); |
||||
break; |
||||
default: |
||||
ubifs_assert(0); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_ensure_cat - ensure LEB properties are categorized. |
||||
* @c: UBIFS file-system description object |
||||
* @lprops: LEB properties |
||||
* |
||||
* A LEB may have fallen off of the bottom of a heap, and ended up as |
||||
* uncategorized even though it has enough space for us now. If that is the case |
||||
* this function will put the LEB back onto a heap. |
||||
*/ |
||||
void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops) |
||||
{ |
||||
int cat = lprops->flags & LPROPS_CAT_MASK; |
||||
|
||||
if (cat != LPROPS_UNCAT) |
||||
return; |
||||
cat = ubifs_categorize_lprops(c, lprops); |
||||
if (cat == LPROPS_UNCAT) |
||||
return; |
||||
ubifs_remove_from_cat(c, lprops, LPROPS_UNCAT); |
||||
ubifs_add_to_cat(c, lprops, cat); |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_categorize_lprops - categorize LEB properties. |
||||
* @c: UBIFS file-system description object |
||||
* @lprops: LEB properties to categorize |
||||
* |
||||
* LEB properties are categorized to enable fast find operations. This function |
||||
* returns the LEB category to which the LEB properties belong. Note however |
||||
* that if the LEB category is stored as a heap and the heap is full, the |
||||
* LEB properties may have their category changed to %LPROPS_UNCAT. |
||||
*/ |
||||
int ubifs_categorize_lprops(const struct ubifs_info *c, |
||||
const struct ubifs_lprops *lprops) |
||||
{ |
||||
if (lprops->flags & LPROPS_TAKEN) |
||||
return LPROPS_UNCAT; |
||||
|
||||
if (lprops->free == c->leb_size) { |
||||
ubifs_assert(!(lprops->flags & LPROPS_INDEX)); |
||||
return LPROPS_EMPTY; |
||||
} |
||||
|
||||
if (lprops->free + lprops->dirty == c->leb_size) { |
||||
if (lprops->flags & LPROPS_INDEX) |
||||
return LPROPS_FRDI_IDX; |
||||
else |
||||
return LPROPS_FREEABLE; |
||||
} |
||||
|
||||
if (lprops->flags & LPROPS_INDEX) { |
||||
if (lprops->dirty + lprops->free >= c->min_idx_node_sz) |
||||
return LPROPS_DIRTY_IDX; |
||||
} else { |
||||
if (lprops->dirty >= c->dead_wm && |
||||
lprops->dirty > lprops->free) |
||||
return LPROPS_DIRTY; |
||||
if (lprops->free > 0) |
||||
return LPROPS_FREE; |
||||
} |
||||
|
||||
return LPROPS_UNCAT; |
||||
} |
||||
|
||||
/**
|
||||
* change_category - change LEB properties category. |
||||
* @c: UBIFS file-system description object |
||||
* @lprops: LEB properties to recategorize |
||||
* |
||||
* LEB properties are categorized to enable fast find operations. When the LEB |
||||
* properties change they must be recategorized. |
||||
*/ |
||||
static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops) |
||||
{ |
||||
int old_cat = lprops->flags & LPROPS_CAT_MASK; |
||||
int new_cat = ubifs_categorize_lprops(c, lprops); |
||||
|
||||
if (old_cat == new_cat) { |
||||
struct ubifs_lpt_heap *heap = &c->lpt_heap[new_cat - 1]; |
||||
|
||||
/* lprops on a heap now must be moved up or down */ |
||||
if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT) |
||||
return; /* Not on a heap */ |
||||
heap = &c->lpt_heap[new_cat - 1]; |
||||
adjust_lpt_heap(c, heap, lprops, lprops->hpos, new_cat); |
||||
} else { |
||||
ubifs_remove_from_cat(c, lprops, old_cat); |
||||
ubifs_add_to_cat(c, lprops, new_cat); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* calc_dark - calculate LEB dark space size. |
||||
* @c: the UBIFS file-system description object |
||||
* @spc: amount of free and dirty space in the LEB |
||||
* |
||||
* This function calculates amount of dark space in an LEB which has @spc bytes |
||||
* of free and dirty space. Returns the calculations result. |
||||
* |
||||
* Dark space is the space which is not always usable - it depends on which |
||||
* nodes are written in which order. E.g., if an LEB has only 512 free bytes, |
||||
* it is dark space, because it cannot fit a large data node. So UBIFS cannot |
||||
* count on this LEB and treat these 512 bytes as usable because it is not true |
||||
* if, for example, only big chunks of uncompressible data will be written to |
||||
* the FS. |
||||
*/ |
||||
static int calc_dark(struct ubifs_info *c, int spc) |
||||
{ |
||||
ubifs_assert(!(spc & 7)); |
||||
|
||||
if (spc < c->dark_wm) |
||||
return spc; |
||||
|
||||
/*
|
||||
* If we have slightly more space then the dark space watermark, we can |
||||
* anyway safely assume it we'll be able to write a node of the |
||||
* smallest size there. |
||||
*/ |
||||
if (spc - c->dark_wm < MIN_WRITE_SZ) |
||||
return spc - MIN_WRITE_SZ; |
||||
|
||||
return c->dark_wm; |
||||
} |
||||
|
||||
/**
|
||||
* is_lprops_dirty - determine if LEB properties are dirty. |
||||
* @c: the UBIFS file-system description object |
||||
* @lprops: LEB properties to test |
||||
*/ |
||||
static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops) |
||||
{ |
||||
struct ubifs_pnode *pnode; |
||||
int pos; |
||||
|
||||
pos = (lprops->lnum - c->main_first) & (UBIFS_LPT_FANOUT - 1); |
||||
pnode = (struct ubifs_pnode *)container_of(lprops - pos, |
||||
struct ubifs_pnode, |
||||
lprops[0]); |
||||
return !test_bit(COW_ZNODE, &pnode->flags) && |
||||
test_bit(DIRTY_CNODE, &pnode->flags); |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_change_lp - change LEB properties. |
||||
* @c: the UBIFS file-system description object |
||||
* @lp: LEB properties to change |
||||
* @free: new free space amount |
||||
* @dirty: new dirty space amount |
||||
* @flags: new flags |
||||
* @idx_gc_cnt: change to the count of idx_gc list |
||||
* |
||||
* This function changes LEB properties (@free, @dirty or @flag). However, the |
||||
* property which has the %LPROPS_NC value is not changed. Returns a pointer to |
||||
* the updated LEB properties on success and a negative error code on failure. |
||||
* |
||||
* Note, the LEB properties may have had to be copied (due to COW) and |
||||
* consequently the pointer returned may not be the same as the pointer |
||||
* passed. |
||||
*/ |
||||
const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, |
||||
const struct ubifs_lprops *lp, |
||||
int free, int dirty, int flags, |
||||
int idx_gc_cnt) |
||||
{ |
||||
/*
|
||||
* This is the only function that is allowed to change lprops, so we |
||||
* discard the const qualifier. |
||||
*/ |
||||
struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp; |
||||
|
||||
dbg_lp("LEB %d, free %d, dirty %d, flags %d", |
||||
lprops->lnum, free, dirty, flags); |
||||
|
||||
ubifs_assert(mutex_is_locked(&c->lp_mutex)); |
||||
ubifs_assert(c->lst.empty_lebs >= 0 && |
||||
c->lst.empty_lebs <= c->main_lebs); |
||||
ubifs_assert(c->freeable_cnt >= 0); |
||||
ubifs_assert(c->freeable_cnt <= c->main_lebs); |
||||
ubifs_assert(c->lst.taken_empty_lebs >= 0); |
||||
ubifs_assert(c->lst.taken_empty_lebs <= c->lst.empty_lebs); |
||||
ubifs_assert(!(c->lst.total_free & 7) && !(c->lst.total_dirty & 7)); |
||||
ubifs_assert(!(c->lst.total_dead & 7) && !(c->lst.total_dark & 7)); |
||||
ubifs_assert(!(c->lst.total_used & 7)); |
||||
ubifs_assert(free == LPROPS_NC || free >= 0); |
||||
ubifs_assert(dirty == LPROPS_NC || dirty >= 0); |
||||
|
||||
if (!is_lprops_dirty(c, lprops)) { |
||||
lprops = ubifs_lpt_lookup_dirty(c, lprops->lnum); |
||||
if (IS_ERR(lprops)) |
||||
return lprops; |
||||
} else |
||||
ubifs_assert(lprops == ubifs_lpt_lookup_dirty(c, lprops->lnum)); |
||||
|
||||
ubifs_assert(!(lprops->free & 7) && !(lprops->dirty & 7)); |
||||
|
||||
spin_lock(&c->space_lock); |
||||
if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size) |
||||
c->lst.taken_empty_lebs -= 1; |
||||
|
||||
if (!(lprops->flags & LPROPS_INDEX)) { |
||||
int old_spc; |
||||
|
||||
old_spc = lprops->free + lprops->dirty; |
||||
if (old_spc < c->dead_wm) |
||||
c->lst.total_dead -= old_spc; |
||||
else |
||||
c->lst.total_dark -= calc_dark(c, old_spc); |
||||
|
||||
c->lst.total_used -= c->leb_size - old_spc; |
||||
} |
||||
|
||||
if (free != LPROPS_NC) { |
||||
free = ALIGN(free, 8); |
||||
c->lst.total_free += free - lprops->free; |
||||
|
||||
/* Increase or decrease empty LEBs counter if needed */ |
||||
if (free == c->leb_size) { |
||||
if (lprops->free != c->leb_size) |
||||
c->lst.empty_lebs += 1; |
||||
} else if (lprops->free == c->leb_size) |
||||
c->lst.empty_lebs -= 1; |
||||
lprops->free = free; |
||||
} |
||||
|
||||
if (dirty != LPROPS_NC) { |
||||
dirty = ALIGN(dirty, 8); |
||||
c->lst.total_dirty += dirty - lprops->dirty; |
||||
lprops->dirty = dirty; |
||||
} |
||||
|
||||
if (flags != LPROPS_NC) { |
||||
/* Take care about indexing LEBs counter if needed */ |
||||
if ((lprops->flags & LPROPS_INDEX)) { |
||||
if (!(flags & LPROPS_INDEX)) |
||||
c->lst.idx_lebs -= 1; |
||||
} else if (flags & LPROPS_INDEX) |
||||
c->lst.idx_lebs += 1; |
||||
lprops->flags = flags; |
||||
} |
||||
|
||||
if (!(lprops->flags & LPROPS_INDEX)) { |
||||
int new_spc; |
||||
|
||||
new_spc = lprops->free + lprops->dirty; |
||||
if (new_spc < c->dead_wm) |
||||
c->lst.total_dead += new_spc; |
||||
else |
||||
c->lst.total_dark += calc_dark(c, new_spc); |
||||
|
||||
c->lst.total_used += c->leb_size - new_spc; |
||||
} |
||||
|
||||
if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size) |
||||
c->lst.taken_empty_lebs += 1; |
||||
|
||||
change_category(c, lprops); |
||||
c->idx_gc_cnt += idx_gc_cnt; |
||||
spin_unlock(&c->space_lock); |
||||
return lprops; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_get_lp_stats - get lprops statistics. |
||||
* @c: UBIFS file-system description object |
||||
* @st: return statistics |
||||
*/ |
||||
void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst) |
||||
{ |
||||
spin_lock(&c->space_lock); |
||||
memcpy(lst, &c->lst, sizeof(struct ubifs_lp_stats)); |
||||
spin_unlock(&c->space_lock); |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_change_one_lp - change LEB properties. |
||||
* @c: the UBIFS file-system description object |
||||
* @lnum: LEB to change properties for |
||||
* @free: amount of free space |
||||
* @dirty: amount of dirty space |
||||
* @flags_set: flags to set |
||||
* @flags_clean: flags to clean |
||||
* @idx_gc_cnt: change to the count of idx_gc list |
||||
* |
||||
* This function changes properties of LEB @lnum. It is a helper wrapper over |
||||
* 'ubifs_change_lp()' which hides lprops get/release. The arguments are the |
||||
* same as in case of 'ubifs_change_lp()'. Returns zero in case of success and |
||||
* a negative error code in case of failure. |
||||
*/ |
||||
int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, |
||||
int flags_set, int flags_clean, int idx_gc_cnt) |
||||
{ |
||||
int err = 0, flags; |
||||
const struct ubifs_lprops *lp; |
||||
|
||||
ubifs_get_lprops(c); |
||||
|
||||
lp = ubifs_lpt_lookup_dirty(c, lnum); |
||||
if (IS_ERR(lp)) { |
||||
err = PTR_ERR(lp); |
||||
goto out; |
||||
} |
||||
|
||||
flags = (lp->flags | flags_set) & ~flags_clean; |
||||
lp = ubifs_change_lp(c, lp, free, dirty, flags, idx_gc_cnt); |
||||
if (IS_ERR(lp)) |
||||
err = PTR_ERR(lp); |
||||
|
||||
out: |
||||
ubifs_release_lprops(c); |
||||
return err; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_update_one_lp - update LEB properties. |
||||
* @c: the UBIFS file-system description object |
||||
* @lnum: LEB to change properties for |
||||
* @free: amount of free space |
||||
* @dirty: amount of dirty space to add |
||||
* @flags_set: flags to set |
||||
* @flags_clean: flags to clean |
||||
* |
||||
* This function is the same as 'ubifs_change_one_lp()' but @dirty is added to |
||||
* current dirty space, not substitutes it. |
||||
*/ |
||||
int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, |
||||
int flags_set, int flags_clean) |
||||
{ |
||||
int err = 0, flags; |
||||
const struct ubifs_lprops *lp; |
||||
|
||||
ubifs_get_lprops(c); |
||||
|
||||
lp = ubifs_lpt_lookup_dirty(c, lnum); |
||||
if (IS_ERR(lp)) { |
||||
err = PTR_ERR(lp); |
||||
goto out; |
||||
} |
||||
|
||||
flags = (lp->flags | flags_set) & ~flags_clean; |
||||
lp = ubifs_change_lp(c, lp, free, lp->dirty + dirty, flags, 0); |
||||
if (IS_ERR(lp)) |
||||
err = PTR_ERR(lp); |
||||
|
||||
out: |
||||
ubifs_release_lprops(c); |
||||
return err; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_read_one_lp - read LEB properties. |
||||
* @c: the UBIFS file-system description object |
||||
* @lnum: LEB to read properties for |
||||
* @lp: where to store read properties |
||||
* |
||||
* This helper function reads properties of a LEB @lnum and stores them in @lp. |
||||
* Returns zero in case of success and a negative error code in case of |
||||
* failure. |
||||
*/ |
||||
int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp) |
||||
{ |
||||
int err = 0; |
||||
const struct ubifs_lprops *lpp; |
||||
|
||||
ubifs_get_lprops(c); |
||||
|
||||
lpp = ubifs_lpt_lookup(c, lnum); |
||||
if (IS_ERR(lpp)) { |
||||
err = PTR_ERR(lpp); |
||||
goto out; |
||||
} |
||||
|
||||
memcpy(lp, lpp, sizeof(struct ubifs_lprops)); |
||||
|
||||
out: |
||||
ubifs_release_lprops(c); |
||||
return err; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_fast_find_free - try to find a LEB with free space quickly. |
||||
* @c: the UBIFS file-system description object |
||||
* |
||||
* This function returns LEB properties for a LEB with free space or %NULL if |
||||
* the function is unable to find a LEB quickly. |
||||
*/ |
||||
const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c) |
||||
{ |
||||
struct ubifs_lprops *lprops; |
||||
struct ubifs_lpt_heap *heap; |
||||
|
||||
ubifs_assert(mutex_is_locked(&c->lp_mutex)); |
||||
|
||||
heap = &c->lpt_heap[LPROPS_FREE - 1]; |
||||
if (heap->cnt == 0) |
||||
return NULL; |
||||
|
||||
lprops = heap->arr[0]; |
||||
ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); |
||||
ubifs_assert(!(lprops->flags & LPROPS_INDEX)); |
||||
return lprops; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_fast_find_empty - try to find an empty LEB quickly. |
||||
* @c: the UBIFS file-system description object |
||||
* |
||||
* This function returns LEB properties for an empty LEB or %NULL if the |
||||
* function is unable to find an empty LEB quickly. |
||||
*/ |
||||
const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c) |
||||
{ |
||||
struct ubifs_lprops *lprops; |
||||
|
||||
ubifs_assert(mutex_is_locked(&c->lp_mutex)); |
||||
|
||||
if (list_empty(&c->empty_list)) |
||||
return NULL; |
||||
|
||||
lprops = list_entry(c->empty_list.next, struct ubifs_lprops, list); |
||||
ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); |
||||
ubifs_assert(!(lprops->flags & LPROPS_INDEX)); |
||||
ubifs_assert(lprops->free == c->leb_size); |
||||
return lprops; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_fast_find_freeable - try to find a freeable LEB quickly. |
||||
* @c: the UBIFS file-system description object |
||||
* |
||||
* This function returns LEB properties for a freeable LEB or %NULL if the |
||||
* function is unable to find a freeable LEB quickly. |
||||
*/ |
||||
const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c) |
||||
{ |
||||
struct ubifs_lprops *lprops; |
||||
|
||||
ubifs_assert(mutex_is_locked(&c->lp_mutex)); |
||||
|
||||
if (list_empty(&c->freeable_list)) |
||||
return NULL; |
||||
|
||||
lprops = list_entry(c->freeable_list.next, struct ubifs_lprops, list); |
||||
ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); |
||||
ubifs_assert(!(lprops->flags & LPROPS_INDEX)); |
||||
ubifs_assert(lprops->free + lprops->dirty == c->leb_size); |
||||
ubifs_assert(c->freeable_cnt > 0); |
||||
return lprops; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_fast_find_frdi_idx - try to find a freeable index LEB quickly. |
||||
* @c: the UBIFS file-system description object |
||||
* |
||||
* This function returns LEB properties for a freeable index LEB or %NULL if the |
||||
* function is unable to find a freeable index LEB quickly. |
||||
*/ |
||||
const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c) |
||||
{ |
||||
struct ubifs_lprops *lprops; |
||||
|
||||
ubifs_assert(mutex_is_locked(&c->lp_mutex)); |
||||
|
||||
if (list_empty(&c->frdi_idx_list)) |
||||
return NULL; |
||||
|
||||
lprops = list_entry(c->frdi_idx_list.next, struct ubifs_lprops, list); |
||||
ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); |
||||
ubifs_assert((lprops->flags & LPROPS_INDEX)); |
||||
ubifs_assert(lprops->free + lprops->dirty == c->leb_size); |
||||
return lprops; |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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); |
||||
} |
@ -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; |
||||
} |
@ -0,0 +1,310 @@ |
||||
/*
|
||||
* 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) |
||||
{ |
||||
return (void *)((struct ubifs_branch *)idx->branches)->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__ */ |
@ -0,0 +1,316 @@ |
||||
/*
|
||||
* This file is part of UBIFS. |
||||
* |
||||
* Copyright (C) 2006-2008 Nokia Corporation. |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License version 2 as published by |
||||
* the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, but WITHOUT |
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
||||
* more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with |
||||
* this program; if not, write to the Free Software Foundation, Inc., 51 |
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
* |
||||
* Author: Adrian Hunter |
||||
*/ |
||||
|
||||
#include "ubifs.h" |
||||
|
||||
/*
|
||||
* An orphan is an inode number whose inode node has been committed to the index |
||||
* with a link count of zero. That happens when an open file is deleted |
||||
* (unlinked) and then a commit is run. In the normal course of events the inode |
||||
* would be deleted when the file is closed. However in the case of an unclean |
||||
* unmount, orphans need to be accounted for. After an unclean unmount, the |
||||
* orphans' inodes must be deleted which means either scanning the entire index |
||||
* looking for them, or keeping a list on flash somewhere. This unit implements |
||||
* the latter approach. |
||||
* |
||||
* The orphan area is a fixed number of LEBs situated between the LPT area and |
||||
* the main area. The number of orphan area LEBs is specified when the file |
||||
* system is created. The minimum number is 1. The size of the orphan area |
||||
* should be so that it can hold the maximum number of orphans that are expected |
||||
* to ever exist at one time. |
||||
* |
||||
* The number of orphans that can fit in a LEB is: |
||||
* |
||||
* (c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64) |
||||
* |
||||
* For example: a 15872 byte LEB can fit 1980 orphans so 1 LEB may be enough. |
||||
* |
||||
* Orphans are accumulated in a rb-tree. When an inode's link count drops to |
||||
* zero, the inode number is added to the rb-tree. It is removed from the tree |
||||
* when the inode is deleted. Any new orphans that are in the orphan tree when |
||||
* the commit is run, are written to the orphan area in 1 or more orphan nodes. |
||||
* If the orphan area is full, it is consolidated to make space. There is |
||||
* always enough space because validation prevents the user from creating more |
||||
* than the maximum number of orphans allowed. |
||||
*/ |
||||
|
||||
/**
|
||||
* tot_avail_orphs - calculate total space. |
||||
* @c: UBIFS file-system description object |
||||
* |
||||
* This function returns the number of orphans that can be written in half |
||||
* the total space. That leaves half the space for adding new orphans. |
||||
*/ |
||||
static int tot_avail_orphs(struct ubifs_info *c) |
||||
{ |
||||
int avail_lebs, avail; |
||||
|
||||
avail_lebs = c->orph_lebs; |
||||
avail = avail_lebs * |
||||
((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64)); |
||||
return avail / 2; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_clear_orphans - erase all LEBs used for orphans. |
||||
* @c: UBIFS file-system description object |
||||
* |
||||
* If recovery is not required, then the orphans from the previous session |
||||
* are not needed. This function locates the LEBs used to record |
||||
* orphans, and un-maps them. |
||||
*/ |
||||
int ubifs_clear_orphans(struct ubifs_info *c) |
||||
{ |
||||
int lnum, err; |
||||
|
||||
for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) { |
||||
err = ubifs_leb_unmap(c, lnum); |
||||
if (err) |
||||
return err; |
||||
} |
||||
c->ohead_lnum = c->orph_first; |
||||
c->ohead_offs = 0; |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* insert_dead_orphan - insert an orphan. |
||||
* @c: UBIFS file-system description object |
||||
* @inum: orphan inode number |
||||
* |
||||
* This function is a helper to the 'do_kill_orphans()' function. The orphan |
||||
* must be kept until the next commit, so it is added to the rb-tree and the |
||||
* deletion list. |
||||
*/ |
||||
static int insert_dead_orphan(struct ubifs_info *c, ino_t inum) |
||||
{ |
||||
struct ubifs_orphan *orphan, *o; |
||||
struct rb_node **p, *parent = NULL; |
||||
|
||||
orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_KERNEL); |
||||
if (!orphan) |
||||
return -ENOMEM; |
||||
orphan->inum = inum; |
||||
|
||||
p = &c->orph_tree.rb_node; |
||||
while (*p) { |
||||
parent = *p; |
||||
o = rb_entry(parent, struct ubifs_orphan, rb); |
||||
if (inum < o->inum) |
||||
p = &(*p)->rb_left; |
||||
else if (inum > o->inum) |
||||
p = &(*p)->rb_right; |
||||
else { |
||||
/* Already added - no problem */ |
||||
kfree(orphan); |
||||
return 0; |
||||
} |
||||
} |
||||
c->tot_orphans += 1; |
||||
rb_link_node(&orphan->rb, parent, p); |
||||
rb_insert_color(&orphan->rb, &c->orph_tree); |
||||
list_add_tail(&orphan->list, &c->orph_list); |
||||
orphan->dnext = c->orph_dnext; |
||||
c->orph_dnext = orphan; |
||||
dbg_mnt("ino %lu, new %d, tot %d", (unsigned long)inum, |
||||
c->new_orphans, c->tot_orphans); |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* do_kill_orphans - remove orphan inodes from the index. |
||||
* @c: UBIFS file-system description object |
||||
* @sleb: scanned LEB |
||||
* @last_cmt_no: cmt_no of last orphan node read is passed and returned here |
||||
* @outofdate: whether the LEB is out of date is returned here |
||||
* @last_flagged: whether the end orphan node is encountered |
||||
* |
||||
* This function is a helper to the 'kill_orphans()' function. It goes through |
||||
* every orphan node in a LEB and for every inode number recorded, removes |
||||
* all keys for that inode from the TNC. |
||||
*/ |
||||
static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, |
||||
unsigned long long *last_cmt_no, int *outofdate, |
||||
int *last_flagged) |
||||
{ |
||||
struct ubifs_scan_node *snod; |
||||
struct ubifs_orph_node *orph; |
||||
unsigned long long cmt_no; |
||||
ino_t inum; |
||||
int i, n, err, first = 1; |
||||
|
||||
list_for_each_entry(snod, &sleb->nodes, list) { |
||||
if (snod->type != UBIFS_ORPH_NODE) { |
||||
ubifs_err("invalid node type %d in orphan area at " |
||||
"%d:%d", snod->type, sleb->lnum, snod->offs); |
||||
dbg_dump_node(c, snod->node); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
orph = snod->node; |
||||
|
||||
/* Check commit number */ |
||||
cmt_no = le64_to_cpu(orph->cmt_no) & LLONG_MAX; |
||||
/*
|
||||
* The commit number on the master node may be less, because |
||||
* of a failed commit. If there are several failed commits in a |
||||
* row, the commit number written on orphan nodes will continue |
||||
* to increase (because the commit number is adjusted here) even |
||||
* though the commit number on the master node stays the same |
||||
* because the master node has not been re-written. |
||||
*/ |
||||
if (cmt_no > c->cmt_no) |
||||
c->cmt_no = cmt_no; |
||||
if (cmt_no < *last_cmt_no && *last_flagged) { |
||||
/*
|
||||
* The last orphan node had a higher commit number and |
||||
* was flagged as the last written for that commit |
||||
* number. That makes this orphan node, out of date. |
||||
*/ |
||||
if (!first) { |
||||
ubifs_err("out of order commit number %llu in " |
||||
"orphan node at %d:%d", |
||||
cmt_no, sleb->lnum, snod->offs); |
||||
dbg_dump_node(c, snod->node); |
||||
return -EINVAL; |
||||
} |
||||
dbg_rcvry("out of date LEB %d", sleb->lnum); |
||||
*outofdate = 1; |
||||
return 0; |
||||
} |
||||
|
||||
if (first) |
||||
first = 0; |
||||
|
||||
n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3; |
||||
for (i = 0; i < n; i++) { |
||||
inum = le64_to_cpu(orph->inos[i]); |
||||
dbg_rcvry("deleting orphaned inode %lu", |
||||
(unsigned long)inum); |
||||
err = ubifs_tnc_remove_ino(c, inum); |
||||
if (err) |
||||
return err; |
||||
err = insert_dead_orphan(c, inum); |
||||
if (err) |
||||
return err; |
||||
} |
||||
|
||||
*last_cmt_no = cmt_no; |
||||
if (le64_to_cpu(orph->cmt_no) & (1ULL << 63)) { |
||||
dbg_rcvry("last orph node for commit %llu at %d:%d", |
||||
cmt_no, sleb->lnum, snod->offs); |
||||
*last_flagged = 1; |
||||
} else |
||||
*last_flagged = 0; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* kill_orphans - remove all orphan inodes from the index. |
||||
* @c: UBIFS file-system description object |
||||
* |
||||
* If recovery is required, then orphan inodes recorded during the previous |
||||
* session (which ended with an unclean unmount) must be deleted from the index. |
||||
* This is done by updating the TNC, but since the index is not updated until |
||||
* the next commit, the LEBs where the orphan information is recorded are not |
||||
* erased until the next commit. |
||||
*/ |
||||
static int kill_orphans(struct ubifs_info *c) |
||||
{ |
||||
unsigned long long last_cmt_no = 0; |
||||
int lnum, err = 0, outofdate = 0, last_flagged = 0; |
||||
|
||||
c->ohead_lnum = c->orph_first; |
||||
c->ohead_offs = 0; |
||||
/* Check no-orphans flag and skip this if no orphans */ |
||||
if (c->no_orphs) { |
||||
dbg_rcvry("no orphans"); |
||||
return 0; |
||||
} |
||||
/*
|
||||
* Orph nodes always start at c->orph_first and are written to each |
||||
* successive LEB in turn. Generally unused LEBs will have been unmapped |
||||
* but may contain out of date orphan nodes if the unmap didn't go |
||||
* through. In addition, the last orphan node written for each commit is |
||||
* marked (top bit of orph->cmt_no is set to 1). It is possible that |
||||
* there are orphan nodes from the next commit (i.e. the commit did not |
||||
* complete successfully). In that case, no orphans will have been lost |
||||
* due to the way that orphans are written, and any orphans added will |
||||
* be valid orphans anyway and so can be deleted. |
||||
*/ |
||||
for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) { |
||||
struct ubifs_scan_leb *sleb; |
||||
|
||||
dbg_rcvry("LEB %d", lnum); |
||||
sleb = ubifs_scan(c, lnum, 0, c->sbuf); |
||||
if (IS_ERR(sleb)) { |
||||
sleb = ubifs_recover_leb(c, lnum, 0, c->sbuf, 0); |
||||
if (IS_ERR(sleb)) { |
||||
err = PTR_ERR(sleb); |
||||
break; |
||||
} |
||||
} |
||||
err = do_kill_orphans(c, sleb, &last_cmt_no, &outofdate, |
||||
&last_flagged); |
||||
if (err || outofdate) { |
||||
ubifs_scan_destroy(sleb); |
||||
break; |
||||
} |
||||
if (sleb->endpt) { |
||||
c->ohead_lnum = lnum; |
||||
c->ohead_offs = sleb->endpt; |
||||
} |
||||
ubifs_scan_destroy(sleb); |
||||
} |
||||
return err; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_mount_orphans - delete orphan inodes and erase LEBs that recorded them. |
||||
* @c: UBIFS file-system description object |
||||
* @unclean: indicates recovery from unclean unmount |
||||
* @read_only: indicates read only mount |
||||
* |
||||
* This function is called when mounting to erase orphans from the previous |
||||
* session. If UBIFS was not unmounted cleanly, then the inodes recorded as |
||||
* orphans are deleted. |
||||
*/ |
||||
int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only) |
||||
{ |
||||
int err = 0; |
||||
|
||||
c->max_orphans = tot_avail_orphs(c); |
||||
|
||||
if (!read_only) { |
||||
c->orph_buf = vmalloc(c->leb_size); |
||||
if (!c->orph_buf) |
||||
return -ENOMEM; |
||||
} |
||||
|
||||
if (unclean) |
||||
err = kill_orphans(c); |
||||
else if (!read_only) |
||||
err = ubifs_clear_orphans(c); |
||||
|
||||
return err; |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,324 @@ |
||||
/*
|
||||
* 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); |
||||
|
||||
/*
|
||||
* The software supports all previous versions but not future versions, |
||||
* due to the unavailability of time-travelling equipment. |
||||
*/ |
||||
c->fmt_version = le32_to_cpu(sup->fmt_version); |
||||
if (c->fmt_version > UBIFS_FORMAT_VERSION) { |
||||
ubifs_err("on-flash format version is %d, but software only " |
||||
"supports up to version %d", c->fmt_version, |
||||
UBIFS_FORMAT_VERSION); |
||||
err = -EINVAL; |
||||
goto out; |
||||
} |
||||
|
||||
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; |
||||
} |
@ -0,0 +1,362 @@ |
||||
/*
|
||||
* This file is part of UBIFS. |
||||
* |
||||
* Copyright (C) 2006-2008 Nokia Corporation |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License version 2 as published by |
||||
* the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, but WITHOUT |
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
||||
* more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with |
||||
* this program; if not, write to the Free Software Foundation, Inc., 51 |
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
* |
||||
* Authors: Adrian Hunter |
||||
* Artem Bityutskiy (Битюцкий Артём) |
||||
*/ |
||||
|
||||
/*
|
||||
* This file implements the scan which is a general-purpose function for |
||||
* determining what nodes are in an eraseblock. The scan is used to replay the |
||||
* journal, to do garbage collection. for the TNC in-the-gaps method, and by |
||||
* debugging functions. |
||||
*/ |
||||
|
||||
#include "ubifs.h" |
||||
|
||||
/**
|
||||
* scan_padding_bytes - scan for padding bytes. |
||||
* @buf: buffer to scan |
||||
* @len: length of buffer |
||||
* |
||||
* This function returns the number of padding bytes on success and |
||||
* %SCANNED_GARBAGE on failure. |
||||
*/ |
||||
static int scan_padding_bytes(void *buf, int len) |
||||
{ |
||||
int pad_len = 0, max_pad_len = min_t(int, UBIFS_PAD_NODE_SZ, len); |
||||
uint8_t *p = buf; |
||||
|
||||
dbg_scan("not a node"); |
||||
|
||||
while (pad_len < max_pad_len && *p++ == UBIFS_PADDING_BYTE) |
||||
pad_len += 1; |
||||
|
||||
if (!pad_len || (pad_len & 7)) |
||||
return SCANNED_GARBAGE; |
||||
|
||||
dbg_scan("%d padding bytes", pad_len); |
||||
|
||||
return pad_len; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_scan_a_node - scan for a node or padding. |
||||
* @c: UBIFS file-system description object |
||||
* @buf: buffer to scan |
||||
* @len: length of buffer |
||||
* @lnum: logical eraseblock number |
||||
* @offs: offset within the logical eraseblock |
||||
* @quiet: print no messages |
||||
* |
||||
* This function returns a scanning code to indicate what was scanned. |
||||
*/ |
||||
int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, |
||||
int offs, int quiet) |
||||
{ |
||||
struct ubifs_ch *ch = buf; |
||||
uint32_t magic; |
||||
|
||||
magic = le32_to_cpu(ch->magic); |
||||
|
||||
if (magic == 0xFFFFFFFF) { |
||||
dbg_scan("hit empty space"); |
||||
return SCANNED_EMPTY_SPACE; |
||||
} |
||||
|
||||
if (magic != UBIFS_NODE_MAGIC) |
||||
return scan_padding_bytes(buf, len); |
||||
|
||||
if (len < UBIFS_CH_SZ) |
||||
return SCANNED_GARBAGE; |
||||
|
||||
dbg_scan("scanning %s", dbg_ntype(ch->node_type)); |
||||
|
||||
if (ubifs_check_node(c, buf, lnum, offs, quiet, 1)) |
||||
return SCANNED_A_CORRUPT_NODE; |
||||
|
||||
if (ch->node_type == UBIFS_PAD_NODE) { |
||||
struct ubifs_pad_node *pad = buf; |
||||
int pad_len = le32_to_cpu(pad->pad_len); |
||||
int node_len = le32_to_cpu(ch->len); |
||||
|
||||
/* Validate the padding node */ |
||||
if (pad_len < 0 || |
||||
offs + node_len + pad_len > c->leb_size) { |
||||
if (!quiet) { |
||||
ubifs_err("bad pad node at LEB %d:%d", |
||||
lnum, offs); |
||||
dbg_dump_node(c, pad); |
||||
} |
||||
return SCANNED_A_BAD_PAD_NODE; |
||||
} |
||||
|
||||
/* Make the node pads to 8-byte boundary */ |
||||
if ((node_len + pad_len) & 7) { |
||||
if (!quiet) { |
||||
dbg_err("bad padding length %d - %d", |
||||
offs, offs + node_len + pad_len); |
||||
} |
||||
return SCANNED_A_BAD_PAD_NODE; |
||||
} |
||||
|
||||
dbg_scan("%d bytes padded, offset now %d", |
||||
pad_len, ALIGN(offs + node_len + pad_len, 8)); |
||||
|
||||
return node_len + pad_len; |
||||
} |
||||
|
||||
return SCANNED_A_NODE; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_start_scan - create LEB scanning information at start of scan. |
||||
* @c: UBIFS file-system description object |
||||
* @lnum: logical eraseblock number |
||||
* @offs: offset to start at (usually zero) |
||||
* @sbuf: scan buffer (must be c->leb_size) |
||||
* |
||||
* This function returns %0 on success and a negative error code on failure. |
||||
*/ |
||||
struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum, |
||||
int offs, void *sbuf) |
||||
{ |
||||
struct ubifs_scan_leb *sleb; |
||||
int err; |
||||
|
||||
dbg_scan("scan LEB %d:%d", lnum, offs); |
||||
|
||||
sleb = kzalloc(sizeof(struct ubifs_scan_leb), GFP_NOFS); |
||||
if (!sleb) |
||||
return ERR_PTR(-ENOMEM); |
||||
|
||||
sleb->lnum = lnum; |
||||
INIT_LIST_HEAD(&sleb->nodes); |
||||
sleb->buf = sbuf; |
||||
|
||||
err = ubi_read(c->ubi, lnum, sbuf + offs, offs, c->leb_size - offs); |
||||
if (err && err != -EBADMSG) { |
||||
ubifs_err("cannot read %d bytes from LEB %d:%d," |
||||
" error %d", c->leb_size - offs, lnum, offs, err); |
||||
kfree(sleb); |
||||
return ERR_PTR(err); |
||||
} |
||||
|
||||
if (err == -EBADMSG) |
||||
sleb->ecc = 1; |
||||
|
||||
return sleb; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_end_scan - update LEB scanning information at end of scan. |
||||
* @c: UBIFS file-system description object |
||||
* @sleb: scanning information |
||||
* @lnum: logical eraseblock number |
||||
* @offs: offset to start at (usually zero) |
||||
* |
||||
* This function returns %0 on success and a negative error code on failure. |
||||
*/ |
||||
void ubifs_end_scan(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, |
||||
int lnum, int offs) |
||||
{ |
||||
lnum = lnum; |
||||
dbg_scan("stop scanning LEB %d at offset %d", lnum, offs); |
||||
ubifs_assert(offs % c->min_io_size == 0); |
||||
|
||||
sleb->endpt = ALIGN(offs, c->min_io_size); |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_add_snod - add a scanned node to LEB scanning information. |
||||
* @c: UBIFS file-system description object |
||||
* @sleb: scanning information |
||||
* @buf: buffer containing node |
||||
* @offs: offset of node on flash |
||||
* |
||||
* This function returns %0 on success and a negative error code on failure. |
||||
*/ |
||||
int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, |
||||
void *buf, int offs) |
||||
{ |
||||
struct ubifs_ch *ch = buf; |
||||
struct ubifs_ino_node *ino = buf; |
||||
struct ubifs_scan_node *snod; |
||||
|
||||
snod = kzalloc(sizeof(struct ubifs_scan_node), GFP_NOFS); |
||||
if (!snod) |
||||
return -ENOMEM; |
||||
|
||||
snod->sqnum = le64_to_cpu(ch->sqnum); |
||||
snod->type = ch->node_type; |
||||
snod->offs = offs; |
||||
snod->len = le32_to_cpu(ch->len); |
||||
snod->node = buf; |
||||
|
||||
switch (ch->node_type) { |
||||
case UBIFS_INO_NODE: |
||||
case UBIFS_DENT_NODE: |
||||
case UBIFS_XENT_NODE: |
||||
case UBIFS_DATA_NODE: |
||||
case UBIFS_TRUN_NODE: |
||||
/*
|
||||
* The key is in the same place in all keyed |
||||
* nodes. |
||||
*/ |
||||
key_read(c, &ino->key, &snod->key); |
||||
break; |
||||
} |
||||
list_add_tail(&snod->list, &sleb->nodes); |
||||
sleb->nodes_cnt += 1; |
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_scanned_corruption - print information after UBIFS scanned corruption. |
||||
* @c: UBIFS file-system description object |
||||
* @lnum: LEB number of corruption |
||||
* @offs: offset of corruption |
||||
* @buf: buffer containing corruption |
||||
*/ |
||||
void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs, |
||||
void *buf) |
||||
{ |
||||
int len; |
||||
|
||||
ubifs_err("corrupted data at LEB %d:%d", lnum, offs); |
||||
if (dbg_failure_mode) |
||||
return; |
||||
len = c->leb_size - offs; |
||||
if (len > 4096) |
||||
len = 4096; |
||||
dbg_err("first %d bytes from LEB %d:%d", len, lnum, offs); |
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1); |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_scan - scan a logical eraseblock. |
||||
* @c: UBIFS file-system description object |
||||
* @lnum: logical eraseblock number |
||||
* @offs: offset to start at (usually zero) |
||||
* @sbuf: scan buffer (must be c->leb_size) |
||||
* |
||||
* This function scans LEB number @lnum and returns complete information about |
||||
* its contents. Returns an error code in case of failure. |
||||
*/ |
||||
struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, |
||||
int offs, void *sbuf) |
||||
{ |
||||
void *buf = sbuf + offs; |
||||
int err, len = c->leb_size - offs; |
||||
struct ubifs_scan_leb *sleb; |
||||
|
||||
sleb = ubifs_start_scan(c, lnum, offs, sbuf); |
||||
if (IS_ERR(sleb)) |
||||
return sleb; |
||||
|
||||
while (len >= 8) { |
||||
struct ubifs_ch *ch = buf; |
||||
int node_len, ret; |
||||
|
||||
dbg_scan("look at LEB %d:%d (%d bytes left)", |
||||
lnum, offs, len); |
||||
|
||||
cond_resched(); |
||||
|
||||
ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 0); |
||||
|
||||
if (ret > 0) { |
||||
/* Padding bytes or a valid padding node */ |
||||
offs += ret; |
||||
buf += ret; |
||||
len -= ret; |
||||
continue; |
||||
} |
||||
|
||||
if (ret == SCANNED_EMPTY_SPACE) |
||||
/* Empty space is checked later */ |
||||
break; |
||||
|
||||
switch (ret) { |
||||
case SCANNED_GARBAGE: |
||||
dbg_err("garbage"); |
||||
goto corrupted; |
||||
case SCANNED_A_NODE: |
||||
break; |
||||
case SCANNED_A_CORRUPT_NODE: |
||||
case SCANNED_A_BAD_PAD_NODE: |
||||
dbg_err("bad node"); |
||||
goto corrupted; |
||||
default: |
||||
dbg_err("unknown"); |
||||
goto corrupted; |
||||
} |
||||
|
||||
err = ubifs_add_snod(c, sleb, buf, offs); |
||||
if (err) |
||||
goto error; |
||||
|
||||
node_len = ALIGN(le32_to_cpu(ch->len), 8); |
||||
offs += node_len; |
||||
buf += node_len; |
||||
len -= node_len; |
||||
} |
||||
|
||||
if (offs % c->min_io_size) |
||||
goto corrupted; |
||||
|
||||
ubifs_end_scan(c, sleb, lnum, offs); |
||||
|
||||
for (; len > 4; offs += 4, buf = buf + 4, len -= 4) |
||||
if (*(uint32_t *)buf != 0xffffffff) |
||||
break; |
||||
for (; len; offs++, buf++, len--) |
||||
if (*(uint8_t *)buf != 0xff) { |
||||
ubifs_err("corrupt empty space at LEB %d:%d", |
||||
lnum, offs); |
||||
goto corrupted; |
||||
} |
||||
|
||||
return sleb; |
||||
|
||||
corrupted: |
||||
ubifs_scanned_corruption(c, lnum, offs, buf); |
||||
err = -EUCLEAN; |
||||
error: |
||||
ubifs_err("LEB %d scanning failed", lnum); |
||||
ubifs_scan_destroy(sleb); |
||||
return ERR_PTR(err); |
||||
} |
||||
|
||||
/**
|
||||
* ubifs_scan_destroy - destroy LEB scanning information. |
||||
* @sleb: scanning information to free |
||||
*/ |
||||
void ubifs_scan_destroy(struct ubifs_scan_leb *sleb) |
||||
{ |
||||
struct ubifs_scan_node *node; |
||||
struct list_head *head; |
||||
|
||||
head = &sleb->nodes; |
||||
while (!list_empty(head)) { |
||||
node = list_entry(head->next, struct ubifs_scan_node, list); |
||||
list_del(&node->list); |
||||
kfree(node); |
||||
} |
||||
kfree(sleb); |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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,751 @@ |
||||
/*
|
||||
* 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 */ |
||||
#define UBIFS_FORMAT_VERSION 4 |
||||
|
||||
/* 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 preferes 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 |
||||
*/ |
||||
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]; |
||||
__u8 padding2[3972]; |
||||
} __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__ */ |
@ -0,0 +1,684 @@ |
||||
/*
|
||||
* This file is part of UBIFS. |
||||
* |
||||
* Copyright (C) 2006-2008 Nokia Corporation. |
||||
* |
||||
* (C) Copyright 2008-2009 |
||||
* 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" |
||||
|
||||
#if !defined(CONFIG_SYS_64BIT_VSPRINTF) |
||||
#warning Please define CONFIG_SYS_64BIT_VSPRINTF for correct output! |
||||
#endif |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
/* compress.c */ |
||||
|
||||
/*
|
||||
* We need a wrapper for gunzip() 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 gunzip(out, *out_len, (unsigned char *)in, &len); |
||||
} |
||||
|
||||
/* 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; |
||||
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; |
||||
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; |
||||
|
||||
ubifs_compressors[UBIFS_COMPR_NONE] = &none_compr; |
||||
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) |
||||
free(file); |
||||
if (dentry) |
||||
free(dentry); |
||||
if (dir) |
||||
free(dir); |
||||
|
||||
if (file->private_data) |
||||
kfree(file->private_data); |
||||
file->private_data = NULL; |
||||
file->f_pos = 2; |
||||
return 0; |
||||
} |
||||
|
||||
static unsigned long ubifs_findfile(struct super_block *sb, char *filename) |
||||
{ |
||||
int ret; |
||||
char *next; |
||||
char fpath[128]; |
||||
char *name = fpath; |
||||
unsigned long root_inum = 1; |
||||
unsigned long inum; |
||||
|
||||
strcpy(fpath, filename); |
||||
|
||||
/* Remove all leading slashes */ |
||||
while (*name == '/') |
||||
name++; |
||||
|
||||
/*
|
||||
* Handle root-direcoty ('/') |
||||
*/ |
||||
inum = root_inum; |
||||
if (!name || *name == '\0') |
||||
return inum; |
||||
|
||||
for (;;) { |
||||
/* 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); |
||||
|
||||
/*
|
||||
* Check if directory with this name exists |
||||
*/ |
||||
|
||||
/* Found the node! */ |
||||
if (!next || *next == '\0') { |
||||
if (ret) |
||||
return inum; |
||||
|
||||
break; |
||||
} |
||||
|
||||
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) |
||||
{ |
||||
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) { |
||||
err = -ENOMEM; |
||||
goto error; |
||||
} |
||||
|
||||
i = 0; |
||||
while (1) { |
||||
int ret; |
||||
|
||||
if (block >= beyond) { |
||||
/* Reading beyond inode */ |
||||
err = -ENOENT; |
||||
memset(addr, 0, UBIFS_BLOCK_SIZE); |
||||
} else { |
||||
ret = read_block(inode, addr, block, dn); |
||||
if (ret) { |
||||
err = ret; |
||||
if (err != -ENOENT) |
||||
break; |
||||
} else if (block + 1 == beyond) { |
||||
int dlen = le32_to_cpu(dn->size); |
||||
int ilen = i_size & (UBIFS_BLOCK_SIZE - 1); |
||||
|
||||
if (ilen && ilen < dlen) |
||||
memset(addr + ilen, 0, dlen - ilen); |
||||
} |
||||
} |
||||
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; |
||||
char link_name[64]; |
||||
struct ubifs_inode *ui; |
||||
|
||||
c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY); |
||||
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; |
||||
} |
||||
|
||||
/*
|
||||
* Check for symbolic link |
||||
*/ |
||||
ui = ubifs_inode(inode); |
||||
if (((inode->i_mode & S_IFMT) == S_IFLNK) && ui->data_len) { |
||||
memcpy(link_name, ui->data, ui->data_len); |
||||
printf("%s is linked to %s!\n", filename, link_name); |
||||
ubifs_iput(inode); |
||||
|
||||
/*
|
||||
* Now we have the "real" filename, call ubifs_load() |
||||
* again (recursive call) to load this file instead |
||||
*/ |
||||
return ubifs_load(link_name, addr, size); |
||||
} |
||||
|
||||
/*
|
||||
* 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++) { |
||||
err = do_readpage(c, inode, &page); |
||||
if (err) |
||||
break; |
||||
|
||||
page.addr += PAGE_SIZE; |
||||
page.index++; |
||||
} |
||||
|
||||
if (err) |
||||
printf("Error reading file '%s'\n", filename); |
||||
else |
||||
printf("Done\n"); |
||||
|
||||
ubifs_iput(inode); |
||||
|
||||
out: |
||||
ubi_close_volume(c->ubi); |
||||
return err; |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,85 @@ |
||||
#ifndef _LINUX_MATH64_H |
||||
#define _LINUX_MATH64_H |
||||
|
||||
#include <linux/types.h> |
||||
|
||||
#if BITS_PER_LONG == 64 |
||||
|
||||
/**
|
||||
* div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder |
||||
* |
||||
* This is commonly provided by 32bit archs to provide an optimized 64bit |
||||
* divide. |
||||
*/ |
||||
static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) |
||||
{ |
||||
*remainder = dividend % divisor; |
||||
return dividend / divisor; |
||||
} |
||||
|
||||
/**
|
||||
* div_s64_rem - signed 64bit divide with 32bit divisor with remainder |
||||
*/ |
||||
static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) |
||||
{ |
||||
*remainder = dividend % divisor; |
||||
return dividend / divisor; |
||||
} |
||||
|
||||
/**
|
||||
* div64_u64 - unsigned 64bit divide with 64bit divisor |
||||
*/ |
||||
static inline u64 div64_u64(u64 dividend, u64 divisor) |
||||
{ |
||||
return dividend / divisor; |
||||
} |
||||
|
||||
#elif BITS_PER_LONG == 32 |
||||
|
||||
#ifndef div_u64_rem |
||||
static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) |
||||
{ |
||||
*remainder = do_div(dividend, divisor); |
||||
return dividend; |
||||
} |
||||
#endif |
||||
|
||||
#ifndef div_s64_rem |
||||
extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); |
||||
#endif |
||||
|
||||
#ifndef div64_u64 |
||||
extern u64 div64_u64(u64 dividend, u64 divisor); |
||||
#endif |
||||
|
||||
#endif /* BITS_PER_LONG */ |
||||
|
||||
/**
|
||||
* div_u64 - unsigned 64bit divide with 32bit divisor |
||||
* |
||||
* This is the most common 64bit divide and should be used if possible, |
||||
* as many 32bit archs can optimize this variant better than a full 64bit |
||||
* divide. |
||||
*/ |
||||
#ifndef div_u64 |
||||
static inline u64 div_u64(u64 dividend, u32 divisor) |
||||
{ |
||||
u32 remainder; |
||||
return div_u64_rem(dividend, divisor, &remainder); |
||||
} |
||||
#endif |
||||
|
||||
/**
|
||||
* div_s64 - signed 64bit divide with 32bit divisor |
||||
*/ |
||||
#ifndef div_s64 |
||||
static inline s64 div_s64(s64 dividend, s32 divisor) |
||||
{ |
||||
s32 remainder; |
||||
return div_s64_rem(dividend, divisor, &remainder); |
||||
} |
||||
#endif |
||||
|
||||
u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder); |
||||
|
||||
#endif /* _LINUX_MATH64_H */ |
Loading…
Reference in new issue