UBIFS: add new flash file system
Artem Bityutskiy [Mon, 14 Jul 2008 16:08:37 +0000 (19:08 +0300)]
This is a new flash file system. See
http://www.linux-mtd.infradead.org/doc/ubifs.html

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>

32 files changed:
fs/ubifs/budget.c [new file with mode: 0644]
fs/ubifs/commit.c [new file with mode: 0644]
fs/ubifs/compress.c [new file with mode: 0644]
fs/ubifs/debug.c [new file with mode: 0644]
fs/ubifs/debug.h [new file with mode: 0644]
fs/ubifs/dir.c [new file with mode: 0644]
fs/ubifs/file.c [new file with mode: 0644]
fs/ubifs/find.c [new file with mode: 0644]
fs/ubifs/gc.c [new file with mode: 0644]
fs/ubifs/io.c [new file with mode: 0644]
fs/ubifs/ioctl.c [new file with mode: 0644]
fs/ubifs/journal.c [new file with mode: 0644]
fs/ubifs/key.h [new file with mode: 0644]
fs/ubifs/log.c [new file with mode: 0644]
fs/ubifs/lprops.c [new file with mode: 0644]
fs/ubifs/lpt.c [new file with mode: 0644]
fs/ubifs/lpt_commit.c [new file with mode: 0644]
fs/ubifs/master.c [new file with mode: 0644]
fs/ubifs/misc.h [new file with mode: 0644]
fs/ubifs/orphan.c [new file with mode: 0644]
fs/ubifs/recovery.c [new file with mode: 0644]
fs/ubifs/replay.c [new file with mode: 0644]
fs/ubifs/sb.c [new file with mode: 0644]
fs/ubifs/scan.c [new file with mode: 0644]
fs/ubifs/shrinker.c [new file with mode: 0644]
fs/ubifs/super.c [new file with mode: 0644]
fs/ubifs/tnc.c [new file with mode: 0644]
fs/ubifs/tnc_commit.c [new file with mode: 0644]
fs/ubifs/tnc_misc.c [new file with mode: 0644]
fs/ubifs/ubifs-media.h [new file with mode: 0644]
fs/ubifs/ubifs.h [new file with mode: 0644]
fs/ubifs/xattr.c [new file with mode: 0644]

