/*
* 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 journal replay code. It runs when the file-system is being
* mounted and requires no locking.
*
* The larger is the journal, the longer it takes to scan it, so the longer it
* takes to mount UBIFS. This is why the journal has limited size which may be
* changed depending on the system requirements. But a larger journal gives
* faster I/O speed because it writes the index less frequently. So this is a
* trade-off. Also, the journal is indexed by the in-memory index (TNC), so the
* larger is the journal, the more memory its index may consume.
*/
#include "ubifs.h"
/*
* Replay flags.
*
* REPLAY_DELETION: node was deleted
* REPLAY_REF: node is a reference node
*/
enum {
REPLAY_DELETION = 1,
REPLAY_REF = 2,
};
/**
* struct replay_entry - replay tree entry.
* @lnum: logical eraseblock number of the node
* @offs: node offset
* @len: node length
* @sqnum: node sequence number
* @flags: replay flags
* @rb: links the replay tree
* @key: node key
* @nm: directory entry name
* @old_size: truncation old size
* @new_size: truncation new size
* @free: amount of free space in a bud
* @dirty: amount of dirty space in a bud from padding and deletion nodes
*
* UBIFS journal replay must compare node sequence numbers, which means it must
* build a tree of node information to insert into the TNC.
*/
struct replay_entry {
int lnum;
int offs;
int len;
unsigned long long sqnum;
int flags;
struct rb_node rb;
union ubifs_key key;
union {
struct qstr nm;
struct {
loff_t old_size;
loff_t new_size;
};
struct {
int free;
int dirty;
};
};
};
/**
* struct bud_entry - entry in the list of buds to replay.
* @list: next bud in the list
* @bud: bud description object
* @free: free bytes in the bud
* @sqnum: reference node sequence number
*/
struct bud_entry {
struct list_head list;
struct ubifs_bud *bud;
int free;
unsigned long long sqnum;
};
/**
* set_bud_lprops - set free and dirty space used by a bud.
* @c: UBIFS file-system description object
* @r: replay entry of bud
*/
static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r)
{
const struct ubifs_lprops *lp;
int err = 0, dirty;
ubifs_get_lprops(c);
lp = ubifs_lpt_lookup_dirty(c, r->lnum);
if (IS_ERR(lp)) {
err = PTR_ERR(lp);
goto out;
}
dirty = lp->dirty;
if (r->offs == 0 && (lp->free != c->leb_size || lp->dirty != 0)) {
/*
* The LEB was added to the journal with a starting offset of
* zero which means the LEB must have been empty. The LEB
* property values should be lp->free == c->leb_size and
* lp->dirty == 0, but that is not the case. The reason is that
* the LEB was garbage collected. The garbage collector resets
* the free and dirty space without recording it anywhere except
* lprops, so if there is not a commit then lprops does not have
* that information next time the file system is mounted.
*
* We do not need to adjust free space because the scan has told
* us the exact value which is recorded in the replay entry as
* r->free.
*
* However we do need to subtract from the dirty space the
* amount of space that the garbage collector reclaimed, which
* is the whole LEB minus the amount of space that was free.
*/
dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum,
lp->free, lp->dirty);
dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum,
lp->free, lp->dirty);
dirty -= c->leb_size - lp->free;
/*
* If the replay order was perfect the dirty space would now be
* zero. The order is not perfect because the the journal heads
* race with eachother. This is not a problem but is does mean
* that the dirty space may temporarily exceed c->leb_size
* during the replay.
*/
if (dirty != 0)
dbg_msg("LEB %d lp: %d free %d dirty "
"replay: %d free %d dirty", r->lnum, lp->free,
lp->dirty, r->free, r->dirty);
}
lp = ubifs_change_lp(c, lp, r->free, dirty + r->dirty,
lp->flags | LPROPS_TAKEN, 0);
if (IS_ERR(lp)) {
err = PTR_ERR(lp);
goto out;
}
out:
ubifs_release_lprops(c);
return err;
}
/**
* trun_remove_range - apply a replay entry for a truncation to the TNC.
* @c: UBIFS file-system description object
* @r: replay entry of truncation
*/
static int trun_remove_range(struct ubifs_info *c, struct replay_entry *r)
{
unsigned min_blk, max_blk;
union ubifs_key min_key, max_key;
ino_t ino;
min_blk = r->new_size / UBIFS_BLOCK_SIZE;
if (r->new_size & (UBIFS_BLOCK_SIZE - 1))
min_blk += 1;