提交 1e51764a 编写于 作者: A Artem Bityutskiy

UBIFS: add new flash file system

This is a new flash file system. See
http://www.linux-mtd.infradead.org/doc/ubifs.htmlSigned-off-by: NArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: NAdrian Hunter <ext-adrian.hunter@nokia.com>
上级 e56a99d5
此差异已折叠。
/*
* 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 functions that manage the running of the commit process.
* Each affected module has its own functions to accomplish their part in the
* commit and those functions are called here.
*
* The commit is the process whereby all updates to the index and LEB properties
* are written out together and the journal becomes empty. This keeps the
* file system consistent - at all times the state can be recreated by reading
* the index and LEB properties and then replaying the journal.
*
* The commit is split into two parts named "commit start" and "commit end".
* During commit start, the commit process has exclusive access to the journal
* by holding the commit semaphore down for writing. As few I/O operations as
* possible are performed during commit start, instead the nodes that are to be
* written are merely identified. During commit end, the commit semaphore is no
* longer held and the journal is again in operation, allowing users to continue
* to use the file system while the bulk of the commit I/O is performed. The
* purpose of this two-step approach is to prevent the commit from causing any
* latency blips. Note that in any case, the commit does not prevent lookups
* (as permitted by the TNC mutex), or access to VFS data structures e.g. page
* cache.
*/
#include <linux/freezer.h>
#include <linux/kthread.h>
#include "ubifs.h"
/**
* do_commit - commit the journal.
* @c: UBIFS file-system description object
*
* This function implements UBIFS commit. It has to be called with commit lock
* locked. Returns zero in case of success and a negative error code in case of
* failure.
*/
static int do_commit(struct ubifs_info *c)
{
int err, new_ltail_lnum, old_ltail_lnum, i;
struct ubifs_zbranch zroot;
struct ubifs_lp_stats lst;
dbg_cmt("start");
if (c->ro_media) {
err = -EROFS;
goto out_up;
}
/* Sync all write buffers (necessary for recovery) */
for (i = 0; i < c->jhead_cnt; i++) {
err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
if (err)
goto out_up;
}
err = ubifs_gc_start_commit(c);
if (err)
goto out_up;
err = dbg_check_lprops(c);
if (err)
goto out_up;
err = ubifs_log_start_commit(c, &new_ltail_lnum);
if (err)
goto out_up;
err = ubifs_tnc_start_commit(c, &zroot);
if (err)
goto out_up;
err = ubifs_lpt_start_commit(c);
if (err)
goto out_up;
err = ubifs_orphan_start_commit(c);
if (err)
goto out_up;
ubifs_get_lp_stats(c, &lst);
up_write(&c->commit_sem);
err = ubifs_tnc_end_commit(c);
if (err)
goto out;
err = ubifs_lpt_end_commit(c);
if (err)
goto out;
err = ubifs_orphan_end_commit(c);
if (err)
goto out;
old_ltail_lnum = c->ltail_lnum;
err = ubifs_log_end_commit(c, new_ltail_lnum);
if (err)
goto out;
err = dbg_check_old_index(c, &zroot);
if (err)
goto out;
mutex_lock(&c->mst_mutex);
c->mst_node->cmt_no = cpu_to_le64(++c->cmt_no);
c->mst_node->log_lnum = cpu_to_le32(new_ltail_lnum);
c->mst_node->root_lnum = cpu_to_le32(zroot.lnum);
c->mst_node->root_offs = cpu_to_le32(zroot.offs);
c->mst_node->root_len = cpu_to_le32(zroot.len);
c->mst_node->ihead_lnum = cpu_to_le32(c->ihead_lnum);
c->mst_node->ihead_offs = cpu_to_le32(c->ihead_offs);
c->mst_node->index_size = cpu_to_le64(c->old_idx_sz);
c->mst_node->lpt_lnum = cpu_to_le32(c->lpt_lnum);
c->mst_node->lpt_offs = cpu_to_le32(c->lpt_offs);
c->mst_node->nhead_lnum = cpu_to_le32(c->nhead_lnum);
c->mst_node->nhead_offs = cpu_to_le32(c->nhead_offs);
c->mst_node->ltab_lnum = cpu_to_le32(c->ltab_lnum);
c->mst_node->ltab_offs = cpu_to_le32(c->ltab_offs);
c->mst_node->lsave_lnum = cpu_to_le32(c->lsave_lnum);
c->mst_node->lsave_offs = cpu_to_le32(c->lsave_offs);
c->mst_node->lscan_lnum = cpu_to_le32(c->lscan_lnum);
c->mst_node->empty_lebs = cpu_to_le32(lst.empty_lebs);
c->mst_node->idx_lebs = cpu_to_le32(lst.idx_lebs);
c->mst_node->total_free = cpu_to_le64(lst.total_free);
c->mst_node->total_dirty = cpu_to_le64(lst.total_dirty);
c->mst_node->total_used = cpu_to_le64(lst.total_used);
c->mst_node->total_dead = cpu_to_le64(lst.total_dead);
c->mst_node->total_dark = cpu_to_le64(lst.total_dark);
if (c->no_orphs)
c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
else
c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_NO_ORPHS);
err = ubifs_write_master(c);
mutex_unlock(&c->mst_mutex);
if (err)
goto out;
err = ubifs_log_post_commit(c, old_ltail_lnum);
if (err)
goto out;
err = ubifs_gc_end_commit(c);
if (err)
goto out;
err = ubifs_lpt_post_commit(c);
if (err)
goto out;
spin_lock(&c->cs_lock);
c->cmt_state = COMMIT_RESTING;
wake_up(&c->cmt_wq);
dbg_cmt("commit end");
spin_unlock(&c->cs_lock);
return 0;
out_up:
up_write(&c->commit_sem);
out:
ubifs_err("commit failed, error %d", err);
spin_lock(&c->cs_lock);
c->cmt_state = COMMIT_BROKEN;
wake_up(&c->cmt_wq);
spin_unlock(&c->cs_lock);
ubifs_ro_mode(c, err);
return err;
}
/**
* run_bg_commit - run background commit if it is needed.
* @c: UBIFS file-system description object
*
* This function runs background commit if it is needed. Returns zero in case
* of success and a negative error code in case of failure.
*/
static int run_bg_commit(struct ubifs_info *c)
{
spin_lock(&c->cs_lock);
/*
* Run background commit only if background commit was requested or if
* commit is required.
*/
if (c->cmt_state != COMMIT_BACKGROUND &&
c->cmt_state != COMMIT_REQUIRED)
goto out;
spin_unlock(&c->cs_lock);
down_write(&c->commit_sem);
spin_lock(&c->cs_lock);
if (c->cmt_state == COMMIT_REQUIRED)
c->cmt_state = COMMIT_RUNNING_REQUIRED;
else if (c->cmt_state == COMMIT_BACKGROUND)
c->cmt_state = COMMIT_RUNNING_BACKGROUND;
else
goto out_cmt_unlock;
spin_unlock(&c->cs_lock);
return do_commit(c);
out_cmt_unlock:
up_write(&c->commit_sem);
out:
spin_unlock(&c->cs_lock);
return 0;
}
/**
* ubifs_bg_thread - UBIFS background thread function.
* @info: points to the file-system description object
*
* This function implements various file-system background activities:
* o when a write-buffer timer expires it synchronizes the appropriate
* write-buffer;
* o when the journal is about to be full, it starts in-advance commit.
*
* Note, other stuff like background garbage collection may be added here in
* future.
*/
int ubifs_bg_thread(void *info)
{
int err;
struct ubifs_info *c = info;
ubifs_msg("background thread \"%s\" started, PID %d",
c->bgt_name, current->pid);
set_freezable();
while (1) {
if (kthread_should_stop())
break;
if (try_to_freeze())
continue;
set_current_state(TASK_INTERRUPTIBLE);
/* Check if there is something to do */
if (!c->need_bgt) {
/*
* Nothing prevents us from going sleep now and
* be never woken up and block the task which
* could wait in 'kthread_stop()' forever.
*/
if (kthread_should_stop())
break;
schedule();
continue;
} else
__set_current_state(TASK_RUNNING);
c->need_bgt = 0;
err = ubifs_bg_wbufs_sync(c);
if (err)
ubifs_ro_mode(c, err);
run_bg_commit(c);
cond_resched();
}
dbg_msg("background thread \"%s\" stops", c->bgt_name);
return 0;
}
/**
* ubifs_commit_required - set commit state to "required".
* @c: UBIFS file-system description object
*
* This function is called if a commit is required but cannot be done from the
* calling function, so it is just flagged instead.
*/
void ubifs_commit_required(struct ubifs_info *c)
{
spin_lock(&c->cs_lock);
switch (c->cmt_state) {
case COMMIT_RESTING:
case COMMIT_BACKGROUND:
dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state),
dbg_cstate(COMMIT_REQUIRED));
c->cmt_state = COMMIT_REQUIRED;
break;
case COMMIT_RUNNING_BACKGROUND:
dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state),
dbg_cstate(COMMIT_RUNNING_REQUIRED));
c->cmt_state = COMMIT_RUNNING_REQUIRED;
break;
case COMMIT_REQUIRED:
case COMMIT_RUNNING_REQUIRED:
case COMMIT_BROKEN:
break;
}
spin_unlock(&c->cs_lock);
}
/**
* ubifs_request_bg_commit - notify the background thread to do a commit.
* @c: UBIFS file-system description object
*
* This function is called if the journal is full enough to make a commit
* worthwhile, so background thread is kicked to start it.
*/
void ubifs_request_bg_commit(struct ubifs_info *c)
{
spin_lock(&c->cs_lock);
if (c->cmt_state == COMMIT_RESTING) {
dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state),
dbg_cstate(COMMIT_BACKGROUND));
c->cmt_state = COMMIT_BACKGROUND;
spin_unlock(&c->cs_lock);
ubifs_wake_up_bgt(c);
} else
spin_unlock(&c->cs_lock);
}
/**
* wait_for_commit - wait for commit.
* @c: UBIFS file-system description object
*
* This function sleeps until the commit operation is no longer running.
*/
static int wait_for_commit(struct ubifs_info *c)
{
dbg_cmt("pid %d goes sleep", current->pid);
/*
* The following sleeps if the condition is false, and will be woken
* when the commit ends. It is possible, although very unlikely, that we
* will wake up and see the subsequent commit running, rather than the
* one we were waiting for, and go back to sleep. However, we will be
* woken again, so there is no danger of sleeping forever.
*/
wait_event(c->cmt_wq, c->cmt_state != COMMIT_RUNNING_BACKGROUND &&
c->cmt_state != COMMIT_RUNNING_REQUIRED);
dbg_cmt("commit finished, pid %d woke up", current->pid);
return 0;
}
/**
* ubifs_run_commit - run or wait for commit.
* @c: UBIFS file-system description object
*
* This function runs commit and returns zero in case of success and a negative
* error code in case of failure.
*/
int ubifs_run_commit(struct ubifs_info *c)
{
int err = 0;
spin_lock(&c->cs_lock);
if (c->cmt_state == COMMIT_BROKEN) {
err = -EINVAL;
goto out;
}
if (c->cmt_state == COMMIT_RUNNING_BACKGROUND)
/*
* We set the commit state to 'running required' to indicate
* that we want it to complete as quickly as possible.
*/
c->cmt_state = COMMIT_RUNNING_REQUIRED;
if (c->cmt_state == COMMIT_RUNNING_REQUIRED) {
spin_unlock(&c->cs_lock);
return wait_for_commit(c);
}
spin_unlock(&c->cs_lock);
/* Ok, the commit is indeed needed */
down_write(&c->commit_sem);
spin_lock(&c->cs_lock);
/*
* Since we unlocked 'c->cs_lock', the state may have changed, so
* re-check it.
*/
if (c->cmt_state == COMMIT_BROKEN) {
err = -EINVAL;
goto out_cmt_unlock;
}
if (c->cmt_state == COMMIT_RUNNING_BACKGROUND)
c->cmt_state = COMMIT_RUNNING_REQUIRED;
if (c->cmt_state == COMMIT_RUNNING_REQUIRED) {
up_write(&c->commit_sem);
spin_unlock(&c->cs_lock);
return wait_for_commit(c);
}
c->cmt_state = COMMIT_RUNNING_REQUIRED;
spin_unlock(&c->cs_lock);
err = do_commit(c);
return err;
out_cmt_unlock:
up_write(&c->commit_sem);
out:
spin_unlock(&c->cs_lock);
return err;
}
/**
* ubifs_gc_should_commit - determine if it is time for GC to run commit.
* @c: UBIFS file-system description object
*
* This function is called by garbage collection to determine if commit should
* be run. If commit state is @COMMIT_BACKGROUND, which means that the journal
* is full enough to start commit, this function returns true. It is not
* absolutely necessary to commit yet, but it feels like this should be better
* then to keep doing GC. This function returns %1 if GC has to initiate commit
* and %0 if not.
*/
int ubifs_gc_should_commit(struct ubifs_info *c)
{
int ret = 0;
spin_lock(&c->cs_lock);
if (c->cmt_state == COMMIT_BACKGROUND) {
dbg_cmt("commit required now");
c->cmt_state = COMMIT_REQUIRED;
} else
dbg_cmt("commit not requested");
if (c->cmt_state == COMMIT_REQUIRED)
ret = 1;
spin_unlock(&c->cs_lock);
return ret;
}
#ifdef CONFIG_UBIFS_FS_DEBUG
/**
* struct idx_node - hold index nodes during index tree traversal.
* @list: list
* @iip: index in parent (slot number of this indexing node in the parent
* indexing node)
* @upper_key: all keys in this indexing node have to be less or equivalent to
* this key
* @idx: index node (8-byte aligned because all node structures must be 8-byte
* aligned)
*/
struct idx_node {
struct list_head list;
int iip;
union ubifs_key upper_key;
struct ubifs_idx_node idx __attribute__((aligned(8)));
};
/**
* dbg_old_index_check_init - get information for the next old index check.
* @c: UBIFS file-system description object
* @zroot: root of the index
*
* This function records information about the index that will be needed for the
* next old index check i.e. 'dbg_check_old_index()'.
*
* This function returns %0 on success and a negative error code on failure.
*/
int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot)
{
struct ubifs_idx_node *idx;
int lnum, offs, len, err = 0;
c->old_zroot = *zroot;
lnum = c->old_zroot.lnum;
offs = c->old_zroot.offs;
len = c->old_zroot.len;
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)
goto out;
c->old_zroot_level = le16_to_cpu(idx->level);
c->old_zroot_sqnum = le64_to_cpu(idx->ch.sqnum);
out:
kfree(idx);
return err;
}
/**
* dbg_check_old_index - check the old copy of the index.
* @c: UBIFS file-system description object
* @zroot: root of the new index
*
* In order to be able to recover from an unclean unmount, a complete copy of
* the index must exist on flash. This is the "old" index. The commit process
* must write the "new" index to flash without overwriting or destroying any
* part of the old index. This function is run at commit end in order to check
* that the old index does indeed exist completely intact.
*
* This function returns %0 on success and a negative error code on failure.
*/
int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot)
{
int lnum, offs, len, err = 0, uninitialized_var(last_level), child_cnt;
int first = 1, iip;
union ubifs_key lower_key, upper_key, l_key, u_key;
unsigned long long uninitialized_var(last_sqnum);
struct ubifs_idx_node *idx;
struct list_head list;
struct idx_node *i;
size_t sz;
if (!(ubifs_chk_flags & UBIFS_CHK_OLD_IDX))
goto out;
INIT_LIST_HEAD(&list);
sz = sizeof(struct idx_node) + ubifs_idx_node_sz(c, c->fanout) -
UBIFS_IDX_NODE_SZ;
/* Start at the old zroot */
lnum = c->old_zroot.lnum;
offs = c->old_zroot.offs;
len = c->old_zroot.len;
iip = 0;
/*
* Traverse the index tree preorder depth-first i.e. do a node and then
* its subtrees from left to right.
*/
while (1) {
struct ubifs_branch *br;
/* Get the next index node */
i = kmalloc(sz, GFP_NOFS);
if (!i) {
err = -ENOMEM;
goto out_free;
}
i->iip = iip;
/* Keep the index nodes on our path in a linked list */
list_add_tail(&i->list, &list);
/* Read the index node */
idx = &i->idx;
err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
if (err)
goto out_free;
/* Validate index node */
child_cnt = le16_to_cpu(idx->child_cnt);
if (child_cnt < 1 || child_cnt > c->fanout) {
err = 1;
goto out_dump;
}
if (first) {
first = 0;
/* Check root level and sqnum */
if (le16_to_cpu(idx->level) != c->old_zroot_level) {
err = 2;
goto out_dump;
}
if (le64_to_cpu(idx->ch.sqnum) != c->old_zroot_sqnum) {
err = 3;
goto out_dump;
}
/* Set last values as though root had a parent */
last_level = le16_to_cpu(idx->level) + 1;
last_sqnum = le64_to_cpu(idx->ch.sqnum) + 1;
key_read(c, ubifs_idx_key(c, idx), &lower_key);
highest_ino_key(c, &upper_key, INUM_WATERMARK);
}
key_copy(c, &upper_key, &i->upper_key);
if (le16_to_cpu(idx->level) != last_level - 1) {
err = 3;
goto out_dump;
}
/*
* The index is always written bottom up hence a child's sqnum
* is always less than the parents.
*/
if (le64_to_cpu(idx->ch.sqnum) >= last_sqnum) {
err = 4;
goto out_dump;
}
/* Check key range */
key_read(c, ubifs_idx_key(c, idx), &l_key);
br = ubifs_idx_branch(c, idx, child_cnt - 1);
key_read(c, &br->key, &u_key);
if (keys_cmp(c, &lower_key, &l_key) > 0) {
err = 5;
goto out_dump;
}
if (keys_cmp(c, &upper_key, &u_key) < 0) {
err = 6;
goto out_dump;
}
if (keys_cmp(c, &upper_key, &u_key) == 0)
if (!is_hash_key(c, &u_key)) {
err = 7;
goto out_dump;
}
/* Go to next index node */
if (le16_to_cpu(idx->level) == 0) {
/* At the bottom, so go up until can go right */
while (1) {
/* Drop the bottom of the list */
list_del(&i->list);
kfree(i);
/* No more list means we are done */
if (list_empty(&list))
goto out;
/* Look at the new bottom */
i = list_entry(list.prev, struct idx_node,
list);
idx = &i->idx;
/* Can we go right */
if (iip + 1 < le16_to_cpu(idx->child_cnt)) {
iip = iip + 1;
break;
} else
/* Nope, so go up again */
iip = i->iip;
}
} else
/* Go down left */
iip = 0;
/*
* We have the parent in 'idx' and now we set up for reading the
* child pointed to by slot 'iip'.
*/
last_level = le16_to_cpu(idx->level);
last_sqnum = le64_to_cpu(idx->ch.sqnum);
br = ubifs_idx_branch(c, idx, iip);
lnum = le32_to_cpu(br->lnum);
offs = le32_to_cpu(br->offs);
len = le32_to_cpu(br->len);
key_read(c, &br->key, &lower_key);
if (iip + 1 < le16_to_cpu(idx->child_cnt)) {
br = ubifs_idx_branch(c, idx, iip + 1);
key_read(c, &br->key, &upper_key);
} else
key_copy(c, &i->upper_key, &upper_key);
}
out:
err = dbg_old_index_check_init(c, zroot);
if (err)
goto out_free;
return 0;
out_dump:
dbg_err("dumping index node (iip=%d)", i->iip);
dbg_dump_node(c, idx);
list_del(&i->list);
kfree(i);
if (!list_empty(&list)) {
i = list_entry(list.prev, struct idx_node, list);
dbg_err("dumping parent index node");
dbg_dump_node(c, &i->idx);
}
out_free:
while (!list_empty(&list)) {
i = list_entry(list.next, struct idx_node, list);
list_del(&i->list);
kfree(i);
}
ubifs_err("failed, error %d", err);
if (err > 0)
err = -EINVAL;
return err;
}
#endif /* CONFIG_UBIFS_FS_DEBUG */
/*
* 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: Adrian Hunter
* Artem Bityutskiy (Битюцкий Артём)
* Zoltan Sogor
*/
/*
* This file provides a single place to access to compression and
* decompression.
*/
#include <linux/crypto.h>
#include "ubifs.h"
/* Fake description object for the "none" compressor */
static struct ubifs_compressor none_compr = {
.compr_type = UBIFS_COMPR_NONE,
.name = "no compression",
.capi_name = "",
};
#ifdef CONFIG_UBIFS_FS_LZO
static DEFINE_MUTEX(lzo_mutex);
static struct ubifs_compressor lzo_compr = {
.compr_type = UBIFS_COMPR_LZO,
.comp_mutex = &lzo_mutex,
.name = "LZO",
.capi_name = "lzo",
};
#else
static struct ubifs_compressor lzo_compr = {
.compr_type = UBIFS_COMPR_LZO,
.name = "LZO",
};
#endif
#ifdef CONFIG_UBIFS_FS_ZLIB
static DEFINE_MUTEX(deflate_mutex);
static DEFINE_MUTEX(inflate_mutex);
static struct ubifs_compressor zlib_compr = {
.compr_type = UBIFS_COMPR_ZLIB,
.comp_mutex = &deflate_mutex,
.decomp_mutex = &inflate_mutex,
.name = "zlib",
.capi_name = "deflate",
};
#else
static struct ubifs_compressor zlib_compr = {
.compr_type = UBIFS_COMPR_ZLIB,
.name = "zlib",
};
#endif
/* All UBIFS compressors */
struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
/**
* ubifs_compress - compress data.
* @in_buf: data to compress
* @in_len: length of the data to compress
* @out_buf: output buffer where compressed data should be stored
* @out_len: output buffer length is returned here
* @compr_type: type of compression to use on enter, actually used compression
* type on exit
*
* This function compresses input buffer @in_buf of length @in_len and stores
* the result in the output buffer @out_buf and the resulting length in
* @out_len. If the input buffer does not compress, it is just copied to the
* @out_buf. The same happens if @compr_type is %UBIFS_COMPR_NONE or if
* compression error occurred.
*
* Note, if the input buffer was not compressed, it is copied to the output
* buffer and %UBIFS_COMPR_NONE is returned in @compr_type.
*
* This functions returns %0 on success or a negative error code on failure.
*/
void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
int *compr_type)
{
int err;
struct ubifs_compressor *compr = ubifs_compressors[*compr_type];
if (*compr_type == UBIFS_COMPR_NONE)
goto no_compr;
/* If the input data is small, do not even try to compress it */
if (in_len < UBIFS_MIN_COMPR_LEN)
goto no_compr;
if (compr->comp_mutex)
mutex_lock(compr->comp_mutex);
err = crypto_comp_compress(compr->cc, in_buf, in_len, out_buf,
out_len);
if (compr->comp_mutex)
mutex_unlock(compr->comp_mutex);
if (unlikely(err)) {
ubifs_warn("cannot compress %d bytes, compressor %s, "
"error %d, leave data uncompressed",
in_len, compr->name, err);
goto no_compr;
}
/*
* Presently, we just require that compression results in less data,
* rather than any defined minimum compression ratio or amount.
*/
if (ALIGN(*out_len, 8) >= ALIGN(in_len, 8))
goto no_compr;
return;
no_compr:
memcpy(out_buf, in_buf, in_len);
*out_len = in_len;
*compr_type = UBIFS_COMPR_NONE;
}
/**
* 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;
}
if (compr->decomp_mutex)
mutex_lock(compr->decomp_mutex);
err = crypto_comp_decompress(compr->cc, in_buf, in_len, out_buf,
out_len);
if (compr->decomp_mutex)
mutex_unlock(compr->decomp_mutex);
if (err)
ubifs_err("cannot decompress %d bytes, compressor %s, "
"error %d", in_len, compr->name, err);
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)
{
if (compr->capi_name) {
compr->cc = crypto_alloc_comp(compr->capi_name, 0, 0);
if (IS_ERR(compr->cc)) {
ubifs_err("cannot initialize compressor %s, error %ld",
compr->name, PTR_ERR(compr->cc));
return PTR_ERR(compr->cc);
}
}
ubifs_compressors[compr->compr_type] = compr;
return 0;
}
/**
* compr_exit - de-initialize a compressor.
* @compr: compressor description object
*/
static void compr_exit(struct ubifs_compressor *compr)
{
if (compr->capi_name)
crypto_free_comp(compr->cc);
return;
}
/**
* 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)
goto out_lzo;
ubifs_compressors[UBIFS_COMPR_NONE] = &none_compr;
return 0;
out_lzo:
compr_exit(&lzo_compr);
return err;
}
/**
* ubifs_compressors_exit - de-initialize UBIFS compressors.
*/
void __exit ubifs_compressors_exit(void)
{
compr_exit(&lzo_compr);
compr_exit(&zlib_compr);
}
此差异已折叠。
/*
* 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
#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__, current->pid); \
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", current->pid, \
__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
extern spinlock_t dbg_lock;
extern unsigned int ubifs_msg_flags;
extern unsigned int ubifs_chk_flags;
extern unsigned int ubifs_tst_flags;
/* Dump functions */
const char *dbg_ntype(int type);
const char *dbg_cstate(int cmt_state);
const char *dbg_get_key_dump(const struct ubifs_info *c,
const union ubifs_key *key);
void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode);
void dbg_dump_node(const struct ubifs_info *c, const void *node);
void dbg_dump_budget_req(const struct ubifs_budget_req *req);
void dbg_dump_lstats(const struct ubifs_lp_stats *lst);
void dbg_dump_budg(struct ubifs_info *c);
void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp);
void dbg_dump_lprops(struct ubifs_info *c);
void dbg_dump_leb(const struct ubifs_info *c, int lnum);
void dbg_dump_znode(const struct ubifs_info *c,
const struct ubifs_znode *znode);
void dbg_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat);
void dbg_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
struct ubifs_nnode *parent, int iip);
void dbg_dump_tnc(struct ubifs_info *c);
void dbg_dump_index(struct ubifs_info *c);
/* Checking helper functions */
typedef int (*dbg_leaf_callback)(struct ubifs_info *c,
struct ubifs_zbranch *zbr, void *priv);
typedef int (*dbg_znode_callback)(struct ubifs_info *c,
struct ubifs_znode *znode, void *priv);
int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
dbg_znode_callback znode_cb, void *priv);
/* Checking functions */
int dbg_check_lprops(struct ubifs_info *c);
int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot);
int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot);
int dbg_check_cats(struct ubifs_info *c);
int dbg_check_ltab(struct ubifs_info *c);
int dbg_check_synced_i_size(struct inode *inode);
int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir);
int dbg_check_tnc(struct ubifs_info *c, int extra);
int dbg_check_idx_size(struct ubifs_info *c, long long idx_size);
int dbg_check_filesystem(struct ubifs_info *c);
void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
int add_pos);
int dbg_check_lprops(struct ubifs_info *c);
int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
int row, int col);
/* Force the use of in-the-gaps method for testing */
#define dbg_force_in_the_gaps_enabled \
(ubifs_tst_flags & UBIFS_TST_FORCE_IN_THE_GAPS)
int dbg_force_in_the_gaps(void);
/* Failure mode for recovery testing */
#define dbg_failure_mode (ubifs_tst_flags & UBIFS_TST_RCVRY)
void dbg_failure_mode_registration(struct ubifs_info *c);
void dbg_failure_mode_deregistration(struct ubifs_info *c);
#ifndef UBIFS_DBG_PRESERVE_UBI
#define ubi_leb_read dbg_leb_read
#define ubi_leb_write dbg_leb_write
#define ubi_leb_change dbg_leb_change
#define ubi_leb_erase dbg_leb_erase
#define ubi_leb_unmap dbg_leb_unmap
#define ubi_is_mapped dbg_is_mapped
#define ubi_leb_map dbg_leb_map
#endif
int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
int len, int check);
int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
int offset, int len, int dtype);
int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
int len, int dtype);
int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum);
int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum);
int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum);
int dbg_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype);
static inline int dbg_read(struct ubi_volume_desc *desc, int lnum, char *buf,
int offset, int len)
{
return dbg_leb_read(desc, lnum, buf, offset, len, 0);
}
static inline int dbg_write(struct ubi_volume_desc *desc, int lnum,
const void *buf, int offset, int len)
{
return dbg_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN);
}
static inline int dbg_change(struct ubi_volume_desc *desc, int lnum,
const void *buf, int len)
{
return dbg_leb_change(desc, lnum, buf, len, UBI_UNKNOWN);
}
#else /* !CONFIG_UBIFS_FS_DEBUG */
#define UBIFS_DBG(op)
#define ubifs_assert(expr) ({})
#define ubifs_assert_cmt_locked(c)
#define dbg_dump_stack()
#define dbg_err(fmt, ...) ({})
#define dbg_msg(fmt, ...) ({})
#define dbg_key(c, key, fmt, ...) ({})
#define dbg_gen(fmt, ...) ({})
#define dbg_jnl(fmt, ...) ({})
#define dbg_tnc(fmt, ...) ({})
#define dbg_lp(fmt, ...) ({})
#define dbg_find(fmt, ...) ({})
#define dbg_mnt(fmt, ...) ({})
#define dbg_io(fmt, ...) ({})
#define dbg_cmt(fmt, ...) ({})
#define dbg_budg(fmt, ...) ({})
#define dbg_log(fmt, ...) ({})
#define dbg_gc(fmt, ...) ({})
#define dbg_scan(fmt, ...) ({})
#define dbg_rcvry(fmt, ...) ({})
#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_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_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__ */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*
* 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: Zoltan Sogor
* Artem Bityutskiy (Битюцкий Артём)
* Adrian Hunter
*/
/* This file implements EXT2-compatible extended attribute ioctl() calls */
#include <linux/compat.h>
#include <linux/smp_lock.h>
#include <linux/mount.h>
#include "ubifs.h"
/**
* ubifs_set_inode_flags - set VFS inode flags.
* @inode: VFS inode to set flags for
*
* This function propagates flags from UBIFS inode object to VFS inode object.
*/
void ubifs_set_inode_flags(struct inode *inode)
{
unsigned int flags = ubifs_inode(inode)->flags;
inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | S_DIRSYNC);
if (flags & UBIFS_SYNC_FL)
inode->i_flags |= S_SYNC;
if (flags & UBIFS_APPEND_FL)
inode->i_flags |= S_APPEND;
if (flags & UBIFS_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
if (flags & UBIFS_DIRSYNC_FL)
inode->i_flags |= S_DIRSYNC;
}
/*
* ioctl2ubifs - convert ioctl inode flags to UBIFS inode flags.
* @ioctl_flags: flags to convert
*
* This function convert ioctl flags (@FS_COMPR_FL, etc) to UBIFS inode flags
* (@UBIFS_COMPR_FL, etc).
*/
static int ioctl2ubifs(int ioctl_flags)
{
int ubifs_flags = 0;
if (ioctl_flags & FS_COMPR_FL)
ubifs_flags |= UBIFS_COMPR_FL;
if (ioctl_flags & FS_SYNC_FL)
ubifs_flags |= UBIFS_SYNC_FL;
if (ioctl_flags & FS_APPEND_FL)
ubifs_flags |= UBIFS_APPEND_FL;
if (ioctl_flags & FS_IMMUTABLE_FL)
ubifs_flags |= UBIFS_IMMUTABLE_FL;
if (ioctl_flags & FS_DIRSYNC_FL)
ubifs_flags |= UBIFS_DIRSYNC_FL;
return ubifs_flags;
}
/*
* ubifs2ioctl - convert UBIFS inode flags to ioctl inode flags.
* @ubifs_flags: flags to convert
*
* This function convert UBIFS (@UBIFS_COMPR_FL, etc) to ioctl flags
* (@FS_COMPR_FL, etc).
*/
static int ubifs2ioctl(int ubifs_flags)
{
int ioctl_flags = 0;
if (ubifs_flags & UBIFS_COMPR_FL)
ioctl_flags |= FS_COMPR_FL;
if (ubifs_flags & UBIFS_SYNC_FL)
ioctl_flags |= FS_SYNC_FL;
if (ubifs_flags & UBIFS_APPEND_FL)
ioctl_flags |= FS_APPEND_FL;
if (ubifs_flags & UBIFS_IMMUTABLE_FL)
ioctl_flags |= FS_IMMUTABLE_FL;
if (ubifs_flags & UBIFS_DIRSYNC_FL)
ioctl_flags |= FS_DIRSYNC_FL;
return ioctl_flags;
}
static int setflags(struct inode *inode, int flags)
{
int oldflags, err, release;
struct ubifs_inode *ui = ubifs_inode(inode);
struct ubifs_info *c = inode->i_sb->s_fs_info;
struct ubifs_budget_req req = { .dirtied_ino = 1,
.dirtied_ino_d = ui->data_len };
err = ubifs_budget_space(c, &req);
if (err)
return err;
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
* the relevant capability.
*/
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
if (!capable(CAP_LINUX_IMMUTABLE)) {
err = -EPERM;
goto out_unlock;
}
}
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
inode->i_ctime = ubifs_current_time(inode);
release = ui->dirty;
mark_inode_dirty_sync(inode);
mutex_unlock(&ui->ui_mutex);
if (release)
ubifs_release_budget(c, &req);
if (IS_SYNC(inode))
err = write_inode_now(inode, 1);
return err;
out_unlock:
ubifs_err("can't modify inode %lu attributes", inode->i_ino);
mutex_unlock(&ui->ui_mutex);
ubifs_release_budget(c, &req);
return err;
}
long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int flags, err;
struct inode *inode = file->f_path.dentry->d_inode;
switch (cmd) {
case FS_IOC_GETFLAGS:
flags = ubifs2ioctl(ubifs_inode(inode)->flags);
return put_user(flags, (int __user *) arg);
case FS_IOC_SETFLAGS: {
if (IS_RDONLY(inode))
return -EROFS;
if (!is_owner_or_cap(inode))
return -EACCES;
if (get_user(flags, (int __user *) arg))
return -EFAULT;
if (!S_ISDIR(inode->i_mode))
flags &= ~FS_DIRSYNC_FL;
/*
* Make sure the file-system is read-write and make sure it
* will not become read-only while we are changing the flags.
*/
err = mnt_want_write(file->f_path.mnt);
if (err)
return err;
err = setflags(inode, flags);
mnt_drop_write(file->f_path.mnt);
return err;
}
default:
return -ENOTTY;
}
}
#ifdef CONFIG_COMPAT
long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case FS_IOC32_GETFLAGS:
cmd = FS_IOC_GETFLAGS;
break;
case FS_IOC32_SETFLAGS:
cmd = FS_IOC_SETFLAGS;
break;
default:
return -ENOIOCTLCMD;
}
return ubifs_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
}
#endif
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*
* 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;
}
/**
* ubifs_write_master - write master node.
* @c: UBIFS file-system description object
*
* This function writes the master node. The caller has to take the
* @c->mst_mutex lock before calling this function. Returns zero in case of
* success and a negative error code in case of failure. The master node is
* written twice to enable recovery.
*/
int ubifs_write_master(struct ubifs_info *c)
{
int err, lnum, offs, len;
if (c->ro_media)
return -EINVAL;
lnum = UBIFS_MST_LNUM;
offs = c->mst_offs + c->mst_node_alsz;
len = UBIFS_MST_NODE_SZ;
if (offs + UBIFS_MST_NODE_SZ > c->leb_size) {
err = ubifs_leb_unmap(c, lnum);
if (err)
return err;
offs = 0;
}
c->mst_offs = offs;
c->mst_node->highest_inum = cpu_to_le64(c->highest_inum);
err = ubifs_write_node(c, c->mst_node, len, lnum, offs, UBI_SHORTTERM);
if (err)
return err;
lnum += 1;
if (offs == 0) {
err = ubifs_leb_unmap(c, lnum);
if (err)
return err;
}
err = ubifs_write_node(c, c->mst_node, len, lnum, offs, UBI_SHORTTERM);
return err;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册