This adds a few routines to ifile, which are used to find out created,
deleted, or modifed inodes between two versions of ifile, and
enumerate the changes.

Signed-off-by: Ryusuke Konishi <[email protected]>
---
 fs/nilfs2/ifile.c |  184 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nilfs2/ifile.h |   20 ++++++
 2 files changed, 204 insertions(+), 0 deletions(-)

diff --git a/fs/nilfs2/ifile.c b/fs/nilfs2/ifile.c
index 684d763..8980149 100644
--- a/fs/nilfs2/ifile.c
+++ b/fs/nilfs2/ifile.c
@@ -155,6 +155,190 @@ int nilfs_ifile_get_inode_block(struct inode *ifile, 
ino_t ino,
        return err;
 }
 
+static ssize_t
+nilfs_ifile_compare_block(struct inode *ifile1, struct inode *ifile2,
+                         ino_t start, const struct nilfs_bmap_diff *diff,
+                         struct nilfs_ifile_change *changes,
+                         size_t maxchanges)
+{
+       struct buffer_head *ibh1 = NULL, *ibh2 = NULL;
+       struct nilfs_inode *raw_inode1, *raw_inode2;
+       void *kaddr1, *kaddr2;
+       size_t isz = NILFS_MDT(ifile1)->mi_entry_size;
+       __u64 nr;
+       ino_t ino, end;
+       int t;
+       int ret;
+       ssize_t n = 0;
+
+       t = nilfs_palloc_block_type(ifile1, diff->key, &nr);
+       if (t != NILFS_PALLOC_ENTRY_BLOCK)
+               goto out;
+
+       ino = max_t(ino_t, nr, start);
+       end = nr + NILFS_MDT(ifile1)->mi_entries_per_block - 1;
+       if (ino > end)
+               goto out;
+
+       if (diff->ptr1 != NILFS_BMAP_INVALID_PTR) {
+               ret = nilfs_palloc_get_entry_block(ifile1, nr, 0, &ibh1);
+               if (ret < 0) {
+                       WARN_ON(ret == -ENOENT); /* ifile is broken */
+                       n = ret;
+                       goto out;
+               }
+       }
+       if (diff->ptr2 != NILFS_BMAP_INVALID_PTR) {
+               ret = nilfs_palloc_get_entry_block(ifile2, nr, 0, &ibh2);
+               if (ret < 0) {
+                       WARN_ON(ret == -ENOENT); /* ifile is broken */
+                       n = ret;
+                       brelse(ibh1); /* brelse(NULL) is ignored */
+                       goto out;
+               }
+       }
+
+       if (!ibh1) {
+               if (!ibh2)
+                       goto out_bh;
+
+               kaddr2 = kmap_atomic(ibh2->b_page, KM_USER0);
+               raw_inode2 = nilfs_palloc_block_get_entry(ifile2, ino, ibh2,
+                                                         kaddr2);
+               for ( ; ino <= end; ino++) {
+                       if (le16_to_cpu(raw_inode2->i_links_count)) {
+                               changes[n].ino = ino;
+                               changes[n].bh1 = NULL;
+                               get_bh(ibh2);
+                               changes[n].bh2 = ibh2;
+                               n++;
+                               if (n == maxchanges)
+                                       break;
+                       }
+                       raw_inode2 = (void *)raw_inode2 + isz;
+               }
+               kunmap_atomic(kaddr2, KM_USER0);
+
+       } else if (!ibh2) {
+               kaddr1 = kmap_atomic(ibh1->b_page, KM_USER0);
+               raw_inode1 = nilfs_palloc_block_get_entry(ifile1, ino, ibh1,
+                                                         kaddr1);
+               for ( ; ino <= end; ino++) {
+                       if (le16_to_cpu(raw_inode1->i_links_count)) {
+                               changes[n].ino = ino;
+                               get_bh(ibh1);
+                               changes[n].bh1 = ibh1;
+                               changes[n].bh2 = NULL;
+                               n++;
+                               if (n == maxchanges)
+                                       break;
+                       }
+                       raw_inode1 = (void *)raw_inode1 + isz;
+               }
+               kunmap_atomic(kaddr1, KM_USER0);
+       } else {
+               kaddr1 = kmap_atomic(ibh1->b_page, KM_USER0);
+               raw_inode1 = nilfs_palloc_block_get_entry(ifile1, ino, ibh1,
+                                                         kaddr1);
+               kaddr2 = kmap_atomic(ibh2->b_page, KM_USER1);
+               raw_inode2 = nilfs_palloc_block_get_entry(ifile2, ino, ibh2,
+                                                         kaddr2);
+               for (; ino <= end; ino++) {
+                       if (raw_inode1->i_ctime_nsec !=
+                           raw_inode2->i_ctime_nsec ||
+                           raw_inode1->i_ctime != raw_inode2->i_ctime) {
+                               changes[n].ino = ino;
+                               if (le16_to_cpu(raw_inode1->i_links_count)) {
+                                       get_bh(ibh1);
+                                       changes[n].bh1 = ibh1;
+                               } else {
+                                       changes[n].bh1 = NULL;
+                               }
+                               if (le16_to_cpu(raw_inode2->i_links_count)) {
+                                       get_bh(ibh2);
+                                       changes[n].bh2 = ibh2;
+                               } else {
+                                       changes[n].bh2 = NULL;
+                               }
+                               n++;
+                               if (n == maxchanges)
+                                       break;
+                       }
+                       raw_inode1 = (void *)raw_inode1 + isz;
+                       raw_inode2 = (void *)raw_inode2 + isz;
+               }
+               kunmap_atomic(kaddr1, KM_USER0);
+               kunmap_atomic(kaddr2, KM_USER1);
+       }
+out_bh:
+       brelse(ibh1);
+       brelse(ibh2);
+out:
+       return n;
+}
+
+/**
+ * nilfs_ifile_compare - compare two ifiles and find modified inodes
+ * @ifile1: source ifile inode
+ * @ifile2: target ifile inode
+ * @start: start inode number
+ * @changes: buffer to store nilfs_ifile_change structures
+ * @maxchanges: maximum number of changes that can be stored in @changes
+ */
+ssize_t nilfs_ifile_compare(struct inode *ifile1, struct inode *ifile2,
+                           ino_t start, struct nilfs_ifile_change *changes,
+                           size_t maxchanges)
+{
+       struct nilfs_bmap_diff *diffs;
+       __u64 blkoff;
+       size_t maxdiffs;
+       ssize_t nc = 0, nd, n;
+       int i;
+
+       diffs = (struct nilfs_bmap_diff *)__get_free_pages(GFP_NOFS, 0);
+       if (unlikely(!diffs))
+               return -ENOMEM;
+
+       maxdiffs = PAGE_SIZE / sizeof(*diffs);
+
+       blkoff = nilfs_palloc_entry_blkoff(ifile1, start);
+
+       do {
+               nd = nilfs_bmap_compare(NILFS_I(ifile1)->i_bmap,
+                                       NILFS_I(ifile2)->i_bmap,
+                                       blkoff, diffs, maxdiffs);
+               if (nd < 0) {
+                       nc = nd;
+                       break;
+               }
+               if (nd == 0)
+                       break;
+               for (i = 0; i < nd; i++) {
+                       n = nilfs_ifile_compare_block(
+                               ifile1, ifile2, start, &diffs[i],
+                               &changes[nc], maxchanges - nc);
+                       if (n < 0) {
+                               nc = n;
+                               goto failed;
+                       }
+                       nc += n;
+                       if (nc == maxchanges)
+                               goto out;
+               }
+               blkoff = diffs[i - 1].key + 1;
+       } while (nd == maxdiffs);
+out:
+       free_pages((unsigned long)diffs, 0);
+       return nc;
+
+failed:
+       for (i = 0; i < nc; i++) {
+               brelse(changes[i].bh1);
+               brelse(changes[i].bh2);
+       }
+       goto out;
+}
+
 /**
  * nilfs_ifile_read - read or get ifile inode
  * @sb: super block instance
diff --git a/fs/nilfs2/ifile.h b/fs/nilfs2/ifile.h
index 59b6f2b..fe63ae8 100644
--- a/fs/nilfs2/ifile.h
+++ b/fs/nilfs2/ifile.h
@@ -32,6 +32,22 @@
 #include "alloc.h"
 
 
+/**
+ * struct nilfs_ifile_change - array item to store ifile comparison result
+ * @ino: inode number
+ * @bh1: pointers to buffer heads having source of changed disk inode
+ * @bh2: pointers to buffer heads having target of changed disk inode
+ *
+ * For new inodes, ptr array becomes @bh1 == NULL and @bh2 != NULL.
+ * For deleted inodes, ptr array becomes @bh1 != NULL and @bh2 == NULL.
+ * For modifed inodes, both @bh1 and @bh2 point to a valid item.
+ */
+struct nilfs_ifile_change {
+       ino_t ino;
+       struct buffer_head *bh1;
+       struct buffer_head *bh2;
+};
+
 static inline struct nilfs_inode *
 nilfs_ifile_map_inode(struct inode *ifile, ino_t ino, struct buffer_head *ibh)
 {
@@ -49,6 +65,10 @@ int nilfs_ifile_create_inode(struct inode *, ino_t *, struct 
buffer_head **);
 int nilfs_ifile_delete_inode(struct inode *, ino_t);
 int nilfs_ifile_get_inode_block(struct inode *, ino_t, struct buffer_head **);
 
+ssize_t nilfs_ifile_compare(struct inode *ifile1, struct inode *ifile2,
+                           ino_t start, struct nilfs_ifile_change *buf,
+                           size_t maxchanges);
+
 int nilfs_ifile_read(struct super_block *sb, struct nilfs_root *root,
                     size_t inode_size, struct nilfs_inode *raw_inode,
                     struct inode **inodep);
-- 
1.7.3.5

--
To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to