diff --git a/fs/ubifs/budget.c b/fs/ubifs/budget.c
new file mode 100644 (file)
index 0000000..d81fb9e
--- /dev/null
@@ -0,0 +1,731 @@
+/*
+ * 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/writeback.h>
+#include <asm/div64.h>
+
+/*
+ * When pessimistic budget calculations say that there is no enough space,
+ * UBIFS starts writing back dirty inodes and pages, doing garbage collection,
+ * or committing. The below constants define maximum number of times UBIFS
+ * repeats the operations.
+ */
+#define MAX_SHRINK_RETRIES 8
+#define MAX_GC_RETRIES     4
+#define MAX_CMT_RETRIES    2
+#define MAX_NOSPC_RETRIES  1
+
+/*
+ * The below constant defines amount of dirty pages which should be written
+ * back at when trying to shrink the liability.
+ */
+#define NR_TO_WRITE 16
+
+/**
+ * struct retries_info - information about re-tries while making free space.
+ * @prev_liability: previous liability
+ * @shrink_cnt: how many times the liability was shrinked
+ * @shrink_retries: count of liability shrink re-tries (increased when
+ *                  liability does not shrink)
+ * @try_gc: GC should be tried first
+ * @gc_retries: how many times GC was run
+ * @cmt_retries: how many times commit has been done
+ * @nospc_retries: how many times GC returned %-ENOSPC
+ *
+ * Since we consider budgeting to be the fast-path, and this structure has to
+ * be allocated on stack and zeroed out, we make it smaller using bit-fields.
+ */
+struct retries_info {
+       long long prev_liability;
+       unsigned int shrink_cnt;
+       unsigned int shrink_retries:5;
+       unsigned int try_gc:1;
+       unsigned int gc_retries:4;
+       unsigned int cmt_retries:3;
+       unsigned int nospc_retries:1;
+};
+
+/**
+ * shrink_liability - write-back some dirty pages/inodes.
+ * @c: UBIFS file-system description object
+ * @nr_to_write: how many dirty pages to write-back
+ *
+ * This function shrinks UBIFS liability by means of writing back some amount
+ * of dirty inodes and their pages. Returns the amount of pages which were
+ * written back. The returned value does not include dirty inodes which were
+ * synchronized.
+ *
+ * Note, this function synchronizes even VFS inodes which are locked
+ * (@i_mutex) by the caller of the budgeting function, because write-back does
+ * not touch @i_mutex.
+ */
+static int shrink_liability(struct ubifs_info *c, int nr_to_write)
+{
+       int nr_written;
+       struct writeback_control wbc = {
+               .sync_mode   = WB_SYNC_NONE,
+               .range_end   = LLONG_MAX,
+               .nr_to_write = nr_to_write,
+       };
+
+       generic_sync_sb_inodes(c->vfs_sb, &wbc);
+       nr_written = nr_to_write - wbc.nr_to_write;
+
+       if (!nr_written) {
+               /*
+                * Re-try again but wait on pages/inodes which are being
+                * written-back concurrently (e.g., by pdflush).
+                */
+               memset(&wbc, 0, sizeof(struct writeback_control));
+               wbc.sync_mode   = WB_SYNC_ALL;
+               wbc.range_end   = LLONG_MAX;
+               wbc.nr_to_write = nr_to_write;
+               generic_sync_sb_inodes(c->vfs_sb, &wbc);
+               nr_written = nr_to_write - wbc.nr_to_write;
+       }
+
+       dbg_budg("%d pages were written back", nr_written);
+       return nr_written;
+}
+
+
+/**
+ * run_gc - run garbage collector.
+ * @c: UBIFS file-system description object
+ *
+ * This function runs garbage collector to make some more free space. Returns
+ * zero if a free LEB has been produced, %-EAGAIN if commit is required, and a
+ * negative error code in case of failure.
+ */
+static int run_gc(struct ubifs_info *c)
+{
+       int err, lnum;
+
+       /* Make some free space by garbage-collecting dirty space */
+       down_read(&c->commit_sem);
+       lnum = ubifs_garbage_collect(c, 1);
+       up_read(&c->commit_sem);
+       if (lnum < 0)
+               return lnum;
+
+       /* GC freed one LEB, return it to lprops */
+       dbg_budg("GC freed LEB %d", lnum);
+       err = ubifs_return_leb(c, lnum);
+       if (err)
+               return err;
+       return 0;
+}
+
+/**
+ * make_free_space - make more free space on the file-system.
+ * @c: UBIFS file-system description object
+ * @ri: information about previous invocations of this function
+ *
+ * This function is called when an operation cannot be budgeted because there
+ * is supposedly no free space. But in most cases there is some free space:
+ *   o budgeting is pessimistic, so it always budgets more then it is actually
+ *     needed, so shrinking the liability is one way to make free space - the
+ *     cached data will take less space then it was budgeted for;
+ *   o GC may turn some dark space into free space (budgeting treats dark space
+ *     as not available);
+ *   o commit may free some LEB, i.e., turn freeable LEBs into free LEBs.
+ *
+ * So this function tries to do the above. Returns %-EAGAIN if some free space
+ * was presumably made and the caller has to re-try budgeting the operation.
+ * Returns %-ENOSPC if it couldn't do more free space, and other negative error
+ * codes on failures.
+ */
+static int make_free_space(struct ubifs_info *c, struct retries_info *ri)
+{
+       int err;
+
+       /*
+        * If we have some dirty pages and inodes (liability), try to write
+        * them back unless this was tried too many times without effect
+        * already.
+        */
+       if (ri->shrink_retries < MAX_SHRINK_RETRIES && !ri->try_gc) {
+               long long liability;
+
+               spin_lock(&c->space_lock);
+               liability = c->budg_idx_growth + c->budg_data_growth +
+                           c->budg_dd_growth;
+               spin_unlock(&c->space_lock);
+
+               if (ri->prev_liability >= liability) {
+                       /* Liability does not shrink, next time try GC then */
+                       ri->shrink_retries += 1;
+                       if (ri->gc_retries < MAX_GC_RETRIES)
+                               ri->try_gc = 1;
+                       dbg_budg("liability did not shrink: retries %d of %d",
+                                ri->shrink_retries, MAX_SHRINK_RETRIES);
+               }
+
+               dbg_budg("force write-back (count %d)", ri->shrink_cnt);
+               shrink_liability(c, NR_TO_WRITE + ri->shrink_cnt);
+
+               ri->prev_liability = liability;
+               ri->shrink_cnt += 1;
+               return -EAGAIN;
+       }
+
+       /*
+        * Try to run garbage collector unless it was already tried too many
+        * times.
+        */
+       if (ri->gc_retries < MAX_GC_RETRIES) {
+               ri->gc_retries += 1;
+               dbg_budg("run GC, retries %d of %d",
+                        ri->gc_retries, MAX_GC_RETRIES);
+
+               ri->try_gc = 0;
+               err = run_gc(c);
+               if (!err)
+                       return -EAGAIN;
+
+               if (err == -EAGAIN) {
+                       dbg_budg("GC asked to commit");
+                       err = ubifs_run_commit(c);
+                       if (err)
+                               return err;
+                       return -EAGAIN;
+               }
+
+               if (err != -ENOSPC)
+                       return err;
+
+               /*
+                * GC could not make any progress. If this is the first time,
+                * then it makes sense to try to commit, because it might make
+                * some dirty space.
+                */
+               dbg_budg("GC returned -ENOSPC, retries %d",
+                        ri->nospc_retries);
+               if (ri->nospc_retries >= MAX_NOSPC_RETRIES)
+                       return err;
+               ri->nospc_retries += 1;
+       }
+
+       /* Neither GC nor write-back helped, try to commit */
+       if (ri->cmt_retries < MAX_CMT_RETRIES) {
+               ri->cmt_retries += 1;
+               dbg_budg("run commit, retries %d of %d",
+                        ri->cmt_retries, MAX_CMT_RETRIES);
+               err = ubifs_run_commit(c);
+               if (err)
+                       return err;
+               return -EAGAIN;
+       }
+       return -ENOSPC;
+}
+
+/**
+ * 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 ret;
+       uint64_t idx_size;
+
+       idx_size = c->old_idx_sz + c->budg_idx_growth + c->budg_uncommitted_idx;
+
+       /* And make sure we have twice the index size of space reserved */
+       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.
+        */
+       if (do_div(idx_size, c->leb_size - c->max_idx_node_sz))
+               ret = idx_size + 1;
+       else
+               ret = idx_size;
+       /*
+        * The index head is not available for the in-the-gaps method, so add an
+        * extra LEB to compensate.
+        */
+       ret += 1;
+       /*
+        * At present the index needs at least 2 LEBs: one for the index head
+        * and one for in-the-gaps method (which currently does not cater for
+        * the index head and so excludes it from consideration).
+        */
+       if (ret < 2)
+               ret = 2;
+       return ret;
+}
+
+/**
+ * ubifs_calc_available - calculate available FS space.
+ * @c: UBIFS file-system description object
+ * @min_idx_lebs: minimum number of LEBs reserved for the index
+ *
+ * This function calculates and returns amount of FS space available for use.
+ */
+long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs)
+{
+       int subtract_lebs;
+       long long available;
+
+       /*
+        * Force the amount available to the total size reported if the used
+        * space is zero.
+        */
+       if (c->lst.total_used <= UBIFS_INO_NODE_SZ &&
+           c->budg_data_growth + c->budg_dd_growth == 0) {
+               /* Do the same calculation as for c->block_cnt */
+               available = c->main_lebs - 2;
+               available *= c->leb_size - c->dark_wm;
+               return available;
+       }
+
+       available = c->main_bytes - c->lst.total_used;
+
+       /*
+        * Now 'available' contains theoretically available flash space
+        * assuming there is no index, so we have to subtract the space which
+        * is reserved for the index.
+        */
+       subtract_lebs = min_idx_lebs;
+
+       /* Take into account that GC reserves one LEB for its own needs */
+       subtract_lebs += 1;
+
+       /*
+        * The GC journal head LEB is not really accessible. And since
+        * different write types go to different heads, we may count only on
+        * one head's space.
+        */
+       subtract_lebs += c->jhead_cnt - 1;
+
+       /* We also reserve one LEB for deletions, which bypass budgeting */
+       subtract_lebs += 1;
+
+       available -= (long long)subtract_lebs * c->leb_size;
+
+       /* Subtract the dead space which is not available for use */
+       available -= c->lst.total_dead;
+
+       /*
+        * Subtract dark space, which might or might not be usable - it depends
+        * on the data which we have on the media and which will be written. If
+        * this is a lot of uncompressed or not-compressible data, the dark
+        * space cannot be used.
+        */
+       available -= c->lst.total_dark;
+
+       /*
+        * However, there is more dark space. The index may be bigger than
+        * @min_idx_lebs. Those extra LEBs are assumed to be available, but
+        * their dark space is not included in total_dark, so it is subtracted
+        * here.
+        */
+       if (c->lst.idx_lebs > min_idx_lebs) {
+               subtract_lebs = c->lst.idx_lebs - min_idx_lebs;
+               available -= subtract_lebs * c->dark_wm;
+       }
+
+       /* The calculations are rough and may end up with a negative number */
+       return available > 0 ? available : 0;
+}
+
+/**
+ * can_use_rp - check whether the user is allowed to use reserved pool.
+ * @c: UBIFS file-system description object
+ *
+ * UBIFS has so-called "reserved pool" which is flash space reserved
+ * for the superuser and for uses whose UID/GID is recorded in UBIFS superblock.
+ * This function checks whether current user is allowed to use reserved pool.
+ * Returns %1  current user is allowed to use reserved pool and %0 otherwise.
+ */
+static int can_use_rp(struct ubifs_info *c)
+{
+       if (current->fsuid == c->rp_uid || capable(CAP_SYS_RESOURCE) ||
+           (c->rp_gid != 0 && in_group_p(c->rp_gid)))
+               return 1;
+       return 0;
+}
+
+/**
+ * do_budget_space - reserve flash space for index and data growth.
+ * @c: UBIFS file-system description object
+ *
+ * This function makes sure UBIFS has enough free eraseblocks for index growth
+ * and data.
+ *
+ * When budgeting index space, UBIFS reserves twice as more LEBs as the index
+ * would take if it was consolidated and written to the flash. This guarantees
+ * that the "in-the-gaps" commit method always succeeds and UBIFS will always
+ * be able to commit dirty index. So this function basically adds amount of
+ * budgeted index space to the size of the current index, multiplies this by 2,
+ * and makes sure this does not exceed the amount of free eraseblocks.
+ *
+ * Notes about @c->min_idx_lebs and @c->lst.idx_lebs variables:
+ * o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might
+ *    be large, because UBIFS does not do any index consolidation as long as
+ *    there is free space. IOW, the index may take a lot of LEBs, but the LEBs
+ *    will contain a lot of dirt.
+ * o @c->min_idx_lebs is the the index presumably takes. IOW, the index may be
+ *   consolidated to take up to @c->min_idx_lebs LEBs.
+ *
+ * This function returns zero in case of success, and %-ENOSPC in case of
+ * failure.
+ */
+static int do_budget_space(struct ubifs_info *c)
+{
+       long long outstanding, available;
+       int lebs, rsvd_idx_lebs, min_idx_lebs;
+
+       /* First budget index space */
+       min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+       /* Now 'min_idx_lebs' contains number of LEBs to reserve */
+       if (min_idx_lebs > c->lst.idx_lebs)
+               rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs;
+       else
+               rsvd_idx_lebs = 0;
+
+       /*
+        * The number of LEBs that are available to be used by the index is:
+        *
+        *    @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt -
+        *    @c->lst.taken_empty_lebs
+        *
+        * @empty_lebs are available because they are empty. @freeable_cnt are
+        * available because they contain only free and dirty space and the
+        * index allocation always occurs after wbufs are synch'ed.
+        * @idx_gc_cnt are available because they are index LEBs that have been
+        * garbage collected (including trivial GC) and are awaiting the commit
+        * before they can be unmapped - note that the in-the-gaps method will
+        * grab these if it needs them. @taken_empty_lebs are empty_lebs that
+        * have already been allocated for some purpose (also includes those
+        * LEBs on the @idx_gc list).
+        *
+        * Note, @taken_empty_lebs may temporarily be higher by one because of
+        * the way we serialize LEB allocations and budgeting. See a comment in
+        * 'ubifs_find_free_space()'.
+        */
+       lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
+              c->lst.taken_empty_lebs;
+       if (unlikely(rsvd_idx_lebs > lebs)) {
+               dbg_budg("out of indexing space: min_idx_lebs %d (old %d), "
+                        "rsvd_idx_lebs %d", min_idx_lebs, c->min_idx_lebs,
+                        rsvd_idx_lebs);
+               return -ENOSPC;
+       }
+
+       available = ubifs_calc_available(c, min_idx_lebs);
+       outstanding = c->budg_data_growth + c->budg_dd_growth;
+
+       if (unlikely(available < outstanding)) {
+               dbg_budg("out of data space: available %lld, outstanding %lld",
+                        available, outstanding);
+               return -ENOSPC;
+       }
+
+       if (available - outstanding <= c->rp_size && !can_use_rp(c))
+               return -ENOSPC;
+
+       c->min_idx_lebs = min_idx_lebs;
+       return 0;
+}
+
+/**
+ * calc_idx_growth - calculate approximate index growth from budgeting request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ *
+ * For now we assume each new node adds one znode. But this is rather poor
+ * approximation, though.
+ */
+static int calc_idx_growth(const struct ubifs_info *c,
+                          const struct ubifs_budget_req *req)
+{
+       int znodes;
+
+       znodes = req->new_ino + (req->new_page << UBIFS_BLOCKS_PER_PAGE_SHIFT) +
+                req->new_dent;
+       return znodes * c->max_idx_node_sz;
+}
+
+/**
+ * calc_data_growth - calculate approximate amount of new data from budgeting
+ * request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ */
+static int calc_data_growth(const struct ubifs_info *c,
+                           const struct ubifs_budget_req *req)
+{
+       int data_growth;
+
+       data_growth = req->new_ino  ? c->inode_budget : 0;
+       if (req->new_page)
+               data_growth += c->page_budget;
+       if (req->new_dent)
+               data_growth += c->dent_budget;
+       data_growth += req->new_ino_d;
+       return data_growth;
+}
+
+/**
+ * calc_dd_growth - calculate approximate amount of data which makes other data
+ * dirty from budgeting request.
+ * @c: UBIFS file-system description object
+ * @req: budgeting request
+ */
+static int calc_dd_growth(const struct ubifs_info *c,
+                         const struct ubifs_budget_req *req)
+{
+       int dd_growth;
+
+       dd_growth = req->dirtied_page ? c->page_budget : 0;
+
+       if (req->dirtied_ino)
+               dd_growth += c->inode_budget << (req->dirtied_ino - 1);
+       if (req->mod_dent)
+               dd_growth += c->dent_budget;
+       dd_growth += req->dirtied_ino_d;
+       return dd_growth;
+}
+
+/**
+ * ubifs_budget_space - ensure there is enough space to complete an operation.
+ * @c: UBIFS file-system description object
+ * @req: budget request
+ *
+ * This function allocates budget for an operation. It uses pessimistic
+ * approximation of how much flash space the operation needs. The goal of this
+ * function is to make sure UBIFS always has flash space to flush all dirty
+ * pages, dirty inodes, and dirty znodes (liability). This function may force
+ * commit, garbage-collection or write-back. Returns zero in case of success,
+ * %-ENOSPC if there is no free space and other negative error codes in case of
+ * failures.
+ */
+int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req)
+{
+       int uninitialized_var(cmt_retries), uninitialized_var(wb_retries);
+       int err, idx_growth, data_growth, dd_growth;
+       struct retries_info ri;
+
+       ubifs_assert(req->dirtied_ino <= 4);
+       ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4);
+
+       data_growth = calc_data_growth(c, req);
+       dd_growth = calc_dd_growth(c, req);
+       if (!data_growth && !dd_growth)
+               return 0;
+       idx_growth = calc_idx_growth(c, req);
+       memset(&ri, 0, sizeof(struct retries_info));
+
+again:
+       spin_lock(&c->space_lock);
+       ubifs_assert(c->budg_idx_growth >= 0);
+       ubifs_assert(c->budg_data_growth >= 0);
+       ubifs_assert(c->budg_dd_growth >= 0);
+
+       if (unlikely(c->nospace) && (c->nospace_rp || !can_use_rp(c))) {
+               dbg_budg("no space");
+               spin_unlock(&c->space_lock);
+               return -ENOSPC;
+       }
+
+       c->budg_idx_growth += idx_growth;
+       c->budg_data_growth += data_growth;
+       c->budg_dd_growth += dd_growth;
+
+       err = do_budget_space(c);
+       if (likely(!err)) {
+               req->idx_growth = idx_growth;
+               req->data_growth = data_growth;
+               req->dd_growth = dd_growth;
+               spin_unlock(&c->space_lock);
+               return 0;
+       }
+
+       /* Restore the old values */
+       c->budg_idx_growth -= idx_growth;
+       c->budg_data_growth -= data_growth;
+       c->budg_dd_growth -= dd_growth;
+       spin_unlock(&c->space_lock);
+
+       if (req->fast) {
+               dbg_budg("no space for fast budgeting");
+               return err;
+       }
+
+       err = make_free_space(c, &ri);
+       if (err == -EAGAIN) {
+               dbg_budg("try again");
+               cond_resched();
+               goto again;
+       } else if (err == -ENOSPC) {
+               dbg_budg("FS is full, -ENOSPC");
+               c->nospace = 1;
+               if (can_use_rp(c) || c->rp_size == 0)
+                       c->nospace_rp = 1;
+               smp_wmb();
+       } else
+               ubifs_err("cannot budget space, error %d", err);
+       return err;
+}
+
+/**
+ * ubifs_release_budget - release budgeted free space.
+ * @c: UBIFS file-system description object
+ * @req: budget request
+ *
+ * This function releases the space budgeted by 'ubifs_budget_space()'. Note,
+ * since the index changes (which were budgeted for in @req->idx_growth) will
+ * only be written to the media on commit, this function moves the index budget
+ * from @c->budg_idx_growth to @c->budg_uncommitted_idx. The latter will be
+ * zeroed by the commit operation.
+ */
+void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req)
+{
+       ubifs_assert(req->dirtied_ino <= 4);
+       ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4);
+       if (!req->recalculate) {
+               ubifs_assert(req->idx_growth >= 0);
+               ubifs_assert(req->data_growth >= 0);
+               ubifs_assert(req->dd_growth >= 0);
+       }
+
+       if (req->recalculate) {
+               req->data_growth = calc_data_growth(c, req);
+               req->dd_growth = calc_dd_growth(c, req);
+               req->idx_growth = calc_idx_growth(c, req);
+       }
+
+       if (!req->data_growth && !req->dd_growth)
+               return;
+
+       c->nospace = c->nospace_rp = 0;
+       smp_wmb();
+
+       spin_lock(&c->space_lock);
+       c->budg_idx_growth -= req->idx_growth;
+       c->budg_uncommitted_idx += req->idx_growth;
+       c->budg_data_growth -= req->data_growth;
+       c->budg_dd_growth -= req->dd_growth;
+       c->min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+       ubifs_assert(c->budg_idx_growth >= 0);
+       ubifs_assert(c->budg_data_growth >= 0);
+       ubifs_assert(c->min_idx_lebs < c->main_lebs);
+       spin_unlock(&c->space_lock);
+}
+
+/**
+ * ubifs_convert_page_budget - convert budget of a new page.
+ * @c: UBIFS file-system description object
+ *
+ * This function converts budget which was allocated for a new page of data to
+ * the budget of changing an existing page of data. The latter is smaller then
+ * the former, so this function only does simple re-calculation and does not
+ * involve any write-back.
+ */
+void ubifs_convert_page_budget(struct ubifs_info *c)
+{
+       spin_lock(&c->space_lock);
+       /* Release the index growth reservation */
+       c->budg_idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT;
+       /* Release the data growth reservation */
+       c->budg_data_growth -= c->page_budget;
+       /* Increase the dirty data growth reservation instead */
+       c->budg_dd_growth += c->page_budget;
+       /* And re-calculate the indexing space reservation */
+       c->min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+       spin_unlock(&c->space_lock);
+}
+
+/**
+ * ubifs_release_dirty_inode_budget - release dirty inode budget.
+ * @c: UBIFS file-system description object
+ * @ui: UBIFS inode to release the budget for
+ *
+ * This function releases budget corresponding to a dirty inode. It is usually
+ * called when after the inode has been written to the media and marked as
+ * clean.
+ */
+void ubifs_release_dirty_inode_budget(struct ubifs_info *c,
+                                     struct ubifs_inode *ui)
+{
+       struct ubifs_budget_req req = {.dd_growth = c->inode_budget,
+                                      .dirtied_ino_d = ui->data_len};
+
+       ubifs_release_budget(c, &req);
+}
+
+/**
+ * ubifs_budg_get_free_space - return amount of free space.
+ * @c: UBIFS file-system description object
+ *
+ * This function returns amount of free space on the file-system.
+ */
+long long ubifs_budg_get_free_space(struct ubifs_info *c)
+{
+       int min_idx_lebs, rsvd_idx_lebs;
+       long long available, outstanding, free;
+
+       /* Do exactly the same calculations as in 'do_budget_space()' */
+       spin_lock(&c->space_lock);
+       min_idx_lebs = ubifs_calc_min_idx_lebs(c);
+
+       if (min_idx_lebs > c->lst.idx_lebs)
+               rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs;
+       else
+               rsvd_idx_lebs = 0;
+
+       if (rsvd_idx_lebs > c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt
+                               - c->lst.taken_empty_lebs) {
+               spin_unlock(&c->space_lock);
+               return 0;
+       }
+
+       available = ubifs_calc_available(c, min_idx_lebs);
+       outstanding = c->budg_data_growth + c->budg_dd_growth;
+       c->min_idx_lebs = min_idx_lebs;
+       spin_unlock(&c->space_lock);
+
+       if (available > outstanding)
+               free = ubifs_reported_space(c, available - outstanding);
+       else
+               free = 0;
+       return free;
+}
diff --git a/fs/ubifs/commit.c b/fs/ubifs/commit.c
new file mode 100644 (file)
index 0000000..3b51631
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * 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 */
diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
new file mode 100644 (file)
index 0000000..5bb51da
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * 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);
+}
diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c
new file mode 100644 (file)
index 0000000..4e3aaeb
--- /dev/null
@@ -0,0 +1,2289 @@
+/*
+ * 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"
+#include <linux/module.h>
+#include <linux/moduleparam.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_fmt(int fmt)
+{
+       switch (fmt) {
+       case UBIFS_SIMPLE_KEY_FMT:
+               return "simple";
+       default:
+               return "unknown/invalid format";
+       }
+}
+
+static const char *get_key_hash(int hash)
+{
+       switch (hash) {
+       case UBIFS_KEY_HASH_R5:
+               return "R5";
+       case UBIFS_KEY_HASH_TEST:
+               return "test";
+       default:
+               return "unknown/invalid name hash";
+       }
+}
+
+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)", key_inum(c, key),
+                              get_key_type(type));
+                       break;
+               case UBIFS_DENT_KEY:
+               case UBIFS_XENT_KEY:
+                       sprintf(p, "(%lu, %s, %#08x)", key_inum(c, key),
+                               get_key_type(type), key_hash(c, key));
+                       break;
+               case UBIFS_DATA_KEY:
+                       sprintf(p, "(%lu, %s, %u)", key_inum(c, key),
+                               get_key_type(type), key_block(c, key));
+                       break;
+               case UBIFS_TRUN_KEY:
+                       sprintf(p, "(%lu, %s)",
+                               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;
+}
+
+const char *dbg_ntype(int type)
+{
+       switch (type) {
+       case UBIFS_PAD_NODE:
+               return "padding node";
+       case UBIFS_SB_NODE:
+               return "superblock node";
+       case UBIFS_MST_NODE:
+               return "master node";
+       case UBIFS_REF_NODE:
+               return "reference node";
+       case UBIFS_INO_NODE:
+               return "inode node";
+       case UBIFS_DENT_NODE:
+               return "direntry node";
+       case UBIFS_XENT_NODE:
+               return "xentry node";
+       case UBIFS_DATA_NODE:
+               return "data node";
+       case UBIFS_TRUN_NODE:
+               return "truncate node";
+       case UBIFS_IDX_NODE:
+               return "indexing node";
+       case UBIFS_CS_NODE:
+               return "commit start node";
+       case UBIFS_ORPH_NODE:
+               return "orphan node";
+       default:
+               return "unknown node";
+       }
+}
+
+static const char *dbg_gtype(int type)
+{
+       switch (type) {
+       case UBIFS_NO_NODE_GROUP:
+               return "no node group";
+       case UBIFS_IN_NODE_GROUP:
+               return "in node group";
+       case UBIFS_LAST_OF_NODE_GROUP:
+               return "last of node group";
+       default:
+               return "unknown";
+       }
+}
+
+const char *dbg_cstate(int cmt_state)
+{
+       switch (cmt_state) {
+       case COMMIT_RESTING:
+               return "commit resting";
+       case COMMIT_BACKGROUND:
+               return "background commit requested";
+       case COMMIT_REQUIRED:
+               return "commit required";
+       case COMMIT_RUNNING_BACKGROUND:
+               return "BACKGROUND commit running";
+       case COMMIT_RUNNING_REQUIRED:
+               return "commit running and required";
+       case COMMIT_BROKEN:
+               return "broken commit";
+       default:
+               return "unknown commit state";
+       }
+}
+
+static void dump_ch(const struct ubifs_ch *ch)
+{
+       printk(KERN_DEBUG "\tmagic          %#x\n", le32_to_cpu(ch->magic));
+       printk(KERN_DEBUG "\tcrc            %#x\n", le32_to_cpu(ch->crc));
+       printk(KERN_DEBUG "\tnode_type      %d (%s)\n", ch->node_type,
+              dbg_ntype(ch->node_type));
+       printk(KERN_DEBUG "\tgroup_type     %d (%s)\n", ch->group_type,
+              dbg_gtype(ch->group_type));
+       printk(KERN_DEBUG "\tsqnum          %llu\n",
+              (unsigned long long)le64_to_cpu(ch->sqnum));
+       printk(KERN_DEBUG "\tlen            %u\n", le32_to_cpu(ch->len));
+}
+
+void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode)
+{
+       const struct ubifs_inode *ui = ubifs_inode(inode);
+
+       printk(KERN_DEBUG "inode      %lu\n", inode->i_ino);
+       printk(KERN_DEBUG "size       %llu\n",
+              (unsigned long long)i_size_read(inode));
+       printk(KERN_DEBUG "nlink      %u\n", inode->i_nlink);
+       printk(KERN_DEBUG "uid        %u\n", (unsigned int)inode->i_uid);
+       printk(KERN_DEBUG "gid        %u\n", (unsigned int)inode->i_gid);
+       printk(KERN_DEBUG "atime      %u.%u\n",
+              (unsigned int)inode->i_atime.tv_sec,
+              (unsigned int)inode->i_atime.tv_nsec);
+       printk(KERN_DEBUG "mtime      %u.%u\n",
+              (unsigned int)inode->i_mtime.tv_sec,
+              (unsigned int)inode->i_mtime.tv_nsec);
+       printk(KERN_DEBUG "ctime       %u.%u\n",
+              (unsigned int)inode->i_ctime.tv_sec,
+              (unsigned int)inode->i_ctime.tv_nsec);
+       printk(KERN_DEBUG "creat_sqnum %llu\n", ui->creat_sqnum);
+       printk(KERN_DEBUG "xattr_size  %u\n", ui->xattr_size);
+       printk(KERN_DEBUG "xattr_cnt   %u\n", ui->xattr_cnt);
+       printk(KERN_DEBUG "xattr_names %u\n", ui->xattr_names);
+       printk(KERN_DEBUG "dirty       %u\n", ui->dirty);
+       printk(KERN_DEBUG "xattr       %u\n", ui->xattr);
+       printk(KERN_DEBUG "flags       %d\n", ui->flags);
+       printk(KERN_DEBUG "compr_type  %d\n", ui->compr_type);
+       printk(KERN_DEBUG "data_len    %d\n", ui->data_len);
+}
+
+void dbg_dump_node(const struct ubifs_info *c, const void *node)
+{
+       int i, n;
+       union ubifs_key key;
+       const struct ubifs_ch *ch = node;
+
+       if (dbg_failure_mode)
+               return;
+
+       /* If the magic is incorrect, just hexdump the first bytes */
+       if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) {
+               printk(KERN_DEBUG "Not a node, first %zu bytes:", UBIFS_CH_SZ);
+               print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+                              (void *)node, UBIFS_CH_SZ, 1);
+               return;
+       }
+
+       spin_lock(&dbg_lock);
+       dump_ch(node);
+
+       switch (ch->node_type) {
+       case UBIFS_PAD_NODE:
+       {
+               const struct ubifs_pad_node *pad = node;
+
+               printk(KERN_DEBUG "\tpad_len        %u\n",
+                      le32_to_cpu(pad->pad_len));
+               break;
+       }
+       case UBIFS_SB_NODE:
+       {
+               const struct ubifs_sb_node *sup = node;
+               unsigned int sup_flags = le32_to_cpu(sup->flags);
+
+               printk(KERN_DEBUG "\tkey_hash       %d (%s)\n",
+                      (int)sup->key_hash, get_key_hash(sup->key_hash));
+               printk(KERN_DEBUG "\tkey_fmt        %d (%s)\n",
+                      (int)sup->key_fmt, get_key_fmt(sup->key_fmt));
+               printk(KERN_DEBUG "\tflags          %#x\n", sup_flags);
+               printk(KERN_DEBUG "\t  big_lpt      %u\n",
+                      !!(sup_flags & UBIFS_FLG_BIGLPT));
+               printk(KERN_DEBUG "\tmin_io_size    %u\n",
+                      le32_to_cpu(sup->min_io_size));
+               printk(KERN_DEBUG "\tleb_size       %u\n",
+                      le32_to_cpu(sup->leb_size));
+               printk(KERN_DEBUG "\tleb_cnt        %u\n",
+                      le32_to_cpu(sup->leb_cnt));
+               printk(KERN_DEBUG "\tmax_leb_cnt    %u\n",
+                      le32_to_cpu(sup->max_leb_cnt));
+               printk(KERN_DEBUG "\tmax_bud_bytes  %llu\n",
+                      (unsigned long long)le64_to_cpu(sup->max_bud_bytes));
+               printk(KERN_DEBUG "\tlog_lebs       %u\n",
+                      le32_to_cpu(sup->log_lebs));
+               printk(KERN_DEBUG "\tlpt_lebs       %u\n",
+                      le32_to_cpu(sup->lpt_lebs));
+               printk(KERN_DEBUG "\torph_lebs      %u\n",
+                      le32_to_cpu(sup->orph_lebs));
+               printk(KERN_DEBUG "\tjhead_cnt      %u\n",
+                      le32_to_cpu(sup->jhead_cnt));
+               printk(KERN_DEBUG "\tfanout         %u\n",
+                      le32_to_cpu(sup->fanout));
+               printk(KERN_DEBUG "\tlsave_cnt      %u\n",
+                      le32_to_cpu(sup->lsave_cnt));
+               printk(KERN_DEBUG "\tdefault_compr  %u\n",
+                      (int)le16_to_cpu(sup->default_compr));
+               printk(KERN_DEBUG "\trp_size        %llu\n",
+                      (unsigned long long)le64_to_cpu(sup->rp_size));
+               printk(KERN_DEBUG "\trp_uid         %u\n",
+                      le32_to_cpu(sup->rp_uid));
+               printk(KERN_DEBUG "\trp_gid         %u\n",
+                      le32_to_cpu(sup->rp_gid));
+               printk(KERN_DEBUG "\tfmt_version    %u\n",
+                      le32_to_cpu(sup->fmt_version));
+               printk(KERN_DEBUG "\ttime_gran      %u\n",
+                      le32_to_cpu(sup->time_gran));
+               printk(KERN_DEBUG "\tUUID           %02X%02X%02X%02X-%02X%02X"
+                      "-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
+                      sup->uuid[0], sup->uuid[1], sup->uuid[2], sup->uuid[3],
+                      sup->uuid[4], sup->uuid[5], sup->uuid[6], sup->uuid[7],
+                      sup->uuid[8], sup->uuid[9], sup->uuid[10], sup->uuid[11],
+                      sup->uuid[12], sup->uuid[13], sup->uuid[14],
+                      sup->uuid[15]);
+               break;
+       }
+       case UBIFS_MST_NODE:
+       {
+               const struct ubifs_mst_node *mst = node;
+
+               printk(KERN_DEBUG "\thighest_inum   %llu\n",
+                      (unsigned long long)le64_to_cpu(mst->highest_inum));
+               printk(KERN_DEBUG "\tcommit number  %llu\n",
+                      (unsigned long long)le64_to_cpu(mst->cmt_no));
+               printk(KERN_DEBUG "\tflags          %#x\n",
+                      le32_to_cpu(mst->flags));
+               printk(KERN_DEBUG "\tlog_lnum       %u\n",
+                      le32_to_cpu(mst->log_lnum));
+               printk(KERN_DEBUG "\troot_lnum      %u\n",
+                      le32_to_cpu(mst->root_lnum));
+               printk(KERN_DEBUG "\troot_offs      %u\n",
+                      le32_to_cpu(mst->root_offs));
+               printk(KERN_DEBUG "\troot_len       %u\n",
+                      le32_to_cpu(mst->root_len));
+               printk(KERN_DEBUG "\tgc_lnum        %u\n",
+                      le32_to_cpu(mst->gc_lnum));
+               printk(KERN_DEBUG "\tihead_lnum     %u\n",
+                      le32_to_cpu(mst->ihead_lnum));
+               printk(KERN_DEBUG "\tihead_offs     %u\n",
+                      le32_to_cpu(mst->ihead_offs));
+               printk(KERN_DEBUG "\tindex_size     %u\n",
+                      le32_to_cpu(mst->index_size));
+               printk(KERN_DEBUG "\tlpt_lnum       %u\n",
+                      le32_to_cpu(mst->lpt_lnum));
+               printk(KERN_DEBUG "\tlpt_offs       %u\n",
+                      le32_to_cpu(mst->lpt_offs));
+               printk(KERN_DEBUG "\tnhead_lnum     %u\n",
+                      le32_to_cpu(mst->nhead_lnum));
+               printk(KERN_DEBUG "\tnhead_offs     %u\n",
+                      le32_to_cpu(mst->nhead_offs));
+               printk(KERN_DEBUG "\tltab_lnum      %u\n",
+                      le32_to_cpu(mst->ltab_lnum));
+               printk(KERN_DEBUG "\tltab_offs      %u\n",
+                      le32_to_cpu(mst->ltab_offs));
+               printk(KERN_DEBUG "\tlsave_lnum     %u\n",
+                      le32_to_cpu(mst->lsave_lnum));
+               printk(KERN_DEBUG "\tlsave_offs     %u\n",
+                      le32_to_cpu(mst->lsave_offs));
+               printk(KERN_DEBUG "\tlscan_lnum     %u\n",
+                      le32_to_cpu(mst->lscan_lnum));
+               printk(KERN_DEBUG "\tleb_cnt        %u\n",
+                      le32_to_cpu(mst->leb_cnt));
+               printk(KERN_DEBUG "\tempty_lebs     %u\n",
+                      le32_to_cpu(mst->empty_lebs));
+               printk(KERN_DEBUG "\tidx_lebs       %u\n",
+                      le32_to_cpu(mst->idx_lebs));
+               printk(KERN_DEBUG "\ttotal_free     %llu\n",
+                      (unsigned long long)le64_to_cpu(mst->total_free));
+               printk(KERN_DEBUG "\ttotal_dirty    %llu\n",
+                      (unsigned long long)le64_to_cpu(mst->total_dirty));
+               printk(KERN_DEBUG "\ttotal_used     %llu\n",
+                      (unsigned long long)le64_to_cpu(mst->total_used));
+               printk(KERN_DEBUG "\ttotal_dead     %llu\n",
+                      (unsigned long long)le64_to_cpu(mst->total_dead));
+               printk(KERN_DEBUG "\ttotal_dark     %llu\n",
+                      (unsigned long long)le64_to_cpu(mst->total_dark));
+               break;
+       }
+       case UBIFS_REF_NODE:
+       {
+               const struct ubifs_ref_node *ref = node;
+
+               printk(KERN_DEBUG "\tlnum           %u\n",
+                      le32_to_cpu(ref->lnum));
+               printk(KERN_DEBUG "\toffs           %u\n",
+                      le32_to_cpu(ref->offs));
+               printk(KERN_DEBUG "\tjhead          %u\n",
+                      le32_to_cpu(ref->jhead));
+               break;
+       }
+       case UBIFS_INO_NODE:
+       {
+               const struct ubifs_ino_node *ino = node;
+
+               key_read(c, &ino->key, &key);
+               printk(KERN_DEBUG "\tkey            %s\n", DBGKEY(&key));
+               printk(KERN_DEBUG "\tcreat_sqnum    %llu\n",
+                      (unsigned long long)le64_to_cpu(ino->creat_sqnum));
+               printk(KERN_DEBUG "\tsize           %llu\n",
+                      (unsigned long long)le64_to_cpu(ino->size));
+               printk(KERN_DEBUG "\tnlink          %u\n",
+                      le32_to_cpu(ino->nlink));
+               printk(KERN_DEBUG "\tatime          %lld.%u\n",
+                      (long long)le64_to_cpu(ino->atime_sec),
+                      le32_to_cpu(ino->atime_nsec));
+               printk(KERN_DEBUG "\tmtime          %lld.%u\n",
+                      (long long)le64_to_cpu(ino->mtime_sec),
+                      le32_to_cpu(ino->mtime_nsec));
+               printk(KERN_DEBUG "\tctime          %lld.%u\n",
+                      (long long)le64_to_cpu(ino->ctime_sec),
+                      le32_to_cpu(ino->ctime_nsec));
+               printk(KERN_DEBUG "\tuid            %u\n",
+                      le32_to_cpu(ino->uid));
+               printk(KERN_DEBUG "\tgid            %u\n",
+                      le32_to_cpu(ino->gid));
+               printk(KERN_DEBUG "\tmode           %u\n",
+                      le32_to_cpu(ino->mode));
+               printk(KERN_DEBUG "\tflags          %#x\n",
+                      le32_to_cpu(ino->flags));
+               printk(KERN_DEBUG "\txattr_cnt      %u\n",
+                      le32_to_cpu(ino->xattr_cnt));
+               printk(KERN_DEBUG "\txattr_size     %u\n",
+                      le32_to_cpu(ino->xattr_size));
+               printk(KERN_DEBUG "\txattr_names    %u\n",
+                      le32_to_cpu(ino->xattr_names));
+               printk(KERN_DEBUG "\tcompr_type     %#x\n",
+                      (int)le16_to_cpu(ino->compr_type));
+               printk(KERN_DEBUG "\tdata len       %u\n",
+                      le32_to_cpu(ino->data_len));
+               break;
+       }
+       case UBIFS_DENT_NODE:
+       case UBIFS_XENT_NODE:
+       {
+               const struct ubifs_dent_node *dent = node;
+               int nlen = le16_to_cpu(dent->nlen);
+
+               key_read(c, &dent->key, &key);
+               printk(KERN_DEBUG "\tkey            %s\n", DBGKEY(&key));
+               printk(KERN_DEBUG "\tinum           %llu\n",
+                      (unsigned long long)le64_to_cpu(dent->inum));
+               printk(KERN_DEBUG "\ttype           %d\n", (int)dent->type);
+               printk(KERN_DEBUG "\tnlen           %d\n", nlen);
+               printk(KERN_DEBUG "\tname           ");
+
+               if (nlen > UBIFS_MAX_NLEN)
+                       printk(KERN_DEBUG "(bad name length, not printing, "
+                                         "bad or corrupted node)");
+               else {
+                       for (i = 0; i < nlen && dent->name[i]; i++)
+                               printk("%c", dent->name[i]);
+               }
+               printk("\n");
+
+               break;
+       }
+       case UBIFS_DATA_NODE:
+       {
+               const struct ubifs_data_node *dn = node;
+               int dlen = le32_to_cpu(ch->len) - UBIFS_DATA_NODE_SZ;
+
+               key_read(c, &dn->key, &key);
+               printk(KERN_DEBUG "\tkey            %s\n", DBGKEY(&key));
+               printk(KERN_DEBUG "\tsize           %u\n",
+                      le32_to_cpu(dn->size));
+               printk(KERN_DEBUG "\tcompr_typ      %d\n",
+                      (int)le16_to_cpu(dn->compr_type));
+               printk(KERN_DEBUG "\tdata size      %d\n",
+                      dlen);
+               printk(KERN_DEBUG "\tdata:\n");
+               print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET, 32, 1,
+                              (void *)&dn->data, dlen, 0);
+               break;
+       }
+       case UBIFS_TRUN_NODE:
+       {
+               const struct ubifs_trun_node *trun = node;
+
+               printk(KERN_DEBUG "\tinum           %u\n",
+                      le32_to_cpu(trun->inum));
+               printk(KERN_DEBUG "\told_size       %llu\n",
+                      (unsigned long long)le64_to_cpu(trun->old_size));
+               printk(KERN_DEBUG "\tnew_size       %llu\n",
+                      (unsigned long long)le64_to_cpu(trun->new_size));
+               break;
+       }
+       case UBIFS_IDX_NODE:
+       {
+               const struct ubifs_idx_node *idx = node;
+
+               n = le16_to_cpu(idx->child_cnt);
+               printk(KERN_DEBUG "\tchild_cnt      %d\n", n);
+               printk(KERN_DEBUG "\tlevel          %d\n",
+                      (int)le16_to_cpu(idx->level));
+               printk(KERN_DEBUG "\tBranches:\n");
+
+               for (i = 0; i < n && i < c->fanout - 1; i++) {
+                       const struct ubifs_branch *br;
+
+                       br = ubifs_idx_branch(c, idx, i);
+                       key_read(c, &br->key, &key);
+                       printk(KERN_DEBUG "\t%d: LEB %d:%d len %d key %s\n",
+                              i, le32_to_cpu(br->lnum), le32_to_cpu(br->offs),
+                              le32_to_cpu(br->len), DBGKEY(&key));
+               }
+               break;
+       }
+       case UBIFS_CS_NODE:
+               break;
+       case UBIFS_ORPH_NODE:
+       {
+               const struct ubifs_orph_node *orph = node;
+
+               printk(KERN_DEBUG "\tcommit number  %llu\n",
+                      (unsigned long long)
+                               le64_to_cpu(orph->cmt_no) & LLONG_MAX);
+               printk(KERN_DEBUG "\tlast node flag %llu\n",
+                      (unsigned long long)(le64_to_cpu(orph->cmt_no)) >> 63);
+               n = (le32_to_cpu(ch->len) - UBIFS_ORPH_NODE_SZ) >> 3;
+               printk(KERN_DEBUG "\t%d orphan inode numbers:\n", n);
+               for (i = 0; i < n; i++)
+                       printk(KERN_DEBUG "\t  ino %llu\n",
+                              le64_to_cpu(orph->inos[i]));
+               break;
+       }
+       default:
+               printk(KERN_DEBUG "node type %d was not recognized\n",
+                      (int)ch->node_type);
+       }
+       spin_unlock(&dbg_lock);
+}
+
+void dbg_dump_budget_req(const struct ubifs_budget_req *req)
+{
+       spin_lock(&dbg_lock);
+       printk(KERN_DEBUG "Budgeting request: new_ino %d, dirtied_ino %d\n",
+              req->new_ino, req->dirtied_ino);
+       printk(KERN_DEBUG "\tnew_ino_d   %d, dirtied_ino_d %d\n",
+              req->new_ino_d, req->dirtied_ino_d);
+       printk(KERN_DEBUG "\tnew_page    %d, dirtied_page %d\n",
+              req->new_page, req->dirtied_page);
+       printk(KERN_DEBUG "\tnew_dent    %d, mod_dent     %d\n",
+              req->new_dent, req->mod_dent);
+       printk(KERN_DEBUG "\tidx_growth  %d\n", req->idx_growth);
+       printk(KERN_DEBUG "\tdata_growth %d dd_growth     %d\n",
+              req->data_growth, req->dd_growth);
+       spin_unlock(&dbg_lock);
+}
+
+void dbg_dump_lstats(const struct ubifs_lp_stats *lst)
+{
+       spin_lock(&dbg_lock);
+       printk(KERN_DEBUG "Lprops statistics: empty_lebs %d, idx_lebs  %d\n",
+              lst->empty_lebs, lst->idx_lebs);
+       printk(KERN_DEBUG "\ttaken_empty_lebs %d, total_free %lld, "
+              "total_dirty %lld\n", lst->taken_empty_lebs, lst->total_free,
+              lst->total_dirty);
+       printk(KERN_DEBUG "\ttotal_used %lld, total_dark %lld, "
+              "total_dead %lld\n", lst->total_used, lst->total_dark,
+              lst->total_dead);
+       spin_unlock(&dbg_lock);
+}
+
+void dbg_dump_budg(struct ubifs_info *c)
+{
+       int i;
+       struct rb_node *rb;
+       struct ubifs_bud *bud;
+       struct ubifs_gced_idx_leb *idx_gc;
+
+       spin_lock(&dbg_lock);
+       printk(KERN_DEBUG "Budgeting info: budg_data_growth %lld, "
+              "budg_dd_growth %lld, budg_idx_growth %lld\n",
+              c->budg_data_growth, c->budg_dd_growth, c->budg_idx_growth);
+       printk(KERN_DEBUG "\tdata budget sum %lld, total budget sum %lld, "
+              "freeable_cnt %d\n", c->budg_data_growth + c->budg_dd_growth,
+              c->budg_data_growth + c->budg_dd_growth + c->budg_idx_growth,
+              c->freeable_cnt);
+       printk(KERN_DEBUG "\tmin_idx_lebs %d, old_idx_sz %lld, "
+              "calc_idx_sz %lld, idx_gc_cnt %d\n", c->min_idx_lebs,
+              c->old_idx_sz, c->calc_idx_sz, c->idx_gc_cnt);
+       printk(KERN_DEBUG "\tdirty_pg_cnt %ld, dirty_zn_cnt %ld, "
+              "clean_zn_cnt %ld\n", atomic_long_read(&c->dirty_pg_cnt),
+              atomic_long_read(&c->dirty_zn_cnt),
+              atomic_long_read(&c->clean_zn_cnt));
+       printk(KERN_DEBUG "\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n",
+              c->dark_wm, c->dead_wm, c->max_idx_node_sz);
+       printk(KERN_DEBUG "\tgc_lnum %d, ihead_lnum %d\n",
+              c->gc_lnum, c->ihead_lnum);
+       for (i = 0; i < c->jhead_cnt; i++)
+               printk(KERN_DEBUG "\tjhead %d\t LEB %d\n",
+                      c->jheads[i].wbuf.jhead, c->jheads[i].wbuf.lnum);
+       for (rb = rb_first(&c->buds); rb; rb = rb_next(rb)) {
+               bud = rb_entry(rb, struct ubifs_bud, rb);
+               printk(KERN_DEBUG "\tbud LEB %d\n", bud->lnum);
+       }
+       list_for_each_entry(bud, &c->old_buds, list)
+               printk(KERN_DEBUG "\told bud LEB %d\n", bud->lnum);
+       list_for_each_entry(idx_gc, &c->idx_gc, list)
+               printk(KERN_DEBUG "\tGC'ed idx LEB %d unmap %d\n",
+                      idx_gc->lnum, idx_gc->unmap);
+       printk(KERN_DEBUG "\tcommit state %d\n", c->cmt_state);
+       spin_unlock(&dbg_lock);
+}
+
+void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
+{
+       printk(KERN_DEBUG "LEB %d lprops: free %d, dirty %d (used %d), "
+              "flags %#x\n", lp->lnum, lp->free, lp->dirty,
+              c->leb_size - lp->free - lp->dirty, lp->flags);
+}
+
+void dbg_dump_lprops(struct ubifs_info *c)
+{
+       int lnum, err;
+       struct ubifs_lprops lp;
+       struct ubifs_lp_stats lst;
+
+       printk(KERN_DEBUG "Dumping LEB properties\n");
+       ubifs_get_lp_stats(c, &lst);
+       dbg_dump_lstats(&lst);
+
+       for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
+               err = ubifs_read_one_lp(c, lnum, &lp);
+               if (err)
+                       ubifs_err("cannot read lprops for LEB %d", lnum);
+
+               dbg_dump_lprop(c, &lp);
+       }
+}
+
+void dbg_dump_leb(const struct ubifs_info *c, int lnum)
+{
+       struct ubifs_scan_leb *sleb;
+       struct ubifs_scan_node *snod;
+
+       if (dbg_failure_mode)
+               return;
+
+       printk(KERN_DEBUG "Dumping LEB %d\n", lnum);
+
+       sleb = ubifs_scan(c, lnum, 0, c->dbg_buf);
+       if (IS_ERR(sleb)) {
+               ubifs_err("scan error %d", (int)PTR_ERR(sleb));
+               return;
+       }
+
+       printk(KERN_DEBUG "LEB %d has %d nodes ending at %d\n", lnum,
+              sleb->nodes_cnt, sleb->endpt);
+
+       list_for_each_entry(snod, &sleb->nodes, list) {
+               cond_resched();
+               printk(KERN_DEBUG "Dumping node at LEB %d:%d len %d\n", lnum,
+                      snod->offs, snod->len);
+               dbg_dump_node(c, snod->node);
+       }
+
+       ubifs_scan_destroy(sleb);
+       return;
+}
+
+void dbg_dump_znode(const struct ubifs_info *c,
+                   const struct ubifs_znode *znode)
+{
+       int n;
+       const struct ubifs_zbranch *zbr;
+
+       spin_lock(&dbg_lock);
+       if (znode->parent)
+               zbr = &znode->parent->zbranch[znode->iip];
+       else
+               zbr = &c->zroot;
+
+       printk(KERN_DEBUG "znode %p, LEB %d:%d len %d parent %p iip %d level %d"
+              " child_cnt %d flags %lx\n", znode, zbr->lnum, zbr->offs,
+              zbr->len, znode->parent, znode->iip, znode->level,
+              znode->child_cnt, znode->flags);
+
+       if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) {
+               spin_unlock(&dbg_lock);
+               return;
+       }
+
+       printk(KERN_DEBUG "zbranches:\n");
+       for (n = 0; n < znode->child_cnt; n++) {
+               zbr = &znode->zbranch[n];
+               if (znode->level > 0)
+                       printk(KERN_DEBUG "\t%d: znode %p LEB %d:%d len %d key "
+                                         "%s\n", n, zbr->znode, zbr->lnum,
+                                         zbr->offs, zbr->len,
+                                         DBGKEY(&zbr->key));
+               else
+                       printk(KERN_DEBUG "\t%d: LNC %p LEB %d:%d len %d key "
+                                         "%s\n", n, zbr->znode, zbr->lnum,
+                                         zbr->offs, zbr->len,
+                                         DBGKEY(&zbr->key));
+       }
+       spin_unlock(&dbg_lock);
+}
+
+void dbg_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat)
+{
+       int i;
+
+       printk(KERN_DEBUG "Dumping heap cat %d (%d elements)\n",
+              cat, heap->cnt);
+       for (i = 0; i < heap->cnt; i++) {
+               struct ubifs_lprops *lprops = heap->arr[i];
+
+               printk(KERN_DEBUG "\t%d. LEB %d hpos %d free %d dirty %d "
+                      "flags %d\n", i, lprops->lnum, lprops->hpos,
+                      lprops->free, lprops->dirty, lprops->flags);
+       }
+}
+
+void dbg_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
+                   struct ubifs_nnode *parent, int iip)
+{
+       int i;
+
+       printk(KERN_DEBUG "Dumping pnode:\n");
+       printk(KERN_DEBUG "\taddress %zx parent %zx cnext %zx\n",
+              (size_t)pnode, (size_t)parent, (size_t)pnode->cnext);
+       printk(KERN_DEBUG "\tflags %lu iip %d level %d num %d\n",
+              pnode->flags, iip, pnode->level, pnode->num);
+       for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+               struct ubifs_lprops *lp = &pnode->lprops[i];
+
+               printk(KERN_DEBUG "\t%d: free %d dirty %d flags %d lnum %d\n",
+                      i, lp->free, lp->dirty, lp->flags, lp->lnum);
+       }
+}
+
+void dbg_dump_tnc(struct ubifs_info *c)
+{
+       struct ubifs_znode *znode;
+       int level;
+
+       printk(KERN_DEBUG "\n");
+       printk(KERN_DEBUG "Dumping the TNC tree\n");
+       znode = ubifs_tnc_levelorder_next(c->zroot.znode, NULL);
+       level = znode->level;
+       printk(KERN_DEBUG "== Level %d ==\n", level);
+       while (znode) {
+               if (level != znode->level) {
+                       level = znode->level;
+                       printk(KERN_DEBUG "== Level %d ==\n", level);
+               }
+               dbg_dump_znode(c, znode);
+               znode = ubifs_tnc_levelorder_next(c->zroot.znode, znode);
+       }
+
+       printk(KERN_DEBUG "\n");
+}
+
+static int dump_znode(struct ubifs_info *c, struct ubifs_znode *znode,
+                     void *priv)
+{
+       dbg_dump_znode(c, znode);
+       return 0;
+}
+
+/**
+ * dbg_dump_index - dump the on-flash index.
+ * @c: UBIFS file-system description object
+ *
+ * This function dumps whole UBIFS indexing B-tree, unlike 'dbg_dump_tnc()'
+ * which dumps only in-memory znodes and does not read znodes which from flash.
+ */
+void dbg_dump_index(struct ubifs_info *c)
+{
+       dbg_walk_index(c, NULL, dump_znode, NULL);
+}
+
+/**
+ * dbg_check_synced_i_size - check synchronized inode size.
+ * @inode: inode to check
+ *
+ * If inode is clean, synchronized inode size has to be equivalent to current
+ * inode size. This function has to be called only for locked inodes (@i_mutex
+ * has to be locked). Returns %0 if synchronized inode size if correct, and
+ * %-EINVAL if not.
+ */
+int dbg_check_synced_i_size(struct inode *inode)
+{
+       int err = 0;
+       struct ubifs_inode *ui = ubifs_inode(inode);
+
+       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+               return 0;
+       if (!S_ISREG(inode->i_mode))
+               return 0;
+
+       mutex_lock(&ui->ui_mutex);
+       spin_lock(&ui->ui_lock);
+       if (ui->ui_size != ui->synced_i_size && !ui->dirty) {
+               ubifs_err("ui_size is %lld, synced_i_size is %lld, but inode "
+                         "is clean", ui->ui_size, ui->synced_i_size);
+               ubifs_err("i_ino %lu, i_mode %#x, i_size %lld", inode->i_ino,
+                         inode->i_mode, i_size_read(inode));
+               dbg_dump_stack();
+               err = -EINVAL;
+       }
+       spin_unlock(&ui->ui_lock);
+       mutex_unlock(&ui->ui_mutex);
+       return err;
+}
+
+/*
+ * dbg_check_dir - check directory inode size and link count.
+ * @c: UBIFS file-system description object
+ * @dir: the directory to calculate size for
+ * @size: the result is returned here
+ *
+ * This function makes sure that directory size and link count are correct.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ *
+ * Note, it is good idea to make sure the @dir->i_mutex is locked before
+ * calling this function.
+ */
+int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
+{
+       unsigned int nlink = 2;
+       union ubifs_key key;
+       struct ubifs_dent_node *dent, *pdent = NULL;
+       struct qstr nm = { .name = NULL };
+       loff_t size = UBIFS_INO_NODE_SZ;
+
+       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+               return 0;
+
+       if (!S_ISDIR(dir->i_mode))
+               return 0;
+
+       lowest_dent_key(c, &key, dir->i_ino);
+       while (1) {
+               int err;
+
+               dent = ubifs_tnc_next_ent(c, &key, &nm);
+               if (IS_ERR(dent)) {
+                       err = PTR_ERR(dent);
+                       if (err == -ENOENT)
+                               break;
+                       return err;
+               }
+
+               nm.name = dent->name;
+               nm.len = le16_to_cpu(dent->nlen);
+               size += CALC_DENT_SIZE(nm.len);
+               if (dent->type == UBIFS_ITYPE_DIR)
+                       nlink += 1;
+               kfree(pdent);
+               pdent = dent;
+               key_read(c, &dent->key, &key);
+       }
+       kfree(pdent);
+
+       if (i_size_read(dir) != size) {
+               ubifs_err("directory inode %lu has size %llu, "
+                         "but calculated size is %llu", dir->i_ino,
+                         (unsigned long long)i_size_read(dir),
+                         (unsigned long long)size);
+               dump_stack();
+               return -EINVAL;
+       }
+       if (dir->i_nlink != nlink) {
+               ubifs_err("directory inode %lu has nlink %u, but calculated "
+                         "nlink is %u", dir->i_ino, dir->i_nlink, nlink);
+               dump_stack();
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * dbg_check_key_order - make sure that colliding keys are properly ordered.
+ * @c: UBIFS file-system description object
+ * @zbr1: first zbranch
+ * @zbr2: following zbranch
+ *
+ * In UBIFS indexing B-tree colliding keys has to be sorted in binary order of
+ * names of the direntries/xentries which are referred by the keys. This
+ * function reads direntries/xentries referred by @zbr1 and @zbr2 and makes
+ * sure the name of direntry/xentry referred by @zbr1 is less than
+ * direntry/xentry referred by @zbr2. Returns zero if this is true, %1 if not,
+ * and a negative error code in case of failure.
+ */
+static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1,
+                              struct ubifs_zbranch *zbr2)
+{
+       int err, nlen1, nlen2, cmp;
+       struct ubifs_dent_node *dent1, *dent2;
+       union ubifs_key key;
+
+       ubifs_assert(!keys_cmp(c, &zbr1->key, &zbr2->key));
+       dent1 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+       if (!dent1)
+               return -ENOMEM;
+       dent2 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+       if (!dent2) {
+               err = -ENOMEM;
+               goto out_free;
+       }
+
+       err = ubifs_tnc_read_node(c, zbr1, dent1);
+       if (err)
+               goto out_free;
+       err = ubifs_validate_entry(c, dent1);
+       if (err)
+               goto out_free;
+
+       err = ubifs_tnc_read_node(c, zbr2, dent2);
+       if (err)
+               goto out_free;
+       err = ubifs_validate_entry(c, dent2);
+       if (err)
+               goto out_free;
+
+       /* Make sure node keys are the same as in zbranch */
+       err = 1;
+       key_read(c, &dent1->key, &key);
+       if (keys_cmp(c, &zbr1->key, &key)) {
+               dbg_err("1st entry at %d:%d has key %s", zbr1->lnum,
+                       zbr1->offs, DBGKEY(&key));
+               dbg_err("but it should have key %s according to tnc",
+                       DBGKEY(&zbr1->key));
+                       dbg_dump_node(c, dent1);
+                       goto out_free;
+       }
+
+       key_read(c, &dent2->key, &key);
+       if (keys_cmp(c, &zbr2->key, &key)) {
+               dbg_err("2nd entry at %d:%d has key %s", zbr1->lnum,
+                       zbr1->offs, DBGKEY(&key));
+               dbg_err("but it should have key %s according to tnc",
+                       DBGKEY(&zbr2->key));
+                       dbg_dump_node(c, dent2);
+                       goto out_free;
+       }
+
+       nlen1 = le16_to_cpu(dent1->nlen);
+       nlen2 = le16_to_cpu(dent2->nlen);
+
+       cmp = memcmp(dent1->name, dent2->name, min_t(int, nlen1, nlen2));
+       if (cmp < 0 || (cmp == 0 && nlen1 < nlen2)) {
+               err = 0;
+               goto out_free;
+       }
+       if (cmp == 0 && nlen1 == nlen2)
+               dbg_err("2 xent/dent nodes with the same name");
+       else
+               dbg_err("bad order of colliding key %s",
+                       DBGKEY(&key));
+
+       dbg_msg("first node at %d:%d\n", zbr1->lnum, zbr1->offs);
+       dbg_dump_node(c, dent1);
+       dbg_msg("second node at %d:%d\n", zbr2->lnum, zbr2->offs);
+       dbg_dump_node(c, dent2);
+
+out_free:
+       kfree(dent2);
+       kfree(dent1);
+       return err;
+}
+
+/**
+ * dbg_check_znode - check if znode is all right.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch which points to this znode
+ *
+ * This function makes sure that znode referred to by @zbr is all right.
+ * Returns zero if it is, and %-EINVAL if it is not.
+ */
+static int dbg_check_znode(struct ubifs_info *c, struct ubifs_zbranch *zbr)
+{
+       struct ubifs_znode *znode = zbr->znode;
+       struct ubifs_znode *zp = znode->parent;
+       int n, err, cmp;
+
+       if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) {
+               err = 1;
+               goto out;
+       }
+       if (znode->level < 0) {
+               err = 2;
+               goto out;
+       }
+       if (znode->iip < 0 || znode->iip >= c->fanout) {
+               err = 3;
+               goto out;
+       }
+
+       if (zbr->len == 0)
+               /* Only dirty zbranch may have no on-flash nodes */
+               if (!ubifs_zn_dirty(znode)) {
+                       err = 4;
+                       goto out;
+               }
+
+       if (ubifs_zn_dirty(znode)) {
+               /*
+                * If znode is dirty, its parent has to be dirty as well. The
+                * order of the operation is important, so we have to have
+                * memory barriers.
+                */
+               smp_mb();
+               if (zp && !ubifs_zn_dirty(zp)) {
+                       /*
+                        * The dirty flag is atomic and is cleared outside the
+                        * TNC mutex, so znode's dirty flag may now have
+                        * been cleared. The child is always cleared before the
+                        * parent, so we just need to check again.
+                        */
+                       smp_mb();
+                       if (ubifs_zn_dirty(znode)) {
+                               err = 5;
+                               goto out;
+                       }
+               }
+       }
+
+       if (zp) {
+               const union ubifs_key *min, *max;
+
+               if (znode->level != zp->level - 1) {
+                       err = 6;
+                       goto out;
+               }
+
+               /* Make sure the 'parent' pointer in our znode is correct */
+               err = ubifs_search_zbranch(c, zp, &zbr->key, &n);
+               if (!err) {
+                       /* This zbranch does not exist in the parent */
+                       err = 7;
+                       goto out;
+               }
+
+               if (znode->iip >= zp->child_cnt) {
+                       err = 8;
+                       goto out;
+               }
+
+               if (znode->iip != n) {
+                       /* This may happen only in case of collisions */
+                       if (keys_cmp(c, &zp->zbranch[n].key,
+                                    &zp->zbranch[znode->iip].key)) {
+                               err = 9;
+                               goto out;
+                       }
+                       n = znode->iip;
+               }
+
+               /*
+                * Make sure that the first key in our znode is greater than or
+                * equal to the key in the pointing zbranch.
+                */
+               min = &zbr->key;
+               cmp = keys_cmp(c, min, &znode->zbranch[0].key);
+               if (cmp == 1) {
+                       err = 10;
+                       goto out;
+               }
+
+               if (n + 1 < zp->child_cnt) {
+                       max = &zp->zbranch[n + 1].key;
+
+                       /*
+                        * Make sure the last key in our znode is less or
+                        * equivalent than the the key in zbranch which goes
+                        * after our pointing zbranch.
+                        */
+                       cmp = keys_cmp(c, max,
+                               &znode->zbranch[znode->child_cnt - 1].key);
+                       if (cmp == -1) {
+                               err = 11;
+                               goto out;
+                       }
+               }
+       } else {
+               /* This may only be root znode */
+               if (zbr != &c->zroot) {
+                       err = 12;
+                       goto out;
+               }
+       }
+
+       /*
+        * Make sure that next key is greater or equivalent then the previous
+        * one.
+        */
+       for (n = 1; n < znode->child_cnt; n++) {
+               cmp = keys_cmp(c, &znode->zbranch[n - 1].key,
+                              &znode->zbranch[n].key);
+               if (cmp > 0) {
+                       err = 13;
+                       goto out;
+               }
+               if (cmp == 0) {
+                       /* This can only be keys with colliding hash */
+                       if (!is_hash_key(c, &znode->zbranch[n].key)) {
+                               err = 14;
+                               goto out;
+                       }
+
+                       if (znode->level != 0 || c->replaying)
+                               continue;
+
+                       /*
+                        * Colliding keys should follow binary order of
+                        * corresponding xentry/dentry names.
+                        */
+                       err = dbg_check_key_order(c, &znode->zbranch[n - 1],
+                                                 &znode->zbranch[n]);
+                       if (err < 0)
+                               return err;
+                       if (err) {
+                               err = 15;
+                               goto out;
+                       }
+               }
+       }
+
+       for (n = 0; n < znode->child_cnt; n++) {
+               if (!znode->zbranch[n].znode &&
+                   (znode->zbranch[n].lnum == 0 ||
+                    znode->zbranch[n].len == 0)) {
+                       err = 16;
+                       goto out;
+               }
+
+               if (znode->zbranch[n].lnum != 0 &&
+                   znode->zbranch[n].len == 0) {
+                       err = 17;
+                       goto out;
+               }
+
+               if (znode->zbranch[n].lnum == 0 &&
+                   znode->zbranch[n].len != 0) {
+                       err = 18;
+                       goto out;
+               }
+
+               if (znode->zbranch[n].lnum == 0 &&
+                   znode->zbranch[n].offs != 0) {
+                       err = 19;
+                       goto out;
+               }
+
+               if (znode->level != 0 && znode->zbranch[n].znode)
+                       if (znode->zbranch[n].znode->parent != znode) {
+                               err = 20;
+                               goto out;
+                       }
+       }
+
+       return 0;
+
+out:
+       ubifs_err("failed, error %d", err);
+       ubifs_msg("dump of the znode");
+       dbg_dump_znode(c, znode);
+       if (zp) {
+               ubifs_msg("dump of the parent znode");
+               dbg_dump_znode(c, zp);
+       }
+       dump_stack();
+       return -EINVAL;
+}
+
+/**
+ * dbg_check_tnc - check TNC tree.
+ * @c: UBIFS file-system description object
+ * @extra: do extra checks that are possible at start commit
+ *
+ * This function traverses whole TNC tree and checks every znode. Returns zero
+ * if everything is all right and %-EINVAL if something is wrong with TNC.
+ */
+int dbg_check_tnc(struct ubifs_info *c, int extra)
+{
+       struct ubifs_znode *znode;
+       long clean_cnt = 0, dirty_cnt = 0;
+       int err, last;
+
+       if (!(ubifs_chk_flags & UBIFS_CHK_TNC))
+               return 0;
+
+       ubifs_assert(mutex_is_locked(&c->tnc_mutex));
+       if (!c->zroot.znode)
+               return 0;
+
+       znode = ubifs_tnc_postorder_first(c->zroot.znode);
+       while (1) {
+               struct ubifs_znode *prev;
+               struct ubifs_zbranch *zbr;
+
+               if (!znode->parent)
+                       zbr = &c->zroot;
+               else
+                       zbr = &znode->parent->zbranch[znode->iip];
+
+               err = dbg_check_znode(c, zbr);
+               if (err)
+                       return err;
+
+               if (extra) {
+                       if (ubifs_zn_dirty(znode))
+                               dirty_cnt += 1;
+                       else
+                               clean_cnt += 1;
+               }
+
+               prev = znode;
+               znode = ubifs_tnc_postorder_next(znode);
+               if (!znode)
+                       break;
+
+               /*
+                * If the last key of this znode is equivalent to the first key
+                * of the next znode (collision), then check order of the keys.
+                */
+               last = prev->child_cnt - 1;
+               if (prev->level == 0 && znode->level == 0 && !c->replaying &&
+                   !keys_cmp(c, &prev->zbranch[last].key,
+                             &znode->zbranch[0].key)) {
+                       err = dbg_check_key_order(c, &prev->zbranch[last],
+                                                 &znode->zbranch[0]);
+                       if (err < 0)
+                               return err;
+                       if (err) {
+                               ubifs_msg("first znode");
+                               dbg_dump_znode(c, prev);
+                               ubifs_msg("second znode");
+                               dbg_dump_znode(c, znode);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       if (extra) {
+               if (clean_cnt != atomic_long_read(&c->clean_zn_cnt)) {
+                       ubifs_err("incorrect clean_zn_cnt %ld, calculated %ld",
+                                 atomic_long_read(&c->clean_zn_cnt),
+                                 clean_cnt);
+                       return -EINVAL;
+               }
+               if (dirty_cnt != atomic_long_read(&c->dirty_zn_cnt)) {
+                       ubifs_err("incorrect dirty_zn_cnt %ld, calculated %ld",
+                                 atomic_long_read(&c->dirty_zn_cnt),
+                                 dirty_cnt);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * dbg_walk_index - walk the on-flash index.
+ * @c: UBIFS file-system description object
+ * @leaf_cb: called for each leaf node
+ * @znode_cb: called for each indexing node
+ * @priv: private date which is passed to callbacks
+ *
+ * This function walks the UBIFS index and calls the @leaf_cb for each leaf
+ * node and @znode_cb for each indexing node. Returns zero in case of success
+ * and a negative error code in case of failure.
+ *
+ * It would be better if this function removed every znode it pulled to into
+ * the TNC, so that the behavior more closely matched the non-debugging
+ * behavior.
+ */
+int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
+                  dbg_znode_callback znode_cb, void *priv)
+{
+       int err;
+       struct ubifs_zbranch *zbr;
+       struct ubifs_znode *znode, *child;
+
+       mutex_lock(&c->tnc_mutex);
+       /* If the root indexing node is not in TNC - pull it */
+       if (!c->zroot.znode) {
+               c->zroot.znode = ubifs_load_znode(c, &c->zroot, NULL, 0);
+               if (IS_ERR(c->zroot.znode)) {
+                       err = PTR_ERR(c->zroot.znode);
+                       c->zroot.znode = NULL;
+                       goto out_unlock;
+               }
+       }
+
+       /*
+        * We are going to traverse the indexing tree in the postorder manner.
+        * Go down and find the leftmost indexing node where we are going to
+        * start from.
+        */
+       znode = c->zroot.znode;
+       while (znode->level > 0) {
+               zbr = &znode->zbranch[0];
+               child = zbr->znode;
+               if (!child) {
+                       child = ubifs_load_znode(c, zbr, znode, 0);
+                       if (IS_ERR(child)) {
+                               err = PTR_ERR(child);
+                               goto out_unlock;
+                       }
+                       zbr->znode = child;
+               }
+
+               znode = child;
+       }
+
+       /* Iterate over all indexing nodes */
+       while (1) {
+               int idx;
+
+               cond_resched();
+
+               if (znode_cb) {
+                       err = znode_cb(c, znode, priv);
+                       if (err) {
+                               ubifs_err("znode checking function returned "
+                                         "error %d", err);
+                               dbg_dump_znode(c, znode);
+                               goto out_dump;
+                       }
+               }
+               if (leaf_cb && znode->level == 0) {
+                       for (idx = 0; idx < znode->child_cnt; idx++) {
+                               zbr = &znode->zbranch[idx];
+                               err = leaf_cb(c, zbr, priv);
+                               if (err) {
+                                       ubifs_err("leaf checking function "
+                                                 "returned error %d, for leaf "
+                                                 "at LEB %d:%d",
+                                                 err, zbr->lnum, zbr->offs);
+                                       goto out_dump;
+                               }
+                       }
+               }
+
+               if (!znode->parent)
+                       break;
+
+               idx = znode->iip + 1;
+               znode = znode->parent;
+               if (idx < znode->child_cnt) {
+                       /* Switch to the next index in the parent */
+                       zbr = &znode->zbranch[idx];
+                       child = zbr->znode;
+                       if (!child) {
+                               child = ubifs_load_znode(c, zbr, znode, idx);
+                               if (IS_ERR(child)) {
+                                       err = PTR_ERR(child);
+                                       goto out_unlock;
+                               }
+                               zbr->znode = child;
+                       }
+                       znode = child;
+               } else
+                       /*
+                        * This is the last child, switch to the parent and
+                        * continue.
+                        */
+                       continue;
+
+               /* Go to the lowest leftmost znode in the new sub-tree */
+               while (znode->level > 0) {
+                       zbr = &znode->zbranch[0];
+                       child = zbr->znode;
+                       if (!child) {
+                               child = ubifs_load_znode(c, zbr, znode, 0);
+                               if (IS_ERR(child)) {
+                                       err = PTR_ERR(child);
+                                       goto out_unlock;
+                               }
+                               zbr->znode = child;
+                       }
+                       znode = child;
+               }
+       }
+
+       mutex_unlock(&c->tnc_mutex);
+       return 0;
+
+out_dump:
+       if (znode->parent)
+               zbr = &znode->parent->zbranch[znode->iip];
+       else
+               zbr = &c->zroot;
+       ubifs_msg("dump of znode at LEB %d:%d", zbr->lnum, zbr->offs);
+       dbg_dump_znode(c, znode);
+out_unlock:
+       mutex_unlock(&c->tnc_mutex);
+       return err;
+}
+
+/**
+ * add_size - add znode size to partially calculated index size.
+ * @c: UBIFS file-system description object
+ * @znode: znode to add size for
+ * @priv: partially calculated index size
+ *
+ * This is a helper function for 'dbg_check_idx_size()' which is called for
+ * every indexing node and adds its size to the 'long long' variable pointed to
+ * by @priv.
+ */
+static int add_size(struct ubifs_info *c, struct ubifs_znode *znode, void *priv)
+{
+       long long *idx_size = priv;
+       int add;
+
+       add = ubifs_idx_node_sz(c, znode->child_cnt);
+       add = ALIGN(add, 8);
+       *idx_size += add;
+       return 0;
+}
+
+/**
+ * dbg_check_idx_size - check index size.
+ * @c: UBIFS file-system description object
+ * @idx_size: size to check
+ *
+ * This function walks the UBIFS index, calculates its size and checks that the
+ * size is equivalent to @idx_size. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int dbg_check_idx_size(struct ubifs_info *c, long long idx_size)
+{
+       int err;
+       long long calc = 0;
+
+       if (!(ubifs_chk_flags & UBIFS_CHK_IDX_SZ))
+               return 0;
+
+       err = dbg_walk_index(c, NULL, add_size, &calc);
+       if (err) {
+               ubifs_err("error %d while walking the index", err);
+               return err;
+       }
+
+       if (calc != idx_size) {
+               ubifs_err("index size check failed: calculated size is %lld, "
+                         "should be %lld", calc, idx_size);
+               dump_stack();
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * struct fsck_inode - information about an inode used when checking the file-system.
+ * @rb: link in the RB-tree of inodes
+ * @inum: inode number
+ * @mode: inode type, permissions, etc
+ * @nlink: inode link count
+ * @xattr_cnt: count of extended attributes
+ * @references: how many directory/xattr entries refer this inode (calculated
+ *              while walking the index)
+ * @calc_cnt: for directory inode count of child directories
+ * @size: inode size (read from on-flash inode)
+ * @xattr_sz: summary size of all extended attributes (read from on-flash
+ *            inode)
+ * @calc_sz: for directories calculated directory size
+ * @calc_xcnt: count of extended attributes
+ * @calc_xsz: calculated summary size of all extended attributes
+ * @xattr_nms: sum of lengths of all extended attribute names belonging to this
+ *             inode (read from on-flash inode)
+ * @calc_xnms: calculated sum of lengths of all extended attribute names
+ */
+struct fsck_inode {
+       struct rb_node rb;
+       ino_t inum;
+       umode_t mode;
+       unsigned int nlink;
+       unsigned int xattr_cnt;
+       int references;
+       int calc_cnt;
+       long long size;
+       unsigned int xattr_sz;
+       long long calc_sz;
+       long long calc_xcnt;
+       long long calc_xsz;
+       unsigned int xattr_nms;
+       long long calc_xnms;
+};
+
+/**
+ * struct fsck_data - private FS checking information.
+ * @inodes: RB-tree of all inodes (contains @struct fsck_inode objects)
+ */
+struct fsck_data {
+       struct rb_root inodes;
+};
+
+/**
+ * add_inode - add inode information to RB-tree of inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ * @ino: raw UBIFS inode to add
+ *
+ * This is a helper function for 'check_leaf()' which adds information about
+ * inode @ino to the RB-tree of inodes. Returns inode information pointer in
+ * case of success and a negative error code in case of failure.
+ */
+static struct fsck_inode *add_inode(struct ubifs_info *c,
+                                   struct fsck_data *fsckd,
+                                   struct ubifs_ino_node *ino)
+{
+       struct rb_node **p, *parent = NULL;
+       struct fsck_inode *fscki;
+       ino_t inum = key_inum_flash(c, &ino->key);
+
+       p = &fsckd->inodes.rb_node;
+       while (*p) {
+               parent = *p;
+               fscki = rb_entry(parent, struct fsck_inode, rb);
+               if (inum < fscki->inum)
+                       p = &(*p)->rb_left;
+               else if (inum > fscki->inum)
+                       p = &(*p)->rb_right;
+               else
+                       return fscki;
+       }
+
+       if (inum > c->highest_inum) {
+               ubifs_err("too high inode number, max. is %lu",
+                         c->highest_inum);
+               return ERR_PTR(-EINVAL);
+       }
+
+       fscki = kzalloc(sizeof(struct fsck_inode), GFP_NOFS);
+       if (!fscki)
+               return ERR_PTR(-ENOMEM);
+
+       fscki->inum = inum;
+       fscki->nlink = le32_to_cpu(ino->nlink);
+       fscki->size = le64_to_cpu(ino->size);
+       fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
+       fscki->xattr_sz = le32_to_cpu(ino->xattr_size);
+       fscki->xattr_nms = le32_to_cpu(ino->xattr_names);
+       fscki->mode = le32_to_cpu(ino->mode);
+       if (S_ISDIR(fscki->mode)) {
+               fscki->calc_sz = UBIFS_INO_NODE_SZ;
+               fscki->calc_cnt = 2;
+       }
+       rb_link_node(&fscki->rb, parent, p);
+       rb_insert_color(&fscki->rb, &fsckd->inodes);
+       return fscki;
+}
+
+/**
+ * search_inode - search inode in the RB-tree of inodes.
+ * @fsckd: FS checking information
+ * @inum: inode number to search
+ *
+ * This is a helper function for 'check_leaf()' which searches inode @inum in
+ * the RB-tree of inodes and returns an inode information pointer or %NULL if
+ * the inode was not found.
+ */
+static struct fsck_inode *search_inode(struct fsck_data *fsckd, ino_t inum)
+{
+       struct rb_node *p;
+       struct fsck_inode *fscki;
+
+       p = fsckd->inodes.rb_node;
+       while (p) {
+               fscki = rb_entry(p, struct fsck_inode, rb);
+               if (inum < fscki->inum)
+                       p = p->rb_left;
+               else if (inum > fscki->inum)
+                       p = p->rb_right;
+               else
+                       return fscki;
+       }
+       return NULL;
+}
+
+/**
+ * read_add_inode - read inode node and add it to RB-tree of inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ * @inum: inode number to read
+ *
+ * This is a helper function for 'check_leaf()' which finds inode node @inum in
+ * the index, reads it, and adds it to the RB-tree of inodes. Returns inode
+ * information pointer in case of success and a negative error code in case of
+ * failure.
+ */
+static struct fsck_inode *read_add_inode(struct ubifs_info *c,
+                                        struct fsck_data *fsckd, ino_t inum)
+{
+       int n, err;
+       union ubifs_key key;
+       struct ubifs_znode *znode;
+       struct ubifs_zbranch *zbr;
+       struct ubifs_ino_node *ino;
+       struct fsck_inode *fscki;
+
+       fscki = search_inode(fsckd, inum);
+       if (fscki)
+               return fscki;
+
+       ino_key_init(c, &key, inum);
+       err = ubifs_lookup_level0(c, &key, &znode, &n);
+       if (!err) {
+               ubifs_err("inode %lu not found in index", inum);
+               return ERR_PTR(-ENOENT);
+       } else if (err < 0) {
+               ubifs_err("error %d while looking up inode %lu", err, inum);
+               return ERR_PTR(err);
+       }
+
+       zbr = &znode->zbranch[n];
+       if (zbr->len < UBIFS_INO_NODE_SZ) {
+               ubifs_err("bad node %lu node length %d", inum, zbr->len);
+               return ERR_PTR(-EINVAL);
+       }
+
+       ino = kmalloc(zbr->len, GFP_NOFS);
+       if (!ino)
+               return ERR_PTR(-ENOMEM);
+
+       err = ubifs_tnc_read_node(c, zbr, ino);
+       if (err) {
+               ubifs_err("cannot read inode node at LEB %d:%d, error %d",
+                         zbr->lnum, zbr->offs, err);
+               kfree(ino);
+               return ERR_PTR(err);
+       }
+
+       fscki = add_inode(c, fsckd, ino);
+       kfree(ino);
+       if (IS_ERR(fscki)) {
+               ubifs_err("error %ld while adding inode %lu node",
+                         PTR_ERR(fscki), inum);
+               return fscki;
+       }
+
+       return fscki;
+}
+
+/**
+ * check_leaf - check leaf node.
+ * @c: UBIFS file-system description object
+ * @zbr: zbranch of the leaf node to check
+ * @priv: FS checking information
+ *
+ * This is a helper function for 'dbg_check_filesystem()' which is called for
+ * every single leaf node while walking the indexing tree. It checks that the
+ * leaf node referred from the indexing tree exists, has correct CRC, and does
+ * some other basic validation. This function is also responsible for building
+ * an RB-tree of inodes - it adds all inodes into the RB-tree. It also
+ * calculates reference count, size, etc for each inode in order to later
+ * compare them to the information stored inside the inodes and detect possible
+ * inconsistencies. Returns zero in case of success and a negative error code
+ * in case of failure.
+ */
+static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+                     void *priv)
+{
+       ino_t inum;
+       void *node;
+       struct ubifs_ch *ch;
+       int err, type = key_type(c, &zbr->key);
+       struct fsck_inode *fscki;
+
+       if (zbr->len < UBIFS_CH_SZ) {
+               ubifs_err("bad leaf length %d (LEB %d:%d)",
+                         zbr->len, zbr->lnum, zbr->offs);
+               return -EINVAL;
+       }
+
+       node = kmalloc(zbr->len, GFP_NOFS);
+       if (!node)
+               return -ENOMEM;
+
+       err = ubifs_tnc_read_node(c, zbr, node);
+       if (err) {
+               ubifs_err("cannot read leaf node at LEB %d:%d, error %d",
+                         zbr->lnum, zbr->offs, err);
+               goto out_free;
+       }
+
+       /* If this is an inode node, add it to RB-tree of inodes */
+       if (type == UBIFS_INO_KEY) {
+               fscki = add_inode(c, priv, node);
+               if (IS_ERR(fscki)) {
+                       err = PTR_ERR(fscki);
+                       ubifs_err("error %d while adding inode node", err);
+                       goto out_dump;
+               }
+               goto out;
+       }
+
+       if (type != UBIFS_DENT_KEY && type != UBIFS_XENT_KEY &&
+           type != UBIFS_DATA_KEY) {
+               ubifs_err("unexpected node type %d at LEB %d:%d",
+                         type, zbr->lnum, zbr->offs);
+               err = -EINVAL;
+               goto out_free;
+       }
+
+       ch = node;
+       if (le64_to_cpu(ch->sqnum) > c->max_sqnum) {
+               ubifs_err("too high sequence number, max. is %llu",
+                         c->max_sqnum);
+               err = -EINVAL;
+               goto out_dump;
+       }
+
+       if (type == UBIFS_DATA_KEY) {
+               long long blk_offs;
+               struct ubifs_data_node *dn = node;
+
+               /*
+                * Search the inode node this data node belongs to and insert
+                * it to the RB-tree of inodes.
+                */
+               inum = key_inum_flash(c, &dn->key);
+               fscki = read_add_inode(c, priv, inum);
+               if (IS_ERR(fscki)) {
+                       err = PTR_ERR(fscki);
+                       ubifs_err("error %d while processing data node and "
+                                 "trying to find inode node %lu", err, inum);
+                       goto out_dump;
+               }
+
+               /* Make sure the data node is within inode size */
+               blk_offs = key_block_flash(c, &dn->key);
+               blk_offs <<= UBIFS_BLOCK_SHIFT;
+               blk_offs += le32_to_cpu(dn->size);
+               if (blk_offs > fscki->size) {
+                       ubifs_err("data node at LEB %d:%d is not within inode "
+                                 "size %lld", zbr->lnum, zbr->offs,
+                                 fscki->size);
+                       err = -EINVAL;
+                       goto out_dump;
+               }
+       } else {
+               int nlen;
+               struct ubifs_dent_node *dent = node;
+               struct fsck_inode *fscki1;
+
+               err = ubifs_validate_entry(c, dent);
+               if (err)
+                       goto out_dump;
+
+               /*
+                * Search the inode node this entry refers to and the parent
+                * inode node and insert them to the RB-tree of inodes.
+                */
+               inum = le64_to_cpu(dent->inum);
+               fscki = read_add_inode(c, priv, inum);
+               if (IS_ERR(fscki)) {
+                       err = PTR_ERR(fscki);
+                       ubifs_err("error %d while processing entry node and "
+                                 "trying to find inode node %lu", err, inum);
+                       goto out_dump;
+               }
+
+               /* Count how many direntries or xentries refers this inode */
+               fscki->references += 1;
+
+               inum = key_inum_flash(c, &dent->key);
+               fscki1 = read_add_inode(c, priv, inum);
+               if (IS_ERR(fscki1)) {
+                       err = PTR_ERR(fscki);
+                       ubifs_err("error %d while processing entry node and "
+                                 "trying to find parent inode node %lu",
+                                 err, inum);
+                       goto out_dump;
+               }
+
+               nlen = le16_to_cpu(dent->nlen);
+               if (type == UBIFS_XENT_KEY) {
+                       fscki1->calc_xcnt += 1;
+                       fscki1->calc_xsz += CALC_DENT_SIZE(nlen);
+                       fscki1->calc_xsz += CALC_XATTR_BYTES(fscki->size);
+                       fscki1->calc_xnms += nlen;
+               } else {
+                       fscki1->calc_sz += CALC_DENT_SIZE(nlen);
+                       if (dent->type == UBIFS_ITYPE_DIR)
+                               fscki1->calc_cnt += 1;
+               }
+       }
+
+out:
+       kfree(node);
+       return 0;
+
+out_dump:
+       ubifs_msg("dump of node at LEB %d:%d", zbr->lnum, zbr->offs);
+       dbg_dump_node(c, node);
+out_free:
+       kfree(node);
+       return err;
+}
+
+/**
+ * free_inodes - free RB-tree of inodes.
+ * @fsckd: FS checking information
+ */
+static void free_inodes(struct fsck_data *fsckd)
+{
+       struct rb_node *this = fsckd->inodes.rb_node;
+       struct fsck_inode *fscki;
+
+       while (this) {
+               if (this->rb_left)
+                       this = this->rb_left;
+               else if (this->rb_right)
+                       this = this->rb_right;
+               else {
+                       fscki = rb_entry(this, struct fsck_inode, rb);
+                       this = rb_parent(this);
+                       if (this) {
+                               if (this->rb_left == &fscki->rb)
+                                       this->rb_left = NULL;
+                               else
+                                       this->rb_right = NULL;
+                       }
+                       kfree(fscki);
+               }
+       }
+}
+
+/**
+ * check_inodes - checks all inodes.
+ * @c: UBIFS file-system description object
+ * @fsckd: FS checking information
+ *
+ * This is a helper function for 'dbg_check_filesystem()' which walks the
+ * RB-tree of inodes after the index scan has been finished, and checks that
+ * inode nlink, size, etc are correct. Returns zero if inodes are fine,
+ * %-EINVAL if not, and a negative error code in case of failure.
+ */
+static int check_inodes(struct ubifs_info *c, struct fsck_data *fsckd)
+{
+       int n, err;
+       union ubifs_key key;
+       struct ubifs_znode *znode;
+       struct ubifs_zbranch *zbr;
+       struct ubifs_ino_node *ino;
+       struct fsck_inode *fscki;
+       struct rb_node *this = rb_first(&fsckd->inodes);
+
+       while (this) {
+               fscki = rb_entry(this, struct fsck_inode, rb);
+               this = rb_next(this);
+
+               if (S_ISDIR(fscki->mode)) {
+                       /*
+                        * Directories have to have exactly one reference (they
+                        * cannot have hardlinks), although root inode is an
+                        * exception.
+                        */
+                       if (fscki->inum != UBIFS_ROOT_INO &&
+                           fscki->references != 1) {
+                               ubifs_err("directory inode %lu has %d "
+                                         "direntries which refer it, but "
+                                         "should be 1", fscki->inum,
+                                         fscki->references);
+                               goto out_dump;
+                       }
+                       if (fscki->inum == UBIFS_ROOT_INO &&
+                           fscki->references != 0) {
+                               ubifs_err("root inode %lu has non-zero (%d) "
+                                         "direntries which refer it",
+                                         fscki->inum, fscki->references);
+                               goto out_dump;
+                       }
+                       if (fscki->calc_sz != fscki->size) {
+                               ubifs_err("directory inode %lu size is %lld, "
+                                         "but calculated size is %lld",
+                                         fscki->inum, fscki->size,
+                                         fscki->calc_sz);
+                               goto out_dump;
+                       }
+                       if (fscki->calc_cnt != fscki->nlink) {
+                               ubifs_err("directory inode %lu nlink is %d, "
+                                         "but calculated nlink is %d",
+                                         fscki->inum, fscki->nlink,
+                                         fscki->calc_cnt);
+                               goto out_dump;
+                       }
+               } else {
+                       if (fscki->references != fscki->nlink) {
+                               ubifs_err("inode %lu nlink is %d, but "
+                                         "calculated nlink is %d", fscki->inum,
+                                         fscki->nlink, fscki->references);
+                               goto out_dump;
+                       }
+               }
+               if (fscki->xattr_sz != fscki->calc_xsz) {
+                       ubifs_err("inode %lu has xattr size %u, but "
+                                 "calculated size is %lld",
+                                 fscki->inum, fscki->xattr_sz,
+                                 fscki->calc_xsz);
+                       goto out_dump;
+               }
+               if (fscki->xattr_cnt != fscki->calc_xcnt) {
+                       ubifs_err("inode %lu has %u xattrs, but "
+                                 "calculated count is %lld", fscki->inum,
+                                 fscki->xattr_cnt, fscki->calc_xcnt);
+                       goto out_dump;
+               }
+               if (fscki->xattr_nms != fscki->calc_xnms) {
+                       ubifs_err("inode %lu has xattr names' size %u, but "
+                                 "calculated names' size is %lld",
+                                 fscki->inum, fscki->xattr_nms,
+                                 fscki->calc_xnms);
+                       goto out_dump;
+               }
+       }
+
+       return 0;
+
+out_dump:
+       /* Read the bad inode and dump it */
+       ino_key_init(c, &key, fscki->inum);
+       err = ubifs_lookup_level0(c, &key, &znode, &n);
+       if (!err) {
+               ubifs_err("inode %lu not found in index", fscki->inum);
+               return -ENOENT;
+       } else if (err < 0) {
+               ubifs_err("error %d while looking up inode %lu",
+                         err, fscki->inum);
+               return err;
+       }
+
+       zbr = &znode->zbranch[n];
+       ino = kmalloc(zbr->len, GFP_NOFS);
+       if (!ino)
+               return -ENOMEM;
+
+       err = ubifs_tnc_read_node(c, zbr, ino);
+       if (err) {
+               ubifs_err("cannot read inode node at LEB %d:%d, error %d",
+                         zbr->lnum, zbr->offs, err);
+               kfree(ino);
+               return err;
+       }
+
+       ubifs_msg("dump of the inode %lu sitting in LEB %d:%d",
+                 fscki->inum, zbr->lnum, zbr->offs);
+       dbg_dump_node(c, ino);
+       kfree(ino);
+       return -EINVAL;
+}
+
+/**
+ * dbg_check_filesystem - check the file-system.
+ * @c: UBIFS file-system description object
+ *
+ * This function checks the file system, namely:
+ * o makes sure that all leaf nodes exist and their CRCs are correct;
+ * o makes sure inode nlink, size, xattr size/count are correct (for all
+ *   inodes).
+ *
+ * The function reads whole indexing tree and all nodes, so it is pretty
+ * heavy-weight. Returns zero if the file-system is consistent, %-EINVAL if
+ * not, and a negative error code in case of failure.
+ */
+int dbg_check_filesystem(struct ubifs_info *c)
+{
+       int err;
+       struct fsck_data fsckd;
+
+       if (!(ubifs_chk_flags & UBIFS_CHK_FS))
+               return 0;
+
+       fsckd.inodes = RB_ROOT;
+       err = dbg_walk_index(c, check_leaf, NULL, &fsckd);
+       if (err)
+               goto out_free;
+
+       err = check_inodes(c, &fsckd);
+       if (err)
+               goto out_free;
+
+       free_inodes(&fsckd);
+       return 0;
+
+out_free:
+       ubifs_err("file-system check failed with error %d", err);
+       dump_stack();
+       free_inodes(&fsckd);
+       return err;
+}
+
+static int invocation_cnt;
+
+int dbg_force_in_the_gaps(void)
+{
+       if (!dbg_force_in_the_gaps_enabled)
+               return 0;
+       /* Force in-the-gaps every 8th commit */
+       return !((invocation_cnt++) & 0x7);
+}
+
+/* Failure mode for recovery testing */
+
+#define chance(n, d) (simple_rand() <= (n) * 32768LL / (d))
+
+struct failure_mode_info {
+       struct list_head list;
+       struct ubifs_info *c;
+};
+
+static LIST_HEAD(fmi_list);
+static DEFINE_SPINLOCK(fmi_lock);
+
+static unsigned int next;
+
+static int simple_rand(void)
+{
+       if (next == 0)
+               next = current->pid;
+       next = next * 1103515245 + 12345;
+       return (next >> 16) & 32767;
+}
+
+void dbg_failure_mode_registration(struct ubifs_info *c)
+{
+       struct failure_mode_info *fmi;
+
+       fmi = kmalloc(sizeof(struct failure_mode_info), GFP_NOFS);
+       if (!fmi) {
+               dbg_err("Failed to register failure mode - no memory");
+               return;
+       }
+       fmi->c = c;
+       spin_lock(&fmi_lock);
+       list_add_tail(&fmi->list, &fmi_list);
+       spin_unlock(&fmi_lock);
+}
+
+void dbg_failure_mode_deregistration(struct ubifs_info *c)
+{
+       struct failure_mode_info *fmi, *tmp;
+
+       spin_lock(&fmi_lock);
+       list_for_each_entry_safe(fmi, tmp, &fmi_list, list)
+               if (fmi->c == c) {
+                       list_del(&fmi->list);
+                       kfree(fmi);
+               }
+       spin_unlock(&fmi_lock);
+}
+
+static struct ubifs_info *dbg_find_info(struct ubi_volume_desc *desc)
+{
+       struct failure_mode_info *fmi;
+
+       spin_lock(&fmi_lock);
+       list_for_each_entry(fmi, &fmi_list, list)
+               if (fmi->c->ubi == desc) {
+                       struct ubifs_info *c = fmi->c;
+
+                       spin_unlock(&fmi_lock);
+                       return c;
+               }
+       spin_unlock(&fmi_lock);
+       return NULL;
+}
+
+static int in_failure_mode(struct ubi_volume_desc *desc)
+{
+       struct ubifs_info *c = dbg_find_info(desc);
+
+       if (c && dbg_failure_mode)
+               return c->failure_mode;
+       return 0;
+}
+
+static int do_fail(struct ubi_volume_desc *desc, int lnum, int write)
+{
+       struct ubifs_info *c = dbg_find_info(desc);
+
+       if (!c || !dbg_failure_mode)
+               return 0;
+       if (c->failure_mode)
+               return 1;
+       if (!c->fail_cnt) {
+               /* First call - decide delay to failure */
+               if (chance(1, 2)) {
+                       unsigned int delay = 1 << (simple_rand() >> 11);
+
+                       if (chance(1, 2)) {
+                               c->fail_delay = 1;
+                               c->fail_timeout = jiffies +
+                                                 msecs_to_jiffies(delay);
+                               dbg_rcvry("failing after %ums", delay);
+                       } else {
+                               c->fail_delay = 2;
+                               c->fail_cnt_max = delay;
+                               dbg_rcvry("failing after %u calls", delay);
+                       }
+               }
+               c->fail_cnt += 1;
+       }
+       /* Determine if failure delay has expired */
+       if (c->fail_delay == 1) {
+               if (time_before(jiffies, c->fail_timeout))
+                       return 0;
+       } else if (c->fail_delay == 2)
+               if (c->fail_cnt++ < c->fail_cnt_max)
+                       return 0;
+       if (lnum == UBIFS_SB_LNUM) {
+               if (write) {
+                       if (chance(1, 2))
+                               return 0;
+               } else if (chance(19, 20))
+                       return 0;
+               dbg_rcvry("failing in super block LEB %d", lnum);
+       } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) {
+               if (chance(19, 20))
+                       return 0;
+               dbg_rcvry("failing in master LEB %d", lnum);
+       } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) {
+               if (write) {
+                       if (chance(99, 100))
+                               return 0;
+               } else if (chance(399, 400))
+                       return 0;
+               dbg_rcvry("failing in log LEB %d", lnum);
+       } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) {
+               if (write) {
+                       if (chance(7, 8))
+                               return 0;
+               } else if (chance(19, 20))
+                       return 0;
+               dbg_rcvry("failing in LPT LEB %d", lnum);
+       } else if (lnum >= c->orph_first && lnum <= c->orph_last) {
+               if (write) {
+                       if (chance(1, 2))
+                               return 0;
+               } else if (chance(9, 10))
+                       return 0;
+               dbg_rcvry("failing in orphan LEB %d", lnum);
+       } else if (lnum == c->ihead_lnum) {
+               if (chance(99, 100))
+                       return 0;
+               dbg_rcvry("failing in index head LEB %d", lnum);
+       } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) {
+               if (chance(9, 10))
+                       return 0;
+               dbg_rcvry("failing in GC head LEB %d", lnum);
+       } else if (write && !RB_EMPTY_ROOT(&c->buds) &&
+                  !ubifs_search_bud(c, lnum)) {
+               if (chance(19, 20))
+                       return 0;
+               dbg_rcvry("failing in non-bud LEB %d", lnum);
+       } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND ||
+                  c->cmt_state == COMMIT_RUNNING_REQUIRED) {
+               if (chance(999, 1000))
+                       return 0;
+               dbg_rcvry("failing in bud LEB %d commit running", lnum);
+       } else {
+               if (chance(9999, 10000))
+                       return 0;
+               dbg_rcvry("failing in bud LEB %d commit not running", lnum);
+       }
+       ubifs_err("*** SETTING FAILURE MODE ON (LEB %d) ***", lnum);
+       c->failure_mode = 1;
+       dump_stack();
+       return 1;
+}
+
+static void cut_data(const void *buf, int len)
+{
+       int flen, i;
+       unsigned char *p = (void *)buf;
+
+       flen = (len * (long long)simple_rand()) >> 15;
+       for (i = flen; i < len; i++)
+               p[i] = 0xff;
+}
+
+int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
+                int len, int check)
+{
+       if (in_failure_mode(desc))
+               return -EIO;
+       return ubi_leb_read(desc, lnum, buf, offset, len, check);
+}
+
+int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
+                 int offset, int len, int dtype)
+{
+       int err;
+
+       if (in_failure_mode(desc))
+               return -EIO;
+       if (do_fail(desc, lnum, 1))
+               cut_data(buf, len);
+       err = ubi_leb_write(desc, lnum, buf, offset, len, dtype);
+       if (err)
+               return err;
+       if (in_failure_mode(desc))
+               return -EIO;
+       return 0;
+}
+
+int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
+                  int len, int dtype)
+{
+       int err;
+
+       if (do_fail(desc, lnum, 1))
+               return -EIO;
+       err = ubi_leb_change(desc, lnum, buf, len, dtype);
+       if (err)
+               return err;
+       if (do_fail(desc, lnum, 1))
+               return -EIO;
+       return 0;
+}
+
+int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum)
+{
+       int err;
+
+       if (do_fail(desc, lnum, 0))
+               return -EIO;
+       err = ubi_leb_erase(desc, lnum);
+       if (err)
+               return err;
+       if (do_fail(desc, lnum, 0))
+               return -EIO;
+       return 0;
+}
+
+int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum)
+{
+       int err;
+
+       if (do_fail(desc, lnum, 0))
+               return -EIO;
+       err = ubi_leb_unmap(desc, lnum);
+       if (err)
+               return err;
+       if (do_fail(desc, lnum, 0))
+               return -EIO;
+       return 0;
+}
+
+int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum)
+{
+       if (in_failure_mode(desc))
+               return -EIO;
+       return ubi_is_mapped(desc, lnum);
+}
+
+int dbg_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
+{
+       int err;
+
+       if (do_fail(desc, lnum, 0))
+               return -EIO;
+       err = ubi_leb_map(desc, lnum, dtype);
+       if (err)
+               return err;
+       if (do_fail(desc, lnum, 0))
+               return -EIO;
+       return 0;
+}
+
+#endif /* CONFIG_UBIFS_FS_DEBUG */
diff --git a/fs/ubifs/debug.h b/fs/ubifs/debug.h
new file mode 100644 (file)
index 0000000..3c4f1e9
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * 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__ */
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
new file mode 100644 (file)
index 0000000..e90374b
--- /dev/null
@@ -0,0 +1,1240 @@
+/* * 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 directory operations.
+ *
+ * All FS operations in this file allocate budget before writing anything to the
+ * media. If they fail to allocate it, the error is returned. The only
+ * exceptions are 'ubifs_unlink()' and 'ubifs_rmdir()' which keep working even
+ * if they unable to allocate the budget, because deletion %-ENOSPC failure is
+ * not what users are usually ready to get. UBIFS budgeting subsystem has some
+ * space reserved for these purposes.
+ *
+ * All operations in this file write all inodes which they change straight
+ * away, instead of marking them dirty. For example, 'ubifs_link()' changes
+ * @i_size of the parent inode and writes the parent inode together with the
+ * target inode. This was done to simplify file-system recovery which would
+ * otherwise be very difficult to do. The only exception is rename which marks
+ * the re-named inode dirty (because its @i_ctime is updated) but does not
+ * write it, but just marks it as dirty.
+ */
+
+#include "ubifs.h"
+
+/**
+ * inherit_flags - inherit flags of the parent inode.
+ * @dir: parent inode
+ * @mode: new inode mode flags
+ *
+ * This is a helper function for 'ubifs_new_inode()' which inherits flag of the
+ * parent directory inode @dir. UBIFS inodes inherit the following flags:
+ * o %UBIFS_COMPR_FL, which is useful to switch compression on/of on
+ *   sub-directory basis;
+ * o %UBIFS_SYNC_FL - useful for the same reasons;
+ * o %UBIFS_DIRSYNC_FL - similar, but relevant only to directories.
+ *
+ * This function returns the inherited flags.
+ */
+static int inherit_flags(const struct inode *dir, int mode)
+{
+       int flags;
+       const struct ubifs_inode *ui = ubifs_inode(dir);
+
+       if (!S_ISDIR(dir->i_mode))
+               /*
+                * The parent is not a directory, which means that an extended
+                * attribute inode is being created. No flags.
+                */
+               return 0;
+
+       flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL);
+       if (!S_ISDIR(mode))
+               /* The "DIRSYNC" flag only applies to directories */
+               flags &= ~UBIFS_DIRSYNC_FL;
+       return flags;
+}
+
+/**
+ * ubifs_new_inode - allocate new UBIFS inode object.
+ * @c: UBIFS file-system description object
+ * @dir: parent directory inode
+ * @mode: inode mode flags
+ *
+ * This function finds an unused inode number, allocates new inode and
+ * initializes it. Returns new inode in case of success and an error code in
+ * case of failure.
+ */
+struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
+                             int mode)
+{
+       struct inode *inode;
+       struct ubifs_inode *ui;
+
+       inode = new_inode(c->vfs_sb);
+       ui = ubifs_inode(inode);
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+
+       /*
+        * Set 'S_NOCMTIME' to prevent VFS form updating [mc]time of inodes and
+        * marking them dirty in file write path (see 'file_update_time()').
+        * UBIFS has to fully control "clean <-> dirty" transitions of inodes
+        * to make budgeting work.
+        */
+       inode->i_flags |= (S_NOCMTIME);
+
+       inode->i_uid = current->fsuid;
+       if (dir->i_mode & S_ISGID) {
+               inode->i_gid = dir->i_gid;
+               if (S_ISDIR(mode))
+                       mode |= S_ISGID;
+       } else
+               inode->i_gid = current->fsgid;
+       inode->i_mode = mode;
+       inode->i_mtime = inode->i_atime = inode->i_ctime =
+                        ubifs_current_time(inode);
+       inode->i_mapping->nrpages = 0;
+       /* Disable readahead */
+       inode->i_mapping->backing_dev_info = &c->bdi;
+
+       switch (mode & S_IFMT) {
+       case S_IFREG:
+               inode->i_mapping->a_ops = &ubifs_file_address_operations;
+               inode->i_op = &ubifs_file_inode_operations;
+               inode->i_fop = &ubifs_file_operations;
+               break;
+       case S_IFDIR:
+               inode->i_op  = &ubifs_dir_inode_operations;
+               inode->i_fop = &ubifs_dir_operations;
+               inode->i_size = ui->ui_size = UBIFS_INO_NODE_SZ;
+               break;
+       case S_IFLNK:
+               inode->i_op = &ubifs_symlink_inode_operations;
+               break;
+       case S_IFSOCK:
+       case S_IFIFO:
+       case S_IFBLK:
+       case S_IFCHR:
+               inode->i_op  = &ubifs_file_inode_operations;
+               break;
+       default:
+               BUG();
+       }
+
+       ui->flags = inherit_flags(dir, mode);
+       ubifs_set_inode_flags(inode);
+       if (S_ISREG(mode))
+               ui->compr_type = c->default_compr;
+       else
+               ui->compr_type = UBIFS_COMPR_NONE;
+       ui->synced_i_size = 0;
+
+       spin_lock(&c->cnt_lock);
+       /* Inode number overflow is currently not supported */
+       if (c->highest_inum >= INUM_WARN_WATERMARK) {
+               if (c->highest_inum >= INUM_WATERMARK) {
+                       spin_unlock(&c->cnt_lock);
+                       ubifs_err("out of inode numbers");
+                       make_bad_inode(inode);
+                       iput(inode);
+                       return ERR_PTR(-EINVAL);
+               }
+               ubifs_warn("running out of inode numbers (current %lu, max %d)",
+                          c->highest_inum, INUM_WATERMARK);
+       }
+
+       inode->i_ino = ++c->highest_inum;
+       inode->i_generation = ++c->vfs_gen;
+       /*
+        * The creation sequence number remains with this inode for its
+        * lifetime. All nodes for this inode have a greater sequence number,
+        * and so it is possible to distinguish obsolete nodes belonging to a
+        * previous incarnation of the same inode number - for example, for the
+        * purpose of rebuilding the index.
+        */
+       ui->creat_sqnum = ++c->max_sqnum;
+       spin_unlock(&c->cnt_lock);
+       return inode;
+}
+
+#ifdef CONFIG_UBIFS_FS_DEBUG
+
+static int dbg_check_name(struct ubifs_dent_node *dent, struct qstr *nm)
+{
+       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+               return 0;
+       if (le16_to_cpu(dent->nlen) != nm->len)
+               return -EINVAL;
+       if (memcmp(dent->name, nm->name, nm->len))
+               return -EINVAL;
+       return 0;
+}
+
+#else
+
+#define dbg_check_name(dent, nm) 0
+
+#endif
+
+static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
+                                  struct nameidata *nd)
+{
+       int err;
+       union ubifs_key key;
+       struct inode *inode = NULL;
+       struct ubifs_dent_node *dent;
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+
+       dbg_gen("'%.*s' in dir ino %lu",
+               dentry->d_name.len, dentry->d_name.name, dir->i_ino);
+
+       if (dentry->d_name.len > UBIFS_MAX_NLEN)
+               return ERR_PTR(-ENAMETOOLONG);
+
+       dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+       if (!dent)
+               return ERR_PTR(-ENOMEM);
+
+       dent_key_init(c, &key, dir->i_ino, &dentry->d_name);
+
+       err = ubifs_tnc_lookup_nm(c, &key, dent, &dentry->d_name);
+       if (err) {
+               /*
+                * Do not hash the direntry if parent 'i_nlink' is zero, because
+                * this has side-effects - '->delete_inode()' call will not be
+                * called for the parent orphan inode, because 'd_count' of its
+                * direntry will stay 1 (it'll be negative direntry I guess)
+                * and prevent 'iput_final()' until the dentry is destroyed due
+                * to unmount or memory pressure.
+                */
+               if (err == -ENOENT && dir->i_nlink != 0) {
+                       dbg_gen("not found");
+                       goto done;
+               }
+               goto out;
+       }
+
+       if (dbg_check_name(dent, &dentry->d_name)) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum));
+       if (IS_ERR(inode)) {
+               /*
+                * This should not happen. Probably the file-system needs
+                * checking.
+                */
+               err = PTR_ERR(inode);
+               ubifs_err("dead directory entry '%.*s', error %d",
+                         dentry->d_name.len, dentry->d_name.name, err);
+               ubifs_ro_mode(c, err);
+               goto out;
+       }
+
+done:
+       kfree(dent);
+       /*
+        * Note, d_splice_alias() would be required instead if we supported
+        * NFS.
+        */
+       d_add(dentry, inode);
+       return NULL;
+
+out:
+       kfree(dent);
+       return ERR_PTR(err);
+}
+
+static int ubifs_create(struct inode *dir, struct dentry *dentry, int mode,
+                       struct nameidata *nd)
+{
+       struct inode *inode;
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+       int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+       struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+                                       .dirtied_ino = 1 };
+       struct ubifs_inode *dir_ui = ubifs_inode(dir);
+
+       /*
+        * Budget request settings: new inode, new direntry, changing the
+        * parent directory inode.
+        */
+
+       dbg_gen("dent '%.*s', mode %#x in dir ino %lu",
+               dentry->d_name.len, dentry->d_name.name, mode, dir->i_ino);
+
+       err = ubifs_budget_space(c, &req);
+       if (err)
+               return err;
+
+       inode = ubifs_new_inode(c, dir, mode);
+       if (IS_ERR(inode)) {
+               err = PTR_ERR(inode);
+               goto out_budg;
+       }
+
+       mutex_lock(&dir_ui->ui_mutex);
+       dir->i_size += sz_change;
+       dir_ui->ui_size = dir->i_size;
+       dir->i_mtime = dir->i_ctime = inode->i_ctime;
+       err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+       if (err)
+               goto out_cancel;
+       mutex_unlock(&dir_ui->ui_mutex);
+
+       ubifs_release_budget(c, &req);
+       insert_inode_hash(inode);
+       d_instantiate(dentry, inode);
+       return 0;
+
+out_cancel:
+       dir->i_size -= sz_change;
+       dir_ui->ui_size = dir->i_size;
+       mutex_unlock(&dir_ui->ui_mutex);
+       make_bad_inode(inode);
+       iput(inode);
+out_budg:
+       ubifs_release_budget(c, &req);
+       ubifs_err("cannot create regular file, error %d", err);
+       return err;
+}
+
+/**
+ * vfs_dent_type - get VFS directory entry type.
+ * @type: UBIFS directory entry type
+ *
+ * This function converts UBIFS directory entry type into VFS directory entry
+ * type.
+ */
+static unsigned int vfs_dent_type(uint8_t type)
+{
+       switch (type) {
+       case UBIFS_ITYPE_REG:
+               return DT_REG;
+       case UBIFS_ITYPE_DIR:
+               return DT_DIR;
+       case UBIFS_ITYPE_LNK:
+               return DT_LNK;
+       case UBIFS_ITYPE_BLK:
+               return DT_BLK;
+       case UBIFS_ITYPE_CHR:
+               return DT_CHR;
+       case UBIFS_ITYPE_FIFO:
+               return DT_FIFO;
+       case UBIFS_ITYPE_SOCK:
+               return DT_SOCK;
+       default:
+               BUG();
+       }
+       return 0;
+}
+
+/*
+ * The classical Unix view for directory is that it is a linear array of
+ * (name, inode number) entries. Linux/VFS assumes this model as well.
+ * Particularly, 'readdir()' call wants us to return a directory entry offset
+ * which later may be used to continue 'readdir()'ing the directory or to
+ * 'seek()' to that specific direntry. Obviously UBIFS does not really fit this
+ * model because directory entries are identified by keys, which may collide.
+ *
+ * UBIFS uses directory entry hash value for directory offsets, so
+ * 'seekdir()'/'telldir()' may not always work because of possible key
+ * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work
+ * properly by means of saving full directory entry name in the private field
+ * of the file description object.
+ *
+ * This means that UBIFS cannot support NFS which requires full
+ * 'seekdir()'/'telldir()' support.
+ */
+static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+       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;
+
+       /* File positions 0 and 1 correspond to "." and ".." */
+       if (file->f_pos == 0) {
+               ubifs_assert(!file->private_data);
+               over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR);
+               if (over)
+                       return 0;
+               file->f_pos = 1;
+       }
+
+       if (file->f_pos == 1) {
+               ubifs_assert(!file->private_data);
+               over = filldir(dirent, "..", 2, 1,
+                              parent_ino(file->f_path.dentry), DT_DIR);
+               if (over)
+                       return 0;
+
+               /* 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, le64_to_cpu(dent->inum),
+                       key_hash_flash(c, &dent->key));
+               ubifs_assert(dent->ch.sqnum > ubifs_inode(dir)->creat_sqnum);
+
+               nm.len = le16_to_cpu(dent->nlen);
+               over = filldir(dirent, dent->name, nm.len, file->f_pos,
+                              le64_to_cpu(dent->inum),
+                              vfs_dent_type(dent->type));
+               if (over)
+                       return 0;
+
+               /* Switch to the next entry */
+               key_read(c, &dent->key, &key);
+               nm.name = 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;
+}
+
+/* If a directory is seeked, we have to free saved readdir() state */
+static loff_t ubifs_dir_llseek(struct file *file, loff_t offset, int origin)
+{
+       kfree(file->private_data);
+       file->private_data = NULL;
+       return generic_file_llseek(file, offset, origin);
+}
+
+/* Free saved readdir() state when the directory is closed */
+static int ubifs_dir_release(struct inode *dir, struct file *file)
+{
+       kfree(file->private_data);
+       file->private_data = NULL;
+       return 0;
+}
+
+/**
+ * lock_2_inodes - lock two UBIFS inodes.
+ * @inode1: first inode
+ * @inode2: second inode
+ */
+static void lock_2_inodes(struct inode *inode1, struct inode *inode2)
+{
+       if (inode1->i_ino < inode2->i_ino) {
+               mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_2);
+               mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_3);
+       } else {
+               mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
+               mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_3);
+       }
+}
+
+/**
+ * unlock_2_inodes - unlock two UBIFS inodes inodes.
+ * @inode1: first inode
+ * @inode2: second inode
+ */
+static void unlock_2_inodes(struct inode *inode1, struct inode *inode2)
+{
+       mutex_unlock(&ubifs_inode(inode1)->ui_mutex);
+       mutex_unlock(&ubifs_inode(inode2)->ui_mutex);
+}
+
+static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
+                     struct dentry *dentry)
+{
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+       struct inode *inode = old_dentry->d_inode;
+       struct ubifs_inode *ui = ubifs_inode(inode);
+       struct ubifs_inode *dir_ui = ubifs_inode(dir);
+       int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+       struct ubifs_budget_req req = { .new_dent = 1, .dirtied_ino = 2,
+                                       .dirtied_ino_d = ui->data_len };
+
+       /*
+        * Budget request settings: new direntry, changing the target inode,
+        * changing the parent inode.
+        */
+
+       dbg_gen("dent '%.*s' to ino %lu (nlink %d) in dir ino %lu",
+               dentry->d_name.len, dentry->d_name.name, inode->i_ino,
+               inode->i_nlink, dir->i_ino);
+       err = dbg_check_synced_i_size(inode);
+       if (err)
+               return err;
+
+       err = ubifs_budget_space(c, &req);
+       if (err)
+               return err;
+
+       lock_2_inodes(dir, inode);
+       inc_nlink(inode);
+       atomic_inc(&inode->i_count);
+       inode->i_ctime = ubifs_current_time(inode);
+       dir->i_size += sz_change;
+       dir_ui->ui_size = dir->i_size;
+       dir->i_mtime = dir->i_ctime = inode->i_ctime;
+       err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+       if (err)
+               goto out_cancel;
+       unlock_2_inodes(dir, inode);
+
+       ubifs_release_budget(c, &req);
+       d_instantiate(dentry, inode);
+       return 0;
+
+out_cancel:
+       dir->i_size -= sz_change;
+       dir_ui->ui_size = dir->i_size;
+       drop_nlink(inode);
+       unlock_2_inodes(dir, inode);
+       ubifs_release_budget(c, &req);
+       iput(inode);
+       return err;
+}
+
+static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
+{
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+       struct inode *inode = dentry->d_inode;
+       struct ubifs_inode *dir_ui = ubifs_inode(dir);
+       int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+       int err, budgeted = 1;
+       struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
+
+       /*
+        * Budget request settings: deletion direntry, deletion inode (+1 for
+        * @dirtied_ino), changing the parent directory inode. If budgeting
+        * fails, go ahead anyway because we have extra space reserved for
+        * deletions.
+        */
+
+       dbg_gen("dent '%.*s' from ino %lu (nlink %d) in dir ino %lu",
+               dentry->d_name.len, dentry->d_name.name, inode->i_ino,
+               inode->i_nlink, dir->i_ino);
+       err = dbg_check_synced_i_size(inode);
+       if (err)
+               return err;
+
+       err = ubifs_budget_space(c, &req);
+       if (err) {
+               if (err != -ENOSPC)
+                       return err;
+               err = 0;
+               budgeted = 0;
+       }
+
+       lock_2_inodes(dir, inode);
+       inode->i_ctime = ubifs_current_time(dir);
+       drop_nlink(inode);
+       dir->i_size -= sz_change;
+       dir_ui->ui_size = dir->i_size;
+       dir->i_mtime = dir->i_ctime = inode->i_ctime;
+       err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, 0);
+       if (err)
+               goto out_cancel;
+       unlock_2_inodes(dir, inode);
+
+       if (budgeted)
+               ubifs_release_budget(c, &req);
+       else {
+               /* We've deleted something - clean the "no space" flags */
+               c->nospace = c->nospace_rp = 0;
+               smp_wmb();
+       }
+       return 0;
+
+out_cancel:
+       dir->i_size += sz_change;
+       dir_ui->ui_size = dir->i_size;
+       inc_nlink(inode);
+       unlock_2_inodes(dir, inode);
+       if (budgeted)
+               ubifs_release_budget(c, &req);
+       return err;
+}
+
+/**
+ * check_dir_empty - check if a directory is empty or not.
+ * @c: UBIFS file-system description object
+ * @dir: VFS inode object of the directory to check
+ *
+ * This function checks if directory @dir is empty. Returns zero if the
+ * directory is empty, %-ENOTEMPTY if it is not, and other negative error codes
+ * in case of of errors.
+ */
+static int check_dir_empty(struct ubifs_info *c, struct inode *dir)
+{
+       struct qstr nm = { .name = NULL };
+       struct ubifs_dent_node *dent;
+       union ubifs_key key;
+       int err;
+
+       lowest_dent_key(c, &key, dir->i_ino);
+       dent = ubifs_tnc_next_ent(c, &key, &nm);
+       if (IS_ERR(dent)) {
+               err = PTR_ERR(dent);
+               if (err == -ENOENT)
+                       err = 0;
+       } else {
+               kfree(dent);
+               err = -ENOTEMPTY;
+       }
+       return err;
+}
+
+static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+       struct inode *inode = dentry->d_inode;
+       int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+       int err, budgeted = 1;
+       struct ubifs_inode *dir_ui = ubifs_inode(dir);
+       struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
+
+       /*
+        * Budget request settings: deletion direntry, deletion inode and
+        * changing the parent inode. If budgeting fails, go ahead anyway
+        * because we have extra space reserved for deletions.
+        */
+
+       dbg_gen("directory '%.*s', ino %lu in dir ino %lu", dentry->d_name.len,
+               dentry->d_name.name, inode->i_ino, dir->i_ino);
+
+       err = check_dir_empty(c, dentry->d_inode);
+       if (err)
+               return err;
+
+       err = ubifs_budget_space(c, &req);
+       if (err) {
+               if (err != -ENOSPC)
+                       return err;
+               budgeted = 0;
+       }
+
+       lock_2_inodes(dir, inode);
+       inode->i_ctime = ubifs_current_time(dir);
+       clear_nlink(inode);
+       drop_nlink(dir);
+       dir->i_size -= sz_change;
+       dir_ui->ui_size = dir->i_size;
+       dir->i_mtime = dir->i_ctime = inode->i_ctime;
+       err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, 0);
+       if (err)
+               goto out_cancel;
+       unlock_2_inodes(dir, inode);
+
+       if (budgeted)
+               ubifs_release_budget(c, &req);
+       else {
+               /* We've deleted something - clean the "no space" flags */
+               c->nospace = c->nospace_rp = 0;
+               smp_wmb();
+       }
+       return 0;
+
+out_cancel:
+       dir->i_size += sz_change;
+       dir_ui->ui_size = dir->i_size;
+       inc_nlink(dir);
+       inc_nlink(inode);
+       inc_nlink(inode);
+       unlock_2_inodes(dir, inode);
+       if (budgeted)
+               ubifs_release_budget(c, &req);
+       return err;
+}
+
+static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       struct inode *inode;
+       struct ubifs_inode *dir_ui = ubifs_inode(dir);
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+       int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+       struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+                                       .dirtied_ino_d = 1 };
+
+       /*
+        * Budget request settings: new inode, new direntry and changing parent
+        * directory inode.
+        */
+
+       dbg_gen("dent '%.*s', mode %#x in dir ino %lu",
+               dentry->d_name.len, dentry->d_name.name, mode, dir->i_ino);
+
+       err = ubifs_budget_space(c, &req);
+       if (err)
+               return err;
+
+       inode = ubifs_new_inode(c, dir, S_IFDIR | mode);
+       if (IS_ERR(inode)) {
+               err = PTR_ERR(inode);
+               goto out_budg;
+       }
+
+       mutex_lock(&dir_ui->ui_mutex);
+       insert_inode_hash(inode);
+       inc_nlink(inode);
+       inc_nlink(dir);
+       dir->i_size += sz_change;
+       dir_ui->ui_size = dir->i_size;
+       dir->i_mtime = dir->i_ctime = inode->i_ctime;
+       err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+       if (err) {
+               ubifs_err("cannot create directory, error %d", err);
+               goto out_cancel;
+       }
+       mutex_unlock(&dir_ui->ui_mutex);
+
+       ubifs_release_budget(c, &req);
+       d_instantiate(dentry, inode);
+       return 0;
+
+out_cancel:
+       dir->i_size -= sz_change;
+       dir_ui->ui_size = dir->i_size;
+       drop_nlink(dir);
+       mutex_unlock(&dir_ui->ui_mutex);
+       make_bad_inode(inode);
+       iput(inode);
+out_budg:
+       ubifs_release_budget(c, &req);
+       return err;
+}
+
+static int ubifs_mknod(struct inode *dir, struct dentry *dentry,
+                      int mode, dev_t rdev)
+{
+       struct inode *inode;
+       struct ubifs_inode *ui;
+       struct ubifs_inode *dir_ui = ubifs_inode(dir);
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+       union ubifs_dev_desc *dev = NULL;
+       int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+       int err, devlen = 0;
+       struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+                                       .new_ino_d = devlen, .dirtied_ino = 1 };
+
+       /*
+        * Budget request settings: new inode, new direntry and changing parent
+        * directory inode.
+        */
+
+       dbg_gen("dent '%.*s' in dir ino %lu",
+               dentry->d_name.len, dentry->d_name.name, dir->i_ino);
+
+       if (!new_valid_dev(rdev))
+               return -EINVAL;
+
+       if (S_ISBLK(mode) || S_ISCHR(mode)) {
+               dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
+               if (!dev)
+                       return -ENOMEM;
+               devlen = ubifs_encode_dev(dev, rdev);
+       }
+
+       err = ubifs_budget_space(c, &req);
+       if (err) {
+               kfree(dev);
+               return err;
+       }
+
+       inode = ubifs_new_inode(c, dir, mode);
+       if (IS_ERR(inode)) {
+               kfree(dev);
+               err = PTR_ERR(inode);
+               goto out_budg;
+       }
+
+       init_special_inode(inode, inode->i_mode, rdev);
+       inode->i_size = ubifs_inode(inode)->ui_size = devlen;
+       ui = ubifs_inode(inode);
+       ui->data = dev;
+       ui->data_len = devlen;
+
+       mutex_lock(&dir_ui->ui_mutex);
+       dir->i_size += sz_change;
+       dir_ui->ui_size = dir->i_size;
+       dir->i_mtime = dir->i_ctime = inode->i_ctime;
+       err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+       if (err)
+               goto out_cancel;
+       mutex_unlock(&dir_ui->ui_mutex);
+
+       ubifs_release_budget(c, &req);
+       insert_inode_hash(inode);
+       d_instantiate(dentry, inode);
+       return 0;
+
+out_cancel:
+       dir->i_size -= sz_change;
+       dir_ui->ui_size = dir->i_size;
+       mutex_unlock(&dir_ui->ui_mutex);
+       make_bad_inode(inode);
+       iput(inode);
+out_budg:
+       ubifs_release_budget(c, &req);
+       return err;
+}
+
+static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
+                        const char *symname)
+{
+       struct inode *inode;
+       struct ubifs_inode *ui;
+       struct ubifs_inode *dir_ui = ubifs_inode(dir);
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+       int err, len = strlen(symname);
+       int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+       struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
+                                       .new_ino_d = len, .dirtied_ino = 1 };
+
+       /*
+        * Budget request settings: new inode, new direntry and changing parent
+        * directory inode.
+        */
+
+       dbg_gen("dent '%.*s', target '%s' in dir ino %lu", dentry->d_name.len,
+               dentry->d_name.name, symname, dir->i_ino);
+
+       if (len > UBIFS_MAX_INO_DATA)
+               return -ENAMETOOLONG;
+
+       err = ubifs_budget_space(c, &req);
+       if (err)
+               return err;
+
+       inode = ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO);
+       if (IS_ERR(inode)) {
+               err = PTR_ERR(inode);
+               goto out_budg;
+       }
+
+       ui = ubifs_inode(inode);
+       ui->data = kmalloc(len + 1, GFP_NOFS);
+       if (!ui->data) {
+               err = -ENOMEM;
+               goto out_inode;
+       }
+
+       memcpy(ui->data, symname, len);
+       ((char *)ui->data)[len] = '\0';
+       /*
+        * The terminating zero byte is not written to the flash media and it
+        * is put just to make later in-memory string processing simpler. Thus,
+        * data length is @len, not @len + %1.
+        */
+       ui->data_len = len;
+       inode->i_size = ubifs_inode(inode)->ui_size = len;
+
+       mutex_lock(&dir_ui->ui_mutex);
+       dir->i_size += sz_change;
+       dir_ui->ui_size = dir->i_size;
+       dir->i_mtime = dir->i_ctime = inode->i_ctime;
+       err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+       if (err)
+               goto out_cancel;
+       mutex_unlock(&dir_ui->ui_mutex);
+
+       ubifs_release_budget(c, &req);
+       insert_inode_hash(inode);
+       d_instantiate(dentry, inode);
+       return 0;
+
+out_cancel:
+       dir->i_size -= sz_change;
+       dir_ui->ui_size = dir->i_size;
+       mutex_unlock(&dir_ui->ui_mutex);
+out_inode:
+       make_bad_inode(inode);
+       iput(inode);
+out_budg:
+       ubifs_release_budget(c, &req);
+       return err;
+}
+
+/**
+ * lock_3_inodes - lock three UBIFS inodes for rename.
+ * @inode1: first inode
+ * @inode2: second inode
+ * @inode3: third inode
+ *
+ * For 'ubifs_rename()', @inode1 may be the same as @inode2 whereas @inode3 may
+ * be null.
+ */
+static void lock_3_inodes(struct inode *inode1, struct inode *inode2,
+                         struct inode *inode3)
+{
+       struct inode *i1, *i2, *i3;
+
+       if (!inode3) {
+               if (inode1 != inode2) {
+                       lock_2_inodes(inode1, inode2);
+                       return;
+               }
+               mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
+               return;
+       }
+
+       if (inode1 == inode2) {
+               lock_2_inodes(inode1, inode3);
+               return;
+       }
+
+       /* 3 different inodes */
+       if (inode1 < inode2) {
+               i3 = inode2;
+               if (inode1 < inode3) {
+                       i1 = inode1;
+                       i2 = inode3;
+               } else {
+                       i1 = inode3;
+                       i2 = inode1;
+               }
+       } else {
+               i3 = inode1;
+               if (inode2 < inode3) {
+                       i1 = inode2;
+                       i2 = inode3;
+               } else {
+                       i1 = inode3;
+                       i2 = inode2;
+               }
+       }
+       mutex_lock_nested(&ubifs_inode(i1)->ui_mutex, WB_MUTEX_1);
+       lock_2_inodes(i2, i3);
+}
+
+/**
+ * unlock_3_inodes - unlock three UBIFS inodes for rename.
+ * @inode1: first inode
+ * @inode2: second inode
+ * @inode3: third inode
+ */
+static void unlock_3_inodes(struct inode *inode1, struct inode *inode2,
+                           struct inode *inode3)
+{
+       mutex_unlock(&ubifs_inode(inode1)->ui_mutex);
+       if (inode1 != inode2)
+               mutex_unlock(&ubifs_inode(inode2)->ui_mutex);
+       if (inode3)
+               mutex_unlock(&ubifs_inode(inode3)->ui_mutex);
+}
+
+static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
+                       struct inode *new_dir, struct dentry *new_dentry)
+{
+       struct ubifs_info *c = old_dir->i_sb->s_fs_info;
+       struct inode *old_inode = old_dentry->d_inode;
+       struct inode *new_inode = new_dentry->d_inode;
+       struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode);
+       int err, release, sync = 0, move = (new_dir != old_dir);
+       int is_dir = S_ISDIR(old_inode->i_mode);
+       int unlink = !!new_inode;
+       int new_sz = CALC_DENT_SIZE(new_dentry->d_name.len);
+       int old_sz = CALC_DENT_SIZE(old_dentry->d_name.len);
+       struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
+                                       .dirtied_ino = 3 };
+       struct ubifs_budget_req ino_req = { .dirtied_ino = 1,
+                               .dirtied_ino_d = old_inode_ui->data_len };
+       struct timespec time;
+
+       /*
+        * Budget request settings: deletion direntry, new direntry, removing
+        * the old inode, and changing old and new parent directory inodes.
+        *
+        * However, this operation also marks the target inode as dirty and
+        * does not write it, so we allocate budget for the target inode
+        * separately.
+        */
+
+       dbg_gen("dent '%.*s' ino %lu in dir ino %lu to dent '%.*s' in "
+               "dir ino %lu", old_dentry->d_name.len, old_dentry->d_name.name,
+               old_inode->i_ino, old_dir->i_ino, new_dentry->d_name.len,
+               new_dentry->d_name.name, new_dir->i_ino);
+
+       if (unlink && is_dir) {
+               err = check_dir_empty(c, new_inode);
+               if (err)
+                       return err;
+       }
+
+       err = ubifs_budget_space(c, &req);
+       if (err)
+               return err;
+       err = ubifs_budget_space(c, &ino_req);
+       if (err) {
+               ubifs_release_budget(c, &req);
+               return err;
+       }
+
+       lock_3_inodes(old_dir, new_dir, new_inode);
+
+       /*
+        * Like most other Unix systems, set the @i_ctime for inodes on a
+        * rename.
+        */
+       time = ubifs_current_time(old_dir);
+       old_inode->i_ctime = time;
+
+       /* We must adjust parent link count when renaming directories */
+       if (is_dir) {
+               if (move) {
+                       /*
+                        * @old_dir loses a link because we are moving
+                        * @old_inode to a different directory.
+                        */
+                       drop_nlink(old_dir);
+                       /*
+                        * @new_dir only gains a link if we are not also
+                        * overwriting an existing directory.
+                        */
+                       if (!unlink)
+                               inc_nlink(new_dir);
+               } else {
+                       /*
+                        * @old_inode is not moving to a different directory,
+                        * but @old_dir still loses a link if we are
+                        * overwriting an existing directory.
+                        */
+                       if (unlink)
+                               drop_nlink(old_dir);
+               }
+       }
+
+       old_dir->i_size -= old_sz;
+       ubifs_inode(old_dir)->ui_size = old_dir->i_size;
+       old_dir->i_mtime = old_dir->i_ctime = time;
+       new_dir->i_mtime = new_dir->i_ctime = time;
+
+       /*
+        * And finally, if we unlinked a direntry which happened to have the
+        * same name as the moved direntry, we have to decrement @i_nlink of
+        * the unlinked inode and change its ctime.
+        */
+       if (unlink) {
+               /*
+                * Directories cannot have hard-links, so if this is a
+                * directory, decrement its @i_nlink twice because an empty
+                * directory has @i_nlink 2.
+                */
+               if (is_dir)
+                       drop_nlink(new_inode);
+               new_inode->i_ctime = time;
+               drop_nlink(new_inode);
+       } else {
+               new_dir->i_size += new_sz;
+               ubifs_inode(new_dir)->ui_size = new_dir->i_size;
+       }
+
+       /*
+        * Do not ask 'ubifs_jnl_rename()' to flush write-buffer if @old_inode
+        * is dirty, because this will be done later on at the end of
+        * 'ubifs_rename()'.
+        */
+       if (IS_SYNC(old_inode)) {
+               sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
+               if (unlink && IS_SYNC(new_inode))
+                       sync = 1;
+       }
+       err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry,
+                              sync);
+       if (err)
+               goto out_cancel;
+
+       unlock_3_inodes(old_dir, new_dir, new_inode);
+       ubifs_release_budget(c, &req);
+
+       mutex_lock(&old_inode_ui->ui_mutex);
+       release = old_inode_ui->dirty;
+       mark_inode_dirty_sync(old_inode);
+       mutex_unlock(&old_inode_ui->ui_mutex);
+
+       if (release)
+               ubifs_release_budget(c, &ino_req);
+       if (IS_SYNC(old_inode))
+               err = old_inode->i_sb->s_op->write_inode(old_inode, 1);
+       return err;
+
+out_cancel:
+       if (unlink) {
+               if (is_dir)
+                       inc_nlink(new_inode);
+               inc_nlink(new_inode);
+       } else {
+               new_dir->i_size -= new_sz;
+               ubifs_inode(new_dir)->ui_size = new_dir->i_size;
+       }
+       old_dir->i_size += old_sz;
+       ubifs_inode(old_dir)->ui_size = old_dir->i_size;
+       if (is_dir) {
+               if (move) {
+                       inc_nlink(old_dir);
+                       if (!unlink)
+                               drop_nlink(new_dir);
+               } else {
+                       if (unlink)
+                               inc_nlink(old_dir);
+               }
+       }
+       unlock_3_inodes(old_dir, new_dir, new_inode);
+       ubifs_release_budget(c, &ino_req);
+       ubifs_release_budget(c, &req);
+       return err;
+}
+
+int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+                 struct kstat *stat)
+{
+       loff_t size;
+       struct inode *inode = dentry->d_inode;
+       struct ubifs_inode *ui = ubifs_inode(inode);
+
+       mutex_lock(&ui->ui_mutex);
+       stat->dev = inode->i_sb->s_dev;
+       stat->ino = inode->i_ino;
+       stat->mode = inode->i_mode;
+       stat->nlink = inode->i_nlink;
+       stat->uid = inode->i_uid;
+       stat->gid = inode->i_gid;
+       stat->rdev = inode->i_rdev;
+       stat->atime = inode->i_atime;
+       stat->mtime = inode->i_mtime;
+       stat->ctime = inode->i_ctime;
+       stat->blksize = UBIFS_BLOCK_SIZE;
+       stat->size = ui->ui_size;
+
+       /*
+        * Unfortunately, the 'stat()' system call was designed for block
+        * device based file systems, and it is not appropriate for UBIFS,
+        * because UBIFS does not have notion of "block". For example, it is
+        * difficult to tell how many block a directory takes - it actually
+        * takes less than 300 bytes, but we have to round it to block size,
+        * which introduces large mistake. This makes utilities like 'du' to
+        * report completely senseless numbers. This is the reason why UBIFS
+        * goes the same way as JFFS2 - it reports zero blocks for everything
+        * but regular files, which makes more sense than reporting completely
+        * wrong sizes.
+        */
+       if (S_ISREG(inode->i_mode)) {
+               size = ui->xattr_size;
+               size += stat->size;
+               size = ALIGN(size, UBIFS_BLOCK_SIZE);
+               /*
+                * Note, user-space expects 512-byte blocks count irrespectively
+                * of what was reported in @stat->size.
+                */
+               stat->blocks = size >> 9;
+       } else
+               stat->blocks = 0;
+       mutex_unlock(&ui->ui_mutex);
+       return 0;
+}
+
+struct inode_operations ubifs_dir_inode_operations = {
+       .lookup      = ubifs_lookup,
+       .create      = ubifs_create,
+       .link        = ubifs_link,
+       .symlink     = ubifs_symlink,
+       .unlink      = ubifs_unlink,
+       .mkdir       = ubifs_mkdir,
+       .rmdir       = ubifs_rmdir,
+       .mknod       = ubifs_mknod,
+       .rename      = ubifs_rename,
+       .setattr     = ubifs_setattr,
+       .getattr     = ubifs_getattr,
+#ifdef CONFIG_UBIFS_FS_XATTR
+       .setxattr    = ubifs_setxattr,
+       .getxattr    = ubifs_getxattr,
+       .listxattr   = ubifs_listxattr,
+       .removexattr = ubifs_removexattr,
+#endif
+};
+
+struct file_operations ubifs_dir_operations = {
+       .llseek         = ubifs_dir_llseek,
+       .release        = ubifs_dir_release,
+       .read           = generic_read_dir,
+       .readdir        = ubifs_readdir,
+       .fsync          = ubifs_fsync,
+       .unlocked_ioctl = ubifs_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = ubifs_compat_ioctl,
+#endif
+};
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
new file mode 100644 (file)
index 0000000..005a3b8
--- /dev/null
@@ -0,0 +1,1275 @@
+/*
+ * 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 VFS file and inode operations of regular files, device
+ * nodes and symlinks as well as address space operations.
+ *
+ * UBIFS uses 2 page flags: PG_private and PG_checked. PG_private is set if the
+ * page is dirty and is used for budgeting purposes - dirty pages should not be
+ * budgeted. The PG_checked flag is set if full budgeting is required for the
+ * page e.g., when it corresponds to a file hole or it is just beyond the file
+ * size. The budgeting is done in 'ubifs_write_begin()', because it is OK to
+ * fail in this function, and the budget is released in 'ubifs_write_end()'. So
+ * the PG_private and PG_checked flags carry the information about how the page
+ * was budgeted, to make it possible to release the budget properly.
+ *
+ * A thing to keep in mind: inode's 'i_mutex' is locked in most VFS operations
+ * we implement. However, this is not true for '->writepage()', which might be
+ * called with 'i_mutex' unlocked. For example, when pdflush is performing
+ * write-back, it calls 'writepage()' with unlocked 'i_mutex', although the
+ * inode has 'I_LOCK' flag in this case. At "normal" work-paths 'i_mutex' is
+ * locked in '->writepage', e.g. in "sys_write -> alloc_pages -> direct reclaim
+ * path'. So, in '->writepage()' we are only guaranteed that the page is
+ * locked.
+ *
+ * Similarly, 'i_mutex' does not have to be locked in readpage(), e.g.,
+ * readahead path does not have it locked ("sys_read -> generic_file_aio_read
+ * -> ondemand_readahead -> readpage"). In case of readahead, 'I_LOCK' flag is
+ * not set as well. However, UBIFS disables readahead.
+ *
+ * This, for example means that there might be 2 concurrent '->writepage()'
+ * calls for the same inode, but different inode dirty pages.
+ */
+
+#include "ubifs.h"
+#include <linux/mount.h>
+
+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(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 page *page)
+{
+       void *addr;
+       int err = 0, i;
+       unsigned int block, beyond;
+       struct ubifs_data_node *dn;
+       struct inode *inode = page->mapping->host;
+       loff_t i_size = i_size_read(inode);
+
+       dbg_gen("ino %lu, pg %lu, i_size %lld, flags %#lx",
+               inode->i_ino, page->index, i_size, page->flags);
+       ubifs_assert(!PageChecked(page));
+       ubifs_assert(!PagePrivate(page));
+
+       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 */
+               SetPageChecked(page);
+               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;
+                       }
+               }
+               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 */
+                       SetPageChecked(page);
+                       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:
+       SetPageUptodate(page);
+       ClearPageError(page);
+       flush_dcache_page(page);
+       kunmap(page);
+       return 0;
+
+error:
+       kfree(dn);
+       ClearPageUptodate(page);
+       SetPageError(page);
+       flush_dcache_page(page);
+       kunmap(page);
+       return err;
+}
+
+/**
+ * release_new_page_budget - release budget of a new page.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function which releases budget corresponding to the budget
+ * of one new page of data.
+ */
+static void release_new_page_budget(struct ubifs_info *c)
+{
+       struct ubifs_budget_req req = { .recalculate = 1, .new_page = 1 };
+
+       ubifs_release_budget(c, &req);
+}
+
+/**
+ * release_existing_page_budget - release budget of an existing page.
+ * @c: UBIFS file-system description object
+ *
+ * This is a helper function which releases budget corresponding to the budget
+ * of changing one one page of data which already exists on the flash media.
+ */
+static void release_existing_page_budget(struct ubifs_info *c)
+{
+       struct ubifs_budget_req req = { .dd_growth = c->page_budget};
+
+       ubifs_release_budget(c, &req);
+}
+
+static int write_begin_slow(struct address_space *mapping,
+                           loff_t pos, unsigned len, struct page **pagep)
+{
+       struct inode *inode = mapping->host;
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+       pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+       struct ubifs_budget_req req = { .new_page = 1 };
+       int uninitialized_var(err), appending = !!(pos + len > inode->i_size);
+       struct page *page;
+
+       dbg_gen("ino %lu, pos %llu, len %u, i_size %lld",
+               inode->i_ino, pos, len, inode->i_size);
+
+       /*
+        * At the slow path we have to budget before locking the page, because
+        * budgeting may force write-back, which would wait on locked pages and
+        * deadlock if we had the page locked. At this point we do not know
+        * anything about the page, so assume that this is a new page which is
+        * written to a hole. This corresponds to largest budget. Later the
+        * budget will be amended if this is not true.
+        */
+       if (appending)
+               /* We are appending data, budget for inode change */
+               req.dirtied_ino = 1;
+
+       err = ubifs_budget_space(c, &req);
+       if (unlikely(err))
+               return err;
+
+       page = __grab_cache_page(mapping, index);
+       if (unlikely(!page)) {
+               ubifs_release_budget(c, &req);
+               return -ENOMEM;
+       }
+
+       if (!PageUptodate(page)) {
+               if (!(pos & PAGE_CACHE_MASK) && len == PAGE_CACHE_SIZE)
+                       SetPageChecked(page);
+               else {
+                       err = do_readpage(page);
+                       if (err) {
+                               unlock_page(page);
+                               page_cache_release(page);
+                               return err;
+                       }
+               }
+
+               SetPageUptodate(page);
+               ClearPageError(page);
+       }
+
+       if (PagePrivate(page))
+               /*
+                * The page is dirty, which means it was budgeted twice:
+                *   o first time the budget was allocated by the task which
+                *     made the page dirty and set the PG_private flag;
+                *   o and then we budgeted for it for the second time at the
+                *     very beginning of this function.
+                *
+                * So what we have to do is to release the page budget we
+                * allocated.
+                */
+               release_new_page_budget(c);
+       else if (!PageChecked(page))
+               /*
+                * We are changing a page which already exists on the media.
+                * This means that changing the page does not make the amount
+                * of indexing information larger, and this part of the budget
+                * which we have already acquired may be released.
+                */
+               ubifs_convert_page_budget(c);
+
+       if (appending) {
+               struct ubifs_inode *ui = ubifs_inode(inode);
+
+               /*
+                * 'ubifs_write_end()' is optimized from the fast-path part of
+                * 'ubifs_write_begin()' and expects the @ui_mutex to be locked
+                * if data is appended.
+                */
+               mutex_lock(&ui->ui_mutex);
+               if (ui->dirty)
+                       /*
+                        * The inode is dirty already, so we may free the
+                        * budget we allocated.
+                        */
+                       ubifs_release_dirty_inode_budget(c, ui);
+       }
+
+       *pagep = page;
+       return 0;
+}
+
+/**
+ * allocate_budget - allocate budget for 'ubifs_write_begin()'.
+ * @c: UBIFS file-system description object
+ * @page: page to allocate budget for
+ * @ui: UBIFS inode object the page belongs to
+ * @appending: non-zero if the page is appended
+ *
+ * This is a helper function for 'ubifs_write_begin()' which allocates budget
+ * for the operation. The budget is allocated differently depending on whether
+ * this is appending, whether the page is dirty or not, and so on. This
+ * function leaves the @ui->ui_mutex locked in case of appending. Returns zero
+ * in case of success and %-ENOSPC in case of failure.
+ */
+static int allocate_budget(struct ubifs_info *c, struct page *page,
+                          struct ubifs_inode *ui, int appending)
+{
+       struct ubifs_budget_req req = { .fast = 1 };
+
+       if (PagePrivate(page)) {
+               if (!appending)
+                       /*
+                        * The page is dirty and we are not appending, which
+                        * means no budget is needed at all.
+                        */
+                       return 0;
+
+               mutex_lock(&ui->ui_mutex);
+               if (ui->dirty)
+                       /*
+                        * The page is dirty and we are appending, so the inode
+                        * has to be marked as dirty. However, it is already
+                        * dirty, so we do not need any budget. We may return,
+                        * but @ui->ui_mutex hast to be left locked because we
+                        * should prevent write-back from flushing the inode
+                        * and freeing the budget. The lock will be released in
+                        * 'ubifs_write_end()'.
+                        */
+                       return 0;
+
+               /*
+                * The page is dirty, we are appending, the inode is clean, so
+                * we need to budget the inode change.
+                */
+               req.dirtied_ino = 1;
+       } else {
+               if (PageChecked(page))
+                       /*
+                        * The page corresponds to a hole and does not
+                        * exist on the media. So changing it makes
+                        * make the amount of indexing information
+                        * larger, and we have to budget for a new
+                        * page.
+                        */
+                       req.new_page = 1;
+               else
+                       /*
+                        * Not a hole, the change will not add any new
+                        * indexing information, budget for page
+                        * change.
+                        */
+                       req.dirtied_page = 1;
+
+               if (appending) {
+                       mutex_lock(&ui->ui_mutex);
+                       if (!ui->dirty)
+                               /*
+                                * The inode is clean but we will have to mark
+                                * it as dirty because we are appending. This
+                                * needs a budget.
+                                */
+                               req.dirtied_ino = 1;
+               }
+       }
+
+       return ubifs_budget_space(c, &req);
+}
+
+/*
+ * This function is called when a page of data is going to be written. Since
+ * the page of data will not necessarily go to the flash straight away, UBIFS
+ * has to reserve space on the media for it, which is done by means of
+ * budgeting.
+ *
+ * This is the hot-path of the file-system and we are trying to optimize it as
+ * much as possible. For this reasons it is split on 2 parts - slow and fast.
+ *
+ * There many budgeting cases:
+ *     o a new page is appended - we have to budget for a new page and for
+ *       changing the inode; however, if the inode is already dirty, there is
+ *       no need to budget for it;
+ *     o an existing clean page is changed - we have budget for it; if the page
+ *       does not exist on the media (a hole), we have to budget for a new
+ *       page; otherwise, we may budget for changing an existing page; the
+ *       difference between these cases is that changing an existing page does
+ *       not introduce anything new to the FS indexing information, so it does
+ *       not grow, and smaller budget is acquired in this case;
+ *     o an existing dirty page is changed - no need to budget at all, because
+ *       the page budget has been acquired by earlier, when the page has been
+ *       marked dirty.
+ *
+ * UBIFS budgeting sub-system may force write-back if it thinks there is no
+ * space to reserve. This imposes some locking restrictions and makes it
+ * impossible to take into account the above cases, and makes it impossible to
+ * optimize budgeting.
+ *
+ * The solution for this is that the fast path of 'ubifs_write_begin()' assumes
+ * there is a plenty of flash space and the budget will be acquired quickly,
+ * without forcing write-back. The slow path does not make this assumption.
+ */
+static int ubifs_write_begin(struct file *file, struct address_space *mapping,
+                            loff_t pos, unsigned len, unsigned flags,
+                            struct page **pagep, void **fsdata)
+{
+       struct inode *inode = mapping->host;
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+       struct ubifs_inode *ui = ubifs_inode(inode);
+       pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+       int uninitialized_var(err), appending = !!(pos + len > inode->i_size);
+       struct page *page;
+
+
+       ubifs_assert(ubifs_inode(inode)->ui_size == inode->i_size);
+
+       if (unlikely(c->ro_media))
+               return -EROFS;
+
+       /* Try out the fast-path part first */
+       page = __grab_cache_page(mapping, index);
+       if (unlikely(!page))
+               return -ENOMEM;
+
+       if (!PageUptodate(page)) {
+               /* The page is not loaded from the flash */
+               if (!(pos & PAGE_CACHE_MASK) && len == PAGE_CACHE_SIZE)
+                       /*
+                        * We change whole page so no need to load it. But we
+                        * have to set the @PG_checked flag to make the further
+                        * code the page is new. This might be not true, but it
+                        * is better to budget more that to read the page from
+                        * the media.
+                        */
+                       SetPageChecked(page);
+               else {
+                       err = do_readpage(page);
+                       if (err) {
+                               unlock_page(page);
+                               page_cache_release(page);
+                               return err;
+                       }
+               }
+
+               SetPageUptodate(page);
+               ClearPageError(page);
+       }
+
+       err = allocate_budget(c, page, ui, appending);
+       if (unlikely(err)) {
+               ubifs_assert(err == -ENOSPC);
+               /*
+                * Budgeting failed which means it would have to force
+                * write-back but didn't, because we set the @fast flag in the
+                * request. Write-back cannot be done now, while we have the
+                * page locked, because it would deadlock. Unlock and free
+                * everything and fall-back to slow-path.
+                */
+               if (appending) {
+                       ubifs_assert(mutex_is_locked(&ui->ui_mutex));
+                       mutex_unlock(&ui->ui_mutex);
+               }
+               unlock_page(page);
+               page_cache_release(page);
+
+               return write_begin_slow(mapping, pos, len, pagep);
+       }
+
+       /*
+        * Whee, we aquired budgeting quickly - without involving
+        * garbage-collection, committing or forceing write-back. We return
+        * with @ui->ui_mutex locked if we are appending pages, and unlocked
+        * otherwise. This is an optimization (slightly hacky though).
+        */
+       *pagep = page;
+       return 0;
+
+}
+
+/**
+ * cancel_budget - cancel budget.
+ * @c: UBIFS file-system description object
+ * @page: page to cancel budget for
+ * @ui: UBIFS inode object the page belongs to
+ * @appending: non-zero if the page is appended
+ *
+ * This is a helper function for a page write operation. It unlocks the
+ * @ui->ui_mutex in case of appending.
+ */
+static void cancel_budget(struct ubifs_info *c, struct page *page,
+                         struct ubifs_inode *ui, int appending)
+{
+       if (appending) {
+               if (!ui->dirty)
+                       ubifs_release_dirty_inode_budget(c, ui);
+               mutex_unlock(&ui->ui_mutex);
+       }
+       if (!PagePrivate(page)) {
+               if (PageChecked(page))
+                       release_new_page_budget(c);
+               else
+                       release_existing_page_budget(c);
+       }
+}
+
+static int ubifs_write_end(struct file *file, struct address_space *mapping,
+                          loff_t pos, unsigned len, unsigned copied,
+                          struct page *page, void *fsdata)
+{
+       struct inode *inode = mapping->host;
+       struct ubifs_inode *ui = ubifs_inode(inode);
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+       loff_t end_pos = pos + len;
+       int appending = !!(end_pos > inode->i_size);
+
+       dbg_gen("ino %lu, pos %llu, pg %lu, len %u, copied %d, i_size %lld",
+               inode->i_ino, pos, page->index, len, copied, inode->i_size);
+
+       if (unlikely(copied < len && len == PAGE_CACHE_SIZE)) {
+               /*
+                * VFS copied less data to the page that it intended and
+                * declared in its '->write_begin()' call via the @len
+                * argument. If the page was not up-to-date, and @len was
+                * @PAGE_CACHE_SIZE, the 'ubifs_write_begin()' function did
+                * not load it from the media (for optimization reasons). This
+                * means that part of the page contains garbage. So read the
+                * page now.
+                */
+               dbg_gen("copied %d instead of %d, read page and repeat",
+                       copied, len);
+               cancel_budget(c, page, ui, appending);
+
+               /*
+                * Return 0 to force VFS to repeat the whole operation, or the
+                * error code if 'do_readpage()' failes.
+                */
+               copied = do_readpage(page);
+               goto out;
+       }
+
+       if (!PagePrivate(page)) {
+               SetPagePrivate(page);
+               atomic_long_inc(&c->dirty_pg_cnt);
+               __set_page_dirty_nobuffers(page);
+       }
+
+       if (appending) {
+               i_size_write(inode, end_pos);
+               ui->ui_size = end_pos;
+               /*
+                * Note, we do not set @I_DIRTY_PAGES (which means that the
+                * inode has dirty pages), this has been done in
+                * '__set_page_dirty_nobuffers()'.
+                */
+               __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
+               ubifs_assert(mutex_is_locked(&ui->ui_mutex));
+               mutex_unlock(&ui->ui_mutex);
+       }
+
+out:
+       unlock_page(page);
+       page_cache_release(page);
+       return copied;
+}
+
+static int ubifs_readpage(struct file *file, struct page *page)
+{
+       do_readpage(page);
+       unlock_page(page);
+       return 0;
+}
+
+static int do_writepage(struct page *page, int len)
+{
+       int err = 0, i, blen;
+       unsigned int block;
+       void *addr;
+       union ubifs_key key;
+       struct inode *inode = page->mapping->host;
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+
+#ifdef UBIFS_DEBUG
+       spin_lock(&ui->ui_lock);
+       ubifs_assert(page->index <= ui->synced_i_size << PAGE_CACHE_SIZE);
+       spin_unlock(&ui->ui_lock);
+#endif
+
+       /* Update radix tree tags */
+       set_page_writeback(page);
+
+       addr = kmap(page);
+       block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
+       i = 0;
+       while (len) {
+               blen = min_t(int, len, UBIFS_BLOCK_SIZE);
+               data_key_init(c, &key, inode->i_ino, block);
+               err = ubifs_jnl_write_data(c, inode, &key, addr, blen);
+               if (err)
+                       break;
+               if (++i >= UBIFS_BLOCKS_PER_PAGE)
+                       break;
+               block += 1;
+               addr += blen;
+               len -= blen;
+       }
+       if (err) {
+               SetPageError(page);
+               ubifs_err("cannot write page %lu of inode %lu, error %d",
+                         page->index, inode->i_ino, err);
+               ubifs_ro_mode(c, err);
+       }
+
+       ubifs_assert(PagePrivate(page));
+       if (PageChecked(page))
+               release_new_page_budget(c);
+       else
+               release_existing_page_budget(c);
+
+       atomic_long_dec(&c->dirty_pg_cnt);
+       ClearPagePrivate(page);
+       ClearPageChecked(page);
+
+       kunmap(page);
+       unlock_page(page);
+       end_page_writeback(page);
+       return err;
+}
+
+/*
+ * When writing-back dirty inodes, VFS first writes-back pages belonging to the
+ * inode, then the inode itself. For UBIFS this may cause a problem. Consider a
+ * situation when a we have an inode with size 0, then a megabyte of data is
+ * appended to the inode, then write-back starts and flushes some amount of the
+ * dirty pages, the journal becomes full, commit happens and finishes, and then
+ * an unclean reboot happens. When the file system is mounted next time, the
+ * inode size would still be 0, but there would be many pages which are beyond
+ * the inode size, they would be indexed and consume flash space. Because the
+ * journal has been committed, the replay would not be able to detect this
+ * situation and correct the inode size. This means UBIFS would have to scan
+ * whole index and correct all inode sizes, which is long an unacceptable.
+ *
+ * To prevent situations like this, UBIFS writes pages back only if they are
+ * within last synchronized inode size, i.e. the the size which has been
+ * written to the flash media last time. Otherwise, UBIFS forces inode
+ * write-back, thus making sure the on-flash inode contains current inode size,
+ * and then keeps writing pages back.
+ *
+ * Some locking issues explanation. 'ubifs_writepage()' first is called with
+ * the page locked, and it locks @ui_mutex. However, write-back does take inode
+ * @i_mutex, which means other VFS operations may be run on this inode at the
+ * same time. And the problematic one is truncation to smaller size, from where
+ * we have to call 'vmtruncate()', which first changes @inode->i_size, then
+ * drops the truncated pages. And while dropping the pages, it takes the page
+ * lock. This means that 'do_truncation()' cannot call 'vmtruncate()' with
+ * @ui_mutex locked, because it would deadlock with 'ubifs_writepage()'. This
+ * means that @inode->i_size is changed while @ui_mutex is unlocked.
+ *
+ * But in 'ubifs_writepage()' we have to guarantee that we do not write beyond
+ * inode size. How do we do this if @inode->i_size may became smaller while we
+ * are in the middle of 'ubifs_writepage()'? The UBIFS solution is the
+ * @ui->ui_isize "shadow" field which UBIFS uses instead of @inode->i_size
+ * internally and updates it under @ui_mutex.
+ *
+ * Q: why we do not worry that if we race with truncation, we may end up with a
+ * situation when the inode is truncated while we are in the middle of
+ * 'do_writepage()', so we do write beyond inode size?
+ * A: If we are in the middle of 'do_writepage()', truncation would be locked
+ * on the page lock and it would not write the truncated inode node to the
+ * journal before we have finished.
+ */
+static int ubifs_writepage(struct page *page, struct writeback_control *wbc)
+{
+       struct inode *inode = page->mapping->host;
+       struct ubifs_inode *ui = ubifs_inode(inode);
+       loff_t i_size =  i_size_read(inode), synced_i_size;
+       pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
+       int err, len = i_size & (PAGE_CACHE_SIZE - 1);
+       void *kaddr;
+
+       dbg_gen("ino %lu, pg %lu, pg flags %#lx",
+               inode->i_ino, page->index, page->flags);
+       ubifs_assert(PagePrivate(page));
+
+       /* Is the page fully outside @i_size? (truncate in progress) */
+       if (page->index > end_index || (page->index == end_index && !len)) {
+               err = 0;
+               goto out_unlock;
+       }
+
+       spin_lock(&ui->ui_lock);
+       synced_i_size = ui->synced_i_size;
+       spin_unlock(&ui->ui_lock);
+
+       /* Is the page fully inside @i_size? */
+       if (page->index < end_index) {
+               if (page->index >= synced_i_size >> PAGE_CACHE_SHIFT) {
+                       err = inode->i_sb->s_op->write_inode(inode, 1);
+                       if (err)
+                               goto out_unlock;
+                       /*
+                        * The inode has been written, but the write-buffer has
+                        * not been synchronized, so in case of an unclean
+                        * reboot we may end up with some pages beyond inode
+                        * size, but they would be in the journal (because
+                        * commit flushes write buffers) and recovery would deal
+                        * with this.
+                        */
+               }
+               return do_writepage(page, PAGE_CACHE_SIZE);
+       }
+
+       /*
+        * The page straddles @i_size. It must be zeroed out on each and every
+        * writepage invocation because it may be mmapped. "A file is mapped
+        * in multiples of the page size. For a file that is not a multiple of
+        * the page size, the remaining memory is zeroed when mapped, and
+        * writes to that region are not written out to the file."
+        */
+       kaddr = kmap_atomic(page, KM_USER0);
+       memset(kaddr + len, 0, PAGE_CACHE_SIZE - len);
+       flush_dcache_page(page);
+       kunmap_atomic(kaddr, KM_USER0);
+
+       if (i_size > synced_i_size) {
+               err = inode->i_sb->s_op->write_inode(inode, 1);
+               if (err)
+                       goto out_unlock;
+       }
+
+       return do_writepage(page, len);
+
+out_unlock:
+       unlock_page(page);
+       return err;
+}
+
+/**
+ * do_attr_changes - change inode attributes.
+ * @inode: inode to change attributes for
+ * @attr: describes attributes to change
+ */
+static void do_attr_changes(struct inode *inode, const struct iattr *attr)
+{
+       if (attr->ia_valid & ATTR_UID)
+               inode->i_uid = attr->ia_uid;
+       if (attr->ia_valid & ATTR_GID)
+               inode->i_gid = attr->ia_gid;
+       if (attr->ia_valid & ATTR_ATIME)
+               inode->i_atime = timespec_trunc(attr->ia_atime,
+                                               inode->i_sb->s_time_gran);
+       if (attr->ia_valid & ATTR_MTIME)
+               inode->i_mtime = timespec_trunc(attr->ia_mtime,
+                                               inode->i_sb->s_time_gran);
+       if (attr->ia_valid & ATTR_CTIME)
+               inode->i_ctime = timespec_trunc(attr->ia_ctime,
+                                               inode->i_sb->s_time_gran);
+       if (attr->ia_valid & ATTR_MODE) {
+               umode_t mode = attr->ia_mode;
+
+               if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+                       mode &= ~S_ISGID;
+               inode->i_mode = mode;
+       }
+}
+
+/**
+ * do_truncation - truncate an inode.
+ * @c: UBIFS file-system description object
+ * @inode: inode to truncate
+ * @attr: inode attribute changes description
+ *
+ * This function implements VFS '->setattr()' call when the inode is truncated
+ * to a smaller size. Returns zero in case of success and a negative error code
+ * in case of failure.
+ */
+static int do_truncation(struct ubifs_info *c, struct inode *inode,
+                        const struct iattr *attr)
+{
+       int err;
+       struct ubifs_budget_req req;
+       loff_t old_size = inode->i_size, new_size = attr->ia_size;
+       int offset = new_size & (UBIFS_BLOCK_SIZE - 1);
+       struct ubifs_inode *ui = ubifs_inode(inode);
+
+       dbg_gen("ino %lu, size %lld -> %lld", inode->i_ino, old_size, new_size);
+       memset(&req, 0, sizeof(struct ubifs_budget_req));
+
+       /*
+        * If this is truncation to a smaller size, and we do not truncate on a
+        * block boundary, budget for changing one data block, because the last
+        * block will be re-written.
+        */
+       if (new_size & (UBIFS_BLOCK_SIZE - 1))
+               req.dirtied_page = 1;
+
+       req.dirtied_ino = 1;
+       /* A funny way to budget for truncation node */
+       req.dirtied_ino_d = UBIFS_TRUN_NODE_SZ;
+       err = ubifs_budget_space(c, &req);
+       if (err)
+               return err;
+
+       err = vmtruncate(inode, new_size);
+       if (err)
+               goto out_budg;
+
+       if (offset) {
+               pgoff_t index = new_size >> PAGE_CACHE_SHIFT;
+               struct page *page;
+
+               page = find_lock_page(inode->i_mapping, index);
+               if (page) {
+                       if (PageDirty(page)) {
+                               /*
+                                * 'ubifs_jnl_truncate()' will try to truncate
+                                * the last data node, but it contains
+                                * out-of-date data because the page is dirty.
+                                * Write the page now, so that
+                                * 'ubifs_jnl_truncate()' will see an already
+                                * truncated (and up to date) data node.
+                                */
+                               ubifs_assert(PagePrivate(page));
+
+                               clear_page_dirty_for_io(page);
+                               if (UBIFS_BLOCKS_PER_PAGE_SHIFT)
+                                       offset = new_size &
+                                                (PAGE_CACHE_SIZE - 1);
+                               err = do_writepage(page, offset);
+                               page_cache_release(page);
+                               if (err)
+                                       goto out_budg;
+                               /*
+                                * We could now tell 'ubifs_jnl_truncate()' not
+                                * to read the last block.
+                                */
+                       } else {
+                               /*
+                                * We could 'kmap()' the page and pass the data
+                                * to 'ubifs_jnl_truncate()' to save it from
+                                * having to read it.
+                                */
+                               unlock_page(page);
+                               page_cache_release(page);
+                       }
+               }
+       }
+
+       mutex_lock(&ui->ui_mutex);
+       ui->ui_size = inode->i_size;
+       /* Truncation changes inode [mc]time */
+       inode->i_mtime = inode->i_ctime = ubifs_current_time(inode);
+       /* The other attributes may be changed at the same time as well */
+       do_attr_changes(inode, attr);
+
+       err = ubifs_jnl_truncate(c, inode, old_size, new_size);
+       mutex_unlock(&ui->ui_mutex);
+out_budg:
+       ubifs_release_budget(c, &req);
+       return err;
+}
+
+/**
+ * do_setattr - change inode attributes.
+ * @c: UBIFS file-system description object
+ * @inode: inode to change attributes for
+ * @attr: inode attribute changes description
+ *
+ * This function implements VFS '->setattr()' call for all cases except
+ * truncations to smaller size. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+static int do_setattr(struct ubifs_info *c, struct inode *inode,
+                     const struct iattr *attr)
+{
+       int err, release;
+       loff_t new_size = attr->ia_size;
+       struct ubifs_inode *ui = ubifs_inode(inode);
+       struct ubifs_budget_req req = { .dirtied_ino = 1,
+                                       .dirtied_ino_d = ui->data_len };
+
+       err = ubifs_budget_space(c, &req);
+       if (err)
+               return err;
+
+       if (attr->ia_valid & ATTR_SIZE) {
+               dbg_gen("size %lld -> %lld", inode->i_size, new_size);
+               err = vmtruncate(inode, new_size);
+               if (err)
+                       goto out;
+       }
+
+       mutex_lock(&ui->ui_mutex);
+       if (attr->ia_valid & ATTR_SIZE) {
+               /* Truncation changes inode [mc]time */
+               inode->i_mtime = inode->i_ctime = ubifs_current_time(inode);
+               /* 'vmtruncate()' changed @i_size, update @ui_size */
+               ui->ui_size = inode->i_size;
+       }
+
+       do_attr_changes(inode, attr);
+
+       release = ui->dirty;
+       if (attr->ia_valid & ATTR_SIZE)
+               /*
+                * Inode length changed, so we have to make sure
+                * @I_DIRTY_DATASYNC is set.
+                */
+                __mark_inode_dirty(inode, I_DIRTY_SYNC | I_DIRTY_DATASYNC);
+       else
+               mark_inode_dirty_sync(inode);
+       mutex_unlock(&ui->ui_mutex);
+
+       if (release)
+               ubifs_release_budget(c, &req);
+       if (IS_SYNC(inode))
+               err = inode->i_sb->s_op->write_inode(inode, 1);
+       return err;
+
+out:
+       ubifs_release_budget(c, &req);
+       return err;
+}
+
+int ubifs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+       int err;
+       struct inode *inode = dentry->d_inode;
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+
+       dbg_gen("ino %lu, ia_valid %#x", inode->i_ino, attr->ia_valid);
+       err = inode_change_ok(inode, attr);
+       if (err)
+               return err;
+
+       err = dbg_check_synced_i_size(inode);
+       if (err)
+               return err;
+
+       if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size < inode->i_size)
+               /* Truncation to a smaller size */
+               err = do_truncation(c, inode, attr);
+       else
+               err = do_setattr(c, inode, attr);
+
+       return err;
+}
+
+static void ubifs_invalidatepage(struct page *page, unsigned long offset)
+{
+       struct inode *inode = page->mapping->host;
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+
+       ubifs_assert(PagePrivate(page));
+       if (offset)
+               /* Partial page remains dirty */
+               return;
+
+       if (PageChecked(page))
+               release_new_page_budget(c);
+       else
+               release_existing_page_budget(c);
+
+       atomic_long_dec(&c->dirty_pg_cnt);
+       ClearPagePrivate(page);
+       ClearPageChecked(page);
+}
+
+static void *ubifs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+       struct ubifs_inode *ui = ubifs_inode(dentry->d_inode);
+
+       nd_set_link(nd, ui->data);
+       return NULL;
+}
+
+int ubifs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+       struct inode *inode = dentry->d_inode;
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+       int err;
+
+       dbg_gen("syncing inode %lu", inode->i_ino);
+
+       /*
+        * VFS has already synchronized dirty pages for this inode. Synchronize
+        * the inode unless this is a 'datasync()' call.
+        */
+       if (!datasync || (inode->i_state & I_DIRTY_DATASYNC)) {
+               err = inode->i_sb->s_op->write_inode(inode, 1);
+               if (err)
+                       return err;
+       }
+
+       /*
+        * Nodes related to this inode may still sit in a write-buffer. Flush
+        * them.
+        */
+       err = ubifs_sync_wbufs_by_inode(c, inode);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+/**
+ * mctime_update_needed - check if mtime or ctime update is needed.
+ * @inode: the inode to do the check for
+ * @now: current time
+ *
+ * This helper function checks if the inode mtime/ctime should be updated or
+ * not. If current values of the time-stamps are within the UBIFS inode time
+ * granularity, they are not updated. This is an optimization.
+ */
+static inline int mctime_update_needed(const struct inode *inode,
+                                      const struct timespec *now)
+{
+       if (!timespec_equal(&inode->i_mtime, now) ||
+           !timespec_equal(&inode->i_ctime, now))
+               return 1;
+       return 0;
+}
+
+/**
+ * update_ctime - update mtime and ctime of an inode.
+ * @c: UBIFS file-system description object
+ * @inode: inode to update
+ *
+ * This function updates mtime and ctime of the inode if it is not equivalent to
+ * current time. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int update_mctime(struct ubifs_info *c, struct inode *inode)
+{
+       struct timespec now = ubifs_current_time(inode);
+       struct ubifs_inode *ui = ubifs_inode(inode);
+
+       if (mctime_update_needed(inode, &now)) {
+               int err, release;
+               struct ubifs_budget_req req = { .dirtied_ino = 1,
+                                               .dirtied_ino_d = ui->data_len };
+
+               err = ubifs_budget_space(c, &req);
+               if (err)
+                       return err;
+
+               mutex_lock(&ui->ui_mutex);
+               inode->i_mtime = 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);
+       }
+
+       return 0;
+}
+
+static ssize_t ubifs_aio_write(struct kiocb *iocb, const struct iovec *iov,
+                              unsigned long nr_segs, loff_t pos)
+{
+       int err;
+       ssize_t ret;
+       struct inode *inode = iocb->ki_filp->f_mapping->host;
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+
+       err = update_mctime(c, inode);
+       if (err)
+               return err;
+
+       ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
+       if (ret < 0)
+               return ret;
+
+       if (ret > 0 && (IS_SYNC(inode) || iocb->ki_filp->f_flags & O_SYNC)) {
+               err = ubifs_sync_wbufs_by_inode(c, inode);
+               if (err)
+                       return err;
+       }
+
+       return ret;
+}
+
+static int ubifs_set_page_dirty(struct page *page)
+{
+       int ret;
+
+       ret = __set_page_dirty_nobuffers(page);
+       /*
+        * An attempt to dirty a page without budgeting for it - should not
+        * happen.
+        */
+       ubifs_assert(ret == 0);
+       return ret;
+}
+
+static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags)
+{
+       /*
+        * An attempt to release a dirty page without budgeting for it - should
+        * not happen.
+        */
+       if (PageWriteback(page))
+               return 0;
+       ubifs_assert(PagePrivate(page));
+       ubifs_assert(0);
+       ClearPagePrivate(page);
+       ClearPageChecked(page);
+       return 1;
+}
+
+/*
+ * mmap()d file has taken write protection fault and is being made
+ * writable. UBIFS must ensure page is budgeted for.
+ */
+static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page)
+{
+       struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
+       struct timespec now = ubifs_current_time(inode);
+       struct ubifs_budget_req req = { .new_page = 1 };
+       int err, update_time;
+
+       dbg_gen("ino %lu, pg %lu, i_size %lld", inode->i_ino, page->index,
+               i_size_read(inode));
+       ubifs_assert(!(inode->i_sb->s_flags & MS_RDONLY));
+
+       if (unlikely(c->ro_media))
+               return -EROFS;
+
+       /*
+        * We have not locked @page so far so we may budget for changing the
+        * page. Note, we cannot do this after we locked the page, because
+        * budgeting may cause write-back which would cause deadlock.
+        *
+        * At the moment we do not know whether the page is dirty or not, so we
+        * assume that it is not and budget for a new page. We could look at
+        * the @PG_private flag and figure this out, but we may race with write
+        * back and the page state may change by the time we lock it, so this
+        * would need additional care. We do not bother with this at the
+        * moment, although it might be good idea to do. Instead, we allocate
+        * budget for a new page and amend it later on if the page was in fact
+        * dirty.
+        *
+        * The budgeting-related logic of this function is similar to what we
+        * do in 'ubifs_write_begin()' and 'ubifs_write_end()'. Glance there
+        * for more comments.
+        */
+       update_time = mctime_update_needed(inode, &now);
+       if (update_time)
+               /*
+                * We have to change inode time stamp which requires extra
+                * budgeting.
+                */
+               req.dirtied_ino = 1;
+
+       err = ubifs_budget_space(c, &req);
+       if (unlikely(err)) {
+               if (err == -ENOSPC)
+                       ubifs_warn("out of space for mmapped file "
+                                  "(inode number %lu)", inode->i_ino);
+               return err;
+       }
+
+       lock_page(page);
+       if (unlikely(page->mapping != inode->i_mapping ||
+                    page_offset(page) > i_size_read(inode))) {
+               /* Page got truncated out from underneath us */
+               err = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (PagePrivate(page))
+               release_new_page_budget(c);
+       else {
+               if (!PageChecked(page))
+                       ubifs_convert_page_budget(c);
+               SetPagePrivate(page);
+               atomic_long_inc(&c->dirty_pg_cnt);
+               __set_page_dirty_nobuffers(page);
+       }
+
+       if (update_time) {
+               int release;
+               struct ubifs_inode *ui = ubifs_inode(inode);
+
+               mutex_lock(&ui->ui_mutex);
+               inode->i_mtime = inode->i_ctime = ubifs_current_time(inode);
+               release = ui->dirty;
+               mark_inode_dirty_sync(inode);
+               mutex_unlock(&ui->ui_mutex);
+               if (release)
+                       ubifs_release_dirty_inode_budget(c, ui);
+       }
+
+       unlock_page(page);
+       return 0;
+
+out_unlock:
+       unlock_page(page);
+       ubifs_release_budget(c, &req);
+       return err;
+}
+
+static struct vm_operations_struct ubifs_file_vm_ops = {
+       .fault        = filemap_fault,
+       .page_mkwrite = ubifs_vm_page_mkwrite,
+};
+
+static int ubifs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       int err;
+
+       /* 'generic_file_mmap()' takes care of NOMMU case */
+       err = generic_file_mmap(file, vma);
+       if (err)
+               return err;
+       vma->vm_ops = &ubifs_file_vm_ops;
+       return 0;
+}
+
+struct address_space_operations ubifs_file_address_operations = {
+       .readpage       = ubifs_readpage,
+       .writepage      = ubifs_writepage,
+       .write_begin    = ubifs_write_begin,
+       .write_end      = ubifs_write_end,
+       .invalidatepage = ubifs_invalidatepage,
+       .set_page_dirty = ubifs_set_page_dirty,
+       .releasepage    = ubifs_releasepage,
+};
+
+struct inode_operations ubifs_file_inode_operations = {
+       .setattr     = ubifs_setattr,
+       .getattr     = ubifs_getattr,
+#ifdef CONFIG_UBIFS_FS_XATTR
+       .setxattr    = ubifs_setxattr,
+       .getxattr    = ubifs_getxattr,
+       .listxattr   = ubifs_listxattr,
+       .removexattr = ubifs_removexattr,
+#endif
+};
+
+struct inode_operations ubifs_symlink_inode_operations = {
+       .readlink    = generic_readlink,
+       .follow_link = ubifs_follow_link,
+       .setattr     = ubifs_setattr,
+       .getattr     = ubifs_getattr,
+};
+
+struct file_operations ubifs_file_operations = {
+       .llseek         = generic_file_llseek,
+       .read           = do_sync_read,
+       .write          = do_sync_write,
+       .aio_read       = generic_file_aio_read,
+       .aio_write      = ubifs_aio_write,
+       .mmap           = ubifs_file_mmap,
+       .fsync          = ubifs_fsync,
+       .unlocked_ioctl = ubifs_ioctl,
+       .splice_read    = generic_file_splice_read,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = ubifs_compat_ioctl,
+#endif
+};
diff --git a/fs/ubifs/find.c b/fs/ubifs/find.c
new file mode 100644 (file)
index 0000000..10394c5
--- /dev/null
@@ -0,0 +1,975 @@
+/*
+ * 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 functions for finding LEBs for various purposes e.g.
+ * garbage collection. In general, lprops category heaps and lists are used
+ * for fast access, falling back on scanning the LPT as a last resort.
+ */
+
+#include <linux/sort.h>
+#include "ubifs.h"
+
+/**
+ * struct scan_data - data provided to scan callback functions
+ * @min_space: minimum number of bytes for which to scan
+ * @pick_free: whether it is OK to scan for empty LEBs
+ * @lnum: LEB number found is returned here
+ * @exclude_index: whether to exclude index LEBs
+ */
+struct scan_data {
+       int min_space;
+       int pick_free;
+       int lnum;
+       int exclude_index;
+};
+
+/**
+ * valuable - determine whether LEB properties are valuable.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties
+ *
+ * This function return %1 if the LEB properties should be added to the LEB
+ * properties tree in memory. Otherwise %0 is returned.
+ */
+static int valuable(struct ubifs_info *c, const struct ubifs_lprops *lprops)
+{
+       int n, cat = lprops->flags & LPROPS_CAT_MASK;
+       struct ubifs_lpt_heap *heap;
+
+       switch (cat) {
+       case LPROPS_DIRTY:
+       case LPROPS_DIRTY_IDX:
+       case LPROPS_FREE:
+               heap = &c->lpt_heap[cat - 1];
+               if (heap->cnt < heap->max_cnt)
+                       return 1;
+               if (lprops->free + lprops->dirty >= c->dark_wm)
+                       return 1;
+               return 0;
+       case LPROPS_EMPTY:
+               n = c->lst.empty_lebs + c->freeable_cnt -
+                   c->lst.taken_empty_lebs;
+               if (n < c->lsave_cnt)
+                       return 1;
+               return 0;
+       case LPROPS_FREEABLE:
+               return 1;
+       case LPROPS_FRDI_IDX:
+               return 1;
+       }
+       return 0;
+}
+
+/**
+ * scan_for_dirty_cb - dirty space scan callback.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @data: information passed to and from the caller of the scan
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_for_dirty_cb(struct ubifs_info *c,
+                            const struct ubifs_lprops *lprops, int in_tree,
+                            struct scan_data *data)
+{
+       int ret = LPT_SCAN_CONTINUE;
+
+       /* Exclude LEBs that are currently in use */
+       if (lprops->flags & LPROPS_TAKEN)
+               return LPT_SCAN_CONTINUE;
+       /* Determine whether to add these LEB properties to the tree */
+       if (!in_tree && valuable(c, lprops))
+               ret |= LPT_SCAN_ADD;
+       /* Exclude LEBs with too little space */
+       if (lprops->free + lprops->dirty < data->min_space)
+               return ret;
+       /* If specified, exclude index LEBs */
+       if (data->exclude_index && lprops->flags & LPROPS_INDEX)
+               return ret;
+       /* If specified, exclude empty or freeable LEBs */
+       if (lprops->free + lprops->dirty == c->leb_size) {
+               if (!data->pick_free)
+                       return ret;
+       /* Exclude LEBs with too little dirty space (unless it is empty) */
+       } else if (lprops->dirty < c->dead_wm)
+               return ret;
+       /* Finally we found space */
+       data->lnum = lprops->lnum;
+       return LPT_SCAN_ADD | LPT_SCAN_STOP;
+}
+
+/**
+ * scan_for_dirty - find a data LEB with free space.
+ * @c: the UBIFS file-system description object
+ * @min_space: minimum amount free plus dirty space the returned LEB has to
+ *             have
+ * @pick_free: if it is OK to return a free or freeable LEB
+ * @exclude_index: whether to exclude index LEBs
+ *
+ * This function returns a pointer to the LEB properties found or a negative
+ * error code.
+ */
+static const struct ubifs_lprops *scan_for_dirty(struct ubifs_info *c,
+                                                int min_space, int pick_free,
+                                                int exclude_index)
+{
+       const struct ubifs_lprops *lprops;
+       struct ubifs_lpt_heap *heap;
+       struct scan_data data;
+       int err, i;
+
+       /* There may be an LEB with enough dirty space on the free heap */
+       heap = &c->lpt_heap[LPROPS_FREE - 1];
+       for (i = 0; i < heap->cnt; i++) {
+               lprops = heap->arr[i];
+               if (lprops->free + lprops->dirty < min_space)
+                       continue;
+               if (lprops->dirty < c->dead_wm)
+                       continue;
+               return lprops;
+       }
+       /*
+        * A LEB may have fallen off of the bottom of the dirty heap, and ended
+        * up as uncategorized even though it has enough dirty space for us now,
+        * so check the uncategorized list. N.B. neither empty nor freeable LEBs
+        * can end up as uncategorized because they are kept on lists not
+        * finite-sized heaps.
+        */
+       list_for_each_entry(lprops, &c->uncat_list, list) {
+               if (lprops->flags & LPROPS_TAKEN)
+                       continue;
+               if (lprops->free + lprops->dirty < min_space)
+                       continue;
+               if (exclude_index && (lprops->flags & LPROPS_INDEX))
+                       continue;
+               if (lprops->dirty < c->dead_wm)
+                       continue;
+               return lprops;
+       }
+       /* We have looked everywhere in main memory, now scan the flash */
+       if (c->pnodes_have >= c->pnode_cnt)
+               /* All pnodes are in memory, so skip scan */
+               return ERR_PTR(-ENOSPC);
+       data.min_space = min_space;
+       data.pick_free = pick_free;
+       data.lnum = -1;
+       data.exclude_index = exclude_index;
+       err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum,
+                                   (ubifs_lpt_scan_callback)scan_for_dirty_cb,
+                                   &data);
+       if (err)
+               return ERR_PTR(err);
+       ubifs_assert(data.lnum >= c->main_first && data.lnum < c->leb_cnt);
+       c->lscan_lnum = data.lnum;
+       lprops = ubifs_lpt_lookup_dirty(c, data.lnum);
+       if (IS_ERR(lprops))
+               return lprops;
+       ubifs_assert(lprops->lnum == data.lnum);
+       ubifs_assert(lprops->free + lprops->dirty >= min_space);
+       ubifs_assert(lprops->dirty >= c->dead_wm ||
+                    (pick_free &&
+                     lprops->free + lprops->dirty == c->leb_size));
+       ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
+       ubifs_assert(!exclude_index || !(lprops->flags & LPROPS_INDEX));
+       return lprops;
+}
+
+/**
+ * ubifs_find_dirty_leb - find a dirty LEB for the Garbage Collector.
+ * @c: the UBIFS file-system description object
+ * @ret_lp: LEB properties are returned here on exit
+ * @min_space: minimum amount free plus dirty space the returned LEB has to
+ *             have
+ * @pick_free: controls whether it is OK to pick empty or index LEBs
+ *
+ * This function tries to find a dirty logical eraseblock which has at least
+ * @min_space free and dirty space. It prefers to take an LEB from the dirty or
+ * dirty index heap, and it falls-back to LPT scanning if the heaps are empty
+ * or do not have an LEB which satisfies the @min_space criteria.
+ *
+ * Note:
+ *   o LEBs which have less than dead watermark of dirty space are never picked
+ *   by this function;
+ *
+ * Returns zero and the LEB properties of
+ * found dirty LEB in case of success, %-ENOSPC if no dirty LEB was found and a
+ * negative error code in case of other failures. The returned LEB is marked as
+ * "taken".
+ *
+ * The additional @pick_free argument controls if this function has to return a
+ * free or freeable LEB if one is present. For example, GC must to set it to %1,
+ * when called from the journal space reservation function, because the
+ * appearance of free space may coincide with the loss of enough dirty space
+ * for GC to succeed anyway.
+ *
+ * In contrast, if the Garbage Collector is called from budgeting, it should
+ * just make free space, not return LEBs which are already free or freeable.
+ *
+ * In addition @pick_free is set to %2 by the recovery process in order to
+ * recover gc_lnum in which case an index LEB must not be returned.
+ */
+int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp,
+                        int min_space, int pick_free)
+{
+       int err = 0, sum, exclude_index = pick_free == 2 ? 1 : 0;
+       const struct ubifs_lprops *lp = NULL, *idx_lp = NULL;
+       struct ubifs_lpt_heap *heap, *idx_heap;
+
+       ubifs_get_lprops(c);
+
+       if (pick_free) {
+               int lebs, rsvd_idx_lebs = 0;
+
+               spin_lock(&c->space_lock);
+               lebs = c->lst.empty_lebs;
+               lebs += c->freeable_cnt - c->lst.taken_empty_lebs;
+
+               /*
+                * Note, the index may consume more LEBs than have been reserved
+                * for it. It is OK because it might be consolidated by GC.
+                * But if the index takes fewer LEBs than it is reserved for it,
+                * this function must avoid picking those reserved LEBs.
+                */
+               if (c->min_idx_lebs >= c->lst.idx_lebs) {
+                       rsvd_idx_lebs = c->min_idx_lebs -  c->lst.idx_lebs;
+                       exclude_index = 1;
+               }
+               spin_unlock(&c->space_lock);
+
+               /* Check if there are enough free LEBs for the index */
+               if (rsvd_idx_lebs < lebs) {
+                       /* OK, try to find an empty LEB */
+                       lp = ubifs_fast_find_empty(c);
+                       if (lp)
+                               goto found;
+
+                       /* Or a freeable LEB */
+                       lp = ubifs_fast_find_freeable(c);
+                       if (lp)
+                               goto found;
+               } else
+                       /*
+                        * We cannot pick free/freeable LEBs in the below code.
+                        */
+                       pick_free = 0;
+       } else {
+               spin_lock(&c->space_lock);
+               exclude_index = (c->min_idx_lebs >= c->lst.idx_lebs);
+               spin_unlock(&c->space_lock);
+       }
+
+       /* Look on the dirty and dirty index heaps */
+       heap = &c->lpt_heap[LPROPS_DIRTY - 1];
+       idx_heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
+
+       if (idx_heap->cnt && !exclude_index) {
+               idx_lp = idx_heap->arr[0];
+               sum = idx_lp->free + idx_lp->dirty;
+               /*
+                * Since we reserve twice as more space for the index than it
+                * actually takes, it does not make sense to pick indexing LEBs
+                * with less than half LEB of dirty space.
+                */
+               if (sum < min_space || sum < c->half_leb_size)
+                       idx_lp = NULL;
+       }
+
+       if (heap->cnt) {
+               lp = heap->arr[0];
+               if (lp->dirty + lp->free < min_space)
+                       lp = NULL;
+       }
+
+       /* Pick the LEB with most space */
+       if (idx_lp && lp) {
+               if (idx_lp->free + idx_lp->dirty >= lp->free + lp->dirty)
+                       lp = idx_lp;
+       } else if (idx_lp && !lp)
+               lp = idx_lp;
+
+       if (lp) {
+               ubifs_assert(lp->dirty >= c->dead_wm);
+               goto found;
+       }
+
+       /* Did not find a dirty LEB on the dirty heaps, have to scan */
+       dbg_find("scanning LPT for a dirty LEB");
+       lp = scan_for_dirty(c, min_space, pick_free, exclude_index);
+       if (IS_ERR(lp)) {
+               err = PTR_ERR(lp);
+               goto out;
+       }
+       ubifs_assert(lp->dirty >= c->dead_wm ||
+                    (pick_free && lp->free + lp->dirty == c->leb_size));
+
+found:
+       dbg_find("found LEB %d, free %d, dirty %d, flags %#x",
+                lp->lnum, lp->free, lp->dirty, lp->flags);
+
+       lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
+                            lp->flags | LPROPS_TAKEN, 0);
+       if (IS_ERR(lp)) {
+               err = PTR_ERR(lp);
+               goto out;
+       }
+
+       memcpy(ret_lp, lp, sizeof(struct ubifs_lprops));
+
+out:
+       ubifs_release_lprops(c);
+       return err;
+}
+
+/**
+ * scan_for_free_cb - free space scan callback.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @data: information passed to and from the caller of the scan
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_for_free_cb(struct ubifs_info *c,
+                           const struct ubifs_lprops *lprops, int in_tree,
+                           struct scan_data *data)
+{
+       int ret = LPT_SCAN_CONTINUE;
+
+       /* Exclude LEBs that are currently in use */
+       if (lprops->flags & LPROPS_TAKEN)
+               return LPT_SCAN_CONTINUE;
+       /* Determine whether to add these LEB properties to the tree */
+       if (!in_tree && valuable(c, lprops))
+               ret |= LPT_SCAN_ADD;
+       /* Exclude index LEBs */
+       if (lprops->flags & LPROPS_INDEX)
+               return ret;
+       /* Exclude LEBs with too little space */
+       if (lprops->free < data->min_space)
+               return ret;
+       /* If specified, exclude empty LEBs */
+       if (!data->pick_free && lprops->free == c->leb_size)
+               return ret;
+       /*
+        * LEBs that have only free and dirty space must not be allocated
+        * because they may have been unmapped already or they may have data
+        * that is obsolete only because of nodes that are still sitting in a
+        * wbuf.
+        */
+       if (lprops->free + lprops->dirty == c->leb_size && lprops->dirty > 0)
+               return ret;
+       /* Finally we found space */
+       data->lnum = lprops->lnum;
+       return LPT_SCAN_ADD | LPT_SCAN_STOP;
+}
+
+/**
+ * do_find_free_space - find a data LEB with free space.
+ * @c: the UBIFS file-system description object
+ * @min_space: minimum amount of free space required
+ * @pick_free: whether it is OK to scan for empty LEBs
+ * @squeeze: whether to try to find space in a non-empty LEB first
+ *
+ * This function returns a pointer to the LEB properties found or a negative
+ * error code.
+ */
+static
+const struct ubifs_lprops *do_find_free_space(struct ubifs_info *c,
+                                             int min_space, int pick_free,
+                                             int squeeze)
+{
+       const struct ubifs_lprops *lprops;
+       struct ubifs_lpt_heap *heap;
+       struct scan_data data;
+       int err, i;
+
+       if (squeeze) {
+               lprops = ubifs_fast_find_free(c);
+               if (lprops && lprops->free >= min_space)
+                       return lprops;
+       }
+       if (pick_free) {
+               lprops = ubifs_fast_find_empty(c);
+               if (lprops)
+                       return lprops;
+       }
+       if (!squeeze) {
+               lprops = ubifs_fast_find_free(c);
+               if (lprops && lprops->free >= min_space)
+                       return lprops;
+       }
+       /* There may be an LEB with enough free space on the dirty heap */
+       heap = &c->lpt_heap[LPROPS_DIRTY - 1];
+       for (i = 0; i < heap->cnt; i++) {
+               lprops = heap->arr[i];
+               if (lprops->free >= min_space)
+                       return lprops;
+       }
+       /*
+        * A LEB may have fallen off of the bottom of the free heap, and ended
+        * up as uncategorized even though it has enough free space for us now,
+        * so check the uncategorized list. N.B. neither empty nor freeable LEBs
+        * can end up as uncategorized because they are kept on lists not
+        * finite-sized heaps.
+        */
+       list_for_each_entry(lprops, &c->uncat_list, list) {
+               if (lprops->flags & LPROPS_TAKEN)
+                       continue;
+               if (lprops->flags & LPROPS_INDEX)
+                       continue;
+               if (lprops->free >= min_space)
+                       return lprops;
+       }
+       /* We have looked everywhere in main memory, now scan the flash */
+       if (c->pnodes_have >= c->pnode_cnt)
+               /* All pnodes are in memory, so skip scan */
+               return ERR_PTR(-ENOSPC);
+       data.min_space = min_space;
+       data.pick_free = pick_free;
+       data.lnum = -1;
+       err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum,
+                                   (ubifs_lpt_scan_callback)scan_for_free_cb,
+                                   &data);
+       if (err)
+               return ERR_PTR(err);
+       ubifs_assert(data.lnum >= c->main_first && data.lnum < c->leb_cnt);
+       c->lscan_lnum = data.lnum;
+       lprops = ubifs_lpt_lookup_dirty(c, data.lnum);
+       if (IS_ERR(lprops))
+               return lprops;
+       ubifs_assert(lprops->lnum == data.lnum);
+       ubifs_assert(lprops->free >= min_space);
+       ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
+       ubifs_assert(!(lprops->flags & LPROPS_INDEX));
+       return lprops;
+}
+
+/**
+ * ubifs_find_free_space - find a data LEB with free space.
+ * @c: the UBIFS file-system description object
+ * @min_space: minimum amount of required free space
+ * @free: contains amount of free space in the LEB on exit
+ * @squeeze: whether to try to find space in a non-empty LEB first
+ *
+ * This function looks for an LEB with at least @min_space bytes of free space.
+ * It tries to find an empty LEB if possible. If no empty LEBs are available,
+ * this function searches for a non-empty data LEB. The returned LEB is marked
+ * as "taken".
+ *
+ * This function returns found LEB number in case of success, %-ENOSPC if it
+ * failed to find a LEB with @min_space bytes of free space and other a negative
+ * error codes in case of failure.
+ */
+int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *free,
+                         int squeeze)
+{
+       const struct ubifs_lprops *lprops;
+       int lebs, rsvd_idx_lebs, pick_free = 0, err, lnum, flags;
+
+       dbg_find("min_space %d", min_space);
+       ubifs_get_lprops(c);
+
+       /* Check if there are enough empty LEBs for commit */
+       spin_lock(&c->space_lock);
+       if (c->min_idx_lebs > c->lst.idx_lebs)
+               rsvd_idx_lebs = c->min_idx_lebs -  c->lst.idx_lebs;
+       else
+               rsvd_idx_lebs = 0;
+       lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
+              c->lst.taken_empty_lebs;
+       ubifs_assert(lebs + c->lst.idx_lebs >= c->min_idx_lebs);
+       if (rsvd_idx_lebs < lebs)
+               /*
+                * OK to allocate an empty LEB, but we still don't want to go
+                * looking for one if there aren't any.
+                */
+               if (c->lst.empty_lebs - c->lst.taken_empty_lebs > 0) {
+                       pick_free = 1;
+                       /*
+                        * Because we release the space lock, we must account
+                        * for this allocation here. After the LEB properties
+                        * flags have been updated, we subtract one. Note, the
+                        * result of this is that lprops also decreases
+                        * @taken_empty_lebs in 'ubifs_change_lp()', so it is
+                        * off by one for a short period of time which may
+                        * introduce a small disturbance to budgeting
+                        * calculations, but this is harmless because at the
+                        * worst case this would make the budgeting subsystem
+                        * be more pessimistic than needed.
+                        *
+                        * Fundamentally, this is about serialization of the
+                        * budgeting and lprops subsystems. We could make the
+                        * @space_lock a mutex and avoid dropping it before
+                        * calling 'ubifs_change_lp()', but mutex is more
+                        * heavy-weight, and we want budgeting to be as fast as
+                        * possible.
+                        */
+                       c->lst.taken_empty_lebs += 1;
+               }
+       spin_unlock(&c->space_lock);
+
+       lprops = do_find_free_space(c, min_space, pick_free, squeeze);
+       if (IS_ERR(lprops)) {
+               err = PTR_ERR(lprops);
+               goto out;
+       }
+
+       lnum = lprops->lnum;
+       flags = lprops->flags | LPROPS_TAKEN;
+
+       lprops = ubifs_change_lp(c, lprops, LPROPS_NC, LPROPS_NC, flags, 0);
+       if (IS_ERR(lprops)) {
+               err = PTR_ERR(lprops);
+               goto out;
+       }
+
+       if (pick_free) {
+               spin_lock(&c->space_lock);
+               c->lst.taken_empty_lebs -= 1;
+               spin_unlock(&c->space_lock);
+       }
+
+       *free = lprops->free;
+       ubifs_release_lprops(c);
+
+       if (*free == c->leb_size) {
+               /*
+                * Ensure that empty LEBs have been unmapped. They may not have
+                * been, for example, because of an unclean unmount.  Also
+                * LEBs that were freeable LEBs (free + dirty == leb_size) will
+                * not have been unmapped.
+                */
+               err = ubifs_leb_unmap(c, lnum);
+               if (err)
+                       return err;
+       }
+
+       dbg_find("found LEB %d, free %d", lnum, *free);
+       ubifs_assert(*free >= min_space);
+       return lnum;
+
+out:
+       if (pick_free) {
+               spin_lock(&c->space_lock);
+               c->lst.taken_empty_lebs -= 1;
+               spin_unlock(&c->space_lock);
+       }
+       ubifs_release_lprops(c);
+       return err;
+}
+
+/**
+ * scan_for_idx_cb - callback used by the scan for a free LEB for the index.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @data: information passed to and from the caller of the scan
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_for_idx_cb(struct ubifs_info *c,
+                          const struct ubifs_lprops *lprops, int in_tree,
+                          struct scan_data *data)
+{
+       int ret = LPT_SCAN_CONTINUE;
+
+       /* Exclude LEBs that are currently in use */
+       if (lprops->flags & LPROPS_TAKEN)
+               return LPT_SCAN_CONTINUE;
+       /* Determine whether to add these LEB properties to the tree */
+       if (!in_tree && valuable(c, lprops))
+               ret |= LPT_SCAN_ADD;
+       /* Exclude index LEBS */
+       if (lprops->flags & LPROPS_INDEX)
+               return ret;
+       /* Exclude LEBs that cannot be made empty */
+       if (lprops->free + lprops->dirty != c->leb_size)
+               return ret;
+       /*
+        * We are allocating for the index so it is safe to allocate LEBs with
+        * only free and dirty space, because write buffers are sync'd at commit
+        * start.
+        */
+       data->lnum = lprops->lnum;
+       return LPT_SCAN_ADD | LPT_SCAN_STOP;
+}
+
+/**
+ * scan_for_leb_for_idx - scan for a free LEB for the index.
+ * @c: the UBIFS file-system description object
+ */
+static const struct ubifs_lprops *scan_for_leb_for_idx(struct ubifs_info *c)
+{
+       struct ubifs_lprops *lprops;
+       struct scan_data data;
+       int err;
+
+       data.lnum = -1;
+       err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum,
+                                   (ubifs_lpt_scan_callback)scan_for_idx_cb,
+                                   &data);
+       if (err)
+               return ERR_PTR(err);
+       ubifs_assert(data.lnum >= c->main_first && data.lnum < c->leb_cnt);
+       c->lscan_lnum = data.lnum;
+       lprops = ubifs_lpt_lookup_dirty(c, data.lnum);
+       if (IS_ERR(lprops))
+               return lprops;
+       ubifs_assert(lprops->lnum == data.lnum);
+       ubifs_assert(lprops->free + lprops->dirty == c->leb_size);
+       ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
+       ubifs_assert(!(lprops->flags & LPROPS_INDEX));
+       return lprops;
+}
+
+/**
+ * ubifs_find_free_leb_for_idx - find a free LEB for the index.
+ * @c: the UBIFS file-system description object
+ *
+ * This function looks for a free LEB and returns that LEB number. The returned
+ * LEB is marked as "taken", "index".
+ *
+ * Only empty LEBs are allocated. This is for two reasons. First, the commit
+ * calculates the number of LEBs to allocate based on the assumption that they
+ * will be empty. Secondly, free space at the end of an index LEB is not
+ * guaranteed to be empty because it may have been used by the in-the-gaps
+ * method prior to an unclean unmount.
+ *
+ * If no LEB is found %-ENOSPC is returned. For other failures another negative
+ * error code is returned.
+ */
+int ubifs_find_free_leb_for_idx(struct ubifs_info *c)
+{
+       const struct ubifs_lprops *lprops;
+       int lnum = -1, err, flags;
+
+       ubifs_get_lprops(c);
+
+       lprops = ubifs_fast_find_empty(c);
+       if (!lprops) {
+               lprops = ubifs_fast_find_freeable(c);
+               if (!lprops) {
+                       ubifs_assert(c->freeable_cnt == 0);
+                       if (c->lst.empty_lebs - c->lst.taken_empty_lebs > 0) {
+                               lprops = scan_for_leb_for_idx(c);
+                               if (IS_ERR(lprops)) {
+                                       err = PTR_ERR(lprops);
+                                       goto out;
+                               }
+                       }
+               }
+       }
+
+       if (!lprops) {
+               err = -ENOSPC;
+               goto out;
+       }
+
+       lnum = lprops->lnum;
+
+       dbg_find("found LEB %d, free %d, dirty %d, flags %#x",
+                lnum, lprops->free, lprops->dirty, lprops->flags);
+
+       flags = lprops->flags | LPROPS_TAKEN | LPROPS_INDEX;
+       lprops = ubifs_change_lp(c, lprops, c->leb_size, 0, flags, 0);
+       if (IS_ERR(lprops)) {
+               err = PTR_ERR(lprops);
+               goto out;
+       }
+
+       ubifs_release_lprops(c);
+
+       /*
+        * Ensure that empty LEBs have been unmapped. They may not have been,
+        * for example, because of an unclean unmount. Also LEBs that were
+        * freeable LEBs (free + dirty == leb_size) will not have been unmapped.
+        */
+       err = ubifs_leb_unmap(c, lnum);
+       if (err) {
+               ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
+                                   LPROPS_TAKEN | LPROPS_INDEX, 0);
+               return err;
+       }
+
+       return lnum;
+
+out:
+       ubifs_release_lprops(c);
+       return err;
+}
+
+static int cmp_dirty_idx(const struct ubifs_lprops **a,
+                        const struct ubifs_lprops **b)
+{
+       const struct ubifs_lprops *lpa = *a;
+       const struct ubifs_lprops *lpb = *b;
+
+       return lpa->dirty + lpa->free - lpb->dirty - lpb->free;
+}
+
+static void swap_dirty_idx(struct ubifs_lprops **a, struct ubifs_lprops **b,
+                          int size)
+{
+       struct ubifs_lprops *t = *a;
+
+       *a = *b;
+       *b = t;
+}
+
+/**
+ * ubifs_save_dirty_idx_lnums - save an array of the most dirty index LEB nos.
+ * @c: the UBIFS file-system description object
+ *
+ * This function is called each commit to create an array of LEB numbers of
+ * dirty index LEBs sorted in order of dirty and free space.  This is used by
+ * the in-the-gaps method of TNC commit.
+ */
+int ubifs_save_dirty_idx_lnums(struct ubifs_info *c)
+{
+       int i;
+
+       ubifs_get_lprops(c);
+       /* Copy the LPROPS_DIRTY_IDX heap */
+       c->dirty_idx.cnt = c->lpt_heap[LPROPS_DIRTY_IDX - 1].cnt;
+       memcpy(c->dirty_idx.arr, c->lpt_heap[LPROPS_DIRTY_IDX - 1].arr,
+              sizeof(void *) * c->dirty_idx.cnt);
+       /* Sort it so that the dirtiest is now at the end */
+       sort(c->dirty_idx.arr, c->dirty_idx.cnt, sizeof(void *),
+            (int (*)(const void *, const void *))cmp_dirty_idx,
+            (void (*)(void *, void *, int))swap_dirty_idx);
+       dbg_find("found %d dirty index LEBs", c->dirty_idx.cnt);
+       if (c->dirty_idx.cnt)
+               dbg_find("dirtiest index LEB is %d with dirty %d and free %d",
+                        c->dirty_idx.arr[c->dirty_idx.cnt - 1]->lnum,
+                        c->dirty_idx.arr[c->dirty_idx.cnt - 1]->dirty,
+                        c->dirty_idx.arr[c->dirty_idx.cnt - 1]->free);
+       /* Replace the lprops pointers with LEB numbers */
+       for (i = 0; i < c->dirty_idx.cnt; i++)
+               c->dirty_idx.arr[i] = (void *)(size_t)c->dirty_idx.arr[i]->lnum;
+       ubifs_release_lprops(c);
+       return 0;
+}
+
+/**
+ * scan_dirty_idx_cb - callback used by the scan for a dirty index LEB.
+ * @c: the UBIFS file-system description object
+ * @lprops: LEB properties to scan
+ * @in_tree: whether the LEB properties are in main memory
+ * @data: information passed to and from the caller of the scan
+ *
+ * This function returns a code that indicates whether the scan should continue
+ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
+ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
+ * (%LPT_SCAN_STOP).
+ */
+static int scan_dirty_idx_cb(struct ubifs_info *c,
+                          const struct ubifs_lprops *lprops, int in_tree,
+                          struct scan_data *data)
+{
+       int ret = LPT_SCAN_CONTINUE;
+
+       /* Exclude LEBs that are currently in use */
+       if (lprops->flags & LPROPS_TAKEN)
+               return LPT_SCAN_CONTINUE;
+       /* Determine whether to add these LEB properties to the tree */
+       if (!in_tree && valuable(c, lprops))
+               ret |= LPT_SCAN_ADD;
+       /* Exclude non-index LEBs */
+       if (!(lprops->flags & LPROPS_INDEX))
+               return ret;
+       /* Exclude LEBs with too little space */
+       if (lprops->free + lprops->dirty < c->min_idx_node_sz)
+               return ret;
+       /* Finally we found space */
+       data->lnum = lprops->lnum;
+       return LPT_SCAN_ADD | LPT_SCAN_STOP;
+}
+
+/**
+ * find_dirty_idx_leb - find a dirty index LEB.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns LEB number upon success and a negative error code upon
+ * failure.  In particular, -ENOSPC is returned if a dirty index LEB is not
+ * found.
+ *
+ * Note that this function scans the entire LPT but it is called very rarely.
+ */
+static int find_dirty_idx_leb(struct ubifs_info *c)
+{
+       const struct ubifs_lprops *lprops;
+       struct ubifs_lpt_heap *heap;
+       struct scan_data data;
+       int err, i, ret;
+
+       /* Check all structures in memory first */
+       data.lnum = -1;
+       heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1];
+       for (i = 0; i < heap->cnt; i++) {
+               lprops = heap->arr[i];
+               ret = scan_dirty_idx_cb(c, lprops, 1, &data);
+               if (ret & LPT_SCAN_STOP)
+                       goto found;
+       }
+       list_for_each_entry(lprops, &c->frdi_idx_list, list) {
+               ret = scan_dirty_idx_cb(c, lprops, 1, &data);
+               if (ret & LPT_SCAN_STOP)
+                       goto found;
+       }
+       list_for_each_entry(lprops, &c->uncat_list, list) {
+               ret = scan_dirty_idx_cb(c, lprops, 1, &data);
+               if (ret & LPT_SCAN_STOP)
+                       goto found;
+       }
+       if (c->pnodes_have >= c->pnode_cnt)
+               /* All pnodes are in memory, so skip scan */
+               return -ENOSPC;
+       err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum,
+                                   (ubifs_lpt_scan_callback)scan_dirty_idx_cb,
+                                   &data);
+       if (err)
+               return err;
+found:
+       ubifs_assert(data.lnum >= c->main_first && data.lnum < c->leb_cnt);
+       c->lscan_lnum = data.lnum;
+       lprops = ubifs_lpt_lookup_dirty(c, data.lnum);
+       if (IS_ERR(lprops))
+               return PTR_ERR(lprops);
+       ubifs_assert(lprops->lnum == data.lnum);
+       ubifs_assert(lprops->free + lprops->dirty >= c->min_idx_node_sz);
+       ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
+       ubifs_assert((lprops->flags & LPROPS_INDEX));
+
+       dbg_find("found dirty LEB %d, free %d, dirty %d, flags %#x",
+                lprops->lnum, lprops->free, lprops->dirty, lprops->flags);
+
+       lprops = ubifs_change_lp(c, lprops, LPROPS_NC, LPROPS_NC,
+                                lprops->flags | LPROPS_TAKEN, 0);
+       if (IS_ERR(lprops))
+               return PTR_ERR(lprops);
+
+       return lprops->lnum;
+}
+
+/**
+ * get_idx_gc_leb - try to get a LEB number from trivial GC.
+ * @c: the UBIFS file-system description object
+ */
+static int get_idx_gc_leb(struct ubifs_info *c)
+{
+       const struct ubifs_lprops *lp;
+       int err, lnum;
+
+       err = ubifs_get_idx_gc_leb(c);
+       if (err < 0)
+               return err;
+       lnum = err;
+       /*
+        * The LEB was due to be unmapped after the commit but
+        * it is needed now for this commit.
+        */
+       lp = ubifs_lpt_lookup_dirty(c, lnum);
+       if (unlikely(IS_ERR(lp)))
+               return PTR_ERR(lp);
+       lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
+                            lp->flags | LPROPS_INDEX, -1);
+       if (unlikely(IS_ERR(lp)))
+               return PTR_ERR(lp);
+       dbg_find("LEB %d, dirty %d and free %d flags %#x",
+                lp->lnum, lp->dirty, lp->free, lp->flags);
+       return lnum;
+}
+
+/**
+ * find_dirtiest_idx_leb - find dirtiest index LEB from dirtiest array.
+ * @c: the UBIFS file-system description object
+ */
+static int find_dirtiest_idx_leb(struct ubifs_info *c)
+{
+       const struct ubifs_lprops *lp;
+       int lnum;
+
+       while (1) {
+               if (!c->dirty_idx.cnt)
+                       return -ENOSPC;
+               /* The lprops pointers were replaced by LEB numbers */
+               lnum = (size_t)c->dirty_idx.arr[--c->dirty_idx.cnt];
+               lp = ubifs_lpt_lookup(c, lnum);
+               if (IS_ERR(lp))
+                       return PTR_ERR(lp);
+               if ((lp->flags & LPROPS_TAKEN) || !(lp->flags & LPROPS_INDEX))
+                       continue;
+               lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
+                                    lp->flags | LPROPS_TAKEN, 0);
+               if (IS_ERR(lp))
+                       return PTR_ERR(lp);
+               break;
+       }
+       dbg_find("LEB %d, dirty %d and free %d flags %#x", lp->lnum, lp->dirty,
+                lp->free, lp->flags);
+       ubifs_assert(lp->flags | LPROPS_TAKEN);
+       ubifs_assert(lp->flags | LPROPS_INDEX);
+       return lnum;
+}
+
+/**
+ * ubifs_find_dirty_idx_leb - try to find dirtiest index LEB as at last commit.
+ * @c: the UBIFS file-system description object
+ *
+ * This function attempts to find an untaken index LEB with the most free and
+ * dirty space that can be used without overwriting index nodes that were in the
+ * last index committed.
+ */
+int ubifs_find_dirty_idx_leb(struct ubifs_info *c)
+{
+       int err;
+
+       ubifs_get_lprops(c);
+
+       /*
+        * We made an array of the dirtiest index LEB numbers as at the start of
+        * last commit.  Try that array first.
+        */
+       err = find_dirtiest_idx_leb(c);
+
+       /* Next try scanning the entire LPT */
+       if (err == -ENOSPC)
+               err = find_dirty_idx_leb(c);
+
+       /* Finally take any index LEBs awaiting trivial GC */
+       if (err == -ENOSPC)
+               err = get_idx_gc_leb(c);
+
+       ubifs_release_lprops(c);
+       return err;
+}
diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
new file mode 100644 (file)
index 0000000..d0f3dac
--- /dev/null
@@ -0,0 +1,773 @@
+/*
+ * 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 garbage collection. The procedure for garbage collection
+ * is different depending on whether a LEB as an index LEB (contains index
+ * nodes) or not. For non-index LEBs, garbage collection finds a LEB which
+ * contains a lot of dirty space (obsolete nodes), and copies the non-obsolete
+ * nodes to the journal, at which point the garbage-collected LEB is free to be
+ * reused. For index LEBs, garbage collection marks the non-obsolete index nodes
+ * dirty in the TNC, and after the next commit, the garbage-collected LEB is
+ * to be reused. Garbage collection will cause the number of dirty index nodes
+ * to grow, however sufficient space is reserved for the index to ensure the
+ * commit will never run out of space.
+ */
+
+#include <linux/pagemap.h>
+#include "ubifs.h"
+
+/*
+ * GC tries to optimize the way it fit nodes to available space, and it sorts
+ * nodes a little. The below constants are watermarks which define "large",
+ * "medium", and "small" nodes.
+ */
+#define MEDIUM_NODE_WM (UBIFS_BLOCK_SIZE / 4)
+#define SMALL_NODE_WM  UBIFS_MAX_DENT_NODE_SZ
+
+/*
+ * GC may need to move more then one LEB to make progress. The below constants
+ * define "soft" and "hard" limits on the number of LEBs the garbage collector
+ * may move.
+ */
+#define SOFT_LEBS_LIMIT 4
+#define HARD_LEBS_LIMIT 32
+
+/**
+ * switch_gc_head - switch the garbage collection journal head.
+ * @c: UBIFS file-system description object
+ * @buf: buffer to write
+ * @len: length of the buffer to write
+ * @lnum: LEB number written is returned here
+ * @offs: offset written is returned here
+ *
+ * This function switch the GC head to the next LEB which is reserved in
+ * @c->gc_lnum. Returns %0 in case of success, %-EAGAIN if commit is required,
+ * and other negative error code in case of failures.
+ */
+static int switch_gc_head(struct ubifs_info *c)
+{
+       int err, gc_lnum = c->gc_lnum;
+       struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+
+       ubifs_assert(gc_lnum != -1);
+       dbg_gc("switch GC head from LEB %d:%d to LEB %d (waste %d bytes)",
+              wbuf->lnum, wbuf->offs + wbuf->used, gc_lnum,
+              c->leb_size - wbuf->offs - wbuf->used);
+
+       err = ubifs_wbuf_sync_nolock(wbuf);
+       if (err)
+               return err;
+
+       /*
+        * The GC write-buffer was synchronized, we may safely unmap
+        * 'c->gc_lnum'.
+        */
+       err = ubifs_leb_unmap(c, gc_lnum);
+       if (err)
+               return err;
+
+       err = ubifs_add_bud_to_log(c, GCHD, gc_lnum, 0);
+       if (err)
+               return err;
+
+       c->gc_lnum = -1;
+       err = ubifs_wbuf_seek_nolock(wbuf, gc_lnum, 0, UBI_LONGTERM);
+       return err;
+}
+
+/**
+ * move_nodes - move nodes.
+ * @c: UBIFS file-system description object
+ * @sleb: describes nodes to move
+ *
+ * This function moves valid nodes from data LEB described by @sleb to the GC
+ * journal head. The obsolete nodes are dropped.
+ *
+ * When moving nodes we have to deal with classical bin-packing problem: the
+ * space in the current GC journal head LEB and in @c->gc_lnum are the "bins",
+ * where the nodes in the @sleb->nodes list are the elements which should be
+ * fit optimally to the bins. This function uses the "first fit decreasing"
+ * strategy, although it does not really sort the nodes but just split them on
+ * 3 classes - large, medium, and small, so they are roughly sorted.
+ *
+ * This function returns zero in case of success, %-EAGAIN if commit is
+ * required, and other negative error codes in case of other failures.
+ */
+static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
+{
+       struct ubifs_scan_node *snod, *tmp;
+       struct list_head large, medium, small;
+       struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+       int avail, err, min = INT_MAX;
+
+       INIT_LIST_HEAD(&large);
+       INIT_LIST_HEAD(&medium);
+       INIT_LIST_HEAD(&small);
+
+       list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
+               struct list_head *lst;
+
+               ubifs_assert(snod->type != UBIFS_IDX_NODE);
+               ubifs_assert(snod->type != UBIFS_REF_NODE);
+               ubifs_assert(snod->type != UBIFS_CS_NODE);
+
+               err = ubifs_tnc_has_node(c, &snod->key, 0, sleb->lnum,
+                                        snod->offs, 0);
+               if (err < 0)
+                       goto out;
+
+               lst = &snod->list;
+               list_del(lst);
+               if (!err) {
+                       /* The node is obsolete, remove it from the list */
+                       kfree(snod);
+                       continue;
+               }
+
+               /*
+                * Sort the list of nodes so that large nodes go first, and
+                * small nodes go last.
+                */
+               if (snod->len > MEDIUM_NODE_WM)
+                       list_add(lst, &large);
+               else if (snod->len > SMALL_NODE_WM)
+                       list_add(lst, &medium);
+               else
+                       list_add(lst, &small);
+
+               /* And find the smallest node */
+               if (snod->len < min)
+                       min = snod->len;
+       }
+
+       /*
+        * Join the tree lists so that we'd have one roughly sorted list
+        * ('large' will be the head of the joined list).
+        */
+       list_splice(&medium, large.prev);
+       list_splice(&small, large.prev);
+
+       if (wbuf->lnum == -1) {
+               /*
+                * The GC journal head is not set, because it is the first GC
+                * invocation since mount.
+                */
+               err = switch_gc_head(c);
+               if (err)
+                       goto out;
+       }
+
+       /* Write nodes to their new location. Use the first-fit strategy */
+       while (1) {
+               avail = c->leb_size - wbuf->offs - wbuf->used;
+               list_for_each_entry_safe(snod, tmp, &large, list) {
+                       int new_lnum, new_offs;
+
+                       if (avail < min)
+                               break;
+
+                       if (snod->len > avail)
+                               /* This node does not fit */
+                               continue;
+
+                       cond_resched();
+
+                       new_lnum = wbuf->lnum;
+                       new_offs = wbuf->offs + wbuf->used;
+                       err = ubifs_wbuf_write_nolock(wbuf, snod->node,
+                                                     snod->len);
+                       if (err)
+                               goto out;
+                       err = ubifs_tnc_replace(c, &snod->key, sleb->lnum,
+                                               snod->offs, new_lnum, new_offs,
+                                               snod->len);
+                       if (err)
+                               goto out;
+
+                       avail = c->leb_size - wbuf->offs - wbuf->used;
+                       list_del(&snod->list);
+                       kfree(snod);
+               }
+
+               if (list_empty(&large))
+                       break;
+
+               /*
+                * Waste the rest of the space in the LEB and switch to the
+                * next LEB.
+                */
+               err = switch_gc_head(c);
+               if (err)
+                       goto out;
+       }
+
+       return 0;
+
+out:
+       list_for_each_entry_safe(snod, tmp, &large, list) {
+               list_del(&snod->list);
+               kfree(snod);
+       }
+       return err;
+}
+
+/**
+ * gc_sync_wbufs - sync write-buffers for GC.
+ * @c: UBIFS file-system description object
+ *
+ * We must guarantee that obsoleting nodes are on flash. Unfortunately they may
+ * be in a write-buffer instead. That is, a node could be written to a
+ * write-buffer, obsoleting another node in a LEB that is GC'd. If that LEB is
+ * erased before the write-buffer is sync'd and then there is an unclean
+ * unmount, then an existing node is lost. To avoid this, we sync all
+ * write-buffers.
+ *
+ * This function returns %0 on success or a negative error code on failure.
+ */
+static int gc_sync_wbufs(struct ubifs_info *c)
+{
+       int err, i;
+
+       for (i = 0; i < c->jhead_cnt; i++) {
+               if (i == GCHD)
+                       continue;
+               err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+/**
+ * ubifs_garbage_collect_leb - garbage-collect a logical eraseblock.
+ * @c: UBIFS file-system description object
+ * @lp: describes the LEB to garbage collect
+ *
+ * This function garbage-collects an LEB and returns one of the @LEB_FREED,
+ * @LEB_RETAINED, etc positive codes in case of success, %-EAGAIN if commit is
+ * required, and other negative error codes in case of failures.
+ */
+int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp)
+{
+       struct ubifs_scan_leb *sleb;
+       struct ubifs_scan_node *snod;
+       struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
+       int err = 0, lnum = lp->lnum;
+
+       ubifs_assert(c->gc_lnum != -1 || wbuf->offs + wbuf->used == 0 ||
+                    c->need_recovery);
+       ubifs_assert(c->gc_lnum != lnum);
+       ubifs_assert(wbuf->lnum != lnum);
+
+       /*
+        * We scan the entire LEB even though we only really need to scan up to
+        * (c->leb_size - lp->free).
+        */
+       sleb = ubifs_scan(c, lnum, 0, c->sbuf);
+       if (IS_ERR(sleb))
+               return PTR_ERR(sleb);
+
+       ubifs_assert(!list_empty(&sleb->nodes));
+       snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list);
+
+       if (snod->type == UBIFS_IDX_NODE) {
+               struct ubifs_gced_idx_leb *idx_gc;
+
+               dbg_gc("indexing LEB %d (free %d, dirty %d)",
+                      lnum, lp->free, lp->dirty);
+               list_for_each_entry(snod, &sleb->nodes, list) {
+                       struct ubifs_idx_node *idx = snod->node;
+                      &nb